allow searching in both selected language and fallback langauage in crafting

This commit is contained in:
Isse 2024-04-29 13:33:11 +02:00
parent a0b1f76b9b
commit 6c0da4beeb
No known key found for this signature in database
2 changed files with 94 additions and 66 deletions

View File

@ -236,6 +236,11 @@ pub struct LocalizationGuard {
} }
impl LocalizationGuard { impl LocalizationGuard {
/// Get a localized text from the given key in the fallback language.
pub fn try_fallback_msg(&self, key: &str) -> Option<Cow<str>> {
self.fallback.as_ref().and_then(|fb| fb.try_msg(key, None))
}
/// Get a localized text from the given key /// Get a localized text from the given key
/// ///
/// First lookup is done in the active language, second in /// First lookup is done in the active language, second in
@ -243,7 +248,7 @@ impl LocalizationGuard {
pub fn try_msg(&self, key: &str) -> Option<Cow<str>> { pub fn try_msg(&self, key: &str) -> Option<Cow<str>> {
self.active self.active
.try_msg(key, None) .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 /// 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<String, String> {
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. /// Tries its best to localize compound message.
/// ///
/// # Example /// # Example
@ -383,55 +436,7 @@ impl LocalizationGuard {
// //
// TODO: return Cow<str>? // TODO: return Cow<str>?
pub fn get_content(&self, content: &Content) -> String { pub fn get_content(&self, content: &Content) -> String {
// Function to localize content for given language. match Self::get_content_for_lang(&self.active, content) {
//
// 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<String, String> {
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) {
Ok(text) => text, Ok(text) => text,
// If localisation or some part of it failed, repeat with fallback. // If localisation or some part of it failed, repeat with fallback.
// If it did fail as well, it's probably because fallback was disabled, // If it did fail as well, it's probably because fallback was disabled,
@ -440,11 +445,20 @@ impl LocalizationGuard {
Err(broken_text) => self Err(broken_text) => self
.fallback .fallback
.as_ref() .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), .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. /// NOTE: Exists for legacy reasons, avoid.
/// ///
/// Get a localized text from the variation of given key with given /// Get a localized text from the variation of given key with given

View File

@ -594,34 +594,48 @@ impl<'a> Widget for Crafting<'a> {
// then unavailable ones, each sorted by quality and then alphabetically // then unavailable ones, each sorted by quality and then alphabetically
// In the tuple, "name" is the recipe book key, and "recipe.output.0.name()" // In the tuple, "name" is the recipe book key, and "recipe.output.0.name()"
// is the display name (as stored in the item descriptors) // 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<ItemDef>| {
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 let mut ordered_recipes: Vec<_> = self
.client .client
.recipe_book() .recipe_book()
.iter() .iter()
.filter(|(_, recipe)| match search_filter { .filter(|(_, recipe)| match search_filter {
SearchFilter::None => { SearchFilter::None => {
#[allow(deprecated)] search(&recipe.output.0)
let output_name = recipe.output.0.name().to_lowercase();
search_keys
.iter()
.all(|&substring| output_name.contains(substring))
}, },
SearchFilter::Input => recipe.inputs().any(|(input, _, _)| { SearchFilter::Input => recipe.inputs().any(|(input, _, _)| {
let search = |input_name: &str| { let search_tag = |name: &str| {
let input_name = input_name.to_lowercase();
search_keys search_keys
.iter() .iter()
.all(|&substring| input_name.contains(substring)) .all(|&substring| name.contains(substring))
}; };
match input { match input {
#[allow(deprecated)] RecipeInput::Item(def) => search(def),
RecipeInput::Item(def) => search(&def.name()), RecipeInput::Tag(tag) => search_tag(tag.name()),
RecipeInput::Tag(tag) => search(tag.name()), RecipeInput::TagSameItem(tag) => search_tag(tag.name()),
RecipeInput::TagSameItem(tag) => search(tag.name()),
#[allow(deprecated)]
RecipeInput::ListSameItem(defs) => { RecipeInput::ListSameItem(defs) => {
defs.iter().any(|def| search(&def.name())) defs.iter().any(search)
}, },
} }
}), }),