From abfcbbfac2bbed743e77ac48b206ed12946879e9 Mon Sep 17 00:00:00 2001 From: Ku6epXBOCTuK Date: Sun, 13 Sep 2026 05:25:56 +0500 Subject: [PATCH] docs: update plan and backlog --- docs/backlog.md | 8 +++++++ docs/plan-preview-i18n.md | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/docs/backlog.md b/docs/backlog.md index 2c7e22a..1f34c41 100644 --- a/docs/backlog.md +++ b/docs/backlog.md @@ -2,6 +2,11 @@ ## lint и прочие правила +- [ ] Кросс-языковой линтер словарей (warn-only: паритет ключей, пустые + значения, совпадение `{placeholder}`-переменных между всеми локалями) — + правило `i18n/dict-consistency`, детали в §Фаза 8 + `docs/plan-preview-i18n.md` + ## Мелочи всякие - [ ] Пересмотреть прозрачность в инструментах - Create empty PNG - прозрачность @@ -48,6 +53,9 @@ ## Переезд - [ ] добавить i18n в preview +- [ ] множественные числа (часть/части/частей; «N параметров», счётчики) — + `tPlural()` и формат `PluralForms`, детали в §Фаза 9 + `docs/plan-preview-i18n.md` ## SEO/GEO diff --git a/docs/plan-preview-i18n.md b/docs/plan-preview-i18n.md index 777bcc6..27136c4 100644 --- a/docs/plan-preview-i18n.md +++ b/docs/plan-preview-i18n.md @@ -267,6 +267,53 @@ `scoreDoc()`/`normalizeForSearch()` из `matching.ts` (входит в объём, см. решение D). Последний шаг, аккуратный отдельный коммит. +### Фаза 8: Линтер словарей (i18n key linter) + +Замена ручной проверки ключей на автоматическую. Инфраструктура уже есть: +`eslint-plugins/` умеет читать соседние файлы (`isolation/no-mixed-imports`) и +гоняться фикстурами (`test:rules`). + +- **8.1** Новое правило `i18n/dict-consistency` в `web/eslint-plugins/`. + Применяется к файлам-словарям (конвенция: экспортируют `Dict`, лежат в + `lib/i18n/`); новые локали подхватываются автоматически — правка правила не + нужна. +- **8.2** Проверки — **только `warn`, никогда `error`** (пропущенный перевод не + валит сборку): + - **Паритет дерева ключей**: каждый ключ (dot-path `a.b.c`) присутствует во + всех остальных локалях, не только против BASE. Нет — + `missing key "x.y" in en`. + - **Пустые значения**: `""` и строка из одних пробелов — + `empty value "x.y" in en`. + - **Паритет плейсхолдеров**: у одинакового ключа набор `{name}` одинаковый во + всех локалях — поймать потерянную при переводе переменную. +- **8.3** Цель — zero-warn. Сейчас `en.ts` неполный (в `tools` только + результаты-помощники, остальной английский — из реестра), поэтому после + включения будет много warn'ов: доводим `en.ts` до полноты `ru.ts` (секции + + `tools` полностью, source — registry-English, без дрейфа). До достижения цели + допускается allowlist-механизм в ядре правила для осознанных отклонений. +- **8.4** Тесты правила — фикстуры в `web/eslint-plugins/__fixtures__/`, док в + `web/eslint-plugins/README.md`; включение в `eslint.config.js` как `"warn"`. + +### Фаза 9: Множественные числа (plural-формы) + +- **9.1** Тип значения словаря расширяем: `string | PluralForms`, где + `PluralForms = { other: string } & Partial<{ one, few, many }>` по + CLDR-правилам локали (ru: обязательны `one/few/many`; `other` — всегда, как + фолбэк). Новая функция `tPlural(path, n)` в `lib/i18n/`: выбирает категорию + через `Intl.PluralRules(locale).select(n)` и подставляет её с интерполяцией + `{n}`. +- **9.2** Первые потребители: «часть/части/частей» (split-into-parts-png), + счётчики инструментов (`catalog.toolsCount`), «N параметров», «N файлов». +- **9.3** Паритет в линтере (Фаза 8): если ключ — plural-объект в одной локали, + он обязан быть plural-объектом во всех остальных (тип-паритет; string в одной + из локалей — warn). Набор форм между локалями не сравнивается дословно — он + проверяется по правилам конкретной локали (`other` обязателен; en: one/other, + ru: one/few/many). +- **9.4** Открытый вопрос на этапе реализации: неоднозначность «объект-секция» + (`Record`) vs plural-объект в дереве словаря. Варианты: + отдельная секция `plurals` в словаре, конвенция имён ключей, вывод типа из + контекста. Фаза начинается с проработки этого вопроса, не с кода. + ## 5. Решения - **A. Layout groups**: заголовки групп храним i18n-ключами прямо в схеме