Skip to main content
Docusaurus

Search documentation

Type to search this documentation.

On this pageOverview

i18n - Tutorial

mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

This tutorial will walk you through the basics of the Docusaurus i18n system.

We will add French translations to a newly initialized English Docusaurus website.

Initialize a new site (like this one):

npm2yarn
npx create-docusaurus@latest website classic

Modify docusaurus.config.js to add the i18n support for the French language.

Use the site i18n configuration to declare the i18n locales:

docusaurus.config.js
export default {
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'fr', 'fa'],
    localeConfigs: {
      en: {
        htmlLang: 'en-GB',
      },
      // You can omit a locale (e.g. fr) if you don't need to override the defaults
      fa: {
        direction: 'rtl',
      },
    },
  },
};

The locale names are used for the translation files' locations, as well as your translated locales' base URL. When building all locales, only the default locale will have its name omitted in the base URL.

Docusaurus uses the locale names to provide sensible defaults: the <html lang="..."> attribute, locale label, calendar format, etc. You can customize these defaults with the localeConfigs.

Add a navbar item of type localeDropdown so that users can select the locale they want:

docusaurus.config.js
export default {  themeConfig: {    navbar: {      items: [        {          type: 'localeDropdown',          position: 'left',        },      ],    },  },};

Start your localized site in dev mode, using the locale of your choice:

npm2yarn
npm run start -- --locale fr

Your site is accessible at http://localhost:3000/fr/.

We haven't provided any translation yet, so the site is mostly untranslated.

All translation data for the French locale is stored in website/i18n/fr. Each plugin sources its own translated content under the corresponding folder, while the code.json file defines all text labels used in the React code.

For any React code you've written yourself: React pages, React components, etc., you will use the translation APIs.

Locate all text labels in your React code that will be visible to your users, and mark them with the translation APIs. There are two kinds of APIs:

  • The <Translate> component wraps a string as a JSX element;
  • The translate() callback takes a message and returns a string.

Use the one that better fits the context semantically. For example, the <Translate> can be used as React children, while for props that expect a string, the callback can be used.

mdx-code-block
<Tabs>
<TabItem value="Before">
src/pages/index.js
import React from 'react';import Layout from '@theme/Layout';import Link from '@docusaurus/Link';export default function Home() {  return (    <Layout>      {/* highlight-next-line */}      <h1>Welcome to my website</h1>      <main>        {/* highlight-start */}        You can also visit my        <Link to="https://docusaurus.io/blog">blog</Link>        {/* highlight-end */}        <img          src="/img/home.png"          alt="Home icon"        />      </main>    </Layout>  );}
mdx-code-block
</TabItem>
<TabItem value="After">
src/pages/index.js
import React from 'react';import Layout from '@theme/Layout';import Link from '@docusaurus/Link';import Translate, {translate} from '@docusaurus/Translate';export default function Home() {  return (    <Layout>      <h1>        {/* highlight-next-line */}        <Translate>Welcome to my website</Translate>      </h1>      <main>        {/* highlight-start */}        <Translate          id="homepage.visitMyBlog"          description="The homepage message to ask the user to visit my blog"          values={{            blogLink: (              <Link to="https://docusaurus.io/blog">                <Translate                  id="homepage.visitMyBlog.linkLabel"                  description="The label for the link to my blog">                  blog                </Translate>              </Link>            ),          }}>          {'You can also visit my {blogLink}'}        </Translate>        {/* highlight-end */}        <img          src="/img/home.png"          alt={            translate({              message: 'Home icon',              description: 'The homepage icon alt message',            })          }        />      </main>    </Layout>  );}
mdx-code-block
</TabItem>
</Tabs>

The docusaurus write-translations command will statically analyze all React code files used in your site, extract calls to these APIs, and aggregate them in the code.json file. The translation files will be stored as maps from IDs to translation message objects (including the translated label and the description of the label). In your calls to the translation APIs (<Translate> or translate()), you need to specify either the default untranslated message or the ID, in order for Docusaurus to correctly correlate each translation entry to the API call.

When you run write-translations, you will notice that some labels are pluralized:

i18n/en/code.json
{
  // ...
  "theme.blog.post.plurals": "One post|{count} posts"
  // ...
}

Every language will have a list of possible plural categories. Docusaurus will arrange them in the order of ["zero", "one", "two", "few", "many", "other"]. For example, because English (en) has two plural forms ("one" and "other"), the translation message has two labels separated by a pipe (|). For Polish (pl) which has three plural forms ("one", "few", and "many"), you would provide three labels in that order, joined by pipes.

You can pluralize your own code's messages as well:

JSX
import {translate} from '@docusaurus/Translate';
import {usePluralForm} from '@docusaurus/theme-common';

function ItemsList({items}) {
  // `usePluralForm` will provide the plural selector for the current locale
  const {selectMessage} = usePluralForm();
  // Select the appropriate pluralized label based on `items.length`
  const message = selectMessage(
    items.length,
    translate(
      {message: 'One item|{count} items'},
      {count: items.length},
    ),
  );
  return (
    <>
      <h2>{message}</h2>
      <ul>{items.map((item) => <li key={item.id}>{item.title}</li>)}<ul>
    </>
  );
}

JSON translation files are used for everything that is interspersed in your code:

  • React code, including the translated labels you have marked above
  • Navbar and footer labels in theme config
  • Docs sidebar category labels in sidebars.js
  • Blog sidebar title in plugin options
  • ...

Run the write-translations command:

npm2yarn
npm run write-translations -- --locale fr

It will extract and initialize the JSON translation files that you need to translate. The code.json file at the root includes all translation API calls extracted from the source code, which could either be written by you or provided by the themes, some of which may already be translated by default.

i18n/fr/code.json
{
  // No ID for the <Translate> component: the default message is used as ID
  "Welcome to my website": {
    "message": "Welcome to my website"
  },
  "home.visitMyBlog": {
    "message": "You can also visit my {blog}",
    "description": "The homepage message to ask the user to visit my blog"
  },
  "homepage.visitMyBlog.linkLabel": {
    "message": "Blog",
    "description": "The label for the link to my blog"
  },
  "Home icon": {
    "message": "Home icon",
    "description": "The homepage icon alt message"
  }
}

Plugins and themes will also write their own JSON translation files, such as:

i18n/fr/docusaurus-theme-classic/navbar.json
{
  "title": {
    "message": "My Site",
    "description": "The title in the navbar"
  },
  "item.label.Docs": {
    "message": "Docs",
    "description": "Navbar item with label Docs"
  },
  "item.label.Blog": {
    "message": "Blog",
    "description": "Navbar item with label Blog"
  },
  "item.label.GitHub": {
    "message": "GitHub",
    "description": "Navbar item with label GitHub"
  }
}

Translate the message attribute in the JSON files of i18n/fr, and your site layout and homepage should now be translated.

Official Docusaurus content plugins extensively use Markdown/MDX files and allow you to translate them.

Copy your docs Markdown files from docs/ to i18n/fr/docusaurus-plugin-content-docs/current, and translate them:

Bash
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current
cp -r docs/. i18n/fr/docusaurus-plugin-content-docs/current

Copy your blog Markdown files to i18n/fr/docusaurus-plugin-content-blog, and translate them:

Bash
mkdir -p i18n/fr/docusaurus-plugin-content-blog
cp -r blog/. i18n/fr/docusaurus-plugin-content-blog

Copy your pages Markdown files to i18n/fr/docusaurus-plugin-content-pages, and translate them:

Bash
mkdir -p i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/. i18n/fr/docusaurus-plugin-content-pages

You can choose to deploy your site under a single domain or use multiple (sub)domains.

Run the following command:

npm2yarn
npm run build

Docusaurus will build one single-page application per locale:

  • website/build: for the default, English language
  • website/build/fr: for the French language

You can now deploy the build folder to the static hosting solution of your choice.

You can also build your site for a single locale:

npm2yarn
npm run build -- --locale fr

When building a single locale at a time, Docusaurus will not add the /fr/ URL prefix automatically, assuming you want to deploy each locale to a distinct domain.

On your static hosting provider:

  • create one deployment per locale
  • configure the appropriate build command, using the --locale option
  • configure the (sub)domain of your choice for each deployment

It is possible to have some locales using sub-paths, and others using subdomains.

It is also possible to deploy each locale as a separate subdomain, assemble the subdomains in a single unified domain at the CDN level:

  • Deploy your site as fr.docusaurus.io
  • Configure a CDN to serve it from docusaurus.io/fr

Docusaurus doesn't care about how you manage your translations: all it needs is that all translation files (JSON, Markdown, or other data files) are available in the file system during building. However, as site creators, you would need to consider how translations are managed so your translation contributors could collaborate well.

We will share two common translation collaboration strategies: using git and using Crowdin.

Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu