diff --git a/ATTRIBUTION.toml b/ATTRIBUTION.toml index e1e692bb3..6ac6349b2 100644 --- a/ATTRIBUTION.toml +++ b/ATTRIBUTION.toml @@ -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" diff --git a/ROADMAP.md b/ROADMAP.md index 52ed232ac..d8020954b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -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%). diff --git a/crates/ui-egui/src/i18n/mod.rs b/crates/ui-egui/src/i18n/mod.rs index 9dba8608a..5e4620666 100644 --- a/crates/ui-egui/src/i18n/mod.rs +++ b/crates/ui-egui/src/i18n/mod.rs @@ -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"); diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index c8cebdb32..16dcad00b 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -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} 个字段 diff --git a/crates/ui-egui/tests/control.rs b/crates/ui-egui/tests/control.rs index 4c7b95357..d88238df4 100644 --- a/crates/ui-egui/tests/control.rs +++ b/crates/ui-egui/tests/control.rs @@ -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(); diff --git a/docs/localization.md b/docs/localization.md index 2b4035cce..8c5bed86b 100644 --- a/docs/localization.md +++ b/docs/localization.md @@ -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/`): diff --git a/parity/acrobat-features.toml b/parity/acrobat-features.toml index 50338b2a6..9cdcc46b7 100644 --- a/parity/acrobat-features.toml +++ b/parity/acrobat-features.toml @@ -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"