Как сделать сайт на Astro 7 поддерживающим 9 языков ― Перевод блога и мультиязычная архитектура
Содержание
- Стратегия мультиязычности
- Определение объёма
- Дизайн URL
- Реализация основы i18n
- Конфигурация i18n в Astro
- Утилиты перевода
- Паттерн View Component
- Мультиязычная поддержка контента блога
- Структура каталогов
- Утилиты разрешения контента
- Правила файлов перевода
- Рабочий процесс перевода
- Мультиязычные View Components
- Реализация BlogPostPage
- Реализация страниц-списков
- Особенности сборки
- Экранирование во frontmatter YAML
- Фильтрация маршрутов OG-изображений
- Мультиязычная поддержка Pages CMS
- UI переключения языка
- Мультиязычное отображение тегов
- Мультиязычные данные авторов
- SEO для мультиязычного сайта
- Поддержка hreflang в карте сайта
- Поддержка языка в структурированных данных JSON-LD
- Мультиязычные RSS-ленты
- Итоги

Мы расширили официальный сайт Acecore с японского языка до поддержки 9 языков. При первом внедрении 21 статья была переведена на 8 языков, что дало 168 файлов. На 29 июля 2026 года репозиторий содержит 29 японских статей и 208 переводов — всего 237 файлов статей; сборка создаёт 652 страницы. Локализованный URL публикуется только при наличии файла перевода.
Стратегия мультиязычности
Определение объёма
Мы реализовали мультиязычную поддержку в три этапа:
- Основа i18n: Настройка встроенной маршрутизации i18n в Astro, утилиты перевода и JSON-файлы переводов для 9 языков
- Перевод текстов UI: Тексты компонентов в шапке, подвале, боковой панели и на всех страницах
- Перевод статей блога: При первом внедрении 21 статья переведена на 8 языков (сгенерировано 168 файлов)
Дизайн URL
Мы использовали prefixDefaultLocale: false в Astro, обслуживая японский на корневом пути (/blog/...), а другие языки с префиксами (/en/blog/..., /zh-cn/blog/... и т.д.).
# Японский (по умолчанию)
/blog/astro-performance-tuning/
# Английский
/en/blog/astro-performance-tuning/
# Упрощённый китайский
/zh-cn/blog/astro-performance-tuning/
Использование одного и того же slug для всех языков упрощает сопоставление URL при переключении языка.
Реализация основы i18n
Конфигурация i18n в Astro
Маршрутизация i18n настраивается в astro.config.mjs.
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'ja',
locales: ['ja', 'en', 'zh-cn', 'es', 'pt', 'fr', 'ko', 'de', 'ru'],
routing: {
prefixDefaultLocale: false,
},
},
})
Утилиты перевода
Файлы конфигурации, утилитные функции и JSON-файлы переводов объединены в src/i18n/.
// src/i18n/utils.ts
export function t(locale: Locale, key: string): string {
return translations[locale]?.[key] ?? translations[defaultLocale][key] ?? key
}
Файлы переводов хранятся в формате JSON в src/i18n/locales/, управляя примерно 100 ключами для навигации, подвала, UI блога и метаданных.
Паттерн View Component
Реализация страниц использует паттерн View Component. Вёрстка и логика централизованы в src/views/, а файлы маршрутов (src/pages/) — это лёгкие обёртки, которые просто передают locale.
---
// src/pages/[locale]/about.astro (файл маршрута)
import AboutPage from '../../views/AboutPage.astro'
const { locale } = Astro.params
---
<AboutPage locale={locale} />
Этот дизайн устраняет дублирование логики между японским маршрутом (/about) и мультиязычными маршрутами (/en/about).
Мультиязычная поддержка контента блога
Структура каталогов
Переведённые статьи размещаются в подкаталогах с кодом языка. Glob-загрузчик Astro автоматически обнаруживает их рекурсивно с паттерном **/*.md.
src/content/blog/
astro-performance-tuning.md # Японский (базовый)
website-renewal.md
en/
astro-performance-tuning.md # Английская версия
website-renewal.md
zh-cn/
astro-performance-tuning.md # Версия на упрощённом китайском
website-renewal.md
es/
...
Утилиты разрешения контента
В src/utils/blog-i18n.ts реализованы 3 функции.
// Определить, является ли пост базовой статьёй (нет слэша в ID = базовая)
export function isBasePost(post: CollectionEntry<'blog'>): boolean {
return !post.id.includes('/')
}
// Удалить префикс locale из ID для получения базового slug
export function getBaseSlug(postId: string): string {
const idx = postId.indexOf('/')
return idx !== -1 ? postId.slice(idx + 1) : postId
}
// Получить локализованную версию базовой статьи (фолбэк на оригинал)
export function localizePost(
post: CollectionEntry<'blog'>,
allPosts: CollectionEntry<'blog'>[],
locale: Locale,
): CollectionEntry<'blog'> {
if (locale === defaultLocale) return post
return allPosts.find((p) => p.id === `${locale}/${post.id}`) ?? post
}
localizePost() по-прежнему возвращает исходную статью как защитный фолбэк, но публичные маршруты и списки используют isPostAvailableInLocale() и фильтры, выбирая только существующие переводы. Для отсутствующего перевода локализованный URL не создаётся.
Ключевой момент — не изменять существующую схему коллекции контента. Glob-загрузчик Astro автоматически распознаёт файлы в подкаталогах с ID вида en/astro-performance-tuning, поэтому изменения конфигурации не требуются.
Правила файлов перевода
Файлы перевода генерировались по следующим правилам:
- Ключи frontmatter остаются на английском (
title,description,dateи т.д.) - Значения тегов сохраняются на японском (
['技術', 'Astro']и т.д.) - URL, пути к изображениям, блоки кода и HTML не изменяются
- Дата и автор остаются без изменений
- Текст тела и текстовые значения frontmatter (title, description, callout, FAQ и т.д.) переводятся
Рабочий процесс перевода
Процесс перевода проходит в следующем порядке:
- Создание английского как промежуточного языка: Перевод с японского оригинала на английский
- Перевод с английского на каждый язык: Расширение с английского на 7 языков
- Пакетная обработка: Обработка 5–6 статей за раз с GitHub Copilot
Двухэтапный перевод (японский → английский → целевые языки) снижает разброс качества. Путь через английский как промежуточный язык даёт более стабильное качество, чем прямой перевод с японского на каждый язык.
Мультиязычные View Components
Реализация BlogPostPage
Страница статьи блога получает локализованную версию контента с помощью localizePost() и присваивает её переменной шаблона.
---
// src/views/BlogPostPage.astro
const localizedPost = localizePost(basePost, allPosts, locale)
const post = localizedPost // существующие ссылки шаблона работают как есть
---
Этот подход обеспечивает мультиязычную поддержку без изменения каких-либо ссылок на post.data.title или post.body в шаблоне.
Реализация страниц-списков
Списки блога, списки тегов, списки авторов и архивные страницы фильтруют только базовые статьи с помощью isBasePost(), затем подменяют их переведёнными версиями через localizePost() при отображении.
---
const allPosts = await getCollection('blog')
const basePosts = allPosts.filter(isBasePost)
const displayPosts = basePosts.map((p) => localizePost(p, allPosts, locale))
---
Особенности сборки
Экранирование во frontmatter YAML
Французские переводы вызывали проблемы, когда апострофы (l'atelier, qu'on и т.д.) конфликтовали с одинарными кавычками YAML.
# NG: Ошибка парсинга YAML
title: 'Le métavers est plus proche qu'on ne le pense'
# OK: Переключиться на двойные кавычки
title: "Le métavers est plus proche qu'on ne le pense"
Скрипт Node.js использовался для массового исправления всех файлов. Английский текст вроде Acecore's имеет ту же проблему, поэтому тип кавычек нужно учитывать при генерации файлов перевода.
Фильтрация маршрутов OG-изображений
/blog/og/[slug].png.ts также захватывал slug переведённых статей (en/aceserver-hijacked и т.д.), вызывая ошибки параметров. Решено фильтрацией через isBasePost().
export const getStaticPaths: GetStaticPaths = async () => {
const allPosts = await getCollection('blog')
const posts = allPosts.filter(isBasePost)
return posts.map((post) => ({
params: { slug: post.id },
props: { title: post.data.title },
}))
}
Мультиязычная поддержка Pages CMS
Pages CMS (.pages.yml) работает только с файлами непосредственно в указанном каталоге path, поэтому подкаталоги переводов были зарегистрированы как отдельные коллекции.
content:
- name: blog
label: ブログ(日本語)
path: src/content/blog
- name: blog-en
label: Blog(English)
path: src/content/blog/en
- name: blog-zh-cn
label: 博客(简体中文)
path: src/content/blog/zh-cn
# ... настроено для каждого языка
Метки написаны на каждом языке, чтобы в CMS было сразу понятно, какая коллекция соответствует какому языку.
UI переключения языка
В шапку добавлен компонент LanguageSwitcher, предоставляющий UI переключения языка для десктопа и мобильных. При переключении языка пользователи переходят к соответствующей locale-версии той же страницы. При первом посещении определяется navigator.language браузера для автоматического перенаправления.
Мультиязычное отображение тегов
Теги статей сохраняют японские slug в URL, при этом переводится только отображаемое название. Это позволяет избежать сложности маршрутизации, показывая теги на родном языке пользователя.
Определения тегов централизованы в src/content/tags/{tagId}.json, каждый тег содержит поле i18n.name. Это переносит источник истины для переводов тегов из JSON-переводов в коллекцию тегов.
{
"id": "technology",
"name": "技術",
"i18n": {
"en": { "name": "Technology" },
"fr": { "name": "Technologie" }
}
}
Карточки статей, боковая панель, список тегов и детали статей обращаются к коллекции тегов для смены отображаемого названия, обеспечивая единообразное отображение всех тегов на соответствующем языке.
Мультиязычные данные авторов
Имена авторов, биографии и списки навыков также переключаются в зависимости от языка. В src/content/authors/{authorId}.json добавлено поле i18n для хранения переводов на каждый язык.
{
"id": "hatt",
"name": "ハット",
"bio": "代表取締役。Web制作・サーバー運用…",
"skills": ["TypeScript", "Astro", "..."]
"i18n": {
"en": {
"name": "Hatt",
"bio": "CEO and representative director. Web development...",
"skills": ["TypeScript", "Astro", "..."]
}
}
}
Утилита getLocalizedAuthor() получает информацию об авторе, соответствующую locale.
// src/utils/blog-i18n.ts
export function getLocalizedAuthor(author: Author, locale: Locale) {
const localized = author.i18n?.[locale]
return localized ? { ...author, ...localized } : author
}
SEO для мультиязычного сайта
Для максимизации SEO-преимуществ мультиязычной поддержки мы реализовали механизмы, позволяющие поисковым системам правильно определять и индексировать каждую языковую версию.
Поддержка hreflang в карте сайта
Опция i18n в @astrojs/sitemap используется вместе с фильтром, который проверяет наличие файлов перевода. Sitemap включает только существующие языковые версии и автоматически выводит соответствующие теги xhtml:link rel="alternate".
// astro.config.mjs
sitemap({
filter(page) {
return !isMissingLocalizedBlogPost(page)
},
i18n: {
defaultLocale: 'ja',
locales: {
ja: 'ja',
en: 'en',
'zh-cn': 'zh-CN',
es: 'es',
pt: 'pt',
fr: 'fr',
ko: 'ko',
de: 'de',
ru: 'ru',
},
},
})
Статьи, доступные на всех 9 языках, получают hreflang-кластер из 9 языков. Статья только на японском остаётся отдельной японской записью sitemap без ссылок на несуществующие локализованные URL.
Поддержка языка в структурированных данных JSON-LD
Поле inLanguage добавлено в структурированные данные BlogPosting статей, информируя поисковые системы о языке каждой статьи.
// BlogPostPage.astro (выдержка JSON-LD)
{
"@type": "BlogPosting",
"inLanguage": htmlLangMap[locale], // "ja", "en", "zh-CN" и т.д.
"headline": post.data.title,
// ...
}
Мультиязычные RSS-ленты
Помимо японского /rss.xml, генерируются RSS-ленты для каждой языковой версии (/en/rss.xml, /zh-cn/rss.xml и т.д.). Заголовки и описания лент переведены на каждый язык, тег <language> выводит коды языков, совместимые с BCP47.
// src/pages/[locale]/rss.xml.ts
export const getStaticPaths = () =>
locales
.filter((l) => l !== defaultLocale)
.map((l) => ({ params: { locale: l } }))
<link rel="alternate" type="application/rss+xml"> в BaseLayout.astro также автоматически устанавливает соответствующий locale URL RSS.
Итоги
Сейчас сайт использует встроенные возможности i18n Astro 7.1.3 для мультиязычной статической сборки.
- Основа i18n: Без префикса для японского с
prefixDefaultLocale: falseв Astro - Перевод UI: Нулевое дублирование логики благодаря паттерну View Component
- Перевод контента: Подход с подкаталогами без изменений схемы
- Перевод тегов: Японские slug в URL, отображаемые названия переведены по языкам
- Перевод данных авторов: Bio и навыки переключаются по языкам
- SEO: Hreflang в карте сайта,
inLanguageв JSON-LD, мультиязычные RSS-ленты - Отсутствующие переводы: Локализованный URL не создаётся; японская статья остаётся по исходному URL
- Поддержка CMS: Статьи каждого языка редактируются индивидуально в Pages CMS
Файлы перевода будут и дальше добавляться постепенно. Пока перевода нет, публикуется только японская статья; добавление файла locale включает URL, запись sitemap и связь hreflang для этого языка.
Мультиязычный рабочий процесс
Основа i18n
Настройка встроенной маршрутизации i18n в Astro и утилит перевода.
Перевод текстов UI
Перевод отображаемых текстов в шапке, подвале и всех компонентах.
Перевод статей блога
Генерация 168 файлов при первом внедрении (21 статья × 8 языков).
CMS и верификация сборки
Мультиязычная настройка Pages CMS и верификация сборки всех страниц.
Только японский
- Только 1 язык (японский)
- 23 статей в блоге
- 523 сгенерированных страницы (после мультиязычной поддержки UI)
- Pages CMS с 1 коллекцией блога
- Теги и данные авторов только на японском
- 1 RSS-лента
9 языков (первое внедрение)
- Японский + 8 языков (en, zh-cn, es, pt, fr, ko, de, ru)
- 23 статей + 168 переводов = 191 всего
- 621 страница сгенерирована при первом внедрении
- Pages CMS с 9 коллекциями по языкам
- 25 тегов и данные авторов переведены на каждый язык
- Мультиязычные RSS-ленты (9 языков)
Почему были выбраны 9 языков?
Как обеспечивается качество перевода?
Что происходит, когда переведённая статья не существует?
Нужно ли переводить при добавлении новой статьи?
Комментарии
Gui
Генеральный директор Acecore. Руководит бизнес-системами, вебом, базами данных и инфраструктурой, качеством и внедрением ИИ от формулирования бизнес-задач до проектирования, запуска и дальнейшего улучшения. Опирается на практическую экспертизу C#/.NET и также учитывает PHP/JavaScript, SQL Server/PostgreSQL/MySQL и Linux/Windows Server, проектируя требования, технологический выбор, стандарты качества и GitHub-ориентированные процессы разработки как единую систему. Встраивает генеративный ИИ в процессы разработки, проверки и организации информации как практическую основу, помогающую небольшим командам быстрее и надежнее достигать результата.
Хотите узнать больше о наших услугах?
Мы обеспечиваем комплексную поддержку в разработке систем, веб-дизайне, серверном администрировании и графическом дизайне.
Похожие статьи
Как развивать сайт на Astro + Cloudflare по функциям7 июня 2026 г. в 19:00
Как добавить комментарии в Astro-блог только на Cloudflare7 июня 2026 г. в 18:00
Руководство по повышению качества сайта на Astro, продолжение - финальные настройки для 100 баллов по всем категориям PageSpeed Insights29 марта 2026 г. в 02:30