Перейти к содержимому

Многоязычность и переключение языков

В EpoCanvas Docs встроена полноценная многоязычность на уровне контента: тексты интерфейса — панель навигации, боковая панель, подсказки поиска и другие — и основной текст каждого документа доступны в 10 отдельных языковых версиях. Выбрав язык в правом верхнем углу страницы, читатель переходит к той же статье на выбранном языке: переключаются и основной текст, и интерфейс.


Список поддерживаемых языков

Сейчас сайт предоставляет 10 языков, из которых упрощённый китайский — язык по умолчанию:

ЯзыкНазвание языкаПрефикс URLАббревиатура в интерфейсе
简体中文Упрощённый китайский (по умолчанию)без префиксаZH
繁體中文Традиционный китайский/zh-tw/TW
Englishанглийский/en/EN
日本語японский/ja/JA
한국어корейский/ko/KO
Españolиспанский/es/ES
Françaisфранцузский/fr/FR
Deutschнемецкий/de/DE
Русскийрусский/ru/RU
Portuguêsпортугальский/pt/PT

Список языков определён в SUPPORTED_LANGUAGES в src/utils/i18n.ts, а языковые маршруты зарегистрированы в конфигурации locales в astro.config.mjs; эти два места должны оставаться согласованными.


Как переключить язык

  1. Найдите справа в верхней панели навигации кнопку языка (на ней показана аббревиатура текущего языка, например ZH);
  2. Кликните кнопку — в выпадающем меню перечислены все 10 языков;
  3. Кликните нужный язык (например, English или 日本語);
  4. Браузер перейдёт к версии текущей статьи на выбранном языке: навигация, боковая панель, оглавление и основной текст одновременно переключатся на этот язык.

Реальный вид с аннотациями при раскрытии выпадающего меню переключения языков: ① кнопка языка ② список языков ③ текущий язык

Рисунок: реальный вид с аннотациями после клика по кнопке языка на странице «Многоязычность и переключение языков». ① кнопка переключения языка справа в верхней панели; ② раскрытое выпадающее меню со всеми 10 языками (список прокручивается); ③ текущий активный язык (отмечен галочкой).

Схема механизма переключения языков

Рисунок: порядок выполнения переключения языка. При сборке для каждого языка генерируется свой набор статических страниц; клик по кнопке языка ведёт к той же статье на соответствующем языке; если у отдельных страниц перевод отсутствует, автоматически показывается резервный вариант на китайском.


Правила URL

У каждого языка свой префикс URL; у одной и той же статьи на разных языках различается только префикс:

/canvas/i18n/ → упрощённый китайский (язык по умолчанию, без префикса)
/en/canvas/i18n/ → английский
/ja/canvas/i18n/ → японский
/zh-tw/canvas/i18n/ → традиционный китайский
  • Информация о языке закодирована прямо в URL: страницу после переключения можно сразу добавлять в закладки и делиться ею, а поисковые системы индексируют языки по отдельности;
  • Внутренние ссылки на странице (навигация, боковая панель, рекомендательные ссылки в основном тексте) автоматически получают префикс текущего языка, поэтому при непрерывном чтении в рамках одного языка читатель не возвращается на китайские страницы;
  • Каждая языковая версия — отдельная статическая страница, сгенерированная на этапе сборки; атрибут <html lang> страницы соответствует языку, поэтому скринридеры и средства перевода распознают его корректно.

Что происходит при отсутствии перевода

Отсутствующие переводы система обрабатывает по стратегии «резервный механизм на языке по умолчанию»: если у документа ещё нет перевода на какой-то язык, страница этого языка показывает содержимое на упрощённом китайском по умолчанию, тогда как остальная часть страницы (навигация, боковая панель, тексты интерфейса) остаётся на выбранном языке; ошибки 404 или пустые страницы при этом не появляются.

Преимущество этой стратегии: при добавлении нового документа не нужно ждать готовности переводов на все языки — сначала публикуется китайская версия, а после дополнения переводов достаточно выполнить сборку заново.


Как это реализовано

Многоязычность обеспечивается механизмами интернационализации Astro и Starlight. Процесс сборки выглядит так:

  1. Каталоги контента разделены по языкам: документы языка по умолчанию лежат в src/content/docs/ (упрощённый китайский), остальные языки — в src/content/docs/<язык>/; структура каталогов полностью соответствует китайской, например английская версия «Многоязычность и переключение языков» — это src/content/docs/en/canvas/i18n.md;
  2. При сборке генерируются страницы всех языков: astro build читает конфигурацию locales из astro.config.mjs и создаёт полный набор статических страниц для каждого языка;
  3. Интерфейсные строки рендерятся по языкам: тексты интерфейса — навигация в верхней панели, подсказки поиска, заголовки оглавления и т.д. — компоненты выводят на этапе сборки через getTranslation(key, lang) сразу на нужном языке; скриптов подмены во время выполнения на странице нет;
  4. Внутренние ссылки в тексте локализуются автоматически: плагин rehypeLocalizeInternalLinks в конвейере сборки автоматически добавляет префикс текущего языка к внутренним ссылкам /canvas/xxx в переведённом тексте;
  5. Полнотекстовый поиск изолирован по языкам: Pagefind строит отдельный индекс для каждого языка, поэтому поиск на английской странице возвращает только английские результаты.
  6. Изображения в тексте подставляются по языку: плагин rehypeLocalizeDiagramImages переписывает в переводе /images/canvas/<файл> на /images/canvas/<язык>/<файл>, языковые версии лежат в public/images/canvas/<язык>/; если файла с тем же именем нет, остаётся оригинал — новое изображение не обязательно сразу готовить на все 10 языков;
  7. Выноски и сноски следуют языку страницы: rehypeLocalizeAsides и rehypeLocalizeFootnotes подменяют заголовки выносок и текст сносок по каталогу языка исходника — Starlight определяет язык на этапе remark по абсолютному пути, а локальная сборка передаёт относительный, без этого всё откатывалось бы к китайскому;
  8. hreflang и страница 404: после сборки hreflangXDefault() добавляет на каждую страницу ссылку hreflang="x-default" на упрощённую китайскую версию, а src/pages/404.astro использует тот же словарь, чтобы вывести текст по языку браузера; меню языков на ней ведёт на главную каждого языка.

Для разработчиков: как добавить новый язык

Пример: добавление итальянского языка (Italiano, код it):

  1. Зарегистрируйте языковой маршрут: добавьте в locales в astro.config.mjs запись it: { label: 'Italiano', lang: 'it' };
  2. Дополните тексты боковой панели: в SIDEBAR_I18N в том же файле добавьте переводы it для всех групп и пунктов;
  3. Дополните интерфейсные строки: зарегистрируйте язык в SUPPORTED_LANGUAGES в src/utils/i18n.ts (код, название, аббревиатура, префикс URL) и добавьте интерфейсные строки этого языка в UI_TRANSLATIONS;
  4. Переведите содержимое документов: создайте каталог src/content/docs/it/ и разместите в нём переводы, повторяя структуру каталогов китайской версии. Непереведённые страницы сначала показываются на китайском (резервный вариант), их можно дополнять постепенно; Если нужно перевести и снимки интерфейса, создайте каталог public/images/canvas/it/ с файлами тех же имён — страницы подхватят их сами.
  5. Пересоберите сайт: выполните pnpm run build — будут сгенерированы страницы нового языка и поисковый индекс. После сборки проверьте структуру перевода и ссылки на изображения командами node scripts/audit-i18n.mjs и node scripts/check-localized-images.mjs; в CI оба checks обязательны.

Для разработчиков: как добавить или изменить интерфейсные строки

Добавляя новый элемент интерфейса, дополните словарь UI_TRANSLATIONS в src/utils/i18n.ts:

src/utils/i18n.ts
// Словарь организован в два уровня «язык → ключ строки», в том же порядке, в котором его читает getTranslation
export const UI_TRANSLATIONS: Record<string, Record<string, string>> = {
// Шаг 1: добавьте строки в язык по умолчанию zh-CN (при их отсутствии интерфейс показывает имя ключа)
'zh-CN': {
'nav.plugins': '插件生态',
// ...остальные существующие строки
},
// Шаг 2: добавьте перевод с тем же ключом для других языков; языки без перевода автоматически откатываются к китайскому
en: {
'nav.plugins': 'Plugins',
},
ja: {
'nav.plugins': 'プラグイン',
},
};

Затем вызовите getTranslation в компоненте, передав текущий язык:

---
import { getLangFromLocale, getTranslation } from '../../utils/i18n';
const lang = getLangFromLocale(Astro.currentLocale);
---
<a href={localizedHref('/plugins/', lang)}>{getTranslation('nav.plugins', lang)}</a>

Интерфейсные строки при сборке рендерятся как статический текст на соответствующем языке, без накладных расходов во время выполнения.