# Manual hreflang Links for Translated Pages

> Adds a box to the editor where you list the URLs of a page's translations; the matching hreflang tags (plus x-default) are printed in the page head, for multilingual sites that do not use a translation plugin.

- Type: PHP
- Runs: Run everywhere
- Category: [Multilingual](https://wpide.com/library/category/multilingual/)
- Tags: hreflang, seo, translations
- Author: WPIDE Team
- Installs: 0
- Updated: 2026-10-28
- Page: https://wpide.com/library/i18n-manual-hreflang/

## Code

```php
add_action( 'add_meta_boxes', function () {
    add_meta_box( 'wpide-hreflang', __( 'Translations (hreflang)' ), function ( $post ) {
        wp_nonce_field( 'wpide_hreflang_save', 'wpide_hreflang_nonce' );
        printf(
            '<textarea name="wpide_hreflang" rows="4" style="width:100%%" placeholder="en https://example.com/page/&#10;fr https://example.com/fr/page/">%s</textarea>',
            esc_textarea( get_post_meta( $post->ID, '_wpide_hreflang', true ) )
        );
    }, array( 'post', 'page' ), 'side' );
} );

add_action( 'save_post', function ( $post_id ) {
    if ( ! isset( $_POST['wpide_hreflang_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['wpide_hreflang_nonce'] ), 'wpide_hreflang_save' ) || ! current_user_can( 'edit_post', $post_id ) ) {
        return;
    }

    $lines = array();
    foreach ( preg_split( '/\R/', (string) wp_unslash( $_POST['wpide_hreflang'] ?? '' ) ) as $line ) {
        $parts = preg_split( '/\s+/', trim( $line ), 2 );
        if ( 2 === count( $parts ) && preg_match( '/^([a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})?|x-default)$/', $parts[0] ) && wp_http_validate_url( $parts[1] ) ) {
            $lines[] = $parts[0] . ' ' . esc_url_raw( $parts[1] );
        }
    }

    update_post_meta( $post_id, '_wpide_hreflang', implode( "\n", $lines ) );
} );

add_action( 'wp_head', function () {
    if ( ! is_singular() ) {
        return;
    }

    $raw = get_post_meta( get_queried_object_id(), '_wpide_hreflang', true );

    foreach ( array_filter( explode( "\n", (string) $raw ) ) as $line ) {
        list( $lang, $url ) = array_pad( explode( ' ', $line, 2 ), 2, '' );
        if ( $url ) {
            printf( '<link rel="alternate" hreflang="%1$s" href="%2$s">' . "\n", esc_attr( $lang ), esc_url( $url ) );
        }
    }
}, 2 );
```

## Notes

One per line, as language code, a space, then the URL (e.g. "fr https://example.com/fr/page/"). Add "x-default URL" for the fallback version. List the page itself too.

## How to use it

Install the free WPIDE plugin (https://wordpress.org/plugins/wpide/), find "Manual hreflang Links for Translated Pages" in its snippet library and add it in one click; or create a new PHP snippet in WPIDE (or any code snippets plugin) and paste the code above. More snippets: https://wpide.com/library/
