i18n - Utilisation de Crowdin
The i18n system of Docusaurus is decoupled from any translation software.
You can integrate Docusaurus with the tools and SaaS of your choice, as long as you put the translation files at the correct location.
We document the usage of Crowdin, as one possible integration example.
This is not an endorsement of Crowdin as the unique choice to translate a Docusaurus site, but it is successfully used by Facebook to translate documentation projects such as Jest, Docusaurus, and ReasonML.
Refer to the Crowdin documentation and Crowdin support for help.
Use this community-driven GitHub discussion to discuss anything related to Docusaurus + Crowdin.
Crowdin overview
Crowdin is a translation SaaS, offering a free plan for open-source projects.
Nous recommandons le flux de travail de traduction suivant :
- Upload sources to Crowdin (untranslated files)
- Use Crowdin to translate the content
- Download translations from Crowdin (localized translation files)
Crowdin provides a CLI to upload sources and download translations, allowing you to automate the translation process.
The crowdin.yml
configuration file is convenient for Docusaurus, and permits to download the localized translation files at the expected location (in i18n/[locale]/..
).
Read the official documentation to know more about advanced features and different translation workflows.
Crowdin tutorial
This is a walk-through of using Crowdin to translate a newly initialized English Docusaurus website into French, and assume you already followed the i18n tutorial.
The end result can be seen at docusaurus-crowdin-example.netlify.app (repository).
Prepare the Docusaurus site
Initialisez un nouveau site Docusaurus :
npx create-docusaurus@latest website classic
Ajoutez la configuration du site pour la langue française :
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
themeConfig: {
navbar: {
items: [
// ...
{
type: 'localeDropdown',
position: 'left',
},
// ...
],
},
},
// ...
};
Traduisez la page d'accueil :
import React from 'react';
import Translate from '@docusaurus/Translate';
import Layout from '@theme/Layout';
export default function Home() {
return (
<Layout>
<h1 style={{margin: 20}}>
<Translate description="The homepage main heading">
Welcome to my Docusaurus translated site!
</Translate>
</h1>
</Layout>
);
}
Create a Crowdin project
Sign up on Crowdin, and create a project.
Utiliser l'anglais comme langue source et le français comme langue cible.
Votre projet est créé, mais il est vide pour le moment. Nous allons déverser les fichiers à traduire dans les prochaines étapes.
Create the Crowdin configuration
This configuration (doc) provides a mapping for the Crowdin CLI to understand:
- Où trouver les fichiers source à déverser (JSON et Markdown)
- Where to download the files after translation (in
i18n/[locale]
)
Create crowdin.yml
in website
:
project_id: '123456'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
files:
# JSON translation files
- source: /i18n/en/**/*
translation: /i18n/%two_letters_code%/**/%original_file_name%
# Docs Markdown files
- source: /docs/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
# Blog Markdown files
- source: /blog/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%
Crowdin a sa propre syntaxe pour déclarer les chemins source/traduction :
**/*
: everything in a subfolder%two_letters_code%
: the 2-letters variant of Crowdin target languages (fr
in our case)**/%original_file_name%
: the translations will preserve the original folder/file hierarchy
Les avertissements de Crowdin CLI ne sont pas toujours faciles à comprendre.
Nous vous conseillons :
- modifier une chose à la fois
- re-déverser les sources après tout changement de configuration
- use paths starting with
/
(./
does not work) - avoid fancy globbing patterns like
/docs/**/*.(md|mdx)
(does not work)
Access token
The api_token_env
attribute defines the env variable name read by the Crowdin CLI.
You can obtain a Personal Access Token
on your personal profile page.
You can keep the default value CROWDIN_PERSONAL_TOKEN
, and set this environment variable and on your computer and on the CI server to the generated access token.
A Personal Access Tokens grant read-write access to all your Crowdin projects.
You should not commit it, and it may be a good idea to create a dedicated Crowdin profile for your company instead of using a personal account.
Other configuration fields
project_id
: can be hardcoded, and is found onhttps://crowdin.com/project/<MY_PROJECT_NAME>/settings#api
preserve_hierarchy
: preserve the folder's hierarchy of your docs on Crowdin UI instead of flattening everything
Install the Crowdin CLI
This tutorial uses the CLI version 3.5.2
, but we expect 3.x
releases to keep working.
Installez Crowdin CLI en tant que paquet npm sur votre site Docusaurus :
- npm
- Yarn
- pnpm
npm install @crowdin/cli@3
yarn add @crowdin/cli@3
pnpm add @crowdin/cli@3
Add a crowdin
script:
{
"scripts": {
// ...
"write-translations": "docusaurus write-translations",
"crowdin": "crowdin"
}
}
Vérifiez que vous pouvez exécuter le CLI de Crowdin :
- npm
- Yarn
- pnpm
npm run crowdin -- --version
yarn crowdin --version
pnpm run crowdin -- --version
Set the CROWDIN_PERSONAL_TOKEN
env variable on your computer, to allow the CLI to authenticate with the Crowdin API.
Temporarily, you can hardcode your personal token in crowdin.yml
with api_token: 'MY-TOKEN'
.
Upload the sources
Generate the JSON translation files for the default language in website/i18n/en
:
- npm
- Yarn
- pnpm
npm run write-translations
yarn write-translations
pnpm run write-translations
Déverser tous les fichiers de traduction JSON et Markdown :
- npm
- Yarn
- pnpm
npm run crowdin upload
yarn crowdin upload
pnpm run crowdin upload
Your source files are now visible on the Crowdin interface: https://crowdin.com/project/<MY_PROJECT_NAME>/settings#files
Translate the sources
On https://crowdin.com/project/<MY_PROJECT_NAME>
, click on the French target language.
Traduisez des fichiers Markdown.
Use Hide String
to make sure translators don't translate things that should not be:
- Front matter:
id
,slug
,tags
... - Admonitions:
:::
,:::note
,:::tip
...
Traduisez des fichiers JSON.
The description
attribute of JSON translation files is visible on Crowdin to help translate the strings.
Pre-translate your site, and fix pre-translation mistakes manually (enable the Global Translation Memory in settings first).
Use the Hide String
feature first, as Crowdin is pre-translating things too optimistically.
Download the translations
Utilisez Crowdin CLI pour télécharger les fichiers JSON et Markdown traduits.
- npm
- Yarn
- pnpm
npm run crowdin download
yarn crowdin download
pnpm run crowdin download
The translated content should be downloaded in i18n/fr
.
Démarrez votre site dans la langue française :
- npm
- Yarn
- pnpm
npm run start -- --locale fr
yarn run start --locale fr
pnpm run start -- --locale fr
Make sure that your website is now translated in French at http://localhost:3000/fr/
.
Automate with CI
We will configure the CI to download the Crowdin translations at build time and keep them outside of Git.
Add website/i18n
to .gitignore
.
Set the CROWDIN_PERSONAL_TOKEN
env variable on your CI.
Create an npm script to sync
Crowdin (extract sources, upload sources, download translations):
{
"scripts": {
"crowdin:sync": "docusaurus write-translations && crowdin upload && crowdin download"
}
}
Call the npm run crowdin:sync
script in your CI, just before building the Docusaurus site.
Keep your deploy-previews fast: don't download translations, and use npm run build -- --locale en
for feature branches.
Crowdin ne supporte pas bien plusieurs déversements/téléchargements simultanés : il est préférable d'ajouter des traductions uniquement à votre déploiement de production, et de continuer à déployer des aperçus non traduits.
Advanced Crowdin topics
MDX
Faites particulièrement attention aux fragments JSX dans les documents MDX !
Crowdin does not support officially MDX, but they added support for the .mdx
extension, and interpret such files as Markdown (instead of plain text).
MDX problems
Crowdin pense que la syntaxe JSX est du HTML intégré, et qu'elle peut se mélanger avec le balisage JSX lorsque vous téléchargez les traductions, ce qui conduit à un site qui ne se construit pas en raison d'un JSX invalide.
Simple JSX fragments using simple string props like <Username name="Sebastien"/>
will work fine; more complex JSX fragments using object/array props like <User person={{name: "Sebastien"}}/>
are more likely to fail due to a syntax that does not look like HTML.
MDX solutions
Nous recommandons d'extraire le code JSX complexe intégré en tant que composants autonomes distincts. We also added an mdx-code-block
escape hatch syntax:
# How to deploy Docusaurus
To deploy Docusaurus, run the following command:
````mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs>
<TabItem value="bash" label="Bash">
```bash
GIT_USER=<GITHUB_USERNAME> yarn deploy
```
</TabItem>
<TabItem value="windows" label="Windows">
```batch
cmd /C "set "GIT_USER=<GITHUB_USERNAME>" && yarn deploy"
```
</TabItem>
</Tabs>
````
Ceci va :
- être interprété par Crowdin comme des blocs de code (et ne pas se mélanger avec le balisage au téléchargement)
- être interprété par Docusaurus comme du JSX normal (comme s'il n'était enveloppé par aucun bloc de code)
- malheureusement, il n'est pas possible d'utiliser les outils MDX (coloration syntaxique des IDE, Prettier...)
Docs versioning
Configure translation files for the website/versioned_docs
folder.
When creating a new version, the source strings will generally be quite similar to the current version (website/docs
), and you don't want to translate the new version docs again and again.
Crowdin provides a Duplicate Strings
setting.
We recommend using Hide
, but the ideal setting depends on how much your versions are different.
Not using Hide
leads to a much larger amount of source strings
in quotas, and will affect the pricing.
Multi-instance plugins
Vous devez configurer les fichiers de traduction pour chaque instance de plugin.
If you have a docs plugin instance with id=ios
, you will need to configure those source files as well
website/ios
website/ios_versioned_docs
(if versioned)
Maintaining your site
Sometimes, you will remove or rename a source file on Git, and Crowdin will display CLI warnings:
When your sources are refactored, you should use the Crowdin UI to update your Crowdin files manually:
VCS (Git) integrations
Crowdin has multiple VCS integrations for GitHub, GitLab, Bitbucket.
Nous recommandons de les éviter.
It could have been helpful to be able to edit the translations in both Git and Crowdin, and have a bi-directional sync between the 2 systems.
In practice, it didn't work very reliably for a few reasons:
- The Crowdin -> Git sync works fine (with a pull request)
- The Git -> Crowdin sync is manual (you have to press a button)
- L'heuristique utilisée par Crowdin pour faire correspondre les traductions Markdown existantes aux sources Markdown existantes n'est pas fiable à 100%, et vous devez vérifier le résultat sur l'interface Crowdin après toute synchronisation à partir de Git
- 2 utilisateurs éditant simultanément sur Git et Crowdin peuvent entraîner une perte de traduction
- It requires the
crowdin.yml
file to be at the root of the repository
In-Context localization
Crowdin has an In-Context localization feature.
Malheureusement, cela ne fonctionne pas encore pour des raisons techniques, mais nous avons bon espoir que cela puisse être résolu.
Crowdin replaces Markdown strings with technical IDs such as crowdin:id12345
, but it does so too aggressively, including hidden strings, and messes up with front matter, admonitions, JSX...
Localize edit URLs
When the user is browsing a page at /fr/doc1
, the edit button will link by default to the unlocalized doc at website/docs/doc1.md
.
You may prefer the edit button to link to the Crowdin interface instead by using the editUrl
function to customize the edit URLs on a per-locale basis.
const DefaultLocale = 'en';
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
editUrl: ({locale, versionDocsDirPath, docPath}) => {
// Link to Crowdin for French docs
if (locale !== DefaultLocale) {
return `https://crowdin.com/project/docusaurus-v2/${locale}`;
}
// Link to GitHub for English docs
return `https://github.com/facebook/docusaurus/edit/main/website/${versionDocsDirPath}/${docPath}`;
},
},
blog: {
editUrl: ({locale, blogDirPath, blogPath}) => {
if (locale !== DefaultLocale) {
return `https://crowdin.com/project/docusaurus-v2/${locale}`;
}
return `https://github.com/facebook/docusaurus/edit/main/website/${blogDirPath}/${blogPath}`;
},
},
},
],
],
};
It is currently not possible to link to a specific file in Crowdin.
Example configuration
The Docusaurus v2 configuration file is a good example of using versioning and multi-instance:
project_id: '428890'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
languages_mapping: &languages_mapping
two_letters_code:
pt-BR: pt-BR
files:
- source: /website/i18n/en/**/*
translation: /website/i18n/%two_letters_code%/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/docs/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/community/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs-community/current/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/versioned_docs/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/blog/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%
languages_mapping: *languages_mapping
- source: /website/src/pages/**/*
translation: /website/i18n/%two_letters_code%/docusaurus-plugin-content-pages/**/%original_file_name%
ignore: [/**/*.js, /**/*.jsx, /**/*.ts, /**/*.tsx, /**/*.css]
languages_mapping: *languages_mapping