Avoid to use an hash map in translations

This commit is contained in:
Greg Burri 2025-01-07 16:47:24 +01:00
parent 03ebbb74fa
commit 91ab379718
10 changed files with 334 additions and 237 deletions

View file

@ -2,7 +2,7 @@ use rinja_axum::Template;
use crate::{
data::model,
translation::{Sentence, Tr},
translation::{self, Sentence, Tr},
};
pub struct Recipes {

View file

@ -231,7 +231,7 @@ async fn translation(
let language = if let Some(user) = user {
user.lang
} else {
let available_codes = Tr::available_codes();
let available_codes = translation::available_codes();
let jar = CookieJar::from_headers(req.headers());
match jar.get(consts::COOKIE_LANG_NAME) {
Some(lang) if available_codes.contains(&lang.value()) => lang.value().to_string(),

View file

@ -249,7 +249,7 @@ pub async fn set_language(
Extension(user): Extension<Option<model::User>>,
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeLanguage>,
) -> Result<StatusCode> {
if !crate::translation::Tr::available_codes()
if !crate::translation::available_codes()
.iter()
.any(|&l| l == ron.lang)
{

View file

@ -1,15 +1,16 @@
use std::{fs::File, sync::LazyLock};
use ron::de::from_reader;
use rustc_hash::FxHashMap;
use serde::Deserialize;
use strum::EnumCount;
use strum_macros::EnumCount;
use tracing::{event, Level};
use crate::consts;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Clone)]
#[derive(Debug, Clone, EnumCount, Deserialize)]
pub enum Sentence {
MainTitle,
MainTitle = 0,
CreateNewRecipe,
UnpublishedRecipes,
UntitledRecipe,
@ -107,6 +108,8 @@ pub enum Sentence {
RecipeIngredientComment,
}
const DEFAULT_LANGUAGE_CODE: &str = "en";
#[derive(Clone)]
pub struct Tr {
lang: &'static Language,
@ -114,61 +117,48 @@ pub struct Tr {
impl Tr {
pub fn new(code: &str) -> Self {
for lang in TRANSLATIONS.iter() {
if lang.code == code {
return Self { lang };
}
Self {
lang: get_language_translation(code),
}
event!(
Level::WARN,
"Unable to find translation for language {}",
code
);
Tr::new("en")
}
pub fn t(&self, sentence: Sentence) -> String {
match self.lang.translation.get(&sentence) {
Some(str) => str.clone(),
None => format!(
"Translation missing, lang: {}/{}, element: {:?}",
self.lang.name, self.lang.code, sentence
),
}
//&'static str {
self.lang.get(sentence).to_string()
// match self.lang.translation.get(&sentence) {
// Some(str) => str.clone(),
// None => format!(
// "Translation missing, lang: {}/{}, element: {:?}",
// self.lang.name, self.lang.code, sentence
// ),
// }
}
pub fn tp(&self, sentence: Sentence, params: &[Box<dyn ToString + Send>]) -> String {
match self.lang.translation.get(&sentence) {
Some(str) => {
let mut result = str.clone();
for p in params {
result = result.replacen("{}", &p.to_string(), 1);
}
result
}
None => format!(
"Translation missing, lang: {}/{}, element: {:?}",
self.lang.name, self.lang.code, sentence
),
// match self.lang.translation.get(&sentence) {
// Some(str) => {
// let mut result = str.clone();
// for p in params {
// result = result.replacen("{}", &p.to_string(), 1);
// }
// result
// }
// None => format!(
// "Translation missing, lang: {}/{}, element: {:?}",
// self.lang.name, self.lang.code, sentence
// ),
// }
let text = self.lang.get(sentence);
let mut result = text.to_string();
for p in params {
result = result.replacen("{}", &p.to_string(), 1);
}
result
}
pub fn current_lang_code(&self) -> &str {
&self.lang.code
}
pub fn available_languages() -> Vec<(&'static str, &'static str)> {
TRANSLATIONS
.iter()
.map(|tr| (tr.code.as_ref(), tr.name.as_ref()))
.collect()
}
pub fn available_codes() -> Vec<&'static str> {
TRANSLATIONS.iter().map(|tr| tr.code.as_ref()).collect()
}
}
// #[macro_export]
@ -185,22 +175,98 @@ impl Tr {
// };
// }
#[derive(Debug, Deserialize, Clone)]
#[derive(Debug, Deserialize)]
struct StoredLanguage {
code: String,
name: String,
translation: Vec<(Sentence, String)>,
}
#[derive(Debug)]
struct Language {
code: String,
name: String,
translation: FxHashMap<Sentence, String>,
translation: Vec<String>,
}
impl Language {
pub fn from_stored_language(stored_language: StoredLanguage) -> Self {
println!("!!!!!!!!!!!! {:?}", &stored_language.code);
Self {
code: stored_language.code,
name: stored_language.name,
translation: {
let mut translation = vec![String::new(); Sentence::COUNT];
for (sentence, text) in stored_language.translation {
translation[sentence as usize] = text;
}
translation
},
}
}
pub fn get(&'static self, sentence: Sentence) -> &'static str {
let text: &str = self
.translation
.get(sentence.clone() as usize)
.unwrap()
.as_ref();
if text.is_empty() && self.code != DEFAULT_LANGUAGE_CODE {
return get_language_translation(DEFAULT_LANGUAGE_CODE).get(sentence);
}
text
}
}
pub fn available_languages() -> Vec<(&'static str, &'static str)> {
TRANSLATIONS
.iter()
.map(|tr| (tr.code.as_ref(), tr.name.as_ref()))
.collect()
}
pub fn available_codes() -> Vec<&'static str> {
TRANSLATIONS.iter().map(|tr| tr.code.as_ref()).collect()
}
fn get_language_translation(code: &str) -> &'static Language {
for lang in TRANSLATIONS.iter() {
if lang.code == code {
return lang;
}
}
event!(
Level::WARN,
"Unable to find translation for language {}",
code
);
if code != DEFAULT_LANGUAGE_CODE {
get_language_translation(DEFAULT_LANGUAGE_CODE)
} else {
// 'DEFAULT_LANGUAGE_CODE' must exist.
panic!("Unable to find language {}", code);
}
}
static TRANSLATIONS: LazyLock<Vec<Language>> =
LazyLock::new(|| match File::open(consts::TRANSLATION_FILE) {
Ok(file) => from_reader(file).unwrap_or_else(|error| {
panic!(
"Failed to read translation file {}: {}",
consts::TRANSLATION_FILE,
error
)
}),
Ok(file) => {
let stored_languages: Vec<StoredLanguage> = from_reader(file).unwrap_or_else(|error| {
{
panic!(
"Failed to read translation file {}: {}",
consts::TRANSLATION_FILE,
error
)
}
});
stored_languages
.into_iter()
.map(Language::from_stored_language)
.collect()
}
Err(error) => {
panic!(
"Failed to open translation file {}: {}",