mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
i18n: add Hungarian (hu) UI translation (#3831)
* i18n(cs): drop duplicate catalog entries
The Czech catalog listed Tool, Clone Stamp, History Brush, Dodge, Burn,
Sponge and Stroke Path… twice, which fails `cargo xtask i18n-coverage`
and the bundled_catalogs_are_consistent test. Keep one row each; for
Stroke Path… keep the later row, the one the loader already used.
* test(crop): pin the UI language to English in crop_default_mode
The test queries widgets by their English labels ("Ratio", "Swaps height
and width") but its harness followed the system locale, so it failed on a
machine whose locale has a registered catalog. Set interface.language to
"en" in the harness, as paint_bucket_all_layers and export_as_scaling do.
* i18n: add Hungarian (hu) UI translation
A complete Hungarian catalog (hu.tsv, 3908 entries: 3829 plain, 50
contextual, 21 plural, 8 command-id overrides) registered with the
complete-menu languages; hu-HU locales resolve to it.
Plural rule: plural_none. A Hungarian count takes the singular noun
("3 réteg"), so every @plural entry has one form.
Clean room: written from the plain meaning of the English labels with
ordinary Hungarian image-editing vocabulary; no proprietary localisation
resource, string table or product UI was used.
Also listed in the coverage test, LICENSE-translations.txt,
ATTRIBUTION.md and docs/localization.md, with tag, system-locale and
plural tests.
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Brandon Thomas
Claude Opus 5.5
parent
b3309e27d7
commit
9e623e7a45
@@ -19,6 +19,7 @@ next to them.
|
||||
| `crates/ui-egui/src/i18n/pt-br.tsv` | Brazilian Portuguese UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, [`LICENSE-translations.txt`](crates/ui-egui/src/i18n/LICENSE-translations.txt) |
|
||||
| `crates/ui-egui/src/i18n/uk.tsv` | Ukrainian UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, [`LICENSE-translations.txt`](crates/ui-egui/src/i18n/LICENSE-translations.txt) |
|
||||
| `crates/ui-egui/src/i18n/cs.tsv` | Czech UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, [`LICENSE-translations.txt`](crates/ui-egui/src/i18n/LICENSE-translations.txt) |
|
||||
| `crates/ui-egui/src/i18n/hu.tsv` | Hungarian UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, [`LICENSE-translations.txt`](crates/ui-egui/src/i18n/LICENSE-translations.txt) |
|
||||
| `crates/ui-egui/src/i18n/vi.tsv` | Vietnamese UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, [`LICENSE-translations.txt`](crates/ui-egui/src/i18n/LICENSE-translations.txt) |
|
||||
| `crates/ui-egui/src/i18n/tr.tsv` | Turkish UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, [`LICENSE-translations.txt`](crates/ui-egui/src/i18n/LICENSE-translations.txt) |
|
||||
| `assets/fonts/Inter-Regular.ttf`, `Inter-Medium.ttf`, `Inter-SemiBold.ttf` | Inter 4.001 (UI font) | The Inter Project Authors (Rasmus Andersson) | <https://github.com/rsms/inter> | SIL OFL 1.1, [`assets/fonts/OFL-Inter.txt`](assets/fonts/OFL-Inter.txt) |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
PhotoCraft's Korean, German, Brazilian Portuguese, Italian, Ukrainian, Dutch, Czech and Turkish UI translation catalogs are
|
||||
licensed under MIT OR Apache-2.0, at your option. The full license texts are in ../../../../LICENSE-MIT
|
||||
and ../../../../LICENSE-APACHE.
|
||||
PhotoCraft's Korean, German, Brazilian Portuguese, Italian, Ukrainian, Dutch, Czech, Turkish and
|
||||
Hungarian UI translation catalogs are licensed under MIT OR Apache-2.0, at your option. The full
|
||||
license texts are in ../../../../LICENSE-MIT and ../../../../LICENSE-APACHE.
|
||||
|
||||
Korean translations: PhotoCraft contributors, original translations of
|
||||
PhotoCraft's English labels; no proprietary translation resources were used.
|
||||
@@ -25,3 +25,6 @@ PhotoCraft's English labels; no proprietary translation resources were used.
|
||||
|
||||
Turkish translations: PhotoCraft contributors, original translations of
|
||||
PhotoCraft's English labels; no proprietary translation resources were used.
|
||||
|
||||
Hungarian translations: PhotoCraft contributors, original translations of
|
||||
PhotoCraft's English labels; no proprietary translation resources were used.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@ pub struct LangInfo {
|
||||
/// Catalog file contents, raw-deflated by build.rs (empty for the built-in English); see
|
||||
/// [`LangInfo::text`].
|
||||
pub source: &'static [u8],
|
||||
/// Plural form index for a count (English: 0 = one, 1 = other; Japanese and Chinese: always 0;
|
||||
/// Plural form index for a count (English: 0 = one, 1 = other; Japanese, Chinese and Hungarian: always 0;
|
||||
/// Czech: 0 = one, 1 = few (2–4), 2 = other; French: 0 = one (0 and 1), 1 = other). A catalog's `@plural` entries list one form per
|
||||
/// index.
|
||||
pub plural: fn(u64) -> usize,
|
||||
@@ -141,7 +141,7 @@ fn inflate(source: &[u8]) -> Result<String, String> {
|
||||
}
|
||||
|
||||
/// The registry. English first: it is the fallback and the source language.
|
||||
pub static LANGUAGES: [LangInfo; 19] = [
|
||||
pub static LANGUAGES: [LangInfo; 20] = [
|
||||
LangInfo { code: "en", name: "English", source: b"", plural: plural_one_other, complete_menus: false, catalog: OnceLock::new() },
|
||||
LangInfo { code: "ja", name: "日本語", source: catalog!("ja"), plural: plural_none, complete_menus: true, catalog: OnceLock::new() },
|
||||
LangInfo { code: "zh-hans", name: "简体中文", source: catalog!("zh-hans"), plural: plural_none, complete_menus: true, catalog: OnceLock::new() },
|
||||
@@ -164,6 +164,8 @@ pub static LANGUAGES: [LangInfo; 19] = [
|
||||
// Dutch; `nl-NL` and `nl-BE` locales both resolve here.
|
||||
LangInfo { code: "nl", name: "Nederlands", source: catalog!("nl"), plural: plural_one_other, complete_menus: true, catalog: OnceLock::new() },
|
||||
LangInfo { code: "it", name: "Italiano", source: catalog!("it"), plural: plural_one_other, complete_menus: true, catalog: OnceLock::new() },
|
||||
// Hungarian: a count takes the singular noun ("3 réteg"), so one plural form.
|
||||
LangInfo { code: "hu", name: "Magyar", source: catalog!("hu"), plural: plural_none, complete_menus: true, catalog: OnceLock::new() },
|
||||
// Turkish; `tr`, `tr-TR` and `tr_TR` locales resolve here.
|
||||
LangInfo { code: "tr", name: "Türkçe", source: catalog!("tr"), plural: plural_none, complete_menus: true, catalog: OnceLock::new() },
|
||||
// Vietnamese; `vi-VN` resolves here.
|
||||
@@ -420,6 +422,8 @@ mod tests {
|
||||
assert_eq!(lang_from_tag("it-CH"), Lang::from_code("it"));
|
||||
assert_eq!(lang_from_tag("nl_NL.UTF-8"), Lang::from_code("nl"));
|
||||
assert_eq!(lang_from_tag("nl-BE"), Lang::from_code("nl"));
|
||||
assert_eq!(lang_from_tag("hu_HU.UTF-8"), Lang::from_code("hu"));
|
||||
assert_eq!(lang_from_tag("hu-HU"), Lang::from_code("hu"));
|
||||
assert_eq!(lang_from_tag("tr_TR.UTF-8"), Lang::from_code("tr"));
|
||||
assert_eq!(lang_from_tag("tr-TR"), Lang::from_code("tr"));
|
||||
assert_eq!(lang_from_tag("tr"), Lang::from_code("tr"));
|
||||
@@ -518,6 +522,22 @@ mod tests {
|
||||
assert_eq!(trn(es, 3, "{n} item", "{n} items"), "3 elementos");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hungarian_resolves_and_keeps_the_singular_after_counts() {
|
||||
let hu = Lang::from_code("hu").expect("hu registered");
|
||||
for tag in ["hu", "hu_HU.UTF-8", "hu-HU"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(hu), "{tag}");
|
||||
}
|
||||
assert!(hu.complete_menus());
|
||||
assert_eq!(hu.name(), "Magyar");
|
||||
assert_eq!(tr(hu, "Layer"), "Réteg");
|
||||
for (n, expected) in [(0, "0 réteg"), (1, "1 réteg"), (3, "3 réteg"), (21, "21 réteg")] {
|
||||
assert_eq!(trn(hu, n, "{n} layer", "{n} layers"), expected);
|
||||
}
|
||||
assert_eq!(tr_ctx(hu, "layerLabel", "Red"), "Piros");
|
||||
assert_eq!(tr(hu, "Red"), "Vörös");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brazilian_portuguese_resolves_and_pluralises() {
|
||||
let pt = Lang::from_code("pt-br").expect("pt-br registered");
|
||||
|
||||
@@ -96,6 +96,7 @@ mod tests {
|
||||
(vec!["pt-PT"], "pt-br"),
|
||||
(vec!["it-IT", "en-US"], "it"),
|
||||
(vec!["nl-BE", "fr-BE"], "nl"),
|
||||
(vec!["hu-HU", "en-US"], "hu"),
|
||||
(vec!["tr-TR", "en-US"], "tr"),
|
||||
(vec![], "en"),
|
||||
] {
|
||||
|
||||
@@ -24,6 +24,8 @@ fn harness() -> Option<Harness> {
|
||||
egui_kittest::Harness::builder().with_size(egui::vec2(900.0, 640.0)).with_max_steps(64).wgpu().build_eframe(|cc| {
|
||||
PhotocraftApp::setup_context(&cc.egui_ctx, Default::default());
|
||||
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), Default::default());
|
||||
// The queries below use English labels; don't follow the system locale.
|
||||
app.session.prefs.edit(|p| p.interface.language = "en".into());
|
||||
if let Some(rs) = cc.wgpu_render_state.as_ref() {
|
||||
app.set_wgpu(rs.clone());
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ contextual, command-ID and plural translations; missing entries fall back to Eng
|
||||
| Polski | `pl` | one / few (2–4, not 12–14) / many |
|
||||
| Ελληνικά | `el` | one / other |
|
||||
| Nederlands | `nl` | one / other |
|
||||
| Magyar | `hu` | one form |
|
||||
| Tiếng Việt | `vi` | one form |
|
||||
|
||||
Languages registered with `complete_menus` cover the current menu labels, `tl!` literals, blend
|
||||
|
||||
@@ -579,6 +579,7 @@ mod tests {
|
||||
("el", "el.tsv"),
|
||||
("uk", "uk.tsv"),
|
||||
("nl", "nl.tsv"),
|
||||
("hu", "hu.tsv"),
|
||||
("tr", "tr.tsv"),
|
||||
("vi", "vi.tsv"),
|
||||
] {
|
||||
|
||||
Reference in New Issue
Block a user