i18n+filtering fork - fluent-templates v2
at main 5.0 kB view raw
1use anyhow::Result; 2use fluent::{bundle::FluentBundle, FluentArgs, FluentResource}; 3use std::collections::HashMap; 4use unic_langid::LanguageIdentifier; 5 6// Define error types here instead of importing them 7// Using errors from i18n module 8use crate::i18n::errors::I18nError; 9 10pub type Bundle = FluentBundle<FluentResource, intl_memoizer::concurrent::IntlLangMemoizer>; 11 12pub struct Locales(pub HashMap<LanguageIdentifier, Bundle>); 13 14impl Locales { 15 pub fn new(locales: Vec<LanguageIdentifier>) -> Self { 16 let mut store = HashMap::new(); 17 for locale in &locales { 18 let bundle: FluentBundle<FluentResource, intl_memoizer::concurrent::IntlLangMemoizer> = 19 FluentBundle::new_concurrent(vec![locale.clone()]); 20 store.insert(locale.clone(), bundle); 21 } 22 Self(store) 23 } 24 25 pub fn add_bundle( 26 &mut self, 27 locale: LanguageIdentifier, 28 content: String, 29 ) -> Result<(), I18nError> { 30 let bundle = self.0.get_mut(&locale).ok_or(I18nError::InvalidLanguage)?; 31 32 let resource = FluentResource::try_new(content) 33 .map_err(|(_, errors)| I18nError::LanguageResourceFailed(errors))?; 34 35 bundle 36 .add_resource(resource) 37 .map_err(I18nError::BundleLoadFailed)?; 38 39 Ok(()) 40 } 41 42 pub fn format_error(&self, locale: &LanguageIdentifier, bare: &str, partial: &str) -> String { 43 let bundle = self.0.get(locale); 44 if bundle.is_none() { 45 return partial.to_string(); 46 } 47 48 let bundle = bundle.unwrap(); 49 50 let bundle_message = bundle.get_message(bare); 51 52 if bundle_message.is_none() { 53 return partial.to_string(); 54 } 55 56 let bundle_message = bundle_message.unwrap(); 57 58 let mut errors = Vec::new(); 59 60 if bundle_message.value().is_none() { 61 return partial.to_string(); 62 } 63 let bundle_message_value = bundle_message.value().unwrap(); 64 65 let formatted_pattern = bundle.format_pattern(bundle_message_value, None, &mut errors); 66 67 formatted_pattern.to_string() 68 } 69 70 pub fn format_message( 71 &self, 72 locale: &LanguageIdentifier, 73 message: &str, 74 args: FluentArgs, 75 ) -> String { 76 let bundle = self.0.get(locale); 77 if bundle.is_none() { 78 return message.to_string(); 79 } 80 81 let bundle = bundle.unwrap(); 82 83 let bundle_message = bundle.get_message(message); 84 85 if bundle_message.is_none() { 86 return message.to_string(); 87 } 88 89 let bundle_message = bundle_message.unwrap(); 90 91 let mut errors = Vec::new(); 92 93 if bundle_message.value().is_none() { 94 return message.to_string(); 95 } 96 let bundle_message_value = bundle_message.value().unwrap(); 97 98 let formatted_pattern = 99 bundle.format_pattern(bundle_message_value, Some(&args), &mut errors); 100 101 formatted_pattern.to_string() 102 } 103} 104 105#[cfg(feature = "embed")] 106pub mod embed { 107 use rust_embed::Embed; 108 use unic_langid::LanguageIdentifier; 109 110 use crate::i18n::{errors::I18nError, Locales}; 111 112 #[derive(Embed)] 113 #[folder = "i18n/"] 114 struct I18nAssets; 115 116 pub fn populate_locale( 117 supported_locales: &Vec<LanguageIdentifier>, 118 locales: &mut Locales, 119 ) -> Result<(), I18nError> { 120 let locale_files = vec!["errors"]; 121 122 for locale in supported_locales { 123 for file in &locale_files { 124 let source_file = format!("{}/{}.ftl", locale.to_string().to_lowercase(), file); 125 let i18n_asset = I18nAssets::get(&source_file).expect("locale file not found"); 126 let content = std::str::from_utf8(i18n_asset.data.as_ref()) 127 .expect("invalid utf-8 in locale file"); 128 locales.add_bundle(locale.clone(), content.to_string())?; 129 } 130 } 131 Ok(()) 132 } 133} 134 135#[cfg(feature = "reload")] 136pub mod reload { 137 use std::path::PathBuf; 138 use unic_langid::LanguageIdentifier; 139 140 use crate::i18n::{errors::I18nError, Locales}; 141 142 pub fn populate_locale( 143 supported_locales: &Vec<LanguageIdentifier>, 144 locales: &mut Locales, 145 ) -> Result<(), I18nError> { 146 let locale_files = vec!["errors"]; 147 148 for locale in supported_locales { 149 let locale_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 150 .join("i18n") 151 .join(locale.to_string().to_lowercase()); 152 for file in &locale_files { 153 let source_file = locale_dir.join(format!("{}.ftl", file)); 154 tracing::info!("Loading locale file: {:?}", source_file); 155 let i18n_asset = std::fs::read(source_file).expect("failed to read locale file"); 156 let content = 157 std::str::from_utf8(&i18n_asset).expect("invalid utf-8 in locale file"); 158 locales.add_bundle(locale.clone(), content.to_string())?; 159 } 160 } 161 Ok(()) 162 } 163}