rusty2048_shared/
i18n.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Supported languages
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub enum Language {
7    English,
8    Chinese,
9}
10
11impl Language {
12    /// Get language code
13    pub fn code(&self) -> &'static str {
14        match self {
15            Language::English => "en",
16            Language::Chinese => "zh",
17        }
18    }
19
20    /// Get language name
21    pub fn name(&self) -> &'static str {
22        match self {
23            Language::English => "English",
24            Language::Chinese => "中文",
25        }
26    }
27
28    /// Get all supported languages
29    pub fn all() -> Vec<Self> {
30        vec![Language::English, Language::Chinese]
31    }
32
33    /// Parse from string
34    pub fn from_code(code: &str) -> Option<Self> {
35        match code {
36            "en" | "en-US" | "en-GB" => Some(Language::English),
37            "zh" | "zh-CN" | "zh-TW" => Some(Language::Chinese),
38            _ => None,
39        }
40    }
41}
42
43/// Translation keys
44#[derive(Debug, Clone, PartialEq, Eq, Hash)]
45pub enum TranslationKey {
46    // Game UI
47    Title,
48    Score,
49    Best,
50    Moves,
51    Time,
52    NewGame,
53    Undo,
54    GameOver,
55    Congratulations,
56    YouWon,
57    PressRToRestart,
58    ContinuePlaying,
59
60    // Controls
61    Controls,
62    MoveTiles,
63    Restart,
64    UndoMove,
65    CycleTheme,
66    SelectTheme,
67    ThemeHelp,
68    ReplayMode,
69    StatisticsCharts,
70    AIMode,
71    Help,
72    Quit,
73
74    // Replay Mode
75    ReplayModeTitle,
76    StartRecording,
77    LoadReplay,
78    ListReplays,
79    BackToMenu,
80    PlayPause,
81    StepThrough,
82    AdjustSpeed,
83    StopRecording,
84
85    // AI Mode
86    AIModeTitle,
87    ToggleAutoPlay,
88    SwitchAlgorithm,
89    AdjustSpeedAI,
90    ExitImmediately,
91    Greedy,
92    Expectimax,
93    MCTS,
94
95    // Charts
96    ChartsTitle,
97    Summary,
98    ScoreTrend,
99    EfficiencyTrend,
100    TileAchievements,
101    RecentGames,
102    NavigateCharts,
103    ToggleCharts,
104
105    // Statistics
106    Statistics,
107    GamesPlayed,
108    GamesWon,
109    WinRate,
110    HighestScore,
111    AverageScore,
112    TotalMoves,
113    AverageMoves,
114    TotalPlayTime,
115    AverageDuration,
116    HighestTile,
117    ScoreDistribution,
118    LowScore,
119    MediumScore,
120    HighScore,
121    VeryHighScore,
122    NoDataAvailable,
123    NoGamesPlayed,
124    NoRecentGames,
125
126    // Themes
127    ThemeClassic,
128    ThemeDark,
129    ThemeNeon,
130    ThemeRetro,
131    ThemePastel,
132    AvailableThemes,
133    PressTToCycle,
134    PressNumbersToSelect,
135
136    // Messages
137    Loading,
138    Error,
139    Success,
140    Warning,
141    Info,
142
143    // Time formatting
144    Hours,
145    Minutes,
146    Seconds,
147}
148
149/// Translation data
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct TranslationData {
152    pub language: Language,
153    pub translations: HashMap<String, String>,
154}
155
156impl TranslationData {
157    /// Create English translations
158    pub fn english() -> Self {
159        let mut translations = HashMap::new();
160
161        // Game UI
162        translations.insert("title".to_string(), "Rusty2048".to_string());
163        translations.insert("score".to_string(), "Score".to_string());
164        translations.insert("best".to_string(), "Best".to_string());
165        translations.insert("moves".to_string(), "Moves".to_string());
166        translations.insert("time".to_string(), "Time".to_string());
167        translations.insert("new_game".to_string(), "New Game".to_string());
168        translations.insert("undo".to_string(), "Undo".to_string());
169        translations.insert("game_over".to_string(), "Game Over!".to_string());
170        translations.insert(
171            "congratulations".to_string(),
172            "🎉 Congratulations!".to_string(),
173        );
174        translations.insert("you_won".to_string(), "You won!".to_string());
175        translations.insert(
176            "press_r_to_restart".to_string(),
177            "Press R to restart".to_string(),
178        );
179        translations.insert(
180            "continue_playing".to_string(),
181            "or continue playing".to_string(),
182        );
183
184        // Controls
185        translations.insert("controls".to_string(), "Controls".to_string());
186        translations.insert("move_tiles".to_string(), "WASD/Arrow Keys".to_string());
187        translations.insert("restart".to_string(), "R".to_string());
188        translations.insert("undo_move".to_string(), "U".to_string());
189        translations.insert("cycle_theme".to_string(), "T".to_string());
190        translations.insert("select_theme".to_string(), "1-5".to_string());
191        translations.insert("theme_help".to_string(), "H".to_string());
192        translations.insert("replay_mode".to_string(), "Replay".to_string());
193        translations.insert("statistics_charts".to_string(), "Charts".to_string());
194        translations.insert("ai_mode".to_string(), "AI".to_string());
195        translations.insert("help".to_string(), "Help".to_string());
196        translations.insert("quit".to_string(), "Quit".to_string());
197
198        // Replay Mode
199        translations.insert("replay_mode_title".to_string(), "Replay Mode".to_string());
200        translations.insert("start_recording".to_string(), "Start Recording".to_string());
201        translations.insert("load_replay".to_string(), "Load Replay".to_string());
202        translations.insert("list_replays".to_string(), "List Replays".to_string());
203        translations.insert("back_to_menu".to_string(), "Back to Menu".to_string());
204        translations.insert("play_pause".to_string(), "Space".to_string());
205        translations.insert("step_through".to_string(), "Left/Right".to_string());
206        translations.insert("adjust_speed".to_string(), "+/-".to_string());
207        translations.insert("stop_recording".to_string(), "S".to_string());
208
209        // AI Mode
210        translations.insert("ai_mode_title".to_string(), "AI Mode".to_string());
211        translations.insert("toggle_auto_play".to_string(), "O".to_string());
212        translations.insert("switch_algorithm".to_string(), "[ ]".to_string());
213        translations.insert("adjust_speed_ai".to_string(), "+/-".to_string());
214        translations.insert("exit_immediately".to_string(), "Q/ESC".to_string());
215        translations.insert("greedy".to_string(), "Greedy".to_string());
216        translations.insert("expectimax".to_string(), "Expectimax".to_string());
217        translations.insert("mcts".to_string(), "MCTS".to_string());
218
219        // Charts
220        translations.insert("charts_title".to_string(), "Statistics Charts".to_string());
221        translations.insert("summary".to_string(), "Summary".to_string());
222        translations.insert("score_trend".to_string(), "Score Trend".to_string());
223        translations.insert(
224            "efficiency_trend".to_string(),
225            "Efficiency Trend".to_string(),
226        );
227        translations.insert(
228            "tile_achievements".to_string(),
229            "Tile Achievements".to_string(),
230        );
231        translations.insert("recent_games".to_string(), "Recent Games".to_string());
232        translations.insert("navigate_charts".to_string(), "Left/Right".to_string());
233        translations.insert("toggle_charts".to_string(), "C".to_string());
234
235        // Statistics
236        translations.insert("statistics".to_string(), "Statistics".to_string());
237        translations.insert("games_played".to_string(), "Games Played".to_string());
238        translations.insert("games_won".to_string(), "Won".to_string());
239        translations.insert("win_rate".to_string(), "Win Rate".to_string());
240        translations.insert("highest_score".to_string(), "Highest Score".to_string());
241        translations.insert("average_score".to_string(), "Average Score".to_string());
242        translations.insert("total_moves".to_string(), "Total Moves".to_string());
243        translations.insert("average_moves".to_string(), "Avg Moves".to_string());
244        translations.insert("total_play_time".to_string(), "Total Play Time".to_string());
245        translations.insert("average_duration".to_string(), "Avg Duration".to_string());
246        translations.insert("highest_tile".to_string(), "Highest Tile".to_string());
247        translations.insert(
248            "score_distribution".to_string(),
249            "Score Distribution".to_string(),
250        );
251        translations.insert("low_score".to_string(), "0-1000".to_string());
252        translations.insert("medium_score".to_string(), "1001-5000".to_string());
253        translations.insert("high_score".to_string(), "5001-10000".to_string());
254        translations.insert("very_high_score".to_string(), "10001+".to_string());
255        translations.insert(
256            "no_data_available".to_string(),
257            "No data available".to_string(),
258        );
259        translations.insert(
260            "no_games_played".to_string(),
261            "No games played yet!".to_string(),
262        );
263        translations.insert("no_recent_games".to_string(), "No recent games".to_string());
264
265        // Themes
266        translations.insert("theme_classic".to_string(), "Classic".to_string());
267        translations.insert("theme_dark".to_string(), "Dark".to_string());
268        translations.insert("theme_neon".to_string(), "Neon".to_string());
269        translations.insert("theme_retro".to_string(), "Retro".to_string());
270        translations.insert("theme_pastel".to_string(), "Pastel".to_string());
271        translations.insert(
272            "available_themes".to_string(),
273            "Available Themes".to_string(),
274        );
275        translations.insert(
276            "press_t_to_cycle".to_string(),
277            "Press T to cycle themes".to_string(),
278        );
279        translations.insert(
280            "press_numbers_to_select".to_string(),
281            "or number keys 1-5 to select directly".to_string(),
282        );
283
284        // Messages
285        translations.insert("loading".to_string(), "Loading...".to_string());
286        translations.insert("error".to_string(), "Error".to_string());
287        translations.insert("success".to_string(), "Success".to_string());
288        translations.insert("warning".to_string(), "Warning".to_string());
289        translations.insert("info".to_string(), "Info".to_string());
290
291        // Time formatting
292        translations.insert("hours".to_string(), "h".to_string());
293        translations.insert("minutes".to_string(), "m".to_string());
294        translations.insert("seconds".to_string(), "s".to_string());
295
296        Self {
297            language: Language::English,
298            translations,
299        }
300    }
301
302    /// Create Chinese translations
303    pub fn chinese() -> Self {
304        let mut translations = HashMap::new();
305
306        // Game UI
307        translations.insert("title".to_string(), "Rusty2048".to_string());
308        translations.insert("score".to_string(), "分数".to_string());
309        translations.insert("best".to_string(), "最高分".to_string());
310        translations.insert("moves".to_string(), "步数".to_string());
311        translations.insert("time".to_string(), "时间".to_string());
312        translations.insert("new_game".to_string(), "新游戏".to_string());
313        translations.insert("undo".to_string(), "撤销".to_string());
314        translations.insert("game_over".to_string(), "游戏结束!".to_string());
315        translations.insert("congratulations".to_string(), "🎉 恭喜!".to_string());
316        translations.insert("you_won".to_string(), "你赢了!".to_string());
317        translations.insert("press_r_to_restart".to_string(), "按R重新开始".to_string());
318        translations.insert("continue_playing".to_string(), "或继续游戏".to_string());
319
320        // Controls
321        translations.insert("controls".to_string(), "控制".to_string());
322        translations.insert("move_tiles".to_string(), "WASD/方向键".to_string());
323        translations.insert("restart".to_string(), "R".to_string());
324        translations.insert("undo_move".to_string(), "U".to_string());
325        translations.insert("cycle_theme".to_string(), "T".to_string());
326        translations.insert("select_theme".to_string(), "1-5".to_string());
327        translations.insert("theme_help".to_string(), "H".to_string());
328        translations.insert("replay_mode".to_string(), "回放".to_string());
329        translations.insert("statistics_charts".to_string(), "图表".to_string());
330        translations.insert("ai_mode".to_string(), "AI".to_string());
331        translations.insert("help".to_string(), "帮助".to_string());
332        translations.insert("quit".to_string(), "退出".to_string());
333
334        // Replay Mode
335        translations.insert("replay_mode_title".to_string(), "回放模式".to_string());
336        translations.insert("start_recording".to_string(), "开始录制".to_string());
337        translations.insert("load_replay".to_string(), "加载回放".to_string());
338        translations.insert("list_replays".to_string(), "回放列表".to_string());
339        translations.insert("back_to_menu".to_string(), "返回菜单".to_string());
340        translations.insert("play_pause".to_string(), "空格".to_string());
341        translations.insert("step_through".to_string(), "左右键".to_string());
342        translations.insert("adjust_speed".to_string(), "+/-".to_string());
343        translations.insert("stop_recording".to_string(), "S".to_string());
344
345        // AI Mode
346        translations.insert("ai_mode_title".to_string(), "AI模式".to_string());
347        translations.insert("toggle_auto_play".to_string(), "O".to_string());
348        translations.insert("switch_algorithm".to_string(), "[ ]".to_string());
349        translations.insert("adjust_speed_ai".to_string(), "+/-".to_string());
350        translations.insert("exit_immediately".to_string(), "Q/ESC".to_string());
351        translations.insert("greedy".to_string(), "贪心".to_string());
352        translations.insert("expectimax".to_string(), "期望最大化".to_string());
353        translations.insert("mcts".to_string(), "蒙特卡洛".to_string());
354
355        // Charts
356        translations.insert("charts_title".to_string(), "统计图表".to_string());
357        translations.insert("summary".to_string(), "摘要".to_string());
358        translations.insert("score_trend".to_string(), "分数趋势".to_string());
359        translations.insert("efficiency_trend".to_string(), "效率趋势".to_string());
360        translations.insert("tile_achievements".to_string(), "瓦片成就".to_string());
361        translations.insert("recent_games".to_string(), "最近游戏".to_string());
362        translations.insert("navigate_charts".to_string(), "左右键".to_string());
363        translations.insert("toggle_charts".to_string(), "C".to_string());
364
365        // Statistics
366        translations.insert("statistics".to_string(), "统计".to_string());
367        translations.insert("games_played".to_string(), "游戏局数".to_string());
368        translations.insert("games_won".to_string(), "胜利".to_string());
369        translations.insert("win_rate".to_string(), "胜率".to_string());
370        translations.insert("highest_score".to_string(), "最高分".to_string());
371        translations.insert("average_score".to_string(), "平均分".to_string());
372        translations.insert("total_moves".to_string(), "总步数".to_string());
373        translations.insert("average_moves".to_string(), "平均步数".to_string());
374        translations.insert("total_play_time".to_string(), "总游戏时间".to_string());
375        translations.insert("average_duration".to_string(), "平均时长".to_string());
376        translations.insert("highest_tile".to_string(), "最高瓦片".to_string());
377        translations.insert("score_distribution".to_string(), "分数分布".to_string());
378        translations.insert("low_score".to_string(), "0-1000".to_string());
379        translations.insert("medium_score".to_string(), "1001-5000".to_string());
380        translations.insert("high_score".to_string(), "5001-10000".to_string());
381        translations.insert("very_high_score".to_string(), "10001+".to_string());
382        translations.insert("no_data_available".to_string(), "暂无数据".to_string());
383        translations.insert(
384            "no_games_played".to_string(),
385            "还没有玩过游戏!".to_string(),
386        );
387        translations.insert("no_recent_games".to_string(), "没有最近游戏".to_string());
388
389        // Themes
390        translations.insert("theme_classic".to_string(), "经典".to_string());
391        translations.insert("theme_dark".to_string(), "暗黑".to_string());
392        translations.insert("theme_neon".to_string(), "霓虹".to_string());
393        translations.insert("theme_retro".to_string(), "复古".to_string());
394        translations.insert("theme_pastel".to_string(), "粉彩".to_string());
395        translations.insert("available_themes".to_string(), "可用主题".to_string());
396        translations.insert(
397            "press_t_to_cycle".to_string(),
398            "按T循环切换主题".to_string(),
399        );
400        translations.insert(
401            "press_numbers_to_select".to_string(),
402            "或按数字键1-5直接选择".to_string(),
403        );
404
405        // Messages
406        translations.insert("loading".to_string(), "加载中...".to_string());
407        translations.insert("error".to_string(), "错误".to_string());
408        translations.insert("success".to_string(), "成功".to_string());
409        translations.insert("warning".to_string(), "警告".to_string());
410        translations.insert("info".to_string(), "信息".to_string());
411
412        // Time formatting
413        translations.insert("hours".to_string(), "时".to_string());
414        translations.insert("minutes".to_string(), "分".to_string());
415        translations.insert("seconds".to_string(), "秒".to_string());
416
417        Self {
418            language: Language::Chinese,
419            translations,
420        }
421    }
422}
423
424/// Internationalization manager
425#[derive(Debug, Clone)]
426pub struct I18n {
427    current_language: Language,
428    translations: HashMap<Language, TranslationData>,
429}
430
431impl I18n {
432    /// Create a new I18n instance
433    pub fn new() -> Self {
434        let mut translations = HashMap::new();
435        translations.insert(Language::English, TranslationData::english());
436        translations.insert(Language::Chinese, TranslationData::chinese());
437
438        Self {
439            current_language: Language::English,
440            translations,
441        }
442    }
443
444    /// Set current language
445    pub fn set_language(&mut self, language: Language) {
446        self.current_language = language;
447    }
448
449    /// Get current language
450    pub fn current_language(&self) -> Language {
451        self.current_language
452    }
453
454    /// Get translation for a key
455    pub fn t(&self, key: &TranslationKey) -> String {
456        let key_str = self.key_to_string(key);
457        if let Some(translation_data) = self.translations.get(&self.current_language) {
458            if let Some(translation) = translation_data.translations.get(&key_str) {
459                return translation.clone();
460            }
461        }
462
463        // Fallback to English
464        if let Some(translation_data) = self.translations.get(&Language::English) {
465            if let Some(translation) = translation_data.translations.get(&key_str) {
466                return translation.clone();
467            }
468        }
469
470        // Return key as fallback
471        key_str
472    }
473
474    /// Get translation with parameters
475    pub fn t_with_params(&self, key: &TranslationKey, params: &[(&str, &str)]) -> String {
476        let mut text = self.t(key);
477        for (param, value) in params {
478            text = text.replace(&format!("{{{}}}", param), value);
479        }
480        text
481    }
482
483    /// Convert translation key to string
484    fn key_to_string(&self, key: &TranslationKey) -> String {
485        match key {
486            TranslationKey::Title => "title".to_string(),
487            TranslationKey::Score => "score".to_string(),
488            TranslationKey::Best => "best".to_string(),
489            TranslationKey::Moves => "moves".to_string(),
490            TranslationKey::Time => "time".to_string(),
491            TranslationKey::NewGame => "new_game".to_string(),
492            TranslationKey::Undo => "undo".to_string(),
493            TranslationKey::GameOver => "game_over".to_string(),
494            TranslationKey::Congratulations => "congratulations".to_string(),
495            TranslationKey::YouWon => "you_won".to_string(),
496            TranslationKey::PressRToRestart => "press_r_to_restart".to_string(),
497            TranslationKey::ContinuePlaying => "continue_playing".to_string(),
498            TranslationKey::Controls => "controls".to_string(),
499            TranslationKey::MoveTiles => "move_tiles".to_string(),
500            TranslationKey::Restart => "restart".to_string(),
501            TranslationKey::UndoMove => "undo_move".to_string(),
502            TranslationKey::CycleTheme => "cycle_theme".to_string(),
503            TranslationKey::SelectTheme => "select_theme".to_string(),
504            TranslationKey::ThemeHelp => "theme_help".to_string(),
505            TranslationKey::ReplayMode => "replay_mode".to_string(),
506            TranslationKey::StatisticsCharts => "statistics_charts".to_string(),
507            TranslationKey::AIMode => "ai_mode".to_string(),
508            TranslationKey::Help => "help".to_string(),
509            TranslationKey::Quit => "quit".to_string(),
510            TranslationKey::ReplayModeTitle => "replay_mode_title".to_string(),
511            TranslationKey::StartRecording => "start_recording".to_string(),
512            TranslationKey::LoadReplay => "load_replay".to_string(),
513            TranslationKey::ListReplays => "list_replays".to_string(),
514            TranslationKey::BackToMenu => "back_to_menu".to_string(),
515            TranslationKey::PlayPause => "play_pause".to_string(),
516            TranslationKey::StepThrough => "step_through".to_string(),
517            TranslationKey::AdjustSpeed => "adjust_speed".to_string(),
518            TranslationKey::StopRecording => "stop_recording".to_string(),
519            TranslationKey::AIModeTitle => "ai_mode_title".to_string(),
520            TranslationKey::ToggleAutoPlay => "toggle_auto_play".to_string(),
521            TranslationKey::SwitchAlgorithm => "switch_algorithm".to_string(),
522            TranslationKey::AdjustSpeedAI => "adjust_speed_ai".to_string(),
523            TranslationKey::ExitImmediately => "exit_immediately".to_string(),
524            TranslationKey::Greedy => "greedy".to_string(),
525            TranslationKey::Expectimax => "expectimax".to_string(),
526            TranslationKey::MCTS => "mcts".to_string(),
527            TranslationKey::ChartsTitle => "charts_title".to_string(),
528            TranslationKey::Summary => "summary".to_string(),
529            TranslationKey::ScoreTrend => "score_trend".to_string(),
530            TranslationKey::EfficiencyTrend => "efficiency_trend".to_string(),
531            TranslationKey::TileAchievements => "tile_achievements".to_string(),
532            TranslationKey::RecentGames => "recent_games".to_string(),
533            TranslationKey::NavigateCharts => "navigate_charts".to_string(),
534            TranslationKey::ToggleCharts => "toggle_charts".to_string(),
535            TranslationKey::Statistics => "statistics".to_string(),
536            TranslationKey::GamesPlayed => "games_played".to_string(),
537            TranslationKey::GamesWon => "games_won".to_string(),
538            TranslationKey::WinRate => "win_rate".to_string(),
539            TranslationKey::HighestScore => "highest_score".to_string(),
540            TranslationKey::AverageScore => "average_score".to_string(),
541            TranslationKey::TotalMoves => "total_moves".to_string(),
542            TranslationKey::AverageMoves => "average_moves".to_string(),
543            TranslationKey::TotalPlayTime => "total_play_time".to_string(),
544            TranslationKey::AverageDuration => "average_duration".to_string(),
545            TranslationKey::HighestTile => "highest_tile".to_string(),
546            TranslationKey::ScoreDistribution => "score_distribution".to_string(),
547            TranslationKey::LowScore => "low_score".to_string(),
548            TranslationKey::MediumScore => "medium_score".to_string(),
549            TranslationKey::HighScore => "high_score".to_string(),
550            TranslationKey::VeryHighScore => "very_high_score".to_string(),
551            TranslationKey::NoDataAvailable => "no_data_available".to_string(),
552            TranslationKey::NoGamesPlayed => "no_games_played".to_string(),
553            TranslationKey::NoRecentGames => "no_recent_games".to_string(),
554            TranslationKey::ThemeClassic => "theme_classic".to_string(),
555            TranslationKey::ThemeDark => "theme_dark".to_string(),
556            TranslationKey::ThemeNeon => "theme_neon".to_string(),
557            TranslationKey::ThemeRetro => "theme_retro".to_string(),
558            TranslationKey::ThemePastel => "theme_pastel".to_string(),
559            TranslationKey::AvailableThemes => "available_themes".to_string(),
560            TranslationKey::PressTToCycle => "press_t_to_cycle".to_string(),
561            TranslationKey::PressNumbersToSelect => "press_numbers_to_select".to_string(),
562            TranslationKey::Loading => "loading".to_string(),
563            TranslationKey::Error => "error".to_string(),
564            TranslationKey::Success => "success".to_string(),
565            TranslationKey::Warning => "warning".to_string(),
566            TranslationKey::Info => "info".to_string(),
567            TranslationKey::Hours => "hours".to_string(),
568            TranslationKey::Minutes => "minutes".to_string(),
569            TranslationKey::Seconds => "seconds".to_string(),
570        }
571    }
572
573    /// Get all supported languages
574    pub fn supported_languages(&self) -> Vec<Language> {
575        Language::all()
576    }
577
578    /// Format duration in localized format
579    pub fn format_duration(&self, seconds: u64) -> String {
580        let hours = seconds / 3600;
581        let minutes = (seconds % 3600) / 60;
582        let secs = seconds % 60;
583
584        if hours > 0 {
585            format!(
586                "{}{}{:02}{}{:02}{}",
587                hours,
588                self.t(&TranslationKey::Hours),
589                minutes,
590                self.t(&TranslationKey::Minutes),
591                secs,
592                self.t(&TranslationKey::Seconds)
593            )
594        } else {
595            format!(
596                "{}{}{:02}{}",
597                minutes,
598                self.t(&TranslationKey::Minutes),
599                secs,
600                self.t(&TranslationKey::Seconds)
601            )
602        }
603    }
604}
605
606impl Default for I18n {
607    fn default() -> Self {
608        Self::new()
609    }
610}