use crate::cli::RupassArgs;
use crate::core::app_errors::{GenerationError, Result};
use fluent::{FluentArgs, FluentBundle, FluentResource};
use rust_embed::RustEmbed;
use std::str::FromStr;
use unic_langid::LanguageIdentifier;
const DEFAULT_LANGUAGE: &str = "eng";
#[derive(RustEmbed)]
#[folder = "./translation"]
#[include = "*.ftl"]
struct Translations;
fn get_embedded_resource(filename: &str) -> Option<String> {
Translations::get(filename)
.and_then(|data: rust_embed::EmbeddedFile| String::from_utf8(data.data.to_vec()).ok())
}
fn canonical_language_code(code: &str) -> String {
let lower = code.trim().to_ascii_lowercase();
match lower.as_str() {
"en" | "eng" => "eng".to_owned(),
"ja" | "jp" | "jpn" => "jpn".to_owned(),
"de" | "deu" | "ger" => "deu".to_owned(),
_ => lower,
}
}
fn map_to_fluent_code(code: &str) -> Result<(LanguageIdentifier, String)> {
let canonical = canonical_language_code(code);
let langid = LanguageIdentifier::from_str(&canonical)
.map_err(|_| GenerationError::UnsupportedLanguage)?;
Ok((langid, canonical))
}
fn resolve_language(args: &RupassArgs) -> String {
let raw = args
.language
.as_ref()
.map(|l| l.to_string())
.unwrap_or_else(|| DEFAULT_LANGUAGE.to_string());
canonical_language_code(&raw)
}
fn load_fluent_bundle(language: &str) -> Result<FluentBundle<FluentResource>> {
let (langid, canonical) = map_to_fluent_code(language)?;
let resource_filename = format!("{canonical}.ftl");
let ftl_string =
get_embedded_resource(&resource_filename).ok_or(GenerationError::UnsupportedLanguage)?;
let ftl_resource = FluentResource::try_new(ftl_string)
.map_err(|_parse_errors| GenerationError::ResourceParseError)?;
let mut bundle = FluentBundle::new(vec![langid]);
bundle
.add_resource(ftl_resource)
.map_err(|_resource_errors| GenerationError::ResourceParseError)?;
Ok(bundle)
}
#[doc(alias = "i18n")]
#[doc(alias = "internationalization")]
#[doc(alias = "localization")]
pub fn initialize_bundle(args: &RupassArgs) -> Result<FluentBundle<FluentResource>> {
let language = resolve_language(args);
load_fluent_bundle(&language)
}
#[doc(alias = "translate")]
#[doc(alias = "get message")]
pub fn get_translation<'bundle>(
bundle: &'bundle FluentBundle<FluentResource>,
key: &str,
args: Option<&FluentArgs<'bundle>>,
) -> Result<String> {
let message = bundle
.get_message(key)
.ok_or_else(|| GenerationError::TranslationMissing(key.to_string()))?;
let value = message
.value()
.ok_or_else(|| GenerationError::TranslationMissing(key.to_string()))?;
let formatted_value = bundle.format_pattern(value, args, &mut Vec::new());
Ok(formatted_value.trim_matches('"').to_owned())
}