Plainwork

Plainwork / Polyglot for Ghost / Ghost translate helper with multiple languages

Ghost's translate helper on a multilingual site

Ghost themes translate their built-in words (Subscribe, Sign in, Read more) with the {{t}} helper, which reads one file from locales/ chosen by the site's single publication language. If your site serves English at / and Spanish at /es/, every visitor gets the same language for those words. This page explains why, and shows a small partial that picks the string file per page instead. The partial was tested on Ghost 6.70 with the Source theme.

Why {{t}} cannot do it alone

Ghost's own translation FAQ says the helper is built for a single-language site, and recommends one Ghost install per language when the content differs. Setting the publication language in Ghost Admin changes the word list for the whole site at once. A page cannot ask for a different one, so a bilingual theme has to decide the language itself.

Deciding the language of a page

On one install, the usual setup (see the guide Multilingual Ghost on one install) is a path prefix per language in routes.yaml plus an internal tag such as #es on each translated post. A template can then work out the language in this order:

  1. The URL starts with /es/: the page is Spanish. This covers the Spanish home page, tag pages and listings.
  2. The current page carries the #es tag: Spanish. This covers posts and pages.
  3. Inside a post loop, the post carries the tag: Spanish.
  4. Otherwise it is the default language, and the normal {{t}} applies.

The partial

Put this in partials/polyglot/t.hbs in your theme. It is the output the Polyglot kit generates for English plus Spanish; add one branch per extra language.

{{#match (url) "~^" "/es/"}}{{> polyglot/t-es k=k}}{{else}}{{#has tag="#es"}}{{> polyglot/t-es k=k}}{{else}}{{#with post}}{{#has tag="#es"}}{{> polyglot/t-es ..}}{{else}}{{t ../k}}{{/has}}{{else}}{{t k}}{{/with}}{{/has}}{{/match}}

Then partials/polyglot/t-es.hbs holds one match per string and falls back to {{t k}} when a string is missing, so an untranslated word shows in the default language instead of breaking the page:

{{#match k "Subscribe"}}Suscribirse{{else match k "Sign in"}}Iniciar sesión{{else}}{{t k}}{{/match}}

In the theme, replace {{t "Subscribe"}} with {{> "polyglot/t" k="Subscribe"}}. Two things we hit while testing:

  • Do not use @root in these partials. The Ghost theme validator (gscan) rejects it. Pass the key in as k, as above.
  • Wrapping a string in {{#with post}} hides the key inside the block, which is why the loop branch passes .. and reads ../k.

What this does not translate

Newsletter emails, member portal screens and Ghost's built-in search are still sent in the publication language. Content you write yourself (titles, body, navigation labels) is not touched by any of this. Strings with placeholders, such as "By {authors}", need their own handling and are best left alone at first. Spanish wording in any list of strings should be checked by a native speaker before you publish it.

The shortcut

Writing and testing the partials for every string by hand takes an afternoon. The Polyglot for Ghost kit generates them from a config file, ships string files for several languages, and includes a patched Source theme that passes gscan. If you only need the routes, use the free routes.yaml generator, and check the result with the hreflang checker.

More from the catalogue

Other small tools

See everything →