Multi-language support including locale configuration, translation files, and runtime i18n state.
Main internationalization configuration for multi-language sites.
/**
* Main i18n configuration for multi-language support
*/
interface I18nConfig {
/**
* Default locale that:
* - Has no locale prefix in URLs
* - Is used by `docusaurus start` without --locale option
* - Gets the `<link hrefLang="x-default">` tag
*/
defaultLocale: string;
/**
* Root folder for all locale folders
* Can be absolute or relative to config file
*/
path: string;
/** List of all locales. Must include defaultLocale. */
locales: [string, ...string[]];
/** Individual configuration for each locale */
localeConfigs: {[locale: string]: Partial<I18nLocaleConfig>};
}Usage Example:
import type { I18nConfig } from '@docusaurus/types';
const i18nConfig: I18nConfig = {
defaultLocale: 'en',
path: 'i18n',
locales: ['en', 'fr', 'ja'],
localeConfigs: {
en: {
label: 'English',
direction: 'ltr',
htmlLang: 'en-US'
},
fr: {
label: 'Français',
direction: 'ltr',
htmlLang: 'fr-FR'
},
ja: {
label: '日本語',
direction: 'ltr',
htmlLang: 'ja-JP'
}
}
};Configuration for individual locales including display settings and paths.
/**
* Configuration for an individual locale
*/
interface I18nLocaleConfig {
/** Display label for this locale in dropdowns */
label: string;
/**
* BCP 47 language tag for:
* - <html lang="..."> attribute
* - <link hreflang="..."> tags
*/
htmlLang: string;
/** Text direction for CSS and HTML attributes */
direction: 'ltr' | 'rtl';
/**
* Calendar system for date formatting
* Controls date era calculation, not display format
*/
calendar: string;
/**
* Root folder for this locale's translations
* Relative to i18n.path, defaults to locale name
*/
path: string;
}Runtime internationalization state available throughout the application.
/**
* Runtime i18n state with complete configuration
*/
interface I18n extends DeepRequired<I18nConfig> {
/** Currently active locale */
currentLocale: string;
}Usage Example:
import { useDocusaurusContext } from '@docusaurus/core';
function LocaleInfo() {
const { i18n } = useDocusaurusContext();
const currentLocaleConfig = i18n.localeConfigs[i18n.currentLocale];
return (
<div dir={currentLocaleConfig.direction}>
<p>Current locale: {i18n.currentLocale}</p>
<p>Label: {currentLocaleConfig.label}</p>
<p>HTML lang: {currentLocaleConfig.htmlLang}</p>
<p>Available locales: {i18n.locales.join(', ')}</p>
</div>
);
}Structure for managing translation files and their content.
/**
* Individual translation message with optional description
*/
interface TranslationMessage {
/** The translated text */
message: string;
/** Optional description for translators */
description?: string;
}
/**
* Content of a translation file mapping message IDs to translations
*/
interface TranslationFileContent {
[msgId: string]: TranslationMessage;
}
/**
* Abstract representation of a translation file
*/
interface TranslationFile {
/**
* File path relative to plugin's i18n directory
* Should NOT include file extension
*/
path: string;
/** Translation content */
content: TranslationFileContent;
}Usage Example:
// Example translation file content
const blogTranslations: TranslationFileContent = {
'blog.title': {
message: 'Blog',
description: 'Title for the blog section'
},
'blog.readMore': {
message: 'Read more',
description: 'Link text to read full blog post'
},
'blog.author': {
message: 'By {author}',
description: 'Author attribution with placeholder'
}
};
// Creating translation file
const translationFile: TranslationFile = {
path: 'blog', // Will be saved as blog.json
content: blogTranslations
};How plugins provide and use translations:
import type { Plugin, TranslationFile } from '@docusaurus/types';
const blogPlugin: Plugin = {
name: 'blog-plugin',
// Provide translation files
async getTranslationFiles({ content }) {
return [
{
path: 'blog',
content: {
'blog.title': {
message: 'Blog',
description: 'Blog section title'
},
'blog.posts.count': {
message: '{count} posts',
description: 'Number of posts with count placeholder'
}
}
}
];
},
// Provide default code translations
getDefaultCodeTranslationMessages() {
return {
'theme.blog.readMore': 'Read more',
'theme.blog.author': 'By {author}'
};
},
// Translate content using translation files
translateContent({ content, translationFiles }) {
// Apply translations to content
return translatedContent;
},
// Translate theme configuration
translateThemeConfig({ themeConfig, translationFiles }) {
// Apply translations to theme config
return translatedThemeConfig;
}
};Runtime code translations for use in React components.
/**
* Runtime code translations mapping message IDs to translated strings
*/
interface CodeTranslations {
[msgId: string]: string;
}Usage Example:
import { translate } from '@docusaurus/Translate';
import { useDocusaurusContext } from '@docusaurus/core';
function BlogPost({ author, readingTime }: Props) {
const { codeTranslations } = useDocusaurusContext();
// Using translate function with message ID
const readMoreText = translate({
id: 'theme.blog.readMore',
message: 'Read more',
description: 'Link to read full blog post'
});
// Direct access to code translations
const authorText = codeTranslations['theme.blog.author'];
return (
<article>
<p>{authorText.replace('{author}', author)}</p>
<p>Reading time: {readingTime} min</p>
<a href="#full-post">{readMoreText}</a>
</article>
);
}Translation files are organized by plugin and locale:
i18n/
├── en/
│ ├── docusaurus-plugin-content-docs/
│ │ ├── current/
│ │ │ └── docs.json
│ │ └── version-1.0/
│ │ └── docs.json
│ ├── docusaurus-plugin-content-blog/
│ │ └── blog.json
│ └── docusaurus-theme-classic/
│ └── theme.json
├── fr/
│ ├── docusaurus-plugin-content-docs/
│ │ └── current/
│ │ └── docs.json
│ └── docusaurus-theme-classic/
│ └── theme.json
└── ja/
└── docusaurus-theme-classic/
└── theme.jsonimport { useDocusaurusContext } from '@docusaurus/core';
function LocalizedNavigation() {
const { i18n, siteConfig } = useDocusaurusContext();
// Generate locale-aware URLs
const getLocalizedUrl = (path: string, locale?: string) => {
const targetLocale = locale || i18n.currentLocale;
const isDefaultLocale = targetLocale === i18n.defaultLocale;
const baseUrl = siteConfig.baseUrl;
const localePrefix = isDefaultLocale ? '' : `/${targetLocale}`;
return `${baseUrl}${localePrefix}${path}`;
};
return (
<nav>
{i18n.locales.map(locale => (
<a
key={locale}
href={getLocalizedUrl('/docs', locale)}
hrefLang={i18n.localeConfigs[locale].htmlLang}
>
{i18n.localeConfigs[locale].label}
</a>
))}
</nav>
);
}