mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-08-06 18:59:43 +00:00
feat: add Cyrillic keyboard layout pack
This commit is contained in:
@@ -27,7 +27,8 @@ Trail Mate 把本地化明确建模为三类 pack:
|
||||
- `Font Pack`
|
||||
拥有字形覆盖元数据以及外部 `font.bin` 资源。
|
||||
- `IME Pack`
|
||||
声明脚本特定的输入行为,例如简体中文拼音。IME pack 只有在固件里已经有真实后端时才允许进入 runtime payload。
|
||||
声明脚本特定的输入行为,例如简体中文拼音,或直接提交目标脚本字符的键盘布局。
|
||||
IME pack 只有在固件里已经有真实后端时才允许进入 runtime payload。
|
||||
|
||||
Settings 页选择的是 `Locale Pack`,而不是直接选择字体或 IME。
|
||||
|
||||
@@ -235,16 +236,29 @@ display_name=Pinyin
|
||||
backend=builtin-pinyin
|
||||
```
|
||||
|
||||
直接键盘布局使用同一类 IME manifest,但必须把后端与布局分开声明:
|
||||
|
||||
```ini
|
||||
kind=ime
|
||||
id=ru-cyrillic-keyboard
|
||||
display_name=Russian Cyrillic Keyboard
|
||||
backend=builtin-keyboard-layout
|
||||
layout=ru-cyrillic
|
||||
```
|
||||
|
||||
目前可启用的输入法实现是:
|
||||
|
||||
- `zh-hans-pinyin`,后端为 `builtin-pinyin`
|
||||
- `ru-cyrillic-keyboard`,后端为 `builtin-keyboard-layout`,布局为 `ru-cyrillic`
|
||||
|
||||
backend 的真实实现位于固件代码中。IME pack manifest 是把它注册进运行时并暴露出来的那一层。
|
||||
`layout` 是直接键盘布局后端的目标布局名;转换型后端例如拼音可以不声明 `layout`。
|
||||
|
||||
硬规则:
|
||||
|
||||
- 只有已经有真实输入引擎和正确候选词表的 IME 才能出现在 runtime payload 中。
|
||||
- 未实现的假名、韩文、阿拉伯、西里尔、繁中注音/仓颉和泛 Latin 软键盘,不得用 `backend=builtin-*` 提前占位。
|
||||
- 未实现的假名、韩文、阿拉伯、繁中注音/仓颉和泛 Latin 软键盘,不得用 `backend=builtin-*` 提前占位。
|
||||
- 西里尔输入只有 `ru-cyrillic-keyboard` 当前可发布;它是直接键盘布局,不是候选词转换引擎。
|
||||
- 台湾繁中不得默认拼音。未来 `zh-Hant-TW` 的首选输入法应是注音,仓颉/速成可以作为可选扩展。
|
||||
- 欧洲 Latin 语言通常不需要 IME pack;沿用普通 `EN` / `123` 输入路径即可。
|
||||
- locale manifest 只有在真实 IME 可用时才写 `ime_pack`。display-only locale 不写假依赖。
|
||||
@@ -523,11 +537,12 @@ runtime registry 仍然直接从 `/trailmate/packs` 对已解包资源进行编
|
||||
|
||||
## 当前 IME 覆盖
|
||||
|
||||
目前仓库 bundle 中,只有简体中文声明并发布了 IME pack:
|
||||
目前仓库 bundle 中,简体中文和俄语声明了 IME pack:
|
||||
|
||||
- `zh-Hans` -> `zh-hans-pinyin`
|
||||
- `ru` -> `ru-cyrillic-keyboard`
|
||||
|
||||
繁体中文、日文、韩文、阿拉伯语、俄语和欧洲 Latin bundle 目前都是 display-only review 包。它们有意继续沿用现有 `EN` / `123` 输入路径,直到对应脚本的专用 IME pack 出现。
|
||||
繁体中文、日文、韩文、阿拉伯语和欧洲 Latin bundle 目前都是 display-only review 包。它们有意继续沿用现有 `EN` / `123` 输入路径,直到对应脚本的专用 IME pack 出现。
|
||||
|
||||
具体约束:
|
||||
|
||||
@@ -535,6 +550,7 @@ runtime registry 仍然直接从 `/trailmate/packs` 对已解包资源进行编
|
||||
- `ja` 必须等假名/罗马字日文输入模型完成后才能挂 IME。
|
||||
- `ko` 必须等韩文输入模型完成后才能挂 IME。
|
||||
- `ar` 必须等阿拉伯键盘、RTL 编辑与 shaping 行为闭环后才能挂 IME。
|
||||
- `ru` 可以挂 `ru-cyrillic-keyboard`;该能力只承诺直接 Cyrillic 键盘布局,不承诺物理 QWERTY 到俄文的硬件重映射。
|
||||
- 欧洲 Latin locale 不应为了“有输入法列表项”而挂虚假的 `builtin-latin`。
|
||||
|
||||
## 新增一种语言
|
||||
|
||||
@@ -232,12 +232,16 @@ python scripts/build_pack_repository.py --pack-root packs --site-root site
|
||||
|
||||
1. `zh-Hans`
|
||||
- 可以作为 `release`。
|
||||
- 当前唯一可发布 IME 是 `zh-hans-pinyin`。
|
||||
- 可发布 IME 是 `zh-hans-pinyin`。
|
||||
2. `zh-Hant`
|
||||
- 按台湾繁中 `review` 包处理。
|
||||
- 不挂拼音 IME。
|
||||
- 进入 `release` 前必须经过台湾用语与输入法习惯审查。
|
||||
3. `ar`、`ru`、`de`、`es`、`fr`、`it`、`nl`、`pl`、`pt-PT`、`ja`、`ko`
|
||||
3. `ru`
|
||||
- 可以作为 `review` 包处理。
|
||||
- 可发布 IME 是 `ru-cyrillic-keyboard`,后端为 `builtin-keyboard-layout`,布局为 `ru-cyrillic`。
|
||||
- 该 IME 是直接虚拟键盘布局,不承诺物理键盘硬件重映射。
|
||||
4. `ar`、`de`、`es`、`fr`、`it`、`nl`、`pl`、`pt-PT`、`ja`、`ko`
|
||||
- 可打包为 `review`。
|
||||
- 不进入普通用户语言选择器。
|
||||
- 不发布假 IME。
|
||||
|
||||
@@ -182,9 +182,12 @@ IME pack manifest 只是输入法后端的声明,不是后端本身。
|
||||
一个输入法要成为可启用输入法,必须同时满足:
|
||||
|
||||
1. 固件中存在真实后端。
|
||||
2. 候选词、组合逻辑、提交行为与目标语言/地区习惯匹配。
|
||||
2. 候选词、组合逻辑或直接键盘布局、提交行为与目标语言/地区习惯匹配。
|
||||
3. UI widget 知道如何展示该输入模式。
|
||||
4. locale manifest 显式依赖它,且该 locale 已经是可发布质量。
|
||||
4. 需要作为 locale 默认输入能力时,locale manifest 显式依赖它。
|
||||
|
||||
`translation_status` 仍然独立决定 locale 是否进入普通用户语言选择器。
|
||||
`review` locale 可以依赖真实 IME 以便运行时完整校验和安装该能力,但不能因此被当成 release UI locale。
|
||||
|
||||
因此,不允许用 `backend=builtin-kana`、`backend=builtin-hangul`、`backend=builtin-arabic` 这类尚未实现的名字提前占位。未实现输入法可以写进规划文档,但不能作为 runtime IME pack 进入 payload。
|
||||
|
||||
@@ -283,6 +286,11 @@ locale id 必须按 BCP-47 思路表达语言、脚本与地区语境,而不
|
||||
- `zh-hans-pinyin`
|
||||
- 后端:`builtin-pinyin`
|
||||
- 适用范围:简体中文拼音输入。
|
||||
2. `ru`
|
||||
- `ru-cyrillic-keyboard`
|
||||
- 后端:`builtin-keyboard-layout`
|
||||
- 布局:`ru-cyrillic`
|
||||
- 适用范围:俄文 Cyrillic 直接键盘布局;不包含候选词转换,也不承诺物理 QWERTY 到俄文的硬件重映射。
|
||||
|
||||
当前不得启用的输入法包括:
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ struct ImeInfo
|
||||
const char* id = nullptr;
|
||||
const char* display_name = nullptr;
|
||||
const char* backend = nullptr;
|
||||
const char* layout = nullptr;
|
||||
bool builtin = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ class ImeWidget
|
||||
enum class Mode
|
||||
{
|
||||
EN,
|
||||
CN,
|
||||
SCRIPT,
|
||||
CN = SCRIPT,
|
||||
NUM
|
||||
};
|
||||
|
||||
@@ -57,7 +58,10 @@ class ImeWidget
|
||||
void refresh_touch_candidates();
|
||||
void sync_textarea();
|
||||
bool handle_key_code(uint32_t key);
|
||||
bool handle_text_token(const char* token);
|
||||
bool commit_candidate(int candidate_index);
|
||||
bool pinyin_mode() const;
|
||||
bool direct_keyboard_mode() const;
|
||||
|
||||
static void on_toggle_clicked(lv_event_t* e);
|
||||
static void on_touch_key_event(lv_event_t* e);
|
||||
|
||||
@@ -96,6 +96,7 @@ struct ImePackRecord
|
||||
std::string id;
|
||||
std::string display_name;
|
||||
std::string backend;
|
||||
std::string layout;
|
||||
bool builtin = false;
|
||||
};
|
||||
|
||||
@@ -557,10 +558,13 @@ void append_unique_string(std::vector<std::string>& values, const std::string& v
|
||||
|
||||
bool ime_backend_supports_runtime_input(const ImePackRecord& pack)
|
||||
{
|
||||
// The current widget has one real conversion engine and dictionary:
|
||||
// Simplified Chinese Pinyin. Other manifests may still be cataloged as
|
||||
// pack metadata, but exposing them would route users into the wrong script.
|
||||
return pack.id == "zh-hans-pinyin" && pack.backend == "builtin-pinyin";
|
||||
if (pack.id == "zh-hans-pinyin" && pack.backend == "builtin-pinyin")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return pack.id == "ru-cyrillic-keyboard" &&
|
||||
pack.backend == "builtin-keyboard-layout" &&
|
||||
pack.layout == "ru-cyrillic";
|
||||
}
|
||||
|
||||
bool ime_backend_can_be_enabled(const ImePackRecord& pack)
|
||||
@@ -1446,6 +1450,7 @@ void rebuild_ime_views()
|
||||
view.id = pack.id.c_str();
|
||||
view.display_name = pack.display_name.c_str();
|
||||
view.backend = pack.backend.c_str();
|
||||
view.layout = pack.layout.empty() ? nullptr : pack.layout.c_str();
|
||||
view.builtin = pack.builtin;
|
||||
s_ime_views.push_back(view);
|
||||
}
|
||||
@@ -1619,11 +1624,16 @@ bool catalog_external_ime_pack(const std::string& pack_dir)
|
||||
{
|
||||
pack.backend = "none";
|
||||
}
|
||||
if (const char* layout = manifest_value(manifest, "layout"))
|
||||
{
|
||||
pack.layout = layout;
|
||||
}
|
||||
|
||||
std::printf("%s ime pack catalog id=%s backend=%s\n",
|
||||
std::printf("%s ime pack catalog id=%s backend=%s layout=%s\n",
|
||||
kLogTag,
|
||||
pack.id.c_str(),
|
||||
pack.backend.c_str());
|
||||
pack.backend.c_str(),
|
||||
pack.layout.empty() ? "<none>" : pack.layout.c_str());
|
||||
s_ime_packs.push_back(std::move(pack));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,58 @@ static constexpr int kCompactCandidatesPerPage = 7;
|
||||
static constexpr lv_coord_t kCompactImeRowHeight = 22;
|
||||
static constexpr lv_coord_t kCompactImeControlHeight = 18;
|
||||
|
||||
bool script_input_available()
|
||||
enum class ScriptInputKind
|
||||
{
|
||||
return ::ui::i18n::any_enabled_script_input();
|
||||
None,
|
||||
Pinyin,
|
||||
RuCyrillicKeyboard
|
||||
};
|
||||
|
||||
const ::ui::i18n::ImeInfo* active_ime_info()
|
||||
{
|
||||
const char* active_id = ::ui::i18n::active_ime_pack_id();
|
||||
if (!active_id || active_id[0] == '\0')
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::size_t count = ::ui::i18n::ime_count();
|
||||
for (std::size_t index = 0; index < count; ++index)
|
||||
{
|
||||
const ::ui::i18n::ImeInfo* ime = ::ui::i18n::ime_at(index);
|
||||
if (ime != nullptr && ime->id != nullptr && std::strcmp(ime->id, active_id) == 0)
|
||||
{
|
||||
return ime;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScriptInputKind active_script_input_kind()
|
||||
{
|
||||
const ::ui::i18n::ImeInfo* ime = active_ime_info();
|
||||
if (!ime || !ime->id || !ime->backend)
|
||||
{
|
||||
return ScriptInputKind::None;
|
||||
}
|
||||
if (std::strcmp(ime->id, "zh-hans-pinyin") == 0 &&
|
||||
std::strcmp(ime->backend, "builtin-pinyin") == 0)
|
||||
{
|
||||
return ScriptInputKind::Pinyin;
|
||||
}
|
||||
if (std::strcmp(ime->id, "ru-cyrillic-keyboard") == 0 &&
|
||||
std::strcmp(ime->backend, "builtin-keyboard-layout") == 0 &&
|
||||
ime->layout != nullptr &&
|
||||
std::strcmp(ime->layout, "ru-cyrillic") == 0)
|
||||
{
|
||||
return ScriptInputKind::RuCyrillicKeyboard;
|
||||
}
|
||||
return ScriptInputKind::None;
|
||||
}
|
||||
|
||||
bool script_mode(ImeWidget::Mode mode)
|
||||
{
|
||||
return mode == ImeWidget::Mode::SCRIPT;
|
||||
}
|
||||
|
||||
bool hardware_keyboard_available()
|
||||
@@ -57,6 +106,15 @@ static const char* kTouchNumMap[] = {
|
||||
"-", "/", ":", ";", "(", ")", "$", "&", "@", "Enter", "\n",
|
||||
".", ",", "?", "!", "'", "\"", "%", "+", "\n",
|
||||
"Space", ""};
|
||||
|
||||
static const char* kTouchRuCyrillicMap[] = {
|
||||
"й", "ц", "у", "к", "е", "н", "г", "ш", "щ", "з", "х", "ъ", "Bksp", "\n",
|
||||
"ф", "ы", "в", "а", "п", "р", "о", "л", "д", "ж", "э", "Enter", "\n",
|
||||
"я", "ч", "с", "м", "и", "т", "ь", "б", "ю", ",", ".", "?", "\n",
|
||||
"Space", ""};
|
||||
|
||||
static constexpr const char* kRuCyrillicFontProbe =
|
||||
"йцукенгшщзхъфывапролджэячсмитьбю";
|
||||
#endif
|
||||
std::string make_candidates_text(const std::vector<std::string>& candidates,
|
||||
int active_idx,
|
||||
@@ -173,6 +231,14 @@ bool translate_touch_token(const char* token, uint32_t& key)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool touch_token_is_action(const char* token)
|
||||
{
|
||||
return token != nullptr &&
|
||||
(std::strcmp(token, "Bksp") == 0 ||
|
||||
std::strcmp(token, "Enter") == 0 ||
|
||||
std::strcmp(token, "Space") == 0);
|
||||
}
|
||||
|
||||
bool resolve_touch_button_id(lv_event_t* e, lv_obj_t* matrix, uint32_t& button_id)
|
||||
{
|
||||
if (!matrix)
|
||||
@@ -465,18 +531,20 @@ void ImeWidget::detach()
|
||||
|
||||
void ImeWidget::setMode(Mode mode)
|
||||
{
|
||||
if (mode == Mode::CN && !script_input_available())
|
||||
const ScriptInputKind requested_kind = active_script_input_kind();
|
||||
if (script_mode(mode) && requested_kind == ScriptInputKind::None)
|
||||
{
|
||||
mode = Mode::EN;
|
||||
}
|
||||
|
||||
mode_ = mode;
|
||||
ime_.setEnabled(mode_ == Mode::CN);
|
||||
const bool use_pinyin = pinyin_mode();
|
||||
ime_.setEnabled(use_pinyin);
|
||||
if (textarea_)
|
||||
{
|
||||
const char* cur = lv_textarea_get_text(textarea_);
|
||||
committed_text_ = cur ? std::string(cur) : std::string();
|
||||
if (mode_ == Mode::CN)
|
||||
if (use_pinyin)
|
||||
{
|
||||
ime_.reset();
|
||||
lv_textarea_set_accepted_chars(textarea_, "");
|
||||
@@ -496,9 +564,20 @@ ImeWidget::Mode ImeWidget::mode() const
|
||||
return mode_;
|
||||
}
|
||||
|
||||
bool ImeWidget::pinyin_mode() const
|
||||
{
|
||||
return script_mode(mode_) && active_script_input_kind() == ScriptInputKind::Pinyin;
|
||||
}
|
||||
|
||||
bool ImeWidget::direct_keyboard_mode() const
|
||||
{
|
||||
return script_mode(mode_) &&
|
||||
active_script_input_kind() == ScriptInputKind::RuCyrillicKeyboard;
|
||||
}
|
||||
|
||||
void ImeWidget::cycleMode()
|
||||
{
|
||||
if (!script_input_available())
|
||||
if (active_script_input_kind() == ScriptInputKind::None)
|
||||
{
|
||||
setMode(mode_ == Mode::EN ? Mode::NUM : Mode::EN);
|
||||
return;
|
||||
@@ -506,9 +585,9 @@ void ImeWidget::cycleMode()
|
||||
|
||||
if (mode_ == Mode::EN)
|
||||
{
|
||||
setMode(Mode::CN);
|
||||
setMode(Mode::SCRIPT);
|
||||
}
|
||||
else if (mode_ == Mode::CN)
|
||||
else if (script_mode(mode_))
|
||||
{
|
||||
setMode(Mode::NUM);
|
||||
}
|
||||
@@ -542,7 +621,7 @@ bool ImeWidget::handle_key_code(uint32_t key)
|
||||
bool consumed = false;
|
||||
bool update_text = false;
|
||||
|
||||
if (mode_ == Mode::CN)
|
||||
if (pinyin_mode())
|
||||
{
|
||||
if (key == LV_KEY_BACKSPACE)
|
||||
{
|
||||
@@ -679,6 +758,19 @@ bool ImeWidget::handle_key_code(uint32_t key)
|
||||
return consumed;
|
||||
}
|
||||
|
||||
bool ImeWidget::handle_text_token(const char* token)
|
||||
{
|
||||
if (!textarea_ || !token || token[0] == '\0' || !direct_keyboard_mode())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
committed_text_ += token;
|
||||
sync_textarea();
|
||||
refresh_labels();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImeWidget::handle_key(lv_event_t* e)
|
||||
{
|
||||
if (!textarea_ || !e)
|
||||
@@ -718,7 +810,7 @@ void ImeWidget::setText(const char* text)
|
||||
if (!textarea_) return;
|
||||
committed_text_ = text ? std::string(text) : std::string();
|
||||
sync_textarea();
|
||||
if (mode_ == Mode::CN)
|
||||
if (pinyin_mode())
|
||||
{
|
||||
ime_.reset();
|
||||
}
|
||||
@@ -732,7 +824,18 @@ void ImeWidget::refresh_touch_keyboard()
|
||||
{
|
||||
return;
|
||||
}
|
||||
const char* const* map = (mode_ == Mode::NUM) ? kTouchNumMap : kTouchEnMap;
|
||||
const char* const* map = kTouchEnMap;
|
||||
const lv_font_t* font = ::ui::fonts::localized_font(::ui::fonts::ui_chrome_font());
|
||||
if (mode_ == Mode::NUM)
|
||||
{
|
||||
map = kTouchNumMap;
|
||||
}
|
||||
else if (direct_keyboard_mode())
|
||||
{
|
||||
map = kTouchRuCyrillicMap;
|
||||
font = ::ui::fonts::content_font(kRuCyrillicFontProbe, ::ui::fonts::ui_chrome_font());
|
||||
}
|
||||
lv_obj_set_style_text_font(keyboard_matrix_, font, LV_PART_ITEMS);
|
||||
lv_btnmatrix_set_map(keyboard_matrix_, map);
|
||||
}
|
||||
|
||||
@@ -743,7 +846,7 @@ void ImeWidget::refresh_touch_candidates()
|
||||
return;
|
||||
}
|
||||
|
||||
const bool show_candidates = mode_ == Mode::CN;
|
||||
const bool show_candidates = pinyin_mode();
|
||||
if (!show_candidates)
|
||||
{
|
||||
lv_obj_add_flag(candidate_row_, LV_OBJ_FLAG_HIDDEN);
|
||||
@@ -829,7 +932,7 @@ void ImeWidget::refresh_labels()
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode_ == Mode::CN && !script_input_available())
|
||||
if (script_mode(mode_) && active_script_input_kind() == ScriptInputKind::None)
|
||||
{
|
||||
mode_ = Mode::EN;
|
||||
ime_.setEnabled(false);
|
||||
@@ -837,7 +940,7 @@ void ImeWidget::refresh_labels()
|
||||
|
||||
if (textarea_)
|
||||
{
|
||||
if (mode_ == Mode::CN && ime_.hasBuffer())
|
||||
if (pinyin_mode() && ime_.hasBuffer())
|
||||
{
|
||||
if (lv_group_t* g = lv_group_get_default())
|
||||
{
|
||||
@@ -889,6 +992,23 @@ void ImeWidget::refresh_labels()
|
||||
return;
|
||||
}
|
||||
|
||||
const ScriptInputKind script_kind = active_script_input_kind();
|
||||
if (script_kind == ScriptInputKind::RuCyrillicKeyboard)
|
||||
{
|
||||
lv_label_set_text(toggle_label_, "RU");
|
||||
#if UI_SHARED_TOUCH_IME_ENABLED
|
||||
if (touch_keyboard_enabled_)
|
||||
{
|
||||
set_candidates_label_text(candidates_label_, ::ui::i18n::tr("Cyrillic keyboard"));
|
||||
refresh_touch_keyboard();
|
||||
refresh_touch_candidates();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
set_candidates_label_text(candidates_label_, "");
|
||||
return;
|
||||
}
|
||||
|
||||
lv_label_set_text(toggle_label_, "CN");
|
||||
#if UI_SHARED_TOUCH_IME_ENABLED
|
||||
if (touch_keyboard_enabled_)
|
||||
@@ -912,7 +1032,7 @@ void ImeWidget::refresh_candidates()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ime_.hasBuffer())
|
||||
if (!pinyin_mode() || !ime_.hasBuffer())
|
||||
{
|
||||
set_candidates_label_text(candidates_label_, "");
|
||||
return;
|
||||
@@ -960,6 +1080,12 @@ void ImeWidget::on_touch_key_event(lv_event_t* e)
|
||||
}
|
||||
|
||||
const char* token = lv_btnmatrix_get_btn_text(self->keyboard_matrix_, button_id);
|
||||
if (self->direct_keyboard_mode() && !touch_token_is_action(token))
|
||||
{
|
||||
(void)self->handle_text_token(token);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t key = 0;
|
||||
if (!translate_touch_token(token, key))
|
||||
{
|
||||
|
||||
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) جهات الاتصال (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) جهات الاتصال (RNode)
|
||||
Contacts (RNode - %.3fMHz) جهات الاتصال (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch عدم تطابق بروتوكول المحادثة
|
||||
Cyrillic keyboard لوحة مفاتيح سيريلية
|
||||
Data Exchange تبادل البيانات
|
||||
Debug Logs سجلات التصحيح
|
||||
Decoding: %d%% فك التشفير: %d%%
|
||||
|
||||
|
@@ -1,9 +1,11 @@
|
||||
European Cyrillic Extended installs the Russian locale together with a shared
|
||||
Cyrillic external font pack so interface labels and Russian content can render
|
||||
without inflating the firmware image.
|
||||
without inflating the firmware image. It also registers a Russian Cyrillic
|
||||
keyboard layout backed by the firmware's direct keyboard-layout runtime.
|
||||
|
||||
This bundle is display-only today. It does not add a Russian IME, and the locale
|
||||
remains in review status until native-language review is complete.
|
||||
The locale remains in review status until native-language review is complete.
|
||||
The keyboard layout is a direct virtual-keyboard layout; it does not provide
|
||||
candidate conversion or physical keyboard remapping.
|
||||
|
||||
Credits: Russian translations were contributed by polarikus, based on the
|
||||
polarikus/trail-mate Russian localization PR.
|
||||
|
||||
@@ -6,6 +6,7 @@ It contains:
|
||||
|
||||
- locale pack: `ru`
|
||||
- font pack: `cyrillic-eu`
|
||||
- IME pack: `ru-cyrillic-keyboard`
|
||||
|
||||
The Russian translations were contributed by polarikus.
|
||||
|
||||
@@ -20,6 +21,7 @@ The locale manifest points to:
|
||||
|
||||
- `ui_font_pack=cyrillic-eu`
|
||||
- `content_font_pack=cyrillic-eu`
|
||||
- `ime_pack=ru-cyrillic-keyboard`
|
||||
|
||||
Bundle-level package metadata lives in:
|
||||
|
||||
@@ -41,6 +43,8 @@ Bundle-level package metadata lives in:
|
||||
- `locales/ru/`
|
||||
- `manifest.ini`
|
||||
- `strings.tsv`
|
||||
- `ime/ru-cyrillic-keyboard/`
|
||||
- `manifest.ini`
|
||||
|
||||
## Generate The Font Pack
|
||||
|
||||
@@ -65,7 +69,11 @@ python tools/generate_binfont_with_lv_font_conv.py `
|
||||
|
||||
Notes:
|
||||
|
||||
- This bundle is display-only for now. It does not declare an IME pack yet.
|
||||
- The Russian locale is still in `translation_status=review`, but the bundle now
|
||||
declares the real `ru-cyrillic-keyboard` IME pack.
|
||||
- `ru-cyrillic-keyboard` is a direct virtual-keyboard layout backed by
|
||||
`backend=builtin-keyboard-layout` and `layout=ru-cyrillic`; it is not a
|
||||
candidate-conversion engine and does not remap physical keyboard hardware.
|
||||
- `build.ini` is the source-of-truth for generating `font.bin` during Pages/package builds.
|
||||
- `font.bin` is ignored by Git. Regenerate it whenever the subset changes.
|
||||
- `ranges.txt` and `estimated_ram_bytes` let the runtime decide whether the pack fits the active memory profile.
|
||||
@@ -75,7 +83,7 @@ Notes:
|
||||
|
||||
`python scripts/build_pack_repository.py --pack-root packs --site-root site` produces:
|
||||
|
||||
- `site/assets/packs/europe-cyrillic-ext-1.1.1.zip`
|
||||
- `site/assets/packs/europe-cyrillic-ext-1.2.0.zip`
|
||||
- `site/data/packs.json`
|
||||
|
||||
The zip is the bundle artifact for a future Extensions downloader. Its `payload/` directory unpacks into `/trailmate/packs/...`.
|
||||
@@ -90,6 +98,7 @@ Copy the bundle contents so the SD card ends up with:
|
||||
/trailmate/packs/fonts/cyrillic-eu/font.bin
|
||||
/trailmate/packs/locales/ru/manifest.ini
|
||||
/trailmate/packs/locales/ru/strings.tsv
|
||||
/trailmate/packs/ime/ru-cyrillic-keyboard/manifest.ini
|
||||
```
|
||||
|
||||
After reboot, `Русский` appears in Settings only after the locale is promoted to `translation_status=release`. While it is in `review`, the runtime can install and index the pack but will not offer it as a selectable UI language.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
kind=ime
|
||||
id=ru-cyrillic-keyboard
|
||||
display_name=Russian Cyrillic Keyboard
|
||||
backend=builtin-keyboard-layout
|
||||
layout=ru-cyrillic
|
||||
@@ -5,4 +5,5 @@ native_name=Русский
|
||||
translation_status=review
|
||||
ui_font_pack=cyrillic-eu
|
||||
content_font_pack=cyrillic-eu
|
||||
ime_pack=ru-cyrillic-keyboard
|
||||
strings=strings.tsv
|
||||
|
||||
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Контакты (Meshtastic - %.3fМГц)
|
||||
Contacts (RNode) Контакты (RNode)
|
||||
Contacts (RNode - %.3fMHz) Контакты (RNode - %.3fМГц)
|
||||
Conversation protocol mismatch Несовпадение протокола
|
||||
Cyrillic keyboard Кириллическая клавиатура
|
||||
Data Exchange Обмен данными
|
||||
Debug Logs Журналы отладки
|
||||
Decoding: %d%% Декодирование: %d%%
|
||||
|
||||
|
@@ -1,14 +1,14 @@
|
||||
kind=package
|
||||
id=europe-cyrillic-ext
|
||||
package_type=locale-bundle
|
||||
version=1.1.1
|
||||
version=1.2.0
|
||||
display_name=European Cyrillic Extended
|
||||
summary=Shared Cyrillic font coverage plus a Russian review locale pack; display-only until native review is complete.
|
||||
summary=Shared Cyrillic font coverage, a Russian review locale pack, and a Russian Cyrillic keyboard layout.
|
||||
description=DESCRIPTION.txt
|
||||
readme=README.md
|
||||
author=Trail Mate
|
||||
homepage=https://vicliu624.github.io/trail-mate/
|
||||
min_firmware_version=0.1.24-alpha
|
||||
min_firmware_version=0.1.25-alpha
|
||||
supported_memory_profiles=constrained,standard,extended
|
||||
tags=language,cyrillic,europe,russian,review
|
||||
tags=language,cyrillic,europe,russian,review,ime
|
||||
; Russian translations contributed by polarikus.
|
||||
|
||||
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Kontakte (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) Kontakte (RNode)
|
||||
Contacts (RNode - %.3fMHz) Kontakte (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch Konversationsprotokoll stimmt nicht überein
|
||||
Cyrillic keyboard Kyrillische Tastatur
|
||||
Data Exchange Datenaustausch
|
||||
Debug Logs Debug-Protokolle
|
||||
Decoding: %d%% Dekodierung: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Contactos (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) Contactos (RNode)
|
||||
Contacts (RNode - %.3fMHz) Contactos (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch Protocolo no coincide
|
||||
Cyrillic keyboard Teclado cirílico
|
||||
Data Exchange Intercambio datos
|
||||
Debug Logs Registros depuración
|
||||
Decoding: %d%% Decodificando: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Contacts (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) Contacts (RNode)
|
||||
Contacts (RNode - %.3fMHz) Contacts (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch Protocole incompatible
|
||||
Cyrillic keyboard Clavier cyrillique
|
||||
Data Exchange Échange données
|
||||
Debug Logs Journaux débogage
|
||||
Decoding: %d%% Décodage : %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Contatti (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) Contatti (RNode)
|
||||
Contacts (RNode - %.3fMHz) Contatti (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch Protocollo non corrisponde
|
||||
Cyrillic keyboard Tastiera cirillica
|
||||
Data Exchange Scambio dati
|
||||
Debug Logs Log di debug
|
||||
Decoding: %d%% Decodifica: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Contacten (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) Contacten (RNode)
|
||||
Contacts (RNode - %.3fMHz) Contacten (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch Gespreksprotocol komt niet overeen
|
||||
Cyrillic keyboard Cyrillisch toetsenbord
|
||||
Data Exchange Gegevensuitwisseling
|
||||
Debug Logs Foutopsporingslogboeken
|
||||
Decoding: %d%% Decodering: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Kontakty (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) Kontakty (RNode)
|
||||
Contacts (RNode - %.3fMHz) Kontakty (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch Niezgodność protokołu konwersacji
|
||||
Cyrillic keyboard Klawiatura cyryliczna
|
||||
Data Exchange Wymiana danych
|
||||
Debug Logs Dzienniki debugowania
|
||||
Decoding: %d%% Dekodowanie: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) Contatos (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) Contatos (RNode)
|
||||
Contacts (RNode - %.3fMHz) Contatos (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch Incompatibilidade de protocolo de conversação
|
||||
Cyrillic keyboard Teclado cirílico
|
||||
Data Exchange Troca de dados
|
||||
Debug Logs Logs de depuração
|
||||
Decoding: %d%% Decodificação: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) 連絡先 (Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) 連絡先 (RNode)
|
||||
Contacts (RNode - %.3fMHz) 連絡先 (RNode - %.3fMHz)
|
||||
Conversation protocol mismatch 会話プロトコルが一致しません
|
||||
Cyrillic keyboard キリル キーボード
|
||||
Data Exchange データ交換
|
||||
Debug Logs デバッグ ログ
|
||||
Decoding: %d%% デコード: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) 연락처(Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) 연락처(RNode)
|
||||
Contacts (RNode - %.3fMHz) 연락처(RNode - %.3fMHz)
|
||||
Conversation protocol mismatch 대화 프로토콜 불일치
|
||||
Cyrillic keyboard 키릴 키보드
|
||||
Data Exchange 데이터 교환
|
||||
Debug Logs 디버그 로그
|
||||
Decoding: %d%% 디코딩: %d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) 联系人(Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) 联系人(RNode)
|
||||
Contacts (RNode - %.3fMHz) 联系人(RNode - %.3fMHz)
|
||||
Conversation protocol mismatch 会话协议不匹配
|
||||
Cyrillic keyboard 西里尔键盘
|
||||
Data Exchange 数据交换
|
||||
Debug Logs 调试日志
|
||||
Decoding: %d%% 解码中:%d%%
|
||||
|
||||
|
@@ -42,6 +42,7 @@ Contacts (Meshtastic - %.3fMHz) 聯絡人(Meshtastic - %.3fMHz)
|
||||
Contacts (RNode) 聯絡人(RNode)
|
||||
Contacts (RNode - %.3fMHz) 聯絡人(RNode - %.3fMHz)
|
||||
Conversation protocol mismatch 會話協議不匹配
|
||||
Cyrillic keyboard 西里爾鍵盤
|
||||
Data Exchange 資料交換
|
||||
Debug Logs 除錯日誌
|
||||
Decoding: %d%% 解碼中:%d%%
|
||||
|
||||
|
@@ -362,6 +362,7 @@ def collect_ime_records(stage_bundle_dir: Path) -> list[dict[str, object]]:
|
||||
"id": manifest.get("id", manifest_path.parent.name),
|
||||
"display_name": manifest.get("display_name", manifest_path.parent.name),
|
||||
"backend": manifest.get("backend", "none"),
|
||||
"layout": manifest.get("layout", ""),
|
||||
}
|
||||
)
|
||||
return records
|
||||
|
||||
@@ -43,6 +43,11 @@ ALLOWED_ENGLISH_EQUIVALENTS = {
|
||||
|
||||
REAL_IME_BACKENDS = {
|
||||
"builtin-pinyin": {"zh-hans-pinyin"},
|
||||
"builtin-keyboard-layout": {"ru-cyrillic-keyboard"},
|
||||
}
|
||||
|
||||
REAL_IME_LAYOUTS = {
|
||||
"ru-cyrillic-keyboard": "ru-cyrillic",
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +164,12 @@ def validate_manifests(pack_root: Path) -> list[str]:
|
||||
backend = manifest.get("backend", "none")
|
||||
if ime_id not in REAL_IME_BACKENDS.get(backend, set()):
|
||||
errors.append(f"{manifest_path}: unsupported runtime IME backend/id: {backend}/{ime_id}")
|
||||
expected_layout = REAL_IME_LAYOUTS.get(ime_id, "")
|
||||
layout = manifest.get("layout", "")
|
||||
if expected_layout and layout != expected_layout:
|
||||
errors.append(f"{manifest_path}: expected layout={expected_layout} for IME {ime_id}")
|
||||
elif not expected_layout and layout:
|
||||
errors.append(f"{manifest_path}: unexpected layout for non-layout IME: {ime_id}")
|
||||
ime_backends[ime_id] = backend
|
||||
|
||||
for manifest_path in sorted(pack_root.glob("*/locales/*/manifest.ini")):
|
||||
|
||||
Reference in New Issue
Block a user