Skip to main content

rigsql_i18n/
lib.rs

1rust_i18n::i18n!("locales", fallback = "en");
2
3/// Get a translated rule description by rule code (e.g. "LT01").
4/// Falls back to the provided default if no translation exists.
5pub fn rule_description(code: &str, default: &str) -> String {
6    let key = format!("rules.{code}.description");
7    let translated = rust_i18n::t!(&key);
8    // rust-i18n returns the key itself when no translation is found
9    if translated == key {
10        default.to_string()
11    } else {
12        translated.to_string()
13    }
14}
15
16/// Get a translated rule explanation by rule code (e.g. "LT01").
17/// Falls back to the provided default if no translation exists.
18pub fn rule_explanation(code: &str, default: &str) -> String {
19    let key = format!("rules.{code}.explanation");
20    let translated = rust_i18n::t!(&key);
21    if translated == key {
22        default.to_string()
23    } else {
24        translated.to_string()
25    }
26}
27
28/// Set the global locale. Use "en", "ja", etc.
29pub fn set_locale(locale: &str) {
30    rust_i18n::set_locale(locale);
31}
32
33/// Get the current global locale.
34pub fn get_locale() -> String {
35    rust_i18n::locale().to_string()
36}
37
38/// Translate a CLI message key with no parameters.
39pub fn t(key: &str) -> String {
40    let translated = rust_i18n::t!(key);
41    translated.to_string()
42}
43
44/// Translate a violation message using its key and parameters.
45/// Returns the translated message with parameters substituted.
46/// Falls back to `fallback` if no translation is found or key is empty.
47pub fn rule_message(key: &str, params: &[(String, String)], fallback: &str) -> String {
48    if key.is_empty() {
49        return fallback.to_string();
50    }
51    let translated = rust_i18n::t!(key);
52    let translated_str = translated.to_string();
53    // rust-i18n returns the key itself when no translation is found
54    if translated_str == key {
55        return fallback.to_string();
56    }
57    // Substitute parameters: %{name} → value
58    // Parameter values themselves may have translations (e.g. "upper" → "大文字")
59    let mut result = translated_str;
60    for (k, v) in params {
61        let translated_v = translate_param_value(v);
62        result = result.replace(&format!("%{{{k}}}"), &translated_v);
63    }
64    result
65}
66
67/// Try to translate a parameter value using the "params.*" namespace.
68/// Falls back to the original value if no translation exists.
69fn translate_param_value(value: &str) -> String {
70    let key = format!("params.{value}");
71    let translated = rust_i18n::t!(&key);
72    let translated_str = translated.to_string();
73    if translated_str == key {
74        value.to_string()
75    } else {
76        translated_str
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    // rust-i18n uses a global locale, so tests that change locale must run
85    // sequentially in a single test to avoid races.
86    #[test]
87    fn test_locale_switching() {
88        // English
89        set_locale("en");
90        assert_eq!(
91            rule_description("LT01", "fallback"),
92            "Inappropriate spacing found."
93        );
94        assert_eq!(t("cli.no_sql_files"), "No SQL files found.");
95
96        // Japanese
97        set_locale("ja");
98        assert_eq!(
99            rule_description("LT01", "fallback"),
100            "不適切なスペースが見つかりました。"
101        );
102        assert_eq!(t("cli.no_sql_files"), "SQLファイルが見つかりません。");
103
104        // Fallback for unknown rule
105        set_locale("en");
106        assert_eq!(rule_description("XX99", "my fallback"), "my fallback");
107
108        // Reset
109        set_locale("en");
110    }
111}