docs(v2): i18n doc + polish (#4014)

* add some initial i18n doc

* i18n doc progress

* i18n tutorial progress

* i18n tutorial progress

* polish Crowdin docs

* i18n sidebar in guides

* polish crowdin doc

* update Crowdin doc a bit

* fix annoying relative link to global site resource in template (breaks i18n tutorial)

* template: use simpler export for homepage

* add markdown page example

* rename mdx.md to interactiveDoc.mdx

* update bootstrap/facebook templates too

* sync init template package scripts

* add slug frontmatter doc

* improve i18n doc

* complete i18n doc

* temporarily enable the localeDropdown

* doc typo

* improve the i18n doc

* Add Git i18n doc

* add missing "--" for npm run options (unfortunately they don't get stripped by npm2yarn, and are required foor npm)

* improve a bit the Crowdin doc
This commit is contained in:
Sébastien Lorber 2021-01-19 17:26:31 +01:00 committed by GitHub
parent af8dc63202
commit a8ee7fd3e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1469 additions and 102 deletions

View file

@ -1,22 +1,16 @@
# #
# Your Crowdin credentials # Your Crowdin credentials
# #
'project_id': '428890' project_id: '428890'
'api_token_env': 'CROWDIN_PERSONAL_TOKEN' api_token_env: 'CROWDIN_PERSONAL_TOKEN'
'base_path': '.' # base_path: '.'
'base_url': 'https://api.crowdin.com' # base_url: 'https://api.crowdin.com'
# #
# Choose file structure in Crowdin # Choose file structure in Crowdin
# e.g. true or false # e.g. true or false
# #
'preserve_hierarchy': true preserve_hierarchy: true
settings: {
# Ignore files like .DS_Store etc...
# https://github.com/crowdin/crowdin-cli/wiki/Ignoring-hidden-files-during-upload-sources
"ignore_hidden_files": true
}
# #
# Files configuration # Files configuration
@ -24,42 +18,29 @@ settings: {
files: files:
[ [
{ {
'source': '/website/i18n/en/**/*', source: '/website/i18n/en/**/*',
'translation': '/website/i18n/%two_letters_code%/**/%original_file_name%', translation: '/website/i18n/%two_letters_code%/**/%original_file_name%',
'ignore': ['/**/.DS_Store'],
}, },
{ {
'source': '/website/docs/**/*', source: '/website/docs/**/*',
'translation': '/website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%', translation: '/website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%',
'ignore': ['/**/.DS_Store'],
}, },
{ {
'source': '/website/community/**/*', source: '/website/community/**/*',
'translation': '/website/i18n/%two_letters_code%/docusaurus-plugin-content-docs-community/current/**/%original_file_name%', translation: '/website/i18n/%two_letters_code%/docusaurus-plugin-content-docs-community/current/**/%original_file_name%',
'ignore': ['/**/.DS_Store'],
}, },
{ {
'source': '/website/versioned_docs/**/*', source: '/website/versioned_docs/**/*',
'translation': '/website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/**/%original_file_name%', translation: '/website/i18n/%two_letters_code%/docusaurus-plugin-content-docs/**/%original_file_name%',
'ignore': ['/**/.DS_Store'],
}, },
{ {
'source': '/website-1.x/blog/**/*', source: '/website-1.x/blog/**/*',
'translation': '/website/i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%', translation: '/website/i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%',
'ignore': ['/**/.DS_Store'],
}, },
{ {
'source': '/website/src/pages/**/*', source: '/website/src/pages/**/*',
'translation': '/website/i18n/%two_letters_code%/docusaurus-plugin-content-pages/**/%original_file_name%', translation: '/website/i18n/%two_letters_code%/docusaurus-plugin-content-pages/**/%original_file_name%',
'ignore': ignore: ['/**/*.js', '/**/*.jsx', '/**/*.ts', '/**/*.tsx', '/**/*.css'],
[
'/**/*.js',
'/**/*.jsx',
'/**/*.ts',
'/**/*.tsx',
'/**/*.css',
'/**/.DS_Store',
],
}, },
] ]
# #

View file

@ -54,7 +54,7 @@ function Feature({imageUrl, title, description}) {
); );
} }
function Home() { export default function Home() {
const context = useDocusaurusContext(); const context = useDocusaurusContext();
const {siteConfig = {}} = context; const {siteConfig = {}} = context;
return ( return (
@ -93,5 +93,3 @@ function Home() {
</Layout> </Layout>
); );
} }
export default Home;

View file

@ -29,9 +29,9 @@ To serve as an example page when styling markdown based Docusaurus sites.
## Emphasis ## Emphasis
Emphasis, aka italics, with *asterisks* or _underscores_. Emphasis, aka italics, with _asterisks_ or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__. Strong emphasis, aka bold, with **asterisks** or **underscores**.
Combined emphasis with **asterisks and _underscores_**. Combined emphasis with **asterisks and _underscores_**.
@ -43,16 +43,18 @@ Strikethrough uses two tildes. ~~Scratch this.~~
1. First ordered list item 1. First ordered list item
1. Another item 1. Another item
- Unordered sub-list.
- Unordered sub-list.
1. Actual numbers don't matter, just that it's a number 1. Actual numbers don't matter, just that it's a number
1. Ordered sub-list 1. Ordered sub-list
1. And another item. 1. And another item.
* Unordered list can use asterisks - Unordered list can use asterisks
- Or minuses * Or minuses
+ Or pluses - Or pluses
--- ---
@ -88,10 +90,9 @@ Reference-style: ![alt text][logo]
[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' [logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2'
Images from any folder can be used by providing path to file. Path should be relative to markdown file. Images from any folder can be used by providing path to file. Path should be relative to the original markdown file or absolute to the `/static` folder.
![img](../static/img/logo.svg)
![img](/img/logo.svg)
--- ---

View file

@ -5,13 +5,23 @@ title: Powered by MDX
You can write JSX and use React components within your Markdown thanks to [MDX](https://mdxjs.com/). You can write JSX and use React components within your Markdown thanks to [MDX](https://mdxjs.com/).
export const Highlight = ({children, color}) => ( <span style={{ The `.mdx` extension is not required, but will enable better support from tooling (IDE, Prettier...).
export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color, backgroundColor: color,
borderRadius: '2px', borderRadius: '2px',
color: '#fff', color: '#fff',
padding: '0.2rem', padding: '0.2rem',
}}>{children}</span> ); }}
onClick={() => alert('Highlight pressed!')}>
{children}
</span>
);
<Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors. <Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">
Facebook blue
</Highlight> are my favorite colors.
I can write **Markdown** alongside my _JSX_! I can write **Markdown** alongside my _JSX_!

View file

@ -8,7 +8,9 @@
"build": "docusaurus build", "build": "docusaurus build",
"swizzle": "docusaurus swizzle", "swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy", "deploy": "docusaurus deploy",
"serve": "docusaurus serve" "serve": "docusaurus serve",
"clear": "docusaurus clear",
"write-translations": "write-translations"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "2.0.0-alpha.70", "@docusaurus/core": "2.0.0-alpha.70",

View file

@ -55,7 +55,7 @@ function Feature({imageUrl, title, description}) {
); );
} }
function Home() { export default function Home() {
const context = useDocusaurusContext(); const context = useDocusaurusContext();
const {siteConfig = {}} = context; const {siteConfig = {}} = context;
@ -86,5 +86,3 @@ function Home() {
</Layout> </Layout>
); );
} }
export default Home;

View file

@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

View file

@ -88,9 +88,9 @@ Reference-style: ![alt text][logo]
[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' [logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2'
Images from any folder can be used by providing path to file. Path should be relative to markdown file. Images from any folder can be used by providing path to file. Path should be relative to the original markdown file or absolute to the `/static` folder.
![img](../static/img/logo.svg) ![img](/img/logo.svg)
--- ---

View file

@ -5,13 +5,23 @@ title: Powered by MDX
You can write JSX and use React components within your Markdown thanks to [MDX](https://mdxjs.com/). You can write JSX and use React components within your Markdown thanks to [MDX](https://mdxjs.com/).
export const Highlight = ({children, color}) => ( <span style={{ The `.mdx` extension is not required, but will enable better support from tooling (IDE, Prettier...).
export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color, backgroundColor: color,
borderRadius: '2px', borderRadius: '2px',
color: '#fff', color: '#fff',
padding: '0.2rem', padding: '0.2rem',
}}>{children}</span> ); }}
onClick={() => alert('Highlight pressed!')}>
{children}
</span>
);
<Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors. <Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">
Facebook blue
</Highlight> are my favorite colors.
I can write **Markdown** alongside my _JSX_! I can write **Markdown** alongside my _JSX_!

View file

@ -9,7 +9,8 @@
"swizzle": "docusaurus swizzle", "swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy", "deploy": "docusaurus deploy",
"serve": "docusaurus serve", "serve": "docusaurus serve",
"clear": "docusaurus clear" "clear": "docusaurus clear",
"write-translations": "write-translations"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "2.0.0-alpha.70", "@docusaurus/core": "2.0.0-alpha.70",

View file

@ -54,7 +54,7 @@ function Feature({imageUrl, title, description}) {
); );
} }
function Home() { export default function Home() {
const context = useDocusaurusContext(); const context = useDocusaurusContext();
const {siteConfig = {}} = context; const {siteConfig = {}} = context;
return ( return (
@ -93,5 +93,3 @@ function Home() {
</Layout> </Layout>
); );
} }
export default Home;

View file

@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

View file

@ -29,9 +29,9 @@ To serve as an example page when styling markdown based Docusaurus sites.
## Emphasis ## Emphasis
Emphasis, aka italics, with *asterisks* or _underscores_. Emphasis, aka italics, with _asterisks_ or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__. Strong emphasis, aka bold, with **asterisks** or **underscores**.
Combined emphasis with **asterisks and _underscores_**. Combined emphasis with **asterisks and _underscores_**.
@ -43,16 +43,18 @@ Strikethrough uses two tildes. ~~Scratch this.~~
1. First ordered list item 1. First ordered list item
1. Another item 1. Another item
- Unordered sub-list.
- Unordered sub-list.
1. Actual numbers don't matter, just that it's a number 1. Actual numbers don't matter, just that it's a number
1. Ordered sub-list 1. Ordered sub-list
1. And another item. 1. And another item.
* Unordered list can use asterisks - Unordered list can use asterisks
- Or minuses * Or minuses
+ Or pluses - Or pluses
--- ---
@ -88,9 +90,9 @@ Reference-style: ![alt text][logo]
[logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2' [logo]: https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png 'Logo Title Text 2'
Images from any folder can be used by providing path to file. Path should be relative to markdown file. Images from any folder can be used by providing path to file. Path should be relative to the original markdown file or absolute to the `/static` folder.
![img](../static/img/logo.svg) ![img](/img/logo.svg)
--- ---

View file

@ -5,13 +5,23 @@ title: Powered by MDX
You can write JSX and use React components within your Markdown thanks to [MDX](https://mdxjs.com/). You can write JSX and use React components within your Markdown thanks to [MDX](https://mdxjs.com/).
export const Highlight = ({children, color}) => ( <span style={{ The `.mdx` extension is not required, but will enable better support from tooling (IDE, Prettier...).
export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color, backgroundColor: color,
borderRadius: '2px', borderRadius: '2px',
color: '#fff', color: '#fff',
padding: '0.2rem', padding: '0.2rem',
}}>{children}</span> ); }}
onClick={() => alert('Highlight pressed!')}>
{children}
</span>
);
<Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors. <Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">
Facebook blue
</Highlight> are my favorite colors.
I can write **Markdown** alongside my _JSX_! I can write **Markdown** alongside my _JSX_!

View file

@ -10,10 +10,11 @@
"deploy": "docusaurus deploy", "deploy": "docusaurus deploy",
"serve": "docusaurus serve", "serve": "docusaurus serve",
"clear": "docusaurus clear", "clear": "docusaurus clear",
"write-translations": "write-translations",
"ci": "yarn lint && yarn prettier:diff", "ci": "yarn lint && yarn prettier:diff",
"lint": "eslint --cache \"**/*.js\" && stylelint \"**/*.css\"", "lint": "eslint --cache \"**/*.js\" && stylelint \"**/*.css\"",
"prettier": "prettier --config .prettierrc --write \"**/*.{js,md}\"", "prettier": "prettier --config .prettierrc --write \"**/*.{js,jsx,ts,tsx,md,mdx}\"",
"prettier:diff": "prettier --config .prettierrc --list-different \"**/*.{js,md}\"" "prettier:diff": "prettier --config .prettierrc --list-different \"**/*.{js,jsx,ts,tsx,md,mdx}\""
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "2.0.0-alpha.70", "@docusaurus/core": "2.0.0-alpha.70",

View file

@ -63,7 +63,7 @@ function Feature({imageUrl, title, description}) {
); );
} }
function Home() { export default function Home() {
const context = useDocusaurusContext(); const context = useDocusaurusContext();
const {siteConfig = {}} = context; const {siteConfig = {}} = context;
return ( return (
@ -107,5 +107,3 @@ function Home() {
</Layout> </Layout>
); );
} }
export default Home;

View file

@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

View file

@ -13,6 +13,8 @@ import {useLocation} from '@docusaurus/router';
export default function LocaleDropdownNavbarItem({ export default function LocaleDropdownNavbarItem({
mobile, mobile,
dropdownItemsBefore,
dropdownItemsAfter,
...props ...props
}: Props): JSX.Element { }: Props): JSX.Element {
const { const {
@ -39,7 +41,7 @@ export default function LocaleDropdownNavbarItem({
: `${baseUrlUnlocalized}${locale}/`; : `${baseUrlUnlocalized}${locale}/`;
} }
const items = locales.map((locale) => { const localeItems = locales.map((locale) => {
const to = `${getLocalizedBaseUrl(locale)}${pathnameSuffix}`; const to = `${getLocalizedBaseUrl(locale)}${pathnameSuffix}`;
return { return {
isNavLink: true, isNavLink: true,
@ -51,6 +53,8 @@ export default function LocaleDropdownNavbarItem({
}; };
}); });
const items = [...dropdownItemsBefore, ...localeItems, ...dropdownItemsAfter];
// Mobile is handled a bit differently // Mobile is handled a bit differently
const dropdownLabel = mobile ? 'Languages' : getLocaleLabel(currentLocale); const dropdownLabel = mobile ? 'Languages' : getLocaleLabel(currentLocale);

View file

@ -306,8 +306,12 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' {
declare module '@theme/NavbarItem/LocaleDropdownNavbarItem' { declare module '@theme/NavbarItem/LocaleDropdownNavbarItem' {
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem'; import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
import type {NavLinkProps} from '@theme/NavbarItem/DefaultNavbarItem';
export type Props = DefaultNavbarItemProps; export type Props = DefaultNavbarItemProps & {
readonly dropdownItemsBefore: NavLinkProps[];
readonly dropdownItemsAfter: NavLinkProps[];
};
const LocaleDropdownNavbarItem: (props: Props) => JSX.Element; const LocaleDropdownNavbarItem: (props: Props) => JSX.Element;
export default LocaleDropdownNavbarItem; export default LocaleDropdownNavbarItem;
@ -319,9 +323,9 @@ declare module '@theme/NavbarItem/DocsVersionDropdownNavbarItem' {
export type Props = DefaultNavbarItemProps & { export type Props = DefaultNavbarItemProps & {
readonly docsPluginId?: string; readonly docsPluginId?: string;
dropdownActiveClassDisabled?: boolean; readonly dropdownActiveClassDisabled?: boolean;
dropdownItemsBefore: NavLinkProps[]; readonly dropdownItemsBefore: NavLinkProps[];
dropdownItemsAfter: NavLinkProps[]; readonly dropdownItemsAfter: NavLinkProps[];
}; };
const DocsVersionDropdownNavbarItem: (props: Props) => JSX.Element; const DocsVersionDropdownNavbarItem: (props: Props) => JSX.Element;

View file

@ -99,6 +99,8 @@ const DocItemSchema = Joi.object({
const LocaleDropdownNavbarItemSchema = Joi.object({ const LocaleDropdownNavbarItemSchema = Joi.object({
type: Joi.string().equal('localeDropdown').required(), type: Joi.string().equal('localeDropdown').required(),
position: NavbarItemPosition, position: NavbarItemPosition,
dropdownItemsBefore: Joi.array().items(BaseNavbarItemSchema).default([]),
dropdownItemsAfter: Joi.array().items(BaseNavbarItemSchema).default([]),
}); });
// Can this be made easier? :/ // Can this be made easier? :/

View file

@ -3,5 +3,5 @@
exports[`loadI18n should throw when trying to load undeclared locale 1`] = ` exports[`loadI18n should throw when trying to load undeclared locale 1`] = `
"It is not possible to load Docusaurus with locale=\\"it\\". "It is not possible to load Docusaurus with locale=\\"it\\".
This locale is not in the available locales of your site configuration: config.i18n.locales=[en,fr,de] This locale is not in the available locales of your site configuration: config.i18n.locales=[en,fr,de]
Note: Docusaurus only support running one local at a time." Note: Docusaurus only support running one locale at a time."
`; `;

View file

@ -27,7 +27,7 @@ export async function loadI18n(
This locale is not in the available locales of your site configuration: config.i18n.locales=[${i18nConfig.locales.join( This locale is not in the available locales of your site configuration: config.i18n.locales=[${i18nConfig.locales.join(
',', ',',
)}] )}]
Note: Docusaurus only support running one local at a time.`, Note: Docusaurus only support running one locale at a time.`,
); );
} }

View file

@ -80,6 +80,31 @@ module.exports = {
## Optional fields ## Optional fields
### `i18n`
- Type: `Object`
The i18n configuration object to [localize your site](../i18n/i18n-introduction.md).
Example:
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
localeConfigs: {
en: {
label: 'English',
},
fr: {
label: 'Français',
},
},
},
};
```
### `noIndex` ### `noIndex`
- Type: `boolean` - Type: `boolean`
@ -178,11 +203,7 @@ module.exports = {
- Type: `Object` - Type: `Object`
<!-- TODO: explain that theme configurations will be consumed by the theme, and link to theme doc --> The [theme configuration](./themes/theme-configuration.md) object, to customize your site UI: navbar, footer...
An object containing data needed by the theme you use.<!--, see [theme configurations](#).-->
For Docusaurus' default theme _classic_, we use `themeConfig` to customize your navbar and footer links:
Example: Example:

View file

@ -105,3 +105,24 @@ module.exports = {
], ],
}; };
``` ```
## i18n
Read the [i18n introduction](../../i18n/i18n-introduction.md) first.
### Translation files location
- **Base path**: `website/i18n/<locale>/docusaurus-plugin-content-blog`
- **Multi-instance path**: `website/i18n/<locale>/docusaurus-plugin-content-blog-<pluginId>`
- **JSON files**: N/A
- **Markdown files**: `website/i18n/<locale>/docusaurus-plugin-content-blog`
### Example file-system structure
```bash
website/i18n/<locale>/docusaurus-plugin-content-blog
│ # translations for website/blog
├── first-blog-post.md
└── second-blog-post.md
```

View file

@ -147,6 +147,7 @@ Markdown documents can use the following markdown frontmmatter metadata fields,
- `keywords`: Keywords meta tag for the document page, for search engines. - `keywords`: Keywords meta tag for the document page, for search engines.
- `description`: The description of your document, which will become the `<meta name="description" content="..."/>` and `<meta property="og:description" content="..."/>` in `<head>`, used by search engines. If this field is not present, it will default to the first line of the contents. - `description`: The description of your document, which will become the `<meta name="description" content="..."/>` and `<meta property="og:description" content="..."/>` in `<head>`, used by search engines. If this field is not present, it will default to the first line of the contents.
- `image`: Cover or thumbnail image that will be used when displaying the link to your post. - `image`: Cover or thumbnail image that will be used when displaying the link to your post.
- `slug`: Allows to customize the document url
Example: Example:
@ -163,6 +164,38 @@ keywords:
- docs - docs
- docusaurus - docusaurus
image: https://i.imgur.com/mErPwqL.png image: https://i.imgur.com/mErPwqL.png
slug: /myDoc
--- ---
My Document Markdown content My Document Markdown content
``` ```
## i18n
Read the [i18n introduction](../../i18n/i18n-introduction.md) first.
### Translation files location
- **Base path**: `website/i18n/<locale>/docusaurus-plugin-content-docs`
- **Multi-instance path**: `website/i18n/<locale>/docusaurus-plugin-content-docs-<pluginId>`
- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.md#docusaurus-write-translations)
- **Markdown files**: `website/i18n/<locale>/docusaurus-plugin-content-docs/<version>`
### Example file-system structure
```bash
website/i18n/<locale>/docusaurus-plugin-content-docs
│ # translations for website/docs
├── current
│   ├── api
│   │   └── config.md
│   └── getting-started.md
├── current.json
│ # translations for website/versioned_docs/version-1.0.0
├── version-1.0.0
│   ├── api
│   │   └── config.md
│   └── getting-started.md
└── version-1.0.0.json
```

View file

@ -66,3 +66,24 @@ module.exports = {
], ],
}; };
``` ```
## i18n
Read the [i18n introduction](../../i18n/i18n-introduction.md) first.
### Translation files location
- **Base path**: `website/i18n/<locale>/docusaurus-plugin-content-pages`
- **Multi-instance path**: `website/i18n/<locale>/docusaurus-plugin-content-pages-<pluginId>`
- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.md#docusaurus-write-translations)
- **Markdown files**: `website/i18n/<locale>/docusaurus-plugin-content-pages`
### Example file-system structure
```bash
website/i18n/<locale>/docusaurus-plugin-content-pages
│ # translations for website/src/pages
├── first-markdown-page.md
└── second-markdown-page.md
```

View file

@ -117,6 +117,27 @@ module.exports = {
}; };
``` ```
## i18n
Read the [i18n introduction](../../i18n/i18n-introduction.md) first.
### Translation files location
- **Base path**: `website/i18n/<locale>/docusaurus-theme-<themeName>`
- **Multi-instance path**: N/A
- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.md#docusaurus-write-translations)
- **Markdown files**: `N/A
### Example file-system structure
```bash
website/i18n/<locale>/docusaurus-theme-classic
│ # translations for the theme
├── navbar.json
└── footer.json
```
## Hooks ## Hooks
### `useThemeContext` ### `useThemeContext`
@ -290,7 +311,9 @@ module.exports = {
### Navbar docs version dropdown ### Navbar docs version dropdown
If you use docs with versioning, this special navbar item type that will render a dropdown with all your site's available versions. The user will be able to switch from one version to another, while staying on the same doc (as long as the doc id is constant across versions). If you use docs with versioning, this special navbar item type that will render a dropdown with all your site's available versions.
The user will be able to switch from one version to another, while staying on the same doc (as long as the doc id is constant across versions).
```js {5-8} title="docusaurus.config.js" ```js {5-8} title="docusaurus.config.js"
module.exports = { module.exports = {
@ -341,6 +364,44 @@ module.exports = {
}; };
``` ```
### Navbar locale dropdown
If you use the [i18n feature](../../i18n/i18n-introduction.md), this special navbar item type will render a dropdown with all your site's available locales.
The user will be able to switch from one locale to another, while staying on the same page.
```js {5-8} title="docusaurus.config.js"
module.exports = {
themeConfig: {
navbar: {
items: [
{
type: 'localeDropdown',
//// Optional
position: 'left',
// Add additional dropdown items at the beginning/end of the dropdown.
dropdownItemsBefore: [],
dropdownItemsAfter: [
{
to: 'https://my-site.com/help-us-translate',
label: 'Help us translate',
},
],
},
],
},
},
};
```
````
{
type: 'localeDropdown',
position: 'left',
},
```
### Auto-hide sticky navbar ### Auto-hide sticky navbar
You can enable this cool UI feature that automatically hides the navbar when a user starts scrolling down the page, and show it again when the user scrolls up. You can enable this cool UI feature that automatically hides the navbar when a user starts scrolling down the page, and show it again when the user scrolls up.
@ -355,7 +416,7 @@ module.exports = {
// ... // ...
}, },
}; };
``` ````
### Navbar style ### Navbar style

View file

@ -148,3 +148,15 @@ Serve your built website locally.
Clear a Docusaurus site's generated assets, caches, build artifacts. Clear a Docusaurus site's generated assets, caches, build artifacts.
We recommend running this command before reporting bugs, after upgrading versions, or anytime you have issues with your Docusaurus site. We recommend running this command before reporting bugs, after upgrading versions, or anytime you have issues with your Docusaurus site.
### `docusaurus write-translations`
Write the JSON translation files that you will have to translate.
By default, the files are written in `website/i18n/<defaultLocale>/...`.
| Name | Default | Description |
| --- | --- | --- |
| `--locale` | `<defaultLocale>` | Define which locale folder you want to write translations the JSON files in |
| `--override` | `false` | Override existing translation messages |
| `--messagePrefix` | `''` | Allows to add a prefix to each translation message, to help you highlight untranslated strings |

View file

@ -32,7 +32,7 @@ It is not the most performant solution
Docusaurus can be self hosted using [`docusaurus serve`](cli.md#docusaurus-serve). Change port using `--port` and `--host` to change host. Docusaurus can be self hosted using [`docusaurus serve`](cli.md#docusaurus-serve). Change port using `--port` and `--host` to change host.
```bash npm2yarn ```bash npm2yarn
npm run serve --build --port 80 --host 0.0.0.0 npm run serve -- --build --port 80 --host 0.0.0.0
``` ```
## Deploying to GitHub Pages ## Deploying to GitHub Pages

View file

@ -125,6 +125,58 @@ const MyComponent = () => {
}; };
``` ```
### `<Translate/>`
When [localizing your site](./i18n/i18n-introduction.md), the `<Translate/>` component will allow providing **translation support to React components**, such as your homepage.
The translation strings will be extracted from your code with the [`docusaurus write-translations`](./cli.md#docusaurus-write-translations) CLI and create a `code.json` translation file in `website/i18n/<locale>`.
:::note
The `<Translate/>` props **must be hardcoded strings**.
It is **not possible to use variables**, or the extraction wouldn't work.
:::
#### Props
- `children`: untranslated string in the default site locale`
- `id`: optional value to use as key in JSON translation files
- `description`: optional text to help the translator
#### Example
```jsx title="src/index.js"
import React from 'react';
import Layout from '@theme/Layout';
// highlight-start
import Translate from '@docusaurus/Translate';
// highlight-end
export default function Home() {
return (
<Layout>
<h1>
{/* highlight-start */}
<Translate
id="homepage.title"
description="The homepage welcome message">
Welcome to my website
</Translate>
{/* highlight-end */}
</h1>
<main>
{/* highlight-start */}
<Translate>My website content</Translate>
{/* highlight-end */}
</main>
</Layout>
);
}
```
## Hooks ## Hooks
### `useDocusaurusContext` ### `useDocusaurusContext`
@ -303,6 +355,54 @@ const MyComponent = () => {
}; };
``` ```
## Functions
### `translate`
The imperative counterpart of the [`<Translate>`](#translate) component.
:::tip
Use the imperative API for the **rare cases** when a **component cannot be used**, such as:
- the `placeholder` props of form input
- the page `title` metadata
:::
```jsx title="src/index.js"
import React from 'react';
import Layout from '@theme/Layout';
// highlight-start
import {translate} from '@docusaurus/Translate';
// highlight-end
export default function Home() {
return (
<Layout
// highlight-start
title={translate({message: 'My page meta title'})}
// highlight-end
>
<input
type="text"
placeholder={
// highlight-start
translate({
message: 'Some input placeholder',
// Optional
id: 'homepage.input.placeholder',
description: 'The homepage input placeholder',
})
// highlight-end
}
/>
</Layout>
);
}
```
## Modules ## Modules
### `ExecutionEnvironment` ### `ExecutionEnvironment`

View file

@ -167,7 +167,7 @@ The instance paths will be simpler, and retro-compatible with a single-instance
Each plugin instance will have its own cli command to tag a new version. They will be displayed if you run: Each plugin instance will have its own cli command to tag a new version. They will be displayed if you run:
```bash npm2yarn ```bash npm2yarn
npm run docusaurus --help npm run docusaurus -- --help
``` ```
To version the product/default docs plugin instance: To version the product/default docs plugin instance:

View file

@ -0,0 +1,425 @@
---
id: crowdin
title: i18n - Using Crowdin
sidebar_label: Using Crowdin
slug: /i18n/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](http://crowdin.com/), as **one** possible **integration example**.
:::caution
This is **not an endorsement of Crowdin** as the unique choice to translate a Docusaurus site, but it is used successfully used by Facebook to translate documentation projects such as [Jest](https://jestjs.io/), [Docusaurus](https://docusaurus.io/) and [ReasonML](https://reasonml.github.io/).
Refer to the **[Crowdin documentation](https://support.crowdin.com/)** and **[Crowdin support](mailto:support@crowdin.com)** for help.
:::
:::tip
Use this **[community-driven GitHub issue](https://github.com/facebook/docusaurus/discussions/4052)** to discuss anything related to Docusaurus + Crowdin.
:::
## Crowdin overview
Crowdin is a translation SaaS, offering a [free plan for open-source projects](https://crowdin.com/page/open-source-project-setup-request).
We recommend the following translation workflow:
- **Upload sources** to Crowdin (untranslated files)
- Use Crowdin to **translate the content**
- **Download translations** from Crowdin (localized translation files)
Crowdin provides a [CLI](https://support.crowdin.com/cli-tool/) to **upload sources** and **download translations**, allowing you to automate the translation process.
The [`crowdin.yml` configuration file](https://support.crowdin.com/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](https://support.crowdin.com/)** 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](./i18n-tutorial.md).
The end result can be seen at [docusaurus-crowdin-example.netlify.app](https://docusaurus-crowdin-example.netlify.app/) ([repository](https://github.com/slorber/docusaurus-crowdin-example)).
### Prepare the Docusaurus site
Initialize a new Docusaurus site:
```bash
npx @docusaurus/init@latest init website classic
```
Add the site configuration for the French language:
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
themeConfig: {
navbar: {
items: [
// ...
{
type: 'localeDropdown',
position: 'left',
},
// ...
],
},
},
// ...
};
```
Translate the homepage:
```jsx title="src/index.js"
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](https://crowdin.com/), and create a project.
Use English as source language, and French as target language.
![Create a Crowdin project with english as source language, and french as target language](/img/crowdin/crowdin-create-project.png)
Your project is created, but it is empty for now. We will upload the files to translate in the next steps.
### Create the Crowdin configuration
This configuration ([doc](https://support.crowdin.com/configuration-file/)) provides a mapping for the Crowdin CLI to understand:
- Where to find the source files to upload (JSON and Markdown)
- Where to download the files after translation (in `i18n/<locale>`).
Create `crowdin.yml` in `website`:
```yml title="crowdin.yml"
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 has its own syntax for declaring source/translation paths:
- `**/*`: 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
:::info
The Crowdin CLI warnings are not always easy to understand.
We advise to:
- change one thing at a time
- re-upload sources after any configuration change
- 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](https://crowdin.com/settings#api-key).
:::tip
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.
:::
:::caution
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 on `https://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 use the CLI in version `3.5.2`, but we expect `3.x` releases to keep working.
Install the Crowdin CLI as a NPM package to your Docusaurus site:
```bash npm2yarn
npm install @crowdin/cli@3
```
Add a `crowdin` script:
```json title="package.json"
{
"scripts": {
"crowdin": "crowdin"
}
}
```
Test that you can run the Crowdin CLI:
```bash npm2yarn
npm run crowdin -- --version
```
Set the `CROWDIN_PERSONAL_TOKEN` env variable on your computer, to allow the CLI to authenticate with the Crowdin API.
:::tip
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`:
```bash npm2yarn
npm run write-translations
```
Upload all the JSON and Markdown translation files:
```bash npm2yarn
npm run crowdin upload
```
![Crowdin CLI uploading Docusaurus source files](/img/crowdin/crowdin-upload-sources-cli.png)
Your source files are now visible on the Crowdin interface: `https://crowdin.com/project/<MY_PROJECT_NAME>/settings#files`
![Crowdin UI showing Docusaurus source files](/img/crowdin/crowdin-source-files.png)
### Translate the sources
On `https://crowdin.com/project/<MY_PROJECT_NAME>`, click on the French target language.
![Crowdin UI showing French translation files](/img/crowdin/crowdin-french-translations.png)
Translate some Markdown files.
![Crowdin UI to translate a Markdown file](/img/crowdin/crowdin-translate-markdown.png)
:::tip
Use `Hide String` to make sure translators **don't translate things that should not be**:
- Frontmatter: `id`, `slug`, `tags` ...
- Admonitions: `:::`, `:::note`, `:::tip` ...
![Crowdin UI hide string](/img/crowdin/crowdin-hide-string.png)
:::
Translate some JSON files.
![Crowdin UI to translate a JSON file](/img/crowdin/crowdin-translate-json.png)
:::info
The `description` attribute of JSON translation files is visible on Crowdin to help translate the strings.
:::
:::tip
**[Pre-translate](https://support.crowdin.com/pre-translation-via-machine/)** 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
Use the Crowdin CLI to download the translated JSON and Markdown files.
```bash npm2yarn
npm run crowdin download
```
The translated content should be downloaded in `i18n/fr`.
Start your site on the French locale:
```bash npm2yarn
npm 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 a NPM script to `sync` Crowdin (extract sources, upload sources, download translations):
```json title="package.json"
{
"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.
:::tip
Keep your deploy-previews fast: don't download translations, and use `npm run build -- --locale en` for feature branches.
:::
:::caution
Crowdin does not support well multiple concurrent uploads/downloads: it is preferable to only include translations to your production deployment, and keep deploy previews untranslated.
:::
## Advanced Crowdin topics
### MDX
:::warning
Crowdin does not support the `.mdx` extension, and interpret these files as plain text instead of Markdown, producing a bad translation experience.
:::
We temporarily recommend using the `.md` extension (even if the document contains React code), and are in touch with Crowdin to get this solved.
### 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.
![Crowdin Duplicate Strings option setting](/img/crowdin/crowdin-settings-duplicate-strings.png)
We recommend using `Hide`, but the ideal setting depends on how much your versions are different.
:::caution
Not using `Hide` leads to a much larger amount of `source strings` in quotas, and will affect the pricing.
:::
### Multi-instance plugins
You need to configure translation files for each plugin instance.
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:
![Crowdin CLI: download translation warning](/img/crowdin/crowdin-download-translations-warning.png)
When your sources are refactored, you should use the Crowdin UI to **update your Crowdin files manually**:
![Crowdin UI: renaming a file](/img/crowdin/crowdin-files-rename.png)
### Git / VCS integrations
Crowdin has multiple VCS integrations for [GitHub](https://support.crowdin.com/github-integration/), GitLab, BitBucket...
:::warning
We recommend avoiding them.
:::
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)
- The heuristics used by Crowdin to match existing Markdown translations to existing Markdown sources are not 100% reliable, and you have to verify the result on Crowdin UI after any sync from Git
- 2 users concurrently editing on Git and Crowdin can lead to a translation loss
- It requires the `crowdin.yml` file to be at the root of the repository
### In-Context localization
Crowdin has an [In-Context localization](https://support.crowdin.com/in-context-localization/) feature.
:::caution
Unfortunately, it does not work yet for technical reasons, but we have good hope it can be solved.
Crowdin replaces markdown strings with technical ids such as `crowdin:id12345`, but it does so too aggressively, including hidden strings, and mess-up with the frontmatter, admonitions, jsx...
:::
### Example configuration
The **Docusaurus v2 configuration file** is a good example of using versioning and multi-instance:
import CrowdinConfigV2 from '!!raw-loader!@site/../crowdin-v2.yaml';
import CodeBlock from '@theme/CodeBlock';
<CodeBlock className="language-yaml" title="test">
{CrowdinConfigV2.split('\n')
// remove comments
.map((line) => !line.startsWith('#') && line)
.filter(Boolean)
.join('\n')}
</CodeBlock>

View file

@ -0,0 +1,173 @@
---
id: git
title: i18n - Using git
sidebar_label: Using Git
slug: /i18n/git
---
A **possible translation strategy** is to **version control the translation files** to Git (or any other [VCS](https://en.wikipedia.org/wiki/Version_control)).
## Tradeoffs
This strategy has advantages:
- **Easy to get started**: just add the `i18n` folder to Git
- **Easy for developers**: Git, GitHub and pull-requests are mainstream developer tools
- **Free** (or without any additional cost, assuming you already use Git)
- **Low friction**: does not require signing-up to an external tool
- **Rewarding**: contributors are happy to have a nice contribution history
Using Git also present some shortcomings:
- **Hard for non-developers**: they do not master Git and pull-requests
- **Hard for professional translations**: they are used to SaaS translation softwares and advanced features
- **Hard to maintain**: you have to keep the translated files **in sync** with the untranslated files
:::note
Some **large-scale technical projects** (ReactJS, VueJS, Gatsby, TypeScript, Nuxt...) use Git for translations.
Refer to the [Docusaurus i18n RFC](https://github.com/facebook/docusaurus/issues/3317) for our notes and links studying these systems.
:::
## Git tutorial
This is a walk-through of using Git to translate a newly initialized English Docusaurus website into French, and assume you already followed the [i18n tutorial](./i18n-tutorial.md).
### Prepare the Docusaurus site
Initialize a new Docusaurus site:
```bash
npx @docusaurus/init@latest init website classic
```
Add the site configuration for the French language:
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
themeConfig: {
navbar: {
items: [
// ...
{
type: 'localeDropdown',
position: 'left',
},
// ...
],
},
},
// ...
};
```
Translate the homepage:
```jsx title="src/index.js"
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>
);
}
```
### Initialize the `i18n` folder
Use the [write-translations](../cli.md#docusaurus-write-translations) CLI command to initialize the JSON translation files for the French locale:
```bash npm2yarn
npm run write-translations -- --locale fr
1 translations written at i18n/fr/code.json
11 translations written at i18n/fr/docusaurus-theme-classic/footer.json
4 translations written at i18n/fr/docusaurus-theme-classic/navbar.json
3 translations written at i18n/fr/docusaurus-plugin-content-docs/current.json
```
:::tip
Use the `--messagePrefix '(fr) '` option to make the untranslated strings stand out.
`Hello` will appear as `(fr) Hello` and makes it clear a translation is missing.
:::
Copy your untranslated Markdown files to the French folder:
```
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current
cp -r docs/** i18n/fr/docusaurus-plugin-content-docs/current
mkdir -p i18n/fr/docusaurus-plugin-content-blog
cp -r blog/** i18n/fr/docusaurus-plugin-content-blog
mkdir -p i18n/fr/docusaurus-plugin-content-pages
cp -r pages/**.md i18n/fr/docusaurus-plugin-content-pages
cp -r pages/**.mdx i18n/fr/docusaurus-plugin-content-pages
```
Add all these files to Git.
### Translate the files
Translate the Markdown and JSON files in `i18n/fr` and commit the translation.
You should now be able to start your site in French and see the translations:
```bash npm2yarn
npm run start -- --locale fr
```
You can also build the site locally or on your CI:
```bash npm2yarn
npm run build
# or
npm run build -- --locale fr
```
### Repeat
Follow the same process for each locale you need to support.
## Maintain the translations
Keeping translated files **consistent** with the originals **can be challenging**, in particular for Markdown documents.
### Markdown translations
When an untranslated Markdown document is edited, it is **your responsibility to maintain the respective translated files**, and we unfortunately don't have a good way to help you do so.
To keep your translated sites consistent, when the `website/docs/doc1.md` doc is edited, you need **backport these edits** to `i18n/fr/docusaurus-plugin-content-docs/current/doc1.md`.
### JSON translations
To help you maintain the JSON translation files, it is possible to run again the [write-translations](../cli.md#docusaurus-write-translations) CLI command:
```bash npm2yarn
npm run write-translations -- --locale fr
```
New translation will be appended, and existing ones will not be overridden.
:::tip
Reset your translations with the `--override` option.
:::

View file

@ -0,0 +1,154 @@
---
id: introduction
title: i18n - Introduction
sidebar_label: Introduction
slug: /i18n/introduction
---
It is possible to translate a Docusaurus website through its internationalization support (abbreviated as [i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization)).
:::caution
i18n is a new feature (released early 2021), please report any bug you find.
:::
## Goals
This section should help you understand the design decisions behind the Docusaurus i18n support.
For more context, you can read the initial [RFC](https://github.com/facebook/docusaurus/issues/3317) and [PR](https://github.com/facebook/docusaurus/pull/3325).
### i18n goals
The goals of the Docusaurus i18n system are:
- **Simple**: just put the translated files in the correct file-system location.
- **Flexible translation workflows**: based on Git (monorepo, forks or submodules), SaaS software, FTP...
- **Flexible deployment options**: single or multiple domains.
- **Modular**: allow plugin author to provide i18n support.
- **Low-overhead runtime**: static json/markdown content does not require a heavy i18n JS library.
- **Acceptable build-times**: allow building and deploying localized sites independently.
- **Localize assets**: an image of your site might contain text that should be translated.
- **No coupling**: not forced to use any SaaS, yet the integration is possible.
- **Easy to use with [Crowdin](http://crowdin.com/)**: multiple Docusaurus v1 sites use Crowdin, and should be able to migrate to v2.
### i18n goals (TODO)
Features that are **not yet implemented**:
- **Good SEO defaults**: setting useful html meta headers like `hreflang` for you.
- **RTL support**: one locale should not be harder to use than another.
- **Contextual translations**: reduce friction to contribute to the translation effort.
- **Anchor links**: linking should not break when you localize headings.
- **Advanced configuration options**: customize route paths, file-system paths.
### i18n non-goals
We don't provide support for:
- **Automatic locale detection**: opinionated, and best done on the [server](../deployment.mdx).
- **Translation SaaS software**: you are responsible to understand the external tools of your choice.
- **Translation of slugs**: technically complicated, little SEO value.
## Translation workflow
### Overview
Overview of the workflow to create a translated Docusaurus website:
- **Configure**: declare the default locale and alternative locales in `docusaurus.config.js`.
- **Translate**: put the translation files at the correct file-system location.
- **Deploy**: build and deploy your site using a single or multi-domain strategy.
### Translation files
You will have to work with 2 kind of translation files.
#### Markdown files
This is the main content of your Docusaurus website.
Markdown and MDX documents are translated as a whole, to fully preserve the translation context, instead of splitting each sentance as a separate string.
#### JSON files
JSON is used to translate:
- your React code: using the `<Translate>` component
- your theme: the navbar, footer...
- your plugins: the docs sidebar category labels...
The JSON format used is called **Chrome i18n**:
```json
{
"myTranslationKey1": {
"message": "Translated message 1",
"description": "myTranslationKey1 is used on the homepage"
},
"myTranslationKey2": {
"message": "Translated message 2",
"description": "myTranslationKey2 is used on the FAQ page"
}
}
```
The choice was made for 2 reasons:
- **Description attribute**: to help translators with additional context.
- **Widely supported**: [Chrome extensions](https://developer.chrome.com/docs/extensions/mv2/i18n-messages/), [Crowdin](https://support.crowdin.com/file-formats/chrome-json/), [Transifex](https://docs.transifex.com/formats/chrome-json), [Phrase](https://help.phrase.com/help/chrome-json-messages), [Applanga](https://www.applanga.com/docs/formats/chrome_i18n_json)...
### Translation files location
The translation files should be created at the correct file-system location.
Each locale and plugin has its own `i18n` subfolder:
```
website/i18n/<locale>/<pluginName>/...
```
:::note
For multi-instance plugins, the path is `website/i18n/<locale>/<pluginName>-<pluginId>/...`.
:::
Translating a very simple Docusaurus site in French would lead to the following tree:
```bash
website/i18n
└── fr
├── code.json
├── docusaurus-plugin-content-blog
│   └── 2020-01-01-hello.md
├── docusaurus-plugin-content-docs
│   ├── current #
│   │   ├── doc1.md
│   │   └── doc2.mdx
│   └── current.json
└── docusaurus-theme-classic
├── footer.json
└── navbar.json
```
The JSON files are initialized with the [`docusaurus write-translations`](../cli.md#docusaurus-write-translations) CLI command.
The `code.json` file is extracted from React components using the `<Translate>` api.
:::info
Notice that the `docusaurus-plugin-content-docs` plugin has a `current` subfolder and a `current.json` file, useful for the **docs versioning feature**.
:::
Each content plugin or theme is different, and **define its own translation files location**:
- [Docs i18n](../api/plugins/plugin-content-docs.md#i18n)
- [Blog i18n](../api/plugins/plugin-content-blog.md#i18n)
- [Pages i18n](../api/plugins/plugin-content-pages.md#i18n)
- [Themes i18n](../api/themes/theme-configuration.md#i18n)

View file

@ -0,0 +1,285 @@
---
id: tutorial
title: i18n - Tutorial
sidebar_label: Tutorial
slug: /i18n/tutorial
---
This tutorial will walk you through the basis of the **Docusaurus i18n system**.
We will add **French** translations to a **newly initialized English Docusaurus website**.
Initialize a new site with `npx @docusaurus/init@latest init website classic` (like [this one](https://github.com/facebook/docusaurus/tree/master/examples/classic)).
## Configure your site
Modify `docusaurus.config.js` to add the i18n support for the French language.
### Site configuration
Use the [site i18n configuration](./../api/docusaurus.config.js.md#i18n) to declare the i18n locales:
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
localeConfigs: {
en: {
label: 'English',
},
fr: {
label: 'Français',
},
},
},
};
```
### Theme configuration
Add a **navbar item** of type `localeDropdown` so that users can select the locale they want:
```js title="docusaurus.config.js"
module.exports = {
themeConfig: {
navbar: {
items: [
// highlight-start
{
type: 'localeDropdown',
position: 'left',
},
// highlight-end
],
},
},
};
```
### Start your site
Start your localized site in dev mode, using the locale of your choice:
```bash npm2yarn
npm run start -- --locale fr
```
Your site is accessible at **`http://localhost:3000/fr/`**, but **falls back to untranslated content**.
:::caution
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
The French translations will be added in `website/i18n/fr`.
Docusaurus is modular, and each content plugin has its own subfolder.
:::note
After copying files around, restart your site with `npm run start -- --locale fr`.
Hot-reload will work better when editing existing files.
:::
### Use the translation APIs
Open the homepage, and use the [translation APIs](../docusaurus-core.md#translate):
```jsx title="src/index.js"
import React from 'react';
import Layout from '@theme/Layout';
// highlight-start
import Translate, {translate} from '@docusaurus/Translate';
// highlight-end
export default function Home() {
return (
<Layout>
<h1>
{/* highlight-start */}
<Translate description="The homepage welcome message">
Welcome to my website
</Translate>
{/* highlight-end */}
</h1>
<div>
<input
type="text"
placeholder={
// highlight-start
translate({
message: 'Hello',
description: 'The homepage input placeholder',
})
// highlight-end
}
/>
</div>
</Layout>
);
}
```
:::caution
Docusaurus provides a **very simple and lightweight translation runtime**: documentation websites generally don't need advanced i18n features.
:::
### Translate JSON files
JSON translation files are used for everything that is not contained in a Markdown document:
- React/JSX code
- Layout navbar and footer labels
- Docs sidebar category labels
- ...
Run the [write-translations](../cli.md#docusaurus-write-translations) command:
```bash npm2yarn
npm run write-translations -- --locale fr
```
It will extract and initialize the JSON translation files that you need to translate.
The homepage translations are statically extracted from React source code:
```json title="i18n/fr/code.json"
{
"Welcome to my website": {
"message": "Welcome to my website",
"description": "The homepage welcome message"
},
"Hello": {
"message": "Hello",
"description": "The homepage input placeholder"
}
}
```
Plugins and themes will also write their own **JSON translation files**, such as:
```json title="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.
### Translate Markdown files
Official Docusaurus content plugins extensively use Markdown/MDX files, and allow you to translate them.
#### Translate the docs
Copy your docs Markdown files 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
```
:::info
`current` is needed for the docs versioning feature: each docs version has its own subfolder.
:::
#### Translate the blog
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
```
#### Translate the pages
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 pages/**.md i18n/fr/docusaurus-plugin-content-pages
cp -r pages/**.mdx i18n/fr/docusaurus-plugin-content-pages
```
:::caution
We only copy `.md` and `.mdx` files, as pages React components are translated through JSON translation files already.
:::
## Deploy your site
You can choose to deploy your site under a **single domain**, or use **multiple (sub)domains**.
### Single-domain deployment
Run the following command:
```bash 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](../deployment.mdx) the `build` folder to the static hosting solution of your choice.
:::note
The Docusaurus v2 website use this strategy:
- [https://v2.docusaurus.io](https://v2.docusaurus.io)
- [https://v2.docusaurus.io/fr](https://v2.docusaurus.io/fr)
:::
### Multi-domain deployment
You can also build your site for a single locale:
```bash npm2yarn
npm run build -- --locale fr
```
Docusaurus will not add the `/fr/` url prefix.
On your [static hosting provider](../deployment.mdx):
- create one deployment per locale
- configure the appropriate build command, using the `--locale` option
- configure the (sub)domain of your choice for each deployment
:::caution
This strategy is **not possible** with Github Pages, as it is only possible to **have a single deployment**.
:::

View file

@ -13,7 +13,7 @@ We highly encourage you to **use Docusaurus v2 over Docusaurus v1**.
Most users are already using v2 ([trends](https://www.npmtrends.com/docusaurus-vs-@docusaurus/core)), including [React Native](https://reactnative.dev), [Redux](https://redux.js.org/) and [many others](/showcase). Most users are already using v2 ([trends](https://www.npmtrends.com/docusaurus-vs-@docusaurus/core)), including [React Native](https://reactnative.dev), [Redux](https://redux.js.org/) and [many others](/showcase).
**Use **Docusaurus v2** if:** **Use Docusaurus v2 if:**
- :white_check_mark: You want a modern Jamstack documentation site - :white_check_mark: You want a modern Jamstack documentation site
- :white_check_mark: You want a single-page application (SPA) with client-side routing - :white_check_mark: You want a single-page application (SPA) with client-side routing

View file

@ -29,7 +29,7 @@ Now you can start writing TypeScript theme components.
For themes that supports TypeScript theme components, you can add the `--typescript` flag to the end of swizzling command to get TypeScript source code. For example, the following command will generate `index.tsx` and `styles.module.css` into `src/theme/Footer`. For themes that supports TypeScript theme components, you can add the `--typescript` flag to the end of swizzling command to get TypeScript source code. For example, the following command will generate `index.tsx` and `styles.module.css` into `src/theme/Footer`.
```bash npm2yarn ```bash npm2yarn
npm run swizzle @docusaurus/theme-classic Footer --typescript npm run swizzle @docusaurus/theme-classic Footer -- --typescript
``` ```
At this moment, the only official Docusaurus theme that supports TypeScript theme components is `@docusaurus/theme-classic`. If you are a Docusaurus theme package author who wants to add TypeScript support, see the [Lifecycle APIs docs](./lifecycle-apis.md#gettypescriptthemepath). At this moment, the only official Docusaurus theme that supports TypeScript theme components is `@docusaurus/theme-classic`. If you are a Docusaurus theme package author who wants to add TypeScript support, see the [Lifecycle APIs docs](./lifecycle-apis.md#gettypescriptthemepath).

View file

@ -313,7 +313,16 @@ module.exports = {
}, },
], ],
}, },
// {type: 'localeDropdown', position: 'right'}, {
type: 'localeDropdown',
position: 'right',
dropdownItemsAfter: [
{
to: 'https://github.com/facebook/docusaurus/issues/3526',
label: 'Help Us Translate',
},
],
},
{ {
href: 'https://github.com/facebook/docusaurus', href: 'https://github.com/facebook/docusaurus',
position: 'right', position: 'right',

View file

@ -28,6 +28,7 @@
"netlify:test": "yarn netlify:build:deployPreview && yarn netlify dev --debug" "netlify:test": "yarn netlify:build:deployPreview && yarn netlify dev --debug"
}, },
"dependencies": { "dependencies": {
"@crowdin/cli": "^3.5.2",
"@docusaurus/core": "2.0.0-alpha.70", "@docusaurus/core": "2.0.0-alpha.70",
"@docusaurus/plugin-client-redirects": "2.0.0-alpha.70", "@docusaurus/plugin-client-redirects": "2.0.0-alpha.70",
"@docusaurus/plugin-ideal-image": "2.0.0-alpha.70", "@docusaurus/plugin-ideal-image": "2.0.0-alpha.70",

View file

@ -52,6 +52,16 @@ module.exports = {
'static-assets', 'static-assets',
'search', 'search',
'deployment', 'deployment',
{
type: 'category',
label: 'Internationalization',
items: [
'i18n/introduction',
'i18n/tutorial',
'i18n/git',
'i18n/crowdin',
],
},
], ],
}, },
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB