mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 11:16:56 -04:00
i18n: add Hungarian (hu) UI translation
Add the Hungarian catalog (4,524 rows): every menu string, every `tl!`
literal, the contextual and `@id` rows, the plurals and the status and
error messages, written from the meaning of the English labels in
general Hungarian vector-illustration vocabulary. Conventions are in the
header of `hu.tsv`.
- Register `hu` ("Magyar") in `i18n::LANGUAGES` as a complete language
with `plural_none` (a count takes the singular); every `hu-*` locale
resolves to it.
- Tests: Hungarian joins the menu-label, menu-string, `tl!`-literal and
message coverage checks; `hungarian_reads_as_hungarian` pins the core
vocabulary, the plural, the `file.close` override and a nested
message; ő ű Ő Ű are checked in the bundled UI fonts.
- README, ROADMAP, docs/development.md and docs/localization-parity.md
list Hungarian.
This commit is contained in:
1 parent
70e35807e7
commit
b9716366bf
8 files changed
+4610
-13
No files matched your search
@@ -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, Traditional and Simplified Chinese, Spanish, French, Italian, Russian and Ukrainian (and Czech and Brazilian Portuguese in the menus);
|
||||
speaks English, German, Japanese, 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.
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ Weights, evidence and the missing items per area: [target-app-parity.md](docs/ta
|
||||
| Korean | `ko` | none | 0% |
|
||||
| Vietnamese | `vi` | none | 0% |
|
||||
|
||||
Also shipped: Traditional Chinese (partial, 80%), Italian, Russian and Ukrainian (full), Czech (catalog complete; render-site gaps reduced; native review pending).
|
||||
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).
|
||||
|
||||
## Upcoming
|
||||
|
||||
@@ -739,7 +739,7 @@ mod tests {
|
||||
let fr = crate::i18n::Lang::from_code("fr").unwrap();
|
||||
let shown = crate::i18n::message(fr, "2 font file(s) are now in /Fonts; 1 couldn't be added: a.otf: larger than 256 MB");
|
||||
assert!(shown.contains("se trouvent maintenant dans /Fonts") && shown.contains("a.otf") && shown.contains("plus de 256 Mo"), "{shown}");
|
||||
for code in ["de", "es", "fr", "it", "ru"] {
|
||||
for code in ["de", "es", "fr", "hu", "it", "ru"] {
|
||||
let lang = crate::i18n::Lang::from_code(code).unwrap();
|
||||
for s in [
|
||||
"every search thread stopped on an internal error (please report this bug)",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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; 12] = [
|
||||
pub static LANGUAGES: [LangInfo; 13] = [
|
||||
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`).
|
||||
@@ -143,6 +143,10 @@ pub static LANGUAGES: [LangInfo; 12] = [
|
||||
complete_menus: true,
|
||||
catalog: OnceLock::new(),
|
||||
},
|
||||
// Hungarian: the whole interface and the status and error messages, keeping the same names in
|
||||
// English as Spanish; a count takes the singular ("3 réteg"), so plurals have one form. Every
|
||||
// `hu-*` locale (including `hu-HU`) resolves here.
|
||||
LangInfo { code: "hu", name: "Magyar", source: include_str!("hu.tsv"), plural: plural_none, complete_menus: true, catalog: OnceLock::new() },
|
||||
// Brazilian Portuguese: every menu label (`menu_catalogs_translate_every_menu_label`), every
|
||||
// `tl!` literal and the plural messages; left English on purpose are the `MENU_KEEP_AS_IS`
|
||||
// strings (product name, language names, workspace names, perspective grid presets).
|
||||
|
||||
@@ -26,6 +26,9 @@ fn tags_map_to_languages() {
|
||||
for tag in ["uk", "uk-UA", "uk_UA.UTF-8", "UK-ua", "uk_UA.UTF-8@euro"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(uk()), "{tag}");
|
||||
}
|
||||
for tag in ["hu", "hu-HU", "hu_HU.UTF-8", "HU-hu"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(hu()), "{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()));
|
||||
@@ -443,7 +446,7 @@ fn cs() -> Lang {
|
||||
}
|
||||
|
||||
/// Languages whose catalogs leave [`MENU_KEEP_AS_IS`] in English.
|
||||
const KEEPS_MENU_NAMES: [&str; 9] = ["cs", "de", "es", "fr", "it", "ja", "pt-br", "ru", "uk"];
|
||||
const KEEPS_MENU_NAMES: [&str; 10] = ["cs", "de", "es", "fr", "hu", "it", "ja", "pt-br", "ru", "uk"];
|
||||
|
||||
/// 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).
|
||||
@@ -597,6 +600,37 @@ fn ru() -> Lang {
|
||||
Lang::from_code("ru").expect("ru registered")
|
||||
}
|
||||
|
||||
fn hu() -> Lang {
|
||||
Lang::from_code("hu").expect("hu registered")
|
||||
}
|
||||
|
||||
/// Hungarian uses its vector-illustration vocabulary, drops the word "tool" from tool names, keeps
|
||||
/// a count in the singular (one plural form), gives dialog buttons a verb while the File menu's
|
||||
/// Close keeps its noun through an `@id` row, and translates the reason inside a message too.
|
||||
#[test]
|
||||
fn hungarian_reads_as_hungarian() {
|
||||
for (en, want) in [
|
||||
("Artboard Tool", "Rajztábla"),
|
||||
("Swatches", "Színminták"),
|
||||
("Pathfinder", "Útvonalkezelő"),
|
||||
("Stroke", "Körvonal"),
|
||||
("Fill", "Kitöltés"),
|
||||
("Direct Selection Tool", "Közvetlen kijelölés"),
|
||||
("Save As…", "Mentés másként…"),
|
||||
("Undo", "Visszavonás"),
|
||||
("Close", "Bezár"),
|
||||
] {
|
||||
assert_eq!(tr(hu(), en), want);
|
||||
}
|
||||
assert_eq!(tr_id(hu(), "file.close", "Close"), "Bezárás");
|
||||
assert_eq!(tr_id(hu(), "pathfinder.add", "Add"), "Egyesítés");
|
||||
for n in [0, 1, 3, 21] {
|
||||
assert_eq!(trn(hu(), n, "{n} Layer", "{n} Layers"), format!("{n} réteg"));
|
||||
}
|
||||
let nested = "command `object.group` is not available right now: nothing selected";
|
||||
assert_eq!(message(hu(), nested), "a parancs jelenleg nem érhető el: `object.group`: nincs kijelölés");
|
||||
}
|
||||
|
||||
fn uk() -> Lang {
|
||||
Lang::from_code("uk").expect("uk registered")
|
||||
}
|
||||
@@ -856,7 +890,7 @@ fn russian_plurals_have_three_forms() {
|
||||
assert_eq!(forms, [2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 0]);
|
||||
}
|
||||
|
||||
/// Czech, German, Spanish, French and Italian letters (and the punctuation their text uses) come from each family's own first font, not
|
||||
/// Czech, German, Spanish, French, Italian and Hungarian letters (and the punctuation their text uses) come from each family's own first font, not
|
||||
/// from a fallback further down the stack. (`has_glyph` can't tell: it counts characters of the
|
||||
/// face that draws missing glyphs, the first one, as missing.)
|
||||
#[test]
|
||||
@@ -871,7 +905,7 @@ fn latin_script_glyphs_are_available_without_system_fonts() {
|
||||
let first = first.unwrap();
|
||||
let mut font = fonts.fonts.font(&family);
|
||||
let chars = font.characters();
|
||||
for ch in "áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ„“‚‘…–ñÑüÜ¿¡”àèìòùÀÈÌÒÙ«»’âêîôûçëïÿœæÂÊÎÔÛÇËÏŒÆäöÄÖß\u{a0}".chars()
|
||||
for ch in "áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ„“‚‘…–ñÑüÜ¿¡”àèìòùÀÈÌÒÙ«»’âêîôûçëïÿœæÂÊÎÔÛÇËÏŒÆäöÄÖßőűŐŰ\u{a0}".chars()
|
||||
{
|
||||
assert!(chars.get(&ch).is_some_and(|fonts| fonts.contains(&first)), "{first} ({family:?}) has no {ch}");
|
||||
}
|
||||
@@ -1023,7 +1057,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", "it", "ja", "ru", "uk"];
|
||||
const COMPLETE_MESSAGES: &[&str] = &["cs", "de", "es", "fr", "hu", "it", "ja", "ru", "uk"];
|
||||
|
||||
/// 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"];
|
||||
|
||||
+4
-3
@@ -177,7 +177,8 @@ international Spanish; every `es-*` locale such as `es-ES`, `es-MX`, `es-AR` or
|
||||
complete; `fr-FR`, `fr-BE`, `fr-CA`, `fr-CH` and every other `fr-*` locale resolve to it), Italian (`it`,
|
||||
complete; `it-IT`, `it-CH` and every other `it-*` locale resolve to it), Russian (`ru`, complete; every `ru-*`
|
||||
locale such as `ru-RU`, `ru-BY` or `ru-KZ` resolves to it), Ukrainian (`uk`, complete; `uk-UA`,
|
||||
`uk_UA.UTF-8` and every other `uk-*` locale resolve to it; integer plurals use one/few/many), Czech (`cs`, every menu label) and Brazilian Portuguese
|
||||
`uk_UA.UTF-8` and every other `uk-*` locale resolve to it; integer plurals use one/few/many), Hungarian (`hu`, complete;
|
||||
`hu-HU` and every other `hu-*` locale resolve to it; a count takes the singular, so plurals have one form), Czech (`cs`, every menu label) and Brazilian Portuguese
|
||||
(`pt-br`, every menu label and every `tl!` literal). Untranslated text falls back to English until its rows are added.
|
||||
|
||||
- `tl!("…")` translates a literal into the language the UI is drawn in; `i18n::t(s)` is the same for a
|
||||
@@ -205,10 +206,10 @@ 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, Italian, Japanese, Russian and Ukrainian cover every
|
||||
placeholders are translated in turn (the reason after `: {e}` is often a message too). Spanish, French, Hungarian, Italian, Japanese, Russian and Ukrainian 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`, 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
|
||||
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_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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** minor (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-12 · **Change:** minor (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
|
||||
@@ -49,9 +49,10 @@ plus review. Native-speaker review is human work and isn't in the hours.
|
||||
| Italian | `it` | 4,011 / 4,020 | full |
|
||||
| Russian | `ru` | 4,009 / 4,020 | full |
|
||||
| Ukrainian | `uk` | 4,009 / 4,020 (one/few/many plurals) | full |
|
||||
| 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 |
|
||||
|
||||
Eleven non-English catalogs in all. Illustrator, as its public documentation lists, ships around two dozen
|
||||
Twelve non-English catalogs in all. Illustrator, as its public documentation lists, ships around two dozen
|
||||
interface languages (including Korean, Polish, Dutch, Turkish, the Nordic languages, Hungarian, and Middle East
|
||||
editions with Arabic and Hebrew); it doesn't ship Hindi, Indonesian or Vietnamese.
|
||||
|
||||
@@ -78,5 +79,6 @@ font for the web build.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 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 |
|
||||
| 2026-10-10 | major | First per-language table, measured from the catalog row counts against the complete French catalog |
|
||||
Reference in new issue
Block a user