Назад в блог
Terminal output showing a translation script writing de.json and fr.json, alongside a folder tree and a browser rendering the German locale route.

Как перевести приложение Next.js с помощью PolyLingo менее чем за 30 минут

By Robert

Как перевести приложение Next.js с PolyLingo менее чем за 30 минут

К концу этого руководства у вас будет рабочий многоязычный проект Next.js App Router: строки извлечены в messages/en.json, переведены локальные файлы для каждого нужного языка, next-intl подаёт правильный файл для каждого маршрута, и один Node-скрипт, который вы можете запускать заново при изменении контента.

Не нужно регистрироваться на платформе перевода. Нет фиксированных платежей за язык. Один вызов API обрабатывает все ваши целевые языки одновременно.

Что вам понадобится:

  • Проект Next.js с использованием App Router (Next.js 14 или 15)
  • Node.js 18 или новее
  • Бесплатный аккаунт PolyLingo и API-ключ

Шаг 1: Получите ваш API-ключ PolyLingo (5 минут)

Создайте бесплатный аккаунт на usepolylingo.com. Бесплатный тариф включает 100 000 токенов в месяц, чего достаточно, чтобы несколько раз перевести файл локализации среднего размера на 10+ языков.

После входа перейдите в API keys на панели и создайте ключ. Полное значение ключа показывается только один раз, поэтому сразу скопируйте его.

Добавьте его в проект как переменную окружения. Никогда не коммитьте его в систему контроля версий и не раскрывайте в клиентском коде:

# .env.local
POLYLINGO_API_KEY="pl_your_key_here"

Проверьте доступность API перед дальнейшими действиями:

curl -sS "https://api.usepolylingo.com/v1/health"

Вы должны получить небольшой JSON с "status": "ok".


Шаг 2: Установите next-intl и настройте маршрутизацию (10 минут)

Установите библиотеку:

npm install next-intl

Создайте файл i18n.ts в корне проекта. Он сообщает next-intl, какие локали вы поддерживаете и как загружать нужный файл сообщений для каждого запроса:

// i18n.ts
import { getRequestConfig } from 'next-intl/server'

export const locales = ['en', 'de', 'fr'] as const
export type Locale = (typeof locales)[number]
export const defaultLocale: Locale = 'en'

export default getRequestConfig(async ({ requestLocale }) => {
  let locale = await requestLocale
  if (!locale || !locales.includes(locale as Locale)) {
    locale = defaultLocale
  }
  return {
    locale,
    messages: (await import(`./messages/${locale}.json`)).default,
  }
})

Добавьте middleware, чтобы префиксировать маршруты локалью:

// middleware.ts
import createMiddleware from 'next-intl/middleware'
import { locales, defaultLocale } from './i18n'

export default createMiddleware({
  locales: [...locales],
  defaultLocale,
  localePrefix: 'as-needed',
})

export const config = {
  matcher: ['/((?!api|_next|.*\\..*).*)'],
}

Переместите файлы страниц в app/[locale]/. Обновите корневой layout, чтобы принимать параметр locale и оборачивать дочерние элементы в NextIntlClientProvider:

// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl'
import { getMessages } from 'next-intl/server'

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Promise<{ locale: string }>
}) {
  const { locale } = await params
  const messages = await getMessages()
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  )
}

Шаг 3: Извлеките строки в JSON-файл сообщений (10 минут)

Создайте папку messages/ в корне проекта. Добавьте файл en.json с исходными строками. next-intl использует вложенную структуру ключей:

{
  "Home": {
    "title": "Welcome",
    "cta": "Get started"
  }
}

Обновите страницы, чтобы использовать useTranslations вместо жестко закодированных строк:

// app/[locale]/page.tsx
import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations('Home')
  return (
    <main>
      <h1>{t('title')}</h1>
      <button type="button">{t('cta')}</button>
    </main>
  )
}

Теперь напишите скрипт перевода. Он читает messages/en.json, отправляет его в API PolyLingo с format: "json" и записывает один выходной файл на каждый целевой локаль. Флаг format: "json" сообщает API сохранить структуру ключей и переводить только строковые значения — вложенные ключи, массивы и нестроковые типы остаются без изменений.

// scripts/translate-messages.mjs
// Запуск: node scripts/translate-messages.mjs

import fs from 'node:fs'
import path from 'node:path'

const API_KEY = process.env.POLYLINGO_API_KEY
const API_URL = (process.env.POLYLINGO_API_URL || 'https://api.usepolylingo.com/v1').replace(/\/$/, '')
const TARGETS = ['de', 'fr'] // расширьте этот массив для добавления локалей

const enPath = path.join('messages', 'en.json')
const en = JSON.parse(fs.readFileSync(enPath, 'utf8'))

const res = await fetch(`${API_URL}/translate`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    content: JSON.stringify(en),
    format: 'json',
    targets: TARGETS,
    model: 'standard',
  }),
})

if (!res.ok) {
  const err = await res.text()
  throw new Error(`PolyLingo ${res.status}: ${err}`)
}

const { translations } = await res.json()

for (const locale of TARGETS) {
  const out = path.join('messages', `${locale}.json`)
  fs.writeFileSync(out, JSON.stringify(JSON.parse(translations[locale]), null, 2) + '\n')
  console.log('Wrote', out)
}

Запустите:

node scripts/translate-messages.mjs

Вы увидите вывод:

Wrote messages/de.json
Wrote messages/fr.json

Откройте эти файлы и проверьте. Ключи будут идентичны en.json. Изменятся только строковые значения.


Шаг 4: Быстрое тестирование маршрутов (5 минут)

Запустите сервер разработки:

npm run dev

Посетите http://localhost:3000 и http://localhost:3000/de. Заголовок и кнопка должны отображаться на английском и немецком соответственно. Добавьте больше локалей, расширив массив TARGETS в скрипте и массив locales в i18n.ts, затем запустите скрипт снова.

Проверьте использование токенов в панели PolyLingo в разделе Usage. Для небольшого файла локализации, переведённого на два языка, вы использовали несколько сотен токенов из месячного лимита.


Что делать дальше

Добавьте больше локалей. Скрипт отправляет один запрос независимо от количества элементов в TARGETS. Добавление японского, испанского и арабского стоит один вызов API, а не три.

Интегрируйте в CI. Добавьте POLYLINGO_API_KEY как секрет репозитория в GitHub Actions и запускайте скрипт в вашем пайплайне сборки. Файлы локалей будут автоматически синхронизироваться при изменении en.json.

Переводите другие форматы. Тот же шаблон скрипта работает для Markdown-документации (format: "markdown") и HTML-шаблонов email (format: "html"). API сохраняет структуру во всех случаях.

Используйте batch endpoint для больших проектов. Если у вас несколько отдельных JSON-файлов (например, по функциональным областям), POST /translate/batch принимает до 100 элементов в одном запросе, каждый с собственным id и format.


Попробуйте бесплатно

Бесплатный тариф PolyLingo включает 100 000 токенов в месяц. Кредитная карта не требуется.

curl -sS -X POST "https://api.usepolylingo.com/v1/translate" \
  -H "Authorization: Bearer $POLYLINGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "{\"Home\":{\"title\":\"Welcome\",\"cta\":\"Get started\"}}",
    "format": "json",
    "targets": ["de", "fr", "es", "ja"]
  }'

Получите ваш API-ключ