i18n: add complete Vietnamese localization
This commit is contained in:
parent
22e06db73d
commit
ec8dc0c2da
|
|
@ -217,7 +217,7 @@ doesn't write Affinity files. Current `.af` validation includes 33 pinned files,
|
|||
and every-board SVG/PDF/PSD export, with fixes for Affinity 3 artboards, source-backed JPEGs and text runs.
|
||||
[Scope and limits](crates/affinity/README.md); [source audit and remaining gaps](docs/affinity-validation.md).
|
||||
The interface
|
||||
speaks English, German, Japanese, Korean, Traditional and Simplified Chinese, Spanish, French, Hungarian, Italian, Russian and Ukrainian (and Czech and Brazilian Portuguese in the menus);
|
||||
speaks English, German, Japanese, Korean, Vietnamese, Traditional and Simplified Chinese, Spanish, French, Hungarian, Italian, Russian and Ukrainian (and Czech and Brazilian Portuguese in the menus);
|
||||
[localization-parity.md](docs/localization-parity.md) has the details.
|
||||
The scores are self-assessed from our code against Illustrator's public documentation;
|
||||
[target-app-parity.md](docs/target-app-parity.md#how-this-was-measured-2026-10-10) explains how far to trust them.
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ blocking it ([Alpha gate](docs/roadmap.md#alpha-gate)).
|
|||
|
||||
| Audience | Ready % | Opus 5.5 agent hours to ~95% (one agent) | With 4–6 agents | Work that dominates |
|
||||
|---|---:|---:|---:|---|
|
||||
| **Full Illustrator** (ready for real work; decides the stage) | **~62%** (57–67%) | **~340–560 h** | ~95–160 h | the mainstream work below, plus 3D (50–85 h), raster effects (23–39 h), advanced type (20–28 h), generative AI (30–60 h), localization (60–100 h) and ecosystem |
|
||||
| **Full Illustrator** (ready for real work; decides the stage) | **~62%** (57–67%) | **~340–560 h** | ~95–160 h | the mainstream work below, plus 3D (50–85 h), raster effects (23–39 h), advanced type (20–28 h), generative AI (30–60 h), localization (55–90 h) and ecosystem |
|
||||
| **Mainstream illustrator** | **~65%** (60–70%) | **~230–380 h** | ~65–110 h | the interaction-fidelity pass (60–90 h), hardening and QA (30–50 h), stability (20–35 h), performance budgets (15–25 h), `.ai` exchange (10–20 h), the remaining depth of the everyday areas |
|
||||
| **Essentials user** | **~75%** (70–80%) | **~60–105 h** | ~25–45 h | launch and stability on Windows (20–35 h), in-app help and onboarding (8–15 h), basic QA (10–15 h), Shape Builder regions (4–8 h), UI polish |
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ everything is measured from our code against Illustrator's public documentation.
|
|||
| UI/UX fidelity | 55% | 70–110 | [ui-parity.md](docs/ui-parity.md) |
|
||||
| File formats | 75% | 15–30 | [file-format-parity.md](docs/file-format-parity.md) |
|
||||
| Hardware (GPU, pen, touch, displays) | 35% | 25–45 | [hardware-parity.md](docs/hardware-parity.md) |
|
||||
| Localization (12 key languages) | 63% (measured) | 60–100 | [localization-parity.md](docs/localization-parity.md) |
|
||||
| Localization (12 key languages) | 71% (measured) | 55–90 | [localization-parity.md](docs/localization-parity.md) |
|
||||
| Performance | ~60% | 15–25 | [gaps.md › G15](docs/gaps.md#g15-performance-budgets-and-a-gpu-renderer) |
|
||||
| Stability | ~60% | 30–50 | [gaps.md › G10](docs/gaps.md#g10-launch-and-stability-on-real-machines) |
|
||||
| Platforms | 80% | 15–25 | [gaps.md › G8](docs/gaps.md#g8-hardening-at-scale) |
|
||||
|
|
@ -107,7 +107,7 @@ Weights, evidence and the missing items per area: [target-app-parity.md](docs/ta
|
|||
| Japanese | `ja` | full | 100% |
|
||||
| German | `de` | full | 98% |
|
||||
| Korean | `ko` | full | 100% |
|
||||
| Vietnamese | `vi` | none | 0% |
|
||||
| Vietnamese | `vi` | full | 100% |
|
||||
|
||||
Also shipped: Traditional Chinese (partial, 80%), Hungarian, Italian, Russian and Ukrainian (full), Czech (catalog complete; render-site gaps reduced; native review pending).
|
||||
No catalog has had a native speaker's review. Details: [localization-parity.md](docs/localization-parity.md).
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ fn plural_east_slavic(n: u64) -> usize {
|
|||
}
|
||||
|
||||
/// The registry. English first: it is the fallback and the source language.
|
||||
pub static LANGUAGES: [LangInfo; 14] = [
|
||||
pub static LANGUAGES: [LangInfo; 15] = [
|
||||
LangInfo { code: "en", name: "English", source: "", plural: plural_one_other, complete_menus: false, catalog: OnceLock::new() },
|
||||
// Japanese: the whole interface (every menu string and `tl!` literal) and the status and error
|
||||
// messages, keeping the product, workspace and perspective preset names in English (`MENU_KEEP_AS_IS`).
|
||||
|
|
@ -91,6 +91,11 @@ pub static LANGUAGES: [LangInfo; 14] = [
|
|||
// English as Japanese; a count takes no plural, so plurals have one form. Hangul is drawn with an
|
||||
// installed system font (craft-fonts has no Korean UI face yet); every `ko-*` locale resolves here.
|
||||
LangInfo { code: "ko", name: "한국어", source: include_str!("ko.tsv"), plural: plural_none, complete_menus: true, catalog: OnceLock::new() },
|
||||
// Vietnamese: complete menus and messages, with one plural form.
|
||||
// Every `vi-*` locale resolves here.
|
||||
LangInfo {
|
||||
code: "vi", name: "Tiếng Việt", source: include_str!("vi.tsv"), plural: plural_none, complete_menus: true, catalog: OnceLock::new()
|
||||
},
|
||||
// Czech: the whole interface (every menu string and `tl!` literal) and the status and error
|
||||
// messages, with one/few/other plurals, keeping the same names in English as Spanish.
|
||||
LangInfo { code: "cs", name: "Čeština", source: include_str!("cs.tsv"), plural: plural_czech, complete_menus: true, catalog: OnceLock::new() },
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ fn tags_map_to_languages() {
|
|||
for tag in ["ko", "ko-KR", "ko_KR.UTF-8", "KO-kr"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(ko()), "{tag}");
|
||||
}
|
||||
for tag in ["vi", "vi-VN", "vi_VN.UTF-8", "VI-vn"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(vi()), "{tag}");
|
||||
}
|
||||
// Traditional Chinese: by region, by script, and with a region after the script.
|
||||
assert_eq!(lang_from_tag("zh_TW.UTF-8"), Some(ZH()));
|
||||
assert_eq!(lang_from_tag("zh-TW"), Some(ZH()));
|
||||
|
|
@ -449,7 +452,7 @@ fn cs() -> Lang {
|
|||
}
|
||||
|
||||
/// Languages whose catalogs leave [`MENU_KEEP_AS_IS`] in English.
|
||||
const KEEPS_MENU_NAMES: [&str; 11] = ["cs", "de", "es", "fr", "hu", "it", "ja", "ko", "pt-br", "ru", "uk"];
|
||||
const KEEPS_MENU_NAMES: [&str; 12] = ["cs", "de", "es", "fr", "hu", "it", "ja", "ko", "pt-br", "ru", "uk", "vi"];
|
||||
|
||||
/// Menu labels the menu-complete catalogs (Czech, German, Spanish, Italian, Japanese, Brazilian Portuguese) show as they are: the product name, a format name, the built-in workspace
|
||||
/// names and the perspective grid presets (names, shown untranslated wherever else they appear).
|
||||
|
|
@ -662,6 +665,63 @@ fn korean_reads_as_korean() {
|
|||
assert_eq!(message(ko(), nested), "명령 `object.group`은(는) 지금 사용할 수 없습니다: 선택된 항목이 없습니다");
|
||||
}
|
||||
|
||||
fn vi() -> Lang {
|
||||
Lang::from_code("vi").expect("vi registered")
|
||||
}
|
||||
|
||||
/// Vietnamese has one plural form and covers the complete interface and messages.
|
||||
#[test]
|
||||
fn vietnamese_reads_as_vietnamese() {
|
||||
assert_eq!(vi().name(), "Tiếng Việt");
|
||||
assert!(vi().complete_menus());
|
||||
assert_eq!(Lang::from_pref("vi"), vi());
|
||||
assert_eq!(Lang::from_pref("VI"), vi());
|
||||
for (en, want) in [
|
||||
("Artboard Tool", "Công cụ Bảng vẽ"),
|
||||
("Swatches", "Mẫu màu"),
|
||||
("Stroke", "Nét"),
|
||||
("Fill", "Tô"),
|
||||
("Direct Selection Tool", "Công cụ Chọn trực tiếp"),
|
||||
("Save As…", "Lưu thành…"),
|
||||
("Undo", "Hoàn tác"),
|
||||
("Close", "Đóng"),
|
||||
] {
|
||||
assert_eq!(tr(vi(), en), want);
|
||||
}
|
||||
for n in [0, 1, 3, 21] {
|
||||
assert_eq!(trn(vi(), n, "{n} Layer", "{n} Layers"), format!("{n} lớp"));
|
||||
}
|
||||
let nested = "command `object.group` is not available right now: nothing selected";
|
||||
assert_eq!(message(vi(), nested), "lệnh `object.group` hiện không khả dụng: chưa chọn gì");
|
||||
}
|
||||
|
||||
/// Ambiguous English terms keep their intended Vietnamese meaning across UI contexts.
|
||||
#[test]
|
||||
fn vietnamese_domain_terms_are_consistent() {
|
||||
for (en, want) in [
|
||||
("All Scripts", "Tất cả hệ chữ"),
|
||||
("Pointillize", "Hiệu ứng chấm"),
|
||||
("Object Highlighting", "Đánh dấu đối tượng"),
|
||||
("Color Theme", "Bộ màu"),
|
||||
("Color Themes", "Bộ màu"),
|
||||
("Asset Export", "Xuất asset"),
|
||||
("Envelope Distort", "Biến dạng Envelope"),
|
||||
("Move Spine Point", "Di chuyển điểm trục"),
|
||||
("Dashed Line", "Đường nét đứt"),
|
||||
("Eyedropper Tool", "Công cụ Ống hút màu"),
|
||||
("Character Alignment", "Căn chỉnh ký tự"),
|
||||
] {
|
||||
assert_eq!(tr(vi(), en), want, "{en}");
|
||||
}
|
||||
assert_eq!(tr(vi(), "Appearance"), "Diện mạo");
|
||||
assert_eq!(tr_ctx(vi(), "theme", "Appearance"), "Giao diện");
|
||||
assert_eq!(message(vi(), "no saved themes"), "chưa có bộ màu nào được lưu");
|
||||
assert_eq!(message(vi(), r#"choice must be "merge" or "add", not `foo`"#), r#"lựa chọn phải là "merge" hoặc "add", không phải `foo`"#);
|
||||
assert_eq!(message(vi(), "select a blend"), "hãy chọn một đối tượng hòa trộn");
|
||||
assert_eq!(message(vi(), "select a repeat"), "hãy chọn một đối tượng lặp");
|
||||
assert_eq!(message(vi(), "Replace Spine: select a blend and a path"), "Thay thế trục: chọn một đối tượng hòa trộn và một đường dẫn");
|
||||
}
|
||||
|
||||
fn uk() -> Lang {
|
||||
Lang::from_code("uk").expect("uk registered")
|
||||
}
|
||||
|
|
@ -1103,7 +1163,7 @@ fn select_key_param_error_is_translated() {
|
|||
}
|
||||
|
||||
/// Languages whose catalogs cover every status and error message.
|
||||
const COMPLETE_MESSAGES: &[&str] = &["cs", "de", "es", "fr", "hu", "it", "ja", "ko", "ru", "uk"];
|
||||
const COMPLETE_MESSAGES: &[&str] = &["cs", "de", "es", "fr", "hu", "it", "ja", "ko", "ru", "uk", "vi"];
|
||||
|
||||
/// Crates whose error and status messages reach the status bar.
|
||||
const MESSAGE_CRATES: &[&str] = &["ui-egui", "engine", "doc", "format", "svg", "pdf", "eps", "text", "plugins", "metafile", "cad", "trace"];
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -171,7 +171,7 @@ always use the English ids and labels, so agents and scripts never see translate
|
|||
Languages shipped: English (`en`, the source), Traditional Chinese (`zh-hant`, complete, in the vocabulary used
|
||||
in Taiwan; `zh-TW`, `zh-HK`, `zh-MO` and `zh-Hant-*` locales all resolve to it), Simplified Chinese (`zh-hans`,
|
||||
complete, in the vocabulary used in mainland China; `zh-CN`, `zh-SG`, `zh-Hans-*` and a bare `zh` resolve to it,
|
||||
so the two scripts never mix), Japanese (`ja`, complete), Korean (`ko`, complete; every `ko-*` locale resolves to it; a count takes no plural, and Hangul is drawn with an installed system font), German (`de`, complete; `de-DE`, `de-AT`, `de-CH` and every
|
||||
so the two scripts never mix), Japanese (`ja`, complete), Korean (`ko`, complete; every `ko-*` locale resolves to it; a count takes no plural, and Hangul is drawn with an installed system font), Vietnamese (`vi`, complete; every `vi-*` locale resolves to it; a count takes no plural, so plurals have one form), German (`de`, complete; `de-DE`, `de-AT`, `de-CH` and every
|
||||
other `de-*` locale resolve to it), Spanish (`es`, complete, in neutral
|
||||
international Spanish; every `es-*` locale such as `es-ES`, `es-MX`, `es-AR` or `es-419` resolves to it), French (`fr`,
|
||||
complete; `fr-FR`, `fr-BE`, `fr-CA`, `fr-CH` and every other `fr-*` locale resolve to it), Italian (`it`,
|
||||
|
|
@ -206,10 +206,9 @@ locale such as `ru-RU`, `ru-BY` or `ru-KZ` resolves to it), Ukrainian (`uk`, com
|
|||
control channel, MCP and tests read them) and are translated only where the status bar draws them
|
||||
(`i18n::msg`). `@msg` catalog rows hold a whole message or a template such as
|
||||
`Couldn't open {name}: {e}`; `{_1}`, `{_2}` … stand for the format string's `{}`, and the values in the
|
||||
placeholders are translated in turn (the reason after `: {e}` is often a message too). Spanish, French, Hungarian, Italian, Japanese, Russian and Ukrainian cover every
|
||||
placeholders are translated in turn (the reason after `: {e}` is often a message too). Czech, German, Spanish, French, Hungarian, Italian, Japanese, Korean, Russian, Ukrainian and Vietnamese cover every
|
||||
message literal the test scan finds (`complete_languages_translate_every_message`, languages listed in
|
||||
`COMPLETE_MESSAGES`): a new `Err("…")`, `Other(…)`, `#[error(…)]` or `status(…)` message needs an `es.tsv`,
|
||||
a `fr.tsv`, a `hu.tsv`, an `it.tsv`, a `ja.tsv`, a `ru.tsv` and a `uk.tsv` row (`VECTORCRAFT_I18N_DUMP_MESSAGES=messages.txt cargo test -p vectorcraft-ui-egui
|
||||
`COMPLETE_MESSAGES`): a new `Err("…")`, `Other(…)`, `#[error(…)]` or `status(…)` message needs a row in each of those catalogs (`VECTORCRAFT_I18N_DUMP_MESSAGES=messages.txt cargo test -p vectorcraft-ui-egui
|
||||
complete_languages_translate_every_message` lists them all). Other languages show messages in English
|
||||
until they add `@msg` rows.
|
||||
- Not translated on purpose: names that are user data (layers, swatches, fonts, documents), the tab
|
||||
|
|
|
|||
14
docs/gaps.md
14
docs/gaps.md
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,7 @@
|
|||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** minor (Czech: 128 strings added since the catalog was completed are translated, #1461; scores and estimates unchanged; Korean catalog added, complete under the tests; 12-language score 54% → 63%; Hungarian catalog added; Czech render-site localization follow-up; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-13 · **Change:** minor (Vietnamese catalog added, complete under the tests; 12-language score 63% → 71%; Czech: 128 strings added since the catalog was completed are translated, #1461; scores and estimates unchanged; Korean catalog added, complete under the tests; 12-language score 54% → 63%; Hungarian catalog added; Czech render-site localization follow-up; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
)
|
||||
|
||||
Per-language status of VectorCraft's interface. How the catalogs work and how to add a language:
|
||||
[`development.md` › Localisation](development.md#localisation). Part of
|
||||
|
|
@ -34,10 +35,10 @@ Chinese by `imachus`). None is recorded as reviewed by a native speaker; the cat
|
|||
| Japanese | `ja` | 4,004 / 4,020 (99.6%) | all | CJK UI font as above; Japanese IME; vertical type partial; Japanese crop marks and composition settings | no | full | — (review) |
|
||||
| German | `de` | 3,952 / 4,020 (98%; tests hold menus, literals and messages complete) | all (602 of 604 message rows; landed 2026-10-10) | Latin | contributor-written, not reviewed | full | — (review) |
|
||||
| Korean | `ko` | 4,151 rows: menus, `tl!` literals and messages complete (tests); one plural form | all | Hangul from an installed system font on the desktop (Apple SD Gothic Neo, Malgun Gothic, Noto Sans CJK KR…); the web build has no Korean UI font yet (craft-fonts); IME path exists | no (AI-drafted) | full | — (review; web font) |
|
||||
| Vietnamese | `vi` | 0 | none | Latin with stacked diacritics (precomposed forms render) | no | none | 5–8 |
|
||||
| Vietnamese | `vi` | 4,213 rows: menus, `tl!` literals and messages complete (tests); one plural form; 3,548 plain + 13 `@plural` + 6 contextual + 646 `@msg` | all | Latin with Vietnamese diacritics (precomposed forms; glyph tests pass) | contributor is a Vietnamese speaker; AI-assisted draft, further native review welcome | full | — (review) |
|
||||
|
||||
**12-language score: ~63%** (equal weights: 100 + 85 + 100 + 0 + 0 + 100 + 68 + 0 + 100 + 98 + 100 + 0, over 12).
|
||||
**To `full` for all twelve: ~60–100 h**, most of it Arabic (right-to-left interface) and Hindi (interface
|
||||
**12-language score: ~71%** (equal weights: 100 + 85 + 100 + 0 + 0 + 100 + 68 + 0 + 100 + 98 + 100 + 100, over 12).
|
||||
**To `full` for all twelve: ~55–90 h**, most of it Arabic (right-to-left interface) and Hindi (interface
|
||||
shaping). Each new Latin-script catalog is ~4,000 rows; the existing ones were written in a few agent hours each
|
||||
plus review. Native-speaker review is human work and isn't in the hours.
|
||||
|
||||
|
|
@ -52,7 +53,7 @@ plus review. Native-speaker review is human work and isn't in the hours.
|
|||
| Hungarian | `hu` | 4,524 rows: menus, `tl!` literals and messages complete (tests); one plural form | full |
|
||||
| Czech | `cs` | 4,411 catalog entries, including 636 `@msg` rows (measured locally 2026-10-11); menu, literal and message coverage enforced by tests | catalog complete; render-site gaps reduced; native review pending |
|
||||
|
||||
Thirteen non-English catalogs in all. Illustrator, as its public documentation lists, ships around two dozen
|
||||
Fourteen non-English catalogs in all. Illustrator, as its public documentation lists, ships around two dozen
|
||||
interface languages (including Polish, Dutch, Turkish, the Nordic languages, Hungarian, and Middle East
|
||||
editions with Arabic and Hebrew); it doesn't ship Hindi, Indonesian or Vietnamese.
|
||||
|
||||
|
|
@ -79,7 +80,9 @@ font for the web build.
|
|||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-12 | minor | Vietnamese (`vi`) catalog added, complete under the tests (menus, literals, messages; one plural form); 12-language score 63% → 71%, hours 60–100 → 55–90 |
|
||||
| 2026-10-13 | minor | Czech: 128 more strings translated (tool names, PDF and Image Trace presets, pie graph options and others added since the catalog was completed; #1461); the entry count in the table is the 2026-10-11 measurement; native review still pending; scores and estimates unchanged |
|
||||
)
|
||||
| 2026-10-12 | minor | Korean (`ko`) catalog added, complete under the tests (menus, literals, messages); 12-language score 54% → 63%, hours 70–110 → 60–100 |
|
||||
| 2026-10-12 | minor | Hungarian (`hu`) catalog added, complete under the tests |
|
||||
| 2026-10-11 | minor | Czech render-site follow-up: file-dialog filters, diagnostics, preset numbering, contributor tooltips/plurals, gradient grammar and contextual printer mark styles; estimates unchanged |
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue