Split translation file in multiple files: one per language

This commit is contained in:
Greg Burri 2025-05-05 23:58:09 +02:00
parent ec36391ec8
commit 6010c8600f
6 changed files with 339 additions and 331 deletions

View file

@ -9,7 +9,7 @@ pub const FILE_CONF: &str = "conf.ron";
/// The name of the translation file.
/// it's located in the current directory.
pub const TRANSLATION_FILE: &str = "translation.ron";
pub const TRANSLATION_DIR: &str = "translations";
/// Filename of the database.
/// It's located in the `DB_DIRECTORY` directory.

View file

@ -1,4 +1,4 @@
use std::{borrow::Borrow, fs::File, sync::LazyLock};
use std::{borrow::Borrow, fs::File, path::Path, sync::LazyLock};
use chrono::Weekday;
pub use common::translation::Sentence;
@ -6,7 +6,7 @@ use common::utils;
use ron::de::from_reader;
use serde::Deserialize;
use strum::EnumCount;
use tracing::warn;
use tracing::{error, warn};
use crate::consts;
@ -215,28 +215,38 @@ fn get_language_translation(code: &str) -> &'static Language {
}
}
/// Lazy load all translation files.
static TRANSLATIONS: LazyLock<Vec<Language>> =
LazyLock::new(|| match File::open(consts::TRANSLATION_FILE) {
Ok(file) => {
let stored_languages: Vec<StoredLanguage> = from_reader(file).unwrap_or_else(|error| {
{
panic!(
"Failed to read translation file {}: {}",
consts::TRANSLATION_FILE,
error
)
LazyLock::new(|| match Path::new(consts::TRANSLATION_DIR).read_dir() {
Ok(entries) => entries
.filter_map(|entry| {
match entry {
Ok(entry) => match File::open(entry.path()) {
Ok(file) => match from_reader(file) {
Ok(language) => return Some(Language::from_stored_language(language)),
Err(error) => {
error!(
"Error when reading translation file {}: {}",
entry.path().to_str().unwrap_or_default(),
error
)
}
},
Err(error) => {
error!("Unable to read translation file: {}", error);
}
},
Err(error) => error!("Unable to read translation entry: {}", error),
}
});
stored_languages
.into_iter()
.map(Language::from_stored_language)
.collect()
}
None
})
.collect(),
Err(error) => {
panic!(
"Failed to open translation file {}: {}",
consts::TRANSLATION_FILE,
error!(
"Unable to read translations directory {}: {}",
consts::TRANSLATION_DIR,
error
)
);
vec![]
}
});

View file

@ -1,308 +0,0 @@
[
(
code: "en",
territory: "US",
name: "English",
translation: [
(MainTitle, "Cooking Recipes"),
(CreateNewRecipe, "Create a new recipe"),
(PrivateRecipes, "Private recipes"),
(UntitledRecipe, "Untitled recipe"),
(Name, "Name"),
(EmailAddress, "Email address"),
(Password, "Password"),
(SignOut, "Sign out"),
(Save, "Save"),
(NotLoggedIn, "No logged in"),
(ActionNotAuthorized, "Action not authorized"),
(DatabaseError, "Database error"),
(TemplateError, "Template error"),
(SignInMenu, "Sign in"),
(SignInTitle, "Sign in"),
(SignInButton, "Sign in"),
(SignInSuccess, "Sign in successful"),
(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"),
(SignUpEmailTitle, "Cooking Recipes: Account validation"),
(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"),
(SignUpClosed, "New registration are closed"),
(ChooseAPassword, "Choose a password (minimum {} characters)"),
(ReEnterPassword, "Re-enter password"),
(LostPassword, "Lost password"),
(AskResetChooseNewPassword, "Choose a new password (minimum {} characters)"),
(AskResetButton, "Ask reset"),
(AskResetAlreadyLoggedInError, "Can't ask to reset password when already logged in"),
(AskResetEmailAlreadyResetError, "The password has already been reset for this email"),
(AskResetEmailTitle, "Cooking Recipes: Reset password"),
(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)"),
(ProfileDefaultServings, "Default servings"),
(ProfileFirstDayOfWeek, "First day of the week"),
(ProfileNewPassword, "New password (minimum {} characters)"),
(ProfileFollowEmailTitle, "Cooking Recipes: Email validation"),
(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"),
(RecipeIsPublic, "Is public"),
(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"),
(RecipeDeleteConfirmation, "Are you sure to delete the recipe: '{}'?"),
(RecipeSuccessfullyDeleted, "Recipe successfully deleted"),
(RecipeGroupDeleteConfirmation, "Are you sure to delete the group: '{}'?"),
(RecipeStepDeleteConfirmation, "Are you sure to delete the step: '{}'?"),
(RecipeIngredientDeleteConfirmation, "Are you sure to delete the ingredient: '{}'?"),
(RecipeOneServing, "1 serving"),
(RecipeSomeServings, "{} servings"),
(RecipeEstimatedTimeMinAbbreviation, "min"),
(CalendarMonday, "Monday"),
(CalendarTuesday, "Tuesday"),
(CalendarWednesday, "Wednesday"),
(CalendarThursday, "Thursday"),
(CalendarFriday, "Friday"),
(CalendarSaturday, "Saturday"),
(CalendarSunday, "Sunday"),
(CalendarMondayAbbreviation, "Mon"),
(CalendarTuesdayAbbreviation, "Tue"),
(CalendarWednesdayAbbreviation, "Wed"),
(CalendarThursdayAbbreviation, "Thu"),
(CalendarFridayAbbreviation, "Fri"),
(CalendarSaturdayAbbreviation, "Sat"),
(CalendarSundayAbbreviation, "Sun"),
(CalendarJanuary, "January"),
(CalendarFebruary, "February"),
(CalendarMarch, "March"),
(CalendarApril, "April"),
(CalendarMay, "May"),
(CalendarJune, "June"),
(CalendarJuly, "July"),
(CalendarAugust, "August"),
(CalendarSeptember, "September"),
(CalendarOctober, "October"),
(CalendarNovember, "November"),
(CalendarDecember, "December"),
(CalendarAddToPlanner, "Add to planner"),
(CalendarAddToPlannerSuccess, "Recipe {title} has been scheduled for {date}"),
(CalendarAddToPlannerAlreadyExists, "Recipe {title} has already been scheduled for {date}"),
(CalendarDateFormat, "%A, %-d %B, %C%y"), // See https://docs.rs/chrono/latest/chrono/format/strftime/index.html.
(CalendarAddIngredientsToShoppingList, "Add ingredients to shopping list"),
(CalendarRemoveIngredientsFromShoppingList, "Remove ingredients from shopping list"),
(CalendarUnschedule, "Remove"),
(CalendarUnscheduleConfirmation, "Are you sure to remove {title} on {date}"),
]
),
(
code: "fr",
territory: "FR",
name: "Français",
translation: [
(MainTitle, "Recettes de Cuisine"),
(CreateNewRecipe, "Créer une nouvelle recette"),
(PrivateRecipes, "Recettes privées"),
(UntitledRecipe, "Recette sans nom"),
(Name, "Nom"),
(EmailAddress, "Adresse email"),
(Password, "Mot de passe"),
(SignOut, "Se déconnecter"),
(Save, "Sauvegarder"),
(NotLoggedIn, "Pas connecté"),
(ActionNotAuthorized, "Action non autorisée"),
(DatabaseError, "Erreur de la base de données (Database error)"),
(TemplateError, "Erreur du moteur de modèles (Template error)"),
(SignInMenu, "Se connecter"),
(SignInTitle, "Se connecter"),
(SignInButton, "Se connecter"),
(SignInSuccess, "Connexion réussie"),
(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"),
(SignUpEmailTitle, "Recettes de Cuisine: Validation du 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"),
(SignUpClosed, "Les inscriptions sont actuellement fermées"),
(ChooseAPassword, "Choisir un mot de passe (minimum {} caractères)"),
(ReEnterPassword, "Entrez à nouveau le mot de passe"),
(LostPassword, "Mot de passe perdu"),
(AskResetChooseNewPassword, "Choisir un nouveau mot de passe (minimum {} caractères)"),
(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"),
(AskResetEmailTitle, "Recettes de Cuisine: Réinitialisation du 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"),
(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é)"),
(ProfileDefaultServings, "Nombre de portions par défaut"),
(ProfileFirstDayOfWeek, "Premier jour de la semaine"),
(ProfileNewPassword, "Nouveau mot de passe (minimum {} caractères)"),
(ProfileFollowEmailTitle, "Recettes de Cuisine: Validation de 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"),
(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 portions"),
(RecipeEstimatedTime, "Temps estimé"),
(RecipeDifficulty, "Difficulté"),
(RecipeDifficultyEasy, "Facile"),
(RecipeDifficultyMedium, "Moyen"),
(RecipeDifficultyHard, "Difficile"),
(RecipeTags, "Tags"),
(RecipeLanguage, "Langue"),
(RecipeIsPublic, "Est public"),
(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"),
(RecipeDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer la recette : '{}' ?"),
(RecipeSuccessfullyDeleted, "Recette supprimée avec succès"),
(RecipeGroupDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer le groupe : '{}' ?"),
(RecipeStepDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer l'étape : '{}' ?"),
(RecipeIngredientDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer 'ingrédient : '{}' ?"),
(RecipeOneServing, "pour 1 personne"),
(RecipeSomeServings, "pour {} personnes"),
(RecipeEstimatedTimeMinAbbreviation, "min"),
(CalendarMonday, "lundi"),
(CalendarTuesday, "mardi"),
(CalendarWednesday, "mercredi"),
(CalendarThursday, "jeudi"),
(CalendarFriday, "vendredi"),
(CalendarSaturday, "samedi"),
(CalendarSunday, "dimanche"),
(CalendarMondayAbbreviation, "Lun"),
(CalendarTuesdayAbbreviation, "Mar"),
(CalendarWednesdayAbbreviation, "Mer"),
(CalendarThursdayAbbreviation, "Jeu"),
(CalendarFridayAbbreviation, "Ven"),
(CalendarSaturdayAbbreviation, "Sam"),
(CalendarSundayAbbreviation, "Dim"),
(CalendarJanuary, "janvier"),
(CalendarFebruary, "février"),
(CalendarMarch, "mars"),
(CalendarApril, "avril"),
(CalendarMay, "mai"),
(CalendarJune, "juin"),
(CalendarJuly, "juillet"),
(CalendarAugust, "août"),
(CalendarSeptember, "septembre"),
(CalendarOctober, "octobre"),
(CalendarNovember, "novembre"),
(CalendarDecember, "décembre"),
(CalendarAddToPlanner, "Ajouter au planificateur"),
(CalendarAddToPlannerSuccess, "La recette {title} a été agendée pour le {date}"),
(CalendarAddToPlannerAlreadyExists, "La recette {title} a été déjà été agendée pour le {date}"),
(CalendarDateFormat, "%A %-d %B %C%y"), // See https://docs.rs/chrono/latest/chrono/format/strftime/index.html.
(CalendarAddIngredientsToShoppingList, "Ajouter les ingrédients à la liste de course"),
(CalendarRemoveIngredientsFromShoppingList, "Enlever les ingrédients de la liste de course"),
(CalendarUnschedule, "Enlever"),
(CalendarUnscheduleConfirmation, "Êtes-vous sûr de vouloir enlever {title} du {date}"),
]
)
]

View file

@ -0,0 +1,153 @@
(
code: "en",
territory: "US",
name: "English",
translation: [
(MainTitle, "Cooking Recipes"),
(CreateNewRecipe, "Create a new recipe"),
(PrivateRecipes, "Private recipes"),
(UntitledRecipe, "Untitled recipe"),
(Name, "Name"),
(EmailAddress, "Email address"),
(Password, "Password"),
(SignOut, "Sign out"),
(Save, "Save"),
(NotLoggedIn, "No logged in"),
(ActionNotAuthorized, "Action not authorized"),
(DatabaseError, "Database error"),
(TemplateError, "Template error"),
(SignInMenu, "Sign in"),
(SignInTitle, "Sign in"),
(SignInButton, "Sign in"),
(SignInSuccess, "Sign in successful"),
(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"),
(SignUpEmailTitle, "Cooking Recipes: Account validation"),
(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"),
(SignUpClosed, "New registration are closed"),
(ChooseAPassword, "Choose a password (minimum {} characters)"),
(ReEnterPassword, "Re-enter password"),
(LostPassword, "Lost password"),
(AskResetChooseNewPassword, "Choose a new password (minimum {} characters)"),
(AskResetButton, "Ask reset"),
(AskResetAlreadyLoggedInError, "Can't ask to reset password when already logged in"),
(AskResetEmailAlreadyResetError, "The password has already been reset for this email"),
(AskResetEmailTitle, "Cooking Recipes: Reset password"),
(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)"),
(ProfileDefaultServings, "Default servings"),
(ProfileFirstDayOfWeek, "First day of the week"),
(ProfileNewPassword, "New password (minimum {} characters)"),
(ProfileFollowEmailTitle, "Cooking Recipes: Email validation"),
(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"),
(RecipeIsPublic, "Is public"),
(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"),
(RecipeDeleteConfirmation, "Are you sure to delete the recipe: '{}'?"),
(RecipeSuccessfullyDeleted, "Recipe successfully deleted"),
(RecipeGroupDeleteConfirmation, "Are you sure to delete the group: '{}'?"),
(RecipeStepDeleteConfirmation, "Are you sure to delete the step: '{}'?"),
(RecipeIngredientDeleteConfirmation, "Are you sure to delete the ingredient: '{}'?"),
(RecipeOneServing, "1 serving"),
(RecipeSomeServings, "{} servings"),
(RecipeEstimatedTimeMinAbbreviation, "min"),
(CalendarMonday, "Monday"),
(CalendarTuesday, "Tuesday"),
(CalendarWednesday, "Wednesday"),
(CalendarThursday, "Thursday"),
(CalendarFriday, "Friday"),
(CalendarSaturday, "Saturday"),
(CalendarSunday, "Sunday"),
(CalendarMondayAbbreviation, "Mon"),
(CalendarTuesdayAbbreviation, "Tue"),
(CalendarWednesdayAbbreviation, "Wed"),
(CalendarThursdayAbbreviation, "Thu"),
(CalendarFridayAbbreviation, "Fri"),
(CalendarSaturdayAbbreviation, "Sat"),
(CalendarSundayAbbreviation, "Sun"),
(CalendarJanuary, "January"),
(CalendarFebruary, "February"),
(CalendarMarch, "March"),
(CalendarApril, "April"),
(CalendarMay, "May"),
(CalendarJune, "June"),
(CalendarJuly, "July"),
(CalendarAugust, "August"),
(CalendarSeptember, "September"),
(CalendarOctober, "October"),
(CalendarNovember, "November"),
(CalendarDecember, "December"),
(CalendarAddToPlanner, "Add to planner"),
(CalendarAddToPlannerSuccess, "Recipe {title} has been scheduled for {date}"),
(CalendarAddToPlannerAlreadyExists, "Recipe {title} has already been scheduled for {date}"),
(CalendarDateFormat, "%A, %-d %B, %C%y"), // See https://docs.rs/chrono/latest/chrono/format/strftime/index.html.
(CalendarAddIngredientsToShoppingList, "Add ingredients to shopping list"),
(CalendarRemoveIngredientsFromShoppingList, "Remove ingredients from shopping list"),
(CalendarUnschedule, "Remove"),
(CalendarUnscheduleConfirmation, "Are you sure to remove {title} on {date}"),
]
)

View file

@ -0,0 +1,153 @@
(
code: "fr",
territory: "FR",
name: "Français",
translation: [
(MainTitle, "Recettes de Cuisine"),
(CreateNewRecipe, "Créer une nouvelle recette"),
(PrivateRecipes, "Recettes privées"),
(UntitledRecipe, "Recette sans nom"),
(Name, "Nom"),
(EmailAddress, "Adresse email"),
(Password, "Mot de passe"),
(SignOut, "Se déconnecter"),
(Save, "Sauvegarder"),
(NotLoggedIn, "Pas connecté"),
(ActionNotAuthorized, "Action non autorisée"),
(DatabaseError, "Erreur de la base de données (Database error)"),
(TemplateError, "Erreur du moteur de modèles (Template error)"),
(SignInMenu, "Se connecter"),
(SignInTitle, "Se connecter"),
(SignInButton, "Se connecter"),
(SignInSuccess, "Connexion réussie"),
(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"),
(SignUpEmailTitle, "Recettes de Cuisine: Validation du 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"),
(SignUpClosed, "Les inscriptions sont actuellement fermées"),
(ChooseAPassword, "Choisir un mot de passe (minimum {} caractères)"),
(ReEnterPassword, "Entrez à nouveau le mot de passe"),
(LostPassword, "Mot de passe perdu"),
(AskResetChooseNewPassword, "Choisir un nouveau mot de passe (minimum {} caractères)"),
(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"),
(AskResetEmailTitle, "Recettes de Cuisine: Réinitialisation du 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"),
(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é)"),
(ProfileDefaultServings, "Nombre de portions par défaut"),
(ProfileFirstDayOfWeek, "Premier jour de la semaine"),
(ProfileNewPassword, "Nouveau mot de passe (minimum {} caractères)"),
(ProfileFollowEmailTitle, "Recettes de Cuisine: Validation de 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"),
(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 portions"),
(RecipeEstimatedTime, "Temps estimé"),
(RecipeDifficulty, "Difficulté"),
(RecipeDifficultyEasy, "Facile"),
(RecipeDifficultyMedium, "Moyen"),
(RecipeDifficultyHard, "Difficile"),
(RecipeTags, "Tags"),
(RecipeLanguage, "Langue"),
(RecipeIsPublic, "Est public"),
(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"),
(RecipeDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer la recette : '{}' ?"),
(RecipeSuccessfullyDeleted, "Recette supprimée avec succès"),
(RecipeGroupDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer le groupe : '{}' ?"),
(RecipeStepDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer l'étape : '{}' ?"),
(RecipeIngredientDeleteConfirmation, "Êtes-vous sûr de vouloir supprimer 'ingrédient : '{}' ?"),
(RecipeOneServing, "pour 1 personne"),
(RecipeSomeServings, "pour {} personnes"),
(RecipeEstimatedTimeMinAbbreviation, "min"),
(CalendarMonday, "lundi"),
(CalendarTuesday, "mardi"),
(CalendarWednesday, "mercredi"),
(CalendarThursday, "jeudi"),
(CalendarFriday, "vendredi"),
(CalendarSaturday, "samedi"),
(CalendarSunday, "dimanche"),
(CalendarMondayAbbreviation, "Lun"),
(CalendarTuesdayAbbreviation, "Mar"),
(CalendarWednesdayAbbreviation, "Mer"),
(CalendarThursdayAbbreviation, "Jeu"),
(CalendarFridayAbbreviation, "Ven"),
(CalendarSaturdayAbbreviation, "Sam"),
(CalendarSundayAbbreviation, "Dim"),
(CalendarJanuary, "janvier"),
(CalendarFebruary, "février"),
(CalendarMarch, "mars"),
(CalendarApril, "avril"),
(CalendarMay, "mai"),
(CalendarJune, "juin"),
(CalendarJuly, "juillet"),
(CalendarAugust, "août"),
(CalendarSeptember, "septembre"),
(CalendarOctober, "octobre"),
(CalendarNovember, "novembre"),
(CalendarDecember, "décembre"),
(CalendarAddToPlanner, "Ajouter au planificateur"),
(CalendarAddToPlannerSuccess, "La recette {title} a été agendée pour le {date}"),
(CalendarAddToPlannerAlreadyExists, "La recette {title} a été déjà été agendée pour le {date}"),
(CalendarDateFormat, "%A %-d %B %C%y"), // See https://docs.rs/chrono/latest/chrono/format/strftime/index.html.
(CalendarAddIngredientsToShoppingList, "Ajouter les ingrédients à la liste de course"),
(CalendarRemoveIngredientsFromShoppingList, "Enlever les ingrédients de la liste de course"),
(CalendarUnschedule, "Enlever"),
(CalendarUnscheduleConfirmation, "Êtes-vous sûr de vouloir enlever {title} du {date}"),
]
)

2
do.nu
View file

@ -56,7 +56,7 @@ def "main deploy" [
invoke_ssh [rm -rf recipes/static]
copy_ssh ./backend/static/ $destination
copy_ssh ./backend/sql/ $destination
copy_ssh ./backend/translation.ron $destination
copy_ssh ./backend/translations/ $destination
invoke_ssh [chmod u+x recipes/recipes]
invoke_ssh [sudo systemctl start recipes]
print "Deployment finished"