From 55e0a098ef3a0ffadfe4ce44fe26dcac490d9c43 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 5 Sep 2023 20:24:38 +0200 Subject: [PATCH] appstore: initialize i18n stuff --- store/.gitignore | 1 + store/README.md | 18 + store/app.py | 71 ++-- store/babel.cfg | 2 + store/requirements.txt | 2 + store/templates/app.html | 34 +- store/templates/base.html | 18 +- store/templates/catalog.html | 40 +-- store/templates/index.html | 9 +- store/templates/wishlist.html | 21 +- store/templates/wishlist_add.html | 22 +- store/translations/fr/LC_MESSAGES/messages.mo | Bin 0 -> 7503 bytes store/translations/fr/LC_MESSAGES/messages.po | 334 ++++++++++++++++++ 13 files changed, 473 insertions(+), 99 deletions(-) create mode 100644 store/babel.cfg create mode 100644 store/translations/fr/LC_MESSAGES/messages.mo create mode 100644 store/translations/fr/LC_MESSAGES/messages.po diff --git a/store/.gitignore b/store/.gitignore index 43c9a261..54539b10 100644 --- a/store/.gitignore +++ b/store/.gitignore @@ -1,2 +1,3 @@ config.toml .stars +messages.pot diff --git a/store/README.md b/store/README.md index 6e832f5b..b60700be 100644 --- a/store/README.md +++ b/store/README.md @@ -29,3 +29,21 @@ And then start the dev server: source venv/bin/activate FLASK_APP=app.py FLASK_ENV=development flask run ``` + +## Translation + +It's based on Flask-Babel : https://python-babel.github.io/ + +``` +source venv/bin/activate +pybabel extract --ignore-dirs venv -F babel.cfg -o messages.pot . + +# If working on a new locale : initialize it (in this example: fr) +pybabel init -i messages.pot -d translations -l fr +# Otherwise, update the existing .po: +pybabel update -i messages.pot -d translations + +# ... translate stuff in translations//LC_MESSAGES/messages.po +# then compile: +pybabel compile -d translations +``` diff --git a/store/app.py b/store/app.py index c8dbefdd..08b0b9bb 100644 --- a/store/app.py +++ b/store/app.py @@ -11,6 +11,8 @@ import json import sys from slugify import slugify from flask import Flask, send_from_directory, render_template, session, redirect, request +from flask_babel import Babel +from flask_babel import gettext as _ from github import Github, InputGitAuthor from .utils import get_catalog, get_wishlist, get_stars, get_app_md_and_screenshots @@ -46,6 +48,26 @@ if config.get("DEBUG"): # This is the secret key used for session signing app.secret_key = config["COOKIE_SECRET"] +AVAILABLE_LANGUAGES = ["en"] + os.listdir("translations") + +def get_locale(): + # try to guess the language from the user accept + # header the browser transmits. We support de/fr/en in this + # example. The best match wins. + return request.accept_languages.best_match(AVAILABLE_LANGUAGES) +babel = Babel(app, locale_selector=get_locale) + +@app.template_filter('localize') +def localize(d): + if not isinstance(d, dict): + return d + else: + locale = get_locale() + if locale in d: + return d[locale] + else: + return d["en"] + ############################################################################### @app.route('/favicon.ico') @@ -55,13 +77,14 @@ def favicon(): @app.route('/') def index(): - return render_template("index.html", user=session.get('user', {}), catalog=get_catalog()) + return render_template("index.html", locale=get_locale(), user=session.get('user', {}), catalog=get_catalog()) @app.route('/catalog') def browse_catalog(): return render_template( "catalog.html", + locale=get_locale(), init_sort=request.args.get("sort"), init_search=request.args.get("search"), init_category=request.args.get("category"), @@ -69,7 +92,7 @@ def browse_catalog(): user=session.get('user', {}), catalog=get_catalog(), timestamp_now=int(time.time()), - stars=get_stars() + stars=get_stars(), ) @@ -82,16 +105,16 @@ def app_info(app_id): get_app_md_and_screenshots(app_folder, infos) - return render_template("app.html", user=session.get('user', {}), app_id=app_id, infos=infos, catalog=get_catalog(), stars=get_stars()) + return render_template("app.html", locale=get_locale(), user=session.get('user', {}), app_id=app_id, infos=infos, catalog=get_catalog(), stars=get_stars()) @app.route('/app//') def star_app(app_id, action): assert action in ["star", "unstar"] if app_id not in get_catalog()["apps"] and app_id not in get_wishlist(): - return f"App {app_id} not found", 404 + return _("App %(app_id) not found", app_id=app_id), 404 if not session.get('user', {}): - return f"You must be logged in to be able to star an app", 401 + return _("You must be logged in to be able to star an app"), 401 app_star_folder = os.path.join(".stars", app_id) app_star_for_this_user = os.path.join(".stars", app_id, session.get('user', {})["id"]) @@ -114,7 +137,7 @@ def star_app(app_id, action): @app.route('/wishlist') def browse_wishlist(): - return render_template("wishlist.html", user=session.get('user', {}), wishlist=get_wishlist(), stars=get_stars()) + return render_template("wishlist.html", locale=get_locale(), user=session.get('user', {}), wishlist=get_wishlist(), stars=get_stars()) @app.route('/wishlist/add', methods=['GET', 'POST']) @@ -123,8 +146,8 @@ def add_to_wishlist(): user = session.get('user', {}) if not user: - errormsg = "You must be logged in to submit an app to the wishlist" - return render_template("wishlist_add.html", user=session.get('user', {}), successmsg=None, errormsg=errormsg) + errormsg = _("You must be logged in to submit an app to the wishlist") + return render_template("wishlist_add.html", locale=get_locale(), user=session.get('user', {}), successmsg=None, errormsg=errormsg) name = request.form['name'].strip().replace("\n", "") description = request.form['description'].strip().replace("\n", "") @@ -132,19 +155,19 @@ def add_to_wishlist(): website = request.form['website'].strip().replace("\n", "") checks = [ - (len(name) >= 3, "App name should be at least 3 characters"), - (len(name) <= 30, "App name should be less than 30 characters"), - (len(description) >= 5, "App name should be at least 5 characters"), - (len(description) <= 100, "App name should be less than 100 characters"), - (len(upstream) >= 10, "Upstream code repo URL should be at least 10 characters"), - (len(upstream) <= 150, "Upstream code repo URL should be less than 150 characters"), - (len(website) <= 150, "Website URL should be less than 150 characters"), - (re.match(r"^[\w\.\-\(\)\ ]+$", name), "App name contains special characters"), + (len(name) >= 3, _("App name should be at least 3 characters")), + (len(name) <= 30, _("App name should be less than 30 characters")), + (len(description) >= 5, _("App description should be at least 5 characters")), + (len(description) <= 100, _("App description should be less than 100 characters")), + (len(upstream) >= 10, _("Upstream code repo URL should be at least 10 characters")), + (len(upstream) <= 150, _("Upstream code repo URL should be less than 150 characters")), + (len(website) <= 150, _("Website URL should be less than 150 characters")), + (re.match(r"^[\w\.\-\(\)\ ]+$", name), _("App name contains special characters")), ] for check, errormsg in checks: if not check: - return render_template("wishlist_add.html", user=session.get('user', {}), successmsg=None, errormsg=errormsg) + return render_template("wishlist_add.html", locale=get_locale(), user=session.get('user', {}), successmsg=None, errormsg=errormsg) slug = slugify(name) github = Github(config["GITHUB_TOKEN"]) @@ -156,7 +179,7 @@ def add_to_wishlist(): new_wishlist = toml.loads(current_wishlist_rawtoml) if slug in new_wishlist: - return render_template("wishlist_add.html", user=session.get('user', {}), successmsg=None, errormsg=f"An entry with the name {slug} already exists in the wishlist") + return render_template("wishlist_add.html", locale=get_locale(), user=session.get('user', {}), successmsg=None, errormsg=_("An entry with the name %(slug) already exists in the wishlist", slug=slug)) new_wishlist[slug] = { "name": name, @@ -175,8 +198,8 @@ def add_to_wishlist(): except exception as e: print("... Failed to create branch ?") print(e) - errormsg = "Failed to create the pull request to add the app to the wishlist ... please report the issue to the yunohost team" - return render_template("wishlist_add.html", user=session.get('user', {}), successmsg=None, errormsg=errormsg) + errormsg = _("Failed to create the pull request to add the app to the wishlist ... please report the issue to the yunohost team") + return render_template("wishlist_add.html", locale=get_locale(), user=session.get('user', {}), successmsg=None, errormsg=errormsg) message = f"Add {name} to wishlist" repo.update_file( @@ -206,10 +229,12 @@ Proposed by **{session['user']['username']}** title=message, body=body, head=new_branch, base="app-store" # FIXME app-store -> repo.default_branch ) - successmsg = f"Your proposed app has succesfully been submitted. It must now be validated by the YunoHost team. You can track progress here: https://github.com/YunoHost/apps/pull/{pr.number}" - return render_template("wishlist_add.html", user=session.get('user', {}), successmsg=successmsg) + url = f"https://github.com/YunoHost/apps/pull/{pr.number}" + + successmsg = _("Your proposed app has succesfully been submitted. It must now be validated by the YunoHost team. You can track progress here: %(url)s", url=url) + return render_template("wishlist_add.html", locale=get_locale(), user=session.get('user', {}), successmsg=successmsg) else: - return render_template("wishlist_add.html", user=session.get('user', {}), successmsg=None, errormsg=None) + return render_template("wishlist_add.html", locale=get_locale(), user=session.get('user', {}), successmsg=None, errormsg=None) ############################################################################### diff --git a/store/babel.cfg b/store/babel.cfg new file mode 100644 index 00000000..759e805a --- /dev/null +++ b/store/babel.cfg @@ -0,0 +1,2 @@ +[python: **.py] +[jinja2: **/templates/**.html] diff --git a/store/requirements.txt b/store/requirements.txt index 996af51b..d572e041 100644 --- a/store/requirements.txt +++ b/store/requirements.txt @@ -5,3 +5,5 @@ toml pycmarkgfm gunicorn emoji +Babel +Flask-Babel diff --git a/store/templates/app.html b/store/templates/app.html index ce9ff0f2..f69337e1 100644 --- a/store/templates/app.html +++ b/store/templates/app.html @@ -1,4 +1,3 @@ -{% set locale = 'en' %} {% extends "base.html" %} {% block main %}
@@ -15,7 +14,7 @@

{{ infos["manifest"]["name"] }}

{% if infos['category'] %} - {{ catalog['categories'][infos['category']]['title'][locale].lower() }} + {{ catalog['categories'][infos['category']]['title']|localize|lower }} {% endif %}
@@ -65,7 +64,7 @@ href="{{ infos["manifest"]["upstream"]["demo"] }}" > - Try the demo + {{ _("Try the demo") }} {% endif %}
@@ -77,9 +76,9 @@ -

Current version: {{ infos["manifest"]["version"] }}

+

{{ _("Current version: %(version)s", version=infos["manifest"]["version"]) }}

{% if infos["potential_alternative_to"] %} -

Potential alternative to: {{ infos["potential_alternative_to"]|join(', ') }}

+

{{ _("Potential alternative to: %(alternatives)s", alternatives=infos["potential_alternative_to"]|join(', ')) }}

{% endif %}
{{ infos["full_description_html"]|safe }}
@@ -90,44 +89,45 @@ {% if infos["manifest"]["integration"]["architectures"] != "all" %}
- This app is only compatible with these specific architectures : {{ infos["manifest"]["integration"]["architectures"]|join(', ') }} + {{ _("This app is only compatible with these specific architectures: %(archs)s", archs=infos["manifest"]["integration"]["architectures"]|join(', ')) }}
{% endif %} {% if infos["manifest"]["integration"]["ram"]["build_binary"] >= 500 * 1024 * 1024 %}
- This app requires an unusual amount of RAM to build : {{ infos["manifest"]["integration"]["ram"]["build"] }} + {{ _("This app requires an unusual amount of RAM to install: %(ram)s", ram=infos["manifest"]["integration"]["ram"]["build"]) }}
{% endif %} {% if infos["pre_install_html"] %}
-

Important infos before installing

+

{{ _("Important infos before installing") }}

{{ infos["pre_install_html"] | safe }}
{% endif %} {% if infos["antifeatures"] %} -

Anti-features

(This app has features you may not like)

+

{{ _("Anti-features") }}

+

{{ _("(This app has features you may not like)") }}

    {% for antifeature in infos["antifeatures"] %} -
  • {{ catalog['antifeatures'][antifeature]['description'][locale] }}
  • +
  • {{ catalog['antifeatures'][antifeature]['description']|localize }}
  • {% endfor %}
{% endif %} -

Useful links

+

{{ _("Useful links") }}

{% set upstream = infos["manifest"]["upstream"] %} - License : {{ upstream.license }} - {% if upstream.website %} Official website{% endif %} - {% if upstream.admindoc %} Official admin documentation{% endif %} - {% if upstream.userdoc %} Official user documentation{% endif %} - {% if upstream.code %} Official code repository{% endif %} - YunoHost package repository + {{ _("License: %(license)s", license=upstream.license) }} + {% if upstream.website %} {{ _(" Official website") }}{% endif %} + {% if upstream.admindoc %} {{ _("Official admin documentation") }}{% endif %} + {% if upstream.userdoc %} {{ _("Official user documentation") }}{% endif %} + {% if upstream.code %} {{ _("Official code repository") }}{% endif %} + {{ _("YunoHost package repository") }}
{% endblock %} diff --git a/store/templates/base.html b/store/templates/base.html index 1f643e86..7c61049c 100644 --- a/store/templates/base.html +++ b/store/templates/base.html @@ -1,8 +1,8 @@ - + - YunoHost app store + {{ _("YunoHost app store") }} @@ -53,7 +53,7 @@ class="flex h-16 items-center gap-8 px-4 sm:px-6 lg:px-8" > - Home + {{ _("Home") }} @@ -62,13 +62,13 @@ @@ -81,21 +81,21 @@ href="https://yunohost.org/docs/" > - YunoHost documentation + {{ _("YunoHost documentation") }} {% if not user %} {% else %} diff --git a/store/templates/catalog.html b/store/templates/catalog.html index 95add02c..1b786131 100644 --- a/store/templates/catalog.html +++ b/store/templates/catalog.html @@ -1,5 +1,3 @@ -{% set locale = 'en' %} - {% macro appCard(app, infos, timestamp_now, catalog) -%} {% set this_app_stars = stars.get(app, {})|length %} @@ -53,14 +51,14 @@

- {{ infos['manifest']['description']['en'] }} + {{ infos['manifest']['description']|localize }}

{% if infos['category'] %} - {{ catalog['categories'][infos['category']]['title'][locale].lower() }} + {{ catalog['categories'][infos['category']]['title']|localize|lower }} {% endif %} @@ -73,18 +71,18 @@ {% block main %}

- Application Catalog + {{ _("Application Catalog") }}

- + @@ -100,10 +98,10 @@ id="selectcategory" class="w-full rounded-md border-gray-200 shadow-sm sm:test-sm px-2 py-1.5" > - + {% for id, category in catalog['categories'].items() %} - {{ category['title'][locale] }} - + {{ category['title']|localize }} + {% endfor %}
@@ -111,15 +109,15 @@
- Sort by + {{ _("Sort by") }}
@@ -132,7 +130,7 @@ - Show only apps you starred + {{ _("Show only apps you starred") }}
@@ -148,21 +146,21 @@

- Applications currently broken or low-quality + {{ _("Applications currently flagged as broken or low-quality") }}

- These are apps which failed our automatic tests.
- This is usually a temporary situation which requires packagers to fix something in the app. + {{ _("These are apps which failed our automatic tests.") }}
+ {{ _("This is usually a temporary situation which requires packagers to fix something in the app.") }}

diff --git a/store/templates/index.html b/store/templates/index.html index 0a054163..ec4bba32 100644 --- a/store/templates/index.html +++ b/store/templates/index.html @@ -1,11 +1,10 @@ {% extends "base.html" %} {% block main %} -{% set locale = 'en' %}

- Application Store + {{ _("Application Store") }}

@@ -16,7 +15,7 @@ class="h-full relative block overflow-hidden hover:bg-gray-200 pt-12" >

- Browse all applications + {{ _("Browse all applications") }}

@@ -28,10 +27,10 @@ >

- {{ category['title'][locale] }} + {{ category['title']|localize }}

- {{ category['description'][locale] }} + {{ category['description']|localize }}

diff --git a/store/templates/wishlist.html b/store/templates/wishlist.html index e967ae67..75511908 100644 --- a/store/templates/wishlist.html +++ b/store/templates/wishlist.html @@ -2,19 +2,19 @@ {% block main %}

- Application Wishlist + {{ _("Application Wishlist") }}

- + @@ -23,33 +23,28 @@
- + - Add an app to the wishlist + {{ _("Suggest an app") }}
-
- + diff --git a/store/templates/wishlist_add.html b/store/templates/wishlist_add.html index cd57a9f6..120482f4 100644 --- a/store/templates/wishlist_add.html +++ b/store/templates/wishlist_add.html @@ -2,7 +2,7 @@ {% block main %}

- Add an application to the wishlist + {{ _("Suggest an application to be added to YunoHost's catalog") }}

@@ -23,7 +23,7 @@ {% endif %} @@ -31,10 +31,10 @@ @@ -50,26 +50,26 @@ - + - + - Please be concise and focus on what the app does. No need to repeat "[App] is ...". No need to state that it is free/open-source or self-hosted (otherwise it wouldn't be packaged for YunoHost). Avoid marketing stuff like 'the most', or vague properties like 'easy', 'simple', 'lightweight'. + {{ _("Please be concise and focus on what the app does.") }} {{ _("No need to repeat '[App] is ...'. No need to state that it is free/open-source or self-hosted (otherwise it wouldn't be packaged for YunoHost). Avoid marketing stuff like 'the most', or vague properties like 'easy', 'simple', 'lightweight'.") }} - + - + - Please do not just copy-paste the code repository URL. If the project has no proper website, then leave the field empty. + {{ _("Please *do not* just copy-paste the code repository URL. If the project has no proper website, then leave the field empty.") }} diff --git a/store/translations/fr/LC_MESSAGES/messages.mo b/store/translations/fr/LC_MESSAGES/messages.mo new file mode 100644 index 0000000000000000000000000000000000000000..e32c98a72848092979423af5820b07373c04c6e5 GIT binary patch literal 7503 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, 2023. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2023-09-05 20:41+0200\n" +"PO-Revision-Date: 2023-09-05 19:50+0200\n" +"Last-Translator: FULL NAME \n" +"Language: fr\n" +"Language-Team: fr \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.12.1\n" + +#: app.py:115 +msgid "App %(app_id) not found" +msgstr "L'app %(app_id) n'a pas été trouvée" + +#: app.py:117 +msgid "You must be logged in to be able to star an app" +msgstr "Vous devez être connecté·e pour mettre une app en favoris" + +#: app.py:149 +msgid "You must be logged in to submit an app to the wishlist" +msgstr "Vous devez être connecté·e pour proposer une app pour la liste de souhait" + +#: app.py:158 +msgid "App name should be at least 3 characters" +msgstr "Le nom d'app devrait contenir au moins 3 caractères" + +#: app.py:159 +msgid "App name should be less than 30 characters" +msgstr "Le nom d'app devrait contenir moins de 30 caractères" + +#: app.py:160 +msgid "App description should be at least 5 characters" +msgstr "La description de l'app devrait contenir au moins 5 caractères" + +#: app.py:161 +msgid "App description should be less than 100 characters" +msgstr "La description de l'app devrait contenir moins de 100 caractères" + +#: app.py:162 +msgid "Upstream code repo URL should be at least 10 characters" +msgstr "L'URL du dépôt de code devrait contenir au moins 10 caractères" + +#: app.py:163 +msgid "Upstream code repo URL should be less than 150 characters" +msgstr "L'URL du dépôt de code devrait contenir moins de 150 caractères" + +#: app.py:164 +msgid "Website URL should be less than 150 characters" +msgstr "L'URL du site web devrait contenir moins de 150 caractères" + +#: app.py:165 +msgid "App name contains special characters" +msgstr "Le nom de l'app contiens des caractères spéciaux" + +#: app.py:182 +msgid "An entry with the name %(slug) already exists in the wishlist" +msgstr "Une entrée nommée $(slug) existe déjà dans la liste de souhait" + +#: app.py:201 +msgid "" +"Failed to create the pull request to add the app to the wishlist ... " +"please report the issue to the yunohost team" +msgstr "" +"Échec de la création de la demande d'intégration de l'app dans la liste " +"de souhait ... merci de rapport le problème à l'équipe YunoHost" + +#: app.py:234 +msgid "" +"Your proposed app has succesfully been submitted. It must now be " +"validated by the YunoHost team. You can track progress here: %(url)s" +msgstr "" +"Un demande d'intégration à la liste de souhait a bien été créée pour " +"cette app. Elle doit maintenant être validée par l'équipe YunoHost. Vous " +"pouvez suivre cette demande ici: %(url)s" + +#: templates/app.html:67 +msgid "Try the demo" +msgstr "Essayer la démo" + +#: templates/app.html:79 +#, python-format +msgid "Current version: %(version)s" +msgstr "Version actuelle: %(version)s" + +#: templates/app.html:81 +#, python-format +msgid "Potential alternative to: %(alternatives)s" +msgstr "Alternative potentielle à : %(alternatives)s" + +#: templates/app.html:92 +#, python-format +msgid "This app is only compatible with these specific architectures: %(archs)s" +msgstr "" +"Cette app est uniquement compatible avec les architectures suivantes : " +"%(archs)s" + +#: templates/app.html:98 +#, python-format +msgid "This app requires an unusual amount of RAM to install: %(ram)s" +msgstr "" +"Cette app requiert une quantité inhabituelle de RAM pour être installée :" +" %(ram)s" + +#: templates/app.html:104 +msgid "Important infos before installing" +msgstr "Informations importantes avant l'installation" + +#: templates/app.html:110 +msgid "Anti-features" +msgstr "Anti-fonctionnalités" + +#: templates/app.html:111 +msgid "(This app has features you may not like)" +msgstr "(Cette app a des spécificités que vous pourriez ne pas aimer)" + +#: templates/app.html:122 +msgid "Useful links" +msgstr "Liens utiles" + +#: templates/app.html:125 +#, python-format +msgid "License: %(license)s" +msgstr "Licence: %(license)s" + +#: templates/app.html:126 +msgid " Official website" +msgstr "Site officiel" + +#: templates/app.html:127 +msgid "Official admin documentation" +msgstr "Documentation officielle pour les admins" + +#: templates/app.html:128 +msgid "Official user documentation" +msgstr "Documentation officielle pour les utilisateur·ice·s" + +#: templates/app.html:129 +msgid "Official code repository" +msgstr "Dépôt de code officiel" + +#: templates/app.html:130 +msgid "YunoHost package repository" +msgstr "Dépôt de code du paquet YunoHost" + +#: templates/base.html:5 +msgid "YunoHost app store" +msgstr "Store d'apps de YunoHost" + +#: templates/base.html:56 +msgid "Home" +msgstr "Accueil" + +#: templates/base.html:65 +msgid "Catalog" +msgstr "Catalogue" + +#: templates/base.html:71 +msgid "Wishlist" +msgstr "Liste de souhaits" + +#: templates/base.html:84 +msgid "YunoHost documentation" +msgstr "Documentation YunoHost" + +#: templates/base.html:91 +msgid "Login using YunoHost's forum" +msgstr "Se connecter via le forum YunoHost" + +#: templates/base.html:98 +msgid "Menu" +msgstr "Menu" + +#: templates/base.html:123 +msgid "Toggle menu" +msgstr "Activer le menu" + +#: templates/catalog.html:74 +msgid "Application Catalog" +msgstr "Catalogue d'applications" + +#: templates/catalog.html:80 templates/wishlist.html:12 +msgid "Search" +msgstr "Recherche" + +#: templates/catalog.html:85 templates/wishlist.html:17 +msgid "Search for..." +msgstr "Rechercher..." + +#: templates/catalog.html:101 +msgid "All apps" +msgstr "Toutes les apps" + +#: templates/catalog.html:112 +msgid "Sort by" +msgstr "Trier par" + +#: templates/catalog.html:118 +msgid "Alphabetical" +msgstr "Alphabétique" + +#: templates/catalog.html:119 +msgid "Newest" +msgstr "Nouveauté" + +#: templates/catalog.html:120 templates/wishlist.html:47 +msgid "Popularity" +msgstr "Popularité" + +#: templates/catalog.html:133 +msgid "Show only apps you starred" +msgstr "Montrer uniquement mes favoris" + +#: templates/catalog.html:149 +msgid "No results found." +msgstr "Aucun résultat trouvé." + +#: templates/catalog.html:152 +msgid "Not finding what you are looking for?" +msgstr "Vous ne trouvez pas ce que vous cherchez ?" + +#: templates/catalog.html:153 +msgid "Checkout the wishlist!" +msgstr "Jetez un oeil à la liste de souhait !" + +#: templates/catalog.html:159 +msgid "Applications currently flagged as broken or low-quality" +msgstr "Applications actuellement marquées comme cassées ou de mauvaise qualité" + +#: templates/catalog.html:162 +msgid "These are apps which failed our automatic tests." +msgstr "Il s'agit d'apps qui n'ont pas validé nos tests automatisés." + +#: templates/catalog.html:163 +msgid "" +"This is usually a temporary situation which requires packagers to fix " +"something in the app." +msgstr "" +"Il s'agit généralement d'une situation temporaire qui requiert que des " +"packageur·euse·s corrigent un problème dans l'app." + +#: templates/index.html:7 +msgid "Application Store" +msgstr "Store d'application" + +#: templates/index.html:18 +msgid "Browse all applications" +msgstr "Toutes les applications" + +#: templates/wishlist.html:5 +msgid "Application Wishlist" +msgstr "Liste de souhait d'applications" + +#: templates/wishlist.html:28 +msgid "Suggest an app" +msgstr "Suggérer une app" + +#: templates/wishlist.html:40 templates/wishlist_add.html:53 +msgid "Name" +msgstr "Nom" + +#: templates/wishlist.html:43 templates/wishlist_add.html:56 +msgid "Description" +msgstr "Description" + +#: templates/wishlist_add.html:5 +msgid "Suggest an application to be added to YunoHost's catalog" +msgstr "Suggérer une application à ajouter dans le catalogue de YunoHost" + +#: templates/wishlist_add.html:26 +msgid "You must first login to be allowed to submit an app to the wishlist" +msgstr "Vous devez être connecté·e pour proposer une app pour la liste de souhait" + +#: templates/wishlist_add.html:34 +msgid "Please check the license of the app your are proposing" +msgstr "Merci de vérifier la licence de l'app que vous proposez" + +#: templates/wishlist_add.html:37 +msgid "" +"The YunoHost project will only package free/open-source software (with " +"possible case-by-case exceptions for apps which are not-totally-free)" +msgstr "" +"Le projet YunoHost intègrera uniquement des logiciels libre/open-source " +"(avec quelques possibles exceptions au cas-par-cas pour des apps qui ne " +"sont pas entièrement libres)" + +#: templates/wishlist_add.html:58 +msgid "Please be concise and focus on what the app does." +msgstr "Prière de rester concis et de se concentrer sur ce que l'app fait." + +#: templates/wishlist_add.html:58 +msgid "" +"No need to repeat '[App] is ...'. No need to state that it is free/open-" +"source or self-hosted (otherwise it wouldn't be packaged for YunoHost). " +"Avoid marketing stuff like 'the most', or vague properties like 'easy', " +"'simple', 'lightweight'." +msgstr "" +"Il n'est pas nécessaire de répéter '[App] est ...', ni que l'app est " +"libre/open-source (sinon, elle ne serait pas intégrable au catalogue). " +"Évitez les formulations marketing type 'le meilleur', ou les propriétés " +"vagues telles que 'facile', 'simple', 'léger'." + +#: templates/wishlist_add.html:60 +msgid "Project code repository" +msgstr "Dépôt de code officiel" + +#: templates/wishlist_add.html:63 +msgid "Project website" +msgstr "Site officiel" + +#: templates/wishlist_add.html:65 +msgid "" +"Please *do not* just copy-paste the code repository URL. If the project " +"has no proper website, then leave the field empty." +msgstr "" +"Prière de ne pas juste copier-coller l'URL du dépôt de code. Si le projet" +" n'a pas de vrai site web, laissez le champ vide." + +#: templates/wishlist_add.html:72 +msgid "Submit" +msgstr "Envoyer" + +#~ msgid "Add an app to the wishlist" +#~ msgstr "Ajouter une app à la liste" +
- Name + {{ _("Name") }} - Description + {{ _("Description") }} Popularity{{ _("Popularity") }}