bar app icons & tooltips
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::assets::AssetProvider;
|
||||
|
||||
// a string which optionally has translation key in it
|
||||
@@ -98,10 +100,27 @@ impl I18n {
|
||||
}
|
||||
|
||||
let data_english = provider.load_from_path("lang/en.json")?;
|
||||
let data_translated = provider.load_from_path(&format!("lang/{lang}.json"))?;
|
||||
let path = format!("lang/{lang}.json");
|
||||
let data_translated = provider
|
||||
.load_from_path(&path)
|
||||
.with_context(|| path.clone())
|
||||
.context("Could not load translation file")?;
|
||||
|
||||
let json_root_fallback = serde_json::from_str(str::from_utf8(&data_english)?)?;
|
||||
let json_root_translated = serde_json::from_str(str::from_utf8(&data_translated)?)?;
|
||||
let json_root_fallback = serde_json::from_str(
|
||||
str::from_utf8(&data_english)
|
||||
.with_context(|| path.clone())
|
||||
.context("Translation file not valid UTF-8")?,
|
||||
)
|
||||
.with_context(|| path.clone())
|
||||
.context("Translation file not valid JSON")?;
|
||||
|
||||
let json_root_translated = serde_json::from_str(
|
||||
str::from_utf8(&data_translated)
|
||||
.with_context(|| path.clone())
|
||||
.context("Translation file not valid UTF-8")?,
|
||||
)
|
||||
.with_context(|| path.clone())
|
||||
.context("Translation file not valid JSON")?;
|
||||
|
||||
Ok(Self {
|
||||
json_root_translated,
|
||||
@@ -110,22 +129,16 @@ impl I18n {
|
||||
}
|
||||
|
||||
pub fn translate(&mut self, translation_key_full: &str) -> Rc<str> {
|
||||
let translation_key = translation_key_full
|
||||
.split_once(';')
|
||||
.map_or(translation_key_full, |(a, _)| a);
|
||||
let mut sections = translation_key_full.split(';');
|
||||
let translation_key = sections.next().map_or(translation_key_full, |a| a);
|
||||
|
||||
if let Some(translated) = find_translation(translation_key, &self.json_root_translated) {
|
||||
return Rc::from(translated);
|
||||
return Rc::from(format_translated(translated, sections));
|
||||
}
|
||||
|
||||
if let Some(translated_fallback) = find_translation(translation_key, &self.json_root_fallback) {
|
||||
log::warn!("missing translation for key \"{translation_key}\", using \"en\" instead");
|
||||
return Rc::from(translated_fallback);
|
||||
}
|
||||
|
||||
// not even found in fallback, check if the translation contains ";" (to be used as "MY_TRANSLATION_KEY;A fallback text")
|
||||
if let Some((idx, _)) = translation_key_full.match_indices(';').next() {
|
||||
return Rc::from(&translation_key_full[idx + 1..]);
|
||||
return Rc::from(format_translated(translated_fallback, sections));
|
||||
}
|
||||
|
||||
log::error!("missing translation for key \"{translation_key}\"");
|
||||
@@ -137,3 +150,28 @@ impl I18n {
|
||||
translated.replace(to_replace.0, to_replace.1)
|
||||
}
|
||||
}
|
||||
|
||||
fn format_translated<'a, I>(format: &str, args: I) -> String
|
||||
where
|
||||
I: IntoIterator<Item = &'a str>,
|
||||
{
|
||||
let mut result = String::new();
|
||||
let mut args = args.into_iter();
|
||||
|
||||
let mut chars = format.chars().peekable();
|
||||
while let Some(c) = chars.next() {
|
||||
if c == '{' && chars.peek() == Some(&'}') {
|
||||
chars.next(); //consume }
|
||||
if let Some(arg) = args.next() {
|
||||
result.push_str(arg);
|
||||
} else {
|
||||
// no more args → keep literal {}
|
||||
result.push_str("{}");
|
||||
}
|
||||
} else {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ pub mod gfx;
|
||||
pub mod globals;
|
||||
pub mod i18n;
|
||||
pub mod layout;
|
||||
pub mod log;
|
||||
pub mod parser;
|
||||
pub mod renderer_vk;
|
||||
pub mod sound;
|
||||
|
||||
53
wgui/src/log.rs
Normal file
53
wgui/src/log.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait LogErr {
|
||||
fn log_err(self) -> Self;
|
||||
fn log_err_with(self, additional: &str) -> Self;
|
||||
fn log_warn(self) -> Self;
|
||||
fn log_warn_with(self, additional: &str) -> Self;
|
||||
}
|
||||
|
||||
impl<T, E> LogErr for Result<T, E>
|
||||
where
|
||||
E: Debug + Send + Sync + 'static,
|
||||
{
|
||||
fn log_warn(self) -> Result<T, E> {
|
||||
match self {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(error) => {
|
||||
log::warn!("{error:?}");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn log_warn_with(self, additional: &str) -> Result<T, E> {
|
||||
match self {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(error) => {
|
||||
log::warn!("{additional}: {error:?}");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn log_err(self) -> Result<T, E> {
|
||||
match self {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(error) => {
|
||||
log::error!("{error:?}");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn log_err_with(self, additional: &str) -> Result<T, E> {
|
||||
match self {
|
||||
Ok(ok) => Ok(ok),
|
||||
Err(error) => {
|
||||
log::error!("{additional}: {error:?}");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user