# General Translation Knowledge Base: Писать собственную i18n или использовать General Translation?
URL: https://generaltranslation.com/ru/kb/general-translation-vs-in-house-i18n.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Самописной i18n (по словарю на каждый язык и небольшой вспомогательной функции t()) вполне достаточно для одного-двух языков и небольшого объёма текста. По мере роста приложения вам приходится заново реализовывать правила множественного числа, резервные варианты и синхронизацию файлов, а переводы всё равно нужно где-то брать. Библиотеки General Translation с открытым исходным кодом берут на себя рендеринг, а General Translation генерирует и обновляет переводы на основе вашего кода.
Category: Интернационализация
Last updated: 2026-10-16
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-16

Интернационализацию (i18n) можно реализовать самостоятельно: объект сообщений для каждого языка, вспомогательная функция `t()`, которая находит ключ и подставляет переменные, переключатель локали и встроенные API `Intl` для чисел и дат. Для одного-двух языков и нескольких десятков строк этого зачастую достаточно — и именно такой код обычно пишет агент для написания кода, если попросить его добавить язык. С ростом приложения разрастается всё, что окружает эту функцию: правила множественного числа, отсутствующие ключи, синхронизация всех языковых файлов с кодом и, в первую очередь, сами переводы, которые кто-то должен сделать. Библиотеки General Translation с открытым исходным кодом берут на себя ту часть работы, что приходится на время выполнения, а General Translation генерирует и обновляет переводы на основе вашего кода.

## Video

The video is narrated in English, with English captions.

[Should you build your own i18n or use General Translation?](https://caxecubb9c64cezv.public.blob.vercel-storage.com/films/kb-general-translation-vs-in-house-i18n.mp4) (43 с)

Hand-rolled i18n, a dictionary per language and a t() helper, suits one or two languages, but as an app grows you rebuild plurals, fallbacks and file syncing, and still need translations. General Translation's libraries handle rendering, and General Translation generates and updates translations from your code: write copy inside `<T>` with counts in `<Plural>`, run `npx gt translate` before each build, or move your dictionary to JSON files for the CLI.

## Когда достаточно самописной i18n

* **Никаких сторонних зависимостей.** Словарь и вспомогательная функция — ваши, и вы контролируете каждую строку кода.
* **Форматирование берёт на себя платформа.** `Intl.NumberFormat`, `Intl.DateTimeFormat`, `Intl.PluralRules` и `Intl.Locale` [встроены в современные браузеры и Node.js](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) и форматируют валюты, даты и категории множественного числа с учётом локали.
* **Немного текста, и он редко меняется.** Прототипу, внутреннему инструменту или сайту с одним дополнительным языком, тексты на котором пишет коллега, большего не нужно.
* **Языки с английской моделью множественного числа.** И в английском, и в немецком есть только [`one` и `other`](https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).

## Что ломается по мере роста

Вот типичная первая версия, в которую добавлен польский словарь:

```js title="i18n.mjs"
const messages = {
  en: {
    greeting: 'Hello, {name}!',
    files: { one: '{count} file', other: '{count} files' },
    checkout: 'Checkout',
  },
  pl: {
    greeting: 'Cześć, {name}!',
    files: { one: '{count} plik', other: '{count} pliki' },
    // «checkout» ещё не переведён
  },
};

export function t(locale, key, vars = {}) {
  let message = messages[locale][key];
  if (typeof message === 'object') {
    message = vars.count === 1 ? message.one : message.other;
  }
  return message.replace(/\{(\w+)\}/g, (_, name) => vars[name]);
}

console.log(t('pl', 'greeting', { name: 'Ania' }));
console.log([1, 2, 5].map((count) => t('pl', 'files', { count })).join(', '));
console.log(t('pl', 'checkout'));
```

Запустим его в Node.js 24:

```text title="Output"
Cześć, Ania!
1 plik, 2 pliki, 5 pliki
TypeError: Cannot read properties of undefined (reading 'replace')
```

Сразу всплывают две ошибки. «5 pliki» — неправильно (по-польски говорят «5 plików»), а непереведённый ключ `checkout` ломает рендеринг. В платформе есть всё необходимое, чтобы исправить обе:

```js title="intl.mjs"
// Категории множественного числа для каждого языка
for (const locale of ['en', 'ja', 'pl', 'ar']) {
  console.log(locale, new Intl.PluralRules(locale).resolvedOptions().pluralCategories.join(' '));
}
const pl = new Intl.PluralRules('pl');
console.log([1, 2, 5, 22].map((n) => `${n} → ${pl.select(n)}`).join(', '));

// Валюты и даты
const date = new Date(Date.UTC(2026, 9, 16));
for (const locale of ['en-US', 'de-DE', 'ja-JP']) {
  const price = new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(1234.5);
  const day = new Intl.DateTimeFormat(locale, { dateStyle: 'long', timeZone: 'UTC' }).format(date);
  console.log(locale, price, day);
}

// Направление текста
console.log(new Intl.Locale('ar').getTextInfo().direction);
```

```text title="Output"
en one other
ja other
pl one few many other
ar zero one two few many other
1 → one, 2 → few, 5 → many, 22 → few
en-US €1,234.50 October 16, 2026
de-DE 1.234,50 € 16. Oktober 2026
ja-JP €1,234.50 2026年10月16日
rtl
```

Каждое исправление по отдельности незначительно, но в сумме они дают заметный эффект:

* **Множественное число.** Проверка `n === 1` не работает для польского (четыре категории) и арабского (шесть), поэтому вспомогательной функции нужен [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules), а в каждом словаре — отдельная строка для каждой категории. (См. [Как работать с формами множественного числа в React?](/kb/how-to-handle-plurals-in-react))
* **Порядок слов.** Заполнители вроде `{name}` позволяют каждому языку ставить значение в нужное место. Конкатенация так не умеет: `t('by') + ' ' + author` ставит имя в конец, а по-японски пишут «Ana 著».
* **Форматирование.** `{count}` вставляет число как есть, поэтому вспомогательной функции нужен ещё и [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat), чтобы на польском 12,500 выводилось как «12 500».
* **Отсутствующие ключи.** Вспомогательной функции нужна цепочка резервных вариантов (язык перевода, затем исходный язык, затем сам ключ), иначе пользователи увидят `undefined` или приложение упадёт.
* **Синхронизация файлов.** Никто не подскажет вам, что в `pl` нет `checkout` или там осталась формулировка месячной давности, — пока это не заметит пользователь.
* **Языки с написанием справа налево.** Для арабского и иврита на странице нужны `lang` и `dir="rtl"`, а также зеркальная вёрстка.
* **Поисковые системы.** Если переключатель хранит выбор в состоянии или cookie-файле, все языки остаются на одном URL, тогда как Google рекомендует отдельный URL для каждого языка ([Managing multilingual sites](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites)). (См. [Как настроить SEO для многоязычного приложения Next.js?](/kb/multilingual-nextjs-seo))
* **Сами переводы.** От этих затрат не избавит никакая вспомогательная функция: кто-то должен написать «plików» и все остальные строки на всех языках — и переписывать их при каждом изменении английского текста.

Когда вспомогательная функция научится работать с категориями множественного числа, резервными вариантами и форматированием чисел, окажется, что вы написали ядро библиотеки интернационализации, — а переводы всё равно нужны.

## Что предлагает General Translation

General Translation сохраняет сильные стороны самописного подхода (читаемый текст и встроенное форматирование платформы) и берёт на себя то, что со временем разрастается. С `gt-react` (или `gt-next` для Next.js) текст остаётся прямо в компоненте внутри [`<T>`](/docs/react/reference/components/t), поэтому не нужно поддерживать ни словарь, ни вспомогательную функцию:

```tsx title="Inbox.tsx"
import { T, Plural, Num, Var } from 'gt-react';

export function Inbox({ name, count }: { name: string; count: number }) {
  return (
    <T>
      <h1>Welcome back, <Var>{name}</Var></h1>
      <Plural
        n={count}
        singular={<p>You have <Num>{count}</Num> unread message.</p>}
        plural={<p>You have <Num>{count}</Num> unread messages.</p>}
      />
    </T>
  );
}
```

[`<Plural>`](/docs/react/reference/components/plural) выбирает ветку по правилам множественного числа CLDR для каждой локали — тем же, на которых построен `Intl.PluralRules`, поэтому писать условие `n === 1` и ошибаться в нём не придётся. [`<Num>`](/docs/react/reference/components/num) форматирует число для локали читателя, а значение внутри [`<Var>`](/docs/react/reference/components/var) никогда не отправляется в API General Translation. Ниже — результат серверного рендеринга компонента с `gt-react` 11.4.12. Для наглядности испанский перевод вписан вручную в шаблон, созданный командой [`gt generate`](/docs/cli/reference/commands/generate) (текстовые разделители React удалены):

```html title="Output"
<!-- en, count = 12500 -->
<h1>Welcome back, Ana</h1><p>You have 12,500 unread messages.</p>
<!-- es, count = 12500 -->
<h1>Hola de nuevo, Ana</h1><p>Tienes 12.500 mensajes sin leer.</p>
```

Если для блока в какой-либо локали нет перевода, [`<T>`](/docs/react/reference/components/t) выводит исходное содержимое, а не `undefined` и не ошибку.

Главное отличие в том, что переводы создаёт General Translation:

* **Создаются с учётом контекста.** [`npx gt translate`](/docs/cli/reference/commands/translate) отправляет каждый блок [`<T>`](/docs/react/reference/components/t) вместе с его элементами, переменными, ветвями множественного числа и заметкой [`context`](/docs/react/reference/components/t#context), если она есть, и записывает по файлу на каждую локаль. Glossary закрепляет термины, а пользовательские промпты задают тон во всех проектах, использующих общую контекстную группу. (См. [Определение контекста для переводов](/docs/platform/dashboard/guides/defining-context-for-translations).)
* **Остаются актуальными.** При каждом запуске переводится новое и изменённое содержимое, а для остального повторно используются готовые переводы. Запускайте перевод в CI или поручите [Locadex](/docs/platform/locadex/quickstart), AI-агенту General Translation, открывать пул-реквесты по выбранным вами триггерам. В приложениях на Next.js и React Locadex также может обернуть существующие тексты в [`<T>`](/docs/react/reference/components/t).
* **Проверяются людьми.** Ваша команда редактирует переводы в панели управления. (См. [Проверка и редактирование переводов](/docs/platform/dashboard/guides/reviewing-translations).)
* **Направление письма и маршрутизация.** [`useLocaleDirection`](/docs/react/reference/hooks/use-locale-direction) возвращает `'rtl'` для арабского языка, а `gt-next` с помощью своего [промежуточного ПО](/docs/react/nextjs/app-router-middleware) может отдавать каждую локаль по отдельному URL.

JavaScript-библиотеки распространяются под лицензией MIT и работают без аккаунта General Translation, если вы предоставляете переводы сами. Приложение, настроенное на загрузку всех переводов из своего бандла, не обращается к General Translation во время выполнения. Для создания переводов используйте облачный сервис с [оплатой по факту использования](https://generaltranslation.com/pricing).

Пример с другого конца шкалы — Profound: компания выпускает свою платформу и документацию более чем в 47 локалях с помощью General Translation. (См. [развёртывания в рабочей среде](https://generaltranslation.com/enterprise).)

## Собственное решение, библиотека или General Translation: краткое сравнение

|                            | Собственный словарь и вспомогательная функция               | Библиотека с открытым исходным кодом, например i18next                                                       | General Translation                                                                                                                                                                |
| -------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Зависимости                | Нет                                                         | Сама библиотека и привязки к фреймворку, например `react-i18next`                                            | `gt-react` или `gt-next`, а также CLI `gt`                                                                                                                                         |
| Формы множественного числа | Ваш собственный код выбора формы и все формы в каждом файле | Выбор по правилам CLDR; формы предоставляют люди или подключённый сервис перевода                            | [`<Plural>`](/docs/react/reference/components/plural) выбирает форму по правилам CLDR; при переводе формы для каждого языка перевода генерируются на основе написанных вами ветвей |
| Числа, даты, валюты        | API `Intl`, которые вы вызываете сами                       | Встроенные средства форматирования поверх `Intl`                                                             | [`<Num>`](/docs/react/reference/components/num), [`<Currency>`](/docs/react/reference/components/currency) и [`<DateTime>`](/docs/react/reference/components/datetime)             |
| Отсутствующие переводы     | Зависит от реализации вспомогательной функции               | [Используется заданный резервный язык, затем отображается ключ](https://www.i18next.com/principles/fallback) | Отображается исходное содержимое                                                                                                                                                   |
| Синхронизация языков       | Вручную или собственным скриптом                            | Инструменты извлечения строк и подключённый вами сервис перевода                                             | Каждый запуск [`npx gt translate`](/docs/cli/reference/commands/translate) переводит новое и изменённое содержимое                                                                 |
| Кто переводит              | Люди или интегрированный вами сервис                        | Люди или подключённый сервис перевода                                                                        | ИИ с учётом размеченного контекста JSX, Glossary и пользовательских промптов; ваша команда проверяет переводы в панели управления                                                  |
| Стоимость                  | Ваше время и стоимость интегрированных сервисов             | Библиотека бесплатна, перевод оплачивается отдельно                                                          | Бесплатные библиотеки для JavaScript; [оплата перевода по факту использования](https://generaltranslation.com/pricing)                                                             |

## Когда стоит сделать всё самостоятельно

Делайте всё самостоятельно, если вы поддерживаете один-два языка, текстов немного и они редко меняются, множественное число в ваших языках образуется так же, как в английском, а переводы пишет кто-то из команды, — или если речь идёт о прототипе. Подключите библиотеку с открытым исходным кодом, например i18next, если вам нужны правила множественного числа и резервные варианты, а переводчики или система управления переводами у вас уже есть. Выбирайте General Translation, если вы добавляете сразу несколько языков, тексты меняются с каждым релизом или никому в команде не стоит возиться с польскими формами множественного числа: General Translation генерирует переводы с учётом контекста вашего кода и при каждой сборке поддерживает все языки в актуальном состоянии. (См. [General Translation в сравнении с i18next, react-intl и next-intl](/kb/general-translation-vs-i18n-libraries).)

## Сохраните свой словарь и доверьте его перевод CLI

Чтобы отказаться от ручного перевода, не нужно переписывать вспомогательную функцию. Перенесите сообщения для каждого языка в отдельный JSON-файл (`locales/en.json`, `locales/es.json`), оставьте свою функцию `t()` и укажите путь к этим файлам в `gt.config.json` с помощью заполнителя `[locale]`:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": [
    "es"
  ],
  "files": {
    "json": {
      "include": [
        "locales/[locale].json"
      ]
    }
  }
}
```

[`npx gt translate --dry-run`](/docs/cli/reference/commands/translate) показывает `locales/en.json` как источник, а полный запуск создаёт `locales/es.json`. В этом примере используются английские и испанские сообщения с одинаковой структурой ключей и простыми заполнителями, например `{name}`. В сопутствующем файле `locales/en.metadata.json` можно указать контекст или ограничения на количество символов для каждого ключа. См. [JSON](/docs/cli/reference/formats/json-files) и [Метаданные, привязанные к ключам](/docs/cli/reference/keyed-metadata). Поиск по ключу и выбор формы множественного числа по-прежнему остаются за вашей вспомогательной функцией — проверьте и то и другое на отрендеренном испанском тексте.

Чтобы отказаться от вспомогательной функции, постепенно оборачивайте компоненты в [`<T>`](/docs/react/reference/components/t) — по одному за раз — или сохраните ключи, используя [`useTranslations`](/docs/react/guides/translating-with-dictionaries). Если работу выполняет агент для написания кода, вместо того чтобы просить его написать вспомогательную функцию `t()`, передайте ему [инструкции для агентов](/docs/overview/for-coding-agents) от General Translation. Начните с [Быстрого старта для Next.js](/docs/react/nextjs-quickstart) или [Быстрого старта SPA для React](/docs/react/react-spa-quickstart).

## Основные выводы

* Словаря, вспомогательной функции `t()` и встроенных API `Intl` достаточно, если у вас один-два языка, немного текста и формы множественного числа устроены так же, как в английском.
* По мере роста приложения вам приходится самостоятельно поддерживать выбор форм множественного числа, резервные варианты, форматирование, синхронизацию файлов, поддержку right-to-left, отдельные URL для каждой локали и сам процесс подготовки переводов.
* Библиотеки General Translation берут на себя работу во время выполнения, General Translation генерирует переводы, используя ваш код как контекст, а если вы хотите сохранить самописный JSON-словарь, CLI может перевести и его.

## Related questions

- [General Translation против i18next, react-intl и next-intl: какую библиотеку интернационализации выбрать?](https://generaltranslation.com/ru/kb/general-translation-vs-i18n-libraries.mdx): Библиотеки с открытым исходным кодом, такие как i18next, react-intl и next-intl, ничего не переводят. Они лишь отображают переводы, которые вы сами помещаете в файл каталога для каждого языка, — их пишете вы, ваши переводчики или сторонний сервис. Библиотеки General Translation тоже имеют открытый исходный код и делают то же самое, но General Translation ещё и выполняет перевод, используя ваш код как контекст.
- [Как работать с формами множественного числа в React?](https://generaltranslation.com/ru/kb/how-to-handle-plurals-in-react.mdx): Чтобы работать с формами множественного числа в React, задайте для каждого языка по одному предложению на каждую форму множественного числа и выбирайте нужное по правилам множественного числа этого языка — ведь в английском 2 формы, в русском 4, а в арабском 6. Компонент `<Plural>` от General Translation в gt-react и gt-next принимает формы на исходном языке, а General Translation генерирует формы, нужные каждому языку перевода.
- [Как настроить SEO для многоязычного приложения Next.js?](https://generaltranslation.com/ru/kb/multilingual-nextjs-seo.mdx): Выделите каждому языку собственный URL с префиксом, например /en/about и /es/about, а затем укажите для каждого опубликованного языка html lang, канонический URL, ссылающийся на саму страницу, альтернативные версии hreflang с x-default, переведённые заголовок и описание, а также запись в карте сайта. Неопубликованные версии пометьте noindex. gt-next от General Translation направляет каждый язык на собственный URL и переводит заголовки с помощью getGT.
- [Можно ли делать i18n без файлов перевода?](https://generaltranslation.com/ru/kb/i18n-without-translation-files.mdx): Да. Библиотеки gt-react и gt-next от General Translation обеспечивают i18n для React и Next.js без ключей перевода и без отдельного JSON-файла для каждого языка, который приходится поддерживать. Тексты остаются встроенными в JSX внутри компонента `<T>`, а команда gt translate генерирует переводы для каждого языка во время сборки, учитывая контекст из вашего кода. Locadex может поддерживать их в актуальном состоянии через пул-реквесты.
- [Альтернативы Google Translate: General Translation и Google Translate](https://generaltranslation.com/ru/kb/general-translation-vs-google-translate.mdx): Google Translate — быстрый способ перевести разовый текст, но чтобы выпустить продукт на другом языке, нужно переводить каждую строку с учётом контекста, соблюдать единую терминологию, правильно обрабатывать переменные и формы множественного числа, проводить ревью и выстроить процесс релизов. General Translation понимает контекст каждой строки в вашем коде и автоматизирует весь рабочий процесс локализации — от кода до ревью и каждого релиза.

## Previous and next questions

- Previous question: [Какая библиотека интернационализации лучше всего подходит для приложения React?](https://generaltranslation.com/ru/kb/best-react-i18n-library.mdx)
- Next question: [Чек-лист тестирования локализации для многоязычных приложений](https://generaltranslation.com/ru/kb/multilingual-app-launch-checklist.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/ru/kb/sitemap.md) for every article.
