Skip to content

Мультиязычность интерфейса: добавить английский язык #381

Description

@dontsovcmc

Задача

Добавить английский язык в интерфейс настройки Ватериуса. Ниже — исследование трудоёмкости по текущему коду (ветка dev, прошивка 2.0.46).

Старая просьба от пользователя: #13 (2018, закрыт без реализации).

Главный вывод

Задача небольшая и хорошо локализованная. Весь русский текст, который видит пользователь, лежит в LittleFS (ESP8266/data/) — ~190 уникальных строк, ~800 слов, 12,3 КБ. Прошивка на C++ уже полностью англоязычная.

Оценка: ~1,5–2 дня работы + перевод ~800 слов + перерисовка 8 картинок.

Английский интерфейс доставляется пользователям отдельным OTA-обновлением файловой системы, без перепрошивки кода (ota_parse.h, ota_update.cpp:59-87).

Что уже готово в коде

1. Прошивка языконезависима. В src/ha/resources.h — "Cold Water", "Voltage Battery", "Sleep Period"; CHANNEL_NAMES, MODEL_NAMES, ключи JSON в json.cpp — всё латиница. Кириллицы в Home Assistant discovery, MQTT-payload и JSON на сервер — ноль.

Единственная кириллица в литералах C++ — 7 строк в src/portal/resources.h:55-61, и они уходят только в LOG_ERROR, в релизной сборке не видны.

2. Сервер уже отдаёт коды, а не текст. active_point.cpp:296-306 возвращает "8"…"13" вместо сообщений о статусе Wi-Fi, active_point_api.cpp кладёт в errors числа "14"…"19". Текст подставляет браузер — tr(str_id) в data/static/strings.js:135-159. Слой «код ↔ текст» готов, его надо только сделать двухъязычным.

3. Есть заготовка. В шести страницах закомментирована ссылка <a href="/eng/start.html" class="lang fr">Eng</a> (index.html:23, about.html:21, captive_portal*.html:19), CSS-класс .lang живой — style.css:37.

Объём перевода

Где Единиц Кириллицы
19 HTML-страниц (текстовые узлы + title/placeholder) 229 (150 уникальных) 4 037 симв.
data/static/strings.js — tr(), fill_title(), fill_instruction() 36 литералов 1 398 симв.
data/static/common.js (строка 115) 1 литерал 19 симв.
Итого ~190 уникальных, 667 слов в HTML 6 131 симв. ≈ 12,3 КБ

Остальные 37 466 кириллических символов в src/ — комментарии и changelog, в бинарник не попадают.

Почему дубликат дерева /eng/ не подходит

Свободное место в LittleFS, посчитано по блокам:

Модель Размер ФС Блок Занято Свободно
esp01_1m (Classic, ESP-01) 262 144 Б 4 096 225 280 Б (55 блоков) 36 864 Б
waterius_2 (ESP-12F) 1 024 000 Б 8 192 303 104 Б (37 блоков) 720 896 Б

Дубликат дерева — 19 новых файлов, то есть минимум 19 блоков = 77 824 Б на Classic. Не влезает вдвое. Плюс любая правка вёрстки делается в двух местах.

Словарь влезает: английский текст (1 байт/символ вместо 2) в отдельном файле — +12 КБ нетто.

Запас, если станет тесно: /static/ (common.js 14 КБ + strings.js 8 КБ + style.css 7,7 КБ) отдаётся через serveStatic (active_point.cpp:459-460), который умеет .gz автоматически — это отмечено в комментарии active_point.cpp:436. Сжатие этих трёх файлов сокращает 8 блоков до 3 и освобождает 20 480 Б на Classic.

Две ловушки, которые определяют архитектуру

1. В captive-портале iOS отключён JavaScript

Комментарий active_point.cpp:438: «SAFARI (IOS) popup browser has some severe limitations (javascript disabled, cookies disabled)». Это не теория — четыре страницы captive_portal*.html намеренно написаны без единого <script>, только style.css.

Значит перевод на JS их в iOS-попапе не достанет.

Лечится дёшево: on_root() (active_point.cpp:327-357) — четыре вызова request->send(LittleFS, "/captive_portal*.html", ...). Достаточно прочитать заголовок Accept-Language и подставить префикс пути. ~20 строк C++, язык определяется до того, как пользователь что-то нажал.

2. В картинках счётчиков зашита кириллица

На meter-cold-0.png подписи «ГВС» (красным) и «ХВС» (синим). Таких файлов 8 (meter-{cold,hot,electro,gas}-{0,1}.png, ~40 КБ) — нужна перерисовка. icons.png и tableau.png чистые (иконки и цифры).

Предлагаемое решение

Ключи в HTML + словарь, с русским как встроенным фолбэком. Текст в HTML остаётся на месте, рядом добавляется атрибут:

<p class="text" data-i18n="wifi_list.hint">Выберите сеть 2.4Ghz WiFi через которую…</p>
  • при lang=ru скрипт не делает ничего — русский путь физически не может сломаться, правки только аддитивные;
  • при lang=en текст подменяется по ключу;
  • отсутствующий ключ молча оставляет русский;
  • язык определяется по navigator.language, запоминается в localStorage;
  • переключатель — та самая ссылка Eng, для которой уже есть стиль.

Поле языка в Settings не нужно — прошивка ничего не локализует, выбор живёт в браузере. Если понадобится хранить, в reserved9 свободно ~95 байт (прецеденты: ntp_sync_count, last_time_sync).

Смета

Работа Объём
Проставить data-i18n в 19 HTML 229 атрибутов, скриптуется
Рантайм: автоопределение, подстановка, переключатель, localStorage ~80 строк JS
Словарь en.js ~190 строк, ~9 КБ
strings.js → двухъязычный (tr() + fill_*) ~40 строк правок
4 captive-страницы через Accept-Language ~20 строк C++ + 4 файла (16 КБ)
Проверка полноты словаря в CI ~50 строк python
gzip /static/ — компенсировать место на Classic ~20 строк в сборке
Перерисовать 8 PNG (ГВС/ХВС → HW/CW) дизайн

Риски

Главный риск — не объём, а проверка. У портала нет ни одного теста: хостовый харнесс собирает только src/core/ (build_src_filter = -<*> +<core/*>), вся вёрстка вне его. Проверять придётся руками на живом устройстве, обе модели, 19 страниц × 2 языка, включая мастер первичной настройки.

Скрипт-сверка ключей в CI ловит только пропущенные строки, но не поехавшую вёрстку: английские фразы бывают длиннее русских, и кнопки с <label> в узкой мобильной раскладке могут переноситься.

Открытый вопрос

Английский нужен только в интерфейсе настройки, или ещё в письмах / личном кабинете / телеграм-боте? Если шире — это другие репозитории и другая смета.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestespesp firmwareresearchGood for newcomers

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions