bar app icons & tooltips

This commit is contained in:
galister
2026-01-05 15:45:19 +09:00
parent b86525d65d
commit b56aa1a8de
30 changed files with 291 additions and 129 deletions

View File

@@ -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
}

View File

@@ -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
View 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)
}
}
}
}