Developer docs

A complete guide to translation & multi-language content in Ghost

Ghost handlebars theming has a range of tooling designed to help you create a site for a specific language, create sites for multiple languages, and build themes with multiple language options. The following guide will show you these tools and where to find more information on catering your site to specific languages.

Configure your site for a specific language

To begin configuring your site for a specific language, head to the General view within the Ghost admin. Change the ‘Publication Language’ value to the language code you intend to write content in, check out this tool which can help you find the right language code. Once saved this will change the HTML lang attribute value on your site.

Publication Language setting in Ghost admin

Some themes built for Ghost will cater for alternate languages and use the language code to update parts of the theme automatically, for example “Newer Posts” in the post pagination will change to “Artículos Siguientes” if the code is set to es. Browse our Marketplace to discover the best premium and free Ghost themes.

If your theme doesn’t have translations built in then you can always customise it yourself. Use our comprehensive Handlebars documentation, to modify the theme to your language of choice.

Note: Locales and the publication language are for setting a single primary language for the site. They are not designed to provide multiple languages at the same time.

Configure your site for multiple languages

What if you want to supply your content in multiple languages? With the use of content collections and custom templates your site can have multiple translations of posts with different routes to display them.

Configure collections

First of all, configure your routes.yaml file to include collections for each desired language. Download your routes.yaml file from the Labs view within the Ghost admin and open it with any code editor.

The following example shows how to configure a collection for German with English as a default. Because English is the default language of the site German has been omitted from the default collection.

    routes:

    collections:
      /:
        permalink: /{slug}/
        template: index
        filter: 'tag:-hash-de'
      /de/:
        permalink: /de/{slug}/
        template: index-de
        filter: 'tag:hash-de'

    taxonomies:
      tag: /tag/{slug}/
      author: /author/{slug}/

Working with three languages and more can be done in a similar fashion. This example shows an additional French collection, which only includes posts tagged with ‘fr’. Again, the alternate language has been omitted from the default English collection.

collections:
  /:
    permalink: /{slug}/
    template: index
    filter: 'tag:-[hash-de,hash-fr]'
 /de/:
    permalink: /de/{slug}/
    template: index-de
    filter: 'tag:hash-de'
  /fr/:
    permalink: /fr/{slug}/
    template: index-fr
    filter: 'tag:hash-fr'

You’ll notice that both examples above use tags as a way to determine if a post should or should not be shown in a particular collection. Also private tags are being used (prefixed with the string hash-), which is a method of tagging content in Ghost without the tag being shown in the front-end.

Post settings with “#de” as a tag

Once you’ve configured your desired collections in your routes.yaml upload the file via the Labs view in the Ghost admin.

You’ll now be able to write translated content and tag it for a specific language using private tags. Private tags can be applied to content by prefixing a tag with a # character. These will then be reflected in the front-end, with all German content appearing on mywebsite.com/de/, French content on mywebsite.com/fr/, and all English content mywebsite.com.

Create templates for translated collections

Now that translated content can be displayed on your site in collections the theme needs to provide the right HTML attributes and labelling for each language used on the site. The most key attribute being the lang value applied to the top level <html> tag.

<!-- The default language code of the site, set in Ghost admin -->
<html lang="{{@site.locale}}">

<!-- The language code for German -->
<html lang="de">

One way to customise this value for each of your languages is to create duplicate templates for all your pages, posts and collections. This method would work, but would become difficult to manage within the theme.

The alternative route would be to use the block and contentFor helpers in Handlebars. The block helper acts like a placeholder within a template file. Whenever that template file is inherited by another template, using {{!< template-name}}, that placeholder can be populated using the contentFor helper.

Let’s apply these helpers to our lang attribute use case. In the default.hbs template replace the lang attribute value with a block helper called “lang”:

<figure class="kg-card kg-code-card">

    <html lang="{{{block "lang"}}}">

<figcaption>default.hbs</figcaption>

</figure>

The block helper value can then be updated within the index.hbs and index-de.hbs template files, which will list the main site language posts and German posts respectively:

<figure class="kg-card kg-code-card">

    {{!< default}}

    {{#contentFor "lang"}}{{@site.locale}}{{/contentFor}}

<figcaption>index.hbs</figcaption>

</figure>

<figure class="kg-card kg-code-card">

    {{!< default}}

    {{#contentFor "lang"}}de{{/contentFor}}

<figcaption>index-de.hbs</figcaption>

</figure>

The same can be done for the post and page templates. You can utilise the has helper to check which private tags have been set on the post or page:

<figure class="kg-card kg-code-card">

    {{!< default}}

    {{#post}}
      {{#has tag="#de"}}
        {{#contentFor "lang"}}de{{/contentFor}}
      {{else}}
        {{#contentFor "lang"}}{{@site.locale}}{{/contentFor}}
      {{/has}}
    {{/post}}

<figcaption>post.hbs or page.hbs</figcaption>

</figure>

For more information and assistance with modifying Ghost themes and using Handlebars templating please refer to our Handlebars documentation.

Prepare translated content for SEO

As well as the lang attribute, additional meta data elements are needed for when search bots index your site. Adding rel="alternate" link elements to the head of your posts will let bots know of the alternate translated version of your posts.

These alternate link elements can be added to the custom collection templates in your theme. Link elements can also be added to posts using the Code injection field within post settings.

Adding alternate language meta data to the ghost head code injection field within the post settings

Now when a search engine bot crawls your site it will know which translation to show when someone who reads in a particular language searches for your site.

Creating multi-language themes

For theme developers, Ghost provides the ability to collate translations for labels into locale files. Each locale file contains all the translations for a single language. These translations are used whenever the theme user set a ‘Publication Language’ in Ghost admin.

For more information on how to create theme translations check out our Handlebars documentation on the translate helper and using locales.

Summary

Providing multiple translations for a site can be a large undertaking, which is why it’s sometimes better to offload the work onto other services. WeGlot is a service which automatically adds translations to your site and a way to switch between those translations. Check out our integration guide for WeGlot.

Collections are a great method of grouping content by language. That coupled with the Ghost theme layer makes for a very extensible platform, allowing you to add more translations as your site grows. If you’d like to learn more about Collections check out our docs.