mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
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:
1 parent
42ddb2d546
commit
77893e8ab3
7 files changed
+333
-5
No files matched your search
+1
-1
@@ -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
@@ -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%).
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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.
|
@@ -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();
|
||||
|
||||
@@ -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/`):
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user