M14: complete Simplified Chinese translation catalog (#209)

* M14: complete Simplified Chinese translation catalog

* M14: translate new XFA notices and counts

---------

Co-authored-by: Brandon Thomas <[email protected]>
This commit is contained in:
才倾梦华VandBrandon Thomas authored and GitHub committed 2026-10-07 23:45:02 -04:00
1 parent 42ddb2d546
commit 77893e8ab3
7 files changed
+333 -5

No files matched your search

+1 -1
View File
@@ -19,7 +19,7 @@ licence = "MIT OR Apache-2.0"
licence_file = "LICENSE-MIT"
kind = "translation"
usage = "Simplified Chinese UI strings, compiled in by crates/ui-egui/src/i18n/mod.rs"
sha256 = "1a39d1886fed368cc5e23c376505074b3955fd8bcbcd0678209126ce07a10a95"
sha256 = "16aafe71a832e881671ef63102b94346717885a14e6e33f41d9d6c0734bc95d6"
[[asset]]
path = "crates/ui-egui/src/i18n/zh-hant.tsv"
+3 -1
View File
@@ -103,7 +103,7 @@ Hours are for a single agent (low–high). "Done" is the estimated fraction of t
| M11 | Optimize, preflight, PDF/A/X/UA, print production | 200–350 | 18% | 165–290 | Done: new `optimize` crate: Reduce File Size and the PDF Optimizer (images measured where drawn, bicubic downsampling, JPEG/ZIP recompression only when smaller, discard objects and user data, Flate clean-up, resource merging, object streams). Missing: fonts and transparency panels, space audit, preflight, PDF/A/X/UA |
| M12 | Accessibility, compare, measure, search, XFA | 200–380 | 20% | 160–305 | Done: new `a11y` crate with the Accessibility Checker (all 32 rules, report, Fix/Skip/Explain, options dialog and results panel, agent tools). Missing: autotag, Tags/Order/Content panels, Reading Order tool, alt-text workflow, compare, measure, search index, XFA |
| M13 | Automation (MCP, Action Wizard, CLI) + AI providers | 60–120 | 45% | 33–66 | Done: MCP resources (document info, text, page images); headless tool table (123 tools incl. signing, optimizing, initial view, links, stamps, data exchange, comment review, forms authoring and scripts, redaction, sanitize, print, add content), opt-in MCP server over stdio, CLI `run`/`tools`, UI control channel with drag. Missing: Action Wizard, AI providers |
| M14 | 1.0 polish: performance, localization, installers | 120–250 | 5% | 115–240 | Done: PhotoCraft's translation system (`i18n/`: TSV catalogs, `tl!`, command-id and plural entries, system-language detection, strict catalog tests); every dialog, panel and notice goes through `tl!`; Japanese and Traditional Chinese complete for them (≈1,800 entries each), Simplified Chinese ≈1,600, Czech and Brazilian Portuguese for menus. Missing: more catalogs filled in, Windows language detection, installers, performance budgets |
| M14 | 1.0 polish: performance, localization, installers | 120–250 | 5% | 115–240 | Done: PhotoCraft's translation system (`i18n/`: TSV catalogs, `tl!`, command-id and plural entries, system-language detection, strict catalog tests); every dialog, panel and notice goes through `tl!`; Japanese, Traditional Chinese and Simplified Chinese catalogs have ≈1,800 entries each; Simplified Chinese coverage is checked against the UI source; Czech and Brazilian Portuguese for menus. Missing: more catalogs filled in, Windows language detection, installers, performance budgets |
| | **Total (original plan sizing)** | **2,085–3,840** | **≈ 35%** | **≈ 1,350–2,500 at the planned rate; ≈ 600–1,100 at the measured rate** | |
**Overall progress: about 30–35% of the effort (49.5% of features shipped).** The viewer and the core are far ahead of the editing features, because the viewer was built first so progress could be seen; rendering itself is still borrowed from `hayro`. See [Honest assessment](#honest-assessment-2026-10-05).
@@ -127,6 +127,8 @@ M0 → M1 → M2 → M3 → M4 must happen in order. After M4, M5–M12 can run
Newest first. One line per session: the date, what moved, and the new overall percentage.
- **2026-10-08 (M14, Simplified Chinese):** Added 208 translations, bringing the catalog from 1,625 to 1,833 entries. PhotoCraft-style coverage checks protect registered commands, All tools labels and UI `tl!` literals; generated history and diagnostic regressions preserve user values. Chinese font delivery remains incomplete: the release-pinned craft-fonts input has no Hans face and the web build only embeds the Japanese UI face (see #112 and #140). Localization remains partial; overall ≈ 30–35% (unchanged).
- **2026-10-07 (community, #190):** Create PDF from Images offers embedded resolution, 72 DPI, or custom DPI (1–1200) without resampling. The engine and doc_create share the choice; Windows image context menus open the chooser through --create-images. Overall estimate unchanged (about 30–35%).
- **2026-10-08 (community):** Cut and stack in Print > Multiple: single-sided n-up sheets can be cut into cell piles and restacked in selected page order, with aligned gutter marks and blanks kept in their cells. Available through doc_print; duplex is refused. Grid-size overflow returns an error. No change to the parity count or the overall estimate (about 30–35%).
+84
View File
@@ -670,6 +670,90 @@ mod tests {
}
}
/// Like PhotoCraft's complete-catalog gates, derive coverage from the UI, not another language.
#[test]
fn simplified_chinese_covers_commands_and_catalogue() {
let zh = Lang::from_code("zh-hans").expect("zh-hans registered");
for command in pdfcraft_engine::commands::COMMANDS {
assert!(has(zh, command.label), "missing command: {}", command.label);
if let Some(menu) = command.menu {
assert!(has(zh, menu), "missing menu: {menu}");
}
}
for group in pdfcraft_engine::catalog::TOOL_GROUPS {
assert!(has(zh, group.label), "missing group: {}", group.label);
for section in group.sections {
assert!(has(zh, section.title), "missing section: {}", section.title);
for item in section.items {
assert!(has(zh, item.label), "missing item: {}", item.label);
}
}
}
}
/// New tl!("literal") labels must not silently fall back to English in the complete catalog.
#[test]
fn simplified_chinese_covers_ui_literals() {
let zh = Lang::from_code("zh-hans").expect("zh-hans registered");
let mut stack = vec![std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src")];
let mut literals = std::collections::BTreeSet::new();
while let Some(dir) = stack.pop() {
for entry in std::fs::read_dir(dir).expect("UI source directory") {
let path = entry.expect("UI source entry").path();
if path.is_dir() {
// Catalog implementation and its test-only lookup examples are not UI labels.
if path.file_name().is_some_and(|name| name != "i18n") {
stack.push(path);
}
} else if path.extension().is_some_and(|ext| ext == "rs") {
let source = std::fs::read_to_string(path).expect("UI source file").replace("\r\n", "\n");
let mut rest = source.split("#[cfg(test)]\nmod ").next().unwrap_or_default();
while let Some((_, after)) = rest.split_once("tl!(\"") {
let mut escaped = false;
let end = after
.char_indices()
.find_map(|(i, c)| {
if c == '"' && !escaped {
return Some(i);
}
escaped = c == '\\' && !escaped;
None
})
.expect("closed tl! literal");
let (raw, tail) = after.split_at(end);
if tail.starts_with("\")") {
// Current UI literals use the shared Rust/JSON string escapes.
let label: String = serde_json::from_str(&format!("\"{raw}\"")).expect("UI literal escapes");
literals.insert(label);
}
rest = tail.strip_prefix('"').expect("closing quote");
}
}
}
}
assert!(literals.len() > 900, "source scan found only {} literals", literals.len());
let missing: Vec<_> = literals.iter().filter(|label| !has(zh, label)).collect();
assert!(missing.is_empty(), "untranslated Simplified Chinese UI literals: {missing:#?}");
}
#[test]
fn simplified_chinese_history_and_diagnostics_preserve_user_values() {
let zh = Lang::from_code("zh-hans").expect("zh-hans registered");
set_current(zh);
assert_eq!(command_label("Undo Insert pages from 报告 {n}.pdf"), "撤销 从 报告 {n}.pdf 插入页面");
assert_eq!(command_label("Redo Fill in 联系人 {key}"), "重做 填写 联系人 {key}");
assert_eq!(action_label("Change Title"), "更改标题");
assert_eq!(action_label("Custom action {n}"), "Custom action {n}");
assert_eq!(fmt(t("This page couldn't be displayed.\n{e}"), &[("e", "OS error {n}")]), "无法显示此页面。\nOS error {n}");
assert_eq!(fmt(t("{n} pages selected"), &[("n", "3")]), "已选择 3 页");
assert_eq!(tr(zh, "CheckBox"), "复选框");
assert_eq!(tr(zh, "pages"), "页");
assert_eq!(trn(zh, 1, "{n} page", "{n} pages"), "1 页");
assert_eq!(trn(zh, 0, "{n} field", "{n} fields"), "0 个字段");
assert_eq!(trn(zh, 2, "{n} field", "{n} fields"), "2 个字段");
set_current(Lang::EN);
}
#[test]
fn traditional_chinese_is_registered() {
let zh = Lang::from_code("zh-hant").expect("zh-hant registered");
+212
View File
@@ -1633,3 +1633,215 @@
Digitally sign 数字签名
Files you open in PdfCraft appear here. Drop a PDF anywhere to open it. 在 PdfCraft 中打开的文件会显示在这里。将 PDF 拖放到任意位置即可打开。
PdfCraft works offline. No telemetry, no account, and no cloud processing unless you add a provider. PdfCraft 可离线工作。无遥测、无账号,除非您自行添加服务商,否则无云端处理。
# Remaining dialogs, generated history labels and diagnostics. Translated from the English meaning.
{0}: {1} {0}:{1}
{dpi} pixels/inch {dpi} 像素/英寸
{kind} field {kind}字段
{month} {y} {y} 年 {month}
{n} pages selected 已选择 {n} 页
{name} — unsaved changes {name} — 有未保存的更改
{s} action {s}动作
1 page selected 已选择 1 页
a bookmark cannot be moved inside itself 无法将书签移入其自身内部
a document must keep at least one page 文档必须保留至少一页
A form field has additional actions 表单字段包含附加动作
A stream refers to an external file 数据流引用了外部文件
A stream uses LZW compression 数据流使用 LZW 压缩
Add arrow 添加箭头
Add background 添加背景
Add bookmark 添加书签
Add callout 添加标注框
Add checkmark 添加勾选标记
Add cloud 添加云形
Add connected lines 添加折线
Add cross 添加叉号
Add dot 添加圆点
Add drawing 添加绘图
Add field 添加字段
Add file attachment 添加文件附件
Add header & footer 添加页眉和页脚
Add highlight 添加高亮
Add image 添加图像
Add inserted text 添加插入文本标记
Add line 添加直线
Add link 添加链接
Add oval 添加椭圆
Add polygon 添加多边形
Add rectangle 添加矩形
Add redaction mark 添加密文标记
Add signature 添加签名
Add squiggly underline 添加波浪下划线
Add stamp 添加图章
Add sticky note 添加便笺
Add strikethrough 添加删除线
Add text box 添加文本框
Add underline 添加下划线
An embedded file has no AFRelationship 嵌入文件缺少 AFRelationship
An image asks to be interpolated 图像要求进行插值
An image has alternates or OPI information 图像包含替代图像或 OPI 信息
Button Style: 按钮样式:
Change {key} 更改{key}
Change comment properties 更改注释属性
Change field properties 更改字段属性
Change initial view 更改初始视图
Change initials 更改姓名首字母
Change link properties 更改链接属性
Change signature 更改签名
Check 勾选
Check Box Style: 复选框样式:
Choose images 选择图像
Circle 圆形
Combo 下拉列表
Coming in milestone 计划推出的里程碑
Crop page 裁剪页面
Delete bookmark 删除书签
Delete comment 删除注释
Delete content 删除内容
Delete document JavaScript 删除文档 JavaScript
Delete field 删除字段
Delete image 删除图像
Delete link 删除链接
Delete page 删除页面
Device colour is used, but there is no PDF/A output intent 使用了设备颜色,但未设置 PDF/A 输出意图
DeviceCMYK is used: it needs a CMYK output intent (not added automatically) 使用了 DeviceCMYK:需要设置 CMYK 输出意图(不会自动添加)
Diagnostic details 诊断详情
Diamond 菱形
Double-click 双击
Draw your {what} below. 请在下方绘制您的{what}。
Duplicate page 复制页面
e.g. en-US 例如 en-US
Edit comment 编辑注释
Edit content 编辑内容
Edit document JavaScript 编辑文档 JavaScript
Edit script of {name} 编辑 {name} 的脚本
Edit text 编辑文本
Embedded files must be PDF/A files themselves (use PDF/A-3 for others) 嵌入文件本身必须符合 PDF/A 标准(其他类型的文件请使用 PDF/A-3)
Erase 擦除
Fill & Sign: change initials 填写与签名:更改姓名首字母
Fill & Sign: change signature 填写与签名:更改签名
Fill & Sign: remove saved initials 填写与签名:移除已保存的姓名首字母
Fill & Sign: remove saved signature 填写与签名:移除已保存的签名
Fill in {name} 填写 {name}
Flatten 拼合
Flip image 翻转图像
Font: Helvetica 字体:Helvetica
Go to page {p} 转到第 {p} 页
Images and text 图像和文本
Images and text (converted to PDF) 图像和文本(转换为 PDF)
Import {name} 导入 {name}
Insert pages from {name} 从 {name} 插入页面
Items marked Ready work today. 标记为就绪的项目现已可用。
JavaScript JavaScript
JavaScript, e.g. getField("total").value JavaScript,例如 getField("total").value
List 列表
Lock comment 锁定注释
Mark figure as decorative 将图形标记为装饰性内容
Microsoft Word (.docx) Microsoft Word(.docx)
Move bookmark 移动书签
Move comment 移动注释
Move image 移动图像
Move page 移动页面
Move pages 移动页面
no such document 找不到该文档
nothing in the file matches this document 文件中没有与此文档匹配的数据
open to see what it will include 打开以查看计划包含的功能
Page {p} of {n} 第 {p} 页,共 {n} 页
PDF/A… PDF/A…
Planned 计划中
Planned for milestone 计划推出的里程碑
PostScript / EPS PostScript / EPS
PostScript XObjects aren't allowed 不允许使用 PostScript XObject
Presentation (.pptx) 演示文稿(.pptx)
PushButton 按钮
Radio 单选按钮
Remove redaction marks 移除密文标记
Rename bookmark 重命名书签
Rendering: hayro (bootstrap) · UI: egui · Icons: Lucide (ISC) · Fonts: Inter, JetBrains Mono, Dancing Script (OFL) 渲染:hayro(初期实现) · 界面:egui · 图标:Lucide(ISC) · 字体:Inter、JetBrains Mono、Dancing Script(OFL)
Replace page 替换页面
Resize comment 调整注释大小
Rotate image 旋转图像
Rotate page 旋转页面
Run JavaScript 运行 JavaScript
Save as PDF/A-1b 另存为 PDF/A-1b
Save as PDF/A-2b 另存为 PDF/A-2b
Save as PDF/A-3b 另存为 PDF/A-3b
Save as PDF/UA 另存为 PDF/UA
Save as PDF/X 另存为 PDF/X
Select a file for the stamp 选择图章文件
Select Icon 选择图标
Set bookmark destination 设置书签目标
Set status Accepted 将状态设为已接受
Set status Cancelled 将状态设为已取消
Set status Completed 将状态设为已完成
Set status None 将状态设为无
Set status Rejected 将状态设为已拒绝
Set status Unmarked 将状态设为未标记
Set tab order 设置 Tab 键顺序
Set the image of {name} 设置 {name} 的图像
Spreadsheet (.xlsx) 电子表格(.xlsx)
Square 正方形
Star 星形
The document has additional actions (/AA) 文档包含附加动作(/AA)
The document has document-level JavaScript 文档包含文档级 JavaScript
the document has no form fields 文档没有表单字段
the document has no page tree 文档没有页面树
the document has no tags 文档没有标签
The document has no XMP metadata 文档没有 XMP 元数据
The document is encrypted 文档已加密
the document is protected by a password 文档受密码保护
The form asks viewers to make appearances (NeedAppearances) 表单要求阅读器生成外观(NeedAppearances)
the image is empty 图像为空
The operation failed. 操作失败。
the password is incorrect 密码不正确
the text recognition models are not installed (run `cargo xtask models`, or set PRINTCRAFT_MODELS) 尚未安装文本识别模型(请运行 `cargo xtask models` 或设置 PRINTCRAFT_MODELS)
The title in the metadata and the document information differ 元数据与文档信息中的标题不一致
the title must not be empty 标题不能为空
there are no pages to print 没有可打印的页面
this document is signed: rewriting it would invalidate its signatures (save it incrementally instead) 此文档已签名:重写文件会使签名失效(请改用增量保存)
this file isn't XFDF, FDF, XML or text form data 此文件不是 XFDF、FDF、XML 或文本格式的表单数据
This page couldn't be displayed.\n{e} 无法显示此页面。\n{e}
Type your {what}. 请输入您的{what}。
Unknown 未知
Unlock comment 解锁注释
Update background 更新背景
Update header & footer 更新页眉和页脚
Update watermark 更新水印
Your name 您的姓名
# Additional UI labels, including newer workspace and security prompts.
Change {what} 更改{what}
Cut and stack 裁切后堆叠
Default workspace mode 默认工作区模式
Documents and view 文档与视图
Open email app 打开邮件应用
Open link 打开链接
Open this web page? 是否打开此网页?
Scripts run in a sandbox without file or network access. A script that asks to open a web page needs your permission first. With JavaScript off, Acrobat's standard format, validate and calculate functions still work. 脚本在沙盒中运行,无法访问文件或网络。脚本请求打开网页时,需要先获得您的许可。关闭 JavaScript 后,Acrobat 的标准格式、验证和计算函数仍可使用。
Sign yourself 自行签名
Text recognition isn't installed: its models are missing (run `cargo xtask models`, or set PDFCRAFT_MODELS). 尚未安装文本识别功能:缺少模型(请运行 `cargo xtask models` 或设置 PDFCRAFT_MODELS)。
Used when opening PDFs. An explicit launch or control mode takes precedence for the session. 用于打开 PDF 时。如果启动参数或控制通道明确指定了模式,则本次会话优先使用该模式。
Write this email? 是否撰写此邮件?
{who} in this document wants to open this address in your browser: 此文档中的{who}请求在浏览器中打开以下地址:
{who} in this document wants to open your email app with this address: 此文档中的{who}请求打开邮件应用并使用以下地址:
CheckBox 复选框
Create link 创建链接
Duplicate field 复制字段
Replace pages 替换页面
Set alternate text 设置替代文本
nothing to redo 没有可重做的操作
nothing to undo 没有可撤销的操作
pages 页
there are no redaction marks to apply 没有可应用的密文标记
Create PDF from images 从图像创建 PDF
Selected image files: {count} 已选择的图像文件:{count}
Use image resolution 使用图像分辨率
Use each image's embedded DPI; use 72 DPI when it is absent. 使用各图像内嵌的 DPI;若未指定,则使用 72 DPI。
Use 72 DPI (one point per pixel) 使用 72 DPI(每像素 1 点)
Use custom DPI 使用自定义 DPI
DPI sets the printed page size without resampling the image. DPI 用于设置打印页面尺寸,不会对图像重新采样。
Create 创建
Dynamic XFA form laid out from its template: {pages}, {fields}. 动态 XFA 表单已按模板排版:{pages},{fields}。
XFA form: its fields and its XFA data are kept in step, so other viewers show what you fill in. XFA 表单:字段与 XFA 数据保持同步,以便其他阅读器显示您填写的内容。
@plural {n} page|{n} pages {n} 页
@plural {n} field|{n} fields {n} 个字段
Can't render this file because it contains an unexpected character in line 9 and column 16.
+28
View File
@@ -523,6 +523,34 @@ fn japanese_dialogs_errors_and_custom_action_names() {
}
}
#[test]
fn simplified_chinese_signature_prompts_and_errors_keep_document_state() {
let (mut h, c) = harness();
let documents = ok(&mut h, &c, "ui.state", json!({}))["documents"].clone();
ok(&mut h, &c, "ui.set", json!({"key": "language", "value": "zh-hans"}));
ok(&mut h, &c, "ui.set", json!({"key": "dialog", "value": "signature"}));
let typed = ok(&mut h, &c, "ui.inspect", json!({"query": "请输入您的签名。"}));
assert!(typed["count"].as_u64().unwrap() > 0, "{typed}");
// The shell also has a Draw control; the modal's button is registered after the shell.
let draw = ok(&mut h, &c, "ui.inspect", json!({"query": "绘制"}));
let id = draw["widgets"]
.as_array()
.unwrap()
.iter()
.rev()
.find(|w| w["label"] == "绘制" && w["clickable"] == true)
.expect("signature Draw button")["id"]
.clone();
ok(&mut h, &c, "ui.click", json!({"id": id}));
let drawn = ok(&mut h, &c, "ui.inspect", json!({"query": "请在下方绘制您的签名。"}));
assert!(drawn["count"].as_u64().unwrap() > 0, "{drawn}");
ok(&mut h, &c, "ui.click", json!({"label": "取消"}));
assert!(!h.state_mut().apply_edit(pdfcraft_engine::Edit::DeletePages { pages: vec![0, 1, 2, 3, 4] }));
let state = ok(&mut h, &c, "ui.state", json!({}));
assert_eq!(state["notice"], "删除页面失败:a document must keep at least one page");
assert_eq!(state["documents"], documents, "a language change and failed edit must preserve the document");
}
#[test]
fn inspect_and_click_by_label_and_id() {
let (mut h, c) = harness();
+3 -1
View File
@@ -12,10 +12,12 @@ The control channel exposes the setting through `ui.set`:
The value is `auto` or a language code (`en`, `ja`, `zh-hans`, `zh-hant`, `cs`, `pt-br`), in any case. `ui.state` reports `language` as written in the preferences (`auto`, `en`, `ja`, `zh-hans`, `zh-hant`, `cs` or `pt-br`). Unknown values return an error without changing the current setting. Preferences saved before this setting existed follow the system language.
Dialogs, panels, menus, notices and history labels go through the catalog (`tl!`). Japanese and Traditional Chinese translate them (about 1,800 entries each), Simplified Chinese most of them (about 1,600); Czech and Brazilian Portuguese cover the menus so far. Untranslated labels use English. Error details that come from the engine or the operating system are shown as they are, inside a translated frame ("操作「…」に失敗しました: …"). The command palette matches the translated label, the English label and the command id. Vertical Japanese PDF rendering is an existing viewer feature; this change does not add vertical text editing.
Dialogs, panels, menus, notices and history labels go through the catalog (`tl!`). Japanese, Traditional Chinese and Simplified Chinese translate them (about 1,800 entries each); Czech and Brazilian Portuguese cover the menus so far. Untranslated labels use English. Error details that come from the engine or the operating system are shown as they are, inside a translated frame ("操作「…」に失敗しました: …"). The command palette matches the translated label, the English label and the command id. Vertical Japanese PDF rendering is an existing viewer feature; this change does not add vertical text editing.
Japanese interface text uses BIZ UDPGothic from [craft-fonts](https://github.com/storytold/craft-fonts), an optional build input that every release includes (`CRAFT_FONTS_DIR`; see the README). A build made without it has no Japanese face, so Japanese labels show replacement boxes. Chinese is drawn with the same craft-fonts CJK faces (in Simplified Chinese the `Hans` faces come first, so a line never mixes faces with different baselines), so a character none of them has shows a replacement box (and every Chinese character does in a build without craft-fonts). Czech and Brazilian Portuguese need only the bundled Latin faces (tested in `tests/fonts.rs`).
Simplified Chinese coverage tests, following PhotoCraft's complete-catalog approach, check every registered command, All tools label and `tl!("literal")` in the UI source. Generated history labels and diagnostics are also tested with user-supplied names and error details. Catalog coverage does not guarantee glyph coverage: the release-pinned craft-fonts input currently has no `Hans` face, and the web build embeds only BIZ UDPGothic Regular. Chinese font delivery remains separate work, as discussed in [#112](https://github.com/storytold/pdfcraft/pull/112) and [#140](https://github.com/storytold/pdfcraft/pull/140).
## How translations work
PdfCraft uses the same system as PhotoCraft (`crates/ui-egui/src/i18n/`):
+2 -2
View File
@@ -9806,8 +9806,8 @@ milestone = "M14"
status = "partial"
commands = ["app.preferences"]
tools = []
evidence = ["crates/ui-egui/src/i18n/mod.rs::language_persists_and_invalid_input_keeps_current_language", "crates/ui-egui/src/i18n/mod.rs::bundled_catalogs_are_consistent", "crates/ui-egui/src/i18n/mod.rs::japanese_covers_commands_and_catalogue", "crates/ui-egui/tests/control.rs::language_switch_preserves_document_and_command_ids", "crates/ui-egui/tests/control.rs::japanese_controls_and_search_keep_command_ids", "crates/ui-egui/tests/control.rs::japanese_dialogs_errors_and_custom_action_names"]
notes = "Preferences switches Auto (system language)/English/Japanese/Simplified Chinese/Traditional Chinese/Czech/Brazilian Portuguese and persists; ui.set language auto|en|ja|zh-hans|zh-hant|cs|pt-br and ui.state expose it. TSV catalogs per language (same system as PhotoCraft); every dialog, panel and notice goes through tl!. Japanese and Traditional Chinese cover commands, dialogs, panels, shortcuts and history labels, Simplified Chinese most of them; Czech and Brazilian Portuguese the menus. Engine/OS error details stay untranslated. Missing: the other catalogs filled in, Windows system-language detection, a full localization audit."
evidence = ["crates/ui-egui/src/i18n/mod.rs::language_persists_and_invalid_input_keeps_current_language", "crates/ui-egui/src/i18n/mod.rs::bundled_catalogs_are_consistent", "crates/ui-egui/src/i18n/mod.rs::japanese_covers_commands_and_catalogue", "crates/ui-egui/src/i18n/mod.rs::simplified_chinese_covers_commands_and_catalogue", "crates/ui-egui/src/i18n/mod.rs::simplified_chinese_covers_ui_literals", "crates/ui-egui/src/i18n/mod.rs::simplified_chinese_history_and_diagnostics_preserve_user_values", "crates/ui-egui/tests/control.rs::simplified_chinese_signature_prompts_and_errors_keep_document_state", "crates/ui-egui/tests/control.rs::language_switch_preserves_document_and_command_ids", "crates/ui-egui/tests/control.rs::japanese_controls_and_search_keep_command_ids", "crates/ui-egui/tests/control.rs::japanese_dialogs_errors_and_custom_action_names"]
notes = "Preferences switches Auto (system language)/English/Japanese/Simplified Chinese/Traditional Chinese/Czech/Brazilian Portuguese and persists; ui.set language auto|en|ja|zh-hans|zh-hant|cs|pt-br and ui.state expose it. TSV catalogs per language (same system as PhotoCraft); every dialog, panel and notice goes through tl!. Japanese, Traditional Chinese and Simplified Chinese cover commands, dialogs, panels, shortcuts and history labels; Czech and Brazilian Portuguese the menus. Engine/OS error details stay untranslated. Missing: the other catalogs filled in, Chinese font delivery, Windows system-language detection, a full localization audit."
[[feature]]
id = "misc.prefs-reading"