include!(concat!(env!("OUT_DIR"), "/langs_generated.rs"));
pub(crate) fn strip_json5_comments(s: &str) -> String {
s.lines()
.map(|line| {
if line.trim_start().starts_with("//") {
""
} else {
line
}
})
.collect::<Vec<_>>()
.join("\n")
}
fn parse_lang(entry: &LangEntry) -> serde_json::Value {
serde_json::from_str(&strip_json5_comments(entry.data)).unwrap_or_default()
}
pub(crate) fn load_i18n() -> String {
let mut map = serde_json::Map::new();
for entry in LANGS {
map.insert(entry.key.to_string(), parse_lang(entry));
}
serde_json::Value::Object(map).to_string()
}
pub(crate) fn resolve_lang(language: &str) -> &'static str {
for l in LANGS {
if language == l.value || language == l.key {
return l.key;
}
}
let locale = sys_locale::get_locale().unwrap_or_default().to_lowercase();
for l in LANGS {
if locale.starts_with(l.key) {
return l.key;
}
}
DEFAULT_LANG_KEY
}
pub fn get_lang_data(language: &str) -> serde_json::Value {
let key = resolve_lang(language);
let entry = LANGS
.iter()
.find(|l| l.key == key)
.unwrap_or(&LANGS[LANGS.len() - 1]);
parse_lang(entry)
}
pub fn all_i18n_with_languages() -> serde_json::Value {
let mut result = serde_json::Map::new();
let mut languages = Vec::new();
for entry in LANGS {
let data = parse_lang(entry);
let label = data["_meta.label"]
.as_str()
.unwrap_or(entry.value)
.to_string();
languages.push(serde_json::json!({
"value": entry.value,
"key": entry.key,
"label": label,
}));
result.insert(entry.key.to_string(), data);
}
result.insert(
"_languages".to_string(),
serde_json::Value::Array(languages),
);
serde_json::Value::Object(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strip_json5_comments_removes_line_comments() {
let input = "// this is a comment\n{\"key\": \"value\"}";
let result = strip_json5_comments(input);
assert_eq!(result, "\n{\"key\": \"value\"}");
}
#[test]
fn strip_json5_comments_preserves_non_comment_lines() {
let input = "{\"url\": \"https://example.com\"}";
let result = strip_json5_comments(input);
assert_eq!(result, input);
}
#[test]
fn strip_json5_comments_handles_indented_comments() {
let input = " // indented comment\n value";
let result = strip_json5_comments(input);
assert_eq!(result, "\n value");
}
#[test]
fn strip_json5_comments_empty_input() {
assert_eq!(strip_json5_comments(""), "");
}
#[test]
fn strip_json5_comments_only_comments() {
let input = "// a\n// b\n// c";
let result = strip_json5_comments(input);
assert_eq!(result, "\n\n");
}
#[test]
fn resolve_lang_exact_value_match() {
assert_eq!(resolve_lang("en"), "en");
}
#[test]
fn resolve_lang_locale_value() {
assert_eq!(resolve_lang("zh_CN"), "zh");
}
#[test]
fn resolve_lang_key_match() {
assert_eq!(resolve_lang("zh"), "zh");
}
#[test]
fn resolve_lang_unknown_falls_back() {
let result = resolve_lang("xx_UNKNOWN");
assert!(LANGS.iter().any(|l| l.key == result));
}
#[test]
fn get_lang_data_returns_object() {
let data = get_lang_data("en");
assert!(data.is_object());
}
#[test]
fn all_i18n_with_languages_contains_languages_list() {
let result = all_i18n_with_languages();
let languages = result["_languages"].as_array().unwrap();
assert!(!languages.is_empty());
for lang in languages {
assert!(lang["value"].is_string());
assert!(lang["key"].is_string());
assert!(lang["label"].is_string());
}
}
#[test]
fn load_i18n_returns_valid_json() {
let json_str = load_i18n();
let parsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
assert!(parsed.is_object());
}
}