What would it take to make Ghost multilingual?

Want your site in two+ languages? Here's what it'd take.

What would it take to make Ghost multilingual?
🧠
This is one of those writing-to-think posts, so please bear with me if I meander around a bit. This is a mix of what's possible now, and what would be needed to actually have good multi-language support.
đź’ˇ
This post includes some affiliate links, which help keep the lights on around here.

My process is that I write the post, including links that enhance my writing, and then I look at whether there are any links that could be converted to affiliate links. No link spam, just a little bonus monetization.

What's possible now: Single language support

If you haven't already read it, there's a Ghost FAQ on i18n.

Right now, Ghost has pretty good (although not quite perfect - bug reports appreciated) support for single language i18n. If you want to run your whole blog in a language other than English, you can do that. It's possible that you'll need to contribute some translations, if your language's set of translations aren't complete. This doesn't require technical skill, just language fluency.

Out of the box (with a theme that supports it), you can have your whole Ghost site in one target language of your choice, including having newsletter emails and magic link emails in your single target language.

Big caveat: Official themes are not set up for translations, so if you're on the Ghost Pro starter plan, you won't have access to translations of your theme. 👉Read more about Ghost hosting options here. Tip: If you're thinking about hosting with Magic Pages, the Kyiv theme includes German, French, Spanish, and Ukrainian translations and is available on their Starter plan.

Where things get hairy is for sites that want to support multiple languages. For example, perhaps you write articles in both German and French (de and fr), and you want to localize parts of your site for each of those two languages. (OK, so that's bilingual, not multilingual, but the problems are the same for 3+ as for 2, basically.)

Barriers to multilingual Ghost

  • Transactional email goes out in only the site language
  • Newsletters have a little bit of text (“view in browser”, etc) in one language
  • Navigation only supports one set of links - although you could do a workaround for this within the theme
  • Translated theme text is in one language.

Shhsh. There is some stealth multilingual support.

Obviously missing from the list above are the Ghost "apps", which are JavaScript that loads on each page and provides the Portal, Comments, and Search. If you're worried about getting those into a different language, fear not! That's actually not all that hard, thanks in part to changes I made to {{ghost_head}} Normally, the {{ghost_head}} helper would write out the JavaScript loading these apps, and pass in the sitewide language setting. [I also fixed Portal so that it does this. You're welcome.]

So, that opens up some options. If you edit where your theme is including {{ghost_head}} (probably default.hbs), you can tell it not to write out the script loading. And then you can add separate logic to decide what to pass each script when it loads. So...

{{#has tag="#de"}} 
    {{ghost_head exclude="portal,search"}} 
     <script defer 
        src="https://cdn.jsdelivr.net/ghost/portal@2/umd/portal.min.js" 
        data-i18n="true" 
        data-ghost="{{@site.url}}" 
        data-key="{{content_api_key}}" data-api="{{content_api_url}}" 
        data-locale="de" crossorigin="anonymous">
     </script>
     <script defer 
        src="https://cdn.jsdelivr.net/ghost/sodo-search/umd/sodo-search.min.js" 
        data-key="{{content_api_key}}" data-styles="https://cdn.jsdelivr.net/ghost/sodo-search/umd/main.css" 
        data-sodo-search="{{@site.url}}" data-locale="de" crossorigin="anonymous">
     </script>
{{else}} 
     {{ghost_head}} 
{{/has}}

Comments not shown, due to increased complexity, but will be similar.

You could implement an approach right now that detects a language tag and loads the javascript apps in the right language. (It's a little complex, since you'll also need to set up the index pages, probably with custom templates, but it's not totally horrible.)

Ghost has primary and secondary navigation. If you have just two languages, you could conceivably alter the navigation.hbs partial to switch between them based on a tag on the page. For more than two languages, things get complicated. One option would be to hard-code the options and use some tag-based logic to choose which set of links to show where. (How often do sites really change navigation links? For many sites, the answer is almost never, so needing to edit the theme might not be a problem.) Another option might be to detect the language based on how the link's name is set up, and selectively render. This is the sort of thing that would be a lot easier with just a little more functionality for string parsing in handlebars, that Ghost doesn't currently implement.

Or perhaps you use a theme with dropdown menus, and you put all your French links under a Français header, and all your German links under a Deutsch header. That'd allow still using the navigation functionality in the dashboard, and it allows your multilingual visitors easy access to the whole site, rather than just showing them links for the language of the page they're on.

Theme options

One way to deal with wanting a theme to be multilingual would be to duplicate much of the theme content and wrap it in some logic. So for example, you could check for a #de or #fr tag, and use that to decide what text to render. Many of the built-in helpers will also take arguments, that would allow you to switch what they output depending on the page language. So rather than {{reading_time}}, you'd have:

{{#has tag="#fr"}}
  {{reading_time minute="Une minute pour lire" minutes="% minutes pour lire"}}
{{/has}}

Please forgive the possibly bad French. It's ironic that the person working on i18n in Ghost is pretty much monolingual, huh?

Email woes

Newsletter and magic link emails are probably the biggest of the problems, and since the theme doesn't control either one, there's no way to work around that limitation with theme cleverness or JavaScript.

The big thing that’s missing here is the ability to pass a language preference to outgoing newsletters and to outgoing transactional email. [And they’re two separate things.] Presumably, if a user signs up on a German page, with German language portal, they should get a German email with the magic link. But if they sign up on the French page, they should get a French email. That’s unsupported currently. You can get the right language into the portal, but the magic link emails are going to be in the sitewide language, whatever that is.

Two installs: the current fix for email woes

One of the recommended fixes for multi-language Ghost is to run two Ghost installs. So, for our bilingual example, you'd have one Ghost setup for fr, and another for de.

Cuong over at ghostFam has a theme with some extra logic to support this: 👉Tanaflows. (I haven't used it, but the demo looks interesting.)

Two installs solves the problem of emails going out in the 'wrong' language. But this could get expensive fast if you're in managed hosting, especially for sites with more than two languages. Regardless of hosting setup, two separate Ghost sites is either a synchronization challenge (if you want members to have access to all sites), or a bit siloed. Are you actually running one site, or two? It probably feels mostly like two, and you're definitely going to have two separate sets of site settings, admin dashboards, etc.

Still, this approach does fix the email problem, since each Ghost install sends its own emails. It also allows each site to collect payments in a different currency or with different pricing, which isn't exactly a multilingual issue, but I'm guessing many multi-language sites also want.

No email needed?

Maybe you're writing for Europe, and your target audience has enough fluency in whatever the 'default' language is that it doesn't matter if the magic link email is in the wrong language. Maybe you're going to use social sign-on for Ghost anyway, so you aren't worried about magic link emails. Maybe you're using some other newsletter provider other than Ghost's built-in Mailgun integration.

Getting content translated

A few resources:

  • Ghost has a WeGlot integration. It won't translate the apps (portal, search, comments), but it will translate page content, theme elements, and navigation. This is a pretty painless way to make all your writing multi-lingual, if that's something you want.
  • Magnus has a super-interesting post about using Deepl for automated translation. Highly recommended reading if that's something you want.

Other bits

  • I haven't seen this done too many places, but Kevin Xu's Interconnected Ghost blog handles being a bilingual site by having both languages in the same post. He just has a little bit of HTML (in cards, as a snippet) wrapped around them, and then some JavaScript handles the button clicks and changing which text is visible. I like this approach because both languages' content is at the same URL. An improvement might be to put the second language's content into the excerpt field, which would allow displaying both languages on the homepage.

So when is Ghost going multi-lingual, for real?

When I get time and funding to work on i18n some more, my next target is an overhaul on theme i18n that is blocking the inclusion of translations in official themes. I think that’s higher priority than multi-language support, and probably easier, too. I’ll look to see if there’s a way to pass in a language preference on a page-by-page basis while I'm working on it. Maybe it’ll be straightforward? (I’m not super optimistic.)

Beyond the theme, the big thing that’s missing here is the ability to pass a language preference to outgoing newsletters and to outgoing transactional email. [And they’re two separate things, of course.]

The magic link problem is likely easier than newsletters – it'd be a question of just passing in the language from Portal. Presumably, if a user signs up on a German page, with German language portal, they should get a German email with the magic link. But if they sign up on the French page, they should get a French email. That’s unsupported currently. You can get the right language in the portal, but the magic link emails are going to be in the sitewide language, whatever that is. No data model changes, just an added (optional) parameter sent to the endpoint.

The newsletter problem is going to take more work. Presumably, the language would be set on a per-newsletter basis, so the Ghost admin panel would need a language setting available for the newsletter, and that would need to be stored (likely alongside the other customizations). And then when the newsletter is sent, that language setting would need to be checked and used for generating the newsletter strings. (So you could have a French language newsletter that had the newsletter boilerplate and all dates in French, and a German newsletter with German, and so on.)

If anyone is interested in funding some work in this area, please let me know! I love working on open source, but it mostly doesn't pay the bills.