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,
RecipeIngredientComment,
}
+const DEFAULT_LANGUAGE_CODE: &str = "en";
+
#[derive(Clone)]
pub struct Tr {
lang: &'static Language,
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]
// };
// }
-#[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 {}: {}",
(
code: "en",
name: "English",
- translation: {
- MainTitle: "Cooking Recipes",
- CreateNewRecipe: "Create a new recipe",
- UnpublishedRecipes: "Unpublished recipes",
- UntitledRecipe: "Untitled recipe",
-
- Name: "Name",
- EmailAddress: "Email address",
- Password: "Password",
-
- SignOut: "Sign out",
- Save: "Save",
- NotLoggedIn: "No logged in",
-
- DatabaseError: "Database error",
-
- SignInMenu: "Sign in",
- SignInTitle: "Sign in",
- SignInButton: "Sign in",
- WrongEmailOrPassword: "Wrong email or password",
- AccountMustBeValidatedFirst: "This account must be validated first",
- InvalidEmail: "Invalid email",
- PasswordDontMatch: "Passwords don't match",
- InvalidPassword: "Password must have at least {} characters",
- EmailAlreadyTaken: "This email is not available",
- UnableToSendEmail: "Unable to send the validation email",
-
- ValidationSuccessful: "Email validation successful",
- 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",
- ValidationError: "Validation error",
- ValidationUserAlreadyExists: "User already exists",
-
- SignUpMenu: "Sign up",
- SignUpTitle: "Sign up",
- SignUpButton: "Sign up",
- SignUpEmailSent: "An email has been sent, follow the link to validate your account",
- SignUpFollowEmailLink: "Follow this link to confirm your inscription: {}",
- SignUpEmailValidationSuccess: "Email validation successful, your account has been created",
- SignUpValidationExpired: "The validation has expired. Try to sign up again",
- SignUpValidationErrorTryAgain: "Validation error. Try to sign up again",
- ChooseAPassword: "Choose a password (minimum {} characters)",
- ReEnterPassword: "Re-enter password",
-
- LostPassword: "Lost password",
- AskResetButton: "Ask reset",
- AskResetAlreadyLoggedInError: "Can't ask to reset password when already logged in",
- AskResetEmailAlreadyResetError: "The password has already been reset for this email",
- AskResetFollowEmailLink: "Follow this link to reset your password: {}",
- AskResetEmailSent: "An email has been sent, follow the link to reset your password",
- AskResetTokenMissing: "Reset token missing",
- AskResetTokenExpired: "Token expired, try to reset password again",
- PasswordReset: "Your password has been reset",
- EmailUnknown: "Email unknown",
- UnableToSendResetEmail: "Unable to send the reset password email",
-
- ProfileTitle: "Profile",
- ProfileEmail: "Email (need to be revalidated if changed)",
- ProfileNewPassword: "New password (minimum {} characters)",
- ProfileFollowEmailLink: "Follow this link to validate this email address: {}",
- ProfileEmailSent: "An email has been sent, follow the link to validate your new email",
- ProfileSaved: "Profile saved",
-
- RecipeNotAllowedToEdit: "Not allowed to edit this recipe",
- RecipeNotAllowedToView: "Not allowed the view the recipe {}",
- RecipeNotFound: "Recipe not found",
- RecipeTitle : "Title",
- RecipeDescription : "Description",
- RecipeServings : "Servings",
- RecipeEstimatedTime : "Estimated time [min]",
- RecipeDifficulty : "Difficulty",
- RecipeDifficultyEasy : "Easy",
- RecipeDifficultyMedium : "Medium",
- RecipeDifficultyHard : "Hard",
- RecipeTags : "Tags",
- RecipeLanguage : "Language",
- RecipeIsPublished : "Is published",
- RecipeDelete : "Delete recipe",
- RecipeAddAGroup : "Add a group",
- RecipeRemoveGroup : "Remove group",
- RecipeGroupName : "Name",
- RecipeGroupComment : "Comment",
- RecipeAddAStep : "Add a step",
- RecipeRemoveStep : "Remove step",
- RecipeStepAction : "Action",
- RecipeAddAnIngredient : "Add an ingredient",
- RecipeRemoveIngredient : "Remove ingredient",
- RecipeIngredientName : "Name",
- RecipeIngredientQuantity : "Quantity",
- RecipeIngredientUnit : "Unit",
- RecipeIngredientComment : "Comment",
- }
+ translation: [
+ (MainTitle, "Cooking Recipes"),
+ (CreateNewRecipe, "Create a new recipe"),
+ (UnpublishedRecipes, "Unpublished recipes"),
+ (UntitledRecipe, "Untitled recipe"),
+
+ (Name, "Name"),
+ (EmailAddress, "Email address"),
+ (Password, "Password"),
+
+ (SignOut, "Sign out"),
+ (Save, "Save"),
+ (NotLoggedIn, "No logged in"),
+
+ (DatabaseError, "Database error"),
+
+ (SignInMenu, "Sign in"),
+ (SignInTitle, "Sign in"),
+ (SignInButton, "Sign in"),
+ (WrongEmailOrPassword, "Wrong email or password"),
+ (AccountMustBeValidatedFirst, "This account must be validated first"),
+ (InvalidEmail, "Invalid email"),
+ (PasswordDontMatch, "Passwords don't match"),
+ (InvalidPassword, "Password must have at least {} characters"),
+ (EmailAlreadyTaken, "This email is not available"),
+ (UnableToSendEmail, "Unable to send the validation email"),
+
+ (ValidationSuccessful, "Email validation successful"),
+ (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"),
+ (ValidationError, "Validation error"),
+ (ValidationUserAlreadyExists, "User already exists"),
+
+ (SignUpMenu, "Sign up"),
+ (SignUpTitle, "Sign up"),
+ (SignUpButton, "Sign up"),
+ (SignUpEmailSent, "An email has been sent), follow the link to validate your account"),
+ (SignUpFollowEmailLink, "Follow this link to confirm your inscription, {}"),
+ (SignUpEmailValidationSuccess, "Email validation successful), your account has been created"),
+ (SignUpValidationExpired, "The validation has expired. Try to sign up again"),
+ (SignUpValidationErrorTryAgain, "Validation error. Try to sign up again"),
+ (ChooseAPassword, "Choose a password (minimum {} characters)"),
+ (ReEnterPassword, "Re-enter password"),
+
+ (LostPassword, "Lost password"),
+ (AskResetButton, "Ask reset"),
+ (AskResetAlreadyLoggedInError, "Can't ask to reset password when already logged in"),
+ (AskResetEmailAlreadyResetError, "The password has already been reset for this email"),
+ (AskResetFollowEmailLink, "Follow this link to reset your password, {}"),
+ (AskResetEmailSent, "An email has been sent), follow the link to reset your password"),
+ (AskResetTokenMissing, "Reset token missing"),
+ (AskResetTokenExpired, "Token expired), try to reset password again"),
+ (PasswordReset, "Your password has been reset"),
+ (EmailUnknown, "Email unknown"),
+ (UnableToSendResetEmail, "Unable to send the reset password email"),
+
+ (ProfileTitle, "Profile"),
+ (ProfileEmail, "Email (need to be revalidated if changed)"),
+ (ProfileNewPassword, "New password (minimum {} characters)"),
+ (ProfileFollowEmailLink, "Follow this link to validate this email address, {}"),
+ (ProfileEmailSent, "An email has been sent), follow the link to validate your new email"),
+ (ProfileSaved, "Profile saved"),
+
+ (RecipeNotAllowedToEdit, "Not allowed to edit this recipe"),
+ (RecipeNotAllowedToView, "Not allowed the view the recipe {}"),
+ (RecipeNotFound, "Recipe not found"),
+ (RecipeTitle, "Title"),
+ (RecipeDescription, "Description"),
+ (RecipeServings, "Servings"),
+ (RecipeEstimatedTime, "Estimated time [min]"),
+ (RecipeDifficulty, "Difficulty"),
+ (RecipeDifficultyEasy, "Easy"),
+ (RecipeDifficultyMedium, "Medium"),
+ (RecipeDifficultyHard, "Hard"),
+ (RecipeTags, "Tags"),
+ (RecipeLanguage, "Language"),
+ (RecipeIsPublished, "Is published"),
+ (RecipeDelete, "Delete recipe"),
+ (RecipeAddAGroup, "Add a group"),
+ (RecipeRemoveGroup, "Remove group"),
+ (RecipeGroupName, "Name"),
+ (RecipeGroupComment, "Comment"),
+ (RecipeAddAStep, "Add a step"),
+ (RecipeRemoveStep, "Remove step"),
+ (RecipeStepAction, "Action"),
+ (RecipeAddAnIngredient, "Add an ingredient"),
+ (RecipeRemoveIngredient, "Remove ingredient"),
+ (RecipeIngredientName, "Name"),
+ (RecipeIngredientQuantity, "Quantity"),
+ (RecipeIngredientUnit, "Unit"),
+ (RecipeIngredientComment, "Comment"),
+ ]
),
(
code: "fr",
name: "Français",
- translation: {
- MainTitle: "Recettes de Cuisine",
- CreateNewRecipe: "Créer une nouvelle recette",
- UnpublishedRecipes: "Recettes non-publiés",
- UntitledRecipe: "Recette sans nom",
-
- Name: "Nom",
- EmailAddress: "Adresse email",
- Password: "Mot de passe",
-
- SignOut: "Se déconnecter",
- Save: "Sauvegarder",
- NotLoggedIn: "Pas connecté",
-
- DatabaseError: "Erreur de la base de données",
-
- SignInMenu: "Se connecter",
- SignInTitle: "Se connecter",
- SignInButton: "Se connecter",
- WrongEmailOrPassword: "Mot de passe ou email invalide",
- AccountMustBeValidatedFirst: "Ce compte doit d'abord être validé",
- InvalidEmail: "Adresse email invalide",
- PasswordDontMatch: "Les mots de passe ne correspondent pas",
- InvalidPassword: "Le mot de passe doit avoir au moins {} caractères",
- EmailAlreadyTaken: "Cette adresse email n'est pas disponible",
- UnableToSendEmail: "L'email de validation n'a pas pu être envoyé",
-
- ValidationSuccessful: "Email validé avec succès",
- 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",
- ValidationError: "Erreur de validation",
- ValidationUserAlreadyExists: "Utilisateur déjà existant",
-
- SignUpMenu: "S'inscrire",
- SignUpTitle: "Inscription",
- SignUpButton: "Valider",
- SignUpEmailSent: "Un email a été envoyé, suivez le lien pour valider votre compte",
- 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éé",
- SignUpValidationExpired: "La validation a expiré. Essayez de vous inscrire à nouveau",
- SignUpValidationErrorTryAgain: "Erreur de validation. Essayez de vous inscrire à nouveau",
- ChooseAPassword: "Choisir un mot de passe (minimum {} caractères)",
- ReEnterPassword: "Entrez à nouveau le mot de passe",
-
- LostPassword: "Mot de passe perdu",
- AskResetButton: "Demander la réinitialisation",
- 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",
- 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",
- AskResetTokenMissing: "Jeton de réinitialisation manquant",
- AskResetTokenExpired: "Jeton expiré, essayez de réinitialiser votre mot de passe à nouveau",
- PasswordReset: "Votre mot de passe a été réinitialisé",
- EmailUnknown: "Email inconnu",
- UnableToSendResetEmail: "Impossible d'envoyer l'email pour la réinitialisation du mot de passe",
-
- ProfileTitle: "Profile",
- ProfileEmail: "Email (doit être revalidé si changé)",
- ProfileNewPassword: "Nouveau mot de passe (minimum {} caractères)",
- ProfileFollowEmailLink: "Suivez ce lien pour valider l'adresse email: {}",
- ProfileEmailSent: "Un email a été envoyé, suivez le lien pour valider la nouvelle adresse email",
- ProfileSaved: "Profile sauvegardé",
-
- RecipeNotAllowedToEdit: "Vous n'êtes pas autorisé à éditer cette recette",
- RecipeNotAllowedToView: "Vous n'êtes pas autorisé à voir la recette {}",
- RecipeNotFound: "Recette non-trouvée",
- RecipeTitle : "Titre",
- RecipeDescription : "Description",
- RecipeServings : "Nombre de personnes",
- RecipeEstimatedTime : "Temps estimé",
- RecipeDifficulty : "Difficulté",
- RecipeDifficultyEasy : "Facile",
- RecipeDifficultyMedium : "Moyen",
- RecipeDifficultyHard : "Difficile",
- RecipeTags : "Tags",
- RecipeLanguage : "Langue",
- RecipeIsPublished : "Est publié",
- RecipeDelete : "Supprimer la recette",
- RecipeAddAGroup : "Ajouter un groupe",
- RecipeRemoveGroup : "Supprimer le groupe",
- RecipeGroupName : "Nom",
- RecipeGroupComment : "Commentaire",
- RecipeAddAStep : "Ajouter une étape",
- RecipeRemoveStep : "Supprimer l'étape",
- RecipeStepAction : "Action",
- RecipeAddAnIngredient : "Ajouter un ingrédient",
- RecipeRemoveIngredient : "Supprimer l'ingrédient",
- RecipeIngredientName : "Nom",
- RecipeIngredientQuantity : "Quantité",
- RecipeIngredientUnit : "Unité",
- RecipeIngredientComment : "Commentaire",
- }
+ translation: [
+ (MainTitle, "Recettes de Cuisine"),
+ (CreateNewRecipe, "Créer une nouvelle recette"),
+ (UnpublishedRecipes, "Recettes non-publiés"),
+ (UntitledRecipe, "Recette sans nom"),
+
+ (Name, "Nom"),
+ (EmailAddress, "Adresse email"),
+ (Password, "Mot de passe"),
+
+ (SignOut, "Se déconnecter"),
+ (Save, "Sauvegarder"),
+ (NotLoggedIn, "Pas connecté"),
+
+ (DatabaseError, "Erreur de la base de données"),
+
+ (SignInMenu, "Se connecter"),
+ (SignInTitle, "Se connecter"),
+ (SignInButton, "Se connecter"),
+ (WrongEmailOrPassword, "Mot de passe ou email invalide"),
+ (AccountMustBeValidatedFirst, "Ce compte doit d'abord être validé"),
+ (InvalidEmail, "Adresse email invalide"),
+ (PasswordDontMatch, "Les mots de passe ne correspondent pas"),
+ (InvalidPassword, "Le mot de passe doit avoir au moins {} caractères"),
+ (EmailAlreadyTaken, "Cette adresse email n'est pas disponible"),
+ (UnableToSendEmail, "L'email de validation n'a pas pu être envoyé"),
+
+ (ValidationSuccessful, "Email validé avec succès"),
+ (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"),
+ (ValidationError, "Erreur de validation"),
+ (ValidationUserAlreadyExists, "Utilisateur déjà existant"),
+
+ (SignUpMenu, "S'inscrire"),
+ (SignUpTitle, "Inscription"),
+ (SignUpButton, "Valider"),
+ (SignUpEmailSent, "Un email a été envoyé), suivez le lien pour valider votre compte"),
+ (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éé"),
+ (SignUpValidationExpired, "La validation a expiré. Essayez de vous inscrire à nouveau"),
+ (SignUpValidationErrorTryAgain, "Erreur de validation. Essayez de vous inscrire à nouveau"),
+ (ChooseAPassword, "Choisir un mot de passe (minimum {} caractères)"),
+ (ReEnterPassword, "Entrez à nouveau le mot de passe"),
+
+ (LostPassword, "Mot de passe perdu"),
+ (AskResetButton, "Demander la réinitialisation"),
+ (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"),
+ (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"),
+ (AskResetTokenMissing, "Jeton de réinitialisation manquant"),
+ (AskResetTokenExpired, "Jeton expiré), essayez de réinitialiser votre mot de passe à nouveau"),
+ (PasswordReset, "Votre mot de passe a été réinitialisé"),
+ (EmailUnknown, "Email inconnu"),
+ (UnableToSendResetEmail, "Impossible d'envoyer l'email pour la réinitialisation du mot de passe"),
+
+ (ProfileTitle, "Profile"),
+ (ProfileEmail, "Email (doit être revalidé si changé)"),
+ (ProfileNewPassword, "Nouveau mot de passe (minimum {} caractères)"),
+ (ProfileFollowEmailLink, "Suivez ce lien pour valider l'adresse email, {}"),
+ (ProfileEmailSent, "Un email a été envoyé), suivez le lien pour valider la nouvelle adresse email"),
+ (ProfileSaved, "Profile sauvegardé"),
+
+ (RecipeNotAllowedToEdit, "Vous n'êtes pas autorisé à éditer cette recette"),
+ (RecipeNotAllowedToView, "Vous n'êtes pas autorisé à voir la recette {}"),
+ (RecipeNotFound, "Recette non-trouvée"),
+ (RecipeTitle, "Titre"),
+ (RecipeDescription, "Description"),
+ (RecipeServings, "Nombre de personnes"),
+ (RecipeEstimatedTime, "Temps estimé"),
+ (RecipeDifficulty, "Difficulté"),
+ (RecipeDifficultyEasy, "Facile"),
+ (RecipeDifficultyMedium, "Moyen"),
+ (RecipeDifficultyHard, "Difficile"),
+ (RecipeTags, "Tags"),
+ (RecipeLanguage, "Langue"),
+ (RecipeIsPublished, "Est publié"),
+ (RecipeDelete, "Supprimer la recette"),
+ (RecipeAddAGroup, "Ajouter un groupe"),
+ (RecipeRemoveGroup, "Supprimer le groupe"),
+ (RecipeGroupName, "Nom"),
+ (RecipeGroupComment, "Commentaire"),
+ (RecipeAddAStep, "Ajouter une étape"),
+ (RecipeRemoveStep, "Supprimer l'étape"),
+ (RecipeStepAction, "Action"),
+ (RecipeAddAnIngredient, "Ajouter un ingrédient"),
+ (RecipeRemoveIngredient, "Supprimer l'ingrédient"),
+ (RecipeIngredientName, "Nom"),
+ (RecipeIngredientQuantity, "Quantité"),
+ (RecipeIngredientUnit, "Unité"),
+ (RecipeIngredientComment, "Commentaire"),
+ ]
)
]
\ No newline at end of file