Avoid to use an hash map in translations
This commit is contained in:
parent
03ebbb74fa
commit
91ab379718
10 changed files with 334 additions and 237 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
|
@ -122,9 +122,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.84"
|
version = "0.1.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0"
|
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -1756,18 +1756,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95"
|
checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-internal",
|
"pin-project-internal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-internal"
|
name = "pin-project-internal"
|
||||||
version = "1.1.7"
|
version = "1.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
|
checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -1776,9 +1776,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.15"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
|
|
@ -1930,9 +1930,10 @@ dependencies = [
|
||||||
"rinja",
|
"rinja",
|
||||||
"rinja_axum",
|
"rinja_axum",
|
||||||
"ron",
|
"ron",
|
||||||
"rustc-hash",
|
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"strum",
|
||||||
|
"strum_macros",
|
||||||
"thiserror 2.0.9",
|
"thiserror 2.0.9",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
|
|
@ -2210,9 +2211,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.134"
|
version = "1.0.135"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
|
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -2572,6 +2573,25 @@ version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ ron = "0.8"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
rustc-hash = "2.1"
|
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
|
||||||
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio", "chrono"] }
|
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio", "chrono"] }
|
||||||
|
|
@ -34,6 +33,8 @@ rinja_axum = "0.3"
|
||||||
argon2 = { version = "0.5", features = ["default", "std"] }
|
argon2 = { version = "0.5", features = ["default", "std"] }
|
||||||
rand_core = { version = "0.6", features = ["std"] }
|
rand_core = { version = "0.6", features = ["std"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
strum = "0.26"
|
||||||
|
strum_macros = "0.26"
|
||||||
|
|
||||||
lettre = { version = "0.11", default-features = false, features = [
|
lettre = { version = "0.11", default-features = false, features = [
|
||||||
"smtp-transport",
|
"smtp-transport",
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,12 @@ body {
|
||||||
font-size: 0.5em;
|
font-size: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
.main-container {
|
.main-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use rinja_axum::Template;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data::model,
|
data::model,
|
||||||
translation::{Sentence, Tr},
|
translation::{self, Sentence, Tr},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Recipes {
|
pub struct Recipes {
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ async fn translation(
|
||||||
let language = if let Some(user) = user {
|
let language = if let Some(user) = user {
|
||||||
user.lang
|
user.lang
|
||||||
} else {
|
} else {
|
||||||
let available_codes = Tr::available_codes();
|
let available_codes = translation::available_codes();
|
||||||
let jar = CookieJar::from_headers(req.headers());
|
let jar = CookieJar::from_headers(req.headers());
|
||||||
match jar.get(consts::COOKIE_LANG_NAME) {
|
match jar.get(consts::COOKIE_LANG_NAME) {
|
||||||
Some(lang) if available_codes.contains(&lang.value()) => lang.value().to_string(),
|
Some(lang) if available_codes.contains(&lang.value()) => lang.value().to_string(),
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ pub async fn set_language(
|
||||||
Extension(user): Extension<Option<model::User>>,
|
Extension(user): Extension<Option<model::User>>,
|
||||||
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeLanguage>,
|
ExtractRon(ron): ExtractRon<common::ron_api::SetRecipeLanguage>,
|
||||||
) -> Result<StatusCode> {
|
) -> Result<StatusCode> {
|
||||||
if !crate::translation::Tr::available_codes()
|
if !crate::translation::available_codes()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|&l| l == ron.lang)
|
.any(|&l| l == ron.lang)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
use std::{fs::File, sync::LazyLock};
|
use std::{fs::File, sync::LazyLock};
|
||||||
|
|
||||||
use ron::de::from_reader;
|
use ron::de::from_reader;
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use strum::EnumCount;
|
||||||
|
use strum_macros::EnumCount;
|
||||||
use tracing::{event, Level};
|
use tracing::{event, Level};
|
||||||
|
|
||||||
use crate::consts;
|
use crate::consts;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Clone)]
|
#[derive(Debug, Clone, EnumCount, Deserialize)]
|
||||||
pub enum Sentence {
|
pub enum Sentence {
|
||||||
MainTitle,
|
MainTitle = 0,
|
||||||
CreateNewRecipe,
|
CreateNewRecipe,
|
||||||
UnpublishedRecipes,
|
UnpublishedRecipes,
|
||||||
UntitledRecipe,
|
UntitledRecipe,
|
||||||
|
|
@ -107,6 +108,8 @@ pub enum Sentence {
|
||||||
RecipeIngredientComment,
|
RecipeIngredientComment,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_LANGUAGE_CODE: &str = "en";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Tr {
|
pub struct Tr {
|
||||||
lang: &'static Language,
|
lang: &'static Language,
|
||||||
|
|
@ -114,61 +117,48 @@ pub struct Tr {
|
||||||
|
|
||||||
impl Tr {
|
impl Tr {
|
||||||
pub fn new(code: &str) -> Self {
|
pub fn new(code: &str) -> Self {
|
||||||
for lang in TRANSLATIONS.iter() {
|
Self {
|
||||||
if lang.code == code {
|
lang: get_language_translation(code),
|
||||||
return Self { lang };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event!(
|
|
||||||
Level::WARN,
|
|
||||||
"Unable to find translation for language {}",
|
|
||||||
code
|
|
||||||
);
|
|
||||||
|
|
||||||
Tr::new("en")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn t(&self, sentence: Sentence) -> String {
|
pub fn t(&self, sentence: Sentence) -> String {
|
||||||
match self.lang.translation.get(&sentence) {
|
//&'static str {
|
||||||
Some(str) => str.clone(),
|
self.lang.get(sentence).to_string()
|
||||||
None => format!(
|
// match self.lang.translation.get(&sentence) {
|
||||||
"Translation missing, lang: {}/{}, element: {:?}",
|
// Some(str) => str.clone(),
|
||||||
self.lang.name, self.lang.code, sentence
|
// 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 {
|
pub fn tp(&self, sentence: Sentence, params: &[Box<dyn ToString + Send>]) -> String {
|
||||||
match self.lang.translation.get(&sentence) {
|
// match self.lang.translation.get(&sentence) {
|
||||||
Some(str) => {
|
// Some(str) => {
|
||||||
let mut result = str.clone();
|
// 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 {
|
for p in params {
|
||||||
result = result.replacen("{}", &p.to_string(), 1);
|
result = result.replacen("{}", &p.to_string(), 1);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
None => format!(
|
|
||||||
"Translation missing, lang: {}/{}, element: {:?}",
|
|
||||||
self.lang.name, self.lang.code, sentence
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current_lang_code(&self) -> &str {
|
pub fn current_lang_code(&self) -> &str {
|
||||||
&self.lang.code
|
&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]
|
// #[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 {
|
struct Language {
|
||||||
code: String,
|
code: String,
|
||||||
name: 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>> =
|
static TRANSLATIONS: LazyLock<Vec<Language>> =
|
||||||
LazyLock::new(|| match File::open(consts::TRANSLATION_FILE) {
|
LazyLock::new(|| match File::open(consts::TRANSLATION_FILE) {
|
||||||
Ok(file) => from_reader(file).unwrap_or_else(|error| {
|
Ok(file) => {
|
||||||
|
let stored_languages: Vec<StoredLanguage> = from_reader(file).unwrap_or_else(|error| {
|
||||||
|
{
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to read translation file {}: {}",
|
"Failed to read translation file {}: {}",
|
||||||
consts::TRANSLATION_FILE,
|
consts::TRANSLATION_FILE,
|
||||||
error
|
error
|
||||||
)
|
)
|
||||||
}),
|
}
|
||||||
|
});
|
||||||
|
stored_languages
|
||||||
|
.into_iter()
|
||||||
|
.map(Language::from_stored_language)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to open translation file {}: {}",
|
"Failed to open translation file {}: {}",
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
{% endmatch %}
|
{% endmatch %}
|
||||||
|
|
||||||
<select id="select-website-language">
|
<select id="select-website-language">
|
||||||
{% for lang in Tr::available_languages() %}
|
{% for lang in translation::available_languages() %}
|
||||||
<option value="{{ lang.0 }}"
|
<option value="{{ lang.0 }}"
|
||||||
{%+ if tr.current_lang_code() == lang.0 %}
|
{%+ if tr.current_lang_code() == lang.0 %}
|
||||||
selected
|
selected
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<label for="select-language">{{ tr.t(Sentence::RecipeLanguage) }}</label>
|
<label for="select-language">{{ tr.t(Sentence::RecipeLanguage) }}</label>
|
||||||
<select id="select-language">
|
<select id="select-language">
|
||||||
{% for lang in Tr::available_languages() %}
|
{% for lang in translation::available_languages() %}
|
||||||
<option value="{{ lang.0 }}"
|
<option value="{{ lang.0 }}"
|
||||||
{%+ if recipe.lang == lang.0 %}
|
{%+ if recipe.lang == lang.0 %}
|
||||||
selected
|
selected
|
||||||
|
|
@ -86,6 +86,8 @@
|
||||||
|
|
||||||
<div id="hidden-templates">
|
<div id="hidden-templates">
|
||||||
<div class="group">
|
<div class="group">
|
||||||
|
<div class="drag-handle"></div>
|
||||||
|
|
||||||
<label for="input-group-name">{{ tr.t(Sentence::RecipeGroupName) }}</label>
|
<label for="input-group-name">{{ tr.t(Sentence::RecipeGroupName) }}</label>
|
||||||
<input class="input-group-name" type="text" />
|
<input class="input-group-name" type="text" />
|
||||||
|
|
||||||
|
|
@ -100,6 +102,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="step">
|
<div class="step">
|
||||||
|
<div class="drag-handle"></div>
|
||||||
|
|
||||||
<label for="text-area-step-action">{{ tr.t(Sentence::RecipeStepAction) }}</label>
|
<label for="text-area-step-action">{{ tr.t(Sentence::RecipeStepAction) }}</label>
|
||||||
<textarea class="text-area-step-action"></textarea>
|
<textarea class="text-area-step-action"></textarea>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,193 +2,193 @@
|
||||||
(
|
(
|
||||||
code: "en",
|
code: "en",
|
||||||
name: "English",
|
name: "English",
|
||||||
translation: {
|
translation: [
|
||||||
MainTitle: "Cooking Recipes",
|
(MainTitle, "Cooking Recipes"),
|
||||||
CreateNewRecipe: "Create a new recipe",
|
(CreateNewRecipe, "Create a new recipe"),
|
||||||
UnpublishedRecipes: "Unpublished recipes",
|
(UnpublishedRecipes, "Unpublished recipes"),
|
||||||
UntitledRecipe: "Untitled recipe",
|
(UntitledRecipe, "Untitled recipe"),
|
||||||
|
|
||||||
Name: "Name",
|
(Name, "Name"),
|
||||||
EmailAddress: "Email address",
|
(EmailAddress, "Email address"),
|
||||||
Password: "Password",
|
(Password, "Password"),
|
||||||
|
|
||||||
SignOut: "Sign out",
|
(SignOut, "Sign out"),
|
||||||
Save: "Save",
|
(Save, "Save"),
|
||||||
NotLoggedIn: "No logged in",
|
(NotLoggedIn, "No logged in"),
|
||||||
|
|
||||||
DatabaseError: "Database error",
|
(DatabaseError, "Database error"),
|
||||||
|
|
||||||
SignInMenu: "Sign in",
|
(SignInMenu, "Sign in"),
|
||||||
SignInTitle: "Sign in",
|
(SignInTitle, "Sign in"),
|
||||||
SignInButton: "Sign in",
|
(SignInButton, "Sign in"),
|
||||||
WrongEmailOrPassword: "Wrong email or password",
|
(WrongEmailOrPassword, "Wrong email or password"),
|
||||||
AccountMustBeValidatedFirst: "This account must be validated first",
|
(AccountMustBeValidatedFirst, "This account must be validated first"),
|
||||||
InvalidEmail: "Invalid email",
|
(InvalidEmail, "Invalid email"),
|
||||||
PasswordDontMatch: "Passwords don't match",
|
(PasswordDontMatch, "Passwords don't match"),
|
||||||
InvalidPassword: "Password must have at least {} characters",
|
(InvalidPassword, "Password must have at least {} characters"),
|
||||||
EmailAlreadyTaken: "This email is not available",
|
(EmailAlreadyTaken, "This email is not available"),
|
||||||
UnableToSendEmail: "Unable to send the validation email",
|
(UnableToSendEmail, "Unable to send the validation email"),
|
||||||
|
|
||||||
ValidationSuccessful: "Email validation successful",
|
(ValidationSuccessful, "Email validation successful"),
|
||||||
ValidationExpired: "The validation has expired. Try to sign up again with the same email",
|
(ValidationExpired, "The validation has expired. Try to sign up again with the same email"),
|
||||||
ValidationErrorTryToSignUpAgain: "Validation error. Try to sign up again with the same email",
|
(ValidationErrorTryToSignUpAgain, "Validation error. Try to sign up again with the same email"),
|
||||||
ValidationError: "Validation error",
|
(ValidationError, "Validation error"),
|
||||||
ValidationUserAlreadyExists: "User already exists",
|
(ValidationUserAlreadyExists, "User already exists"),
|
||||||
|
|
||||||
SignUpMenu: "Sign up",
|
(SignUpMenu, "Sign up"),
|
||||||
SignUpTitle: "Sign up",
|
(SignUpTitle, "Sign up"),
|
||||||
SignUpButton: "Sign up",
|
(SignUpButton, "Sign up"),
|
||||||
SignUpEmailSent: "An email has been sent, follow the link to validate your account",
|
(SignUpEmailSent, "An email has been sent), follow the link to validate your account"),
|
||||||
SignUpFollowEmailLink: "Follow this link to confirm your inscription: {}",
|
(SignUpFollowEmailLink, "Follow this link to confirm your inscription, {}"),
|
||||||
SignUpEmailValidationSuccess: "Email validation successful, your account has been created",
|
(SignUpEmailValidationSuccess, "Email validation successful), your account has been created"),
|
||||||
SignUpValidationExpired: "The validation has expired. Try to sign up again",
|
(SignUpValidationExpired, "The validation has expired. Try to sign up again"),
|
||||||
SignUpValidationErrorTryAgain: "Validation error. Try to sign up again",
|
(SignUpValidationErrorTryAgain, "Validation error. Try to sign up again"),
|
||||||
ChooseAPassword: "Choose a password (minimum {} characters)",
|
(ChooseAPassword, "Choose a password (minimum {} characters)"),
|
||||||
ReEnterPassword: "Re-enter password",
|
(ReEnterPassword, "Re-enter password"),
|
||||||
|
|
||||||
LostPassword: "Lost password",
|
(LostPassword, "Lost password"),
|
||||||
AskResetButton: "Ask reset",
|
(AskResetButton, "Ask reset"),
|
||||||
AskResetAlreadyLoggedInError: "Can't ask to reset password when already logged in",
|
(AskResetAlreadyLoggedInError, "Can't ask to reset password when already logged in"),
|
||||||
AskResetEmailAlreadyResetError: "The password has already been reset for this email",
|
(AskResetEmailAlreadyResetError, "The password has already been reset for this email"),
|
||||||
AskResetFollowEmailLink: "Follow this link to reset your password: {}",
|
(AskResetFollowEmailLink, "Follow this link to reset your password, {}"),
|
||||||
AskResetEmailSent: "An email has been sent, follow the link to reset your password",
|
(AskResetEmailSent, "An email has been sent), follow the link to reset your password"),
|
||||||
AskResetTokenMissing: "Reset token missing",
|
(AskResetTokenMissing, "Reset token missing"),
|
||||||
AskResetTokenExpired: "Token expired, try to reset password again",
|
(AskResetTokenExpired, "Token expired), try to reset password again"),
|
||||||
PasswordReset: "Your password has been reset",
|
(PasswordReset, "Your password has been reset"),
|
||||||
EmailUnknown: "Email unknown",
|
(EmailUnknown, "Email unknown"),
|
||||||
UnableToSendResetEmail: "Unable to send the reset password email",
|
(UnableToSendResetEmail, "Unable to send the reset password email"),
|
||||||
|
|
||||||
ProfileTitle: "Profile",
|
(ProfileTitle, "Profile"),
|
||||||
ProfileEmail: "Email (need to be revalidated if changed)",
|
(ProfileEmail, "Email (need to be revalidated if changed)"),
|
||||||
ProfileNewPassword: "New password (minimum {} characters)",
|
(ProfileNewPassword, "New password (minimum {} characters)"),
|
||||||
ProfileFollowEmailLink: "Follow this link to validate this email address: {}",
|
(ProfileFollowEmailLink, "Follow this link to validate this email address, {}"),
|
||||||
ProfileEmailSent: "An email has been sent, follow the link to validate your new email",
|
(ProfileEmailSent, "An email has been sent), follow the link to validate your new email"),
|
||||||
ProfileSaved: "Profile saved",
|
(ProfileSaved, "Profile saved"),
|
||||||
|
|
||||||
RecipeNotAllowedToEdit: "Not allowed to edit this recipe",
|
(RecipeNotAllowedToEdit, "Not allowed to edit this recipe"),
|
||||||
RecipeNotAllowedToView: "Not allowed the view the recipe {}",
|
(RecipeNotAllowedToView, "Not allowed the view the recipe {}"),
|
||||||
RecipeNotFound: "Recipe not found",
|
(RecipeNotFound, "Recipe not found"),
|
||||||
RecipeTitle : "Title",
|
(RecipeTitle, "Title"),
|
||||||
RecipeDescription : "Description",
|
(RecipeDescription, "Description"),
|
||||||
RecipeServings : "Servings",
|
(RecipeServings, "Servings"),
|
||||||
RecipeEstimatedTime : "Estimated time [min]",
|
(RecipeEstimatedTime, "Estimated time [min]"),
|
||||||
RecipeDifficulty : "Difficulty",
|
(RecipeDifficulty, "Difficulty"),
|
||||||
RecipeDifficultyEasy : "Easy",
|
(RecipeDifficultyEasy, "Easy"),
|
||||||
RecipeDifficultyMedium : "Medium",
|
(RecipeDifficultyMedium, "Medium"),
|
||||||
RecipeDifficultyHard : "Hard",
|
(RecipeDifficultyHard, "Hard"),
|
||||||
RecipeTags : "Tags",
|
(RecipeTags, "Tags"),
|
||||||
RecipeLanguage : "Language",
|
(RecipeLanguage, "Language"),
|
||||||
RecipeIsPublished : "Is published",
|
(RecipeIsPublished, "Is published"),
|
||||||
RecipeDelete : "Delete recipe",
|
(RecipeDelete, "Delete recipe"),
|
||||||
RecipeAddAGroup : "Add a group",
|
(RecipeAddAGroup, "Add a group"),
|
||||||
RecipeRemoveGroup : "Remove group",
|
(RecipeRemoveGroup, "Remove group"),
|
||||||
RecipeGroupName : "Name",
|
(RecipeGroupName, "Name"),
|
||||||
RecipeGroupComment : "Comment",
|
(RecipeGroupComment, "Comment"),
|
||||||
RecipeAddAStep : "Add a step",
|
(RecipeAddAStep, "Add a step"),
|
||||||
RecipeRemoveStep : "Remove step",
|
(RecipeRemoveStep, "Remove step"),
|
||||||
RecipeStepAction : "Action",
|
(RecipeStepAction, "Action"),
|
||||||
RecipeAddAnIngredient : "Add an ingredient",
|
(RecipeAddAnIngredient, "Add an ingredient"),
|
||||||
RecipeRemoveIngredient : "Remove ingredient",
|
(RecipeRemoveIngredient, "Remove ingredient"),
|
||||||
RecipeIngredientName : "Name",
|
(RecipeIngredientName, "Name"),
|
||||||
RecipeIngredientQuantity : "Quantity",
|
(RecipeIngredientQuantity, "Quantity"),
|
||||||
RecipeIngredientUnit : "Unit",
|
(RecipeIngredientUnit, "Unit"),
|
||||||
RecipeIngredientComment : "Comment",
|
(RecipeIngredientComment, "Comment"),
|
||||||
}
|
]
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
code: "fr",
|
code: "fr",
|
||||||
name: "Français",
|
name: "Français",
|
||||||
translation: {
|
translation: [
|
||||||
MainTitle: "Recettes de Cuisine",
|
(MainTitle, "Recettes de Cuisine"),
|
||||||
CreateNewRecipe: "Créer une nouvelle recette",
|
(CreateNewRecipe, "Créer une nouvelle recette"),
|
||||||
UnpublishedRecipes: "Recettes non-publiés",
|
(UnpublishedRecipes, "Recettes non-publiés"),
|
||||||
UntitledRecipe: "Recette sans nom",
|
(UntitledRecipe, "Recette sans nom"),
|
||||||
|
|
||||||
Name: "Nom",
|
(Name, "Nom"),
|
||||||
EmailAddress: "Adresse email",
|
(EmailAddress, "Adresse email"),
|
||||||
Password: "Mot de passe",
|
(Password, "Mot de passe"),
|
||||||
|
|
||||||
SignOut: "Se déconnecter",
|
(SignOut, "Se déconnecter"),
|
||||||
Save: "Sauvegarder",
|
(Save, "Sauvegarder"),
|
||||||
NotLoggedIn: "Pas connecté",
|
(NotLoggedIn, "Pas connecté"),
|
||||||
|
|
||||||
DatabaseError: "Erreur de la base de données",
|
(DatabaseError, "Erreur de la base de données"),
|
||||||
|
|
||||||
SignInMenu: "Se connecter",
|
(SignInMenu, "Se connecter"),
|
||||||
SignInTitle: "Se connecter",
|
(SignInTitle, "Se connecter"),
|
||||||
SignInButton: "Se connecter",
|
(SignInButton, "Se connecter"),
|
||||||
WrongEmailOrPassword: "Mot de passe ou email invalide",
|
(WrongEmailOrPassword, "Mot de passe ou email invalide"),
|
||||||
AccountMustBeValidatedFirst: "Ce compte doit d'abord être validé",
|
(AccountMustBeValidatedFirst, "Ce compte doit d'abord être validé"),
|
||||||
InvalidEmail: "Adresse email invalide",
|
(InvalidEmail, "Adresse email invalide"),
|
||||||
PasswordDontMatch: "Les mots de passe ne correspondent pas",
|
(PasswordDontMatch, "Les mots de passe ne correspondent pas"),
|
||||||
InvalidPassword: "Le mot de passe doit avoir au moins {} caractères",
|
(InvalidPassword, "Le mot de passe doit avoir au moins {} caractères"),
|
||||||
EmailAlreadyTaken: "Cette adresse email n'est pas disponible",
|
(EmailAlreadyTaken, "Cette adresse email n'est pas disponible"),
|
||||||
UnableToSendEmail: "L'email de validation n'a pas pu être envoyé",
|
(UnableToSendEmail, "L'email de validation n'a pas pu être envoyé"),
|
||||||
|
|
||||||
ValidationSuccessful: "Email validé avec succès",
|
(ValidationSuccessful, "Email validé avec succès"),
|
||||||
ValidationExpired: "La validation a expiré. Essayez de vous inscrire à nouveau avec la même adresse email",
|
(ValidationExpired, "La validation a expiré. Essayez de vous inscrire à nouveau avec la même adresse email"),
|
||||||
ValidationErrorTryToSignUpAgain: "Erreur de validation. Essayez de vous inscrire à nouveau avec la même adresse email",
|
(ValidationErrorTryToSignUpAgain, "Erreur de validation. Essayez de vous inscrire à nouveau avec la même adresse email"),
|
||||||
ValidationError: "Erreur de validation",
|
(ValidationError, "Erreur de validation"),
|
||||||
ValidationUserAlreadyExists: "Utilisateur déjà existant",
|
(ValidationUserAlreadyExists, "Utilisateur déjà existant"),
|
||||||
|
|
||||||
SignUpMenu: "S'inscrire",
|
(SignUpMenu, "S'inscrire"),
|
||||||
SignUpTitle: "Inscription",
|
(SignUpTitle, "Inscription"),
|
||||||
SignUpButton: "Valider",
|
(SignUpButton, "Valider"),
|
||||||
SignUpEmailSent: "Un email a été envoyé, suivez le lien pour valider votre compte",
|
(SignUpEmailSent, "Un email a été envoyé), suivez le lien pour valider votre compte"),
|
||||||
SignUpFollowEmailLink: "Suivez ce lien pour valider votre inscription: {}",
|
(SignUpFollowEmailLink, "Suivez ce lien pour valider votre inscription, {}"),
|
||||||
SignUpEmailValidationSuccess: "La validation de votre email s'est déroulée avec succès, votre compte a été créé",
|
(SignUpEmailValidationSuccess, "La validation de votre email s'est déroulée avec succès), votre compte a été créé"),
|
||||||
SignUpValidationExpired: "La validation a expiré. Essayez de vous inscrire à nouveau",
|
(SignUpValidationExpired, "La validation a expiré. Essayez de vous inscrire à nouveau"),
|
||||||
SignUpValidationErrorTryAgain: "Erreur de validation. Essayez de vous inscrire à nouveau",
|
(SignUpValidationErrorTryAgain, "Erreur de validation. Essayez de vous inscrire à nouveau"),
|
||||||
ChooseAPassword: "Choisir un mot de passe (minimum {} caractères)",
|
(ChooseAPassword, "Choisir un mot de passe (minimum {} caractères)"),
|
||||||
ReEnterPassword: "Entrez à nouveau le mot de passe",
|
(ReEnterPassword, "Entrez à nouveau le mot de passe"),
|
||||||
|
|
||||||
LostPassword: "Mot de passe perdu",
|
(LostPassword, "Mot de passe perdu"),
|
||||||
AskResetButton: "Demander la réinitialisation",
|
(AskResetButton, "Demander la réinitialisation"),
|
||||||
AskResetAlreadyLoggedInError: "Impossible de demander une réinitialisation du mot de passe lorsque déjà connecté",
|
(AskResetAlreadyLoggedInError, "Impossible de demander une réinitialisation du mot de passe lorsque déjà connecté"),
|
||||||
AskResetEmailAlreadyResetError: "Le mot de passe a déjà été réinitialisé pour cette adresse email",
|
(AskResetEmailAlreadyResetError, "Le mot de passe a déjà été réinitialisé pour cette adresse email"),
|
||||||
AskResetFollowEmailLink: "Suivez ce lien pour réinitialiser votre mot de passe: {}",
|
(AskResetFollowEmailLink, "Suivez ce lien pour réinitialiser votre mot de passe, {}"),
|
||||||
AskResetEmailSent: "Un email a été envoyé, suivez le lien pour réinitialiser votre mot de passe",
|
(AskResetEmailSent, "Un email a été envoyé), suivez le lien pour réinitialiser votre mot de passe"),
|
||||||
AskResetTokenMissing: "Jeton de réinitialisation manquant",
|
(AskResetTokenMissing, "Jeton de réinitialisation manquant"),
|
||||||
AskResetTokenExpired: "Jeton expiré, essayez de réinitialiser votre mot de passe à nouveau",
|
(AskResetTokenExpired, "Jeton expiré), essayez de réinitialiser votre mot de passe à nouveau"),
|
||||||
PasswordReset: "Votre mot de passe a été réinitialisé",
|
(PasswordReset, "Votre mot de passe a été réinitialisé"),
|
||||||
EmailUnknown: "Email inconnu",
|
(EmailUnknown, "Email inconnu"),
|
||||||
UnableToSendResetEmail: "Impossible d'envoyer l'email pour la réinitialisation du mot de passe",
|
(UnableToSendResetEmail, "Impossible d'envoyer l'email pour la réinitialisation du mot de passe"),
|
||||||
|
|
||||||
ProfileTitle: "Profile",
|
(ProfileTitle, "Profile"),
|
||||||
ProfileEmail: "Email (doit être revalidé si changé)",
|
(ProfileEmail, "Email (doit être revalidé si changé)"),
|
||||||
ProfileNewPassword: "Nouveau mot de passe (minimum {} caractères)",
|
(ProfileNewPassword, "Nouveau mot de passe (minimum {} caractères)"),
|
||||||
ProfileFollowEmailLink: "Suivez ce lien pour valider l'adresse email: {}",
|
(ProfileFollowEmailLink, "Suivez ce lien pour valider l'adresse email, {}"),
|
||||||
ProfileEmailSent: "Un email a été envoyé, suivez le lien pour valider la nouvelle adresse email",
|
(ProfileEmailSent, "Un email a été envoyé), suivez le lien pour valider la nouvelle adresse email"),
|
||||||
ProfileSaved: "Profile sauvegardé",
|
(ProfileSaved, "Profile sauvegardé"),
|
||||||
|
|
||||||
RecipeNotAllowedToEdit: "Vous n'êtes pas autorisé à éditer cette recette",
|
(RecipeNotAllowedToEdit, "Vous n'êtes pas autorisé à éditer cette recette"),
|
||||||
RecipeNotAllowedToView: "Vous n'êtes pas autorisé à voir la recette {}",
|
(RecipeNotAllowedToView, "Vous n'êtes pas autorisé à voir la recette {}"),
|
||||||
RecipeNotFound: "Recette non-trouvée",
|
(RecipeNotFound, "Recette non-trouvée"),
|
||||||
RecipeTitle : "Titre",
|
(RecipeTitle, "Titre"),
|
||||||
RecipeDescription : "Description",
|
(RecipeDescription, "Description"),
|
||||||
RecipeServings : "Nombre de personnes",
|
(RecipeServings, "Nombre de personnes"),
|
||||||
RecipeEstimatedTime : "Temps estimé",
|
(RecipeEstimatedTime, "Temps estimé"),
|
||||||
RecipeDifficulty : "Difficulté",
|
(RecipeDifficulty, "Difficulté"),
|
||||||
RecipeDifficultyEasy : "Facile",
|
(RecipeDifficultyEasy, "Facile"),
|
||||||
RecipeDifficultyMedium : "Moyen",
|
(RecipeDifficultyMedium, "Moyen"),
|
||||||
RecipeDifficultyHard : "Difficile",
|
(RecipeDifficultyHard, "Difficile"),
|
||||||
RecipeTags : "Tags",
|
(RecipeTags, "Tags"),
|
||||||
RecipeLanguage : "Langue",
|
(RecipeLanguage, "Langue"),
|
||||||
RecipeIsPublished : "Est publié",
|
(RecipeIsPublished, "Est publié"),
|
||||||
RecipeDelete : "Supprimer la recette",
|
(RecipeDelete, "Supprimer la recette"),
|
||||||
RecipeAddAGroup : "Ajouter un groupe",
|
(RecipeAddAGroup, "Ajouter un groupe"),
|
||||||
RecipeRemoveGroup : "Supprimer le groupe",
|
(RecipeRemoveGroup, "Supprimer le groupe"),
|
||||||
RecipeGroupName : "Nom",
|
(RecipeGroupName, "Nom"),
|
||||||
RecipeGroupComment : "Commentaire",
|
(RecipeGroupComment, "Commentaire"),
|
||||||
RecipeAddAStep : "Ajouter une étape",
|
(RecipeAddAStep, "Ajouter une étape"),
|
||||||
RecipeRemoveStep : "Supprimer l'étape",
|
(RecipeRemoveStep, "Supprimer l'étape"),
|
||||||
RecipeStepAction : "Action",
|
(RecipeStepAction, "Action"),
|
||||||
RecipeAddAnIngredient : "Ajouter un ingrédient",
|
(RecipeAddAnIngredient, "Ajouter un ingrédient"),
|
||||||
RecipeRemoveIngredient : "Supprimer l'ingrédient",
|
(RecipeRemoveIngredient, "Supprimer l'ingrédient"),
|
||||||
RecipeIngredientName : "Nom",
|
(RecipeIngredientName, "Nom"),
|
||||||
RecipeIngredientQuantity : "Quantité",
|
(RecipeIngredientQuantity, "Quantité"),
|
||||||
RecipeIngredientUnit : "Unité",
|
(RecipeIngredientUnit, "Unité"),
|
||||||
RecipeIngredientComment : "Commentaire",
|
(RecipeIngredientComment, "Commentaire"),
|
||||||
}
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
Loading…
Add table
Add a link
Reference in a new issue