diff --git a/backend/src/consts.rs b/backend/src/consts.rs index ee6f3a0..073de69 100644 --- a/backend/src/consts.rs +++ b/backend/src/consts.rs @@ -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. diff --git a/backend/src/translation.rs b/backend/src/translation.rs index ebb48e3..99db5fa 100644 --- a/backend/src/translation.rs +++ b/backend/src/translation.rs @@ -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> = - LazyLock::new(|| match File::open(consts::TRANSLATION_FILE) { - Ok(file) => { - let stored_languages: Vec = 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![] } }); diff --git a/backend/translation.ron b/backend/translation.ron deleted file mode 100644 index c5210ba..0000000 --- a/backend/translation.ron +++ /dev/null @@ -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}"), - ] - ) -] \ No newline at end of file diff --git a/backend/translations/english.ron b/backend/translations/english.ron new file mode 100644 index 0000000..89b5313 --- /dev/null +++ b/backend/translations/english.ron @@ -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}"), + ] +) \ No newline at end of file diff --git a/backend/translations/french.ron b/backend/translations/french.ron new file mode 100644 index 0000000..a12dc62 --- /dev/null +++ b/backend/translations/french.ron @@ -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}"), + ] +) \ No newline at end of file diff --git a/do.nu b/do.nu index 066841d..3f0fd23 100755 --- a/do.nu +++ b/do.nu @@ -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"