Plainwork

Plainwork / Polyglot for Ghost / Ghost language switcher that links to the translated post

A Ghost language switcher that links to the translated post

Most Ghost language switchers send a reader to the other language's home page, which loses them. This page shows how to make the switcher link to the same article in the other language, and fall back to that language's home page only when no translation exists. The approach is the one used in the Polyglot for Ghost kit, which was tested on Ghost 6.70 with the Source theme; Ghost 5 and Ghost(Pro) were not tested.

The problem

Ghost has no notion of "this post is the translation of that post". If you serve English at / and Spanish at /es/ on one install (see Multilingual Ghost on one install), a hard-coded menu link such as /es/ works on the home page and fails everywhere else: someone reading /small-ferries/ clicks "Español" and lands on a Spanish home page instead of the Spanish article.

Pairing posts with an internal tag

Give both versions of an article the same internal tag whose name starts with #pair-, for example #pair-ferries. Internal tags start with #, are never shown to readers, and get a slug beginning hash-, so #pair-ferries has the slug hash-pair-ferries. Mark each language with its own internal tag too: #es on every Spanish post and page. English posts carry no language tag.

To read internal tags in a template you must ask for them: {{#foreach tags visibility="all"}}. Without visibility="all" the loop skips them.

The switcher entry for Spanish, on an English post

Inside the post context, loop over the post's tags, pick the pairing tag, and look up the post that shares it and carries #es:

{{#with post}}{{#foreach tags visibility="all"}}{{#match slug "~^" "hash-pair-"}}
{{#get "posts" filter="tag:{{slug}}+tag:hash-es" limit="1"}}{{#foreach posts}}
<a href="{{url}}" lang="es" hreflang="es">Español</a>
{{/foreach}}{{/get}}
{{/match}}{{/foreach}}{{/with}}

Static pages need the same block with {{#get "pages" ...}}. For the reverse direction, from Spanish back to English, filter on tag:{{slug}}+tag:-[hash-es,hash-fr], which means "has the pairing tag, but none of the other language tags".

The fallback

If no translation is found, nothing is printed by the block above, so put a plain link to the other language's home page right after it. In the kit the fallback link has a tooltip and visually hidden text, in the reader's language, saying that the page is not translated and the link goes to the home page. A reader who is told this does not think the site is broken.

Things that went wrong while testing

  • Template context. The pairing loop only works where post is available. On the home page, tag pages and author pages there is no post, so the block prints nothing and the fallback is the only link. That is correct, but test it.
  • The filter needs the slug, not the name. tag:hash-pair-ferries works; tag:#pair-ferries does not.
  • One partial for the switcher is not enough for three languages. The entry for each language excludes the current one, so you need one variant per language, picked by the same URL-prefix and tag logic described in the translate helper guide.
  • Each switcher link is also an hreflang link. Keep hreflang and lang on the anchors, and print matching <link rel="alternate"> tags in the head. Paste both language versions into the hreflang checker to confirm they point at each other.
  • Query cost. Every switcher entry runs one or two {{#get}} lookups per page render. That is fine for a blog, and is worth knowing before you add ten languages.

Check it works

  1. Create an English post and its Spanish twin, both tagged #pair-test, the Spanish one also #es.
  2. Open the English post: the Spanish link should be the twin's URL.
  3. Open the Spanish twin: the English link should be the original.
  4. Open a post with no twin: the other-language link should go to that language's home page.

The shortcut

If you would rather not write and maintain these partials by hand, the Polyglot for Ghost kit generates the switcher for any set of languages from one config file, together with the hreflang partial, per-language menus and string files. You can click through a bilingual site built with it in the live demo. The routes for your languages come from the free routes.yaml generator.

More from the catalogue

Other small tools

See everything →