diff --git a/client/i18n/src/lib.rs b/client/i18n/src/lib.rs index 6ebbeb1a1d..0f5dce1c95 100644 --- a/client/i18n/src/lib.rs +++ b/client/i18n/src/lib.rs @@ -236,6 +236,11 @@ pub struct LocalizationGuard { } impl LocalizationGuard { + /// Get a localized text from the given key in the fallback language. + pub fn try_fallback_msg(&self, key: &str) -> Option> { + self.fallback.as_ref().and_then(|fb| fb.try_msg(key, None)) + } + /// Get a localized text from the given key /// /// First lookup is done in the active language, second in @@ -243,7 +248,7 @@ impl LocalizationGuard { pub fn try_msg(&self, key: &str) -> Option> { self.active .try_msg(key, None) - .or_else(|| self.fallback.as_ref().and_then(|fb| fb.try_msg(key, None))) + .or_else(|| self.try_fallback_msg(key)) } /// Get a localized text from the given key @@ -344,6 +349,54 @@ impl LocalizationGuard { }) } + // Function to localize content for given language. + // + // Returns Ok(localized_text) if found no errors. + // Returns Err(broken_text) on failure. + // + // broken_text will have i18n keys in it, just i18n key if it was instant miss + // or text with missed keys inlined if it was missed down the chain. + fn get_content_for_lang(lang: &Language, content: &Content) -> Result { + match content { + Content::Plain(text) => Ok(text.clone()), + Content::Key(key) => lang + .try_msg(key, None) + .map(Cow::into_owned) + .ok_or_else(|| key.to_string()), + Content::Attr(key, attr) => lang + .try_attr(key, attr, None) + .map(Cow::into_owned) + .ok_or_else(|| format!("{key}.{attr}")), + Content::Localized { key, seed, args } => { + // flag to detect failure down the chain + let mut is_arg_failure = false; + + let mut fargs = FluentArgs::new(); + for (k, arg) in args { + let arg_val = match arg { + LocalizationArg::Content(content) => { + let arg_res = Self::get_content_for_lang(lang, content) + .unwrap_or_else(|broken_text| { + is_arg_failure = true; + broken_text + }) + .into(); + + FluentValue::String(arg_res) + }, + LocalizationArg::Nat(n) => FluentValue::from(n), + }; + fargs.set(k, arg_val); + } + + lang.try_variation(key, *seed, Some(&fargs)) + .map(Cow::into_owned) + .ok_or_else(|| key.clone()) + .and_then(|text| if is_arg_failure { Err(text) } else { Ok(text) }) + }, + } + } + /// Tries its best to localize compound message. /// /// # Example @@ -383,55 +436,7 @@ impl LocalizationGuard { // // TODO: return Cow? pub fn get_content(&self, content: &Content) -> String { - // Function to localize content for given language. - // - // Returns Ok(localized_text) if found no errors. - // Returns Err(broken_text) on failure. - // - // broken_text will have i18n keys in it, just i18n key if it was instant miss - // or text with missed keys inlined if it was missed down the chain. - fn get_content_for_lang(lang: &Language, content: &Content) -> Result { - match content { - Content::Plain(text) => Ok(text.clone()), - Content::Key(key) => lang - .try_msg(key, None) - .map(Cow::into_owned) - .ok_or_else(|| key.to_string()), - Content::Attr(key, attr) => lang - .try_attr(key, attr, None) - .map(Cow::into_owned) - .ok_or_else(|| format!("{key}.{attr}")), - Content::Localized { key, seed, args } => { - // flag to detect failure down the chain - let mut is_arg_failure = false; - - let mut fargs = FluentArgs::new(); - for (k, arg) in args { - let arg_val = match arg { - LocalizationArg::Content(content) => { - let arg_res = get_content_for_lang(lang, content) - .unwrap_or_else(|broken_text| { - is_arg_failure = true; - broken_text - }) - .into(); - - FluentValue::String(arg_res) - }, - LocalizationArg::Nat(n) => FluentValue::from(n), - }; - fargs.set(k, arg_val); - } - - lang.try_variation(key, *seed, Some(&fargs)) - .map(Cow::into_owned) - .ok_or_else(|| key.clone()) - .and_then(|text| if is_arg_failure { Err(text) } else { Ok(text) }) - }, - } - } - - match get_content_for_lang(&self.active, content) { + match Self::get_content_for_lang(&self.active, content) { Ok(text) => text, // If localisation or some part of it failed, repeat with fallback. // If it did fail as well, it's probably because fallback was disabled, @@ -440,11 +445,20 @@ impl LocalizationGuard { Err(broken_text) => self .fallback .as_ref() - .and_then(|fb| get_content_for_lang(fb, content).ok()) + .and_then(|fb| Self::get_content_for_lang(fb, content).ok()) .unwrap_or(broken_text), } } + pub fn get_content_fallback(&self, content: &Content) -> String { + self.fallback + .as_ref() + .map(|fb| Self::get_content_for_lang(fb, content)) + .transpose() + .map(|msg| msg.unwrap_or_default()) + .unwrap_or_else(|e| e) + } + /// NOTE: Exists for legacy reasons, avoid. /// /// Get a localized text from the variation of given key with given diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index dad5fc506f..a59b472ce5 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -594,34 +594,48 @@ impl<'a> Widget for Crafting<'a> { // then unavailable ones, each sorted by quality and then alphabetically // In the tuple, "name" is the recipe book key, and "recipe.output.0.name()" // is the display name (as stored in the item descriptors) + fn content_contains(content: &common::comp::Content, s: &str) -> bool { + match content { + common::comp::Content::Plain(p) => p.contains(s), + common::comp::Content::Key(k) => k.contains(s), + common::comp::Content::Attr(k, _) => k.contains(s), + common::comp::Content::Localized { key, .. } => key.contains(s), + } + } + let search = |item: &Arc| { + let (name_key, _) = item.i18n(self.item_i18n); + let fallback_name = self + .localized_strings + .get_content_fallback(&name_key) + .to_lowercase(); + let name = self.localized_strings.get_content(&name_key).to_lowercase(); + + search_keys.iter().all(|&substring| { + name.contains(substring) + || fallback_name.contains(substring) + || content_contains(&name_key, substring) + }) + }; let mut ordered_recipes: Vec<_> = self .client .recipe_book() .iter() .filter(|(_, recipe)| match search_filter { SearchFilter::None => { - #[allow(deprecated)] - let output_name = recipe.output.0.name().to_lowercase(); - search_keys - .iter() - .all(|&substring| output_name.contains(substring)) + search(&recipe.output.0) }, SearchFilter::Input => recipe.inputs().any(|(input, _, _)| { - let search = |input_name: &str| { - let input_name = input_name.to_lowercase(); + let search_tag = |name: &str| { search_keys .iter() - .all(|&substring| input_name.contains(substring)) + .all(|&substring| name.contains(substring)) }; - match input { - #[allow(deprecated)] - RecipeInput::Item(def) => search(&def.name()), - RecipeInput::Tag(tag) => search(tag.name()), - RecipeInput::TagSameItem(tag) => search(tag.name()), - #[allow(deprecated)] + RecipeInput::Item(def) => search(def), + RecipeInput::Tag(tag) => search_tag(tag.name()), + RecipeInput::TagSameItem(tag) => search_tag(tag.name()), RecipeInput::ListSameItem(defs) => { - defs.iter().any(|def| search(&def.name())) + defs.iter().any(search) }, } }),