Files
meta-feeder-tmdb/crates/meta-feeder-sdk/src/lang.rs
T
Worph c7299d549c
Publish image / publish (push) Failing after 13s
initial commit
2026-07-25 21:28:35 +02:00

82 lines
3.2 KiB
Rust

//! Language-code normalization — ISO 639 alias folding, **not** filename
//! parsing.
//!
//! This lives apart from [`crate::filename_meta`] on purpose: it doesn't read a
//! release title, it canonicalizes a *language code* (an indexer's declared
//! language, a query's `languages:` filter, a title-derived stamp) onto the
//! single ISO 639-2/B alpha-3 vocabulary the rest of the stack compares against.
//! It is the one piece of the old `filename_meta` module that has no equivalent
//! in the `metamesh-plugin-filename-parser` plugin — because it isn't filename
//! parsing — so it stays in Rust while the title-parsing algorithm itself is
//! owned by the plugin. Callers in the **request hot path** (e.g. the torznab
//! indexer-language fan-out skip) need it synchronously, with no network hop.
/// Normalize a language code to the ISO 639-2/B alpha-3 vocabulary the stack
/// emits (`fre`/`ger`/`chi`, **not** the 639-2/T `fra`/`deu`/`zho` nor the
/// 639-1 two-letter forms), lowercased. This is the space meta-share's
/// "languages I read" preference uses and that the title-derived language stamps
/// write, so an operator who declares an indexer's language as `fra`/`de`/`zh`
/// still lines up with a `fre`/`ger`/`chi` query filter. Unknown /
/// already-canonical codes pass through lowercased+trimmed unchanged.
pub fn normalize_lang_code(code: &str) -> String {
let c = code.trim().to_ascii_lowercase();
let mapped = match c.as_str() {
// 639-2/T and 639-1 → 639-2/B for the codes that actually differ.
"fra" | "fr" => "fre",
"deu" | "de" => "ger",
"zho" | "zh" => "chi",
"nld" | "nl" => "dut",
"ell" | "el" => "gre",
"ron" | "ro" => "rum",
"slk" | "sk" => "slo",
"ces" | "cs" => "cze",
// 639-1 → 639-2 for the rest of the language-tag output space (these
// share the same /B and /T alpha-3, so only the two-letter form maps).
"en" => "eng",
"ja" => "jpn",
"it" => "ita",
"es" => "spa",
"pt" => "por",
"ko" => "kor",
"ru" => "rus",
"pl" => "pol",
"hu" => "hun",
"uk" => "ukr",
"bg" => "bul",
"sv" => "swe",
"no" => "nor",
"da" => "dan",
"fi" => "fin",
"tr" => "tur",
"ar" => "ara",
"hi" => "hin",
"ta" => "tam",
"te" => "tel",
"ml" => "mal",
"vi" => "vie",
"th" => "tha",
_ => return c,
};
mapped.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn normalize_lang_maps_to_639_2b_vocabulary() {
// 639-2/T and 639-1 spellings collapse onto the /B forms the stack uses.
assert_eq!(normalize_lang_code("fra"), "fre");
assert_eq!(normalize_lang_code("FR"), "fre");
assert_eq!(normalize_lang_code("deu"), "ger");
assert_eq!(normalize_lang_code("zh"), "chi");
assert_eq!(normalize_lang_code("nld"), "dut");
// Already-canonical / unknown codes pass through trimmed + lowercased.
assert_eq!(normalize_lang_code(" ENG "), "eng");
assert_eq!(normalize_lang_code("fre"), "fre");
assert_eq!(normalize_lang_code("mul"), "mul");
assert_eq!(normalize_lang_code("xyz"), "xyz");
}
}