Add Spanish, Italian and Russian; dropdown order and language file tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6629472811
commit
a13abf63f3
6 files changed
+3112
-7
No files matched your search
+59
-3
@@ -5,7 +5,17 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
const SOURCES: &[(&str, &str)] = &[("en", include_str!("../lang/en.lang")), ("de", include_str!("../lang/de.lang"))];
|
||||
// English first: it is the fallback for missing keys
|
||||
const SOURCES: &[(&str, &str)] = &[
|
||||
("en", include_str!("../lang/en.lang")),
|
||||
("de", include_str!("../lang/de.lang")),
|
||||
("es", include_str!("../lang/es.lang")),
|
||||
("ru", include_str!("../lang/ru.lang")),
|
||||
("it", include_str!("../lang/it.lang")),
|
||||
];
|
||||
|
||||
// order of the languages in the language dropdown
|
||||
const ORDER: &[&str] = &["de", "en", "es", "pt", "nl", "da", "no", "sv", "fi", "ru", "hr", "hu", "sr", "ro", "sk", "cs", "it", "el"];
|
||||
|
||||
struct I18n {
|
||||
langs: Vec<(String, HashMap<String, String>)>,
|
||||
@@ -35,9 +45,11 @@ thread_local! {
|
||||
|
||||
// Available languages: (code, display name in that language)
|
||||
pub fn languages() -> Vec<(String, String)> {
|
||||
I18N.with(|i| {
|
||||
let mut l: Vec<(String, String)> = I18N.with(|i| {
|
||||
i.borrow().langs.iter().map(|(c, m)| (c.clone(), m.get("lang.name").cloned().unwrap_or_else(|| c.clone()))).collect()
|
||||
})
|
||||
});
|
||||
l.sort_by_key(|(c, _)| ORDER.iter().position(|o| o == c).unwrap_or(ORDER.len()));
|
||||
l
|
||||
}
|
||||
|
||||
pub fn set_language(code: &str) -> bool {
|
||||
@@ -85,3 +97,47 @@ pub fn tr_in(code: &str, key: &str) -> String {
|
||||
i.langs.iter().find(|(c, _)| c == code).and_then(|(_, m)| m.get(key)).cloned().unwrap_or_else(|| key.to_string())
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
fn placeholders(v: &str) -> BTreeSet<String> {
|
||||
let mut out = BTreeSet::new();
|
||||
let mut rest = v;
|
||||
while let Some(a) = rest.find('{') {
|
||||
match rest[a..].find('}') {
|
||||
Some(b) => {
|
||||
out.insert(rest[a..a + b + 1].to_string());
|
||||
rest = &rest[a + b + 1..];
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_language_has_all_keys_and_placeholders() {
|
||||
let en = parse(SOURCES[0].1);
|
||||
for (code, src) in SOURCES.iter().skip(1) {
|
||||
let m = parse(src);
|
||||
let missing: Vec<&String> = en.keys().filter(|k| !m.contains_key(*k)).collect();
|
||||
let extra: Vec<&String> = m.keys().filter(|k| !en.contains_key(*k)).collect();
|
||||
assert!(missing.is_empty() && extra.is_empty(), "{}: missing {:?}, extra {:?}", code, missing, extra);
|
||||
for (k, v) in &en {
|
||||
assert_eq!(placeholders(v), placeholders(&m[k]), "{}: placeholders of {}", code, k);
|
||||
}
|
||||
assert!(m.get("lang.name").map_or(false, |n| !n.is_empty()), "{} has a name", code);
|
||||
assert!(ORDER.contains(code), "{} has a place in the dropdown", code);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_dropdown_follows_the_chosen_order() {
|
||||
let codes: Vec<String> = languages().into_iter().map(|(c, _)| c).collect();
|
||||
let pos = |c: &str| ORDER.iter().position(|o| *o == c).unwrap();
|
||||
assert!(codes.windows(2).all(|w| pos(&w[0]) < pos(&w[1])), "{:?}", codes);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user