Skip to main content

i18n_string/
translate.rs

1use alloc::string::String;
2use core::str::FromStr;
3
4use crate::{I18nString, Resolver};
5
6fn translate_to<R: Resolver + ?Sized>(input: &I18nString, output: &mut String, resolver: &R) {
7    match input {
8        I18nString::Literal(s) => {
9            output.push_str(s);
10        }
11        I18nString::Template(template, args) => {
12            enum ParseState {
13                Normal,
14                HitLeftBrace { pos: usize },
15                HitRightBrace,
16            }
17
18            let template = resolver.resolve(template);
19
20            let mut state = ParseState::Normal;
21
22            for (idx, c) in template.char_indices() {
23                match state {
24                    ParseState::Normal => {
25                        if c == '{' {
26                            state = ParseState::HitLeftBrace { pos: idx };
27                        } else if c == '}' {
28                            state = ParseState::HitRightBrace;
29                        } else {
30                            output.push(c);
31                        }
32                    }
33                    ParseState::HitLeftBrace { pos } => {
34                        if c == '}' {
35                            match usize::from_str(&template[pos + 1..idx]).ok().and_then(|idx| args.get(idx)) {
36                                Some(arg) => {
37                                    translate_to(arg, output, resolver);
38                                }
39                                None => {
40                                    // ignore invalid format or no arg
41                                    output.push('{');
42                                    output.push_str(&template[pos + 1..idx]);
43                                    output.push('}');
44                                }
45                            }
46
47                            state = ParseState::Normal;
48                        } else if c == '{' {
49                            output.push(c);
50                            state = ParseState::Normal;
51                        }
52                    }
53                    ParseState::HitRightBrace => {
54                        if c == '}' {
55                            output.push(c);
56                            state = ParseState::Normal;
57                        } else {
58                            // ignore invalid format
59                            output.push('}');
60                            output.push(c);
61                            state = ParseState::Normal;
62                        }
63                    }
64                }
65            }
66
67            match state {
68                ParseState::Normal => {}
69                ParseState::HitLeftBrace { pos } => {
70                    // ignore unclosed left brace
71                    output.push('{');
72                    output.push_str(&template[pos + 1..]);
73                }
74                ParseState::HitRightBrace => {
75                    // ignore unclosed right brace
76                    output.push('}');
77                }
78            }
79        }
80    }
81}
82
83impl I18nString {
84    pub fn translate<R: Resolver>(&self, resolver: R) -> String {
85        let mut res = String::with_capacity(32);
86        translate_to(self, &mut res, &resolver);
87        res
88    }
89}