AstroMiddleCoding

Как реализовать i18n в Astro?

Astro 4.0+ имеет встроенный i18n-роутинг через i18n в конфиге. Для переводов контента используют Content Collections или сторонние библиотеки (i18next, paraglide).

Встроенный i18n-роутинг в Astro 4

Начиная с Astro 4.0, в ядро встроена поддержка интернационализации. Настройка в astro.config.mjs:

import { defineConfig } from 'astro/config';

export default defineConfig({
  i18n: {
    defaultLocale: 'en',
    locales: ['en', 'ru', 'de'],
    routing: {
      prefixDefaultLocale: false, // /about (не /en/about)
    },
  },
});

Структура директорий

src/pages/
  index.astro          # /  (дефолтный язык)
  about.astro          # /about
  ru/
    index.astro        # /ru/
    about.astro        # /ru/about
  de/
    index.astro        # /de/
    about.astro        # /de/about

Хелперы для i18n

---
import { getRelativeLocaleUrl, getAbsoluteLocaleUrl } from 'astro:i18n';

// Генерация URL для конкретной локали
const ruUrl = getRelativeLocaleUrl('ru', '/about');  // '/ru/about'
const enUrl = getRelativeLocaleUrl('en', '/about');  // '/about'

// Текущая локаль
const currentLocale = Astro.currentLocale; // 'ru' | 'en' | 'de'
---

<nav>
  <a href={enUrl}>English</a>
  <a href={ruUrl}>Русский</a>
</nav>

Переводы строк с i18next

npm install i18next
// src/i18n/index.ts
import i18next from 'i18next';

const resources = {
  en: {
    translation: {
      hero_title: 'Find your dream job',
      hero_cta: 'Browse jobs',
    },
  },
  ru: {
    translation: {
      hero_title: 'Найдите работу мечты',
      hero_cta: 'Смотреть вакансии',
    },
  },
};

export function getTranslations(locale: string) {
  i18next.init({ lng: locale, resources });
  return i18next.t.bind(i18next);
}
---
import { getTranslations } from '../../i18n';
const t = getTranslations(Astro.currentLocale ?? 'en');
---

<h1>{t('hero_title')}</h1>
<a href="/jobs">{t('hero_cta')}</a>

Переводы контента через Content Collections

src/content/
  blog/
    en/
      my-post.md
    ru/
      my-post.md
---
import { getCollection } from 'astro:content';

const locale = Astro.currentLocale ?? 'en';
const posts = await getCollection('blog', (entry) =>
  entry.id.startsWith(`${locale}/`)
);
---

Переадресация на локаль пользователя

// src/middleware.ts
import { defineMiddleware } from 'astro:middleware';

export const onRequest = defineMiddleware(({ request, redirect }, next) => {
  const url = new URL(request.url);
  if (url.pathname === '/') {
    const acceptLang = request.headers.get('accept-language') ?? '';
    const preferred = acceptLang.split(',')[0].split('-')[0];
    if (preferred === 'ru') {
      return redirect('/ru/', 302);
    }
  }
  return next();
});

Подводные камни

  • Встроенный i18n-роутинг не управляет переводами строк — это только маршрутизация. Для переводов нужен отдельный механизм.
  • prefixDefaultLocale: false может конфликтовать с SEO-стратегией: Google рекомендует явные hreflang-теги с полными URL для всех локалей.
  • При статической сборке каждая локаль генерирует полный набор HTML-файлов — сайт с 5 языками и 100 страницами создаёт 500 HTML-файлов.
  • Автоматический detect по Accept-Language в статическом режиме невозможен без middleware (SSR-режим обязателен).
  • Параметр Astro.currentLocale возвращает undefined на страницах вне языковой директории — всегда добавляйте фолбэк.
  • i18next на сервере — синглтон; параллельные запросы с разными локалями могут конфликтовать. Используйте createInstance() вместо глобального инстанса.
  • Отсутствие hreflang-мета-тегов снижает SEO для мультиязычных сайтов — добавляйте их явно в <head>.

Common mistakes

  • Не добавлять hreflang и терять SEO-связь между локалями.
  • Делать словари как Record<string, string> без типизации и пропускать ключи.
  • Игнорировать RTL и сломать вёрстку для ar/he.

What the interviewer is testing

  • Знает конфигурацию i18n в Astro.
  • Использует getRelativeLocaleUrl и getLocaleByPath.
  • Помнит про fallback и hreflang.

Sources

Related topics