Цей сайт — dimaver6.com — починався на next-i18next. Я мігрував його на next-intl за один вихідний. Міграція була прямолінійною. Рішення мігрувати — ні.
Чому переключився
1. Підтримка App Router
next-i18next був зібраний для Pages Router. Коли Next.js перейшов на App Router, next-i18next додав підтримку, але вона відчувалася як прикручена збоку. Server Components потребували обхідних шляхів.
next-intl був спроєктований для App Router від початку. getTranslations() працює природно в Server Components, лейаутах і generateMetadata. Без обхідних шляхів, без прокидування пропсів.
2. Типобезпечність
next-intl підтримує типізовані ключі перекладів з коробки. Якщо я посилаюся на t('Navigation.bog') замість t('Navigation.blog') — TypeScript ловить на етапі збірки. next-i18next потребував додаткового налаштування для того самого рівня типобезпечності.
3. Інтеграція роутингу
next-intl включає шар роутингу (createNavigation, createMiddleware), що обробляє визначення локалі, роутинг із префіксами та генерацію hreflang. З next-i18next я керував роутингом локалей вручну — кастомний middleware, ручні hreflang-теги, окремий скрипт визначення локалі.
next-intl згорнув 4 файли кастомного коду роутингу в 2 файли конфігурації.
Міграція
Крок 1: Встановлення та налаштування (30 хвилин)
Створив i18n/config.ts, i18n/request.ts та i18n/navigation.ts.
Крок 2: Формат файлів перекладів (0 хвилин)
Обидві бібліотеки використовують JSON з однаковою структурою. Файли messages/*.json запрацювали як є.
Крок 3: Заміна middleware (45 хвилин)
60 рядків кастомного middleware → 6 рядків з createMiddleware від next-intl.
Крок 4: Оновлення Server Components (2 години)
Найбільша частина. Кожен Server Component із перекладами потребував оновлення:
// До: useTranslation (клієнтський хук)
// Після: getTranslations (асинхронна серверна функція)
import { getTranslations } from 'next-intl/server';
export default async function MyPage() {
const t = await getTranslations('MyNamespace');
return <h1>{t('title')}</h1>;
}
~25 компонентів. Більшість — механічний find-and-replace.
Крок 5: Оновлення Client Components (1 година)
~10 компонентів: useTranslation → useTranslations.
Крок 6: Оновлення Link (30 хвилин)
Заміна ручних локалізованих URL на Link з @/i18n/navigation. ~15 місць.
Крок 7: Оновлення метаданих (30 хвилин)
generateMetadata з getTranslations — чисто та природно.
Загальний час міграції: ~5 годин
Що зламалося
1. Регістр неймспейсів
next-i18next використовує lowercase (common). Я переключився на PascalCase (Common). Пропустив два місця — порожні рядки в російській локалі. Зловив TypeScript.
2. Синтаксис інтерполяції
Обидві використовують {variable}, але next-i18next також підтримував {{variable}}. Три рядки з подвійними фігурними дужками рендерилися як літеральний текст. Швидкий regex-фікс.
3. Правила множини
next-i18next: key_one, key_other. next-intl: ICU формат {count, plural, one {# item} other {# items}}. 4 рядки з множиною. 15 хвилин на конвертацію.
4. Перемикач мови
Компонент використовував i18next.changeLanguage(). next-intl перемикає через навігацію (редирект з іншим префіксом локалі). Потрібен був рерайт компонента.
Чесне порівняння
| next-intl | next-i18next | |
|---|---|---|
| App Router | Нативна, першокласна підтримка | Додана пізніше, відчувається прикрученою |
| Server Components | getTranslations() — чистий async | Потребує обхідних шляхів |
| Типобезпечність | Вбудована | Можлива, але потребує ручного налаштування |
| Роутинг | Включений (middleware, Link, redirect) | BYO або обмежений роутинг |
| Формат перекладів | JSON | JSON (формат i18next) |
| Множина | ICU MessageFormat | Формат i18next (_one, _other) |
| Екосистема | Менша, специфічна для Next.js | Величезна (екосистема i18next) |
Коли обирати next-intl
- Новий Next.js App Router проєкт
- Хочеш типобезпечні переклади без зайвого налаштування
- Хочеш роутинг локалей з коробки
- Команда фокусується на Next.js
Коли обирати next-i18next
- Існуюча екосистема i18next (спільні переклади з React Native)
- Pages Router без планів міграції
- Перекладачі використовують інструменти з експортом у формат i18next
- Потрібні плагіни i18next (завантаження з CMS)
Чи було воно того варте?
Так. 5 годин міграції, а покращення DX реальне — типобезпечні ключі, чистіші патерни в Server Components, жодного кастомного роутингу. Я написав про повну i18n-архітектуру включаючи роутинг, SEO та RSC-патерни.
Збираєш мультимовний Next.js і не впевнений, який i18n-підхід обрати? Напиши — я відвантажив обидва і можу зекономити тобі податок на неправильну бібліотеку.