# General Translation Knowledge Base: Можно ли реализовать i18n без файлов перевода?
URL: https://generaltranslation.com/ru/kb/i18n-without-translation-files.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Да. С gt-react или gt-next контент остаётся встроенным прямо в ваши компоненты внутри компонента `<T>`, а переводы генерируются во время сборки, поэтому не нужно писать ключи и JSON-файлы для каждой локали и следить за их синхронизацией.
Category: Интернационализация
Last updated: 2026-05-07
Authors: Jackie Chen, Ernest McCarter
Published: 2026-05-07

Каждый, кто интернационализировал JavaScript-приложение, знает этот рабочий процесс. Вы устанавливаете библиотеку интернационализации, создаёте файл `en.json`, выносите из компонентов все строки, которые видит пользователь, присваиваете каждой ключ и подставляете этот ключ туда, где раньше была строка. Затем копируете этот JSON-файл для каждого поддерживаемого языка: `es.json`, `fr.json`, `ja.json`.

Поначалу всё нормально. Тридцать строк, три языка, 90 записей.

Но приложение растёт. Через полгода у вас уже 400 строк и 12 языков — 4 800 записей в дюжине файлов. Разработчик добавляет новую функцию, пишет пять новых строк и забывает обновить три файла перевода. Никто этого не замечает, пока пользователь в Токио не увидит резервный английский текст в японском интерфейсе. И тут кто-то предлагает купить систему управления переводами.

<Video src="https://assets.gtx.dev/i18n-no-translation.mp4" />

## Video

The video is narrated in English, with English captions.

[Can I do i18n without translation files?](https://caxecubb9c64cezv.public.blob.vercel-storage.com/films/kb-i18n-without-translation-files.mp4) (46 с)

General Translation lets you stop writing translation files with `gt-react` or `gt-next`. Run `npx gt init`, write the English inside a `<T>` component, then add `npx gt translate` to your build script so every build writes the translations.

## Налог на управление файлами

Файлы перевода создают нагрузку на сопровождение, которая растёт с каждым новым поддерживаемым языком. Изменили одну строку — и нужно обновить десяток файлов. Со временем эти файлы рассинхронизируются. Чтобы они оставались согласованными, нужны инструменты, автоматизация или строгая дисциплина.

Об этом красноречиво говорит целая экосистема обходных решений: расширения VS Code, автоматически генерирующие ключи перевода; генераторы типов, сверяющие ваш JSON с интерфейсами TypeScript; линтеры, находящие неиспользуемые ключи; проверки в CI, следящие за синхронизацией всех локалей. Для рабочего процесса, который действительно удобен, такие инструменты никто не пишет.

В UI написано одно, в файлах перевода — другое, а связывает их сопоставление по ключам:

```tsx
function CheckoutSummary({ itemCount, discount }) {
  const { t } = useTranslation('checkout');

  return (
    <div>
      <h2>{t('summary.title')}</h2>
      <p>{t('summary.item_count', { count: itemCount })}</p>
      {discount && <p>{t('summary.discount_applied', { percent: discount })}</p>}
      <p>{t('summary.total_label')}</p>
      <small>{t('summary.tax_notice')}</small>
    </div>
  );
}
```

Чтобы понять, что рендерит этот компонент, придётся открыть `checkout.json`, найти пространство имён `summary` и сверить пять ключей. Когда компонентов сотни, рецензирование кода превращается в бесконечные прыжки между файлами.

Синтаксис сообщений ICU только усугубляет ситуацию. Формат, который библиотеки вроде `i18next`, `react-intl` и `next-intl` используют для форм множественного числа, рода и интерполяции, — это отдельный мини-язык внутри строк JSON. Лишняя или пропущенная скобка в `{count, plural, one {# item} other {# items}}` даст о себе знать только во время выполнения. Для этого есть линтеры, но большинство команд ими не пользуется. Встроенный код, напротив, проверяется компилятором TypeScript и вашей IDE прямо в момент набора.

## Перевод кода напрямую

А что, если вообще не извлекать строки?

```tsx
import { T } from 'gt-react';

function CheckoutSummary({ itemCount, discount }) {
  return (
    <T>
      <div>
        <h2>Order Summary</h2>
        <p>You have {itemCount} items in your cart.</p>
        {discount && <p>{discount}% discount applied!</p>}
        <p>Total (before tax):</p>
        <small>Tax calculated at checkout.</small>
      </div>
    </T>
  );
}
```

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

Никаких `t('checkout.summary.title')`, никакого `en.json` и никаких JSON-файлов для каждой локали, которые приходится держать в синхронизации.

## Переводы как результат сборки

Они генерируются во время развертывания. [GT CLI](/docs/cli/reference/commands/translate) сканирует вашу кодовую базу, находит всё содержимое компонентов [`<T>`](/docs/react/reference/components/t) и создает переводы на каждый язык перевода. Результат сохраняется в директорию, добавленную в gitignore, — точно так же, как скомпилированный CSS или собранный в бандл JavaScript.

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["es", "fr", "ja", "de", "ko", "zh"],
  "files": {
    "gt": {
      "output": "public/_gt/[locale].json"
    }
  }
}
```

```txt title=".gitignore"
public/_gt/
```

В режиме разработки переводы выполняются on-demand. Измените строку, обновите страницу — и сразу увидите её на японском. В рабочей среде всё предварительно сгенерировано и раздаётся через CDN. Файлы перевода по-прежнему лежат на диске, но это лишь артефакты сборки, а не то, что вы пишете и поддерживаете сами.

## Что меняется на практике

Разница заметнее в повседневной работе, чем на архитектурных диаграммах.

PR, который добавляет на страницу новый раздел, так и выглядит — как PR, добавляющий на страницу новый раздел. Ревьюер видит сам текст, а не `checkout.summary.discount_applied_notice`. Для рецензирования кода больше не нужно сверяться с открытым рядом JSON-файлом.

Рефакторинг становится проще. Переименуйте компонент, переместите его, разбейте на части — переводы последуют за контентом, ведь никаких ключей переназначать не нужно. Чтобы добавить язык, достаточно изменить одну строку в конфигурации. А если удалить компонент, переводы для него просто перестанут генерироваться, и в 15 файлах не останется осиротевших ключей, которые никому не хочется вычищать.

## Куда движутся библиотеки интернационализации

Во всей экосистеме прослеживается тенденция к встроенным строкам. Первые библиотеки интернационализации, такие как `i18next` и `react-intl`, создавались в то время, когда машинный перевод ещё не годился для реального использования и каждую строку приходилось отдавать переводчику-человеку. Словари были оправданы как формат обмена. Теперь это ограничение исчезло, а издержки для разработчиков, вынужденных поддерживать параллельные файлы со строками, оправдать всё сложнее.

В `next-intl` наряду с режимом словарей появились [экспериментальные встроенные сообщения с автоматическим извлечением](https://next-intl.dev/docs/usage/extraction). [Компилятор Lingui](https://lingui.dev/ref/swc-plugin) извлекает сообщения из встроенных тегированных шаблонов во время сборки. [Paraglide](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) идёт другим путём: компилирует файлы сообщений в отдельные для каждой локали функции с поддержкой tree-shaking. Подходы различаются, но во всех случаях контент становится ближе к компоненту. GT доводит эту идею до логического завершения: источником истины служит ваш JSX, а перевод — лишь один из этапов компиляции.

## Компромисс

Когда вы пишете контент прямо в коде, вы пишете на родном языке. Структура компонентов, построение фраз, логика UI — всё это отражает то, как вы думаете по-английски (или на любом другом языке-источнике). Подход на основе словаря, как в `next-intl`, изначально менее привязан к конкретному языку: компонент никогда не содержит настоящего предложения ни на одном языке — только ключ, который ссылается на текст в другом месте.

Однако большинство разработчиков, создавая UI, и так мыслят на одном языке. Макет, тексты, подписи кнопок — всё это изначально придумывается на английском. Этот перекос заложен в дизайн независимо от того, где хранятся строки — прямо в коде или в JSON-файле. Мы считаем, что i18n-фреймворк должен подстраиваться под то, как вы работаете на самом деле. Пишите приложение так, как вам привычно, а перевод доверьте фреймворку — вместо того чтобы ради языковой нейтральности выносить контент в ключи.

## Начало работы

```bash
npx gt@latest init
```

Мастер настройки сконфигурирует проект, установит зависимости и настроит горячую перезагрузку переводов в режиме разработки. Подробное пошаговое руководство см. в разделе [Быстрый старт CLI](/docs/cli/quickstart).

После этого оберните контент в [`<T>`](/docs/react/reference/components/t), запустите dev-сервер и переключайтесь между языками с помощью компонента [`<LocaleSelector>`](/docs/react/reference/components/locale-selector):

```tsx
import { LocaleSelector } from 'gt-react';

function Header() {
  return (
    <header>
      <nav>{/* ... */}</nav>
      <LocaleSelector />
    </header>
  );
}
```

В режиме разработки переводы выполняются on-demand, поэтому вы сразу видите своё приложение на любом языке. В рабочей среде используются предварительно сгенерированные переводы. Добавьте шаг перевода в процесс сборки и задайте переменные `GT_PROJECT_ID` и `GT_API_KEY` в настройках хостинг-провайдера:

```json title="package.json"
{
  "scripts": {
    "build": "npx gt translate && next build"
  }
}
```

Команда [`translate`](/docs/cli/reference/commands/translate) сканирует вашу кодовую базу, находит все места использования [`<T>`](/docs/react/reference/components/t) и генерирует переводы, так что к моменту сборки приложения все локали уже готовы.

`gt-react` — проект с открытым исходным кодом. Для Next.js App Router используйте [`gt-next`](/docs/react/nextjs-quickstart), для React Native — [`gt-react-native`](/docs/react/react-native-quickstart).

## Related questions

- [Как перевести условный текст в JSX?](https://generaltranslation.com/ru/kb/translate-conditional-jsx.mdx): Не переводите каждую ветвь тернарного оператора по отдельности — оберните всё предложение в компонент `<T>` и задайте условие с помощью `<Branch>`. Так переводчик увидит полный контекст и сможет перестроить предложение для каждого языка.
- [Как работать с формами множественного числа в React?](https://generaltranslation.com/ru/kb/how-to-handle-plurals-in-react.mdx): Не зашивайте логику выбора формы множественного числа в код с помощью проверок вроде n === 1. Для приложений только на английском хватит небольшой вспомогательной функции, но многоязычным приложениям нужны locale-aware правила множественного числа. С ними для любого языка справляется i18n-компонент, например `<Plural>` из gt-react или gt-next.
- [Какое ПО для локализации лучше всего подходит разработчикам?](https://generaltranslation.com/ru/kb/best-localization-software.mdx): Ищите единую систему, которая охватывает весь конвейер — от библиотеки интернационализации в вашем коде до перевода и доставки, — а не собирайте по частям отдельную библиотеку, систему управления переводами и поставщика переводов.

## Previous and next questions

- Previous question: [Какое ПО для локализации лучше всего подходит разработчикам?](https://generaltranslation.com/ru/kb/best-localization-software.mdx)
- Next question: [General Translation и GNU gettext: нужны ли оба?](https://generaltranslation.com/ru/kb/general-translation-vs-gettext.mdx)

## Sitemap

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