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__":