# General Translation Knowledge Base: Как называть ключи переводов i18n?
URL: https://generaltranslation.com/ru/kb/i18n-key-naming.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Называйте ключи по тому, где отображается текст и что он означает, например checkout.summary.title, а не по его английской формулировке. Для каждого отдельного случая использования заводите собственный ключ, даже если английский текст совпадает: в немецком кнопка Open переводится как Öffnen, а статус Open — как Offen.
Category: Интернационализация
Last updated: 2026-10-20
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-20

Ключ — это идентификатор, по которому код находит перевод, например `t('summary.title', { ns: 'checkout' })`. Удачные имена не ломаются при правках текста, подсказывают переводчикам, где отображается текст, и упрощают поиск неиспользуемых ключей. Описанные ниже соглашения подходят для i18next, react-i18next, Rails I18n, vue-i18n и любой другой библиотеки, которая читает JSON или YAML с ключами. В примерах используется синтаксис i18next; они проверены на i18next 26.5.3.

## 1. Пространства имён по функциям или экранам

Начинайте каждый ключ с раздела продукта, к которому он относится, затем указывайте компонент и, наконец, элемент.

* ✅ `checkout.summary.title`, `settings.billing.cancelButton`
* ❌ `title1`, `text_47`, `newString`

Если первый сегмент вынесен в отдельный файл, это пространство имён, а не часть пути ключа. Если `summary.title` хранится в `checkout.json`, i18next найдёт его через `t('summary.title', { ns: 'checkout' })` или `t('checkout:summary.title')`. Вызов `t('checkout.summary.title')` вернёт саму строку ключа, поскольку i18next будет искать объект `checkout` в пространстве имён по умолчанию.

Префикс подсказывает переводчику, где отображается текст, и позволяет разбить каталоги на файлы. В i18next такие файлы называются [пространствами имён](https://www.i18next.com/principles/namespaces). Их рекомендуется выделять по представлениям, функциям или лениво загружаемым модулям, а для меток вроде «Сохранить» и «Отмена» завести отдельное пространство имён `common`. В React библиотека react-i18next загружает пространство имён через `useTranslation('checkout')`, а [опция `keyPrefix`](https://react.i18next.com/latest/usetranslation-hook) позволяет сократить глубоко вложенные пути.

## 2. Описывайте смысл, а не формулировку

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

* ✅ `auth.login.submit`
* ❌ `auth.login.clickHereToSignInNow`

Если позже на кнопке появится надпись «Продолжить», первый ключ останется точным. Второй же будет описывать текст, которого больше нет, и рано или поздно кто-нибудь его переименует — а это сломает все места, где на него ссылаются, и оставит переводы без привязки.

## 3. Один ключ на контекст, а не на английскую строку

Две строки, одинаковые по-английски, часто переводятся по-разному. «Open» на кнопке — глагол, а «Open» как статус тикета — прилагательное. В немецком в первом случае используется *Öffnen*, а во втором — *Offen*.

* ✅ `files.actions.open` и `tickets.status.open`
* ❌ один `common.open` для обоих случаев

Используйте общее пространство имён `common` только для меток, значение которых везде одинаково, например для кнопки «Отмена». В системах, где ключом служит исходный текст, эта задача решается с помощью контекста: [`msgctxt`](https://www.gnu.org/software/gettext/manual/html_node/Contexts.html) в GNU gettext позволяет одной английской строке иметь два перевода.

## 4. Выберите вложенный или плоский JSON и не смешивайте их

В `checkout.json` i18next воспринимает `summary.title` как путь, поскольку `keySeparator` по умолчанию равен `.`. Подходят оба варианта; главное — выбрать один, чтобы все файлы, ревьюеры и инструменты придерживались единого формата.

```json title="locales/en/checkout.json (nested)"
{
  "summary": {
    "title": "Order summary",
    "taxNotice": "Tax is calculated at checkout."
  }
}
```

```json title="locales/en/checkout.json (flat)"
{
  "summary.title": "Order summary",
  "summary.taxNotice": "Tax is calculated at checkout."
}
```

Для плоских файлов [документация по конфигурации](https://www.i18next.com/overview/configuration-options) i18next рекомендует задать `keySeparator` значение `false`. Также выберите единый стиль регистра букв (сегменты в формате `camelCase` или `snake_case`) и придерживайтесь его во всех файлах.

## 5. Используйте суффиксы множественного числа и контекста, предусмотренные вашей библиотекой

i18next выбирает формы множественного числа по суффиксу и требует, чтобы переменная называлась `count`. Запись с суффиксом `_zero` переопределяет правило языка, когда `count` равен 0, а для порядковых числительных добавляется `_ordinal`:

```json title="locales/en/checkout.json"
{
  "cart": {
    "itemCount_zero": "Your cart is empty",
    "itemCount_one": "{{count}} item",
    "itemCount_other": "{{count}} items"
  }
}
```

`t('cart.itemCount', { count: 0 })` возвращает «Your cart is empty», для 1 — «1 item», а для 5 — «5 items». Для языков с большим числом категорий множественного числа добавляются ключи вроде `itemCount_few` и `itemCount_many`. (Подробнее см. [документацию i18next по формам множественного числа](https://www.i18next.com/translation-function/plurals), а категории для каждого языка — в статье [Что такое формат сообщений ICU?](/kb/icu-message-format).)

* ✅ `itemCount_one`, `itemCount_other`, а для текста с учётом рода — ключи [контекста](https://www.i18next.com/translation-function/context), например `friend_male` и `friend_female`
* ❌ придуманные суффиксы вроде `itemCount_singular` и `itemCount_plural`: библиотека их не распознает

Библиотеки на основе формата сообщений ICU (react-intl, next-intl, Lingui), напротив, хранят все формы множественного числа в одном сообщении под одним ключом.

## 6. Никогда не собирайте предложение из нескольких ключей

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

* ❌ `t('account.hello') + name + t('account.welcomeBack')`
* ✅ один ключ с плейсхолдером: `"account.welcome": "Hello, {{name}}! Welcome back."`

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

В [руководстве GNU gettext](https://www.gnu.org/software/gettext/manual/html_node/Preparing-Strings.html) для каталогов действует то же правило: переводите предложения целиком и используйте форматные строки вместо конкатенации.

## 7. Выберите между стабильными ключами и исходным текстом в качестве ключа

Не каждая система заставляет придумывать имена. Вопрос в том, кто отвечает за идентификатор:

| Подход                                                        | Пример                                   | Преимущество                                                                             | Издержки                                                                                                    |
| ------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Семантические ключи (i18next, Rails I18n)                     | `t('summary.title', { ns: 'checkout' })` | Правки текста не затрагивают код; ключи указывают на место использования                 | Кто-то должен придумывать имя для каждого ключа, распределять ключи по файлам и удалять лишние              |
| Исходный текст в качестве ключа (gettext)                     | `_("Order summary")`                     | Код остаётся читаемым; не нужно придумывать имена                                        | Любая правка английского текста создаёт новую запись; для одинаковых строк с разным смыслом нужен `msgctxt` |
| Идентификаторы, генерируемые из встроенного текста (FormatJS) | `defaultMessage="Order summary"`         | Сообщение находится рядом с местом использования; идентификаторы создаются автоматически | Нужны плагин сборки и этап извлечения                                                                       |

Используйте семантические ключи, если тексты ведутся вне кодовой базы (например, контент-команда редактирует строки в TMS) или если веб- и мобильное приложения используют общий каталог. Используйте исходный текст или встроенные сообщения, если тексты пишут сами разработчики и вы хотите, чтобы при рецензировании кода было видно, какие именно слова увидят пользователи.

## 8. Не используйте ключи в URL, аналитике и логике

Ключи и переведённые строки предназначены только для отображения. Не передавайте `t('checkout:cta')` в качестве имени события аналитики (в каждой локали будет регистрироваться своё событие), не формируйте слаг маршрута из ключа и не стройте ветвление на условии `label === t('status.open')`. Используйте фиксированное имя события, слаг, который вы контролируете сами, и перечисление (enum) или код статуса. Тогда переименование ключа или правка перевода не сломают ни панель управления, ни ссылку, ни условие.

## 9. Автоматически отслеживайте неиспользуемые и недостающие ключи

Каталоги со временем расходятся с кодом: ключи остаются после удаления компонента, а новые ключи попадают в релиз без перевода. Запускайте проверку в CI.

* **i18next**: [`i18next-cli`](https://github.com/i18next/i18next-cli) приходит на смену устаревшему `i18next-parser`. На небольшом тестовом проекте команда `npx i18next-cli status --unused` сообщила об осиротевшем ключе `oldPromoBanner`, не изменив ни одного файла; затем `npx i18next-cli extract` удалила его (опция `removeUnusedKeys` по умолчанию равна `true`) и создала пустые немецкие записи для всех ключей, включая `itemCount_one` и `itemCount_other`. Команда `npx i18next-cli status` завершается с ошибкой, пока остаются недостающие переводы, а `rename-key` переименовывает ключ сразу во всех исходных файлах и файлах локалей. Статический анализ не видит ключи, которые формируются во время выполнения, например ``t(`status.${code}`)``, поэтому `extract` считает их неиспользуемыми и удаляет. Перечислите такие префиксы в опции `preservePatterns` (например, `'status.*'`) или явно пропишите каждый ключ в коде.
* **Rails**: i18n-tasks находит недостающие и неиспользуемые ключи в коде на Ruby и ERB. (См. [General Translation и i18n-tasks](/kb/general-translation-vs-i18n-tasks).)

## Как General Translation работает с ключами

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

```tsx title="CheckoutSummary.tsx"
import { T } from 'gt-react';

export function CheckoutSummary() {
  return (
    <T>
      <h2>Order summary</h2>
      <small>Tax is calculated at checkout.</small>
    </T>
  );
}
```

[`npx gt translate`](/docs/cli/reference/commands/translate) извлекает только тот контент, который остался в исходном коде, поэтому после удаления компонента вам не нужно вручную поддерживать его ключи. Разным значениям &quot;Open&quot; по-прежнему нужен контекст: `<T context="Ticket status">Open</T>` или `gt('Open', { $context: 'Ticket status' })` с [`useGT`](/docs/react/reference/hooks/use-gt). Необязательный проп [`id`](/docs/react/reference/components/t#id) у [`<T>`](/docs/react/reference/components/t) или опция `$id` у функции перевода задаёт записи стабильный идентификатор для редактора переводов. (См. [Можно ли реализовать i18n без файлов перевода?](/kb/i18n-without-translation-files).)

Командам, которые предпочитают ключи, не придётся от них отказываться. Создайте один исходный словарь, например `dictionary.json`, и читайте его с помощью [`useTranslations`](/docs/react/reference/hooks/use-translations). Та же команда переведёт и его, а при обращении к id, которого нет в словаре, выбрасывается исключение, а не рендерится необработанный ключ. (См. [Перевод с помощью словарей](/docs/react/guides/translating-with-dictionaries).) Существующие JSON-файлы i18next или next-intl переводятся как есть, а сопутствующий [файл `.metadata.json`](/docs/cli/reference/keyed-metadata) позволяет задать для каждого ключа контекст или ограничение на количество символов — аналог соглашения 3 для подхода с ключами.

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

* Называйте ключи по расположению и смыслу (`checkout.summary.title`), а не по формулировке или позиции (`text_47`).
* Заведите отдельные ключи для «Open» в значении глагола и «Open» в значении статуса; используйте общий ключ, только если значения полностью совпадают.
* Строго следуйте суффиксам форм множественного числа, принятым в библиотеке: i18next использует `_one`, `_other` и `_zero`, а библиотеки на основе ICU хранят все формы множественного числа в одном сообщении.
* Запускайте `i18next-cli status --unused` или i18n-tasks в CI, чтобы неиспользуемые и отсутствующие ключи не накапливались.
* Компоненту [`<T>`](/docs/react/reference/components/t) от General Translation ключи не нужны, а для команд, которые предпочитают работать с ключами, есть словарный режим с [`useTranslations`](/docs/react/reference/hooks/use-translations).

## Related questions

- [Можно ли делать 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 может поддерживать их в актуальном состоянии через пул-реквесты.
- [General Translation или i18n-tasks: что выбрать?](https://generaltranslation.com/ru/kb/general-translation-vs-i18n-tasks.mdx): i18n-tasks — это Ruby-gem, который находит отсутствующие и неиспользуемые ключи Rails I18N, наводит порядок в файлах локалей и умеет заполнять недостающие ключи с помощью Google Translate, DeepL, OpenAI, Yandex или watsonx. General Translation переводит файлы локалей целиком с учётом вашего Glossary и пользовательских промптов, а результат можно проверить в панели управления. Эти инструменты решают разные задачи, и многим командам пригодятся оба.
- [Альтернативы i18next: General Translation или i18next](https://generaltranslation.com/ru/kb/general-translation-vs-i18next.mdx): Выбирайте General Translation, если хотите хранить тексты прямо в React-компонентах и создавать переводы с учётом их JSX-структуры и контекста продукта. Можно также оставить i18next и переводить существующие каталоги с помощью General Translation. i18next отвечает за рендеринг сообщений и выбор форм множественного числа, а сами переводы предоставляет отдельный сервис.
- [Что такое формат сообщений ICU?](https://generaltranslation.com/ru/kb/icu-message-format.mdx): Формат сообщений ICU — это синтаксис, позволяющий уместить в одной переводимой строке переменные, формы множественного числа, варианты выбора (select) и порядковые числительные, например {count, plural, one {# file} other {# files}}. Каждый язык использует только нужные ему категории множественного числа CLDR: в английском это one и other, а в польском к ним добавляются few и many.
- [Как найти захардкоженные строки, которые не переведены?](https://generaltranslation.com/ru/kb/find-hardcoded-strings.mdx): Запустите в CI линтер, который находит строковые литералы, видимые пользователю, — например, правило no-literal-string из eslint-plugin-i18next для JSX, проверку HardcodedText в Android Lint или линтер HardCodedString из erb_lint для Rails. Затем пройдитесь по приложению в псевдолокали, чтобы найти текст, который статические проверки отследить не могут.

## Previous and next questions

- Previous question: [Как локализовать iOS-приложение с помощью String Catalogs?](https://generaltranslation.com/ru/kb/localize-ios-app.mdx)
- Next question: [Как найти захардкоженные строки, которые не переведены?](https://generaltranslation.com/ru/kb/find-hardcoded-strings.mdx)

## Sitemap

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