Docs SEO NEO Version 1.x Multilingual

Multilingual

Hreflang link generation with segment and pagination support, hreflang code map, Open Graph locale map, per-language site name, and multilingual JSON-LD Organization nodes.

How multilingual support works

SEO NEO uses ProcessWire's native language-aware fields throughout. Every SEO field (seoneo_title, seoneo_description, etc.) supports multiple languages automatically when LanguageSupport is installed — no extra configuration needed. Editors switch language on the page-edit screen using PW's standard language tabs, and the SEO tab's Google SERP Preview shows its own language switcher automatically (no config needed) so editors can check each language's output without leaving the SEO tab.

The sections below cover the parts that do require explicit configuration: hreflang codes, Open Graph locales, and multilingual JSON-LD.

Hreflang

SEO NEO emits one <link rel="alternate" hreflang="…"> tag per active language, plus a final hreflang="x-default" pointing at the default-language URL:

<link rel="alternate" hreflang="en-GB" href="https://example.com/walks/nine-standards-rigg/">
<link rel="alternate" hreflang="de"    href="https://example.com/de/wanderungen/nine-standards-rigg/">
<link rel="alternate" hreflang="fi"    href="https://example.com/fi/vaellukset/nine-standards-rigg/">
<link rel="alternate" hreflang="x-default" href="https://example.com/walks/nine-standards-rigg/">

A few things SEO NEO handles that the legacy modules don't:

  • URL segments are preserved. If the current URL is /news/2024/my-article/, the hreflang alternates point to the translated equivalents of that full path, not just the parent page.
  • Pagination is preserved and translated. If $config->pageNumUrlPrefixes maps de to seite, then page 2 in German is /de/news/seite2/, not /de/news/page2/.
  • Multi-domain language setups are honoured. Resolution goes through $page->localHttpUrl($lang), so LanguageSupportPageNames's domain mapping is respected.

Default hreflang code

The Default hreflang code setting (default: en) is used when the language name is default. Set this to a valid BCP47 code such as en-GB or en.

Hreflang code map

By default SEO NEO derives the BCP47 hreflang code from the PW language name. If your language is named de it becomes de; if it's named default it uses the default hreflang code above.

Fix this in Modules > Configure > SeoNeo > Hreflang code map — one entry per line:

default=en-GB
de=de-DE
fi=fi-FI

The x-default entry is always the default-language URL and is added automatically — you don't need to add it to the map.

Open Graph locale

The Locale map for languages in module config controls og:locale and og:locale:alternate. Format is the same — one entry per line:

default=en_GB
de=de_DE
fi=fi_FI

The current language's entry becomes og:locale; all others become og:locale:alternate tags. SEO NEO resolves each language through a three-step fallback chain:

  1. Explicit map entry — wins if present.
  2. Auto-derived from the language name — if no map entry exists, SEO NEO derives a locale from the PW language name. de becomes de_DE, fr becomes fr_FR, pt becomes pt_PT. Hyphenated names like pt-br map to pt_BR. This catches the common case where PW language names happen to be valid two-letter codes.
  3. Default OG locale — final fallback. Used for the default language and any language the auto-derive step can't recognise.

For sites where the auto-derive guesses wrongly (e.g. you named a PW language en but want en_GB rather than the derived en_US), add an explicit map entry to override.

Per-language site name

The Per-language site name setting in module config lets you show a different site name per language in the <title> tag:

de=Mein Beispiel
fi=Minun Esimerkki

Languages without an entry use the main Site name setting. This value also feeds the og:site_name tag for the current language.

Multilingual JSON-LD

The per-page JSON-LD nodes (WebPage, Article, Person, BreadcrumbList) automatically translate with the page — they use $page->title, $page->httpUrl, and the smart-map fallback chain, all of which are language-aware in PW.

The Organization and WebSite nodes get their name and description from two per-language map fields in module config:

// Publisher name (per language)
default=Lakes & Trails
de=Seen & Pfade
fi=Järvet & Polut

// Publisher description (per language)
default=An editorial guide to the Lake District.
de=Ein redaktioneller Führer durch den Lake District.

The @id URIs for Organization and WebSite stay language-invariant — schema.org best practice is to identify the same entity across locales and translate only the descriptive properties.

Multisite considerations

If you're running a Multisite setup where each language is a separate PW installation on its own domain, hreflang resolution still goes through $page->localHttpUrl($lang). As long as LanguageSupportPageNames is configured with the correct domain per language, the generated hreflang URLs will point to the right domains. Test by viewing the page source on each domain and confirming the x-default and self-referencing hreflang tags are correct.

Common gotchas

  • hreflang shows "default" instead of a BCP47 code. Set the hreflang code map in module config. The PW language named default doesn't map to a valid BCP47 code automatically.
  • og:locale is "en_US" on all languages. The locale map hasn't been filled in. Add entries for each active language in Modules > Configure > SeoNeo > Locale map for languages.
  • Translated page title not showing in the SEO preview. The Google SERP Preview reads the current PW language. Use the language switcher on the SEO tab, not the main PW language switcher, to preview other languages.

See also

Last updated