11use fluent_bundle:: { FluentArgs , FluentResource , FluentBundle } ;
22use unic_langid:: LanguageIdentifier ;
33use std:: collections:: HashMap ;
4+ use thiserror:: Error ;
5+
6+ #[ derive( Debug , Error ) ]
7+ pub enum PuzzleError {
8+ #[ error( "Language identifier parse error" ) ]
9+ LangIdParse ,
10+ #[ error( "Failed to parse fluent resource: {0}" ) ]
11+ FluentResourceParse ( String ) ,
12+ #[ error( "Failed to add resource to bundle" ) ]
13+ BundleResourceError ,
14+ }
415
516pub mod test;
617
@@ -21,15 +32,15 @@ pub struct I18nManager {
2132impl I18nManager {
2233 /// Initializes i18n manager with requested locale code (e.g. "fr", "es", "en").
2334 /// If requested locale is missing or invalid, falls back to "en".
24- pub fn new ( requested_locale : Option < & str > ) -> Self {
35+ pub fn new ( requested_locale : Option < & str > ) -> Result < Self , PuzzleError > {
2536 let locale_map: HashMap < & str , & str > = LOCALES . iter ( ) . cloned ( ) . collect ( ) ;
2637
27- let fallback_lang: LanguageIdentifier = "en" . parse ( ) . expect ( "Valid fallback langid" ) ;
38+ let fallback_lang: LanguageIdentifier = "en" . parse ( ) . map_err ( |_| PuzzleError :: LangIdParse ) ? ;
2839 let mut fallback_bundle = FluentBundle :: new ( vec ! [ fallback_lang] ) ;
2940 if let Some ( en_src) = locale_map. get ( "en" ) {
3041 let res = FluentResource :: try_new ( en_src. to_string ( ) )
31- . expect ( "Failed to parse en.ftl resource" ) ;
32- fallback_bundle. add_resource ( res) . expect ( "Failed to add en resource" ) ;
42+ . map_err ( | ( _ , errs ) | PuzzleError :: FluentResourceParse ( format ! ( "{:?}" , errs ) ) ) ? ;
43+ fallback_bundle. add_resource ( res) . map_err ( |_| PuzzleError :: BundleResourceError ) ? ;
3344 }
3445
3546 let requested = requested_locale. unwrap_or ( "en" ) . to_lowercase ( ) ;
@@ -41,21 +52,24 @@ impl I18nManager {
4152
4253 let fallback_triggered = target_code != requested. as_str ( ) ;
4354
44- let target_lang: LanguageIdentifier = target_code. parse ( ) . unwrap_or_else ( |_| "en" . parse ( ) . unwrap ( ) ) ;
55+ let target_lang: LanguageIdentifier = target_code. parse ( ) . unwrap_or_else ( |_| {
56+ // SAFETY: "en" is a known valid LanguageIdentifier and is used as a hardcoded fallback.
57+ "en" . parse ( ) . unwrap ( )
58+ } ) ;
4559 let mut active_bundle = FluentBundle :: new ( vec ! [ target_lang] ) ;
4660
4761 if let Some ( src) = locale_map. get ( target_code) {
48- if let Ok ( res) = FluentResource :: try_new ( src. to_string ( ) ) {
49- let _ = active_bundle . add_resource ( res ) ;
50- }
62+ let res = FluentResource :: try_new ( src. to_string ( ) )
63+ . map_err ( | ( _ , errs ) | PuzzleError :: FluentResourceParse ( format ! ( "{:?}" , errs ) ) ) ? ;
64+ active_bundle . add_resource ( res ) . map_err ( |_| PuzzleError :: BundleResourceError ) ? ;
5165 }
5266
53- Self {
67+ Ok ( Self {
5468 active_locale : target_code. to_string ( ) ,
5569 active_bundle,
5670 fallback_bundle,
5771 fallback_triggered,
58- }
72+ } )
5973 }
6074
6175 /// Resolves localized string by key with optional arguments.
0 commit comments