i18n: add French (fr) UI translation (#410)
* i18n: add French (fr) UI translation A complete French catalog (menus, dialogs, panels, plurals), registered with its plural rule (0 and 1 take the singular). Covers the latest commands, Stamp Visible and Stamp Down included. A test checks the bundled UI fonts carry every French glyph. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * i18n(fr): translate labels added on main since this PR (from #582 by neko233-com) Co-Authored-By: neko233-com <242357072+neko233-com@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * i18n(fr): translate the labels added on main since this catalog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * i18n(fr): Camera Raw scopes and the Patch tool Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: neko233-com <242357072+neko233-com@users.noreply.github.com>
This commit is contained in:
parent
b45dfde498
commit
a4e96991f1
|
|
@ -424,6 +424,21 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// The bundled UI fonts cover French (the `fr` UI language) on their own: every accented
|
||||
/// letter, the ligatures, the guillemets and the no-break space used before `: ; ? !`.
|
||||
#[test]
|
||||
fn bundled_fonts_cover_french() {
|
||||
use skrifa::MetadataProvider;
|
||||
let french = "àâæçéèêëîïôœùûüÿ";
|
||||
let letters: String = french.chars().chain(french.chars().flat_map(char::to_uppercase)).chain("«»\u{a0}’".chars()).collect();
|
||||
for (name, bytes) in super::BUNDLED {
|
||||
let font = skrifa::FontRef::new(bytes).expect("bundled font parses");
|
||||
let cmap = font.charmap();
|
||||
let missing: String = letters.chars().filter(|c| cmap.map(*c).is_none_or(|g| g.to_u32() == 0)).collect();
|
||||
assert!(missing.is_empty(), "{name} lacks French glyphs: {missing:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_font_dirs_are_skipped() {
|
||||
let mut files = Vec::new();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -34,7 +34,7 @@ pub struct LangInfo {
|
|||
/// Catalog file contents (empty for the built-in English).
|
||||
pub source: &'static str,
|
||||
/// Plural form index for a count (English: 0 = one, 1 = other; Japanese and Chinese: always 0;
|
||||
/// Czech: 0 = one, 1 = few (2–4), 2 = other). A catalog's `@plural` entries list one form per
|
||||
/// 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,
|
||||
/// Must the catalog cover every menu string? (checked by the tests)
|
||||
|
|
@ -69,8 +69,13 @@ fn plural_cs(n: u64) -> usize {
|
|||
}
|
||||
}
|
||||
|
||||
/// French: 0 and 1 take the singular, everything else the plural.
|
||||
fn plural_fr(n: u64) -> usize {
|
||||
usize::from(n > 1)
|
||||
}
|
||||
|
||||
/// The registry. English first: it is the fallback and the source language.
|
||||
pub static LANGUAGES: [LangInfo; 7] = [
|
||||
pub static LANGUAGES: [LangInfo; 8] = [
|
||||
LangInfo { code: "en", name: "English", source: "", plural: plural_one_other, complete_menus: false, catalog: OnceLock::new() },
|
||||
LangInfo { code: "ja", name: "日本語", source: include_str!("ja.tsv"), plural: plural_none, complete_menus: true, catalog: OnceLock::new() },
|
||||
LangInfo {
|
||||
|
|
@ -84,6 +89,7 @@ pub static LANGUAGES: [LangInfo; 7] = [
|
|||
LangInfo { code: "es", name: "Español", source: include_str!("es.tsv"), plural: plural_one_other, complete_menus: true, catalog: OnceLock::new() },
|
||||
LangInfo { code: "ru", name: "Русский", source: include_str!("ru.tsv"), plural: plural_russian, complete_menus: true, catalog: OnceLock::new() },
|
||||
LangInfo { code: "cs", name: "Čeština", source: include_str!("cs.tsv"), plural: plural_cs, complete_menus: true, catalog: OnceLock::new() },
|
||||
LangInfo { code: "fr", name: "Français", source: include_str!("fr.tsv"), plural: plural_fr, complete_menus: true, catalog: OnceLock::new() },
|
||||
];
|
||||
|
||||
impl LangInfo {
|
||||
|
|
@ -295,7 +301,7 @@ mod tests {
|
|||
assert_eq!(lang_from_tag("POSIX"), Some(Lang::EN));
|
||||
assert_eq!(lang_from_tag("cs_CZ.UTF-8"), Some(CS()));
|
||||
assert_eq!(lang_from_tag("cs-CZ"), Some(CS()));
|
||||
assert_eq!(lang_from_tag("fr_FR"), None);
|
||||
assert_eq!(lang_from_tag("de_DE"), None);
|
||||
// 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()));
|
||||
|
|
@ -324,7 +330,7 @@ mod tests {
|
|||
#[test]
|
||||
fn macos_language_list_is_parsed() {
|
||||
assert_eq!(first_supported("(\n \"ja-JP\",\n \"en-US\"\n)\n"), Some(JA()));
|
||||
assert_eq!(first_supported("(\n \"fr-FR\",\n \"en-US\"\n)\n"), Some(Lang::EN));
|
||||
assert_eq!(first_supported("(\n \"de-DE\",\n \"en-US\"\n)\n"), Some(Lang::EN));
|
||||
assert_eq!(first_supported("(\n \"zh-Hant-TW\",\n \"en-US\"\n)\n"), Some(ZH()));
|
||||
assert_eq!(first_supported("("), None);
|
||||
}
|
||||
|
|
@ -430,6 +436,22 @@ mod tests {
|
|||
assert_eq!(placeholders("a {x} b {y} {"), ["x", "y"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn french_resolves_and_pluralises() {
|
||||
let fr = Lang::from_code("fr").expect("fr registered");
|
||||
for tag in ["fr", "fr_FR.UTF-8", "fr-CA", "fr_BE", "fr-CH"] {
|
||||
assert_eq!(lang_from_tag(tag), Some(fr), "{tag}");
|
||||
}
|
||||
assert_eq!(tr(fr, "Layer"), "Calque");
|
||||
assert_eq!(tr_id(fr, "select.all", "All"), "Tout sélectionner", "an id override wins over the plain label");
|
||||
assert_eq!(tr(fr, "All"), "Tout");
|
||||
let forms: Vec<usize> = [0, 1, 2, 5, 100, u64::MAX].into_iter().map(plural_fr).collect();
|
||||
assert_eq!(forms, [0, 0, 1, 1, 1, 1]);
|
||||
assert_eq!(trn(fr, 0, "{n} item", "{n} items"), "0 élément");
|
||||
assert_eq!(trn(fr, 1, "{n} item", "{n} items"), "1 élément");
|
||||
assert_eq!(trn(fr, 3, "{n} item", "{n} items"), "3 éléments");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn czech_plural_rule() {
|
||||
let forms: Vec<usize> = [0, 1, 2, 3, 4, 5, 11, 12, 21, 22, 100, u64::MAX].into_iter().map(plural_cs).collect();
|
||||
|
|
|
|||
Loading…
Reference in New Issue