Command Palette

Search for a command to run...

So fügen Sie Hreflang in WordPress hinzu (ohne Plugin)

So fügen Sie Hreflang in WordPress hinzu (ohne Plugin)

T
Toolz Team
|Aug 23, 2026|13 min lesen

Teil der Sammlung SEO-Tools

Die meisten WordPress-Hreflang-Beratungen sind " Installieren Sie ein mehrsprachiges Plugin und es verarbeitet es. " Das ist in Ordnung, wenn das Plugin Ihre Übersetzungen besitzt. Es ist die falsche Antwort für den Fall, den ich am häufigsten treffe: eine Website mit zwei oder drei Sprachversionen, die als separate Unterverzeichnisse erstellt wurden, separate Installationen oder eine Reihe manuell übersetzter Seiten, auf denen niemand ein vollständiges Übersetzungsframework und sein Datenbankschema für vier Seiten haben möchte.

Ich pflege WordPress-Eigenschaften in dieser Form, einschließlich Dokumentationsseiten, auf denen die Übersetzungen handgeschrieben sind und unter denen sie leben /de/ und /es/. Der hreflang es gibt vierzig zeilen in einer themendatei, es liest aus einem array, und es ist nie gedriftet Diese anleitung ist dieser code, plus die teile, die schief gehen, wenn ein plugin hreflang neben ihrem thema aussendet.

Es sitzt unter der Komplette Hreflang-Führung, neben der Next.js-versiondrohen

tl; dr: WordPress gibt keinen Hreflang von sich aus abHaken wp_headSchleife über eine Gebietsschemakarte und Drucken des vollständigen reziproken Satzes einschließlich x-default Auf jeder Seite der Gruppe Erstellen Sie URLs mit home_url() Oder einen Ursprung pro Standort statt einer Hardcodierung, behalten Sie jede Seite bei's kanonisch selbstreferenziell und überprüfen Sie, ob kein SEO-Plugin bereits einen konkurrierenden Block aussendet. Für Multi-Site-Setups, get_sites() Gibt Ihnen die Karte; Für eine einzelne Website mit übersetzten Seiten reicht ein Beitrag zur Metafeldzuordnung aus.

Fügt WordPress von selbst Hreflang hinzu?

No. Core gibt einen kanonischen Link, einen Shortlink und verschiedene Feed- und REST-Links aus wp_head(, aber nichts über Sprache wechselt. get_locale() Weiß, in welcher Sprache die Seite ist und kein Teil des Kerns macht daraus eine Annotation.

Plugins füllen die Lücke Polylang und WPML geben Hreflang für die von ihnen verwalteten Übersetzungen aus, und die meisten SEO-Plugins werden es aussenden, wenn Sie eine Übersetzungsbeziehung konfigurieren, die sie sehen können Wenn Sie bereits eine davon verwenden und sie Ihre gesamte Website abdeckt, verwenden Sie sie und überspringen Sie diesen Artikel. Der Grund zum Handrollen besteht darin, dass Ihre Übersetzungen außerhalb dessen existieren, wovon das Plugin weiß, und ein Plugin, das einen Teilsatz aussendet, ist schlechter als eines, das keine aussendet.

Die Minimalversion: ein Array, ein Hook

Für eine Website, deren Übersetzungen ein bekannter, kleiner Satz von URL-Präfixen sind:

<?php
/**
 * Hreflang annotations for a hand-translated multilingual site.
 * Drop in the theme's functions.php or a small mu-plugin.
 */
function mysite_hreflang_map(): array {
    return [
        'en-US'     => 'https://example.com',
        'de-DE'     => 'https://example.com/de',
        'es-ES'     => 'https://example.com/es',
        'x-default' => 'https://example.com',
    ];
}

function mysite_print_hreflang(): void {
    $path = mysite_current_path(); // e.g. '/pricing/'
    foreach ( mysite_hreflang_map() as $hreflang => $origin ) {
        printf(
            '<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
            esc_attr( $hreflang ),
            esc_url( untrailingslashit( $origin ) . $path )
        );
    }
}
add_action( 'wp_head', 'mysite_print_hreflang', 1 );

Drei Dinge, die das richtig macht, was handgeschriebene Blöcke normalerweise nicht tun.

Es druckt das komplette Set auf jeder Seite aus‘s, einschließlich der page&#39; s eigenen Sprache Die Gegenseitigkeit ist strukturell zufrieden: es gibt keinen Zweig, der einen Eintrag weglassen könnte, denn es gibt überhaupt keinen Zweig.

x-default Zeile im selben Array ist, so wird es von derselben Schleife emittiert und kann nicht auf einer Vorlage vergessen werden.

URLs werden entkommen. esc_url auf einem href in wp_head Ist nicht optional und hreflang-Werte werden durchgegangen esc_attr Aus dem gleichen Grund.

Den aktuellen Weg erarbeiten

Der Weg ist der Teil, der Pflege braucht, denn er muss der Weg sein innerlich Die Sprache, nicht der vollständige Anforderungs-URI.

function mysite_current_path(): string {
    $home = wp_parse_url( home_url(), PHP_URL_PATH ) ?: '';
    $req  = wp_parse_url( $_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH ) ?: '/';

    // Strip the language prefix this install is served under.
    if ( $home && str_starts_with( $req, $home ) ) {
        $req = substr( $req, strlen( $home ) );
    }
    return user_trailingslashit( '/' . ltrim( $req, '/' ) );
}

Wenn Ihre deutsche Installation bei lebt example.com/de Und der Besucher ist am example.com/de/preise/Dies kehrt zurück /preise/Dem dann jeder locale&#39; s-Ursprung vorangestellt wird Die Handhabung des nachlaufenden Schrägstrichs ist wichtiger, als es aussieht: /preise und /preise/ Es gibt unterschiedliche URLs, und das Zeigen einer Anmerkung auf die nichtkanonische Form ist die Nachlauf-schlitz-inkongruenz Das führt zu Return-Tag-Fehlern an einem Set, das sonst perfekt aussieht. user_trailingslashit Respektiert die Site&#39;s-Permalink-Einstellung, sodass sie mit dem übereinstimmt, was WordPress bereitstellt.

Hier wird davon ausgegangen, dass übersetzte URLs strukturell parallel sind: /pricing/ Karten zu /de/pricing/. Wenn Ihr Deutsch slugs auch übersetzt wird, wird /de/preise/Diese Funktion reicht nicht aus und Sie benötigen eine explizite Zuordnung, die den nächsten Abschnitt darstellt.

Wenn slugs sich unterscheiden: eine Post-Meta-Zuordnung

Übersetzte slugs sind besser für Benutzer und für das Ranking, und sie bedeuten, dass der Pfad nicht abgeleitet werden kann Speichern Sie stattdessen die Gegenstücke:

/**
 * Meta key holds a JSON map: {"de-DE": 123, "es-ES": 456} of post IDs,
 * or absolute URLs for external installs.
 */
function mysite_alternates_for( int $post_id ): array {
    $raw = get_post_meta( $post_id, '_hreflang_alternates', true );
    $map = $raw ? json_decode( $raw, true ) : [];
    if ( ! is_array( $map ) ) {
        return [];
    }

    $out = [];
    foreach ( $map as $hreflang => $target ) {
        $url = is_numeric( $target ) ? get_permalink( (int) $target ) : $target;
        if ( $url ) {
            $out[ $hreflang ] = $url;
        }
    }
    return $out;
}

Dann die wp_head Rückruf führt den aktuellen Beitrag zusammen & #39;s eigener Eintrag in diese Karte vor dem Drucken, sodass der Satz vollständig und selbstreferenziell ist:

function mysite_print_hreflang_mapped(): void {
    if ( ! is_singular() ) {
        return;
    }
    $post_id = get_queried_object_id();
    $alts    = mysite_alternates_for( $post_id );
    if ( count( $alts ) < 1 ) {
        return; // Nothing to declare: a one-entry cluster is not a cluster.
    }

    $alts[ mysite_hreflang_for_locale( get_locale() ) ] = get_permalink( $post_id );
    $alts['x-default'] = $alts['en-US'] ?? get_permalink( $post_id );

    foreach ( $alts as $hreflang => $url ) {
        printf(
            '<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
            esc_attr( $hreflang ),
            esc_url( $url )
        );
    }
}

Die Wartungskosten sind, dass die Meta auf beiden Seiten geschrieben werden muss Eine Admin-UI mit einem Gegenstück-Picker ist die ehrliche Lösung; ein WP-CLI-Befehl, der die Karte aus einem CSV füllt, ist der schnelle Was Sie nicht tun dürfen, ist, sie nur auf einer Seite zu füllen, denn eine einseitige Deklaration ist genau die Fehlendes Rückgabetag Versagen.

Multisite: Ableiten der Karte aus dem Netzwerk

Wenn jede Sprache eine Site in einem Multisite-Netzwerk ist, existiert die Karte bereits:

function mysite_network_hreflang_map(): array {
    $map = [];
    foreach ( get_sites( [ 'number' => 50 ] ) as $site ) {
        switch_to_blog( (int) $site->blog_id );
        $map[ mysite_hreflang_for_locale( get_locale() ) ] = home_url();
        restore_current_blog();
    }
    return $map;
}

Das Ergebnis in einem Transient zwischenspeichern; switch_to_blog In einem wp_head Hook auf jede Anfrage ist ein echter Kostenfaktor auf einem Netzwerk jeder Größe Und Vorsicht mit get_locale() Unter switch_to_blog: Es spiegelt die geschaltete Website nur wider, wenn die Website&#39;s WPLANG Option gesetzt, was der übliche Fall ist, aber in Netzwerken, die durch eine Konstante konfiguriert sind, nicht gewährleistet ist.

Welcher Ansatz passt zu Ihrer Seite?

Einrichtung heraufkommen Aufwand
Parallel slugs, wenige Orte Statisches Array plus abgeleiteter Pfad Niedrigste; eine Funktion
Übersetzt slugs Gegenstückkarte nach der Metastufe Moderat; benötigt einen Bearbeitungsworkflow
Multisite, eine Site pro Sprache Ableiten von get_sites()Cached Niedriger Code, benötigt Cache-Disziplin
Polylang oder WPML verwalten Übersetzungen Verwenden Sie das Plugin&#39;s Ausgabe Überprüfen Sie, ob es x-Standard ausgibt
SEO-Plugin mit manuellen Übersetzungsfeldern Verwenden Sie es und überprüfen Sie dann den gerenderten Kopf Achten Sie auf doppelte Blöcke

Stattdessen wird Hreflang in die Sitemap eingefügt

Wenn Ihre Seitenköpfe bereits überfüllt sind oder Sie genügend Lokalitäten haben, sodass der Block länger ist als der Rest des Kopfes, ist die Sitemap die Alternative. WordPress 5.5 und später versenden Kernsitemaps, und sie sind filtrierbar, obwohl die Filter nicht für Alternativen konzipiert wurden und die Namespace-Deklaration auf <urlset> Ist nicht etwas Kern lässt Sie sauber berühren.

Der pragmatische Weg ist eine eigenständige Sitemap, die eher einer Umschreibungsregel als einem Kampf mit dient wp_sitemaps:

add_action( 'init', function () {
    add_rewrite_rule( '^sitemap-i18n\.xml$', 'index.php?mysite_i18n_sitemap=1', 'top' );
} );

add_filter( 'query_vars', fn( $vars ) => array_merge( $vars, [ 'mysite_i18n_sitemap' ] ) );

add_action( 'template_redirect', function () {
    if ( ! get_query_var( 'mysite_i18n_sitemap' ) ) {
        return;
    }
    header( 'Content-Type: application/xml; charset=UTF-8' );

    echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
    echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" ';
    echo 'xmlns:xhtml="http://www.w3.org/1999/xhtml">' . "\n";

    foreach ( mysite_translated_paths() as $path ) {
        echo "  <url>\n    <loc>" . esc_url( home_url( $path ) ) . "</loc>\n";
        foreach ( mysite_hreflang_map() as $hreflang => $origin ) {
            printf(
                '    <xhtml:link rel="alternate" hreflang="%s" href="%s"/>' . "\n",
                esc_attr( $hreflang ),
                esc_url( untrailingslashit( $origin ) . $path )
            );
        }
        echo "  </url>\n";
    }
    echo '</urlset>';
    exit;
} );

Zwei Dinge, um richtig zu werden Die xmlns:xhtml Erklärung am <urlset> Erforderlich ist; ohne sie werden die alternativen Einträge ignoriert und die Datei validiert immer noch als Sitemap, was wieder das Stille-Fehler-Muster ist Und Flush-Rewrite-Regeln einmal nach dem Hinzufügen der Regel, bei der Aktivierung und nicht bei jeder Anfrage.

Nachdem Sie dies getan haben, entfernen Sie den Kopfausgang. Beides auslassen ist Mischimplementierungsmethoden, was Ihnen zwei Quellen gibt, die driften.

Das Duplicate-Block-Problem

Dies ist der WordPress-spezifische Fehler und derjenige, der es wert ist, überprüft zu werden, bevor Sie Code schreiben Mehrere Plugins geben Hreflang aus, und mehr als eines kann gleichzeitig aktiv sein Ein SEO-Plugin, das mit Übersetzungsbeziehungen konfiguriert ist, ein mehrsprachiges Plugin und Ihre neue Theme-Funktion werden alle gerne drucken wp_head, und das Ergebnis sind zwei oder drei Blöcke mit unterschiedlichem Inhalt und möglicherweise zwei x-default Einträge.

Google&#39;s Umgang mit widersprüchlichen Anmerkungen innerhalb einer Seite ist, ihnen zu misstrauen Also eine Seite, die keinen Hreflang hatte und jetzt drei Blöcke hat, kann schlechter dran sein als vorher.

Überprüfen Sie die gerenderte Ausgabe und nicht die Plugin-Einstellungen:

curl -s https://example.com/de/preise/ | grep 'rel="alternate"' | grep hreflang

Zählen Sie die Zeilen und zählen Sie die unterschiedlichen Codes. Ein Eintrag pro Gebietsschema plus eins x-defaultUnd nichts wiederholt Wenn ein Plugin ein Set aussendet, das Sie nicht wegkonfigurieren können, besteht die übliche Notluke darin, seine Aktion zu entfernen wp_head Anstatt Ihre oben hinzuzufügen:

// Example shape: find the real hook and callback with `has_action`.
add_action( 'init', function () {
    remove_action( 'wp_head', 'some_plugin_hreflang_output', 10 );
} );

Kanonische Tags, und sie nicht zu brechen

WordPress emittiert rel="canonical" Für einzigartige Ansichten durch rel_canonical(), und es zeigt auf den post&#39; eigenen Permalink, was Sie wollen Die meisten SEO-Plugins ersetzen es durch ihre eigenen, standardmäßig auch selbstreferenziellen.

Die Regel vom hreflang vs. kanonisch Gilt unverändert: jede Seite kanonisiert sich selbst, und hreflang beschreibt die Beziehungen zwischen diesen selbstkanonischen Seiten Die WordPress-geschmackliche Art, sie zu brechen, ist eine Plugin-Einstellung, die übersetzte Beiträge in eine & quot;primary&quot; Sprachversion kanonisiert, die sich wie eine sinnvolle Deduplikationsoption liest und Ihre Übersetzungen stillschweigend aus dem Index entfernt Wenn eine Einstellung eine sprachübergreifende Kanonisierung anbietet, lassen Sie es weg.

Überprüfung des Ergebnisses

Holen Sie zwei Geschwisterseiten ab und vergleichen Sie ihre Annotationsblöcke Sie sollten identisch sein, wobei sich nur das Kanonische unterscheidet. Überprüfen Sie dann, ob jede von Ihnen benannte URL 200 ohne Umleitung zurückgibt, da bei zurückgezogenen Lokalitäten und geänderten Permalinks ein korrekter Satz in einen kaputten verrottet.

Fügen Sie das Set in die ein Freier Hreflang-Generator Um die Codes zu validieren: Es prüft jeden Wert anhand der ISO-Form und markiert nicht erkannte Untertags wie en-UK, fängt Duplikate, lehnt relative URLs ab und warnt, wenn das Set keinen Fallback hat Es läuft vollständig im Browser, was wichtig ist, wenn die URLs eine Client-Site beschreiben, die noch nicht öffentlich ist Dann verwenden Sie Search Console&#39; s International Targeting-Bericht für die laufende Prüfung, und die hreflang Werkzeugführung Für den Workflow drumherum.

Häufig gestellte Fragen

Fügt WordPress automatisch hreflang-Tags hinzu?

Nr. Core gibt einen kanonischen Link und diverse Headlinks aus aber es wechselt nichts über Sprache Hreflang kommt von einem Plugin oder von einem eigenen wp_head Ausgabe.

Wie füge ich ohne Plugin hreflang in WordPress hinzu?

Haken wp_head und drucken Sie a <link rel="alternate" hreflang> Zeile für jedes Gebietsschema in einer Karte, die Sie kontrollieren, einschließlich einer x-default Eintrag, auf jeder Seite der Gruppe Entfliehen Sie den Werten mit esc_attr und esc_urldrohen

Wohin gehen Hreflang-Tags in einem WordPress-Theme?

In einer Funktion, die an gebunden ist wp_headIdealerweise in einem kleinen Mu-Plugin statt im Theme, sodass beim Theme-Wechsel Ihre Anmerkungen nicht stillschweigend fallen gelassen werden.

Was ist, wenn meine übersetzten Seiten andere slugs haben?

Nichts ableiten Speichern Sie die Gegenposten-IDs oder URLs in Post-Meta und lesen Sie die Karte zur Renderzeit. Füllen Sie sie auf beiden Seiten aus, oder Sie erstellen ein fehlendes Return-Tag.

Behandeln Polylang und WPML Hreflang?

Ja, für die Übersetzungen, die sie verwalten Überprüfen Sie, ob die Ausgabe enthalten ist x-default Und dass kein zweites Plugin einen konkurrierenden Block aussendet.

Warum habe ich zwei hreflang-Blöcke auf einer Seite?

Zwei Dinge sind Druck auf wp_head: normalerweise ein SEO-Plugin plus ein mehrsprachiges Plugin, oder ein Plugin plus eigene Funktion Widersprüchliche Anmerkungen auf einer Seite werden misstraut, also entfernen Sie eine statt Layering.

Sollte jede WordPress-Sprachversion ihr eigenes Kanonisch haben?

Ja, selbstreferenziell Verwenden Sie keine Plugin-Einstellung, die übersetzte Beiträge in eine Primärsprache kanonisiert, da diese aus dem Index entfernt und den Hreflang-Satz aufhebt.

Wie füge ich Hreflang über ein WordPress-Multisite-Netzwerk hinzu?

Schleife get_sites()Wechseln Sie zu jedem Blog, um dessen Gebietsschema zu lesen und home_url()und cache die resultierende Karte in einem Transient. Ausführen switch_to_blog Auf jede Anfrage in wp_head Auf einem großen Netz teuer ist.


Comments

0 comments

0/2000 characters

No comments yet. Be the first to share your thoughts!