From 2d60da9a6fe0a7418d07475b7c0a677f7f2922d4 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 30 Sep 2025 16:03:23 +0200 Subject: Extract server locale code to own crate --- server/locale/Cargo.toml | 8 +++ server/locale/src/lib.rs | 154 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 server/locale/Cargo.toml create mode 100644 server/locale/src/lib.rs (limited to 'server/locale') diff --git a/server/locale/Cargo.toml b/server/locale/Cargo.toml new file mode 100644 index 00000000..4e0b60cf --- /dev/null +++ b/server/locale/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "hurrycurry-locale" +version = "0.1.0" +edition = "2024" + +[dependencies] +hurrycurry-protocol = { path = "../protocol" } +anyhow = "1.0.100" diff --git a/server/locale/src/lib.rs b/server/locale/src/lib.rs new file mode 100644 index 00000000..1d01e4e1 --- /dev/null +++ b/server/locale/src/lib.rs @@ -0,0 +1,154 @@ +/* + Hurry Curry! - a game about cooking + Copyright (C) 2025 Hurry Curry! Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, version 3 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +*/ + +use anyhow::anyhow; +use hurrycurry_protocol::Message; +use std::{ + collections::HashMap, + fmt::{Debug, Display}, + ops::Index, + sync::LazyLock, +}; + +#[macro_export] +macro_rules! trm { + ($id:literal $(, $typ:ident = $param:expr)*) => { + hurrycurry_protocol::Message::Translation { + id: $id.to_owned(), + params: vec![$($crate::trm_param!($typ, $param)),*] + } + }; +} + +#[macro_export] +macro_rules! trm_param { + (s, $x:expr) => { + hurrycurry_protocol::Message::Text($x) + }; + (i, $x:expr) => { + hurrycurry_protocol::Message::Item($x) + }; + (t, $x:expr) => { + hurrycurry_protocol::Message::Tile($x) + }; + (m, $x:expr) => { + $x + }; +} + +pub enum TrError { + Tr { + id: &'static str, + params: Vec, + }, + Plain(String), +} +impl From for Message { + fn from(value: TrError) -> Self { + match value { + TrError::Tr { id, params } => Self::Translation { + id: id.to_owned(), + params, + }, + TrError::Plain(s) => Self::Text(s), + } + } +} +impl Debug for TrError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TrError::Tr { id, params } => write!(f, "{} {:?}", tr(id), params), + TrError::Plain(s) => write!(f, "{s}"), + } + } +} +impl Display for TrError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TrError::Tr { id, params } => { + if params.is_empty() { + write!(f, "{}", tr(id)) + } else { + let mut s = tr(id).to_string(); + for (i, p) in params.iter().enumerate() { + s = s.replace(&format!("{{{i}}}"), &format!("{p:?}")); + } + write!(f, "{s}") + } + } + TrError::Plain(s) => write!(f, "{s}"), + } + } +} + +#[macro_export] +macro_rules! tre { + ($id:literal $(, $typ:ident = $param:expr)*) => { + $crate::TrError::Tr { + id: $id, + params: vec![$($crate::tre_param!($typ, $param)),*] + } + }; +} + +#[macro_export] +macro_rules! tre_param { + (s, $x:expr) => { + hurrycurry_protocol::Message::Text($x) + }; + (i, $x:expr) => { + hurrycurry_protocol::Message::Item($x) + }; + (t, $x:expr) => { + hurrycurry_protocol::Message::Tile($x) + }; + (m, $x:expr) => { + $x + }; +} + +pub struct Strings(HashMap); +impl Index<&'static str> for Strings { + type Output = str; + fn index(&self, index: &'static str) -> &Self::Output { + self.0.get(index).map(|s| s.as_str()).unwrap_or(index) + } +} + +impl Strings { + pub fn load() -> anyhow::Result { + Ok(Self( + include_str!("../../../locale/en.ini") + .lines() + .skip(1) + .map(|l| { + let (k, v) = l.split_once("=").ok_or(anyhow!("'=' missing"))?; + Ok::<_, anyhow::Error>(( + k.trim_end().to_owned(), + v.trim_start().replace("%n", "\n"), + )) + }) + .collect::>>()?, + )) + } +} + +static TR: LazyLock = LazyLock::new(|| Strings::load().unwrap()); +pub fn tr(s: &'static str) -> &'static str { + &TR[s] +} -- cgit v1.2.3-70-g09d2