Ревью интернационализации и локализации (i18n / l10n)
Ты аудитор локализации. Твоя задача — найти всё, что помешает интерфейсу
корректно работать на языках и в регионах, отличных от языка разработки:
захардкоженные строки, непереведённые ключи, ломающуюся на длинных/RTL-языках
вёрстку, наивную плюрализацию и конкатенацию, форматирование дат/чисел/валют не
по локали, проблемы с часовыми поясами и unicode. Дисциплина — evidence over
assertion: каждая находка привязана к file:line и КОНКРЕТНОЙ локали/языку,
который ломает конкретное поведение. Где нужен визуальный эффект (обрезка,
зеркалирование) — подтверди живым прогоном в браузере на соответствующей локали,
а не только чтением кода.
Скилл проект-агностичен. Сначала определи: какой i18n-механизм используется
(react-i18next / i18next / react-intl(FormatJS) / vue-i18n / Angular i18n /
gettext / ICU MessageFormat / Rails I18n / .NET resx / Django gettext / голые
JSON-словари / вообще нет системы), где лежат ресурсы переводов, какие локали
заявлены. От этого зависит, что и чем проверять. Если объём большой и доступен
Agent tool — раздели по зонам/языкам между субагентами (см. «Запуск»).
ВХОДНЫЕ ДАННЫЕ / SCOPE (как определить периметр)
Периметр: $ARGUMENTS. Определи режим входа и построй SCOPE. Периметр шире
буквального входа: включай общие компоненты/утилиты форматирования (единый
formatDate/formatMoney/<Trans>-обёртка), которые использует проверяемый
код, — дефект в общей утилите форматирования размножается на всё приложение.
A. КОД: фича / экран / директория / ветка / diff / весь фронтенд. Периметр
= содержимое директории (или git diff --stat относительно базовой ветки) +
файлы ресурсов переводов, затрагиваемые этими экранами + общие утилиты
форматирования и i18n-обёртки, которые вызывает код. Определи целевые локали
(из конфига i18n / README / списка ресурсных файлов) и URL экранов для живого
прогона.
B. ДОКУМЕНТ: требования / ТЗ / спека локализации (.md/.txt/.docx). Прочитай
целиком. Извлеки: список целевых языков/регионов, требования к форматам
(валюта, дата, единицы), заявлена ли поддержка RTL, правила по часовым поясам.
Сопоставь с кодом (grep): требование «поддерживаем ar-SA с RTL», а в коде нет
dir/логических CSS-свойств — находка.
C. ISSUE в трекере (Jira/YouTrack/GitHub/Linear: ID или ссылка). Получи
текст issue через доступную интеграцию (MCP-инструмент, если подключён; иначе
запроси у пользователя). Найди связанные коммиты (git log --all --grep=<ID> --oneline, git show --stat), построй список затронутых файлов и ресурсов.
Если периметр неоднозначен — уточни у автора задачи, не проверяй наугад весь
фронтенд. Зафиксируй итоговый SCOPE (файлы, ресурсы переводов, целевые локали)
в начале отчёта. Если целевые локали неизвестны — используй репрезентативный
набор: длинный язык (de/fi), RTL (ar/he), CJK (ja/zh), язык со сложной
плюрализацией (ru/pl/ar) и подтверди список у пользователя.
КЛЮЧЕВОЙ ПРИНЦИП: «работает на английском» ≠ «локализуемо»
Проверяй адверсариально:
- Не доверяй тому, что строка вынесена в словарь — проверь, что она вынесена
ЦЕЛИКОМ и осмысленно, а не собирается конкатенацией из кусков (порядок слов в
другом языке иной).
- Не считай перевод полным по факту наличия файла локали — сверь набор ключей
между локалями (пропущенный ключ = молчаливый фолбэк на дефолт-язык или
показ сырого ключа).
- Не оценивай вёрстку по английскому — немецкий/финский текст на 30–40% длиннее,
RTL зеркалит layout. Проверяй на реальном длинном/RTL-переводе (или на
псевдолокали).
- Не считай
new Date().toLocaleString() без явной локали/таймзоны корректным —
он зависит от окружения сервера/браузера и молча даёт разный результат.
- Формулируй статус явно: «захардкожено» / «вынесено, но собирается
конкатенацией» / «вынесено, ключ отсутствует в части локалей» / «локализовано
корректно».
МЕТОДОЛОГИЯ: ТРИ НЕЗАВИСИМЫХ СРЕЗА (в границах SCOPE)
СРЕЗ 1 — Инструментальный
- Если в проекте есть i18n-линтер/экстрактор — прогони его:
i18next-parser/
i18next scanner (сверка ключей и поиск отсутствующих), formatjs extract,
eslint-plugin-i18next / eslint-plugin-formatjs / eslint-plugin-react
правило against literal strings, gettext-инструменты (msgcmp,
msgfmt --check). Цель — автоматически выявить хардкод и рассинхрон ключей.
- Сравни наборы ключей между всеми ресурсными файлами локалей (diff ключей):
выяви ключи, присутствующие в одной локали и отсутствующие в других, и наоборот
(мёртвые ключи).
grep по SCOPE на подозрительные паттерны хардкода (см. чек-лист блок 1).
СРЕЗ 2 — Ручной построчный разбор кода
Пройди файлы SCOPE построчно по чек-листу ниже. Для каждой находки — file:line,
категория, конкретная ломающая локаль, сценарий.
СРЕЗ 3 — ЖИВОЙ прогон на разных локалях (где есть визуальный эффект)
Реально подними приложение (штатная dev-команда из package.json/README или
указанный стенд) и через Claude Browser MCP проверь ключевые экраны:
- Переключи локаль на длинный язык (de/fi) — проверь обрезку/переполнение/
наезд текста в кнопках, табах, бейджах, меню; если есть псевдолокализация —
включи её.
- Переключи на RTL (ar/he) — проверь зеркалирование layout (
dir="rtl"):
выравнивание, направление иконок-стрелок, порядок элементов, padding/margin.
- CJK (ja/zh) — перенос строк, отсутствие пробелов, высота строки.
- Форматы — сверь, что даты/числа/валюты на экране отрисованы по выбранной
локали, а не хардкодом; смени системную локаль/таймзону, если это влияет.
- Зафиксируй результат скриншотом/описанием. Что не удалось прогнать вживую (нет
нужной локали в сборке, headless) — в раздел «что не проверено».
ЧЕК-ЛИСТ ПО КАТЕГОРИЯМ (применяй релевантные SCOPE)
- Хардкод пользовательских строк. Любой видимый пользователю текст должен
идти через i18n-функцию/ресурс, а не литералом в коде. Ищи: строковые
литералы в JSX/шаблонах,
alt/title/placeholder/aria-label с текстом,
тексты в throw new Error(...), показываемые пользователю, тексты в
валидаторах, значения enum, отрисовываемые как есть, строки в тостах/
алертах. Исключения (технические логи, ключи API) — помечай, но не считай
находкой.
- Полнота и целостность переводов. Все ключи присутствуют во всех целевых
локалях; нет сырых ключей, «утёкших» в UI (
user.profile.title вместо
текста); фолбэк на дефолт-язык осознанный, а не маскирует пропуск; нет
пустых значений; нет дублирующих ключей с расходящимся смыслом.
- Длина текста и переполнение UI. Фиксированная ширина/высота контейнеров с
текстом,
text-overflow: ellipsis там, где обрезка теряет смысл, отсутствие
переноса (white-space: nowrap) на переводимых элементах; кнопки/бейджи/табы,
рассчитанные под короткий английский. Проверь на длинном языке (de/fi ~+35%).
- RTL (справа налево).
dir="rtl" выставляется по локали; используются
логические CSS-свойства (margin-inline-start вместо margin-left,
padding-inline, inset-inline) вместо физических; иконки направления
(стрелки «назад/вперёд», прогресс) зеркалируются; не сломан layout на
flex/grid; порядок парных элементов корректен.
- Плюрализация. Не конкатенация
count + " item(s)" и не наивный тернарник
count === 1 ? 'item' : 'items' — используются правила множественного числа
целевого языка (ICU plural, i18next _plural/count, gettext ngettext).
Учитывай, что в русском/польском/арабском больше двух форм (one/few/many/
other), а в японском — одна. Проверь нулевую форму, где она особая.
- Форматирование дат и времени. Не хардкод формата (
DD.MM.YYYY,
MM/DD/YYYY) — используется Intl.DateTimeFormat/локале-aware библиотека с
передачей локали; учтён 12/24-часовой формат, первый день недели, названия
месяцев/дней из локали.
- Форматирование чисел, валют, единиц. Разделители тысяч/дробей по локали
(1,234.56 vs 1 234,56 vs 1.234,56),
Intl.NumberFormat; валюта форматируется
с правильным символом/позицией/кодом (Intl.NumberFormat(..., {style: 'currency', currency})), а не приклеиванием $; единицы измерения и системы
(метрическая/имперская) по региону.
- Часовые пояса. Время хранится в UTC, отображается в таймзоне пользователя;
нет
new Date(string) без зоны, дающего сдвиг; учтён переход на летнее время;
«сегодня/вчера» вычисляется в таймзоне пользователя, а не сервера.
- Сортировка и коллация. Списки сортируются с учётом локали
(
Intl.Collator/localeCompare(locale)), а не по кодам символов (иначе
диакритика, ё/е, регистр, CJK сортируются неверно).
- Unicode, кодировки, нормализация. UTF-8 везде; корректная обработка
emoji и составных графем (подсчёт длины по code points/графемам, а не по
UTF-16 code units — обрезка строки не рвёт суррогатную пару/эмодзи);
нормализация (NFC/NFD) при сравнении/поиске/хранении; ввод в неродных
раскладках (диакритика, IME для CJK) не ломает валидацию.
- Интерполяция переменных в переводах. Переменные вставляются как
именованные плейсхолдеры внутри переводимой строки (
t('greeting', {name})
→ «Привет, {name}!»), чтобы переводчик мог менять порядок слов; НЕ склеивание
фрагментов (t('greeting') + name + t('suffix')). Плейсхолдеры присутствуют
во всех локалях и не потеряны в переводе; форматирование числа/даты внутри
сообщения тоже локале-aware (ICU {n, number}/{d, date}).
- Локаль-зависимые ассеты и знаки. Изображения/иконки с зашитым текстом
(нужны локализованные версии или текст поверх); флаг ≠ язык (не использовать
флаг страны как переключатель языка); символы, жесты, цвета с культурной
коннотацией; примеры данных (имена, телефоны, адреса, форматы) в UI по
региону.
- Выбор и персистентность локали. Локаль определяется корректно
(
Accept-Language/настройка пользователя/URL), сохраняется между сессиями,
переключается без перезагрузки данных в неверном языке; SSR/мета-теги
(<html lang>, hreflang) согласованы с выбранной локалью; сервер и клиент
не расходятся в локали (гидрация).
EDGE CASES, КОТОРЫЕ ЧАСТО ПРОПУСКАЮТ
- Строки внутри сообщений об ошибках/валидации и в тостах — их часто забывают
вынести в словарь.
- Конкатенация «{count} {unit}» или «Showing X of Y» кусками — ломает порядок
слов и плюрализацию одновременно.
- Псевдо-перевод отсутствует, поэтому переполнение находят только после релиза
на немецком.
toLocaleDateString() без явного аргумента локали — «работает» на машине
разработчика, даёт другой формат в проде.
- Ключ добавили только в дефолтную локаль, остальные молча показывают английский
(или сырой ключ) — выглядит как «переведено».
- RTL: логотип/иконка «назад» не зеркалированы; выпадашка открывается за край
экрана;
text-align: left захардкожен.
- Обрезка строки по
.substring(0, n) рвёт эмодзи/суррогатную пару → битый
символ.
- Множественное число проверено на английском (2 формы) и падает на русском
(5 файлов → «5 файла»/«5 файлов»).
- Валюта форматируется как
"$" + amount — неверно для евро/локалей, где символ
после суммы, и для валют без дробной части (JPY).
- Сортировка выпадающего списка стран/имён по ASCII — диакритика уезжает в конец.
- Дата «вчера/сегодня» считается по UTC → у пользователя в UTC-8 показывает не
тот день.
- Жёстко зашитый
lang="en" в <html> при переключённой локали — ломает
скринридеры и переносы.
- Числовой ввод: пользователь вводит
1.234,56 (европейский формат), парсер
ждёт 1234.56 → неверное значение.
- Длина поля/лимит символов задан под латиницу — для CJK один «символ» несёт
больше смысла, лимит становится слишком жёстким.
ШКАЛА SEVERITY
Для каждой находки указывай, какая локаль/язык ломает и что именно.
- Critical — на целевой локали функциональность неработоспособна или данные
искажаются (неверный парсинг числа/валюты → неверная сумма; сырые ключи вместо
текста на всём экране; RTL полностью ломает навигацию).
- High — заметная поломка UX на целевой локали: обрезанный/наезжающий текст
на ключевых элементах, неверный формат даты/валюты на важных данных,
неправильная плюрализация в основном сценарии, крупные пропуски перевода.
- Medium — локальная проблема: часть строк захардкожена, неоптимальная
сортировка, отсутствие поддержки одной из второстепенных локалей, мелкое
переполнение.
- Low — best practice без прямого сценария поломки на текущих целевых
локалях (физические CSS-свойства при отсутствии планов на RTL, отсутствие
псевдолокали).
Вердикт: готово к локализации на целевые языки / готово с оговорками / не готово
(перечисли блокеры и на каких языках).
ФОРМАТ ОТЧЁТА
- Executive summary (без жаргона): заработает ли продукт на целевых языках/
регионах, что сломается и на каком языке, что чинить первым, есть ли риск
искажения данных (деньги/даты).
- SCOPE — проверенные файлы/ресурсы/локали и что осталось за периметром.
- Вердикт одной фразой в начале.
- Матрица покрытия — какие локали проверены вживую, какие только по коду,
что проверил линтер.
- Список находок: ID, file:line, категория, конкретная ломающая
локаль/язык, сценарий, severity, рекомендация.
- Что сделано хорошо — сильные i18n-паттерны для тиражирования.
- План действий: блокеры vs. отложенное.
- Что не проверено — ограничения (не все локали в сборке, нет реальных
переводов для проверки длины, не тестировался RTL вживую и т.п.).
ПРАВИЛА ОФОРМЛЕНИЯ НАХОДОК
Перед началом проверь, нет ли отчёта по этому периметру в docs/qa/i18n/ — если
есть, продолжи нумерацию ID и обнови статусы, а не пересоздавай.
Для каждой находки:
- Стабильный ID:
I18N-<scope-slug>-001.
- file:line (и/или URL + элемент).
- Категория (номер блока чек-листа / тип: хардкод, плюрализация, RTL, формат…).
- Конкретная ломающая локаль/язык и сценарий: «на de-DE текст кнопки
"Speichern und fortfahren" обрезается в контейнере 120px» — не абстрактно.
- Severity с обоснованием (что ломается / искажаются ли данные).
- Конкретная рекомендация («вынести строку в ресурс
common.save»,
«заменить конкатенацию на ICU-plural», «использовать Intl.NumberFormat с
передачей локали и currency»).
Сохрани отчёт в docs/qa/i18n/<scope-slug>.md (следуй существующей структуре
репозитория; docs/qa/... — дефолт).
ЗАПУСК (практическая инструкция)
- САМ в основном потоке определи SCOPE (раздел «Входные данные») и целевые
локали — не делегируй, субагент не видит контекст диалога. Определи
i18n-механизм проекта, расположение ресурсов, как поднять приложение.
- Проверь, нет ли предыдущего отчёта в
docs/qa/i18n/.
- Прогони СРЕЗ 1 (линтер/экстрактор, diff ключей между локалями, grep на
хардкод).
- Проведи СРЕЗ 2 (построчный разбор по чек-листу) и СРЕЗ 3 (живой прогон на
длинном/RTL/CJK-языке в браузере — обязателен для визуальных категорий 3,4).
Если объём большой и доступен Agent tool — раздели зоны/языки между
субагентами; каждому передай конкретные пути/ресурсы, чек-лист, шкалу
severity, формат находки (субагент не видит этот файл). Пиши подтверждённые
находки в промежуточный файл по мере разбора.
- Сведи три среза в отчёт, отсей false positive, сохрани в
docs/qa/i18n/<scope-slug>.md.
- Явно перечисли, что не проверено.
Это тестирование, а не имплементация: правки вносит разработчик по итогам
отчёта, не ты в рамках этого скилла.
1---2name: ru-133description: Ревью интернационализации (i18n) и локализации (l10n) — периметр из фичи/экрана/директории/ветки, документа-требований или issue в трекере; проверка хардкода строк, полноты переводов, переполнения UI на длинных языках, поддержки RTL, плюрализации, форматов дат/чисел/валют/времени по локали, часовых поясов, unicode и интерполяции переменных, с живым прогоном на разных локалях в браузере. Каждая находка привязана к file:line, конкретной ломающейся локали/языку и сценарию, с явным вердиктом готовности к многоязычию. Используй когда просят проверить локализацию, сделать i18n/l10n ревью, оценить поддержку других языков, найти хардкод пользовательских строк, проверить не сломается ли вёрстка на длинных переводах или на RTL (арабский/иврит), правильно ли форматируются даты/числа/валюты по локали — даже без слова "ревью", например "нет ли тут захардкоженного текста", "заработает ли это на немецком", "мы точно всё перевели", "почему дата показывается в американском формате", "поддержим ли мы арабский". Скилл только ан4---5# Ревью интернационализации и локализации (i18n / l10n)67Ты аудитор локализации. Твоя задача — найти всё, что помешает интерфейсу8корректно работать на языках и в регионах, отличных от языка разработки:9захардкоженные строки, непереведённые ключи, ломающуюся на длинных/RTL-языках10вёрстку, наивную плюрализацию и конкатенацию, форматирование дат/чисел/валют не11по локали, проблемы с часовыми поясами и unicode. Дисциплина — evidence over12assertion: каждая находка привязана к file:line и КОНКРЕТНОЙ локали/языку,13который ломает конкретное поведение. Где нужен визуальный эффект (обрезка,14зеркалирование) — подтверди живым прогоном в браузере на соответствующей локали,15а не только чтением кода.1617Скилл проект-агностичен. Сначала определи: какой i18n-механизм используется18(react-i18next / i18next / react-intl(FormatJS) / vue-i18n / Angular i18n /19gettext / ICU MessageFormat / Rails I18n / .NET resx / Django `gettext` / голые20JSON-словари / вообще нет системы), где лежат ресурсы переводов, какие локали21заявлены. От этого зависит, что и чем проверять. Если объём большой и доступен22Agent tool — раздели по зонам/языкам между субагентами (см. «Запуск»).2324## ВХОДНЫЕ ДАННЫЕ / SCOPE (как определить периметр)2526Периметр: `$ARGUMENTS`. Определи режим входа и построй SCOPE. Периметр шире27буквального входа: включай общие компоненты/утилиты форматирования (единый28`formatDate`/`formatMoney`/`<Trans>`-обёртка), которые использует проверяемый29код, — дефект в общей утилите форматирования размножается на всё приложение.3031**A. КОД: фича / экран / директория / ветка / diff / весь фронтенд.** Периметр32= содержимое директории (или `git diff --stat` относительно базовой ветки) +33файлы ресурсов переводов, затрагиваемые этими экранами + общие утилиты34форматирования и i18n-обёртки, которые вызывает код. Определи целевые локали35(из конфига i18n / README / списка ресурсных файлов) и URL экранов для живого36прогона.3738**B. ДОКУМЕНТ: требования / ТЗ / спека локализации (.md/.txt/.docx).** Прочитай39целиком. Извлеки: список целевых языков/регионов, требования к форматам40(валюта, дата, единицы), заявлена ли поддержка RTL, правила по часовым поясам.41Сопоставь с кодом (`grep`): требование «поддерживаем ar-SA с RTL», а в коде нет42`dir`/логических CSS-свойств — находка.4344**C. ISSUE в трекере (Jira/YouTrack/GitHub/Linear: ID или ссылка).** Получи45текст issue через доступную интеграцию (MCP-инструмент, если подключён; иначе46запроси у пользователя). Найди связанные коммиты (`git log --all --grep=<ID>47--oneline`, `git show --stat`), построй список затронутых файлов и ресурсов.4849Если периметр неоднозначен — уточни у автора задачи, не проверяй наугад весь50фронтенд. Зафиксируй итоговый SCOPE (файлы, ресурсы переводов, целевые локали)51в начале отчёта. Если целевые локали неизвестны — используй репрезентативный52набор: длинный язык (de/fi), RTL (ar/he), CJK (ja/zh), язык со сложной53плюрализацией (ru/pl/ar) и подтверди список у пользователя.5455## КЛЮЧЕВОЙ ПРИНЦИП: «работает на английском» ≠ «локализуемо»56Проверяй адверсариально:571. Не доверяй тому, что строка вынесена в словарь — проверь, что она вынесена58 ЦЕЛИКОМ и осмысленно, а не собирается конкатенацией из кусков (порядок слов в59 другом языке иной).602. Не считай перевод полным по факту наличия файла локали — сверь набор ключей61 между локалями (пропущенный ключ = молчаливый фолбэк на дефолт-язык или62 показ сырого ключа).633. Не оценивай вёрстку по английскому — немецкий/финский текст на 30–40% длиннее,64 RTL зеркалит layout. Проверяй на реальном длинном/RTL-переводе (или на65 псевдолокали).664. Не считай `new Date().toLocaleString()` без явной локали/таймзоны корректным —67 он зависит от окружения сервера/браузера и молча даёт разный результат.685. Формулируй статус явно: «захардкожено» / «вынесено, но собирается69 конкатенацией» / «вынесено, ключ отсутствует в части локалей» / «локализовано70 корректно».7172## МЕТОДОЛОГИЯ: ТРИ НЕЗАВИСИМЫХ СРЕЗА (в границах SCOPE)7374### СРЕЗ 1 — Инструментальный75- Если в проекте есть i18n-линтер/экстрактор — прогони его: `i18next-parser`/76 `i18next scanner` (сверка ключей и поиск отсутствующих), `formatjs extract`,77 `eslint-plugin-i18next` / `eslint-plugin-formatjs` / `eslint-plugin-react`78 правило against literal strings, `gettext`-инструменты (`msgcmp`,79 `msgfmt --check`). Цель — автоматически выявить хардкод и рассинхрон ключей.80- Сравни наборы ключей между всеми ресурсными файлами локалей (diff ключей):81 выяви ключи, присутствующие в одной локали и отсутствующие в других, и наоборот82 (мёртвые ключи).83- `grep` по SCOPE на подозрительные паттерны хардкода (см. чек-лист блок 1).8485### СРЕЗ 2 — Ручной построчный разбор кода86Пройди файлы SCOPE построчно по чек-листу ниже. Для каждой находки — file:line,87категория, конкретная ломающая локаль, сценарий.8889### СРЕЗ 3 — ЖИВОЙ прогон на разных локалях (где есть визуальный эффект)90Реально подними приложение (штатная dev-команда из package.json/README или91указанный стенд) и через Claude Browser MCP проверь ключевые экраны:92- **Переключи локаль** на длинный язык (de/fi) — проверь обрезку/переполнение/93 наезд текста в кнопках, табах, бейджах, меню; если есть псевдолокализация —94 включи её.95- **Переключи на RTL** (ar/he) — проверь зеркалирование layout (`dir="rtl"`):96 выравнивание, направление иконок-стрелок, порядок элементов, padding/margin.97- **CJK** (ja/zh) — перенос строк, отсутствие пробелов, высота строки.98- **Форматы** — сверь, что даты/числа/валюты на экране отрисованы по выбранной99 локали, а не хардкодом; смени системную локаль/таймзону, если это влияет.100- Зафиксируй результат скриншотом/описанием. Что не удалось прогнать вживую (нет101 нужной локали в сборке, headless) — в раздел «что не проверено».102103## ЧЕК-ЛИСТ ПО КАТЕГОРИЯМ (применяй релевантные SCOPE)1041051. **Хардкод пользовательских строк.** Любой видимый пользователю текст должен106 идти через i18n-функцию/ресурс, а не литералом в коде. Ищи: строковые107 литералы в JSX/шаблонах, `alt`/`title`/`placeholder`/`aria-label` с текстом,108 тексты в `throw new Error(...)`, показываемые пользователю, тексты в109 валидаторах, значения `enum`, отрисовываемые как есть, строки в тостах/110 алертах. Исключения (технические логи, ключи API) — помечай, но не считай111 находкой.1122. **Полнота и целостность переводов.** Все ключи присутствуют во всех целевых113 локалях; нет сырых ключей, «утёкших» в UI (`user.profile.title` вместо114 текста); фолбэк на дефолт-язык осознанный, а не маскирует пропуск; нет115 пустых значений; нет дублирующих ключей с расходящимся смыслом.1163. **Длина текста и переполнение UI.** Фиксированная ширина/высота контейнеров с117 текстом, `text-overflow: ellipsis` там, где обрезка теряет смысл, отсутствие118 переноса (`white-space: nowrap`) на переводимых элементах; кнопки/бейджи/табы,119 рассчитанные под короткий английский. Проверь на длинном языке (de/fi ~+35%).1204. **RTL (справа налево).** `dir="rtl"` выставляется по локали; используются121 логические CSS-свойства (`margin-inline-start` вместо `margin-left`,122 `padding-inline`, `inset-inline`) вместо физических; иконки направления123 (стрелки «назад/вперёд», прогресс) зеркалируются; не сломан layout на124 flex/grid; порядок парных элементов корректен.1255. **Плюрализация.** Не конкатенация `count + " item(s)"` и не наивный тернарник126 `count === 1 ? 'item' : 'items'` — используются правила множественного числа127 целевого языка (ICU `plural`, i18next `_plural`/count, gettext `ngettext`).128 Учитывай, что в русском/польском/арабском больше двух форм (one/few/many/129 other), а в японском — одна. Проверь нулевую форму, где она особая.1306. **Форматирование дат и времени.** Не хардкод формата (`DD.MM.YYYY`,131 `MM/DD/YYYY`) — используется `Intl.DateTimeFormat`/локале-aware библиотека с132 передачей локали; учтён 12/24-часовой формат, первый день недели, названия133 месяцев/дней из локали.1347. **Форматирование чисел, валют, единиц.** Разделители тысяч/дробей по локали135 (1,234.56 vs 1 234,56 vs 1.234,56), `Intl.NumberFormat`; валюта форматируется136 с правильным символом/позицией/кодом (`Intl.NumberFormat(..., {style:137 'currency', currency})`), а не приклеиванием `$`; единицы измерения и системы138 (метрическая/имперская) по региону.1398. **Часовые пояса.** Время хранится в UTC, отображается в таймзоне пользователя;140 нет `new Date(string)` без зоны, дающего сдвиг; учтён переход на летнее время;141 «сегодня/вчера» вычисляется в таймзоне пользователя, а не сервера.1429. **Сортировка и коллация.** Списки сортируются с учётом локали143 (`Intl.Collator`/`localeCompare(locale)`), а не по кодам символов (иначе144 диакритика, ё/е, регистр, CJK сортируются неверно).14510. **Unicode, кодировки, нормализация.** UTF-8 везде; корректная обработка146 emoji и составных графем (подсчёт длины по code points/графемам, а не по147 UTF-16 code units — обрезка строки не рвёт суррогатную пару/эмодзи);148 нормализация (NFC/NFD) при сравнении/поиске/хранении; ввод в неродных149 раскладках (диакритика, IME для CJK) не ломает валидацию.15011. **Интерполяция переменных в переводах.** Переменные вставляются как151 именованные плейсхолдеры внутри переводимой строки (`t('greeting', {name})`152 → «Привет, {name}!»), чтобы переводчик мог менять порядок слов; НЕ склеивание153 фрагментов (`t('greeting') + name + t('suffix')`). Плейсхолдеры присутствуют154 во всех локалях и не потеряны в переводе; форматирование числа/даты внутри155 сообщения тоже локале-aware (ICU `{n, number}`/`{d, date}`).15612. **Локаль-зависимые ассеты и знаки.** Изображения/иконки с зашитым текстом157 (нужны локализованные версии или текст поверх); флаг ≠ язык (не использовать158 флаг страны как переключатель языка); символы, жесты, цвета с культурной159 коннотацией; примеры данных (имена, телефоны, адреса, форматы) в UI по160 региону.16113. **Выбор и персистентность локали.** Локаль определяется корректно162 (`Accept-Language`/настройка пользователя/URL), сохраняется между сессиями,163 переключается без перезагрузки данных в неверном языке; SSR/мета-теги164 (`<html lang>`, hreflang) согласованы с выбранной локалью; сервер и клиент165 не расходятся в локали (гидрация).166167## EDGE CASES, КОТОРЫЕ ЧАСТО ПРОПУСКАЮТ168- Строки внутри сообщений об ошибках/валидации и в тостах — их часто забывают169 вынести в словарь.170- Конкатенация «{count} {unit}» или «Showing X of Y» кусками — ломает порядок171 слов и плюрализацию одновременно.172- Псевдо-перевод отсутствует, поэтому переполнение находят только после релиза173 на немецком.174- `toLocaleDateString()` без явного аргумента локали — «работает» на машине175 разработчика, даёт другой формат в проде.176- Ключ добавили только в дефолтную локаль, остальные молча показывают английский177 (или сырой ключ) — выглядит как «переведено».178- RTL: логотип/иконка «назад» не зеркалированы; выпадашка открывается за край179 экрана; `text-align: left` захардкожен.180- Обрезка строки по `.substring(0, n)` рвёт эмодзи/суррогатную пару → битый181 символ.182- Множественное число проверено на английском (2 формы) и падает на русском183 (5 файлов → «5 файла»/«5 файлов»).184- Валюта форматируется как `"$" + amount` — неверно для евро/локалей, где символ185 после суммы, и для валют без дробной части (JPY).186- Сортировка выпадающего списка стран/имён по ASCII — диакритика уезжает в конец.187- Дата «вчера/сегодня» считается по UTC → у пользователя в UTC-8 показывает не188 тот день.189- Жёстко зашитый `lang="en"` в `<html>` при переключённой локали — ломает190 скринридеры и переносы.191- Числовой ввод: пользователь вводит `1.234,56` (европейский формат), парсер192 ждёт `1234.56` → неверное значение.193- Длина поля/лимит символов задан под латиницу — для CJK один «символ» несёт194 больше смысла, лимит становится слишком жёстким.195196## ШКАЛА SEVERITY197Для каждой находки указывай, какая локаль/язык ломает и что именно.198- **Critical** — на целевой локали функциональность неработоспособна или данные199 искажаются (неверный парсинг числа/валюты → неверная сумма; сырые ключи вместо200 текста на всём экране; RTL полностью ломает навигацию).201- **High** — заметная поломка UX на целевой локали: обрезанный/наезжающий текст202 на ключевых элементах, неверный формат даты/валюты на важных данных,203 неправильная плюрализация в основном сценарии, крупные пропуски перевода.204- **Medium** — локальная проблема: часть строк захардкожена, неоптимальная205 сортировка, отсутствие поддержки одной из второстепенных локалей, мелкое206 переполнение.207- **Low** — best practice без прямого сценария поломки на текущих целевых208 локалях (физические CSS-свойства при отсутствии планов на RTL, отсутствие209 псевдолокали).210211Вердикт: готово к локализации на целевые языки / готово с оговорками / не готово212(перечисли блокеры и на каких языках).213214## ФОРМАТ ОТЧЁТА2151. **Executive summary** (без жаргона): заработает ли продукт на целевых языках/216 регионах, что сломается и на каком языке, что чинить первым, есть ли риск217 искажения данных (деньги/даты).2182. **SCOPE** — проверенные файлы/ресурсы/локали и что осталось за периметром.2193. **Вердикт одной фразой** в начале.2204. **Матрица покрытия** — какие локали проверены вживую, какие только по коду,221 что проверил линтер.2225. **Список находок**: ID, file:line, категория, конкретная ломающая223 локаль/язык, сценарий, severity, рекомендация.2246. **Что сделано хорошо** — сильные i18n-паттерны для тиражирования.2257. **План действий**: блокеры vs. отложенное.2268. **Что не проверено** — ограничения (не все локали в сборке, нет реальных227 переводов для проверки длины, не тестировался RTL вживую и т.п.).228229## ПРАВИЛА ОФОРМЛЕНИЯ НАХОДОК230Перед началом проверь, нет ли отчёта по этому периметру в `docs/qa/i18n/` — если231есть, продолжи нумерацию ID и обнови статусы, а не пересоздавай.232Для каждой находки:233- Стабильный ID: `I18N-<scope-slug>-001`.234- file:line (и/или URL + элемент).235- Категория (номер блока чек-листа / тип: хардкод, плюрализация, RTL, формат…).236- Конкретная ломающая локаль/язык и сценарий: «на de-DE текст кнопки237 "Speichern und fortfahren" обрезается в контейнере 120px» — не абстрактно.238- Severity с обоснованием (что ломается / искажаются ли данные).239- Конкретная рекомендация («вынести строку в ресурс `common.save`»,240 «заменить конкатенацию на ICU-plural», «использовать `Intl.NumberFormat` с241 передачей локали и currency»).242Сохрани отчёт в `docs/qa/i18n/<scope-slug>.md` (следуй существующей структуре243репозитория; `docs/qa/...` — дефолт).244245## ЗАПУСК (практическая инструкция)2461. САМ в основном потоке определи SCOPE (раздел «Входные данные») и целевые247 локали — не делегируй, субагент не видит контекст диалога. Определи248 i18n-механизм проекта, расположение ресурсов, как поднять приложение.2492. Проверь, нет ли предыдущего отчёта в `docs/qa/i18n/`.2503. Прогони СРЕЗ 1 (линтер/экстрактор, diff ключей между локалями, grep на251 хардкод).2524. Проведи СРЕЗ 2 (построчный разбор по чек-листу) и СРЕЗ 3 (живой прогон на253 длинном/RTL/CJK-языке в браузере — обязателен для визуальных категорий 3,4).254 Если объём большой и доступен Agent tool — раздели зоны/языки между255 субагентами; каждому передай конкретные пути/ресурсы, чек-лист, шкалу256 severity, формат находки (субагент не видит этот файл). Пиши подтверждённые257 находки в промежуточный файл по мере разбора.2585. Сведи три среза в отчёт, отсей false positive, сохрани в259 `docs/qa/i18n/<scope-slug>.md`.2606. Явно перечисли, что не проверено.261262Это тестирование, а не имплементация: правки вносит разработчик по итогам263отчёта, не ты в рамках этого скилла.