From 7b22673200845e84eb9022daa9a6c0280ff4743c Mon Sep 17 00:00:00 2001 From: lapineige Date: Sun, 31 Mar 2024 22:57:19 +0200 Subject: [PATCH] v0.9.1 - add translations --- tools/app_generator/app_generator.py | 260 +++++++++++++++------------ 1 file changed, 147 insertions(+), 113 deletions(-) diff --git a/tools/app_generator/app_generator.py b/tools/app_generator/app_generator.py index fe11d437..69936821 100644 --- a/tools/app_generator/app_generator.py +++ b/tools/app_generator/app_generator.py @@ -35,6 +35,12 @@ from wtforms.validators import ( Length, ) +# Translations +from flask_babel import Babel +from flask_babel import gettext + +from flask import redirect, request, make_response # Language swap by redirecting + # Markdown to HTML - for debugging purposes from misaka import Markdown, HtmlRenderer @@ -45,7 +51,7 @@ from urllib import parse from secrets import token_urlsafe #### GLOBAL VARIABLES -YOLOGEN_VERSION = "0.8.5" +YOLOGEN_VERSION = "0.9.1" GENERATOR_DICT = {"GENERATOR_VERSION": YOLOGEN_VERSION} #### Create FLASK and Jinja Environments @@ -56,6 +62,28 @@ cors = CORS(app) environment = j2.Environment(loader=j2.FileSystemLoader("templates/")) +# Handle translations +BABEL_TRANSLATION_DIRECTORIES='translations' + +babel = Babel() + +LANGUAGES = { + 'en': 'English', + 'fr': 'French' +} + +def configure(app): + babel.init_app(app, locale_selector=get_locale) + app.config['LANGUAGES'] = LANGUAGES +def get_locale(): + print(request.accept_languages.best_match(app.config['LANGUAGES'].keys())) + print(request.cookies.get('lang', 'en')) + #return 'en' # to test + #return 'fr' + return request.cookies.get('lang', 'en') + #return request.accept_languages.best_match(app.config['LANGUAGES'].keys()) # The result is based on the Accept-Language header. For testing purposes, you can directly return a language code, for example: return ‘de’ + +configure(app) #### Custom functions @@ -88,9 +116,9 @@ class GeneralInfos(FlaskForm): app_id = StringField( Markup( - "Identifiant (id) de l'application" + gettext("Application identifier (id)") ), - description="En minuscule et sans espace.", + description=gettext("Small caps and without spaces"), validators=[DataRequired(), Regexp("[a-z_1-9]+.*(? \ - N'indiquez pas de titre du logiciel au début, car ce sera intégré dans une sous-partie "Overview" '''), + Markup(gettext('''Type the content of DESCRIPTION.md file.
\ +Do not give the software name at the beginning, as it will be integrated an 'Overview' subpart''')), validators=[Optional()], render_kw={ "class": "form-control", @@ -452,7 +480,7 @@ class Documentation(FlaskForm): }, ) disclaimer = TextAreaField( - "Saisissez le contenu du fichier DISCLAIMER.md, qui liste des avertissements et points d'attention.", + gettext("Type the DISCLAIMER.md file content, which list warnings and attention points."), validators=[Optional()], render_kw={ "class": "form-control", @@ -460,7 +488,7 @@ class Documentation(FlaskForm): }, ) pre_install = TextAreaField( - "Saisissez le contenu du fichier PRE_INSTALL.md", + gettext("Type the PRE_INSTALL.md file content"), validators=[Optional()], render_kw={ "class": "form-control", @@ -468,7 +496,7 @@ class Documentation(FlaskForm): }, ) post_install = TextAreaField( - "Saisissez le contenu du fichier POST_INSTALL.md", + gettext("Type the POST_INSTALL.md file content"), validators=[Optional()], render_kw={ "class": "form-control", @@ -476,7 +504,7 @@ class Documentation(FlaskForm): }, ) pre_upgrade = TextAreaField( - "Saisissez le contenu du fichier PRE_UPGRADE.md", + gettext("Type the PRE_UPGRADE.md file content"), validators=[Optional()], render_kw={ "class": "form-control", @@ -484,7 +512,7 @@ class Documentation(FlaskForm): }, ) post_upgrade = TextAreaField( - "Saisissez le contenu du fichier POST_UPGRADE.md", + gettext("Type the POST_UPGRADE.md file content"), validators=[Optional()], render_kw={ "class": "form-control", @@ -492,7 +520,7 @@ class Documentation(FlaskForm): }, ) admin = TextAreaField( - "Saisissez le contenu du fichier ADMIN.md", + gettext("Type the ADMIN.md file content"), validators=[Optional()], render_kw={ "class": "form-control", @@ -503,36 +531,36 @@ class Documentation(FlaskForm): class MoreAdvanced(FlaskForm): enable_change_url = BooleanField( - "Gérer le changement d'URL d'installation (script change_url)", + gettext("Handle app install URL change (change_url script)"), default=True, render_kw={ - "title": "Faut-il permettre le changement d'URL pour l'application ? (fichier change_url)" + "title": gettext("Should changing the app URL be allowed ? (change_url change)") }, ) use_logrotate = BooleanField( - "Utiliser logrotate pour les journaux de l'app", + gettext("Use logrotate for the app logs"), default=True, render_kw={ - "title": "Si l'application genère des journaux (log), cette option permet d'en gérer l'archivage. Recommandé." + "title": gettext("If the app generates logs, this option permit to handle their archival. Recommended.") }, ) # TODO : specify custom log file # custom_log_file = "/var/log/$app/$app.log" "/var/log/nginx/${domain}-error.log" use_fail2ban = BooleanField( - "Protéger l'application des attaques par force brute (via fail2ban)", + gettext("Protect the application against brute force attacks (via fail2ban)"), default=False, render_kw={ - "title": "Si l'application genère des journaux (log) d'erreurs de connexion, cette option permet de bannir automatiquement les IP au bout d'un certain nombre d'essais de mot de passe. Recommandé." + "title": gettext("If the app generates failed connexions logs, this option allows to automatically banish the related IP after a certain number of failed password tries. Recommended.") }, ) use_cron = BooleanField( - "Ajouter une tâche CRON pour cette application", - description="Corresponds à des opérations périodiques de l'application", + gettext("Add a CRON task for this application"), + description=gettext("Corresponds to some app periodic operations"), default=False, ) cron_config_file = TextAreaField( - "Saisissez le contenu du fichier CRON", + gettext("Type the CRON file content"), validators=[Optional()], render_kw={ "class": "form-control", @@ -541,13 +569,13 @@ class MoreAdvanced(FlaskForm): ) fail2ban_regex = StringField( - "Expression régulière pour fail2ban", + gettext("Regular expression for fail2ban"), # Regex to match into the log for a failed login validators=[Optional()], render_kw={ - "placeholder": "A regular expression", + "placeholder": gettext("A regular expression"), "class": "form-control", - "title": "Expression régulière à vérifier dans le journal pour que fail2ban s'active (cherchez une ligne qui indique une erreur d'identifiants deconnexion).", + "title": gettext("Regular expression to check in the log file to activate failban (search for a line that indicates a credentials error)."), }, ) @@ -561,17 +589,17 @@ class GeneratorForm( csrf = False generator_mode = SelectField( - "Mode du générateur", - description="En mode tutoriel, l'application générée contiendra des commentaires additionnels pour faciliter la compréhension. En version épurée, l'application générée ne contiendra que le minimum nécessaire.", - choices=[("simple", "Version épurée"), ("tutorial", "Version tutoriel")], + gettext("Generator mode"), + description=gettext("In tutorial version, the generated app will contain additionnal comments to ease the understanding. In steamlined version, the generated app will only contain the necessary minimum."), + choices=[("simple", gettext("Streamlined version")), ("tutorial", gettext("Tutorial version"))], default="true", validators=[DataRequired()], ) - submit_preview = SubmitField("Prévisualiser") - submit_download = SubmitField("Télécharger le .zip") - submit_demo = SubmitField('Remplir avec des valeurs de démonstration', render_kw={"onclick": "fillFormWithDefaultValues()", - "title": "Générer une application minimaliste complète et fonctionnelle à partir de laquelle itérer" + submit_preview = SubmitField(gettext("Previsualise")) + submit_download = SubmitField(gettext("Download the .zip")) + submit_demo = SubmitField(gettext('Fill with demo values'), render_kw={"onclick": "fillFormWithDefaultValues()", + "title": gettext("Generate a complete and functionnal minimalistic app that you can iterate from") }) @@ -679,6 +707,12 @@ def main_form_route(): "index.html", main_form=main_form, generator_info=GENERATOR_DICT, generated_files=app_files, ) +# Localisation +@app.route('/language/') +def set_language(language=None): + response = make_response(redirect(request.referrer or '/')) + response.set_cookie('lang', language) + return response #### Running the web server if __name__ == "__main__":