mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-30 21:17:55 -06:00
feat(i18n): add complete Turkish interface language support (#1057)
* feat(i18n): add complete Turkish interface language support * Turkish: rows for the labels that landed alongside this catalog; refresh hashes --------- Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
co-authored by
Brandon Thomas
parent
d1980ec99f
commit
3d2bdfe71a
@@ -56,7 +56,7 @@ const AR_DAYS: [&str; 7] = ["الأحد", "الاثنين", "الثلاثاء",
|
||||
|
||||
/// The languages month and weekday names can be written in (Preferences ▸ Date format ▸ Language):
|
||||
/// the interface languages, in the same order and with the same names.
|
||||
pub const DATE_LANGUAGES: [DateLanguage; 20] = [
|
||||
pub const DATE_LANGUAGES: [DateLanguage; 21] = [
|
||||
DateLanguage {
|
||||
code: "en",
|
||||
name: "English",
|
||||
@@ -303,6 +303,15 @@ pub const DATE_LANGUAGES: [DateLanguage; 20] = [
|
||||
days: ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"],
|
||||
days_short: ["Κυρ", "Δευ", "Τρί", "Τετ", "Πέμ", "Παρ", "Σάβ"],
|
||||
},
|
||||
DateLanguage {
|
||||
code: "tr",
|
||||
name: "Türkçe",
|
||||
months: ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"],
|
||||
months_with_day: None,
|
||||
months_short: ["Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara"],
|
||||
days: ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"],
|
||||
days_short: ["Paz", "Pzt", "Sal", "Çar", "Per", "Cum", "Cmt"],
|
||||
},
|
||||
];
|
||||
|
||||
/// The date language for `code` (any case), or `None` when there's none.
|
||||
|
||||
@@ -136,7 +136,7 @@ fn plural_sl(n: u64) -> usize {
|
||||
}
|
||||
|
||||
/// The registry. English first: it is the fallback and the source language.
|
||||
pub static LANGUAGES: [LangInfo; 20] = [
|
||||
pub static LANGUAGES: [LangInfo; 21] = [
|
||||
LangInfo { code: "en", name: "English", source: "", plural: plural_one_other, rtl: false, catalog: OnceLock::new() },
|
||||
LangInfo { code: "ja", name: "日本語", source: include_str!("ja.tsv"), plural: plural_none, rtl: false, catalog: OnceLock::new() },
|
||||
// Simplified Chinese; `zh`, `zh-CN`, `zh-SG` and `zh-Hans-*` locales resolve here (see `candidates`).
|
||||
@@ -192,6 +192,8 @@ pub static LANGUAGES: [LangInfo; 20] = [
|
||||
// Slovene; every `sl-*` locale (`sl-SI`) resolves here. Four plural forms (one, two, few, other).
|
||||
LangInfo { code: "sl", name: "Slovenščina", source: include_str!("sl.tsv"), plural: plural_sl, rtl: false, catalog: OnceLock::new() },
|
||||
LangInfo { code: "el", name: "Ελληνικά", source: include_str!("el.tsv"), plural: plural_one_other, rtl: false, catalog: OnceLock::new() },
|
||||
// Turkish; every `tr-*` locale (`tr-TR`, `tr_TR.UTF-8`) resolves here.
|
||||
LangInfo { code: "tr", name: "Türkçe", source: include_str!("tr.tsv"), plural: plural_none, rtl: false, catalog: OnceLock::new() },
|
||||
];
|
||||
|
||||
impl LangInfo {
|
||||
@@ -841,7 +843,7 @@ mod tests {
|
||||
labels.insert(label.to_string());
|
||||
}
|
||||
assert!(labels.len() > 20 && labels.contains("Comb of characters"), "the scan found the dialog's labels: {labels:?}");
|
||||
for code in ["bg", "cs", "de", "es", "fr", "hu", "it", "ja", "pt-br", "ru", "te", "uk", "zh-hans", "zh-hant"] {
|
||||
for code in ["bg", "cs", "de", "es", "fr", "hu", "it", "ja", "pt-br", "ru", "te", "tr", "uk", "zh-hans", "zh-hant"] {
|
||||
let lang = Lang::from_code(code).expect("registered");
|
||||
let missing: Vec<_> = labels.iter().filter(|l| !has(lang, l)).collect();
|
||||
assert!(missing.is_empty(), "{code} lacks Shared Field Properties labels: {missing:#?}");
|
||||
@@ -1391,6 +1393,95 @@ mod tests {
|
||||
set_current(Lang::EN);
|
||||
}
|
||||
|
||||
/// Turkish covers every registered menu title and menu command label, keeps ellipses, and
|
||||
/// persists in preferences.
|
||||
#[test]
|
||||
fn turkish_covers_every_menu_label() {
|
||||
let tr_lang = Lang::from_code("tr").expect("tr registered");
|
||||
assert_eq!(tr_lang.name(), "Türkçe");
|
||||
assert_eq!(tr(tr_lang, "File"), "Dosya");
|
||||
assert_eq!(tr(tr_lang, "Türkçe Belge.pdf"), "Türkçe Belge.pdf");
|
||||
assert_eq!((0..=6).map(|n| (tr_lang.0.plural)(n)).collect::<Vec<_>>(), [0; 7]);
|
||||
for spec in pdfcraft_engine::commands::COMMANDS {
|
||||
let Some(menu) = spec.menu else { continue };
|
||||
assert!(has(tr_lang, menu), "Turkish lacks the menu title {menu:?}");
|
||||
assert!(has(tr_lang, spec.label), "Turkish lacks the {menu} menu label {:?} ({})", spec.label, spec.id);
|
||||
}
|
||||
const KEEP_AS_IS: &[&str] = &[
|
||||
"OK",
|
||||
"PDF/A…",
|
||||
"Microsoft Word (.docx)",
|
||||
"PostScript / EPS",
|
||||
"Preflight",
|
||||
"JavaScript",
|
||||
"ZIP",
|
||||
"{n} {kind}",
|
||||
"{rule} - {status}",
|
||||
"Poster",
|
||||
"{field}: {e}",
|
||||
"`{command}` {when}",
|
||||
"{0}: {1}",
|
||||
"{month} {y}",
|
||||
"+LOC",
|
||||
"−LOC",
|
||||
"ΔLOC",
|
||||
"+Bin",
|
||||
"−Bin",
|
||||
"Model",
|
||||
"Home / End",
|
||||
];
|
||||
let (entries, errors) = parse_entries(tr_lang.0.source, tr_lang.0.plural_forms());
|
||||
assert!(errors.is_empty(), "Turkish catalog errors: {errors:?}");
|
||||
for e in &entries {
|
||||
assert!(
|
||||
e.source != e.translation || KEEP_AS_IS.contains(&e.source.as_str()) || (e.context == "key" && KEY_CAP_NAMES.contains(&e.source.as_str())),
|
||||
"{:?} is untranslated",
|
||||
e.source
|
||||
);
|
||||
assert!(!e.translation.contains("..."), "use … rather than three dots: {:?}", e.translation);
|
||||
assert_eq!(e.source.starts_with(' '), e.translation.starts_with(' '), "leading space mismatch: {:?}", e.translation);
|
||||
assert_eq!(e.source.ends_with(' '), e.translation.ends_with(' '), "trailing space mismatch: {:?}", e.translation);
|
||||
}
|
||||
let mut app = crate::PdfCraftApp::default();
|
||||
app.set_option("language", "tr").unwrap();
|
||||
assert!(app.set_option("language", "tu").is_err());
|
||||
assert_eq!(app.language, "tr");
|
||||
let mut restored = crate::PdfCraftApp::default();
|
||||
restored.restore(&app.persist());
|
||||
assert_eq!(restored.language, "tr");
|
||||
}
|
||||
|
||||
/// Turkish translates every registered command, menu and All tools label.
|
||||
#[test]
|
||||
fn turkish_covers_commands_and_catalogue() {
|
||||
let tr_lang = Lang::from_code("tr").expect("tr registered");
|
||||
for command in pdfcraft_engine::commands::COMMANDS {
|
||||
assert!(has(tr_lang, command.label), "missing command: {}", command.label);
|
||||
if let Some(menu) = command.menu {
|
||||
assert!(has(tr_lang, menu), "missing menu: {menu}");
|
||||
}
|
||||
}
|
||||
for group in pdfcraft_engine::catalog::TOOL_GROUPS {
|
||||
assert!(has(tr_lang, group.label), "missing group: {}", group.label);
|
||||
for section in group.sections {
|
||||
assert!(has(tr_lang, section.title), "missing section: {}", section.title);
|
||||
for item in section.items {
|
||||
assert!(has(tr_lang, item.label), "missing item: {}", item.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Turkish translates every `tl!("…")` literal in the UI source.
|
||||
#[test]
|
||||
fn turkish_covers_ui_literals() {
|
||||
let literals = ui_literals();
|
||||
assert!(literals.len() > 900, "source scan found only {} literals", literals.len());
|
||||
let tr_lang = Lang::from_code("tr").expect("tr registered");
|
||||
let missing: Vec<_> = literals.iter().filter(|label| !has(tr_lang, label)).collect();
|
||||
assert!(missing.is_empty(), "untranslated Turkish UI literals: {missing:#?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simplified_chinese_is_registered() {
|
||||
let zh = Lang::from_code("zh-hans").expect("zh-hans registered");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -238,6 +238,7 @@ fn primary_ui_fonts_cover_latin_and_cyrillic_catalogs() {
|
||||
("hu", include_str!("../src/i18n/hu.tsv")),
|
||||
("uk", include_str!("../src/i18n/uk.tsv")),
|
||||
("it", include_str!("../src/i18n/it.tsv")),
|
||||
("tr", include_str!("../src/i18n/tr.tsv")),
|
||||
("sl", include_str!("../src/i18n/sl.tsv")),
|
||||
] {
|
||||
let mut text = String::from("áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽãõçâêôàÃÕÇÂÊÔÀñÑüÜäÄöÖőűß„“¿¡«»…ґҐєЄіІїЇәғқңөұүһӘҒҚҢӨҰҮҺ");
|
||||
|
||||
Reference in New Issue
Block a user