Skip to main content

localization_macros/
lib.rs

1mod load;
2mod t;
3use std::collections::HashMap;
4
5use load::{default_locale, get_locale};
6use proc_macro2::{Literal, TokenStream, TokenTree};
7use quote::quote;
8use t::{RawTokenStream, parse_t};
9
10fn hashmap_to_tokens(
11    h: &HashMap<String, String>,
12    default_locale: &str,
13) -> (TokenStream, Vec<TokenStream>, usize) {
14    let mut locales = Vec::new();
15    let mut values = Vec::new();
16    let mut default_index = 0;
17    for (i, (key, value)) in h.iter().enumerate() {
18        if key == default_locale {
19            default_index = i + 1;
20        }
21        let key = Literal::string(key);
22        locales.push(quote! {
23            #key => #i,
24        });
25        let value = Literal::string(value);
26        values.push(value);
27    }
28    if default_index == 0 {
29        panic!("Default locale not found");
30    }
31    (
32        quote! {
33            let values = [
34                #(#values),*
35            ];
36        },
37        locales,
38        default_index - 1,
39    )
40}
41
42fn append(l: Literal) -> Literal {
43    let s = l.to_string();
44    let mut s = s[1..s.len() - 1].to_string();
45    s.insert(0, '{');
46    s.insert(0, '{');
47    s.insert(s.len(), '}');
48    s.insert(s.len(), '}');
49    Literal::string(&s)
50}
51
52fn into_literal(ts: &TokenStream) -> Literal {
53    let ts = ts.clone().into_iter();
54    let mut s = String::new();
55    for item in ts {
56        match item {
57            TokenTree::Literal(l) => {
58                s.push_str(&t::trim_literal(l));
59            }
60            TokenTree::Punct(p) => {
61                s.push(p.as_char());
62            }
63            TokenTree::Ident(i) => {
64                s.push_str(&i.to_string());
65            }
66            TokenTree::Group(g) => {
67                s.push_str(&g.to_string());
68            }
69        }
70    }
71    Literal::string(&s)
72}
73
74fn replacement_to_tokens(r: &[(TokenStream, Option<TokenStream>)]) -> TokenStream {
75    let mut tokens = TokenStream::new();
76    for (key, value) in r {
77        let value = if let Some(value) = value { value } else { key };
78        let key = append(into_literal(key));
79        tokens.extend(quote! {
80            value = value.replace(
81                #key,
82                &format!("{}", #value)
83            );
84        });
85    }
86    tokens
87}
88
89/// Use the localization thing
90/// # Example
91/// ```
92/// use localization::t;
93/// fn example() {
94///   let name = "John";
95///   let age = 42;
96///   let s = t!("ja-JP","default:hello", name, age);
97///   println!("{}", s);
98/// }
99/// ```
100#[proc_macro]
101pub fn t(item: RawTokenStream) -> RawTokenStream {
102    let (locale, key, replacement) = parse_t(item);
103    let map = match get_locale().get(&key) {
104        Some(map) => map,
105        None => panic!(
106            "Key not found: {}. Available keys: {:?}",
107            key,
108            get_locale().keys()
109        ),
110    };
111    let replacement = replacement_to_tokens(&replacement);
112    let (values, names, default_index) = hashmap_to_tokens(map, &default_locale());
113    quote!(
114        {
115            #values;
116            let mut value = values[match format!("{}", #locale).as_str() {
117                #(#names)*
118                _ => #default_index,
119            }].to_string();
120            #replacement
121            value
122        }
123    )
124    .into()
125}
126
127#[proc_macro]
128pub fn all(_item: RawTokenStream) -> RawTokenStream {
129    let all = load::get_locale();
130    let mut all_coll = Vec::new();
131    for (key, map) in all {
132        let mut map_coll = Vec::new();
133        for (key, value) in map {
134            let key = Literal::string(key);
135            let value = Literal::string(value);
136            map_coll.push(quote! {
137                map.insert(#key, #value);
138            });
139        }
140        let key = Literal::string(key);
141        all_coll.push(quote! {
142            let mut map = std::collections::HashMap::<&'static str, &'static str>::new();
143            #(#map_coll)*
144            all.insert(#key, map);
145        });
146    }
147    quote!(
148        {
149            let mut all = std::collections::HashMap::new();
150            #(
151                #all_coll
152            )*
153            all
154        }
155    )
156    .into()
157}
158
159/// get all localization
160#[proc_macro]
161pub fn loc(_item: RawTokenStream) -> RawTokenStream {
162    let loc = load::get_locale_list();
163    let loc = loc.iter().map(|x| Literal::string(x));
164    quote!(
165        [
166            #(
167                #loc
168            ),*
169        ]
170    )
171    .into()
172}