diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml new file mode 100644 index 00000000..1bbedb57 --- /dev/null +++ b/.github/workflows/i18n.yml @@ -0,0 +1,29 @@ +name: Autoreformat locale files +on: + push: + branches: + - dev +jobs: + i18n: + name: Autoreformat locale files + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Apply reformating scripts + id: action_reformat + run: | + python3 test/remove_stale_i18n_strings.py + python3 test/autofix_locale_format.py + python3 test/reformat_locales.py + git diff -w --exit-code + - name: Create Pull Request + if: ${{ failure() }} + uses: peter-evans/create-pull-request@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + title: "Reformat locale files" + commit-message: ":robot: Reformat locale files" + body: | + Automatic pull request using the scripts in `test/` + base: ${{ github.head_ref }} + branch: actions/i18nreformat diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 291d1a2b..2c4cf7f5 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -45,3 +45,5 @@ jobs: pip install tox tox-gh-actions - name: Linter run: tox -e py39-invalidcode + - name: Mypy + run: tox -e py39-mypy diff --git a/README.md b/README.md index 3fea31b9..ee0fe44a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@
+![Version](https://img.shields.io/github/v/tag/yunohost/moulinette?label=version&sort=semver) [![Tests status](https://github.com/YunoHost/moulinette/actions/workflows/tox.yml/badge.svg)](https://github.com/YunoHost/moulinette/actions/workflows/tox.yml) [![GitHub license](https://img.shields.io/github/license/YunoHost/moulinette)](https://github.com/YunoHost/moulinette/blob/dev/LICENSE) diff --git a/debian/changelog b/debian/changelog index ca55ba2c..35e4cce4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,29 @@ moulinette (11.0.0~alpha) unstable; urgency=low -- Alexandre Aubin Fri, 05 Feb 2021 00:02:38 +0100 +moulinette (4.3.1) testing; urgency=low + + - [mod] Rework cli prompt mecanisc ([#303](https://github.com/YunoHost/moulinette/pull/303)) + - [i18n] Translations updated for Indonesian, Russian, Turkish + + Thanks to all contributors <3 ! (Éric Gaspar, liimee) + + -- Alexandre Aubin Wed, 29 Sep 2021 22:37:28 +0200 + +moulinette (4.3.0) testing; urgency=low + + - [enh] Allow file type in actionmaps ([#258](https://github.com/YunoHost/moulinette/pull/258)) + - [refactor] Rework and externalize the authenticator system ([#270](https://github.com/YunoHost/moulinette/pull/270)) + - [security] Add httponly to API cookies (8562c05d) + - [enh] Add prefill and multiline in prompt ([#290](https://github.com/YunoHost/moulinette/pull/290), 08f7866f) + - [enh] Support bytes/stream in write_to_file (6e714314) + - [fix] Various technical bugs in utils/process.py (fdc61c91, 4eb60dac, 3741101d) + - [i18n] Translations updated for French, Galician, Persian, Ukrainian + + Thanks to all contributors <3 ! (Éric Gaspar, José M, Kay0u, ljf, Parviz Homayun, ppr, Tymofii-Lytvynenko) + + -- Alexandre Aubin Sun, 19 Sep 2021 21:19:43 +0200 + moulinette (4.2.4) stable; urgency=low - [fix] Avoid warning and use safeloader ([#281](https://github.com/YunoHost/moulinette/pull/281)) diff --git a/debian/control b/debian/control index 42efa1ff..a6d14b00 100644 --- a/debian/control +++ b/debian/control @@ -14,7 +14,9 @@ Depends: ${misc:Depends}, ${python3:Depends}, python3-gevent-websocket, python3-toml, python3-psutil, - python3-tz + python3-tz, + python3-prompt-toolkit, + python3-pygments Breaks: yunohost (<< 4.1) Description: prototype interfaces with ease in Python Quickly and easily prototype interfaces for your application. diff --git a/locales/ar.json b/locales/ar.json index cea71d1a..0bb8ed53 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -1,8 +1,6 @@ { "argument_required": "المُعامِل '{argument}' مطلوب", "authentication_required": "المصادقة مطلوبة", - "authentication_required_long": "المصادقة مطلوبة قبل القيام بهذا الإجراء", - "colon": "{}: ", "confirm": "تأكيد {prompt}", "deprecated_command": "'{prog} {command}' تم التخلي عنه و سوف تتم إزالته مستقبلا", "deprecated_command_alias": "'{prog} {old}' تم التخلي عنه و سوف يتم إزالته مستقبلا، إستخدم '{prog} {new}' بدلا من ذلك", @@ -11,10 +9,7 @@ "folder_exists": "إنّ المجلد موجود من قبل : '{path}'", "instance_already_running": "هناك بالفعل عملية YunoHost جارية. الرجاء الانتظار حتى ينتهي الأمر قبل تشغيل آخر.", "invalid_argument": "المُعامِل غير صالح '{argument}': {error}", - "invalid_password": "كلمة السر خاطئة", "invalid_usage": "إستعمال غير صالح، إستخدم --help لعرض المساعدة", - "ldap_attribute_already_exists": "الخاصية '{attribute}' موجودة مسبقا و تحمل القيمة '{value}'", - "ldap_server_down": "لا يمكن الإتصال بخادم LDAP", "logged_in": "مُتّصل", "logged_out": "تم تسجيل خروجك", "not_logged_in": "لم تقم بعدُ بتسجيل دخولك", @@ -25,7 +20,6 @@ "server_already_running": "هناك خادم يشتغل على ذاك المنفذ", "success": "تم بنجاح !", "unable_authenticate": "تعذرت المصادقة", - "unable_retrieve_session": "تعذرت مواصلة الجلسة بسبب '{exception}'", "unknown_group": "الفريق '{group}' مجهول", "unknown_user": "المستخدم '{user}' مجهول", "values_mismatch": "القيمتين غير متطابقتين", @@ -47,8 +41,5 @@ "info": "معلومة:", "warn_the_user_about_waiting_lock_again": "جارٍ الانتظار…", "warn_the_user_that_lock_is_acquired": "لقد انتهى تنفيذ ذاك الأمر للتوّ ، جارٍ تنفيذ هذا الأمر", - "warn_the_user_about_waiting_lock": "هناك أمر لـ YunoHost قيد التشغيل حاليا. في انتظار انتهاء تنفيذه قبل تشغيل التالي", - "ldap_server_is_down_restart_it": "إنّ خدمة LDAP غير مشغّلة ، نحن بصدد محاولة إعادة تشغيلها…", - "session_expired": "لقد انتهت مدة صلاحية الجلسة. رجاءً أعد الإستيثاق.", - "invalid_token": "إنّ الرمز المميز غير صالح - يرجى الإستيثاق" + "warn_the_user_about_waiting_lock": "هناك أمر لـ YunoHost قيد التشغيل حاليا. في انتظار انتهاء تنفيذه قبل تشغيل التالي" } \ No newline at end of file diff --git a/locales/ca.json b/locales/ca.json index 67b530d5..d3afdcad 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -1,8 +1,6 @@ { "argument_required": "Es requereix l'argument {argument}", "authentication_required": "Es requereix autenticació", - "authentication_required_long": "Es requereix autenticació per realitzar aquesta tasca", - "colon": "{}: ", "confirm": "Confirmar{prompt}", "deprecated_command": "{prog}{command}és obsolet i es desinstal·larà en el futur", "deprecated_command_alias": "{prog}{old}és obsolet i es desinstal·larà en el futur, utilitzeu {prog}{new}en el seu lloc", @@ -11,10 +9,7 @@ "folder_exists": "La carpeta ja existeix: '{path}'", "instance_already_running": "Ja hi ha una operació de YunoHost en curs. Espereu a que s'acabi abans d'executar-ne una altra.", "invalid_argument": "Argument invàlid '{argument}': {error}", - "invalid_password": "Contrasenya invàlida", "invalid_usage": "Utilització invàlida, utilitzeu --help per veure l'ajuda", - "ldap_attribute_already_exists": "L'atribut '{attribute}' ja existeix amb el valor '{value}'", - "ldap_server_down": "No s'ha pogut connectar amb el servidor LDAP", "logged_in": "Sessió iniciada", "logged_out": "Sessió tancada", "not_logged_in": "No ha iniciat sessió", @@ -25,7 +20,6 @@ "server_already_running": "Ja s'està executant un servidor en aquest port", "success": "Èxit!", "unable_authenticate": "No s'ha pogut autenticar", - "unable_retrieve_session": "No s'ha pogut recuperar la sessió a causa de «{exception}»", "unknown_group": "Grup '{group}' desconegut", "unknown_user": "Usuari '{user}' desconegut", "values_mismatch": "Els valors no coincideixen", @@ -48,8 +42,5 @@ "corrupted_toml": "El fitxer TOML ha estat corromput en la lectura des de {ressource} (motiu: {error})", "warn_the_user_about_waiting_lock": "Hi ha una altra ordre de YunoHost en execució, s'executarà aquesta ordre un cop l'anterior hagi acabat", "warn_the_user_about_waiting_lock_again": "Encara en espera…", - "warn_the_user_that_lock_is_acquired": "L'altra ordre tot just ha acabat, ara s'executarà aquesta ordre", - "invalid_token": "Testimoni no vàlid - torneu-vos a autenticar", - "ldap_server_is_down_restart_it": "El servei LDAP està caigut, s'està intentant tornar-lo a engegar…", - "session_expired": "La sessió a expirat. Torneu-vos a autenticar." + "warn_the_user_that_lock_is_acquired": "L'altra ordre tot just ha acabat, ara s'executarà aquesta ordre" } \ No newline at end of file diff --git a/locales/ckb.json b/locales/ckb.json index 0967ef42..9e26dfee 100644 --- a/locales/ckb.json +++ b/locales/ckb.json @@ -1 +1 @@ -{} +{} \ No newline at end of file diff --git a/locales/cmn.json b/locales/cmn.json index b3304180..447a65cd 100644 --- a/locales/cmn.json +++ b/locales/cmn.json @@ -1,8 +1,6 @@ { "argument_required": "参数“{argument}”是必须的", "authentication_required": "需要验证", - "authentication_required_long": "此操作需要验证", - "colon": "{} ", "confirm": "确认 {prompt}", "deprecated_command": "{prog}{command}已经放弃使用,将来会删除", "deprecated_command_alias": "{prog}{old}已经放弃使用,将来会删除,请使用{prog}{new}代替", @@ -12,10 +10,7 @@ "info": "信息:", "instance_already_running": "已经有一个YunoHost操作正在运行。 请等待它完成再运行另一个。", "invalid_argument": "参数错误{argument}:{error}", - "invalid_password": "密码错误", "invalid_usage": "用法错误,输入 --help 查看帮助信息", - "ldap_attribute_already_exists": "参数{attribute}已赋值{value}", - "ldap_server_down": "无法连接LDAP服务器", "logged_in": "登录", "logged_out": "登出", "not_logged_in": "您未登录", @@ -26,7 +21,6 @@ "server_already_running": "服务已运行在指定端口", "success": "成功!", "unable_authenticate": "认证失败", - "unable_retrieve_session": "由于“ {exception}”,无法检索会话", "unknown_group": "未知组{group}", "unknown_user": "未知用户{user}", "values_mismatch": "值不匹配", @@ -48,8 +42,5 @@ "warn_the_user_that_lock_is_acquired": "另一个命令刚刚完成,现在启动此命令", "warn_the_user_about_waiting_lock_again": "还在等...", "warn_the_user_about_waiting_lock": "目前正在运行另一个YunoHost命令,我们在运行此命令之前等待它完成", - "corrupted_toml": "从{ressource:s}读取的TOML损坏(原因:{error:s})", - "invalid_token": "令牌无效-请进行身份验证", - "ldap_server_is_down_restart_it": "LDAP服务已下线,正在尝试重启服务……", - "session_expired": "会话已过期。请重新进行身份验证。" -} + "corrupted_toml": "从{ressource:s}读取的TOML损坏(原因:{error:s})" +} \ No newline at end of file diff --git a/locales/cs.json b/locales/cs.json index 6afbafcd..027cffbd 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -1,7 +1,6 @@ { "password": "Heslo", "logged_out": "Jste odhlášen/a", - "ldap_server_is_down_restart_it": "LDAP služba neběží, probíhá pokus o její nastartování...", "warn_the_user_that_lock_is_acquired": "Předchozí operace dokončena, nyní spouštíme tuto", "warn_the_user_about_waiting_lock_again": "Stále čekáme...", "warn_the_user_about_waiting_lock": "Jiná YunoHost operace právě probíhá, před spuštěním této čekáme na její dokončení", @@ -24,8 +23,6 @@ "values_mismatch": "Hodnoty nesouhlasí", "unknown_user": "Neznámý '{user}' uživatel", "unknown_group": "Neznámá '{group}' skupina", - "session_expired": "Sezení vypršelo. Přihlašte se znovu, prosím.", - "unable_retrieve_session": "Není možné obdržet sezení neboť '{exception}'", "unable_authenticate": "Není možné ověřit", "success": "Zadařilo se!", "server_already_running": "Na tomto portu je server již provozován", @@ -34,11 +31,7 @@ "operation_interrupted": "Operace přerušena", "not_logged_in": "Nejste přihlášen", "logged_in": "Přihlášení", - "ldap_server_down": "Spojení s LDAP serverem se nezdařilo", - "ldap_attribute_already_exists": "Atribut '{attribute}' již obsahuje hodnotu '{value}'", "invalid_usage": "Nesprávné použití, pass --help pro zobrazení nápovědy", - "invalid_token": "Nesprávný token - ověřte se prosím", - "invalid_password": "Nesprávné heslo", "invalid_argument": "Nesprávný argument '{argument}': {error}", "instance_already_running": "Právě probíhá jiná YunoHost operace. Před spuštěním další operace vyčkejte na její dokončení.", "info": "Info:", @@ -48,8 +41,6 @@ "deprecated_command_alias": "'{prog} {old}' je zastaralý a bude odebrán v budoucích verzích, použijte '{prog} {new}'", "deprecated_command": "'{prog} {command}' je zastaralý a bude odebrán v budoucích verzích", "confirm": "Potvrdit {prompt}", - "colon": "{}: ", - "authentication_required_long": "K provedení této akce je vyžadováno ověření", "authentication_required": "Vyžadováno ověření", "argument_required": "Je vyžadován argument '{argument}'" } \ No newline at end of file diff --git a/locales/de.json b/locales/de.json index 72ee01cc..590f8159 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1,18 +1,13 @@ { "argument_required": "Der Parameter {argument} ist erforderlich", "authentication_required": "Anmeldung erforderlich", - "authentication_required_long": "Bitte erst anmelden um diese Aktion auszuführen", - "colon": "{}: ", "confirm": "Bestätige {prompt}", "error": "Fehler:", "file_not_exist": "Datei ist nicht vorhanden: '{path}'", "folder_exists": "Ordner existiert bereits: '{path}'", "instance_already_running": "Es läuft bereits eine YunoHost-Operation. Bitte warte, bis sie fertig ist, bevor du eine weitere startest.", "invalid_argument": "Argument ungültig '{argument}': {error}", - "invalid_password": "Passwort falsch", "invalid_usage": "Falscher Aufruf, verwende --help für den Hilfstext", - "ldap_attribute_already_exists": "Attribute existieren bereits: '{attribute}={value}'", - "ldap_server_down": "LDAP-Server nicht erreichbar", "logged_in": "Angemeldet", "logged_out": "Abgemeldet", "not_logged_in": "Du bist nicht angemeldet", @@ -23,7 +18,6 @@ "server_already_running": "Einen anderer Dienst arbeitet bereits auf diesem Port", "success": "Erfolg!", "unable_authenticate": "Anmelden fehlgeschlagen", - "unable_retrieve_session": "Sitzung konnte nicht abgerufen werden. Grund: '{exception}'", "values_mismatch": "Die Werte passen nicht zusammen", "warning": "Warnung:", "websocket_request_expected": "Eine WebSocket-Anfrage wurde erwartet", @@ -32,7 +26,6 @@ "unknown_group": "Gruppe '{group}' ist unbekannt", "unknown_user": "Benutzer '{user}' ist unbekannt", "info": "Info:", - "invalid_token": "Ungültiger Token - bitte authentifizieren", "corrupted_json": "Beschädigtes JSON gelesen von {ressource} (reason: {error})", "unknown_error_reading_file": "Unbekannter Fehler beim Lesen der Datei {file} (reason: {error})", "cannot_write_file": "Kann Datei {file} nicht schreiben (reason: {error})", @@ -49,7 +42,5 @@ "error_changing_file_permissions": "Fehler beim Ändern der Berechtigungen für {path}: {error}", "error_removing": "Fehler beim Entfernen {path}: {error}", "error_writing_file": "Fehler beim Schreiben von Datei {file}: {error}", - "corrupted_toml": "Beschädigtes TOML gelesen von {ressource} (reason: {error})", - "ldap_server_is_down_restart_it": "Der LDAP-Dienst wurde angehalten. Es wird versucht, ihn erneut zu starten...", - "session_expired": "Die Sitzung ist abgelaufen. Bitte authentifizieren Sie sich neu ." + "corrupted_toml": "Beschädigtes TOML gelesen von {ressource} (reason: {error})" } \ No newline at end of file diff --git a/locales/en.json b/locales/en.json index 68aa640a..ca0ff36d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1,10 +1,10 @@ { "argument_required": "Argument '{argument}' is required", "authentication_required": "Authentication required", - "colon": "{}: ", "confirm": "Confirm {prompt}", "deprecated_command": "'{prog} {command}' is deprecated and will be removed in the future", "deprecated_command_alias": "'{prog} {old}' is deprecated and will be removed in the future, use '{prog} {new}' instead", + "edit_text_question": "{}. Edit this text ? [yN]: ", "error": "Error:", "file_not_exist": "File does not exist: '{path}'", "folder_exists": "Folder already exists: '{path}'", diff --git a/locales/eo.json b/locales/eo.json index 2a084a14..02d8a43b 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -1,6 +1,5 @@ { "password": "Pasvorto", - "colon": "{}: ", "warn_the_user_that_lock_is_acquired": "La alia komando ĵus kompletigis, nun komencante ĉi tiun komandon", "warn_the_user_about_waiting_lock_again": "Ankoraŭ atendanta...", "warn_the_user_about_waiting_lock": "Alia komando de YunoHost funkcias ĝuste nun, ni atendas, ke ĝi finiĝos antaŭ ol funkcii ĉi tiu", @@ -23,7 +22,6 @@ "values_mismatch": "Valoroj ne kongruas", "unknown_user": "Nekonata uzanto '{user}'", "unknown_group": "Nekonata grupo \"{group}\"", - "unable_retrieve_session": "Ne eblas retrovi la sesion ĉar '{exception}'", "unable_authenticate": "Ne eblas aŭtentiĝi", "success": "Sukceson!", "server_already_running": "Servilo jam funkcias sur tiu haveno", @@ -32,10 +30,7 @@ "operation_interrupted": "Operacio interrompita", "not_logged_in": "Vi ne estas ensalutinta", "logged_in": "Ensalutinta", - "ldap_server_down": "Ne eblas atingi la servilon LDAP", - "ldap_attribute_already_exists": "Atributo '{attribute}' jam ekzistas kun valoro '{value}'", "invalid_usage": "Nevalida uzado, preterpase '--help' por vidi helpon", - "invalid_password": "Nevalida pasvorto", "invalid_argument": "Nevalida argumento '{argument}': {error}", "instance_already_running": "Jam funkcias YunoHost-operacio. Bonvolu atendi, ke ĝi finiĝos antaŭ ol funkcii alia.", "info": "informoj:", @@ -45,11 +40,7 @@ "deprecated_command_alias": "'{prog} {old}' malakceptas kaj estos forigita estonte, uzu anstataŭe '{prog} {new}'", "deprecated_command": "'{prog} {command}' malakceptas kaj estos forigita estonte", "confirm": "Konfirmu {prompt}", - "authentication_required_long": "Aŭtentigo necesas por plenumi ĉi tiun agon", "authentication_required": "Aŭtentigo bezonata", "argument_required": "Argumento '{argument}' estas bezonata", - "logged_out": "Ensalutinta", - "invalid_token": "Nevalida tokeno - bonvolu autentiki", - "ldap_server_is_down_restart_it": "La LDAP-servo malpliiĝas, provu rekomenci ĝin...", - "session_expired": "La sesio eksvalidiĝis. Bonvolu re-aŭtentikigi." + "logged_out": "Ensalutinta" } \ No newline at end of file diff --git a/locales/es.json b/locales/es.json index 5e76a6b9..9a454824 100644 --- a/locales/es.json +++ b/locales/es.json @@ -1,8 +1,6 @@ { "argument_required": "Se requiere el argumento «{argument}»", "authentication_required": "Se requiere autentificación", - "authentication_required_long": "Debe autentificarse para realizar esta acción", - "colon": "{}: ", "confirm": "Confirmar {prompt}", "deprecated_command": "«{prog} {command}» está obsoleto y será eliminado en el futuro", "deprecated_command_alias": "«{prog} {old}» está obsoleto y se eliminará en el futuro, use «{prog} {new}» en su lugar", @@ -11,10 +9,7 @@ "folder_exists": "El directorio ya existe: «{path}»", "instance_already_running": "Ya se está ejecutando una instancia de YunoHost. Espere a que termine antes de ejecutar otra.", "invalid_argument": "Argumento no válido «{argument}»: {error}", - "invalid_password": "Contraseña no válida", "invalid_usage": "Uso no válido, utilice --help para ver la ayuda", - "ldap_attribute_already_exists": "El atributo «{attribute}» ya existe con el valor «{value}»", - "ldap_server_down": "No se pudo conectar con el servidor LDAP", "logged_in": "Sesión iniciada", "logged_out": "Sesión cerrada", "not_logged_in": "No ha iniciado sesión", @@ -25,7 +20,6 @@ "server_already_running": "Ya se está ejecutando un servidor en ese puerto", "success": "¡Éxito!", "unable_authenticate": "No se puede autentificar", - "unable_retrieve_session": "No se puede recuperar la sesión por «{exception}»", "unknown_group": "Grupo «{group}» desconocido", "unknown_user": "Usuario «{user}» desconocido", "values_mismatch": "Los valores no coinciden", @@ -48,8 +42,5 @@ "corrupted_toml": "Lectura corrupta de TOML desde {ressource} (motivo: {error})", "warn_the_user_that_lock_is_acquired": "La otra orden recién terminó, iniciando esta orden ahora", "warn_the_user_about_waiting_lock_again": "Aún esperando...", - "warn_the_user_about_waiting_lock": "Otra orden de YunoHost se está ejecutando ahora, estamos esperando a que termine antes de ejecutar esta", - "invalid_token": "Token invalido - vuelva a autenticarte", - "ldap_server_is_down_restart_it": "El servicio LDAP está caído, intentando reiniciarlo...", - "session_expired": "La sesión expiró. Por favor autenticarse de nuevo." + "warn_the_user_about_waiting_lock": "Otra orden de YunoHost se está ejecutando ahora, estamos esperando a que termine antes de ejecutar esta" } \ No newline at end of file diff --git a/locales/eu.json b/locales/eu.json index 0e752883..935bf588 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -1,6 +1,5 @@ { "argument_required": "'{argument}' argumentua beharrezkoa da", "logged_out": "Saioa amaitu", - "password": "Pasahitza", - "colon": "{}: " + "password": "Pasahitza" } \ No newline at end of file diff --git a/locales/fa.json b/locales/fa.json index 8287c887..5873da9d 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -6,11 +6,10 @@ "info": "اطلاعات:", "folder_exists": "پوشه موجود است: '{path}'", "file_not_exist": "فایل وجود ندارد: '{path}'", - "error": "خطا:", + "error": "ایراد:", "deprecated_command_alias": "'{prog} {old}' منسوخ شده است و در آینده حذف خواهد شد ، بجای آن از '{prog} {new}' استفاده کنید", "deprecated_command": "'{prog} {command}' منسوخ شده است و در آینده حذف خواهد شد", - "confirm": "تأیید {prompt}", - "colon": "{}: ", + "confirm": "تایید کردن {prompt}", "authentication_required": "احراز هویّت الزامی است", "argument_required": "استدلال '{argument}' ضروری است", "password": "کلمه عبور", @@ -41,7 +40,7 @@ "server_already_running": "در حال حاضر یک سرور روی آن پورت کار می کند", "root_required": "برای انجام این عمل باید کاربر ریشه باشید", "pattern_not_match": "با الگو مطابقت ندارد", - "operation_interrupted": "عملیات قطع شد", + "operation_interrupted": "عملیات قطع شده است", "not_logged_in": "‌شما وارد نشده اید", "logged_out": "خارج شده" -} +} \ No newline at end of file diff --git a/locales/fr.json b/locales/fr.json index 567cb20a..7c5723e1 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,55 +1,47 @@ { - "argument_required": "L’argument '{argument}' est requis", + "argument_required": "L'argument '{argument}' est requis", "authentication_required": "Authentification requise", - "authentication_required_long": "L’authentification est requise pour exécuter cette action", - "colon": "{} : ", "confirm": "Confirmez {prompt}", "deprecated_command": "'{prog} {command}' est déprécié et sera bientôt supprimé", "deprecated_command_alias": "'{prog} {old}' est déprécié et sera bientôt supprimé, utilisez '{prog} {new}' à la place", "error": "Erreur :", - "file_not_exist": "Le fichier '{path}' n’existe pas", + "file_not_exist": "Le fichier '{path}' n'existe pas", "folder_exists": "Le dossier existe déjà : '{path}'", - "instance_already_running": "Une instance est déjà en cours d’exécution, merci d'attendre sa fin avant d'en lancer une autre.", + "instance_already_running": "Une instance est déjà en cours d'exécution, merci d'attendre sa fin avant d'en lancer une autre.", "invalid_argument": "Argument '{argument}' incorrect : {error}", - "invalid_password": "Mot de passe incorrect", - "invalid_usage": "Utilisation erronée, utilisez --help pour accéder à l’aide", - "ldap_attribute_already_exists": "L’attribut '{attribute}' existe déjà avec la valeur suivante : '{value}'", - "ldap_server_down": "Impossible d’atteindre le serveur LDAP", + "invalid_usage": "Utilisation erronée, utilisez --help pour accéder à l'aide", "logged_in": "Connecté", "logged_out": "Déconnecté", - "not_logged_in": "Vous n’êtes pas connecté", + "not_logged_in": "Vous n'êtes pas connecté", "operation_interrupted": "Opération interrompue", "password": "Mot de passe", "pattern_not_match": "Ne correspond pas au motif", "root_required": "Vous devez être super-utilisateur pour exécuter cette action", - "server_already_running": "Un serveur est déjà en cours d’exécution sur ce port", + "server_already_running": "Un serveur est déjà en cours d'exécution sur ce port", "success": "Succès !", "unable_authenticate": "Impossible de vous authentifier", - "unable_retrieve_session": "Impossible de récupérer la session à cause de '{exception}'", - "unknown_group": "Le groupe « '{group}' » est inconnu", - "unknown_user": "L'utilisateur « {user} » est inconnu", + "unknown_group": "Le groupe '{group}' est inconnu", + "unknown_user": "L'utilisateur '{user}' est inconnu", "values_mismatch": "Les valeurs ne correspondent pas", "warning": "Attention :", "websocket_request_expected": "Une requête WebSocket est attendue", - "cannot_open_file": "Impossible d’ouvrir le fichier {file} (raison : {error})", + "cannot_open_file": "Impossible d'ouvrir le fichier {file} (raison : {error})", "cannot_write_file": "Ne peut pas écrire le fichier {file} (raison : {error})", - "unknown_error_reading_file": "Erreur inconnue en essayant de lire le fichier {file} (cause:{error})", + "unknown_error_reading_file": "Erreur inconnue en essayant de lire le fichier {file} (raison :{error})", "corrupted_json": "Fichier JSON corrompu en lecture depuis {ressource} (raison : {error})", "error_writing_file": "Erreur en écrivant le fichier {file} : {error}", "error_removing": "Erreur lors de la suppression {path} : {error}", "error_changing_file_permissions": "Erreur lors de la modification des autorisations pour {path} : {error}", - "invalid_url": "Impossible de se connecter à {url} ... peut-être que le service est hors service/indisponible/interrompu, ou que vous n'êtes pas correctement connecté à Internet en IPv4/IPv6.", + "invalid_url": "Impossible de se connecter à {url}... peut-être que le service est hors service/indisponible/interrompu, ou que vous n'êtes pas correctement connecté à Internet en IPv4/IPv6.", "download_ssl_error": "Erreur SSL lors de la connexion à {url}", "download_timeout": "{url} a pris trop de temps pour répondre : abandon.", "download_unknown_error": "Erreur lors du téléchargement des données à partir de {url} : {error}", "download_bad_status_code": "{url} renvoie le code d'état {code}", "corrupted_yaml": "Fichier YAML corrompu en lecture depuis {ressource} (raison : {error})", "info": "Info :", - "corrupted_toml": "Fichier TOML corrompu en lecture depuis {ressource} (cause : {error})", + "corrupted_toml": "Fichier TOML corrompu en lecture depuis {ressource} (raison : {error})", "warn_the_user_about_waiting_lock": "Une autre commande YunoHost est actuellement en cours, nous attendons qu'elle se termine avant de démarrer celle là", "warn_the_user_about_waiting_lock_again": "Toujours en attente...", "warn_the_user_that_lock_is_acquired": "La commande précédente vient de se terminer, lancement de cette nouvelle commande", - "invalid_token": "Jeton non valide - veuillez vous authentifier", - "ldap_server_is_down_restart_it": "Le service LDAP s'est arrêté, une tentative de redémarrage est en cours...", - "session_expired": "La session a expiré. Merci de vous ré-authentifier." -} + "edit_text_question": "{}. Modifier ce texte ? [yN] : " +} \ No newline at end of file diff --git a/locales/gl.json b/locales/gl.json index fe05dde9..c4988faf 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -1,8 +1,5 @@ { - "ldap_attribute_already_exists": "O atributo '{attribute}' xa existe e ten o valor '{value}'", "invalid_usage": "Uso non válido, pass --help para ver a axuda", - "invalid_token": "Token non válido - por favor autentícate", - "invalid_password": "Contrasinal non válido", "invalid_argument": "Argumento non válido '{argument}': {error}", "instance_already_running": "Hai unha operación de YunoHost en execución. Por favor agarda a que remate antes de realizar unha nova.", "info": "Info:", @@ -12,8 +9,6 @@ "deprecated_command_alias": "'{prog} {old}' xa non se utiliza e será eliminado no futuro, usa '{prog} {new}' no seu lugar", "deprecated_command": "'{prog} {command}' xa non se utiliza e xa non se usará no futuro", "confirm": "Confirma {prompt}", - "colon": "{}: ", - "authentication_required_long": "Requírese autenticación para realizar esta acción", "authentication_required": "Autenticación requerida", "argument_required": "O argumento '{argument}' é requerido", "logged_out": "Sesión pechada", @@ -22,8 +17,6 @@ "values_mismatch": "Non concordan os valores", "unknown_user": "Usuaria '{user}' descoñecida", "unknown_group": "Grupo '{group}' descoñecido", - "session_expired": "A sesión caducou. Volve a conectar por favor.", - "unable_retrieve_session": "Non se puido obter a sesión porque '{exception}'", "unable_authenticate": "Non se puido autenticar", "success": "Ben feito!", "server_already_running": "Xa hai un servidor a funcionar nese porto", @@ -32,8 +25,6 @@ "operation_interrupted": "Interrumpeuse a operación", "not_logged_in": "Non estás conectada", "logged_in": "Conectada", - "ldap_server_down": "Non se puido acadar o servidor LDAP", - "ldap_server_is_down_restart_it": "O servizo LDAP está caído, intentando reinicialo...", "warn_the_user_that_lock_is_acquired": "O outro comando rematou, agora executarase este", "warn_the_user_about_waiting_lock_again": "Agardando...", "warn_the_user_about_waiting_lock": "Estase executando outro comando de YunoHost neste intre, estamos agardando a que remate para executar este", @@ -51,5 +42,6 @@ "unknown_error_reading_file": "Erro descoñecido ao intentar ler o ficheiro {file} (razón: {error})", "cannot_write_file": "Non se puido escribir o ficheiro {file} (razón: {error})", "cannot_open_file": "Non se puido abrir o ficheiro {file} (razón: {error})", - "websocket_request_expected": "Agardábase unha solicitude WebSocket" -} + "websocket_request_expected": "Agardábase unha solicitude WebSocket", + "edit_text_question": "{}. Editar este texto ? [yN]: " +} \ No newline at end of file diff --git a/locales/hi.json b/locales/hi.json index 4ca0346c..57781db8 100644 --- a/locales/hi.json +++ b/locales/hi.json @@ -1,8 +1,6 @@ { "argument_required": "तर्क '{argument}' आवश्यक है", "authentication_required": "प्रमाणीकरण आवश्यक", - "authentication_required_long": "इस कार्य को करने के लिए प्रमाणीकरण आवश्यक है", - "colon": "{}: ", "confirm": "पुष्टि करें {prompt}", "deprecated_command": "'{prog}' '{command}' का प्रयोग न करे, भविष्य में इसे हटा दिया जाएगा", "deprecated_command_alias": "'{prog} {old}' अब पुराना हो गया है और इसे भविष्य में हटा दिया जाएगा, इस की जगह '{prog} {new}' का प्रयोग करें", @@ -11,10 +9,7 @@ "folder_exists": "फ़ोल्डर में पहले से ही मौजूद है: '{path}'", "instance_already_running": "यूनोहोस्ट का एक कार्य पहले से चल रहा है। कृपया इस कार्य के समाप्त होने का इंतज़ार करें।", "invalid_argument": "अवैध तर्क '{argument}':'{error}'", - "invalid_password": "अवैध पासवर्ड", "invalid_usage": "अवैध उपयोग, सहायता देखने के लिए --help साथ लिखे।", - "ldap_attribute_already_exists": "'{attribute}' तर्क पहले इस वैल्यू '{value}' से मौजूद है।", - "ldap_server_down": "LDAP सर्वर तक पहुंचने में असमर्थ।", "logged_in": "लोग्ड इन", "logged_out": "लॉग आउट", "not_logged_in": "आप लोग्ड इन नहीं हैं।", @@ -25,7 +20,6 @@ "server_already_running": "कोई सर्वर पहले से ही इस पोर्ट पर चल रहा है।", "success": "सफलता!", "unable_authenticate": "प्रमाणित करने में असमर्थ।", - "unable_retrieve_session": "सेशन को प्राप्त करने में असमर्थ।", "unknown_group": "अज्ञात ग्रुप: '{group}'", "unknown_user": "अज्ञात उपयोगकर्ता: '{user}'", "values_mismatch": "वैल्यूज मेल नहीं खाती।", diff --git a/locales/hu.json b/locales/hu.json index 83906ecd..001f1d03 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -11,7 +11,6 @@ "success": "Siker!", "values_mismatch": "Eltérő értékek", "warning": "Figyelem:", - "invalid_password": "Helytelen jelszó", "info": "Információ:", "file_not_exist": "A fájl nem létezik: '{path}'", "error": "Hiba:" diff --git a/locales/id.json b/locales/id.json new file mode 100644 index 00000000..ac55a468 --- /dev/null +++ b/locales/id.json @@ -0,0 +1,19 @@ +{ + "argument_required": "Argumen '{argument}' dibutuhkan", + "authentication_required": "Otentikasi dibutuhkan", + "deprecated_command": "'{prog} {command}' sudah usang dan akan dihapus nanti", + "logged_out": "Berhasil keluar", + "password": "Kata sandi", + "deprecated_command_alias": "'{prog} {old}' sudah usang dan akan dihapus nanti, gunakan '{prog} {new}' saja", + "info": "Informasi:", + "instance_already_running": "Sudah ada operasi YunoHost yang sedang berjalan. Tunggu itu selesai sebelum menjalankan yang lain.", + "logged_in": "Berhasil masuk", + "warning": "Peringatan:", + "cannot_open_file": "Tidak dapat membuka berkas {file} (alasan: {error})", + "error_removing": "Terjadi kesalahan ketika menghapus {path}: {error}", + "success": "Berhasil!", + "warn_the_user_about_waiting_lock": "Perintah YunoHost lain sedang berjalan saat ini, kami sedang menunggu itu selesai sebelum menjalankan yang ini", + "warn_the_user_about_waiting_lock_again": "Masih menunggu...", + "unable_authenticate": "Tidak dapat mengotentikasi", + "warn_the_user_that_lock_is_acquired": "Perintah yang tadi baru saja selesai, akan memulai perintah ini" +} diff --git a/locales/it.json b/locales/it.json index 4487ccb6..e07c30e7 100644 --- a/locales/it.json +++ b/locales/it.json @@ -3,8 +3,6 @@ "password": "Password", "argument_required": "L'argomento '{argument}' è richiesto", "authentication_required": "Autenticazione richiesta", - "authentication_required_long": "Autenticazione richiesta per eseguire questa azione", - "colon": "{}: ", "confirm": "Confermare {prompt}", "deprecated_command": "'{prog} {command}' è deprecato e sarà rimosso in futuro", "deprecated_command_alias": "'{prog} {old}' è deprecato e sarà rimosso in futuro, usa invece '{prog} {new}'", @@ -13,10 +11,7 @@ "folder_exists": "La cartella esiste già: '{path}'", "instance_already_running": "Esiste già un'operazione YunoHost in esecuzione. Attendi il completamento prima di eseguirne un altro.", "invalid_argument": "Argomento non valido '{argument}': {error}", - "invalid_password": "Password non valida", "invalid_usage": "Utilizzo non valido, usa --help per vedere l'aiuto", - "ldap_attribute_already_exists": "L'attributo '{attribute}' esiste già con valore '{value}'", - "ldap_server_down": "Impossibile raggiungere il server LDAP", "logged_in": "Connesso", "not_logged_in": "Non hai effettuato l'accesso", "operation_interrupted": "Operazione interrotta", @@ -25,7 +20,6 @@ "server_already_running": "Un server è già in esecuzione su quella porta", "success": "Riuscito!", "unable_authenticate": "Autenticazione fallita", - "unable_retrieve_session": "Impossibile recuperare la sessione perché \"{exception}\"", "unknown_group": "Gruppo '{group}' sconosciuto", "unknown_user": "Utente '{user}' sconosciuto", "values_mismatch": "I valori non corrispondono", @@ -48,8 +42,5 @@ "warn_the_user_that_lock_is_acquired": "L'altro comando è appena completato, ora avvio questo comando", "warn_the_user_about_waiting_lock_again": "Sto ancora aspettando ...", "warn_the_user_about_waiting_lock": "Un altro comando YunoHost è in esecuzione in questo momento, stiamo aspettando che finisca prima di eseguire questo", - "corrupted_toml": "TOML corrotto da {ressource} (motivo: {error})", - "invalid_token": "Token non valido: autenticare", - "session_expired": "La sessione è terminata. Sei pregato di autenticarti nuovamente.", - "ldap_server_is_down_restart_it": "Il servizio LDAP è terminato, provo a riavviarlo..." -} + "corrupted_toml": "TOML corrotto da {ressource} (motivo: {error})" +} \ No newline at end of file diff --git a/locales/mk.json b/locales/mk.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/locales/mk.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/locales/nb_NO.json b/locales/nb_NO.json index fc4536ed..13e87fd8 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -11,11 +11,9 @@ "operation_interrupted": "Operasjon forstyrret", "not_logged_in": "Du er ikke innlogget", "logged_in": "Innlogget", - "invalid_password": "Ugyldig passord", "info": "Info:", "error": "Feil:", "confirm": "Bekreft {prompt}", - "colon": "{}: ", "logged_out": "Utlogget", "password": "Passord" } \ No newline at end of file diff --git a/locales/ne.json b/locales/ne.json index f0e68fb9..275cf377 100644 --- a/locales/ne.json +++ b/locales/ne.json @@ -4,8 +4,6 @@ "deprecated_command_alias": "'{prog} {old}' अस्वीकृत गरिएको छ र भविष्यमा हटाइनेछ, यसको सट्टा '{prog} {new}' प्रयोग गर्नुहोस्।", "deprecated_command": "'{prog} {command}' अस्वीकृत गरिएको छ र भविष्यमा हटाइनेछ", "confirm": "कन्फर्म {prompt}", - "colon": "{}: ", - "authentication_required_long": "यस कार्य गर्नको लागि प्रमाणीकरण आवाश्यक हुन्छ", "authentication_required": "प्रमाणीकरण आवाश्यक छ", "argument_required": "तर्क '{argument}' आवश्यक छ" } \ No newline at end of file diff --git a/locales/nl.json b/locales/nl.json index b25ac3f3..62145b74 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -1,18 +1,13 @@ { "argument_required": "Argument {argument} is vereist", "authentication_required": "Aanmelding vereist", - "authentication_required_long": "Aanmelding is vereist om deze actie uit te voeren", - "colon": "{}: ", "confirm": "Bevestig {prompt}", "error": "Fout:", "file_not_exist": "Bestand bestaat niet: '{path}'", "folder_exists": "Deze map bestaat al: '{path}'", "instance_already_running": "Er is al een instantie actief, bedankt om te wachten tot deze afgesloten is alvorens een andere te starten.", "invalid_argument": "Ongeldig argument '{argument}': {error}", - "invalid_password": "Ongeldig wachtwoord", "invalid_usage": "Ongeldig gebruik, doe --help om de hulptekst te lezen", - "ldap_attribute_already_exists": "Attribuut '{attribute}' bestaat al met waarde '{value}'", - "ldap_server_down": "Kan LDAP server niet bereiken", "logged_in": "Ingelogd", "logged_out": "Uitgelogd", "not_logged_in": "U bent niet ingelogd", @@ -23,7 +18,6 @@ "server_already_running": "Er is al een server actief op die poort", "success": "Succes!", "unable_authenticate": "Aanmelding niet mogelijk", - "unable_retrieve_session": "Het is onmogelijk op de sessie op te halen omwille van '{exception}'", "values_mismatch": "Waarden zijn niet gelijk", "warning": "Waarschuwing:", "websocket_request_expected": "Verwachtte een WebSocket request", @@ -48,6 +42,5 @@ "warn_the_user_about_waiting_lock": "Een ander YunoHost commando wordt uitgevoerd, we wachten tot het gedaan is alovrens dit te starten", "corrupted_toml": "Ongeldige TOML werd gelezen op {ressource} (reason: {error})", "corrupted_yaml": "Ongeldig YAML bestand op {ressource} (reason: {error})", - "invalid_token": "Ongeldig token - gelieve in te loggen", "info": "Ter info:" } \ No newline at end of file diff --git a/locales/oc.json b/locales/oc.json index 69bfbcd5..266b65e2 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -1,11 +1,9 @@ { "argument_required": "L’argument {argument} es requesit", "authentication_required": "Autentificacion requesida", - "authentication_required_long": "Una autentificacion es requesida per acomplir aquesta accion", "logged_in": "Connectat", "logged_out": "Desconnectat", "password": "Senhal", - "colon": "{}: ", "confirm": "Confirmatz : {prompt}", "deprecated_command": "« {prog} {command} » es despreciat e serà lèu suprimit", "deprecated_command_alias": "« {prog} {old} » es despreciat e serà lèu suprimit, utilizatz « {prog} {new} » allòc", @@ -14,18 +12,14 @@ "folder_exists": "Lo repertòri existís ja : « {path} »", "instance_already_running": "I a ja una operacion de YunoHost en cors. Mercés d’esperar que s’acabe abans de ne lançar una mai.", "invalid_argument": "Argument « {argument} » incorrècte : {error}", - "invalid_password": "Senhal incorrècte", - "ldap_server_down": "Impossible d’aténher lo servidor LDAP", "not_logged_in": "Cap de session començada", "pattern_not_match": "Correspond pas al patron", "root_required": "Cal èsser root per realizar aquesta accion", - "unable_retrieve_session": "Recuperacion impossibla de la session a causa de « {exception} »", "unknown_group": "Grop « {group} » desconegut", "unknown_user": "Utilizaire « {user} » desconegut", "values_mismatch": "Las valors correspondon pas", "warning": "Atencion :", "invalid_usage": "Usatge invalid, utilizatz --help per accedir a l’ajuda", - "ldap_attribute_already_exists": "L’atribut « {attribute} » existís ja amb la valor : {value}", "operation_interrupted": "Operacion interrompuda", "server_already_running": "Un servidor es ja en execucion sus aqueste pòrt", "success": "Capitada !", @@ -48,8 +42,5 @@ "corrupted_toml": "Fichièr TOML corromput en lectura de {ressource} estant (rason : {error})", "warn_the_user_about_waiting_lock": "Una autra comanda YunoHost es en execucion, sèm a esperar qu’acabe abans d’aviar aquesta d’aquí", "warn_the_user_about_waiting_lock_again": "Encara en espèra…", - "warn_the_user_that_lock_is_acquired": "l’autra comanda ven d’acabar, ara lançament d’aquesta comanda", - "invalid_token": "Geton invalid - volgatz vos autentificar", - "ldap_server_is_down_restart_it": "Lo servici LDAP s’es atudat, ensajam de lo reaviar…", - "session_expired": "La session a expirat. Tornatz vos autentificar." + "warn_the_user_that_lock_is_acquired": "l’autra comanda ven d’acabar, ara lançament d’aquesta comanda" } \ No newline at end of file diff --git a/locales/pl.json b/locales/pl.json index 5a048ca6..02be3e72 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -23,7 +23,6 @@ "values_mismatch": "Wartości nie pasują", "unknown_user": "Nieznany użytkownik '{user}'", "unknown_group": "Nieznana grupa '{group}'", - "unable_retrieve_session": "Nie można pobrać sesji, ponieważ „{exception}”", "unable_authenticate": "Nie można uwierzytelnić", "success": "Sukces!", "server_already_running": "Serwer już działa na tym porcie", @@ -32,11 +31,7 @@ "operation_interrupted": "Operacja przerwana", "not_logged_in": "Nie jesteś zalogowany", "logged_in": "Zalogowany", - "ldap_server_down": "Nie można połączyć się z serwerem LDAP", - "ldap_attribute_already_exists": "Atrybut „{attribute}” już istnieje z wartością „{value}”", "invalid_usage": "Nieprawidłowe użycie. Przejdź --help, aby wyświetlić pomoc", - "invalid_token": "Nieprawidłowy token - proszę uwierzytelnić", - "invalid_password": "Nieprawidłowe hasło", "invalid_argument": "Nieprawidłowy argument „{argument}”: {error}", "instance_already_running": "Trwa już operacja YunoHost. Zaczekaj na zakończenie, zanim uruchomisz kolejny.", "info": "Informacje:", @@ -46,10 +41,6 @@ "deprecated_command_alias": "„{prog} {old}” jest przestarzałe i zostanie usunięte w przyszłości, zamiast tego użyj „{prog} {new}”", "deprecated_command": "„{prog} {command}” jest przestarzałe i zostanie usunięte w przyszłości", "confirm": "Potwierdź {prompt}", - "colon": "{}: ", - "authentication_required_long": "Do wykonania tej czynności wymagane jest uwierzytelnienie", "authentication_required": "Wymagane uwierzytelnienie", - "argument_required": "Argument „{argument}” jest wymagany", - "ldap_server_is_down_restart_it": "Usługa LDAP nie działa, próba restartu...", - "session_expired": "Sesja wygasła. Zaloguj się ponownie." + "argument_required": "Argument „{argument}” jest wymagany" } \ No newline at end of file diff --git a/locales/pt.json b/locales/pt.json index d723ef35..2c43d0b0 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -1,18 +1,13 @@ { "argument_required": "O argumento '{argument}' é obrigatório", "authentication_required": "Autenticação obrigatória", - "authentication_required_long": "É preciso autenticar-se para realizar esta ação", - "colon": "{}: ", "confirm": "Confirmar {prompt}", "error": "Erro:", "file_not_exist": "O ficheiro não existe: '{path}'", "folder_exists": "A pasta já existe: '{path}'", "instance_already_running": "Já existe uma operação YunoHost em execução. Aguarde o término antes de executar outro.", "invalid_argument": "Argumento inválido '{argument}': {error}", - "invalid_password": "Senha incorreta", "invalid_usage": "Uso invalido, utilizar --help para ver a ajuda", - "ldap_attribute_already_exists": "O atributo '{attribute}' já existe com valor '{value}'", - "ldap_server_down": "Não foi possível comunicar com o servidor LDAP", "logged_in": "Sessão iniciada", "logged_out": "Sessão terminada", "not_logged_in": "Não tem sessão iniciada", @@ -23,7 +18,6 @@ "server_already_running": "Existe um servidor ativo nessa porta", "success": "Sucesso!", "unable_authenticate": "Não foi possível autenticar", - "unable_retrieve_session": "Não foi possível recuperar a sessão porque '{exception}'", "values_mismatch": "Os valores não coincidem", "warning": "Aviso:", "websocket_request_expected": "Esperado um pedido a WebSocket", @@ -48,8 +42,5 @@ "warn_the_user_about_waiting_lock_again": "Ainda esperando...", "warn_the_user_about_waiting_lock": "Outro comando YunoHost está sendo executado agora, estamos aguardando o término antes de executar este", "corrupted_toml": "TOML corrompido lido em {ressource} (motivo: {error})", - "invalid_token": "Token inválido - autentique", - "info": "Informações:", - "ldap_server_is_down_restart_it": "O serviço LDAP esta caído, tentando reiniciá-lo...", - "session_expired": "A sessão expirou. Se autentique de novo por favor." + "info": "Informações:" } \ No newline at end of file diff --git a/locales/ru.json b/locales/ru.json index 6b285b40..76c8f072 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,8 +1,6 @@ { "argument_required": "Требуется'{argument}' аргумент", "authentication_required": "Требуется аутентификация", - "authentication_required_long": "Для этого действия требуется аутентификация", - "colon": "{}: ", "confirm": "Подтвердить {prompt}", "deprecated_command": "'{prog} {command}' устарела и будет удалена", "deprecated_command_alias": "'{prog} {old}' устарела и будет удалена, вместо неё используйте '{prog} {new}'", @@ -10,8 +8,6 @@ "file_not_exist": "Файл не существует: '{path}'", "folder_exists": "Каталог уже существует: '{path}'", "invalid_argument": "Неправильный аргумент '{argument}': {error}", - "invalid_password": "Неправильный пароль", - "ldap_attribute_already_exists": "Атрибут '{attribute}' уже существует со значением '{value}'", "logged_in": "Вы вошли", "logged_out": "Вы вышли из системы", "not_logged_in": "Вы не залогинились", @@ -29,7 +25,7 @@ "cannot_open_file": "Не могу открыть файл {file} (причина: {error})", "cannot_write_file": "Не могу записать файл {file} (причина: {error})", "unknown_error_reading_file": "Неизвестная ошибка при попытке прочитать файл {file} (причина: {error})", - "corrupted_yaml": "Повреждённой yaml получен от {ressource} (причина: {error})", + "corrupted_yaml": "Повреждённой YAML получен от {ressource} (причина: {error})", "error_writing_file": "Ошибка при записи файла {file}: {error}", "error_removing": "Ошибка при удалении {path}: {error}", "invalid_url": "Неправильный url {url} (этот сайт существует ?)", @@ -38,16 +34,13 @@ "download_unknown_error": "Ошибка при загрузке данных с {url} : {error}", "instance_already_running": "Операция YunoHost уже запущена. Пожалуйста, подождите, пока он закончится, прежде чем запускать другой.", "root_required": "Чтобы выполнить это действие, вы должны иметь права root", - "corrupted_json": "Повреждённый json получен от {ressource} (причина: {error})", + "corrupted_json": "Повреждённый json получен от {ressource} (причина: {error})", "warn_the_user_that_lock_is_acquired": "другая команда только что завершилась, теперь запускает эту команду", "warn_the_user_about_waiting_lock_again": "Все еще жду...", "warn_the_user_about_waiting_lock": "Сейчас запускается еще одна команда YunoHost, мы ждем ее завершения, прежде чем запустить эту", "download_bad_status_code": "{url} вернул код состояния {code}", "error_changing_file_permissions": "Ошибка при изменении разрешений для {path}: {error}", - "corrupted_toml": "Поврежденный том, прочитанный из {ressource} (причина: {error})", - "unable_retrieve_session": "Невозможно получить сеанс, так как '{exception}'", - "ldap_server_down": "Невозможно связаться с сервером LDAP", + "corrupted_toml": "Поврежденный TOML, прочитанный из {ressource} (причина: {error})", "invalid_usage": "Неправильное использование, передайте --help, чтобы увидеть помощь", - "invalid_token": "Неверный токен - пожалуйста, авторизуйтесь", "info": "Информация:" -} \ No newline at end of file +} diff --git a/locales/sv.json b/locales/sv.json index 298b1e6b..005c68e1 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -17,8 +17,6 @@ "operation_interrupted": "Behandling avbruten", "not_logged_in": "Du är inte inloggad", "logged_in": "Inloggad", - "ldap_attribute_already_exists": "Attributet '{attribute}' finns redan med värdet '{value}'", - "invalid_password": "Ogiltigt lösenord", "invalid_argument": "Ogiltig parameter '{argument}': {error}", "logged_out": "Utloggad", "info": "Info:", @@ -28,7 +26,6 @@ "deprecated_command_alias": "'{prog} {old}' rekommenderas inte längre och kommer tas bort i framtiden, använd '{prog} {new}' istället", "deprecated_command": "'{prog} {command}' rekommenderas inte längre och kommer tas bort i framtiden", "confirm": "Bekräfta {prompt}", - "colon": "{}: ", "argument_required": "Parametern '{argument}' krävs", "password": "Lösenord", "warn_the_user_that_lock_is_acquired": "det andra kommandot har bara slutförts, nu startar du det här kommandot", @@ -42,12 +39,8 @@ "corrupted_yaml": "Skadad yaml läst från {ressource} (anledning: {error})", "corrupted_json": "Skadad json läst från {ressource} (anledning: {error})", "unknown_error_reading_file": "Okänt fel vid försök att läsa filen {file} (anledning: {error})", - "unable_retrieve_session": "Det gick inte att hämta sessionen eftersom '{exception}'", "unable_authenticate": "Det går inte att verifiera", - "ldap_server_down": "Det går inte att nå LDAP-servern", "invalid_usage": "Ogiltig användning, pass --help för att se hjälp", - "invalid_token": "Ogiltigt token - verifiera", "instance_already_running": "Det finns redan en YunoHost-operation. Vänta tills den är klar innan du kör en annan.", - "authentication_required_long": "Autentisering krävs för att utföra denna åtgärd", "authentication_required": "Autentisering krävs" } \ No newline at end of file diff --git a/locales/tr.json b/locales/tr.json index 16789dba..7df0f93e 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -1,15 +1,10 @@ { "argument_required": "{argument} argümanı gerekli", "authentication_required": "Yetklendirme gerekli", - "authentication_required_long": "Bu işlemi yapmak içi yetkilendirme gerekli", - "colon": "{}: ", "confirm": "{prompt}'i doğrulayın", "error": "Hata:", "instance_already_running": "Halihazırda bir YunoHost operasyonu var. Lütfen başka bir tane çalıştırmadan önce bitmesini bekleyin.", "invalid_argument": "Geçersiz argüman '{argument}': {error}", - "invalid_password": "Geçersiz parola", - "ldap_attribute_already_exists": "'{attribute}={value}' özelliği zaten mevcut", - "ldap_server_down": "LDAP sunucusuna erişilemiyor", "logged_in": "Giriş yapıldı", "logged_out": "Çıkış yapıldı", "not_logged_in": "Giriş yapmadınız", @@ -20,11 +15,10 @@ "server_already_running": "Bu portta zaten çalışan bir sunucu var", "success": "İşlem Başarılı!", "unable_authenticate": "Yetkilendirme başarısız", - "unable_retrieve_session": "'{exception}' nedeniyle oturum alınamadı", "values_mismatch": "Değerler uyuşmuyor", "warning": "Uyarı:", "websocket_request_expected": "WebSocket isteği gerekli", - "warn_the_user_that_lock_is_acquired": "diğer komut şimdi tamamlandı, şimdi bu komutu başlatıyor", + "warn_the_user_that_lock_is_acquired": "Diğer komut şimdi tamamlandı, şimdi bu komutu başlatıyor", "warn_the_user_about_waiting_lock_again": "Hala bekliyor...", "warn_the_user_about_waiting_lock": "Başka bir YunoHost komutu şu anda çalışıyor, bunu çalıştırmadan önce bitmesini bekliyoruz", "download_bad_status_code": "{url} döndürülen durum kodu {code}", @@ -35,7 +29,7 @@ "error_changing_file_permissions": "{path} için izinler değiştirilirken hata oluştu: {error}", "error_removing": "{path} kaldırılırken hata oluştu: {error}", "error_writing_file": "{file} dosyası yazılırken hata oluştu: {error}", - "corrupted_toml": "{ressource} kaynağından okunan bozuk toml (nedeni: {error})", + "corrupted_toml": "{ressource} kaynağından okunan bozuk TOML(nedeni: {error})", "corrupted_yaml": "{ressource} kaynağından bozuk yaml okunuyor (nedeni: {error})", "corrupted_json": "{ressource} adresinden okunan bozuk json (nedeni: {error})", "unknown_error_reading_file": "{file} dosyasını okumaya çalışırken bilinmeyen hata (nedeni: {error})", @@ -44,7 +38,6 @@ "unknown_user": "Bilinmeyen '{user}' kullanıcı", "unknown_group": "Bilinmeyen '{group}' grubu", "invalid_usage": "Geçersiz kullanım, yardım görmek için --help iletin", - "invalid_token": "Geçersiz simge - lütfen kimlik doğrulaması yapın", "info": "Bilgi:", "folder_exists": "Klasör zaten var: '{path}'", "file_not_exist": "Dosya mevcut değil: '{path}'", diff --git a/locales/uk.json b/locales/uk.json index 1d78f956..017feae9 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -41,7 +41,7 @@ "deprecated_command_alias": "'{prog} {old}' застаріла і буде видалена, замість неї використовуйте '{prog} {new}'", "deprecated_command": "'{prog} {command}' застаріла і буде видалена", "confirm": "Підтвердити {prompt}", - "colon": "{}: ", "authentication_required": "Потрібна автентифікація", - "argument_required": "Потрібен аргумент '{argument}'" -} + "argument_required": "Потрібен аргумент '{argument}'", + "edit_text_question": "{}. Редагувати цей текст? [yN]: " +} \ No newline at end of file diff --git a/moulinette/actionsmap.py b/moulinette/actionsmap.py index b465f33d..386018ec 100644 --- a/moulinette/actionsmap.py +++ b/moulinette/actionsmap.py @@ -6,6 +6,7 @@ import logging import glob import pickle as pickle +from typing import List, Optional from time import time from collections import OrderedDict from importlib import import_module @@ -30,7 +31,6 @@ logger = logging.getLogger("moulinette.actionsmap") class _ExtraParameter(object): - """ Argument parser for an extra parameter. @@ -39,21 +39,14 @@ class _ExtraParameter(object): """ + name: Optional[str] = None + + """A list of interface for which the parameter doesn't apply to""" + skipped_iface: List[str] = [] + def __init__(self, iface): self.iface = iface - # Required variables - # Each extra parameters classes must overwrite these variables. - - """The extra parameter name""" - name = None - - # Optional variables - # Each extra parameters classes can overwrite these variables. - - """A list of interface for which the parameter doesn't apply""" - skipped_iface = [] - # Virtual methods # Each extra parameters classes can implement these methods. @@ -621,7 +614,7 @@ class ActionsMap(object): # This var is ['*'] by default but could be set for example to # ['yunohost', 'yml_*'] - NAMESPACE_PATTERNS = env["NAMESPACES"] + NAMESPACE_PATTERNS = env["NAMESPACES"].split() # Look for all files that match the given patterns in the actionsmap dir for namespace_pattern in NAMESPACE_PATTERNS: diff --git a/moulinette/core.py b/moulinette/core.py index 3aa024ec..6f6fddd7 100644 --- a/moulinette/core.py +++ b/moulinette/core.py @@ -22,8 +22,6 @@ for key in env.keys(): if value_from_environ: env[key] = value_from_environ -env["NAMESPACES"] = env["NAMESPACES"].split() - def during_unittests_run(): return "TESTS_RUN" in os.environ @@ -248,7 +246,7 @@ class Moulinette18n(object): for n in self._namespaces.values(): n.set_locale(locale) - def g(self, key, *args, **kwargs): + def g(self, key: str, *args, **kwargs) -> str: """Retrieve proper translation for a moulinette key Attempt to retrieve value for a key from moulinette translations @@ -260,7 +258,7 @@ class Moulinette18n(object): """ return self._global.translate(key, *args, **kwargs) - def n(self, key, *args, **kwargs): + def n(self, key: str, *args, **kwargs) -> str: """Retrieve proper translation for a moulinette key Attempt to retrieve value for a key from current loaded namespace @@ -273,7 +271,7 @@ class Moulinette18n(object): """ return self._namespaces[self._current_namespace].translate(key, *args, **kwargs) - def key_exists(self, key): + def key_exists(self, key: str) -> bool: """Check if a key exists in the translation files Keyword arguments: @@ -300,7 +298,7 @@ class MoulinetteError(Exception): super(MoulinetteError, self).__init__(msg) self.strerror = msg - def content(self): + def content(self) -> str: return self.strerror diff --git a/moulinette/interfaces/__init__.py b/moulinette/interfaces/__init__.py index a5d32cac..d8bed853 100644 --- a/moulinette/interfaces/__init__.py +++ b/moulinette/interfaces/__init__.py @@ -7,6 +7,7 @@ import copy import datetime from collections import deque, OrderedDict from json.encoder import JSONEncoder +from typing import Optional from moulinette import m18n from moulinette.core import MoulinetteError @@ -44,7 +45,7 @@ class BaseActionsMapParser(object): # Each parser classes must implement these properties. """The name of the interface for which it is the parser""" - interface = None + interface: Optional[str] = None # Virtual methods # Each parser classes must implement these methods. diff --git a/moulinette/interfaces/api.py b/moulinette/interfaces/api.py index 2b57c3c4..9350c76e 100644 --- a/moulinette/interfaces/api.py +++ b/moulinette/interfaces/api.py @@ -268,6 +268,7 @@ class Session: return infos + @staticmethod def delete_infos(): response.set_cookie(f"session.{Session.actionsmap_name}", "", max_age=-1) diff --git a/moulinette/interfaces/cli.py b/moulinette/interfaces/cli.py index c75b8d60..a27f5dc1 100644 --- a/moulinette/interfaces/cli.py +++ b/moulinette/interfaces/cli.py @@ -2,12 +2,13 @@ import os import sys -import getpass import locale import logging import argparse +import tempfile from collections import OrderedDict from datetime import date, datetime +from subprocess import call from moulinette import m18n, Moulinette from moulinette.actionsmap import ActionsMap @@ -522,7 +523,17 @@ class Interface: credentials = self.prompt(msg, True, False, color="yellow") return authenticator.authenticate_credentials(credentials=credentials) - def prompt(self, message, is_password=False, confirm=False, color="blue"): + def prompt( + self, + message, + is_password=False, + confirm=False, + color="blue", + prefill="", + is_multiline=False, + autocomplete=[], + help=None, + ): """Prompt for a value Keyword arguments: @@ -534,15 +545,69 @@ class Interface: "Not a tty, can't do interactive prompts", raw_msg=True ) - if is_password: - prompt = lambda m: getpass.getpass(colorize(m18n.g("colon", m), color)) - else: - prompt = lambda m: input(colorize(m18n.g("colon", m), color)) - value = prompt(message) + def _prompt(message): + + if not is_multiline: + + import prompt_toolkit + from prompt_toolkit.contrib.completers import WordCompleter + from pygments.token import Token + + autocomplete_ = WordCompleter(autocomplete) + style = prompt_toolkit.styles.style_from_dict( + { + Token.Message: f"#ansi{color} bold", + } + ) + + def get_bottom_toolbar_tokens(cli): + if help: + return [(Token, help)] + else: + return [] + + def get_tokens(cli): + return [ + (Token.Message, message), + (Token, ": "), + ] + + return prompt_toolkit.prompt( + get_prompt_tokens=get_tokens, + get_bottom_toolbar_tokens=get_bottom_toolbar_tokens, + style=style, + default=prefill, + true_color=True, + completer=autocomplete_, + is_password=is_password, + ) + + else: + while True: + value = input( + colorize(m18n.g("edit_text_question", message), color) + ) + value = value.lower().strip() + if value in ["", "n", "no"]: + return prefill + elif value in ["y", "yes"]: + break + + initial_message = prefill.encode("utf-8") + + with tempfile.NamedTemporaryFile(suffix=".tmp") as tf: + tf.write(initial_message) + tf.flush() + call(["editor", tf.name]) + tf.seek(0) + edited_message = tf.read() + return edited_message.decode("utf-8") + + value = _prompt(message) if confirm: m = message[0].lower() + message[1:] - if prompt(m18n.g("confirm", prompt=m)) != value: + if _prompt(m18n.g("confirm", prompt=m)) != value: raise MoulinetteValidationError("values_mismatch") return value diff --git a/moulinette/utils/filesystem.py b/moulinette/utils/filesystem.py index 4844dd1f..208987a8 100644 --- a/moulinette/utils/filesystem.py +++ b/moulinette/utils/filesystem.py @@ -15,7 +15,7 @@ from moulinette.core import MoulinetteError # Files & directories -------------------------------------------------- -def read_file(file_path): +def read_file(file_path, file_mode="r"): """ Read a regular text file @@ -35,7 +35,7 @@ def read_file(file_path): # Open file and read content try: - with open(file_path, "r") as f: + with open(file_path, file_mode) as f: file_content = f.read() except IOError as e: raise MoulinetteError("cannot_open_file", file=file_path, error=str(e)) @@ -67,16 +67,17 @@ def read_json(file_path): return loaded_json -def read_yaml(file_path): +def read_yaml(file_): """ Safely read a yaml file Keyword argument: - file_path -- Path to the yaml file + file -- Path or stream to the yaml file """ # Read file - file_content = read_file(file_path) + file_path = file_ if isinstance(file_, str) else file_.name + file_content = read_file(file_) if isinstance(file_, str) else file_ # Try to load yaml to check if it's syntaxically correct try: @@ -118,8 +119,8 @@ def write_to_file(file_path, data, file_mode="w"): file_mode -- Mode used when writing the file. Option meant to be used by append_to_file to avoid duplicating the code of this function. """ - assert isinstance(data, str) or isinstance( - data, list + assert ( + isinstance(data, str) or isinstance(data, bytes) or isinstance(data, list) ), "Error: data '%s' should be either a string or a list but is of type '%s'" % ( data, type(data), @@ -135,7 +136,7 @@ def write_to_file(file_path, data, file_mode="w"): ) # If data is a list, check elements are strings and build a single string - if not isinstance(data, str): + if isinstance(data, list): for element in data: assert isinstance( element, str @@ -364,3 +365,10 @@ def rm(path, recursive=False, force=False): except OSError as e: if not force: raise MoulinetteError("error_removing", path=path, error=str(e)) + + +def cp(source, dest, recursive=False, **kwargs): + if recursive and os.path.isdir(source): + return shutil.copytree(source, dest, symlinks=True, **kwargs) + else: + return shutil.copy2(source, dest, follow_symlinks=False, **kwargs) diff --git a/moulinette/utils/process.py b/moulinette/utils/process.py index 6b60c304..e4f2cf31 100644 --- a/moulinette/utils/process.py +++ b/moulinette/utils/process.py @@ -88,6 +88,13 @@ def call_async_output(args, callback, **kwargs): break callback(message) + while True: + try: + callback, message = log_queue.get_nowait() + except queue.Empty: + break + + callback(message) finally: kwargs["stdout"].close() kwargs["stderr"].close() @@ -108,7 +115,7 @@ class LogPipe(threading.Thread): self.log_callback = log_callback self.fdRead, self.fdWrite = os.pipe() - self.pipeReader = os.fdopen(self.fdRead) + self.pipeReader = os.fdopen(self.fdRead, "rb") self.queue = queue @@ -120,8 +127,8 @@ class LogPipe(threading.Thread): def run(self): """Run the thread, logging everything.""" - for line in iter(self.pipeReader.readline, ""): - self.queue.put((self.log_callback, line.strip("\n"))) + for line in iter(self.pipeReader.readline, b""): + self.queue.put((self.log_callback, line.decode("utf-8").strip("\n"))) self.pipeReader.close() diff --git a/setup.py b/setup.py index 7bc0fc9e..0e53154c 100755 --- a/setup.py +++ b/setup.py @@ -2,9 +2,18 @@ import os import sys +import subprocess + from setuptools import setup, find_packages from moulinette import env +version = ( + subprocess.check_output( + "head debian/changelog -n1 | awk '{print $2}' | tr -d '()'", shell=True + ) + .decode() + .strip() +) LOCALES_DIR = env["LOCALES_DIR"] @@ -24,6 +33,8 @@ install_deps = [ "toml", "gevent-websocket", "bottle", + "prompt-toolkit==1.0.15", # To be bumped to debian version once we're on bullseye (+ need tweaks in cli.py) + "pygments", ] test_deps = [ @@ -31,23 +42,24 @@ test_deps = [ "pytest-cov", "pytest-env", "pytest-mock", + "mock", "requests", "requests-mock", "webtest", ] + extras = { "install": install_deps, "tests": test_deps, } - setup( name="Moulinette", - version="2.0.0", + version=version, description="Prototype interfaces quickly and easily", author="Yunohost Team", author_email="yunohost@yunohost.org", - url="http://yunohost.org", + url="https://yunohost.org", license="AGPL", packages=find_packages(exclude=["test"]), data_files=[(LOCALES_DIR, locale_files)], diff --git a/test/autofix_locale_format.py b/test/autofix_locale_format.py new file mode 100644 index 00000000..dd781263 --- /dev/null +++ b/test/autofix_locale_format.py @@ -0,0 +1,53 @@ +import re +import json +import glob + +# List all locale files (except en.json being the ref) +locale_folder = "locales/" +locale_files = glob.glob(locale_folder + "*.json") +locale_files = [filename.split("/")[-1] for filename in locale_files] +locale_files.remove("en.json") + +reference = json.loads(open(locale_folder + "en.json").read()) + + +def fix_locale(locale_file): + + this_locale = json.loads(open(locale_folder + locale_file).read()) + fixed_stuff = False + + # We iterate over all keys/string in en.json + for key, string in reference.items(): + + # Ignore check if there's no translation yet for this key + if key not in this_locale: + continue + + # Then we check that every "{stuff}" (for python's .format()) + # should also be in the translated string, otherwise the .format + # will trigger an exception! + subkeys_in_ref = [k[0] for k in re.findall(r"{(\w+)(:\w)?}", string)] + subkeys_in_this_locale = [ + k[0] for k in re.findall(r"{(\w+)(:\w)?}", this_locale[key]) + ] + + if set(subkeys_in_ref) != set(subkeys_in_this_locale) and ( + len(subkeys_in_ref) == len(subkeys_in_this_locale) + ): + for i, subkey in enumerate(subkeys_in_ref): + this_locale[key] = this_locale[key].replace( + "{%s}" % subkeys_in_this_locale[i], "{%s}" % subkey + ) + fixed_stuff = True + + if fixed_stuff: + json.dump( + this_locale, + open(locale_folder + locale_file, "w"), + indent=4, + ensure_ascii=False, + ) + + +for locale_file in locale_files: + fix_locale(locale_file) diff --git a/test/reformat_locales.py b/test/reformat_locales.py new file mode 100644 index 00000000..9119c728 --- /dev/null +++ b/test/reformat_locales.py @@ -0,0 +1,60 @@ +import re + + +def reformat(lang, transformations): + + locale = open(f"locales/{lang}.json").read() + for pattern, replace in transformations.items(): + locale = re.compile(pattern).sub(replace, locale) + + open(f"locales/{lang}.json", "w").write(locale) + + +###################################################### + +godamn_spaces_of_hell = [ + "\u00a0", + "\u2000", + "\u2001", + "\u2002", + "\u2003", + "\u2004", + "\u2005", + "\u2006", + "\u2007", + "\u2008", + "\u2009", + "\u200A", + "\u202f", + "\u202F", + "\u3000", +] + +transformations = {s: " " for s in godamn_spaces_of_hell} +transformations.update( + { + "…": "...", + } +) + + +reformat("en", transformations) + +###################################################### + +transformations.update( + { + "courriel": "email", + "e-mail": "email", + "Courriel": "Email", + "E-mail": "Email", + "« ": "'", + "«": "'", + " »": "'", + "»": "'", + "’": "'", + # r"$(\w{1,2})'|( \w{1,2})'": r"\1\2’", + } +) + +reformat("fr", transformations) diff --git a/test/remove_stale_i18n_strings.py b/test/remove_stale_i18n_strings.py index 48f2180e..d3a4fe2a 100644 --- a/test/remove_stale_i18n_strings.py +++ b/test/remove_stale_i18n_strings.py @@ -2,7 +2,7 @@ import json import glob from collections import OrderedDict -locale_folder = "../locales/" +locale_folder = "locales/" locale_files = glob.glob(locale_folder + "*.json") locale_files = [filename.split("/")[-1] for filename in locale_files] locale_files.remove("en.json") diff --git a/test/test_auth.py b/test/test_auth.py index ffb6feb7..a245cc58 100644 --- a/test/test_auth.py +++ b/test/test_auth.py @@ -188,7 +188,7 @@ class TestAuthAPI: class TestAuthCLI: def test_login(self, moulinette_cli, capsys, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="dummy") + mocker.patch("prompt_toolkit.prompt", return_value="dummy") moulinette_cli.run(["testauth", "default"], output_as="plain") message = capsys.readouterr() @@ -201,25 +201,25 @@ class TestAuthCLI: def test_login_bad_password(self, moulinette_cli, capsys, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="Bad Password") + mocker.patch("prompt_toolkit.prompt", return_value="Bad Password") with pytest.raises(MoulinetteError): moulinette_cli.run(["testauth", "default"], output_as="plain") mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="Bad Password") + mocker.patch("prompt_toolkit.prompt", return_value="Bad Password") with pytest.raises(MoulinetteError): moulinette_cli.run(["testauth", "default"], output_as="plain") def test_login_wrong_profile(self, moulinette_cli, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="dummy") + mocker.patch("prompt_toolkit.prompt", return_value="dummy") with pytest.raises(MoulinetteError) as exception: moulinette_cli.run(["testauth", "other-profile"], output_as="none") assert "invalid_password" in str(exception) mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="yoloswag") + mocker.patch("prompt_toolkit.prompt", return_value="yoloswag") with pytest.raises(MoulinetteError) as exception: moulinette_cli.run(["testauth", "default"], output_as="none") @@ -239,7 +239,7 @@ class TestAuthCLI: def test_request_only_cli(self, capsys, moulinette_cli, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="dummy") + mocker.patch("prompt_toolkit.prompt", return_value="dummy") moulinette_cli.run(["testauth", "only-cli"], output_as="plain") message = capsys.readouterr() @@ -248,7 +248,7 @@ class TestAuthCLI: def test_request_not_logged_only_cli(self, capsys, moulinette_cli, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass") + mocker.patch("prompt_toolkit.prompt") with pytest.raises(MoulinetteError) as exception: moulinette_cli.run(["testauth", "only-cli"], output_as="plain") @@ -259,7 +259,7 @@ class TestAuthCLI: def test_request_with_callback(self, moulinette_cli, capsys, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="dummy") + mocker.patch("prompt_toolkit.prompt", return_value="dummy") moulinette_cli.run(["--version"], output_as="plain") message = capsys.readouterr() @@ -278,7 +278,7 @@ class TestAuthCLI: def test_request_with_arg(self, moulinette_cli, capsys, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="dummy") + mocker.patch("prompt_toolkit.prompt", return_value="dummy") moulinette_cli.run(["testauth", "with_arg", "yoloswag"], output_as="plain") message = capsys.readouterr() @@ -286,7 +286,7 @@ class TestAuthCLI: def test_request_arg_with_extra(self, moulinette_cli, capsys, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="dummy") + mocker.patch("prompt_toolkit.prompt", return_value="dummy") moulinette_cli.run( ["testauth", "with_extra_str_only", "YoLoSwAg"], output_as="plain" ) @@ -306,7 +306,7 @@ class TestAuthCLI: def test_request_arg_with_type(self, moulinette_cli, capsys, mocker): mocker.patch("os.isatty", return_value=True) - mocker.patch("getpass.getpass", return_value="dummy") + mocker.patch("prompt_toolkit.prompt", return_value="dummy") moulinette_cli.run(["testauth", "with_type_int", "12345"], output_as="plain") message = capsys.readouterr() diff --git a/test/test_process.py b/test/test_process.py index b3af92ff..2484101a 100644 --- a/test/test_process.py +++ b/test/test_process.py @@ -1,6 +1,7 @@ import os from subprocess import CalledProcessError +import mock import pytest from moulinette.utils.process import run_commands @@ -65,39 +66,71 @@ def test_run_shell_kwargs(): def test_call_async_output(test_file): + + mock_callback_stdout = mock.Mock() + mock_callback_stderr = mock.Mock() + def stdout_callback(a): - assert a == "foo" or a == "bar" + mock_callback_stdout(a) def stderr_callback(a): - assert False # we shouldn't reach this line + mock_callback_stderr(a) callbacks = (lambda l: stdout_callback(l), lambda l: stderr_callback(l)) call_async_output(["cat", str(test_file)], callbacks) + calls = [mock.call("foo"), mock.call("bar")] + mock_callback_stdout.assert_has_calls(calls) + mock_callback_stderr.assert_not_called() + + mock_callback_stdout.reset_mock() + mock_callback_stderr.reset_mock() + with pytest.raises(TypeError): call_async_output(["cat", str(test_file)], 1) - def callbackA(a): - assert a == "foo" or a == "bar" + mock_callback_stdout.assert_not_called() + mock_callback_stderr.assert_not_called() - def callbackB(a): - assert "cat: doesntexists" in a + mock_callback_stdout.reset_mock() + mock_callback_stderr.reset_mock() - callback = (callbackA, callbackB) + def callback_stdout(a): + mock_callback_stdout(a) + + def callback_stderr(a): + mock_callback_stderr(a) + + callback = (callback_stdout, callback_stderr) call_async_output(["cat", str(test_file)], callback) - call_async_output(["cat", "doesntexists"], callback) + calls = [mock.call("foo"), mock.call("bar")] + mock_callback_stdout.assert_has_calls(calls) + mock_callback_stderr.assert_not_called() + mock_callback_stdout.reset_mock() + mock_callback_stderr.reset_mock() + + env_var = {"LANG": "C"} + call_async_output(["cat", "doesntexists"], callback, env=env_var) + calls = [mock.call("cat: doesntexists: No such file or directory")] + mock_callback_stdout.assert_not_called() + mock_callback_stderr.assert_has_calls(calls) def test_call_async_output_kwargs(test_file, mocker): + + mock_callback_stdout = mock.Mock() + mock_callback_stdinfo = mock.Mock() + mock_callback_stderr = mock.Mock() + def stdinfo_callback(a): - assert False # we shouldn't reach this line + mock_callback_stdinfo(a) def stdout_callback(a): - assert a == "foo" or a == "bar" + mock_callback_stdout(a) def stderr_callback(a): - assert False # we shouldn't reach this line + mock_callback_stderr(a) callbacks = ( lambda l: stdout_callback(l), @@ -107,16 +140,43 @@ def test_call_async_output_kwargs(test_file, mocker): with pytest.raises(ValueError): call_async_output(["cat", str(test_file)], callbacks, stdout=None) + mock_callback_stdout.assert_not_called() + mock_callback_stdinfo.assert_not_called() + mock_callback_stderr.assert_not_called() + + mock_callback_stdout.reset_mock() + mock_callback_stdinfo.reset_mock() + mock_callback_stderr.reset_mock() + with pytest.raises(ValueError): call_async_output(["cat", str(test_file)], callbacks, stderr=None) + mock_callback_stdout.assert_not_called() + mock_callback_stdinfo.assert_not_called() + mock_callback_stderr.assert_not_called() + + mock_callback_stdout.reset_mock() + mock_callback_stdinfo.reset_mock() + mock_callback_stderr.reset_mock() + with pytest.raises(TypeError): call_async_output(["cat", str(test_file)], callbacks, stdinfo=None) + mock_callback_stdout.assert_not_called() + mock_callback_stdinfo.assert_not_called() + mock_callback_stderr.assert_not_called() + + mock_callback_stdout.reset_mock() + mock_callback_stdinfo.reset_mock() + mock_callback_stderr.reset_mock() dirname = os.path.dirname(str(test_file)) os.mkdir(os.path.join(dirname, "testcwd")) call_async_output( ["cat", str(test_file)], callbacks, cwd=os.path.join(dirname, "testcwd") ) + calls = [mock.call("foo"), mock.call("bar")] + mock_callback_stdout.assert_has_calls(calls) + mock_callback_stdinfo.assert_not_called() + mock_callback_stderr.assert_not_called() def test_check_output(test_file): diff --git a/tox.ini b/tox.ini index 61c3e75d..ff656a7b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{37,39}-{pytest,lint,invalidcode} + py{37,39}-{pytest,lint,invalidcode,mypy} format format-check docs @@ -14,10 +14,12 @@ deps = py{37,39}-pytest: .[tests] py{37,39}-lint: flake8 py{37,39}-invalidcode: flake8 + py{37,39}-mypy: mypy >= 0.761 commands = - py{37,39}-pytest: pytest {posargs} -c pytest.ini - py{37,39}-lint: flake8 moulinette test - py{37,39}-invalidcode: flake8 moulinette test --select F + py{37,39}-pytest: pytest {posargs} -c pytest.ini + py{37,39}-lint: flake8 moulinette test + py{37,39}-invalidcode: flake8 moulinette test --select F + py{37,39}-mypy: mypy --ignore-missing-imports --install-types --non-interactive moulinette/ [gh-actions] python =