immortal_intl_rs/
lib.rs

1use serde_json::Value;
2use std::sync::OnceLock;
3use std::{collections::HashMap, env};
4use string_template::Template;
5use structs::I18n;
6
7pub use structs::TranslationConfig;
8
9mod structs;
10
11static RESOURCES_KEY: &'static str = "INTL_RS_RESOURCES";
12
13static DEFAULT_LANGUAGE: &'static str = "INTL_RS_LANG";
14
15pub static I18N: OnceLock<I18n> = OnceLock::new();
16
17fn init_i18n() -> I18n {
18    let resources_path = env::var(RESOURCES_KEY).unwrap_or("src/i18n".to_owned());
19    let default_language = env::var(DEFAULT_LANGUAGE).unwrap_or("en".to_owned());
20    let inner = I18n::init(resources_path, default_language);
21    inner
22}
23
24pub fn format_message<S: Into<String>>(key: S, config: Option<&TranslationConfig>) -> String {
25    let i18n = I18N.get_or_init(init_i18n);
26    let key = key.into();
27    let default_config = TranslationConfig::default();
28    let TranslationConfig {
29        accept_language,
30        default_message,
31        args,
32    } = config.unwrap_or(&default_config);
33    let common_languages = accept_language::intersection(
34        accept_language.as_ref().unwrap_or(&i18n.default_language),
35        i18n.supported_languages.iter().map(|s| s as &str).collect(),
36    );
37    let language = common_languages.first().unwrap_or(&i18n.default_language);
38
39    let configs: &Value = &i18n.inner[language];
40
41    let template_string = match key
42        .split('.')
43        .fold(configs, |result: &Value, k| &result[&k])
44    {
45        Value::Null => default_message.clone(),
46        message => message.as_str().map(|other_str| other_str.to_string()),
47    }
48    .unwrap_or(key.to_string());
49    let template = Template::new(&template_string);
50    template.render(
51        &args
52            .as_ref()
53            .unwrap_or(&HashMap::new())
54            .iter()
55            .map(|(a, b)| (a.as_str(), b.as_str()))
56            .collect(),
57    )
58}
59
60#[macro_export]
61macro_rules! t {
62    ($key:expr) => {
63        $crate::format_message($key, None)
64    };
65    ($key:expr,accept_language:$accept_language:expr) => {
66        $crate::format_message(
67            $key,
68            Some(&$crate::TranslationConfig {
69                accept_language: Some($accept_language.into()),
70                ..Default::default()
71            }),
72        )
73    };
74    ($key:expr,accept_language:$accept_language:expr,args:$args:expr) => {{
75        let accept_language: String = $accept_language.into();
76        let mut accept_language_option = None;
77        if !accept_language.is_empty() {
78            accept_language_option = Some(accept_language)
79        }
80        $crate::format_message(
81            $key,
82            Some(&$crate::TranslationConfig {
83                accept_language: accept_language_option,
84                args: Some($args),
85                ..Default::default()
86            }),
87        )
88    }};
89    ($key:expr,config:$config:expr) => {
90        $crate::format_message($key, Some($config))
91    };
92}