i18n - 따라해보기
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 with npx create-docusaurus@latest website classic
(like this one).
Configure your site
Modify docusaurus.config.js
to add the i18n support for the French language.
Site configuration
Use the site i18n configuration to declare the i18n locales:
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',
},
},
},
};
로케일 이름은 번역된 파일 위치와 번역된 로케일 base URL에 사용됩니다. 모든 로케일 빌드 시 기본 로케일만 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
.
Theme configuration
Add a navbar item of type localeDropdown
so that users can select the locale they want:
export default {
themeConfig: {
navbar: {
items: [
{
type: 'localeDropdown',
position: 'left',
},
],
},
},
};
You can pass a query parameter that will be appended to the URL when a user changes the locale using the dropdown (e.g. queryString: '?persistLocale=true'
).
This is useful for implementing an automatic locale detection on your server. For example, you can use this parameter to store the user's preferred locale in a cookie.
Start your site
Start your localized site in dev mode, using the locale of your choice:
- npm
- Yarn
- pnpm
npm run start -- --locale fr
yarn run start --locale fr
pnpm 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.
Docusaurus provides default translations for generic theme labels, such as "Next" and "Previous" for the pagination.
Please help us complete those default translations.
Each locale is a distinct standalone single-page application: it is not possible to start the Docusaurus sites in all locales at the same time.
Translate your site
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.
After copying files around, restart your site with npm run start -- --locale fr
. Hot-reload will work better when editing existing files.
Translate your 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.
파일을 복사하고 npm run start -- --locale fr
명령으로 사이트를 다시 시작합니다. For example, the <Translate>
can be used as React children, while for props that expect a string, the callback can be used.
A JSX element is an object, not a string. Using it in contexts expecting strings (such as the children of <option>
) would coerce it to a string, which returns "[object Object]"
. While we encourage you to use <Translate>
as JSX children, only use the element form when it actually works.
- Before
- After
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
export default function Home() {
return (
<Layout>
<h1>Welcome to my website</h1>
<main>
You can also visit my
<Link to="https://docusaurus.io/blog">blog</Link>
<img
src="/img/home.png"
alt="Home icon"
/>
</main>
</Layout>
);
}
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>
<Translate>Welcome to my website</Translate>
</h1>
<main>
<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>
<img
src="/img/home.png"
alt={
translate({
message: 'Home icon',
description: 'The homepage icon alt message',
})
}
/>
</main>
</Layout>
);
}
Docusaurus provides a very small and lightweight translation runtime on purpose, and only supports basic placeholders interpolation, using a subset of the ICU Message Format.
Most documentation websites are generally static and don't need advanced i18n features (plurals, genders, etc.). Use a library like react-intl for more advanced use-cases.
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.
The docusaurus write-translations
command only does static analysis of your code. 고급 기능을 사용하고 싶다면 react-intl 같은 라이브러리를 사용하세요. Therefore, dynamic messages can't be extracted, as the message is an expression, not a string:
const items = [
{id: 1, title: 'Hello'},
{id: 2, title: 'World'},
];
function ItemsList() {
return (
<ul>
{/* DON'T DO THIS: doesn't work with the write-translations command */}
{items.map((item) => (
<li key={item.id}>
<Translate>{item.title}</Translate>
</li>
))}
<ul>
);
}
This still behaves correctly at runtime. However, in the future, we may provide a "no-runtime" mechanism, allowing the translations to be directly inlined in the React code through Babel transformations, instead of calling the APIs at runtime. Therefore, to be future-proof, you should always prefer statically analyzable messages. For example, we can refactor the code above to:
const items = [
{id: 1, title: <Translate>Hello</Translate>},
{id: 2, title: <Translate>World</Translate>},
];
function ItemsList() {
return (
<ul>
{/* The titles are now already translated when rendering! */}
{items.map((item) => (
<li key={item.id}>{item.title}</li>
))}
<ul>
);
}
You can see the calls to the translation APIs as purely markers that tell Docusaurus that "here's a text label to be replaced with a translated message".
Pluralization
When you run write-translations
, you will notice that some labels are pluralized:
{
// ...
"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.
번역 API에 대한 호출은 도큐사우루스에 "번역된 메시지로 대체할 텍스트 라벨이 있다"고 알려주는 마커 역할만 하게 됩니다.
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>
</>
);
}
Docusaurus uses Intl.PluralRules
to resolve and select plural forms. It is important to provide the right number of plural forms in the right order for selectMessage
to work.
Translate plugin data
여러분의 코드 메시지를 복수화할 수도 있습니다.
- 위에서 처리한 번역된 라벨을 포함한 리액트 코드
- 테마 설정 내에 메뉴바와 푸터 라벨
- Docs sidebar category labels in
sidebars.js
- 플러그인 옵션의 블로그 사이드바 제목
- ...
Run the write-translations command:
- npm
- Yarn
- pnpm
npm run write-translations -- --locale fr
yarn write-translations --locale fr
pnpm run write-translations --locale fr
도큐사우루스는 복수형을 확인하고 선택하기 위해 Intl.PluralRules
를 사용합니다. 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.
{
// 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:
{
"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.
Translate Markdown files
아래와 같이 write-translations 명령을 실행합니다.
Translate the docs
Copy your docs Markdown files from docs/
to i18n/fr/docusaurus-plugin-content-docs/current
, and translate them:
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current
cp -r docs/** i18n/fr/docusaurus-plugin-content-docs/current
Notice that the docusaurus-plugin-content-docs
plugin always divides its content by versions. The data in ./docs
folder will be translated in the current
subfolder and current.json
file. See the doc versioning guide for more information about what "current" means.
Translate the blog
Copy your blog Markdown files to i18n/fr/docusaurus-plugin-content-blog
, and translate them:
mkdir -p i18n/fr/docusaurus-plugin-content-blog
cp -r blog/** i18n/fr/docusaurus-plugin-content-blog
Translate the pages
Copy your pages Markdown files to i18n/fr/docusaurus-plugin-content-pages
, and translate them:
mkdir -p i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.md i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.mdx i18n/fr/docusaurus-plugin-content-pages
We only copy .md
and .mdx
files, as React pages are translated through JSON translation files already.
By default, a Markdown heading ### Hello World
will have a generated ID hello-world
. Other documents can link it with [link](#hello-world)
. However, after translation, the heading becomes ### Bonjour le Monde
, with ID bonjour-le-monde
.
리액트 페이지는 JSON 번역 파일에서 번역하기 때문에 .md
, .mdx
파일만 복사하면 됩니다.
- [link](#hello-world).
+ [link](#bonjour-le-monde)
For localized sites, it is recommended to use explicit heading IDs.
Deploy your site
You can choose to deploy your site under a single domain or use multiple (sub)domains.
Single-domain deployment
이렇게 만들어지는 ID가 번역된 사이트에 항상 적합한 것은 아닙니다. 여러분이 적절한 링크를 직접 수정해주어야 합니다.
- npm
- Yarn
- pnpm
npm run build
yarn build
pnpm run build
Docusaurus will build one single-page application per locale:
website/build
: for the default, English languagewebsite/build/fr
: for the French language
You can now deploy the build
folder to the static hosting solution of your choice.
The Docusaurus website uses this strategy:
Static hosting providers generally redirect /unknown/url
to /404.html
by convention, always showing an English 404 page.
Localize your 404 pages by configuring your host to redirect /fr/*
to /fr/404.html
.
This is not always possible, and depends on your host: GitHub Pages can't do this, Netlify can.
Multi-domain deployment
호스팅 서비스에서는 일반적으로 /unknown/url
페이지는 규칙에 따라 /404.html
로 리다이렉트합니다. 해당 페이지는 영어로 작성된 404 페이지를 보여줍니다.
- npm
- Yarn
- pnpm
npm run build -- --locale fr
yarn build --locale fr
pnpm run build --locale fr
Docusaurus will not add the /fr/
URL prefix.
On your static hosting provider:
- 로케일마다 배포 파일을 만듭니다.
- configure the appropriate build command, using the
--locale
option - 선택한 배포 대상에 따라 (하위) 도메인을 설정해줍니다.
This strategy is not possible with GitHub Pages, as it is only possible to have a single deployment.
Hybrid
여러분이 이용하는 호스팅 서비스에서 아래와 같은 작업이 필요합니다.
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
Managing translations
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.