actix_cloud/
i18n.rs

1use std::collections::HashMap;
2
3pub use actix_cloud_codegen::i18n;
4
5/// Get I18n text
6///
7/// ```no_run
8/// use actix_cloud::{i18n::{i18n, Locale},t};
9///
10/// let mut locale = Locale::new("en-US").add_locale(i18n!("locale"));
11///
12/// // Get default locale's text
13/// t!(locale, "greeting");
14/// // With variables
15/// t!(locale, "messages.hello", name = "Jason");
16/// // Get a special locale's text
17/// t!(locale, "greeting", "de");
18/// // With locale and variables
19/// t!(locale, "messages.hello", "de", name = "Jason");
20/// ```
21#[macro_export]
22macro_rules! t {
23    ($l:expr, $key:expr) => {
24        $l.translate(&$l.default, $key)
25    };
26
27    ($l:expr, $key:expr, $($var_name:tt = $var_val:expr),+) => {
28        {
29            let mut message = $l.translate(&$l.default, $key);
30            $(
31                message = message.replace(concat!("%{", stringify!($var_name), "}"), $var_val);
32            )+
33            message
34        }
35    };
36
37    ($l:expr, $key:expr, $locale:expr) => {
38        $l.translate($locale, $key)
39    };
40
41    ($l:expr, $key:expr, $locale:expr, $($var_name:tt = $var_val:expr),+) => {
42        {
43            let mut message = $l.translate($locale, $key);
44            $(
45                message = message.replace(concat!("%{", stringify!($var_name), "}"), $var_val);
46            )+
47            message
48        }
49    };
50}
51
52/// Make map creation easier.
53///
54/// # Examples
55///
56/// ```
57/// use actix_cloud::map;
58/// let val = map!{"key" => "value"};
59/// ```
60#[macro_export]
61macro_rules! map {
62    {$($key:expr => $value:expr),+} => {{
63        let mut m = std::collections::HashMap::new();
64        $(
65            m.insert($key, $value);
66        )+
67        m
68    }};
69}
70
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72#[derive(Debug)]
73pub struct Locale {
74    pub locale: HashMap<String, String>,
75    pub default: String,
76}
77
78impl Locale {
79    pub fn new<S: Into<String>>(default: S) -> Self {
80        Self {
81            locale: HashMap::new(),
82            default: default.into(),
83        }
84    }
85
86    /// Add new locale items.
87    pub fn add_locale<S: Into<String>>(mut self, l: HashMap<S, S>) -> Self {
88        self.locale
89            .extend(l.into_iter().map(|(a, b)| (a.into(), b.into())));
90        self
91    }
92
93    /// Translate string.
94    /// - Fallback to default language if not exist.
95    /// - Again fallback to `key` if still not found.
96    pub fn translate<S1: AsRef<str>, S2: AsRef<str>>(&self, locale: S1, key: S2) -> String {
97        let locale_key = format!("{}.{}", locale.as_ref(), key.as_ref());
98        self.locale.get(locale_key.as_str()).map_or_else(
99            || {
100                if locale.as_ref() == self.default {
101                    key.as_ref().to_owned()
102                } else {
103                    self.translate(&self.default, key)
104                }
105            },
106            ToString::to_string,
107        )
108    }
109}