1
0
Fork 0
mirror of https://github.com/YunoHost/apps.git synced 2024-09-03 20:06:07 +02:00

app_generator: more cleanup, misc bug fixes, simplify code and development flow (#2313)

This commit is contained in:
Alexandre Aubin 2024-05-08 18:03:33 +02:00 committed by GitHub
parent 43aebcce8b
commit f61a6dae1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 313 additions and 2103 deletions

View file

@ -1,152 +1,107 @@
#### Imports
import logging
from io import BytesIO
import re
import os
import jinja2 as j2
import logging
import zipfile
import random
import string
from io import BytesIO
from flask import (
Flask,
render_template,
render_template_string,
request,
redirect,
flash,
send_file,
make_response,
session,
)
from markupsafe import Markup # No longer imported from Flask
# Form libraries
from flask_wtf import FlaskForm
from flask_babel import Babel, lazy_gettext as _
from wtforms import (
StringField,
RadioField,
SelectField,
SubmitField,
TextAreaField,
BooleanField,
SelectMultipleField,
HiddenField,
)
from wtforms.validators import (
DataRequired,
InputRequired,
Optional,
Regexp,
URL,
Length,
)
from wtforms.fields import HiddenField
# Translations
from flask_babel import Babel
from flask_babel import lazy_gettext as _
YOLOGEN_VERSION = "0.11"
LANGUAGES = {"en": _("English"), "fr": _("French")}
from flask import redirect, request, make_response # Language swap by redirecting
# Markdown to HTML - for debugging purposes
from misaka import Markdown, HtmlRenderer
# Managing zipfiles
import zipfile
from flask_cors import CORS
from urllib import parse
from secrets import token_urlsafe
###############################################################################
# App initialization, misc configs
###############################################################################
logger = logging.getLogger()
#### GLOBAL VARIABLES
YOLOGEN_VERSION = "0.10"
GENERATOR_DICT = {"GENERATOR_VERSION": YOLOGEN_VERSION}
app = Flask(__name__, static_url_path="/static", static_folder="static")
#### Create FLASK and Jinja Environments
app = Flask(__name__)
app.config["SECRET_KEY"] = token_urlsafe(16) # Necessary for the form CORS
cors = CORS(app)
if app.config.get("DEBUG"):
app.config["TEMPLATES_AUTO_RELOAD"] = True
environment = j2.Environment(loader=j2.FileSystemLoader("templates/"))
app.config["LANGUAGES"] = LANGUAGES
app.config["GENERATOR_VERSION"] = YOLOGEN_VERSION
def is_hidden_field_filter(field):
return isinstance(field, HiddenField)
app.jinja_env.globals["bootstrap_is_hidden_field"] = is_hidden_field_filter
# Handle translations
BABEL_TRANSLATION_DIRECTORIES = "translations"
babel = Babel()
LANGUAGES = {"en": _("English"), "fr": _("French")}
@app.context_processor
def inject_conf_var():
return dict(AVAILABLE_LANGUAGES=LANGUAGES)
def configure(app):
babel.init_app(app, locale_selector=get_locale)
app.config["LANGUAGES"] = LANGUAGES
# This is the secret key used for session signing
app.secret_key = "".join(random.choice(string.ascii_lowercase) for i in range(32))
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'
if request.args.get("language"):
print(request.args.get("language"))
session["language"] = request.args.get("language")
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
# Define custom filter
@app.template_filter("render_markdown")
def render_markdown(text):
renderer = HtmlRenderer()
markdown = Markdown(renderer)
return markdown(text)
# Add custom filter
j2.filters.FILTERS["render_markdown"] = render_markdown
# Converting markdown to html
def markdown_file_to_html_string(file):
with open(file, "r") as file:
markdown_content = file.read()
# Convert content from Markdown to HTML
html_content = render_markdown(markdown_content)
# Return Markdown and HTML contents
return markdown_content, html_content
### Forms
# Language selector. Not used (in GeneratorForm) until it's fixed or superseeded.
# Use it in the HTML with {{ form_field(main_form.generator_language) }}
class Translations(FlaskForm):
generator_language = SelectField(
_("Select language"),
choices=[("none", "")] + [language for language in LANGUAGES.items()],
default=["en"],
id="selectLanguage",
return (
session.get("lang")
or request.accept_languages.best_match(LANGUAGES.keys())
or "en"
)
babel = Babel(app, locale_selector=get_locale)
@app.context_processor
def jinja_globals():
d = {
"locale": get_locale(),
}
if app.config.get("DEBUG"):
d["tailwind_local"] = open("static/tailwind-local.css").read()
return d
app.jinja_env.globals["is_hidden_field"] = lambda field: isinstance(field, HiddenField)
@app.route("/lang/<lang>")
def set_lang(lang=None):
assert lang in app.config["LANGUAGES"].keys()
session["lang"] = lang
return make_response(redirect(request.referrer or "/"))
###############################################################################
# Forms
###############################################################################
class GeneralInfos(FlaskForm):
app_id = StringField(
Markup(_("Application identifier (id)")),
_("Application identifier (id)"),
description=_("Small caps and without spaces"),
validators=[DataRequired(), Regexp("[a-z_1-9]+.*(?<!_ynh)$")],
render_kw={
@ -525,10 +480,8 @@ class AppConfig(FlaskForm):
class Documentation(FlaskForm):
# TODO : # screenshot
description = TextAreaField(
Markup(
_(
"""doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly listing the main features, possible warnings and specific details on its functioning in Yunohost (e.g. warning about integration issues)."""
)
_(
"doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly listing the main features, possible warnings and specific details on its functioning in Yunohost (e.g. warning about integration issues)."
),
validators=[Optional()],
render_kw={
@ -701,7 +654,6 @@ def main_form_route():
return render_template(
"index.html",
main_form=main_form,
generator_info=GENERATOR_DICT,
generated_files={},
)
@ -761,29 +713,21 @@ def main_form_route():
template_dir = os.path.dirname(__file__) + "/templates/"
for app_file in app_files:
template = open(template_dir + app_file.id + ".j2").read()
app_file.content = render_template_string(
template, data=dict(request.form | GENERATOR_DICT)
)
app_file.content = render_template_string(template, data=dict(request.form))
app_file.content = re.sub(r"\n\s+$", "\n", app_file.content, flags=re.M)
app_file.content = re.sub(r"\n{3,}", "\n\n", app_file.content, flags=re.M)
print(main_form.use_custom_config_file.data)
if main_form.use_custom_config_file.data:
app_files.append(
AppFile("appconf", "conf/" + main_form.custom_config_file.data)
)
app_files[-1].content = main_form.custom_config_file_content.data
print(main_form.custom_config_file.data)
print(main_form.custom_config_file_content.data)
# TODO : same for cron job
if submit_mode == "download":
# Generate the zip file
f = BytesIO()
with zipfile.ZipFile(f, "w") as zf:
print("Exporting zip archive for app: " + request.form["app_id"])
for app_file in app_files:
print(app_file.id)
zf.writestr(app_file.destination_path, app_file.content)
f.seek(0)
# Send the zip file to the user
@ -794,19 +738,10 @@ def main_form_route():
return render_template(
"index.html",
main_form=main_form,
generator_info=GENERATOR_DICT,
generated_files=app_files,
)
# Localisation
@app.route("/language/<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__":
app.run(debug=True)

View file

@ -1,18 +1,14 @@
blinker==1.6.3
cffi==1.16.0
Babel==2.15.0
blinker==1.8.2
click==8.1.7
dominate==2.8.0
Flask==3.0.0
flask_babel~=4.0.0
Flask-Cors==4.0.0
Flask-Misaka==1.0.0
Flask==3.0.3
flask-babel==4.0.0
Flask-WTF==1.2.1
itsdangerous==2.1.2
Jinja2==3.1.4
MarkupSafe==2.1.3
misaka==2.1.1
pycparser==2.21
visitor==0.1.3
Werkzeug==3.0.1
WTForms==3.0.1
gunicorn==22.0.0
itsdangerous==2.2.0
Jinja2==3.1.4
MarkupSafe==2.1.5
packaging==24.0
pytz==2024.1
Werkzeug==3.0.3
WTForms==3.1.2

View file

@ -1,6 +1,15 @@
# Download standalone tailwind to compile what we need
# Production -> download standalone tailwind to compile only what we need
wget https://github.com/tailwindlabs/tailwindcss/releases/download/v3.3.3/tailwindcss-linux-x64
chmod +x tailwindcss-linux-x64
./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --minify
# Development -> we use the JS magic thingy
curl -L https://cdn.tailwindcss.com?plugins=forms > tailwind-css.js
# Forkawesome
curl https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css > fork-awesome.min.css
sed -i 's@../fonts/@@g' ./fork-awesome.min.css
curl https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/fonts/forkawesome-webfont.woff2?v=1.2.0 > forkawesome-webfont.woff2
curl https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/fonts/forkawesome-webfont.woff?v=1.2.0 > forkawesome-webfont.woff
curl https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/fonts/forkawesome-webfont.ttf?v=1.2.0 > forkawesome-webfont.ttf

View file

@ -13,11 +13,11 @@
}
h2 {
@apply text-xl font-bold;
@apply text-xl font-medium;
}
h3 {
@apply text-lg font-bold;
@apply text-lg font-medium;
}
.hide {
@ -43,50 +43,34 @@
@apply block rounded-lg border border-gray-400 mb-2;
}
.panel-heading {
@apply text-white bg-blue-500 hover:bg-blue-700 p-2 font-bold;
@apply text-white bg-blue-500 hover:bg-blue-600 px-2 py-1.5;
}
.panel-body {
@apply p-2;
transition: max-height 0.2s ease-in;
max-height: 0px;
}
.alert-info {
@apply text-blue-900 border-blue-900 bg-blue-200 rounded-lg p-4;
}
.active, .collapse-button:hover {
background-color: #318ddc;
}
.collapse-title:after {
content: '\002B';
color: white;
font-weight: bold;
float: right;
margin-left: 5px;
margin-right: 5px;
}
.expanded .collapse-title::after {
content: "\2212";
}
.collapsed {
padding: 0px 15px 0px 15px;
}
.collapsible {
max-height: 0px;
overflow: hidden;
transition: max-height 0.2s ease-out;
.active .collapse-title:after {
content: '\2212';
}
label {
@apply font-bold;
}
input {
@apply rounded-lg;
}
.form-group, .checkbox {
@apply px-2 py-4;
}

View file

@ -7,11 +7,5 @@ module.exports = {
plugins: [
require('@tailwindcss/forms'),
],
safelist: [
'safelisted',
{
pattern: /^(text-[a-z]+-600|border-[a-z]+-400)$/,
},
]
}

View file

@ -1,12 +1,18 @@
<!DOCTYPE html>
<html>
<html lang="{{ locale }}">
<head>
<title>{{ gettext("YunoHost app generator") }}</title>
<title>{{ _("YunoHost package generator") }}</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ url_for('static', filename='fork-awesome.min.css') }}">
{% if config.DEBUG %}
<script src="{{ url_for('static', filename='tailwind-css.js') }}"></script>
<style type="text/tailwindcss">
{{ tailwind_local|safe }}
</style>
{% else %}
<link rel="stylesheet" href="{{ url_for('static', filename='tailwind.css') }}">
{% endif %}
</head>
<body>
<main class="my-2 mx-auto max-w-screen-md">

View file

@ -1,9 +1,17 @@
{% import "wtf.html" as wtf %}
{% macro form_errors(form, hiddens=True) %}
{%- if form.errors %}
{%- for fieldname, errors in form.errors.items() %}
{%- if is_hidden_field(form[fieldname]) and hiddens or
not is_hidden_field(form[fieldname]) and hiddens != 'only' %}
{%- for error in errors %}
<p class="error">{{error}}</p>
{%- endfor %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- endmacro %}
{% macro form_field(field,
form_type="basic",
horizontal_columns=('lg', 2, 10),
button_map={}) %}
{% macro form_field(field) %}
{% if field.widget.input_type == 'checkbox' %}
<div class="checkbox">
<label>
@ -13,30 +21,54 @@
<p class="help-block">{{field.description|safe}}</p>
{%- endif %}
</div>
{% else %}
{{ wtf.form_field(field, form_type, horizontal_columns, button_map) }}
{%- elif field.type == 'RadioField' -%}
{% for item in field -%}
<div class="radio">
<label>
{{item|safe}} {{item.label.text|safe}}
</label>
</div>
{% endfor %}
{% else -%}
<div class="form-group {% if field.errors %} has-error{% endif -%}
{%- if field.flags.required %} required{% endif -%}
">
{{field.label(class="control-label")|safe}}
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
{%- if field.errors %}
{%- for error in field.errors %}
<p class="help-block">{{error}}</p>
{%- endfor %}
{%- elif field.description -%}
<p class="help-block">{{field.description|safe}}</p>
{%- endif %}
</div>
{% endif %}
{% endmacro %}
{% extends "base.html" %}
{% block main %}
<div class="mx-auto w-full text-center p-8">
<img alt="YunoHost application logo" src="{{ url_for('static', filename='yunohost-package.png') }}" class="w-32 mx-auto">
<h1 class="text-2xl font-bold">
{{ _("Yunohost application generation form") }}
{{ _("YunoHost package generator") }}
</h1>
<p>Version: {{ generator_info['GENERATOR_VERSION'] }}</p>
<p>Version: {{ config["GENERATOR_VERSION"] }}</p>
<script>
function changeLanguage(lang) {
var url = "{{ url_for('set_language', language=lang) }}" + lang;
var url = "{{ url_for('set_lang', lang=lang) }}" + lang;
window.location.href = url;
}
</script>
<div>
{% for lang in AVAILABLE_LANGUAGES.items() %}
<button class="btn btn-sm bg-gray-200" type="button" onclick="changeLanguage('{{ lang[0] }}')">{{ lang[1] }}</button>
{% for lang_id, lang_label in config["LANGUAGES"].items() %}
<button class="btn btn-sm bg-gray-200" type="button" onclick="changeLanguage('{{ lang_id }}')">{{ lang_label }}</button>
{% endfor %}
</div>
</div>
@ -44,17 +76,18 @@
<form method="POST" role="form">
{{ main_form.hidden_tag() }}
<div class="text-red-800">
{{ wtf.form_errors(main_form, hiddens="only") }}
<div class="text-red-500 text-center">
{% if main_form.errors %}<p>{{ _("The form contains issues") }}<p/>{% endif %}
{{ form_errors(main_form, hiddens="only") }}
</div>
{{ form_field(main_form.generator_mode) }}
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("1/9 - General information") }}</h2>
<h2 class="panel-title collapse-title">{{ _("1/9 - General information") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
<div>
{{ form_field(main_form.app_name) }}
{{ form_field(main_form.app_id) }}
@ -67,10 +100,10 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("2/9 - Upstream information") }}</h2>
<h2 class="panel-title collapse-title">{{ _("2/9 - Upstream information") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<p class="tip">{{ gettext("The word 'upstream' refers to the original project that develops and maintains the app") }}</p>
<div class="panel-body hide">
<p class="tip">{{ _("The word 'upstream' refers to the original project that develops and maintains the app") }}</p>
<div>
{{ form_field(main_form.license) }}
{{ form_field(main_form.website) }}
@ -85,9 +118,9 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("3/9 - Integration in YunoHost") }}</h2>
<h2 class="panel-title collapse-title">{{ _("3/9 - Integration in YunoHost") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
<div>
{{ form_field(main_form.version) }}
{{ form_field(main_form.maintainers) }}
@ -101,13 +134,13 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("4/9 - Questions to ask during installation") }}</h2>
<h2 class="panel-title collapse-title">{{ _("4/9 - Questions to ask during installation") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
<p class="tip">
{{ gettext("This part is meant to indicate the questions that will be asked.") }}
{{ _("This part is meant to indicate the questions that will be asked.") }}
<br/>
{{ gettext("NB: only standard questions are asked here, it might be required to complete it by hand using other questions as a guide.") }}
{{ _("NB: only standard questions are asked here, it might be required to complete it by hand using other questions as a guide.") }}
</p>
<div>
@ -121,11 +154,11 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("5/9 - Resources to initialize") }}</h2>
<h2 class="panel-title collapse-title">{{ _("5/9 - Resources to initialize") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
<p class="tip">
{{ gettext("Technical elements configured before launching the 'real' app install script. Usually : creating a system user, downloading app sources, initialiser le dossier d'install et de données, install apt dependencies, create a database, ...") }}
{{ _("Technical elements configured before launching the 'real' app install script. Usually : creating a system user, downloading app sources, initialiser le dossier d'install et de données, install apt dependencies, create a database, ...") }}
</p>
<h3>Sources du logiciel</h3>
@ -148,13 +181,13 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("6/9 - Specific technology") }}</h2>
<h2 class="panel-title collapse-title">{{ _("6/9 - Specific technology") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
{{ form_field(main_form.main_technology) }}
<div id="php_options">
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("You probably want to make sure to have 'phpX.Y-fpm' and others 'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y being the php version you want to use)") }}</div>
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("The generated application draft will include an nginx configuration snippet that interfaces with PHP-FPM") }}</div>
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ _("You probably want to make sure to have 'phpX.Y-fpm' and others 'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y being the php version you want to use)") }}</div>
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ _("The generated application draft will include an nginx configuration snippet that interfaces with PHP-FPM") }}</div>
{{ form_field(main_form.use_composer) }}
</div>
<div id="nodejs_options">
@ -162,11 +195,11 @@
{{ form_field(main_form.use_yarn) }}
</div>
<div id="python_options">
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("You probably want to make sure to have 'python3' and 'python3-venv' listed in the apt dependencies earlier. Other dependencies should be installed inside a venv (cf the proposed install snippet)") }}</div>
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ _("You probably want to make sure to have 'python3' and 'python3-venv' listed in the apt dependencies earlier. Other dependencies should be installed inside a venv (cf the proposed install snippet)") }}</div>
</div>
{{ form_field(main_form.install_snippet) }}
<div id="systemd_options">
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ gettext("The generated application draft will include an nginx configuration snippet that reverse-proxies to a systemd service using an internal port") }}</div>
<div class="alert alert-info" role="alert"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {{ _("The generated application draft will include an nginx configuration snippet that reverse-proxies to a systemd service using an internal port") }}</div>
{{ form_field(main_form.systemd_execstart) }}
</div>
</div>
@ -234,9 +267,9 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("7/9 - App configuration") }}</h2>
<h2 class="panel-title collapse-title">{{ _("7/9 - App configuration") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
{{ form_field(main_form.use_custom_config_file) }}
<!-- TODO : this show/hide the other fields -->
<div id="custom_config_file_options">
@ -258,9 +291,9 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("8/9 - General and advanced documentation") }}</h2>
<h2 class="panel-title collapse-title">{{ _("8/9 - General and advanced documentation") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
{{ form_field(main_form.description) }}
{{ form_field(main_form.pre_install) }}
{{ form_field(main_form.post_install) }}
@ -272,9 +305,9 @@
<div class="panel">
<div class="panel-heading collapse-button">
<h2 class="panel-title collapse-title">{{ gettext("9/9 - Advanced options") }}</h2>
<h2 class="panel-title collapse-title">{{ _("9/9 - Advanced options") }}</h2>
</div>
<div class="panel-body collapsible collapsed">
<div class="panel-body hide">
{{ form_field(main_form.enable_change_url) }}
{{ form_field(main_form.use_logrotate) }}
{{ form_field(main_form.use_fail2ban) }}
@ -323,7 +356,7 @@
<div class="panel-heading collapse-button">
<h3 class="panel-title collapse-title">{{ file.destination_path }}</h3>
</div>
<div class="panel-body collapsible collapsed p-0">
<div class="panel-body hide p-0">
<pre class="m-0">{{ file.content }}</pre>
</div>
</div>
@ -338,15 +371,9 @@
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
if (this.classList.contains('expanded')) {
this.classList.remove('expanded');
this.nextElementSibling.classList.add('collapsed');
} else {
this.classList.add('expanded');
this.nextElementSibling.classList.remove('collapsed');
}
this.nextElementSibling.classList.toggle("hide");
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;

View file

@ -1,213 +0,0 @@
{% macro form_errors(form, hiddens=True) %}
{%- if form.errors %}
{%- for fieldname, errors in form.errors.items() %}
{%- if bootstrap_is_hidden_field(form[fieldname]) and hiddens or
not bootstrap_is_hidden_field(form[fieldname]) and hiddens != 'only' %}
{%- for error in errors %}
<p class="error">{{error}}</p>
{%- endfor %}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- endmacro %}
{% macro _hz_form_wrap(horizontal_columns, form_type, add_group=False, required=False) %}
{% if form_type == "horizontal" %}
{% if add_group %}<div class="form-group{% if required %} required{% endif %}">{% endif %}
<div class="col-{{horizontal_columns[0]}}-offset-{{horizontal_columns[1]}}
col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}
">
{% endif %}
{{caller()}}
{% if form_type == "horizontal" %}
{% if add_group %}</div>{% endif %}
</div>
{% endif %}
{% endmacro %}
{% macro form_field(field,
form_type="basic",
horizontal_columns=('lg', 2, 10),
button_map={}) %}
{# this is a workaround hack for the more straightforward-code of just passing required=required parameter. older versions of wtforms do not have
the necessary fix for required=False attributes, but will also not set the required flag in the first place. we skirt the issue using the code below #}
{% if field.flags.required and not required in kwargs %}
{% set kwargs = dict(required=True, **kwargs) %}
{% endif %}
{% if field.widget.input_type == 'checkbox' %}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
<div class="checkbox">
<label>
{{field()|safe}} {{field.label.text|safe}}
</label>
</div>
{% endcall %}
{%- elif field.type == 'RadioField' -%}
{# note: A cleaner solution would be rendering depending on the widget,
this is just a hack for now, until I can think of something better #}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
{% for item in field -%}
<div class="radio">
<label>
{{item|safe}} {{item.label.text|safe}}
</label>
</div>
{% endfor %}
{% endcall %}
{%- elif field.type == 'SubmitField' -%}
{# deal with jinja scoping issues? #}
{% set field_kwargs = kwargs %}
{# note: same issue as above - should check widget, not field type #}
{% call _hz_form_wrap(horizontal_columns, form_type, True, required=required) %}
{{field(class='btn btn-%s' % button_map.get(field.name, 'default'),
**field_kwargs)}}
{% endcall %}
{%- elif field.type == 'FormField' -%}
{# note: FormFields are tricky to get right and complex setups requiring
these are probably beyond the scope of what this macro tries to do.
the code below ensures that things don't break horribly if we run into
one, but does not try too hard to get things pretty. #}
<fieldset>
<legend>{{field.label}}</legend>
{%- for subfield in field %}
{% if not bootstrap_is_hidden_field(subfield) -%}
{{ form_field(subfield,
form_type=form_type,
horizontal_columns=horizontal_columns,
button_map=button_map) }}
{%- endif %}
{%- endfor %}
</fieldset>
{% else -%}
<div class="form-group {% if field.errors %} has-error{% endif -%}
{%- if field.flags.required %} required{% endif -%}
">
{%- if form_type == "inline" %}
{{field.label(class="sr-only")|safe}}
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
{% elif form_type == "horizontal" %}
{{field.label(class="control-label " + (
" col-%s-%s" % horizontal_columns[0:2]
))|safe}}
<div class=" col-{{horizontal_columns[0]}}-{{horizontal_columns[2]}}">
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
</div>
{%- if field.errors %}
{%- for error in field.errors %}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{error}}</p>
{% endcall %}
{%- endfor %}
{%- elif field.description -%}
{% call _hz_form_wrap(horizontal_columns, form_type, required=required) %}
<p class="help-block">{{field.description|safe}}</p>
{% endcall %}
{%- endif %}
{%- else -%}
{{field.label(class="control-label")|safe}}
{% if field.type == 'FileField' %}
{{field(**kwargs)|safe}}
{% else %}
{{field(class="form-control", **kwargs)|safe}}
{% endif %}
{%- if field.errors %}
{%- for error in field.errors %}
<p class="help-block">{{error}}</p>
{%- endfor %}
{%- elif field.description -%}
<p class="help-block">{{field.description|safe}}</p>
{%- endif %}
{%- endif %}
</div>
{% endif %}
{% endmacro %}
{# valid form types are "basic", "inline" and "horizontal" #}
{% macro quick_form(form,
action="",
method="post",
extra_classes=None,
role="form",
form_type="basic",
horizontal_columns=('lg', 2, 10),
enctype=None,
button_map={},
id="",
novalidate=False) %}
{#-
action="" is what we want, from http://www.ietf.org/rfc/rfc2396.txt:
4.2. Same-document References
A URI reference that does not contain a URI is a reference to the
current document. In other words, an empty URI reference within a
document is interpreted as a reference to the start of that document,
and a reference containing only a fragment identifier is a reference
to the identified fragment of that document. Traversal of such a
reference should not result in an additional retrieval action.
However, if the URI reference occurs in a context that is always
intended to result in a new request, as in the case of HTML's FORM
element, then an empty URI reference represents the base URI of the
current document and should be replaced by that URI when transformed
into a request.
-#}
{#- if any file fields are inside the form and enctype is automatic, adjust
if file fields are found. could really use the equalto test of jinja2
here, but latter is not available until 2.8
warning: the code below is guaranteed to make you cry =(
#}
{%- set _enctype = [] %}
{%- if enctype is none -%}
{%- for field in form %}
{%- if field.type == 'FileField' %}
{#- for loops come with a fairly watertight scope, so this list-hack is
used to be able to set values outside of it #}
{%- set _ = _enctype.append('multipart/form-data') -%}
{%- endif %}
{%- endfor %}
{%- else %}
{% set _ = _enctype.append(enctype) %}
{%- endif %}
<form
{%- if action != None %} action="{{action}}"{% endif -%}
{%- if id %} id="{{id}}"{% endif -%}
{%- if method %} method="{{method}}"{% endif %}
class="form
{%- if extra_classes %} {{extra_classes}}{% endif -%}
{%- if form_type == "horizontal" %} form-horizontal
{%- elif form_type == "inline" %} form-inline
{%- endif -%}
"
{%- if _enctype[0] %} enctype="{{_enctype[0]}}"{% endif -%}
{%- if role %} role="{{role}}"{% endif -%}
{%- if novalidate %} novalidate{% endif -%}
>
{{ form.hidden_tag() }}
{{ form_errors(form, hiddens='only') }}
{%- for field in form %}
{% if not bootstrap_is_hidden_field(field) -%}
{{ form_field(field,
form_type=form_type,
horizontal_columns=horizontal_columns,
button_map=button_map) }}
{%- endif %}
{%- endfor %}
</form>
{%- endmacro %}

View file

@ -1,760 +0,0 @@
# English translations for PROJECT.
# Copyright (C) 2024 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-26 15:39+0200\n"
"PO-Revision-Date: 2024-03-31 20:23+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n"
#: app.py:68 app.py:334
msgid "English"
msgstr ""
#: app.py:68 app.py:335
msgid "French"
msgstr ""
#: app.py:127
msgid "Select language"
msgstr ""
#: app.py:137
msgid "Application identifier (id)"
msgstr ""
#: app.py:138
msgid "Small caps and without spaces"
msgstr ""
#: app.py:146
msgid "App name"
msgstr ""
#: app.py:147
msgid "It's the application name, displayed in the user interface"
msgstr ""
#: app.py:157
msgid "Short description (en)"
msgstr ""
#: app.py:158 app.py:165
msgid ""
"Explain in a few words (10-15) why this app is useful or what it does "
"(the goal is to give a broad idea for the user browsing an hundred apps "
"long catalog"
msgstr ""
#: app.py:164
msgid "Short description (fr)"
msgstr ""
#: app.py:176
msgid "Version"
msgstr ""
#: app.py:182
msgid "Maintainer of the generated app"
msgstr ""
#: app.py:183
msgid "Usually you put your name here... If you're okay with it ;)"
msgstr ""
#: app.py:189
msgid "Minimal YunoHost version"
msgstr ""
#: app.py:190
msgid "Minimal YunoHost version for the application to work"
msgstr ""
#: app.py:199
msgid "Supported architectures"
msgstr ""
#: app.py:201
msgid "All architectures"
msgstr ""
#: app.py:212
msgid ""
"The app can be installed multiple times at the same time on the same "
"server"
msgstr ""
#: app.py:219
msgid "The app will be integrating LDAP"
msgstr ""
#: app.py:220
msgid ""
"Which means it's possible to use Yunohost credentials to log into this "
"app. 'LDAP' corresponds to the technology used by Yunohost to handle a "
"centralised user base. Bridging the app and Yunohost's LDAP often "
"requires to add the proper technical details in the app's configuration "
"file"
msgstr ""
#: app.py:224 app.py:238
msgid "No"
msgstr ""
#: app.py:225 app.py:237
msgid "Yes"
msgstr ""
#: app.py:226 app.py:239
msgid "Not relevant"
msgstr ""
#: app.py:232
msgid "The app will be integrated in Yunohost SSO (Single Sign On)"
msgstr ""
#: app.py:233
msgid ""
"Which means that people will be logged in the app after logging in "
"YunoHost's portal, without having to sign on specifically into this app."
msgstr ""
#: app.py:249
msgid "Licence"
msgstr ""
#: app.py:250
msgid ""
"You should check this on the upstream repository. The expected format is "
"a SPDX id listed in https://spdx.org/licenses/"
msgstr ""
#: app.py:257
msgid "Official website"
msgstr ""
#: app.py:258
msgid "Leave empty if there is no official website"
msgstr ""
#: app.py:265
msgid "Official app demo"
msgstr ""
#: app.py:266
msgid "Leave empty if there is no official demo"
msgstr ""
#: app.py:273
msgid "Admin documentation"
msgstr ""
#: app.py:274
msgid "Leave empty if there is no official admin doc"
msgstr ""
#: app.py:281
msgid "Usage documentation"
msgstr ""
#: app.py:282
msgid "Leave empty if there is no official user doc"
msgstr ""
#: app.py:289
msgid "Code repository"
msgstr ""
#: app.py:300
msgid ""
"Ask the URL where the app will be installed ('domain' and 'path' "
"variables)"
msgstr ""
#: app.py:305
msgid "Ask domain+path"
msgstr ""
#: app.py:308
msgid ""
"Ask only the domain (the app requires to be installed at the root of a "
"dedicated domain)"
msgstr ""
#: app.py:312
msgid "Do not ask (it isn't a webapp)"
msgstr ""
#: app.py:317
msgid "Ask who can access to the app"
msgstr ""
#: app.py:318
msgid ""
"In the users groups : by default at least 'visitors', 'all_users' et "
"'admins' exists. (It was previously the private/public app concept)"
msgstr ""
#: app.py:325
msgid "Ask who can access to the admin interface"
msgstr ""
#: app.py:326
msgid "In the case where the app has an admin interface"
msgstr ""
#: app.py:331
msgid "Supported languages"
msgstr ""
#: app.py:333
msgid "None / not relevant"
msgstr ""
#: app.py:336
msgid "Spanish"
msgstr ""
#: app.py:337
msgid "Italian"
msgstr ""
#: app.py:338
msgid "German"
msgstr ""
#: app.py:339
msgid "Chinese"
msgstr ""
#: app.py:340
msgid "Japanese"
msgstr ""
#: app.py:341
msgid "Danish"
msgstr ""
#: app.py:342
msgid "Portugese"
msgstr ""
#: app.py:343
msgid "Dutch"
msgstr ""
#: app.py:344
msgid "Russian"
msgstr ""
#: app.py:356
msgid "Application source code or executable"
msgstr ""
#: app.py:363
msgid "Sources sha256 checksum"
msgstr ""
#: app.py:371
msgid "Enable automatic update of sources (using a bot running every night)"
msgstr ""
#: app.py:372
msgid ""
"If the upstream software is hosted in one of the handled sources and "
"publishes proper releases or tags, the bot will create a pull request to "
"update the sources URL and checksum"
msgstr ""
#: app.py:394
msgid "Dependencies to be installed via apt (separated by comma and/or spaces)"
msgstr ""
#: app.py:403
msgid "Initialize an SQL database"
msgstr ""
#: app.py:413
msgid "Initialize a system user for this app"
msgstr ""
#: app.py:418
msgid "Initialize an installation folder for this app"
msgstr ""
#: app.py:419
msgid "By default it's /var/www/$app"
msgstr ""
#: app.py:424
msgid "Initialize a folder to store the app data"
msgstr ""
#: app.py:425
msgid "By default it's /var/yunohost.app/$app"
msgstr ""
#: app.py:433
msgid "App main technology"
msgstr ""
#: app.py:435
msgid "None / Static application"
msgstr ""
#: app.py:440
msgid "Other"
msgstr ""
#: app.py:447
msgid "Installation specific commands"
msgstr ""
#: app.py:448
msgid ""
"These commands are executed from the app installation folder (by default,"
" /var/www/$app) after the sources have been deployed. This field uses by "
"default a classic example based on the selected technology. You should "
"probably compare and adapt it according to the app installation "
"documentation"
msgstr ""
#: app.py:460
msgid "Use composer"
msgstr ""
#: app.py:461
msgid "Composer is a PHP dependencies manager used by some apps"
msgstr ""
#: app.py:472
msgid "NodeJS version"
msgstr ""
#: app.py:473
msgid "For example: 16.4, 18, 18.2, 20, 20.1, ..."
msgstr ""
#: app.py:480
msgid "Install and use Yarn"
msgstr ""
#: app.py:487
msgid "Command to start the app daemon (from systemd service)"
msgstr ""
#: app.py:488
msgid ""
"Corresponds to 'ExecStart' statement in systemd. You can use "
"'__INSTALL_DIR__' to refer to the install directory, or '__APP__' to "
"refer to the app id"
msgstr ""
#: app.py:500
msgid "The app uses a specific configuration file"
msgstr ""
#: app.py:501
msgid "Usually : .env, config.json, conf.ini, params.yml, ..."
msgstr ""
#: app.py:508
msgid "Name or file path to use"
msgstr ""
#: app.py:516
msgid "App configuration file pattern"
msgstr ""
#: app.py:517
msgid ""
"In this pattern, you can use the syntax __FOO_BAR__ which will "
"automatically replaced by the value of the variable $foo_bar"
msgstr ""
#: app.py:529
msgid ""
"doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly "
"listing the main features, possible warnings and specific details on its "
"functioning in Yunohost (e.g. warning about integration issues)."
msgstr ""
#: app.py:539
msgid ""
"doc/PRE_INSTALL.md: important info to be shown to the admin before "
"installing the app"
msgstr ""
#: app.py:540 app.py:548 app.py:556 app.py:564 app.py:572
msgid "Leave empty if not relevant"
msgstr ""
#: app.py:547
msgid ""
"doc/POST_INSTALL.md: important info to be shown to the admin after "
"installing the app"
msgstr ""
#: app.py:555
msgid ""
"doc/PRE_UPGRADE.md: important info to be shown to the admin before "
"upgrading the app"
msgstr ""
#: app.py:563
msgid ""
"doc/POST_UPGRADE.md: important info to be shown to the admin after "
"upgrading the app"
msgstr ""
#: app.py:571
msgid "doc/ADMIN.md: general tips on how to administrate this app"
msgstr ""
#: app.py:583
msgid "Handle app install URL change (change_url script)"
msgstr ""
#: app.py:586
msgid "Should changing the app URL be allowed ? (change_url change)"
msgstr ""
#: app.py:593
msgid "Use logrotate for the app logs"
msgstr ""
#: app.py:596
msgid ""
"If the app generates logs, this option permit to handle their archival. "
"Recommended."
msgstr ""
#: app.py:604
msgid "Protect the application against brute force attacks (via fail2ban)"
msgstr ""
#: app.py:609
msgid ""
"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."
msgstr ""
#: app.py:615
msgid "Add a CRON task for this application"
msgstr ""
#: app.py:616
msgid "Corresponds to some app periodic operations"
msgstr ""
#: app.py:620
msgid "Type the CRON file content"
msgstr ""
#: app.py:629
msgid "Regular expression for fail2ban"
msgstr ""
#: app.py:633
msgid "A regular expression"
msgstr ""
#: app.py:635
msgid ""
"Regular expression to check in the log file to activate failban (search "
"for a line that indicates a credentials error)."
msgstr ""
#: app.py:659
msgid "Generator mode"
msgstr ""
#: app.py:660
msgid ""
"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."
msgstr ""
#: app.py:664
msgid "Streamlined version"
msgstr ""
#: app.py:665
msgid "Tutorial version"
msgstr ""
#: app.py:671
msgid "Previsualise"
msgstr ""
#: app.py:672
msgid "Download the .zip"
msgstr ""
#: app.py:674
msgid "Fill with demo values"
msgstr ""
#: app.py:677
msgid ""
"Generate a complete and functionnal minimalistic app that you can iterate"
" from"
msgstr ""
#: templates/base.html:5
msgid "YunoHost app generator"
msgstr ""
#: templates/index.html:28
msgid "Yunohost application generation form"
msgstr ""
#: templates/index.html:55
msgid "1/9 - General information"
msgstr ""
#: templates/index.html:70
msgid "2/9 - Upstream information"
msgstr ""
#: templates/index.html:73
msgid ""
"The word 'upstream' refers to the original project that develops and "
"maintains the app"
msgstr ""
#: templates/index.html:88
msgid "3/9 - Integration in YunoHost"
msgstr ""
#: templates/index.html:104
msgid "4/9 - Questions to ask during installation"
msgstr ""
#: templates/index.html:108
msgid "This part is meant to indicate the questions that will be asked."
msgstr ""
#: templates/index.html:110
msgid ""
"NB: only standard questions are asked here, it might be required to "
"complete it by hand using other questions as a guide."
msgstr ""
#: templates/index.html:124
msgid "5/9 - Resources to initialize"
msgstr ""
#: templates/index.html:128
msgid ""
"Technical elements configured before launching the 'real' app install "
"script. Usually : creating a system user, downloading app sources, "
"initialiser le dossier d'install et de données, install apt dependencies,"
" create a database, ..."
msgstr ""
#: templates/index.html:151
msgid "6/9 - Specific technology"
msgstr ""
#: templates/index.html:156
msgid ""
"You probably want to make sure to have 'phpX.Y-fpm' and others "
"'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y "
"being the php version you want to use)"
msgstr ""
#: templates/index.html:157
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that interfaces with PHP-FPM"
msgstr ""
#: templates/index.html:165
msgid ""
"You probably want to make sure to have 'python3' and 'python3-venv' "
"listed in the apt dependencies earlier. Other dependencies should be "
"installed inside a venv (cf the proposed install snippet)"
msgstr ""
#: templates/index.html:169
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that reverse-proxies to a systemd service using an internal port"
msgstr ""
#: templates/index.html:237
msgid "7/9 - App configuration"
msgstr ""
#: templates/index.html:261
msgid "8/9 - General and advanced documentation"
msgstr ""
#: templates/index.html:275
msgid "9/9 - Advanced options"
msgstr ""
#~ msgid "Maintener of the generated app"
#~ msgstr ""
#~ msgid "Commonly you put your name here... If you agree with it ;)"
#~ msgstr ""
#~ msgid ""
#~ "Which means it's possible to use "
#~ "Yunohost credential to connect. 'LDAP' "
#~ "corresponds to the technology used by"
#~ " Yunohost to handle a centralised "
#~ "user base. Bridging the APP and "
#~ "Yunohost LDAP often requires to fill "
#~ "some parameters in the app configuration"
#~ msgstr ""
#~ msgid ""
#~ "Which means that one connexion to "
#~ "Yunohost unlock the connexion to the "
#~ "software, without having to sign on "
#~ "specificaly into it. One only has "
#~ "to connect once (Single Sign On)"
#~ msgstr ""
#~ msgid "Activate the automated source update bot"
#~ msgstr ""
#~ msgid ""
#~ "If the software is available in "
#~ "one of the handled sources and "
#~ "publish releases or tags for its "
#~ "new updates, or for each new "
#~ "commit, a bot will provide an "
#~ "update with updated URL and checksum"
#~ msgstr ""
#~ msgid ""
#~ "Dependances to be installed via apt "
#~ "(separated by a quote and/or spaces)"
#~ msgstr ""
#~ msgid "Initialise a SQL database"
#~ msgstr ""
#~ msgid "Initialise a system user for this app"
#~ msgstr ""
#~ msgid "Initialise an installation folder for this app"
#~ msgstr ""
#~ msgid "Initialise a folder to store the app data"
#~ msgstr ""
#~ msgid ""
#~ "Type the content of DESCRIPTION.md file."
#~ " <br> Do not give the software "
#~ "name at the beginning, as it will"
#~ " be integrated an 'Overview' subpart"
#~ msgstr ""
#~ msgid ""
#~ "Type the DISCLAIMER.md file content, "
#~ "which list warnings and attention "
#~ "points."
#~ msgstr ""
#~ msgid "Type the PRE_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the POST_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the PRE_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the POST_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the ADMIN.md file content"
#~ msgstr ""
#~ msgid "2/9 - Informations about the application"
#~ msgstr ""
#~ msgid "The word upstream refers to the original project that maintains the app"
#~ msgstr ""
#~ msgid "5/9 - Ressources to initialise"
#~ msgstr ""
#~ msgid ""
#~ "A more complete presentation that the"
#~ " summary completed above, explaining to "
#~ "what correspond the software, the "
#~ "eventual warnings and specific details "
#~ "on its functioning in Yunohost (it's "
#~ "the place where one can warn about"
#~ " integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "Indications to show at key steps "
#~ "to manage the package : installation,"
#~ " update, message to the admin "
#~ "account. You usually don't have to "
#~ "fill them."
#~ msgstr ""
#~ msgid "Description courte (fr)"
#~ msgstr ""
#~ msgid "Français"
#~ msgstr ""
#~ msgid ""
#~ "doc/DESCRIPTION.md : A comprehensive "
#~ "presentation of the app, possibly "
#~ "listing the main features, possible "
#~ "warnings and specific details on its "
#~ "functioning in Yunohost (e.g. warning "
#~ "about integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_INSTALL.md : important info to "
#~ "be shown to the admin before "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_INSTALL.md : important info to "
#~ "be shown to the admin after "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_UPGRADE.md : important info to "
#~ "be shown to the admin before "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_UPGRADE.md : important info to "
#~ "be shown to the admin after "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid "doc/ADMIN.md : general tips on how to administrate this app"
#~ msgstr ""

View file

@ -1,760 +0,0 @@
# Spanish translations for PROJECT.
# Copyright (C) 2024 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2024.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-26 15:39+0200\n"
"PO-Revision-Date: 2024-03-31 20:02+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: es\n"
"Language-Team: es <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n"
#: app.py:68 app.py:334
msgid "English"
msgstr ""
#: app.py:68 app.py:335
msgid "French"
msgstr ""
#: app.py:127
msgid "Select language"
msgstr ""
#: app.py:137
msgid "Application identifier (id)"
msgstr ""
#: app.py:138
msgid "Small caps and without spaces"
msgstr ""
#: app.py:146
msgid "App name"
msgstr ""
#: app.py:147
msgid "It's the application name, displayed in the user interface"
msgstr ""
#: app.py:157
msgid "Short description (en)"
msgstr ""
#: app.py:158 app.py:165
msgid ""
"Explain in a few words (10-15) why this app is useful or what it does "
"(the goal is to give a broad idea for the user browsing an hundred apps "
"long catalog"
msgstr ""
#: app.py:164
msgid "Short description (fr)"
msgstr ""
#: app.py:176
msgid "Version"
msgstr ""
#: app.py:182
msgid "Maintainer of the generated app"
msgstr ""
#: app.py:183
msgid "Usually you put your name here... If you're okay with it ;)"
msgstr ""
#: app.py:189
msgid "Minimal YunoHost version"
msgstr ""
#: app.py:190
msgid "Minimal YunoHost version for the application to work"
msgstr ""
#: app.py:199
msgid "Supported architectures"
msgstr ""
#: app.py:201
msgid "All architectures"
msgstr ""
#: app.py:212
msgid ""
"The app can be installed multiple times at the same time on the same "
"server"
msgstr ""
#: app.py:219
msgid "The app will be integrating LDAP"
msgstr ""
#: app.py:220
msgid ""
"Which means it's possible to use Yunohost credentials to log into this "
"app. 'LDAP' corresponds to the technology used by Yunohost to handle a "
"centralised user base. Bridging the app and Yunohost's LDAP often "
"requires to add the proper technical details in the app's configuration "
"file"
msgstr ""
#: app.py:224 app.py:238
msgid "No"
msgstr ""
#: app.py:225 app.py:237
msgid "Yes"
msgstr ""
#: app.py:226 app.py:239
msgid "Not relevant"
msgstr ""
#: app.py:232
msgid "The app will be integrated in Yunohost SSO (Single Sign On)"
msgstr ""
#: app.py:233
msgid ""
"Which means that people will be logged in the app after logging in "
"YunoHost's portal, without having to sign on specifically into this app."
msgstr ""
#: app.py:249
msgid "Licence"
msgstr ""
#: app.py:250
msgid ""
"You should check this on the upstream repository. The expected format is "
"a SPDX id listed in https://spdx.org/licenses/"
msgstr ""
#: app.py:257
msgid "Official website"
msgstr ""
#: app.py:258
msgid "Leave empty if there is no official website"
msgstr ""
#: app.py:265
msgid "Official app demo"
msgstr ""
#: app.py:266
msgid "Leave empty if there is no official demo"
msgstr ""
#: app.py:273
msgid "Admin documentation"
msgstr ""
#: app.py:274
msgid "Leave empty if there is no official admin doc"
msgstr ""
#: app.py:281
msgid "Usage documentation"
msgstr ""
#: app.py:282
msgid "Leave empty if there is no official user doc"
msgstr ""
#: app.py:289
msgid "Code repository"
msgstr ""
#: app.py:300
msgid ""
"Ask the URL where the app will be installed ('domain' and 'path' "
"variables)"
msgstr ""
#: app.py:305
msgid "Ask domain+path"
msgstr ""
#: app.py:308
msgid ""
"Ask only the domain (the app requires to be installed at the root of a "
"dedicated domain)"
msgstr ""
#: app.py:312
msgid "Do not ask (it isn't a webapp)"
msgstr ""
#: app.py:317
msgid "Ask who can access to the app"
msgstr ""
#: app.py:318
msgid ""
"In the users groups : by default at least 'visitors', 'all_users' et "
"'admins' exists. (It was previously the private/public app concept)"
msgstr ""
#: app.py:325
msgid "Ask who can access to the admin interface"
msgstr ""
#: app.py:326
msgid "In the case where the app has an admin interface"
msgstr ""
#: app.py:331
msgid "Supported languages"
msgstr ""
#: app.py:333
msgid "None / not relevant"
msgstr ""
#: app.py:336
msgid "Spanish"
msgstr ""
#: app.py:337
msgid "Italian"
msgstr ""
#: app.py:338
msgid "German"
msgstr ""
#: app.py:339
msgid "Chinese"
msgstr ""
#: app.py:340
msgid "Japanese"
msgstr ""
#: app.py:341
msgid "Danish"
msgstr ""
#: app.py:342
msgid "Portugese"
msgstr ""
#: app.py:343
msgid "Dutch"
msgstr ""
#: app.py:344
msgid "Russian"
msgstr ""
#: app.py:356
msgid "Application source code or executable"
msgstr ""
#: app.py:363
msgid "Sources sha256 checksum"
msgstr ""
#: app.py:371
msgid "Enable automatic update of sources (using a bot running every night)"
msgstr ""
#: app.py:372
msgid ""
"If the upstream software is hosted in one of the handled sources and "
"publishes proper releases or tags, the bot will create a pull request to "
"update the sources URL and checksum"
msgstr ""
#: app.py:394
msgid "Dependencies to be installed via apt (separated by comma and/or spaces)"
msgstr ""
#: app.py:403
msgid "Initialize an SQL database"
msgstr ""
#: app.py:413
msgid "Initialize a system user for this app"
msgstr ""
#: app.py:418
msgid "Initialize an installation folder for this app"
msgstr ""
#: app.py:419
msgid "By default it's /var/www/$app"
msgstr ""
#: app.py:424
msgid "Initialize a folder to store the app data"
msgstr ""
#: app.py:425
msgid "By default it's /var/yunohost.app/$app"
msgstr ""
#: app.py:433
msgid "App main technology"
msgstr ""
#: app.py:435
msgid "None / Static application"
msgstr ""
#: app.py:440
msgid "Other"
msgstr ""
#: app.py:447
msgid "Installation specific commands"
msgstr ""
#: app.py:448
msgid ""
"These commands are executed from the app installation folder (by default,"
" /var/www/$app) after the sources have been deployed. This field uses by "
"default a classic example based on the selected technology. You should "
"probably compare and adapt it according to the app installation "
"documentation"
msgstr ""
#: app.py:460
msgid "Use composer"
msgstr ""
#: app.py:461
msgid "Composer is a PHP dependencies manager used by some apps"
msgstr ""
#: app.py:472
msgid "NodeJS version"
msgstr ""
#: app.py:473
msgid "For example: 16.4, 18, 18.2, 20, 20.1, ..."
msgstr ""
#: app.py:480
msgid "Install and use Yarn"
msgstr ""
#: app.py:487
msgid "Command to start the app daemon (from systemd service)"
msgstr ""
#: app.py:488
msgid ""
"Corresponds to 'ExecStart' statement in systemd. You can use "
"'__INSTALL_DIR__' to refer to the install directory, or '__APP__' to "
"refer to the app id"
msgstr ""
#: app.py:500
msgid "The app uses a specific configuration file"
msgstr ""
#: app.py:501
msgid "Usually : .env, config.json, conf.ini, params.yml, ..."
msgstr ""
#: app.py:508
msgid "Name or file path to use"
msgstr ""
#: app.py:516
msgid "App configuration file pattern"
msgstr ""
#: app.py:517
msgid ""
"In this pattern, you can use the syntax __FOO_BAR__ which will "
"automatically replaced by the value of the variable $foo_bar"
msgstr ""
#: app.py:529
msgid ""
"doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly "
"listing the main features, possible warnings and specific details on its "
"functioning in Yunohost (e.g. warning about integration issues)."
msgstr ""
#: app.py:539
msgid ""
"doc/PRE_INSTALL.md: important info to be shown to the admin before "
"installing the app"
msgstr ""
#: app.py:540 app.py:548 app.py:556 app.py:564 app.py:572
msgid "Leave empty if not relevant"
msgstr ""
#: app.py:547
msgid ""
"doc/POST_INSTALL.md: important info to be shown to the admin after "
"installing the app"
msgstr ""
#: app.py:555
msgid ""
"doc/PRE_UPGRADE.md: important info to be shown to the admin before "
"upgrading the app"
msgstr ""
#: app.py:563
msgid ""
"doc/POST_UPGRADE.md: important info to be shown to the admin after "
"upgrading the app"
msgstr ""
#: app.py:571
msgid "doc/ADMIN.md: general tips on how to administrate this app"
msgstr ""
#: app.py:583
msgid "Handle app install URL change (change_url script)"
msgstr ""
#: app.py:586
msgid "Should changing the app URL be allowed ? (change_url change)"
msgstr ""
#: app.py:593
msgid "Use logrotate for the app logs"
msgstr ""
#: app.py:596
msgid ""
"If the app generates logs, this option permit to handle their archival. "
"Recommended."
msgstr ""
#: app.py:604
msgid "Protect the application against brute force attacks (via fail2ban)"
msgstr ""
#: app.py:609
msgid ""
"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."
msgstr ""
#: app.py:615
msgid "Add a CRON task for this application"
msgstr ""
#: app.py:616
msgid "Corresponds to some app periodic operations"
msgstr ""
#: app.py:620
msgid "Type the CRON file content"
msgstr ""
#: app.py:629
msgid "Regular expression for fail2ban"
msgstr ""
#: app.py:633
msgid "A regular expression"
msgstr ""
#: app.py:635
msgid ""
"Regular expression to check in the log file to activate failban (search "
"for a line that indicates a credentials error)."
msgstr ""
#: app.py:659
msgid "Generator mode"
msgstr ""
#: app.py:660
msgid ""
"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."
msgstr ""
#: app.py:664
msgid "Streamlined version"
msgstr ""
#: app.py:665
msgid "Tutorial version"
msgstr ""
#: app.py:671
msgid "Previsualise"
msgstr ""
#: app.py:672
msgid "Download the .zip"
msgstr ""
#: app.py:674
msgid "Fill with demo values"
msgstr ""
#: app.py:677
msgid ""
"Generate a complete and functionnal minimalistic app that you can iterate"
" from"
msgstr ""
#: templates/base.html:5
msgid "YunoHost app generator"
msgstr ""
#: templates/index.html:28
msgid "Yunohost application generation form"
msgstr ""
#: templates/index.html:55
msgid "1/9 - General information"
msgstr ""
#: templates/index.html:70
msgid "2/9 - Upstream information"
msgstr ""
#: templates/index.html:73
msgid ""
"The word 'upstream' refers to the original project that develops and "
"maintains the app"
msgstr ""
#: templates/index.html:88
msgid "3/9 - Integration in YunoHost"
msgstr ""
#: templates/index.html:104
msgid "4/9 - Questions to ask during installation"
msgstr ""
#: templates/index.html:108
msgid "This part is meant to indicate the questions that will be asked."
msgstr ""
#: templates/index.html:110
msgid ""
"NB: only standard questions are asked here, it might be required to "
"complete it by hand using other questions as a guide."
msgstr ""
#: templates/index.html:124
msgid "5/9 - Resources to initialize"
msgstr ""
#: templates/index.html:128
msgid ""
"Technical elements configured before launching the 'real' app install "
"script. Usually : creating a system user, downloading app sources, "
"initialiser le dossier d'install et de données, install apt dependencies,"
" create a database, ..."
msgstr ""
#: templates/index.html:151
msgid "6/9 - Specific technology"
msgstr ""
#: templates/index.html:156
msgid ""
"You probably want to make sure to have 'phpX.Y-fpm' and others "
"'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y "
"being the php version you want to use)"
msgstr ""
#: templates/index.html:157
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that interfaces with PHP-FPM"
msgstr ""
#: templates/index.html:165
msgid ""
"You probably want to make sure to have 'python3' and 'python3-venv' "
"listed in the apt dependencies earlier. Other dependencies should be "
"installed inside a venv (cf the proposed install snippet)"
msgstr ""
#: templates/index.html:169
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that reverse-proxies to a systemd service using an internal port"
msgstr ""
#: templates/index.html:237
msgid "7/9 - App configuration"
msgstr ""
#: templates/index.html:261
msgid "8/9 - General and advanced documentation"
msgstr ""
#: templates/index.html:275
msgid "9/9 - Advanced options"
msgstr ""
#~ msgid "Maintener of the generated app"
#~ msgstr ""
#~ msgid "Commonly you put your name here... If you agree with it ;)"
#~ msgstr ""
#~ msgid ""
#~ "Which means it's possible to use "
#~ "Yunohost credential to connect. 'LDAP' "
#~ "corresponds to the technology used by"
#~ " Yunohost to handle a centralised "
#~ "user base. Bridging the APP and "
#~ "Yunohost LDAP often requires to fill "
#~ "some parameters in the app configuration"
#~ msgstr ""
#~ msgid ""
#~ "Which means that one connexion to "
#~ "Yunohost unlock the connexion to the "
#~ "software, without having to sign on "
#~ "specificaly into it. One only has "
#~ "to connect once (Single Sign On)"
#~ msgstr ""
#~ msgid "Activate the automated source update bot"
#~ msgstr ""
#~ msgid ""
#~ "If the software is available in "
#~ "one of the handled sources and "
#~ "publish releases or tags for its "
#~ "new updates, or for each new "
#~ "commit, a bot will provide an "
#~ "update with updated URL and checksum"
#~ msgstr ""
#~ msgid ""
#~ "Dependances to be installed via apt "
#~ "(separated by a quote and/or spaces)"
#~ msgstr ""
#~ msgid "Initialise a SQL database"
#~ msgstr ""
#~ msgid "Initialise a system user for this app"
#~ msgstr ""
#~ msgid "Initialise an installation folder for this app"
#~ msgstr ""
#~ msgid "Initialise a folder to store the app data"
#~ msgstr ""
#~ msgid ""
#~ "Type the content of DESCRIPTION.md file."
#~ " <br> Do not give the software "
#~ "name at the beginning, as it will"
#~ " be integrated an 'Overview' subpart"
#~ msgstr ""
#~ msgid ""
#~ "Type the DISCLAIMER.md file content, "
#~ "which list warnings and attention "
#~ "points."
#~ msgstr ""
#~ msgid "Type the PRE_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the POST_INSTALL.md file content"
#~ msgstr ""
#~ msgid "Type the PRE_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the POST_UPGRADE.md file content"
#~ msgstr ""
#~ msgid "Type the ADMIN.md file content"
#~ msgstr ""
#~ msgid "2/9 - Informations about the application"
#~ msgstr ""
#~ msgid "The word upstream refers to the original project that maintains the app"
#~ msgstr ""
#~ msgid "5/9 - Ressources to initialise"
#~ msgstr ""
#~ msgid ""
#~ "A more complete presentation that the"
#~ " summary completed above, explaining to "
#~ "what correspond the software, the "
#~ "eventual warnings and specific details "
#~ "on its functioning in Yunohost (it's "
#~ "the place where one can warn about"
#~ " integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "Indications to show at key steps "
#~ "to manage the package : installation,"
#~ " update, message to the admin "
#~ "account. You usually don't have to "
#~ "fill them."
#~ msgstr ""
#~ msgid "Description courte (fr)"
#~ msgstr ""
#~ msgid "Français"
#~ msgstr ""
#~ msgid ""
#~ "doc/DESCRIPTION.md : A comprehensive "
#~ "presentation of the app, possibly "
#~ "listing the main features, possible "
#~ "warnings and specific details on its "
#~ "functioning in Yunohost (e.g. warning "
#~ "about integration issues)."
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_INSTALL.md : important info to "
#~ "be shown to the admin before "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_INSTALL.md : important info to "
#~ "be shown to the admin after "
#~ "installing the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/PRE_UPGRADE.md : important info to "
#~ "be shown to the admin before "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid ""
#~ "doc/POST_UPGRADE.md : important info to "
#~ "be shown to the admin after "
#~ "upgrading the app"
#~ msgstr ""
#~ msgid "doc/ADMIN.md : general tips on how to administrate this app"
#~ msgstr ""

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-04-26 15:39+0200\n"
"POT-Creation-Date: 2024-05-08 17:57+0200\n"
"PO-Revision-Date: 2024-03-31 20:23+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fr\n"
@ -16,43 +16,39 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n"
"Generated-By: Babel 2.15.0\n"
#: app.py:68 app.py:334
#: app.py:41 app.py:291
msgid "English"
msgstr "Anglais"
#: app.py:68 app.py:335
#: app.py:41 app.py:292
msgid "French"
msgstr "Français"
#: app.py:127
msgid "Select language"
msgstr "Langues supportées"
#: app.py:137
#: app.py:100
msgid "Application identifier (id)"
msgstr "Identifiant (id) de l'application"
#: app.py:138
#: app.py:101
msgid "Small caps and without spaces"
msgstr "En minuscule et sans espace"
#: app.py:146
#: app.py:109
msgid "App name"
msgstr "Nom de l'application"
#: app.py:147
#: app.py:110
msgid "It's the application name, displayed in the user interface"
msgstr ""
"Il s'agit du nom l'application, affiché dans les interfaces "
"utilisateur·ice·s"
#: app.py:157
#: app.py:118
msgid "Short description (en)"
msgstr "Description courte (en)"
#: app.py:158 app.py:165
#: app.py:119 app.py:126
msgid ""
"Explain in a few words (10-15) why this app is useful or what it does "
"(the goal is to give a broad idea for the user browsing an hundred apps "
@ -62,39 +58,39 @@ msgstr ""
"fait (l'objectif est de donner une idée grossière pour des utilisateurs "
"qui naviguent dans un catalogue de 100+ apps)"
#: app.py:164
#: app.py:125
msgid "Short description (fr)"
msgstr "Description courte (fr)"
#: app.py:176
#: app.py:137
msgid "Version"
msgstr "Version"
#: app.py:182
#: app.py:143
msgid "Maintainer of the generated app"
msgstr "Mainteneur·euse de l'app YunoHost créée"
#: app.py:183
#: app.py:144
msgid "Usually you put your name here... If you're okay with it ;)"
msgstr "Généralement vous mettez votre nom ici… Si vous êtes d'accord ;)"
#: app.py:189
#: app.py:148
msgid "Minimal YunoHost version"
msgstr "Version YunoHost minimale"
#: app.py:190
#: app.py:149
msgid "Minimal YunoHost version for the application to work"
msgstr "Version minimale de Yunohost pour que l'application fonctionne."
#: app.py:199
#: app.py:156
msgid "Supported architectures"
msgstr "Architectures supportées"
#: app.py:201
#: app.py:158
msgid "All architectures"
msgstr "Toutes les architectures"
#: app.py:212
#: app.py:169
msgid ""
"The app can be installed multiple times at the same time on the same "
"server"
@ -102,11 +98,11 @@ msgstr ""
"L'app pourra être installée simultanément plusieurs fois sur la même "
"machine"
#: app.py:219
#: app.py:176
msgid "The app will be integrating LDAP"
msgstr "L'app s'intègrera avec le LDAP"
#: app.py:220
#: app.py:177
msgid ""
"Which means it's possible to use Yunohost credentials to log into this "
"app. 'LDAP' corresponds to the technology used by Yunohost to handle a "
@ -120,23 +116,23 @@ msgstr ""
"YunoHost nécessite le plus souvent de remplir des paramètres dans la "
"configuration de l'app"
#: app.py:224 app.py:238
#: app.py:181 app.py:195
msgid "No"
msgstr "Non"
#: app.py:225 app.py:237
#: app.py:182 app.py:194
msgid "Yes"
msgstr "Oui"
#: app.py:226 app.py:239
#: app.py:183 app.py:196
msgid "Not relevant"
msgstr "Non pertinent"
#: app.py:232
#: app.py:189
msgid "The app will be integrated in Yunohost SSO (Single Sign On)"
msgstr "L'app s'intègrera avec le SSO (Single Sign On) de YunoHost"
#: app.py:233
#: app.py:190
msgid ""
"Which means that people will be logged in the app after logging in "
"YunoHost's portal, without having to sign on specifically into this app."
@ -145,11 +141,11 @@ msgstr ""
" connectées au portail YunoHost, sans avoir à se connecter spécifiquement"
" dans"
#: app.py:249
#: app.py:206
msgid "Licence"
msgstr "License"
#: app.py:250
#: app.py:207
msgid ""
"You should check this on the upstream repository. The expected format is "
"a SPDX id listed in https://spdx.org/licenses/"
@ -157,43 +153,43 @@ msgstr ""
"Vous devriez chercher cela dans le dépôt du logiciel. Le format attendu "
"est un identifiant SPDX listé dans https://spdx.org/licenses/"
#: app.py:257
#: app.py:214
msgid "Official website"
msgstr "Site web officiel"
#: app.py:258
#: app.py:215
msgid "Leave empty if there is no official website"
msgstr "Laisser vide s'il n'y a pas de site officiel"
#: app.py:265
#: app.py:222
msgid "Official app demo"
msgstr "Démo officielle de l'app"
#: app.py:266
#: app.py:223
msgid "Leave empty if there is no official demo"
msgstr "Laisser vide s'il n'y a pas de démo officielle"
#: app.py:273
#: app.py:230
msgid "Admin documentation"
msgstr "Documentation d'administration"
#: app.py:274
#: app.py:231
msgid "Leave empty if there is no official admin doc"
msgstr "Laisser vide s'il n'y a pas de documentation d'administration officielle"
#: app.py:281
#: app.py:238
msgid "Usage documentation"
msgstr "Documentation d'utilisation"
#: app.py:282
#: app.py:239
msgid "Leave empty if there is no official user doc"
msgstr "Laisser vide s'il n'y a pas de documentation d'utilisation officielle"
#: app.py:289
#: app.py:246
msgid "Code repository"
msgstr "Dépôt de code"
#: app.py:300
#: app.py:257
msgid ""
"Ask the URL where the app will be installed ('domain' and 'path' "
"variables)"
@ -201,11 +197,11 @@ msgstr ""
"Demander l'URL sur laquelle sera installée l'app (variables 'domain' et "
"'path')"
#: app.py:305
#: app.py:262
msgid "Ask domain+path"
msgstr "Demander le domaine+chemin"
#: app.py:308
#: app.py:265
msgid ""
"Ask only the domain (the app requires to be installed at the root of a "
"dedicated domain)"
@ -213,15 +209,15 @@ msgstr ""
"Demander le domaine uniquement (l'app nécessite d'être installée à la "
"racine d'un domaine dédié à cette app)"
#: app.py:312
#: app.py:269
msgid "Do not ask (it isn't a webapp)"
msgstr "Ne pas demander (l'app n'est pas une webapp)"
#: app.py:317
#: app.py:274
msgid "Ask who can access to the app"
msgstr "Demander qui pourra accéder à l'app"
#: app.py:318
#: app.py:275
msgid ""
"In the users groups : by default at least 'visitors', 'all_users' et "
"'admins' exists. (It was previously the private/public app concept)"
@ -230,73 +226,73 @@ msgstr ""
"'all_users' et 'admins' existent. (Corresponds anciennement à la notion "
"d'app privée/publique)"
#: app.py:325
#: app.py:282
msgid "Ask who can access to the admin interface"
msgstr "Demander qui pourra accéder à l'interface d'admin"
#: app.py:326
#: app.py:283
msgid "In the case where the app has an admin interface"
msgstr "Ceci suppose a priori que l'app dispose d'une interface d'admin"
#: app.py:331
#: app.py:288
msgid "Supported languages"
msgstr "Langues supportées"
#: app.py:333
#: app.py:290
msgid "None / not relevant"
msgstr "Aucune / non pertinent"
#: app.py:336
#: app.py:293
msgid "Spanish"
msgstr "Espagnol"
#: app.py:337
#: app.py:294
msgid "Italian"
msgstr "Italien"
#: app.py:338
#: app.py:295
msgid "German"
msgstr "Allemand"
#: app.py:339
#: app.py:296
msgid "Chinese"
msgstr "Chinois"
#: app.py:340
#: app.py:297
msgid "Japanese"
msgstr "Japonais"
#: app.py:341
#: app.py:298
msgid "Danish"
msgstr "Danois"
#: app.py:342
#: app.py:299
msgid "Portugese"
msgstr "Portugais"
#: app.py:343
#: app.py:300
msgid "Dutch"
msgstr "Néerlandais"
#: app.py:344
#: app.py:301
msgid "Russian"
msgstr "Russe"
#: app.py:356
#: app.py:313
msgid "Application source code or executable"
msgstr "Code source ou exécutable de l'application"
#: app.py:363
#: app.py:320
msgid "Sources sha256 checksum"
msgstr "Empreinte sha256 des sources"
#: app.py:371
#: app.py:328
msgid "Enable automatic update of sources (using a bot running every night)"
msgstr ""
"Activer le robot de mise à jour automatique des sources (via un robot "
"chaque nuit)"
#: app.py:372
#: app.py:329
msgid ""
"If the upstream software is hosted in one of the handled sources and "
"publishes proper releases or tags, the bot will create a pull request to "
@ -306,51 +302,51 @@ msgstr ""
"publie des releases ou des tags pour ses nouvelles versions, un robot "
"proposera automatiquement des mises à jour de l'URL et de la checksum."
#: app.py:394
#: app.py:351
msgid "Dependencies to be installed via apt (separated by comma and/or spaces)"
msgstr "Dépendances à installer via apt (séparées par des virgules et/ou espaces)"
#: app.py:403
#: app.py:358
msgid "Initialize an SQL database"
msgstr "Initialiser une base de données SQL"
#: app.py:413
#: app.py:368
msgid "Initialize a system user for this app"
msgstr "Initialiser un utilisateur système pour cet app"
#: app.py:418
#: app.py:373
msgid "Initialize an installation folder for this app"
msgstr "Initialiser un dossier d'installation de l'app"
#: app.py:419
#: app.py:374
msgid "By default it's /var/www/$app"
msgstr "Par défaut il s'agit de /var/www/$app"
#: app.py:424
#: app.py:379
msgid "Initialize a folder to store the app data"
msgstr "Initialiser un dossier destiné à stocker les données de l'app"
#: app.py:425
#: app.py:380
msgid "By default it's /var/yunohost.app/$app"
msgstr "Par défaut il s'agit de /home/yunohost.app/$app"
#: app.py:433
#: app.py:388
msgid "App main technology"
msgstr "Technologie principale de l'app"
#: app.py:435
#: app.py:390
msgid "None / Static application"
msgstr "Aucune / application statique"
#: app.py:440
#: app.py:395
msgid "Other"
msgstr "Autre"
#: app.py:447
#: app.py:402
msgid "Installation specific commands"
msgstr "Commandes spécifiques d'installation"
#: app.py:448
#: app.py:403
msgid ""
"These commands are executed from the app installation folder (by default,"
" /var/www/$app) after the sources have been deployed. This field uses by "
@ -364,31 +360,31 @@ msgstr ""
"technologie sélectionnée. Vous devriez sans-doute le comparer et "
"l'adapter en fonction de la documentation d'installation de l'app."
#: app.py:460
#: app.py:415
msgid "Use composer"
msgstr "Utiliser composer"
#: app.py:461
#: app.py:416
msgid "Composer is a PHP dependencies manager used by some apps"
msgstr "Composer est un gestionnaire de dépendance PHP utilisé par certaines apps"
#: app.py:472
#: app.py:425
msgid "NodeJS version"
msgstr "Version de NodeJS"
#: app.py:473
#: app.py:426
msgid "For example: 16.4, 18, 18.2, 20, 20.1, ..."
msgstr "Par exemple: 16.4, 18, 18.2, 20, 20.1, ..."
#: app.py:480
#: app.py:433
msgid "Install and use Yarn"
msgstr "Installer et utiliser Yarn"
#: app.py:487
#: app.py:440
msgid "Command to start the app daemon (from systemd service)"
msgstr "Commande pour lancer le daemon de l'app (depuis le service systemd)"
#: app.py:488
#: app.py:441
msgid ""
"Corresponds to 'ExecStart' statement in systemd. You can use "
"'__INSTALL_DIR__' to refer to the install directory, or '__APP__' to "
@ -398,23 +394,23 @@ msgstr ""
"'__INSTALL_DIR__' pour faire référence directory, our '__APP__' pour "
"l'identifiant de l'application"
#: app.py:500
#: app.py:453
msgid "The app uses a specific configuration file"
msgstr "L'app utilise un fichier de configuration spécifique"
#: app.py:501
#: app.py:454
msgid "Usually : .env, config.json, conf.ini, params.yml, ..."
msgstr "Typiquement : .env, config.json, conf.ini, params.yml, ..."
#: app.py:508
#: app.py:459
msgid "Name or file path to use"
msgstr "Nom ou chemin du fichier à utiliser"
#: app.py:516
#: app.py:467
msgid "App configuration file pattern"
msgstr "Modèle de fichier de configuration de l'app"
#: app.py:517
#: app.py:468
msgid ""
"In this pattern, you can use the syntax __FOO_BAR__ which will "
"automatically replaced by the value of the variable $foo_bar"
@ -422,7 +418,7 @@ msgstr ""
"Dans ce modèle, vous pouvez utilisez la syntaxe __FOO_BAR__ qui sera "
"automatiquement remplacé par la valeur de la variable $foo_bar"
#: app.py:529
#: app.py:479
msgid ""
"doc/DESCRIPTION.md: A comprehensive presentation of the app, possibly "
"listing the main features, possible warnings and specific details on its "
@ -433,7 +429,7 @@ msgstr ""
"précisions éventuelles sur son fonctionnement dans Yunohost (c'est "
"l'endroit où l'on signale des problèmes d'intégrations)."
#: app.py:539
#: app.py:488
msgid ""
"doc/PRE_INSTALL.md: important info to be shown to the admin before "
"installing the app"
@ -441,11 +437,11 @@ msgstr ""
"doc/PRE_INSTALL.md : info importantes à montrer aux admins avant "
"l'installation de l'app"
#: app.py:540 app.py:548 app.py:556 app.py:564 app.py:572
#: app.py:491 app.py:501 app.py:511 app.py:521 app.py:529
msgid "Leave empty if not relevant"
msgstr "Laisser vide si pas pertinent"
#: app.py:547
#: app.py:498
msgid ""
"doc/POST_INSTALL.md: important info to be shown to the admin after "
"installing the app"
@ -453,7 +449,7 @@ msgstr ""
"doc/POST_INSTALL.md : infos importantes à montrer aux admins après "
"l'installation de l'app"
#: app.py:555
#: app.py:508
msgid ""
"doc/PRE_UPGRADE.md: important info to be shown to the admin before "
"upgrading the app"
@ -461,7 +457,7 @@ msgstr ""
"doc/PRE_UPGRADE.md : infos importantes à montrer aux admins avant la mise"
" à jour de l'app"
#: app.py:563
#: app.py:518
msgid ""
"doc/POST_UPGRADE.md: important info to be shown to the admin after "
"upgrading the app"
@ -469,25 +465,25 @@ msgstr ""
"doc/POST_UPGRADE.md : infos importantes à montrer aux admins après la "
"mise à jour de l'app"
#: app.py:571
#: app.py:528
msgid "doc/ADMIN.md: general tips on how to administrate this app"
msgstr "doc/ADMIN.md : indications générales pour administrer l'app"
#: app.py:583
#: app.py:540
msgid "Handle app install URL change (change_url script)"
msgstr "Gérer le changement d'URL d'installation (script change_url)"
#: app.py:586
#: app.py:543
msgid "Should changing the app URL be allowed ? (change_url change)"
msgstr ""
"Faut-il permettre le changement d'URL pour l'application ? (fichier "
"change_url)"
#: app.py:593
#: app.py:548
msgid "Use logrotate for the app logs"
msgstr "Utiliser logrotate pour les journaux de l'app"
#: app.py:596
#: app.py:551
msgid ""
"If the app generates logs, this option permit to handle their archival. "
"Recommended."
@ -495,11 +491,11 @@ msgstr ""
"Si l'application genère des journaux (log), cette option permet d'en "
"gérer l'archivage. Recommandé."
#: app.py:604
#: app.py:559
msgid "Protect the application against brute force attacks (via fail2ban)"
msgstr "Protéger l'application des attaques par force brute (via fail2ban)"
#: app.py:609
#: app.py:562
msgid ""
"If the app generates failed connexions logs, this option allows to "
"automatically banish the related IP after a certain number of failed "
@ -509,27 +505,27 @@ msgstr ""
"option permet de bannir automatiquement les IP au bout d'un certain "
"nombre d'essais de mot de passe. Recommandé."
#: app.py:615
#: app.py:568
msgid "Add a CRON task for this application"
msgstr "Ajouter une tâche CRON pour cette application"
#: app.py:616
#: app.py:569
msgid "Corresponds to some app periodic operations"
msgstr "Corresponds à des opérations périodiques de l'application"
#: app.py:620
#: app.py:573
msgid "Type the CRON file content"
msgstr "Saisissez le contenu du fichier CRON"
#: app.py:629
#: app.py:582
msgid "Regular expression for fail2ban"
msgstr "Expression régulière pour fail2ban"
#: app.py:633
#: app.py:586
msgid "A regular expression"
msgstr "Une expression régulière"
#: app.py:635
#: app.py:588
msgid ""
"Regular expression to check in the log file to activate failban (search "
"for a line that indicates a credentials error)."
@ -538,11 +534,11 @@ msgstr ""
"s'active (cherchez une ligne qui indique une erreur d'identifiants de "
"connexion)."
#: app.py:659
#: app.py:612
msgid "Generator mode"
msgstr "Mode du générateur"
#: app.py:660
#: app.py:613
msgid ""
"In tutorial version, the generated app will contain additionnal comments "
"to ease the understanding. In steamlined version, the generated app will "
@ -552,27 +548,27 @@ msgstr ""
"additionnels pour faciliter la compréhension. En version épurée, "
"l'application générée ne contiendra que le minimum nécessaire."
#: app.py:664
#: app.py:617
msgid "Streamlined version"
msgstr "Version épurée"
#: app.py:665
#: app.py:618
msgid "Tutorial version"
msgstr "Version tutoriel"
#: app.py:671
#: app.py:624
msgid "Previsualise"
msgstr "Prévisualiser"
#: app.py:672
#: app.py:625
msgid "Download the .zip"
msgstr "Télécharger le .zip"
#: app.py:674
#: app.py:627
msgid "Fill with demo values"
msgstr "Remplir avec des valeurs de démonstration"
#: app.py:677
#: app.py:630
msgid ""
"Generate a complete and functionnal minimalistic app that you can iterate"
" from"
@ -580,23 +576,23 @@ msgstr ""
"Générer une application minimaliste complète et fonctionnelle à partir de"
" laquelle itérer"
#: templates/base.html:5
msgid "YunoHost app generator"
msgstr "Générateur d'app YunoHost"
#: templates/base.html:4 templates/index.html:60
msgid "YunoHost package generator"
msgstr "Générateur de paquet YunoHost"
#: templates/index.html:28
msgid "Yunohost application generation form"
msgstr "Formulaire de génération d'une application Yunohost"
#: templates/index.html:80
msgid "The form contains issues"
msgstr "Le formulaire contient des problèmes"
#: templates/index.html:55
#: templates/index.html:88
msgid "1/9 - General information"
msgstr "1/9 - Informations générales"
#: templates/index.html:70
#: templates/index.html:103
msgid "2/9 - Upstream information"
msgstr "2/9 - Informations sur l'upstream"
#: templates/index.html:73
#: templates/index.html:106
msgid ""
"The word 'upstream' refers to the original project that develops and "
"maintains the app"
@ -604,19 +600,19 @@ msgstr ""
"Le terme 'upstream' désigne le projet original qui développe et maintient"
" l'app"
#: templates/index.html:88
#: templates/index.html:121
msgid "3/9 - Integration in YunoHost"
msgstr "3/9 - Intégration dans YunoHost"
#: templates/index.html:104
#: templates/index.html:137
msgid "4/9 - Questions to ask during installation"
msgstr "4/9 - Questions à poser pendant l'installation"
#: templates/index.html:108
#: templates/index.html:141
msgid "This part is meant to indicate the questions that will be asked."
msgstr "Cette partie sert à indiquer les questions qui devront être posées."
#: templates/index.html:110
#: templates/index.html:143
msgid ""
"NB: only standard questions are asked here, it might be required to "
"complete it by hand using other questions as a guide."
@ -625,11 +621,11 @@ msgstr ""
"éventuellement compléter à la main en suivant le modèle des autres "
"questions."
#: templates/index.html:124
#: templates/index.html:157
msgid "5/9 - Resources to initialize"
msgstr "5/9 - Ressources à initialiser"
#: templates/index.html:128
#: templates/index.html:161
msgid ""
"Technical elements configured before launching the 'real' app install "
"script. Usually : creating a system user, downloading app sources, "
@ -642,11 +638,11 @@ msgstr ""
"données, installer des dépendances avec apt, créer une base de données, "
"..."
#: templates/index.html:151
#: templates/index.html:184
msgid "6/9 - Specific technology"
msgstr "6/9 - Technologie spécifique"
#: templates/index.html:156
#: templates/index.html:189
msgid ""
"You probably want to make sure to have 'phpX.Y-fpm' and others "
"'phpX.Y-foobar' libraries listed the apt dependencies earlier (with X.Y "
@ -656,7 +652,7 @@ msgstr ""
"librairies 'phpX.Y-foobar' listées dans les dépendances apt (X.Y étant la"
" version de PHP que vous voulez utiliser)"
#: templates/index.html:157
#: templates/index.html:190
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that interfaces with PHP-FPM"
@ -664,7 +660,7 @@ msgstr ""
"Le brouillon de l'application inclura une configuration nginx qui "
"s'interface avec PHP-FPM"
#: templates/index.html:165
#: templates/index.html:198
msgid ""
"You probably want to make sure to have 'python3' and 'python3-venv' "
"listed in the apt dependencies earlier. Other dependencies should be "
@ -674,7 +670,7 @@ msgstr ""
" listés dans les dépendances apt. Les autres dépendences devront être "
"installées dans un venv"
#: templates/index.html:169
#: templates/index.html:202
msgid ""
"The generated application draft will include an nginx configuration "
"snippet that reverse-proxies to a systemd service using an internal port"
@ -682,18 +678,14 @@ msgstr ""
"Le brouillon de l'application inclura une configuration nginx qui "
"redirige vers le service systemd en utilisant un port interne"
#: templates/index.html:237
#: templates/index.html:270
msgid "7/9 - App configuration"
msgstr "7/9 - Configuration de l'app"
#: templates/index.html:261
#: templates/index.html:294
msgid "8/9 - General and advanced documentation"
msgstr "8/9 - Documentation générale et avancée"
#: templates/index.html:275
#: templates/index.html:308
msgid "9/9 - Advanced options"
msgstr "9/9 - Options avancées"
#~ msgid "Français"
#~ msgstr "French"