# Локализация ## Принципы - Поддерживаемые языки на старте: **ru**, **en**. Язык по умолчанию — язык системы / браузера, если поддерживается, иначе `en`. - Выбор языка: на экране входа (сохраняется локально) и в профиле (сохраняется в `users.locale` — синхронизируется между устройствами). - **Сервер не возвращает пользовательских текстов**: только машинные коды ошибок и ключи (`activity.running`). Исключение — метаданные музыки (названия треков / исполнителей) и changelog релиза; они не переводятся. - Добавление языка = добавить папку словаря + строку в список `SUPPORTED_LOCALES`, без изменения кода. ## Структура `packages/i18n` ``` packages/i18n/ ├── locales/ │ ├── ru/ common.json auth.json player.json wave.json collection.json admin.json errors.json │ └── en/ ... ├── src/ │ ├── index.ts # инициализация i18next, SUPPORTED_LOCALES, детекция │ └── types.d.ts # типизация ключей (resources → TS), опечатка в ключе = ошибка компиляции └── scripts/check.ts # проверка: все ключи есть во всех языках, нет лишних, плейсхолдеры совпадают ``` - Неймспейсы грузятся лениво (веб — по страницам; админка отдельно). - Плюрализация — ICU / i18next plural (`ru` — one / few / many / other): «1 трек», «3 трека», «5 треков». - Даты, длительности, числа — `Intl.*` с текущей локалью (`Intl.RelativeTimeFormat` для «вчера», группировка истории по дням). - Ошибки API: `errors.json` содержит переводы по `code` с подстановкой `params` (`"validation.password_too_short": "Пароль должен быть не короче {{min}} символов"`). Неизвестный код → общий текст «Что-то пошло не так» + код в деталях. Ключи есть в каждом языке, лишних нет, плейсхолдеры совпадают — это проверяет `packages/i18n/scripts/check.ts`.