mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Merge remote-tracking branch 'origin/bookworm' into new-log-streaming-api
This commit is contained in:
commit
ed6ad7bf5f
56 changed files with 358 additions and 288 deletions
19
.github/workflows/autoblack.yml
vendored
19
.github/workflows/autoblack.yml
vendored
|
@ -8,16 +8,23 @@ jobs:
|
||||||
name: Check / auto apply black
|
name: Check / auto apply black
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Check files using the black formatter
|
- name: Check files using the black formatter
|
||||||
uses: rickstaa/action-black@v1
|
uses: psf/black@stable
|
||||||
id: action_black
|
id: black
|
||||||
with:
|
with:
|
||||||
black_args: "."
|
options: "."
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
- shell: pwsh
|
||||||
|
id: check_files_changed
|
||||||
|
run: |
|
||||||
|
# Diff HEAD with the previous commit
|
||||||
|
$diff = git diff
|
||||||
|
$HasDiff = $diff.Length -gt 0
|
||||||
|
Write-Host "::set-output name=files_changed::$HasDiff"
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: steps.action_black.outputs.is_formatted == 'true'
|
if: steps.check_files_changed.outputs.files_changed == 'true'
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
title: "Format Python code with Black"
|
title: "Format Python code with Black"
|
||||||
|
|
4
.github/workflows/i18n.yml
vendored
4
.github/workflows/i18n.yml
vendored
|
@ -8,7 +8,7 @@ jobs:
|
||||||
name: Autoreformat locale files
|
name: Autoreformat locale files
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Apply reformating scripts
|
- name: Apply reformating scripts
|
||||||
id: action_reformat
|
id: action_reformat
|
||||||
run: |
|
run: |
|
||||||
|
@ -18,7 +18,7 @@ jobs:
|
||||||
git diff -w --exit-code
|
git diff -w --exit-code
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
uses: peter-evans/create-pull-request@v3
|
uses: peter-evans/create-pull-request@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
title: "Reformat locale files"
|
title: "Reformat locale files"
|
||||||
|
|
18
.github/workflows/tox.yml
vendored
18
.github/workflows/tox.yml
vendored
|
@ -12,11 +12,11 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.9]
|
python-version: [3.11]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
|
@ -26,17 +26,17 @@ jobs:
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install tox tox-gh-actions
|
pip install tox tox-gh-actions
|
||||||
- name: Test with tox
|
- name: Test with tox
|
||||||
run: tox -e py39-pytest
|
run: tox -e py311-pytest
|
||||||
|
|
||||||
invalidcode:
|
invalidcode:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.9]
|
python-version: [3.11]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install tox
|
- name: Install tox
|
||||||
|
@ -44,6 +44,6 @@ jobs:
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install tox tox-gh-actions
|
pip install tox tox-gh-actions
|
||||||
- name: Linter
|
- name: Linter
|
||||||
run: tox -e py39-invalidcode
|
run: tox -e py311-invalidcode
|
||||||
- name: Mypy
|
- name: Mypy
|
||||||
run: tox -e py39-mypy
|
run: tox -e py311-mypy
|
||||||
|
|
27
debian/changelog
vendored
27
debian/changelog
vendored
|
@ -4,6 +4,33 @@ moulinette (12.0.0) unstable; urgency=low
|
||||||
|
|
||||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 04 May 2023 20:30:19 +0200
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 04 May 2023 20:30:19 +0200
|
||||||
|
|
||||||
|
moulinette (11.2.1) stable; urgency=low
|
||||||
|
|
||||||
|
- repo chores: various black enhancements
|
||||||
|
- [i18n] Translations updated for Arabic, Basque, Catalan, Chinese (Simplified), Czech, Dutch, English, Esperanto, French, Galician, German, Hindi, Indonesian, Italian, Japanese, Nepali, Norwegian Bokmål, Persian, Polish, Portuguese, Russian, Spanish, Swedish, Turkish, Ukrainian
|
||||||
|
|
||||||
|
Thanks to all contributors <3 ! (Alexandre Aubin, Francescc, José M, Metin Bektas, ppr, Psycojoker, rosbeef andino, Tagada, xabirequejo, xaloc33)
|
||||||
|
|
||||||
|
-- OniriCorpe <oniricorpe@yunohost.org> Sun, 19 May 2024 23:49:00 +0200
|
||||||
|
|
||||||
|
moulinette (11.2) stable; urgency=low
|
||||||
|
|
||||||
|
- [i18n] Translations updated for Japanese
|
||||||
|
|
||||||
|
Thanks to all contributors <3 ! (motcha)
|
||||||
|
|
||||||
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 17 Jul 2023 16:32:34 +0200
|
||||||
|
|
||||||
|
moulinette (11.1.5) stable; urgency=low
|
||||||
|
|
||||||
|
- setup.py: fix version specifier in python_requires, python tooling not happy with * i guess (2373a7fa)
|
||||||
|
- auth: prevent stupid issue where outdated cookie usage would trigger error 500 intead of 401, resulting in a ~bug after Yunohost self-upgrade and the webadmin is confused about the API not being up again (c06e1a91)
|
||||||
|
- i18n: Translations updated for Chinese (Simplified), Indonesian, Japanese
|
||||||
|
|
||||||
|
Thanks to all contributors <3 ! (liimee, motcha, Neko Nekowazarashi, Poesty Li)
|
||||||
|
|
||||||
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 10 Jul 2023 21:32:20 +0200
|
||||||
|
|
||||||
moulinette (11.1.4) stable; urgency=low
|
moulinette (11.1.4) stable; urgency=low
|
||||||
|
|
||||||
- Releasing as stable
|
- Releasing as stable
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Element:
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Returns a basic state dump."""
|
"""Returns a basic state dump."""
|
||||||
return 'Element' + str(self.index) + str(self.attributes)
|
return "Element" + str(self.index) + str(self.attributes)
|
||||||
|
|
||||||
def add(self, line):
|
def add(self, line):
|
||||||
"""Adds a line of input to the object.
|
"""Adds a line of input to the object.
|
||||||
|
@ -57,10 +57,10 @@ class Element:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _valid(line):
|
def _valid(line):
|
||||||
return line and not line.startswith('#')
|
return line and not line.startswith("#")
|
||||||
|
|
||||||
def _interesting(line):
|
def _interesting(line):
|
||||||
return line != 'objectClass: top'
|
return line != "objectClass: top"
|
||||||
|
|
||||||
if self.is_valid() and not _valid(line):
|
if self.is_valid() and not _valid(line):
|
||||||
return True
|
return True
|
||||||
|
@ -70,11 +70,11 @@ class Element:
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
"""Indicates whether a valid entry has been read."""
|
"""Indicates whether a valid entry has been read."""
|
||||||
return len(self.attributes) != 0 and self.attributes[0].startswith('dn: ')
|
return len(self.attributes) != 0 and self.attributes[0].startswith("dn: ")
|
||||||
|
|
||||||
def dn(self):
|
def dn(self):
|
||||||
"""Returns the DN for this entry."""
|
"""Returns the DN for this entry."""
|
||||||
if self.attributes[0].startswith('dn: '):
|
if self.attributes[0].startswith("dn: "):
|
||||||
return self.attributes[0][4:]
|
return self.attributes[0][4:]
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -86,12 +86,12 @@ class Element:
|
||||||
Element objects) and returns a string which declares a DOT edge, or an
|
Element objects) and returns a string which declares a DOT edge, or an
|
||||||
empty string, if no parent was found.
|
empty string, if no parent was found.
|
||||||
"""
|
"""
|
||||||
dn_components = self.dn().split(',')
|
dn_components = self.dn().split(",")
|
||||||
for i in range(1, len(dn_components) + 1):
|
for i in range(1, len(dn_components) + 1):
|
||||||
parent = ','.join(dn_components[i:])
|
parent = ",".join(dn_components[i:])
|
||||||
if parent in dnmap:
|
if parent in dnmap:
|
||||||
return ' n%d->n%d\n' % (dnmap[parent].index, self.index)
|
return " n%d->n%d\n" % (dnmap[parent].index, self.index)
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
def dot(self, dnmap):
|
def dot(self, dnmap):
|
||||||
"""Returns a text representation of the node and perhaps its parent edge.
|
"""Returns a text representation of the node and perhaps its parent edge.
|
||||||
|
@ -99,6 +99,7 @@ class Element:
|
||||||
Args:
|
Args:
|
||||||
- dnmap: dictionary mapping dn names to Element objects
|
- dnmap: dictionary mapping dn names to Element objects
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _format(attributes):
|
def _format(attributes):
|
||||||
result = [TITLE_ENTRY_TEMPALTE % attributes[0]]
|
result = [TITLE_ENTRY_TEMPALTE % attributes[0]]
|
||||||
|
|
||||||
|
@ -107,7 +108,12 @@ class Element:
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return TABLE_TEMPLATE % (self.index, '\n '.join(_format(self.attributes)), self.edge(dnmap))
|
return TABLE_TEMPLATE % (
|
||||||
|
self.index,
|
||||||
|
"\n ".join(_format(self.attributes)),
|
||||||
|
self.edge(dnmap),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Converter:
|
class Converter:
|
||||||
"""An LDIF to DOT converter."""
|
"""An LDIF to DOT converter."""
|
||||||
|
@ -144,7 +150,11 @@ class Converter:
|
||||||
e = Element()
|
e = Element()
|
||||||
if e.is_valid():
|
if e.is_valid():
|
||||||
self._append(e)
|
self._append(e)
|
||||||
return (BASE_TEMPLATE % (name, ''.join([e.dot(self.dnmap) for e in self.elements])))
|
return BASE_TEMPLATE % (
|
||||||
|
name,
|
||||||
|
"".join([e.dot(self.dnmap) for e in self.elements]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
BASE_TEMPLATE = """\
|
BASE_TEMPLATE = """\
|
||||||
strict digraph "%s" {
|
strict digraph "%s" {
|
||||||
|
@ -191,13 +201,13 @@ ENTRY_TEMPALTE = """\
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) > 2:
|
if len(sys.argv) > 2:
|
||||||
raise 'Expected at most one argument.'
|
raise "Expected at most one argument."
|
||||||
elif len(sys.argv) == 2:
|
elif len(sys.argv) == 2:
|
||||||
name = sys.argv[1]
|
name = sys.argv[1]
|
||||||
file = open(sys.argv[1], 'r')
|
file = open(sys.argv[1], "r")
|
||||||
else:
|
else:
|
||||||
name = '<stdin>'
|
name = "<stdin>"
|
||||||
file = sys.stdin
|
file = sys.stdin
|
||||||
print Converter().parse(file, name)
|
print(Converter().parse(file, name))
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
"error_writing_file": "طرأ هناك خطأ أثناء الكتابة في الملف {file}: {error}",
|
"error_writing_file": "طرأ هناك خطأ أثناء الكتابة في الملف {file}: {error}",
|
||||||
"error_removing": "خطأ أثناء عملية حذف {path}: {error}",
|
"error_removing": "خطأ أثناء عملية حذف {path}: {error}",
|
||||||
"error_changing_file_permissions": "خطأ أثناء عملية تعديل التصريحات لـ {path}: {error}",
|
"error_changing_file_permissions": "خطأ أثناء عملية تعديل التصريحات لـ {path}: {error}",
|
||||||
"invalid_url": "فشل الاتصال بـ {url} ... ربما تكون الخدمة معطلة ، أو أنك غير متصل بشكل صحيح بالإنترنت في IPv4 / IPv6.",
|
"invalid_url": "فشل الاتصال بـ {url}… ربما تكون الخدمة معطلة ، أو أنك غير متصل بشكل صحيح بالإنترنت في IPv4 / IPv6.",
|
||||||
"download_ssl_error": "خطأ في الاتصال الآمن عبر الـ SSL أثناء محاولة الربط بـ {url}",
|
"download_ssl_error": "خطأ في الاتصال الآمن عبر الـ SSL أثناء محاولة الربط بـ {url}",
|
||||||
"download_timeout": "{url} استغرق مدة طويلة جدا للإستجابة، فتوقّف.",
|
"download_timeout": "{url} استغرق مدة طويلة جدا للإستجابة، فتوقّف.",
|
||||||
"download_unknown_error": "خطأ أثناء عملية تنزيل البيانات مِن {url} : {error}",
|
"download_unknown_error": "خطأ أثناء عملية تنزيل البيانات مِن {url} : {error}",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"argument_required": "Es requereix l'argument {argument}",
|
"argument_required": "Es requereix l'argument «{argument}»",
|
||||||
"authentication_required": "Es requereix autenticació",
|
"authentication_required": "Es requereix autenticació",
|
||||||
"confirm": "Confirmar{prompt}",
|
"confirm": "Confirmar {prompt}",
|
||||||
"deprecated_command": "{prog}{command}és obsolet i es desinstal·larà en el futur",
|
"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",
|
"deprecated_command_alias": "{prog}{old}és obsolet i es desinstal·larà en el futur, utilitzeu {prog}{new}en el seu lloc",
|
||||||
"error": "Error:",
|
"error": "Error:",
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
"error_writing_file": "Error al escriure el fitxer {file}: {error}",
|
"error_writing_file": "Error al escriure el fitxer {file}: {error}",
|
||||||
"error_removing": "Error al eliminar {path}: {error}",
|
"error_removing": "Error al eliminar {path}: {error}",
|
||||||
"error_changing_file_permissions": "Error al canviar els permisos per {path}: {error}",
|
"error_changing_file_permissions": "Error al canviar els permisos per {path}: {error}",
|
||||||
"invalid_url": "URL invàlid {url} (el lloc web existeix?)",
|
"invalid_url": "No s'ha pogut connectar a {url}… pot ser que el servei estigui caigut, o que no hi hagi connexió a Internet amb IPv4/IPv6.",
|
||||||
"download_ssl_error": "Error SSL al connectar amb {url}",
|
"download_ssl_error": "Error SSL al connectar amb {url}",
|
||||||
"download_timeout": "{url} ha tardat massa en respondre, s'ha deixat d'esperar.",
|
"download_timeout": "{url} ha tardat massa en respondre, s'ha deixat d'esperar.",
|
||||||
"download_unknown_error": "Error al baixar dades des de {url}: {error}",
|
"download_unknown_error": "Error al baixar dades des de {url}: {error}",
|
||||||
|
@ -42,5 +42,6 @@
|
||||||
"corrupted_toml": "El fitxer TOML ha estat corromput en la lectura des de {ressource} (motiu: {error})",
|
"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": "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_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"
|
"warn_the_user_that_lock_is_acquired": "L'altra ordre tot just ha acabat, ara s'executarà aquesta ordre",
|
||||||
|
"edit_text_question": "{}. Edita aquest text ? [yN]: "
|
||||||
}
|
}
|
|
@ -34,13 +34,13 @@
|
||||||
"error_writing_file": "写入文件{file}失败:{error}",
|
"error_writing_file": "写入文件{file}失败:{error}",
|
||||||
"error_removing": "删除路径{path}失败:{error}",
|
"error_removing": "删除路径{path}失败:{error}",
|
||||||
"error_changing_file_permissions": "目录{path}权限修改失败:{error}",
|
"error_changing_file_permissions": "目录{path}权限修改失败:{error}",
|
||||||
"invalid_url": "{url} 连接失败... 可能是服务中断了,或者你没有正确连接到IPv4/IPv6的互联网。",
|
"invalid_url": "{url} 连接失败… 可能是服务中断了,或者你没有正确连接到IPv4/IPv6的互联网。",
|
||||||
"download_ssl_error": "连接{url}时发生SSL错误",
|
"download_ssl_error": "连接{url}时发生SSL错误",
|
||||||
"download_timeout": "{url}响应超时,放弃。",
|
"download_timeout": "{url}响应超时,放弃。",
|
||||||
"download_unknown_error": "下载{url}失败:{error}",
|
"download_unknown_error": "下载{url}失败:{error}",
|
||||||
"download_bad_status_code": "{url}返回状态码:{code}",
|
"download_bad_status_code": "{url}返回状态码:{code}",
|
||||||
"warn_the_user_that_lock_is_acquired": "另一个命令刚刚完成,现在启动此命令",
|
"warn_the_user_that_lock_is_acquired": "另一个命令刚刚完成,现在启动此命令",
|
||||||
"warn_the_user_about_waiting_lock_again": "仍在等待...",
|
"warn_the_user_about_waiting_lock_again": "仍在等待…",
|
||||||
"warn_the_user_about_waiting_lock": "目前正在运行另一个YunoHost命令,我们在运行此命令之前等待它完成",
|
"warn_the_user_about_waiting_lock": "目前正在运行另一个YunoHost命令,我们在运行此命令之前等待它完成",
|
||||||
"corrupted_toml": "从{ressource}读取的TOML已损坏(原因:{error})",
|
"corrupted_toml": "从{ressource}读取的TOML已损坏(原因:{error})",
|
||||||
"edit_text_question": "{}.编辑此文本?[yN]: "
|
"edit_text_question": "{}.编辑此文本?[yN]: "
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
"logged_out": "Jste odhlášen/a",
|
"logged_out": "Jste odhlášen/a",
|
||||||
"warn_the_user_that_lock_is_acquired": "Předchozí operace dokončena, nyní spouštíme tuto",
|
"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_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í",
|
"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í",
|
||||||
"download_bad_status_code": "{url} vrátil stavový kód {code}",
|
"download_bad_status_code": "{url} vrátil stavový kód {code}",
|
||||||
"download_unknown_error": "Chyba při stahování dat z {url}: {error}",
|
"download_unknown_error": "Chyba při stahování dat z {url}: {error}",
|
||||||
|
|
|
@ -32,13 +32,13 @@
|
||||||
"cannot_open_file": "Datei {file} konnte nicht geöffnet werden (Ursache: {error})",
|
"cannot_open_file": "Datei {file} konnte nicht geöffnet werden (Ursache: {error})",
|
||||||
"corrupted_yaml": "Beschädigtes YAML gelesen von {ressource} (reason: {error})",
|
"corrupted_yaml": "Beschädigtes YAML gelesen von {ressource} (reason: {error})",
|
||||||
"warn_the_user_that_lock_is_acquired": "Der andere Befehl wurde gerade abgeschlossen, starte jetzt diesen Befehl",
|
"warn_the_user_that_lock_is_acquired": "Der andere Befehl wurde gerade abgeschlossen, starte jetzt diesen Befehl",
|
||||||
"warn_the_user_about_waiting_lock_again": "Immer noch wartend...",
|
"warn_the_user_about_waiting_lock_again": "Immer noch wartend…",
|
||||||
"warn_the_user_about_waiting_lock": "Ein anderer YunoHost Befehl läuft gerade, wir warten bis er fertig ist, bevor dieser laufen kann",
|
"warn_the_user_about_waiting_lock": "Ein anderer YunoHost Befehl läuft gerade, wir warten bis er fertig ist, bevor dieser laufen kann",
|
||||||
"download_bad_status_code": "{url} lieferte folgende(n) Status Code(s) {code}",
|
"download_bad_status_code": "{url} lieferte folgende(n) Status Code(s) {code}",
|
||||||
"download_unknown_error": "Fehler beim Herunterladen von Daten von {url}: {error}",
|
"download_unknown_error": "Fehler beim Herunterladen von Daten von {url}: {error}",
|
||||||
"download_timeout": "{url} brauchte zu lange zum Antworten, hab aufgegeben.",
|
"download_timeout": "{url} brauchte zu lange zum Antworten, hab aufgegeben.",
|
||||||
"download_ssl_error": "SSL Fehler beim Verbinden zu {url}",
|
"download_ssl_error": "SSL Fehler beim Verbinden zu {url}",
|
||||||
"invalid_url": "Konnte keine Verbindung zu {url} herstellen... vielleicht ist der Dienst ausgefallen, oder Sie sind nicht richtig mit dem Internet in IPv4/IPv6 verbunden.",
|
"invalid_url": "Konnte keine Verbindung zu {url} herstellen… vielleicht ist der Dienst ausgefallen, oder Sie sind nicht richtig mit dem Internet in IPv4/IPv6 verbunden.",
|
||||||
"error_changing_file_permissions": "Fehler beim Ändern der Berechtigungen für {path}: {error}",
|
"error_changing_file_permissions": "Fehler beim Ändern der Berechtigungen für {path}: {error}",
|
||||||
"error_removing": "Fehler beim Entfernen {path}: {error}",
|
"error_removing": "Fehler beim Entfernen {path}: {error}",
|
||||||
"error_writing_file": "Fehler beim Schreiben von Datei {file}: {error}",
|
"error_writing_file": "Fehler beim Schreiben von Datei {file}: {error}",
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
"error_writing_file": "Error when writing file {file}: {error}",
|
"error_writing_file": "Error when writing file {file}: {error}",
|
||||||
"error_removing": "Error when removing {path}: {error}",
|
"error_removing": "Error when removing {path}: {error}",
|
||||||
"error_changing_file_permissions": "Error when changing permissions for {path}: {error}",
|
"error_changing_file_permissions": "Error when changing permissions for {path}: {error}",
|
||||||
"invalid_url": "Failed to connect to {url} ... maybe the service is down, or you are not properly connected to the Internet in IPv4/IPv6.",
|
"invalid_url": "Failed to connect to {url}... maybe the service is down, or you are not properly connected to the Internet in IPv4/IPv6.",
|
||||||
"download_ssl_error": "SSL error when connecting to {url}",
|
"download_ssl_error": "SSL error when connecting to {url}",
|
||||||
"download_timeout": "{url} took too long to answer, gave up.",
|
"download_timeout": "{url} took too long to answer, gave up.",
|
||||||
"download_unknown_error": "Error when downloading data from {url}: {error}",
|
"download_unknown_error": "Error when downloading data from {url}: {error}",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"password": "Pasvorto",
|
"password": "Pasvorto",
|
||||||
"warn_the_user_that_lock_is_acquired": "La alia komando ĵus kompletigis, nun komencante ĉi tiun komandon",
|
"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_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",
|
"warn_the_user_about_waiting_lock": "Alia komando de YunoHost funkcias ĝuste nun, ni atendas, ke ĝi finiĝos antaŭ ol funkcii ĉi tiu",
|
||||||
"download_bad_status_code": "{url} redonita statuskodo {code}",
|
"download_bad_status_code": "{url} redonita statuskodo {code}",
|
||||||
"download_unknown_error": "Eraro dum elŝutado de datumoj de {url}: {error}",
|
"download_unknown_error": "Eraro dum elŝutado de datumoj de {url}: {error}",
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
"corrupted_json": "Koruptita JSON legis de {ressource} (Kialo: {error})",
|
"corrupted_json": "Koruptita JSON legis de {ressource} (Kialo: {error})",
|
||||||
"unknown_error_reading_file": "Nekonata eraro dum provi legi dosieron {file} (kialo: {error})",
|
"unknown_error_reading_file": "Nekonata eraro dum provi legi dosieron {file} (kialo: {error})",
|
||||||
"cannot_write_file": "Ne povis skribi dosieron {file} (kialo: {error})",
|
"cannot_write_file": "Ne povis skribi dosieron {file} (kialo: {error})",
|
||||||
"cannot_open_file": "Ne povis malfermi dosieron {file: s} (kialo: {error: s})",
|
"cannot_open_file": "Ne povis malfermi dosieron {file} (kialo: {error})",
|
||||||
"websocket_request_expected": "Atendis ret-peto",
|
"websocket_request_expected": "Atendis ret-peto",
|
||||||
"warning": "Averto:",
|
"warning": "Averto:",
|
||||||
"values_mismatch": "Valoroj ne kongruas",
|
"values_mismatch": "Valoroj ne kongruas",
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
"error_writing_file": "Error al escribir el archivo {file}: {error}",
|
"error_writing_file": "Error al escribir el archivo {file}: {error}",
|
||||||
"error_removing": "Error al eliminar {path}: {error}",
|
"error_removing": "Error al eliminar {path}: {error}",
|
||||||
"error_changing_file_permissions": "Error al cambiar los permisos para {path}: {error}",
|
"error_changing_file_permissions": "Error al cambiar los permisos para {path}: {error}",
|
||||||
"invalid_url": "URL inválida {url} (¿Existe este sitio?).",
|
"invalid_url": "Imposible de conectarse a {url} (¿ la URL esta correcta, existe este sitio, o internet esta accesible?).",
|
||||||
"download_ssl_error": "Error SSL al conectar con {url}",
|
"download_ssl_error": "Error SSL al conectar con {url}",
|
||||||
"download_timeout": "{url} tardó demasiado en responder, abandono.",
|
"download_timeout": "{url} tardó demasiado en responder, abandono.",
|
||||||
"download_unknown_error": "Error al descargar datos desde {url} : {error}",
|
"download_unknown_error": "Error al descargar datos desde {url} : {error}",
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
"info": "Información:",
|
"info": "Información:",
|
||||||
"corrupted_toml": "Lectura corrupta de TOML desde {ressource} (motivo: {error})",
|
"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_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_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",
|
"warn_the_user_about_waiting_lock": "Otra orden de YunoHost se está ejecutando ahora, estamos esperando a que termine antes de ejecutar esta",
|
||||||
"edit_text_question": "{}. Editar este texto ? [sN]: "
|
"edit_text_question": "{}. Editar este texto ? [sN]: "
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"download_ssl_error": "SSL errorea {url}-(e)ra konektatzean",
|
"download_ssl_error": "SSL errorea {url}-(e)ra konektatzean",
|
||||||
"corrupted_toml": "{ressource}-eko/go TOMLa kaltetuta dago (zergatia: {error})",
|
"corrupted_toml": "{ressource}-eko/go TOMLa kaltetuta dago (zergatia: {error})",
|
||||||
"warn_the_user_about_waiting_lock": "YunoHosten beste komando bat ari da exekutatzen, horrek amaitu arte gaude zain",
|
"warn_the_user_about_waiting_lock": "YunoHosten beste komando bat ari da exekutatzen, horrek amaitu arte gaude zain",
|
||||||
"warn_the_user_about_waiting_lock_again": "Zain…",
|
"warn_the_user_about_waiting_lock_again": "Zain...",
|
||||||
"folder_exists": "Direktorioa existitzen da dagoeneko: '{path}'",
|
"folder_exists": "Direktorioa existitzen da dagoeneko: '{path}'",
|
||||||
"instance_already_running": "YunoHosten eragiketa bat exekutatzen ari da dagoeneko. Itxaron amaitu arte beste eragiketa bat abiarazi baino lehen.",
|
"instance_already_running": "YunoHosten eragiketa bat exekutatzen ari da dagoeneko. Itxaron amaitu arte beste eragiketa bat abiarazi baino lehen.",
|
||||||
"invalid_usage": "Erabilera okerra, idatzi --help aukerak ikusteko",
|
"invalid_usage": "Erabilera okerra, idatzi --help aukerak ikusteko",
|
||||||
|
@ -42,6 +42,6 @@
|
||||||
"error_writing_file": "Errorea {file} fitxategia idazterakoan: {error}",
|
"error_writing_file": "Errorea {file} fitxategia idazterakoan: {error}",
|
||||||
"error_removing": "Errorea {path} ezabatzerakoan: {error}",
|
"error_removing": "Errorea {path} ezabatzerakoan: {error}",
|
||||||
"download_bad_status_code": "{url} helbideak {code} egoera kodea agertu du",
|
"download_bad_status_code": "{url} helbideak {code} egoera kodea agertu du",
|
||||||
"invalid_url": "Ezinezkoa izan da {url}-(e)ra konektatzea… agian zerbitzua ez dago martxan, edo zeu ez zaude IPv4/IPv6 bidez ondo konektatuta internetera.",
|
"invalid_url": "{url}-(e)ra konektatzeak huts egin du... agian zerbitzua ez dago martxan, edo ez zaude IPv4/IPv6 bidez ondo konektatuta internetera.",
|
||||||
"download_timeout": "{url}(e)k denbora gehiegi behar izan du erantzuteko, bertan behera utzi du zerbitzariak."
|
"download_timeout": "{url}(e)k denbora gehiegi behar izan du erantzuteko, bertan behera utzi du zerbitzariak."
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
"argument_required": "استدلال '{argument}' ضروری است",
|
"argument_required": "استدلال '{argument}' ضروری است",
|
||||||
"password": "کلمه عبور",
|
"password": "کلمه عبور",
|
||||||
"warn_the_user_that_lock_is_acquired": "فرمان دیگر به تازگی تکمیل شده است ، اکنون این دستور را شروع کنید",
|
"warn_the_user_that_lock_is_acquired": "فرمان دیگر به تازگی تکمیل شده است ، اکنون این دستور را شروع کنید",
|
||||||
"warn_the_user_about_waiting_lock_again": "هنوز در انتظار...",
|
"warn_the_user_about_waiting_lock_again": "هنوز در انتظار…",
|
||||||
"warn_the_user_about_waiting_lock": "یکی دیگر از دستورات YunoHost در حال اجرا است ، ما منتظر هستیم تا قبل از اجرای این دستور به پایان برسد",
|
"warn_the_user_about_waiting_lock": "یکی دیگر از دستورات YunoHost در حال اجرا است ، ما منتظر هستیم تا قبل از اجرای این دستور به پایان برسد",
|
||||||
"download_bad_status_code": "{url} کد وضعیّت بازگشتی {code}",
|
"download_bad_status_code": "{url} کد وضعیّت بازگشتی {code}",
|
||||||
"download_unknown_error": "خطا هنگام بارگیری داده ها از {url}: {error}",
|
"download_unknown_error": "خطا هنگام بارگیری داده ها از {url}: {error}",
|
||||||
"download_timeout": "پاسخ {url} خیلی طول کشید ، منصرف شو.",
|
"download_timeout": "پاسخ {url} خیلی طول کشید ، منصرف شو.",
|
||||||
"download_ssl_error": "خطای SSL هنگام اتصال به {url}",
|
"download_ssl_error": "خطای SSL هنگام اتصال به {url}",
|
||||||
"invalid_url": "اتصال به {url} انجام نشد ... شاید سرویس خاموش باشد یا در IPv4/IPv6 به درستی به اینترنت متصل نشده باشید.",
|
"invalid_url": "اتصال به {url} انجام نشد … شاید سرویس خاموش باشد یا در IPv4/IPv6 به درستی به اینترنت متصل نشده باشید.",
|
||||||
"error_changing_file_permissions": "خطا هنگام تغییر مجوزهای {path}: {error}",
|
"error_changing_file_permissions": "خطا هنگام تغییر مجوزهای {path}: {error}",
|
||||||
"error_removing": "خطا هنگام حذف {path}: {error}",
|
"error_removing": "خطا هنگام حذف {path}: {error}",
|
||||||
"error_writing_file": "خطا هنگام نوشتن فایل {file}: {error}",
|
"error_writing_file": "خطا هنگام نوشتن فایل {file}: {error}",
|
||||||
|
|
|
@ -4,11 +4,11 @@
|
||||||
"confirm": "Confirmez {prompt}",
|
"confirm": "Confirmez {prompt}",
|
||||||
"deprecated_command": "'{prog} {command}' est déprécié et sera bientôt supprimé",
|
"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",
|
"deprecated_command_alias": "'{prog} {old}' est déprécié et sera bientôt supprimé, utilisez '{prog} {new}' à la place",
|
||||||
"error": "Erreur :",
|
"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}'",
|
"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_argument": "Argument '{argument}' incorrect : {error}",
|
||||||
"invalid_usage": "Utilisation erronée, utilisez --help pour accéder à l'aide",
|
"invalid_usage": "Utilisation erronée, utilisez --help pour accéder à l'aide",
|
||||||
"logged_in": "Connecté",
|
"logged_in": "Connecté",
|
||||||
"logged_out": "Déconnecté",
|
"logged_out": "Déconnecté",
|
||||||
|
@ -16,32 +16,32 @@
|
||||||
"operation_interrupted": "Opération interrompue",
|
"operation_interrupted": "Opération interrompue",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"pattern_not_match": "Ne correspond pas au motif",
|
"pattern_not_match": "Ne correspond pas au motif",
|
||||||
"root_required": "Vous devez être super-utilisateur pour exécuter cette action",
|
"root_required": "Vous devez avoir les droits d'administration 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 !",
|
"success": "Succès !",
|
||||||
"unable_authenticate": "Impossible de vous authentifier",
|
"unable_authenticate": "Impossible de vous authentifier",
|
||||||
"unknown_group": "Le groupe '{group}' est inconnu",
|
"unknown_group": "Le groupe '{group}' est inconnu",
|
||||||
"unknown_user": "L'utilisateur '{user}' est inconnu",
|
"unknown_user": "Le compte '{user}' est inconnu",
|
||||||
"values_mismatch": "Les valeurs ne correspondent pas",
|
"values_mismatch": "Les valeurs ne correspondent pas",
|
||||||
"warning": "Attention :",
|
"warning": "Attention :",
|
||||||
"websocket_request_expected": "Une requête WebSocket est attendue",
|
"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})",
|
"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} (raison :{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})",
|
"corrupted_json": "Fichier JSON corrompu en lecture depuis {ressource} (raison : {error})",
|
||||||
"error_writing_file": "Erreur en écrivant le fichier {file} : {error}",
|
"error_writing_file": "Erreur en écrivant le fichier {file} : {error}",
|
||||||
"error_removing": "Erreur lors de la suppression {path} : {error}",
|
"error_removing": "Erreur lors de la suppression {path} : {error}",
|
||||||
"error_changing_file_permissions": "Erreur lors de la modification des autorisations pour {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 en panne ou que vous n'êtes pas correctement connecté à Internet en IPv4/IPv6.",
|
||||||
"download_ssl_error": "Erreur SSL lors de la connexion à {url}",
|
"download_ssl_error": "Erreur SSL lors de la connexion à {url}",
|
||||||
"download_timeout": "{url} a pris trop de temps pour répondre : abandon.",
|
"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_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}",
|
"download_bad_status_code": "{url} renvoie le code d'état {code}",
|
||||||
"corrupted_yaml": "Fichier YAML corrompu en lecture depuis {ressource} (raison : {error})",
|
"corrupted_yaml": "Fichier YAML corrompu en lecture depuis {ressource} (raison : {error})",
|
||||||
"info": "Info :",
|
"info": "Info :",
|
||||||
"corrupted_toml": "Fichier TOML corrompu en lecture depuis {ressource} (raison : {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": "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_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",
|
"warn_the_user_that_lock_is_acquired": "La commande précédente vient de se terminer, lancement de cette nouvelle commande",
|
||||||
"edit_text_question": "{}. Modifier ce texte ? [yN] : "
|
"edit_text_question": "{}. Modifier ce texte ? [yN] : "
|
||||||
}
|
}
|
|
@ -26,13 +26,13 @@
|
||||||
"not_logged_in": "Non iniciaches sesión",
|
"not_logged_in": "Non iniciaches sesión",
|
||||||
"logged_in": "Sesión iniciada",
|
"logged_in": "Sesión iniciada",
|
||||||
"warn_the_user_that_lock_is_acquired": "O outro comando rematou, agora executarase este",
|
"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_again": "Agardando…",
|
||||||
"warn_the_user_about_waiting_lock": "Estase executando outro comando de YunoHost neste intre, estamos agardando a que remate para executar este",
|
"warn_the_user_about_waiting_lock": "Estase executando outro comando de YunoHost neste intre, estamos agardando a que remate para executar este",
|
||||||
"download_bad_status_code": "{url} devolveu o código de estado {code}",
|
"download_bad_status_code": "{url} devolveu o código de estado {code}",
|
||||||
"download_unknown_error": "Erro ao descargar os datos desde {url}: {error}",
|
"download_unknown_error": "Erro ao descargar os datos desde {url}: {error}",
|
||||||
"download_timeout": "{url} está tardando en responder, deixámolo.",
|
"download_timeout": "{url} está tardando en responder, deixámolo.",
|
||||||
"download_ssl_error": "Erro SSL ao conectar con {url}",
|
"download_ssl_error": "Erro SSL ao conectar con {url}",
|
||||||
"invalid_url": "Fallou a conexión con {url} ... pode que o servizo esté caído, ou que non teñas conexión a Internet con IPv4/IPv6.",
|
"invalid_url": "Fallou a conexión con {url}... pode que o servizo estea caído, ou que non teñas conexión a Internet con IPv4/IPv6.",
|
||||||
"error_changing_file_permissions": "Erro ao cambiar os permisos de {path}: {error}",
|
"error_changing_file_permissions": "Erro ao cambiar os permisos de {path}: {error}",
|
||||||
"error_removing": "Erro ao eliminar {path}: {error}",
|
"error_removing": "Erro ao eliminar {path}: {error}",
|
||||||
"error_writing_file": "Erro ao escribir o ficheiro {file}: {error}",
|
"error_writing_file": "Erro ao escribir o ficheiro {file}: {error}",
|
||||||
|
|
|
@ -1,19 +1,46 @@
|
||||||
{
|
{
|
||||||
"argument_required": "Argumen '{argument}' dibutuhkan",
|
"argument_required": "Argumen '{argument}' dibutuhkan",
|
||||||
"authentication_required": "Otentikasi dibutuhkan",
|
"authentication_required": "Autentikasi dibutuhkan",
|
||||||
"deprecated_command": "'{prog} {command}' sudah usang dan akan dihapus nanti",
|
"deprecated_command": "'{prog} {command}' sudah usang dan akan dihapus nanti",
|
||||||
"logged_out": "Berhasil keluar",
|
"logged_out": "Berhasil keluar",
|
||||||
"password": "Kata sandi",
|
"password": "Kata sandi",
|
||||||
"deprecated_command_alias": "'{prog} {old}' sudah usang dan akan dihapus nanti, gunakan '{prog} {new}' saja",
|
"deprecated_command_alias": "'{prog} {old}' sudah usang dan akan dihapus nanti, gunakan '{prog} {new}'",
|
||||||
"info": "Informasi:",
|
"info": "Informasi:",
|
||||||
"instance_already_running": "Sudah ada operasi YunoHost yang sedang berjalan. Tunggu itu selesai sebelum menjalankan yang lain.",
|
"instance_already_running": "Sudah ada tindakan YunoHost yang sedang berjalan. Tunggu itu selesai sebelum menjalankan yang lain.",
|
||||||
"logged_in": "Berhasil masuk",
|
"logged_in": "Berhasil masuk",
|
||||||
"warning": "Peringatan:",
|
"warning": "Peringatan:",
|
||||||
"cannot_open_file": "Tidak dapat membuka berkas {file} (alasan: {error})",
|
"cannot_open_file": "Tidak dapat membuka berkas {file} (alasan: {error})",
|
||||||
"error_removing": "Terjadi kesalahan ketika menghapus {path}: {error}",
|
"error_removing": "Terjadi galat ketika menghapus {path}: {error}",
|
||||||
"success": "Berhasil!",
|
"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": "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...",
|
"warn_the_user_about_waiting_lock_again": "Masih menunggu…",
|
||||||
"unable_authenticate": "Tidak dapat mengotentikasi",
|
"unable_authenticate": "Tidak dapat mengautentikasi",
|
||||||
"warn_the_user_that_lock_is_acquired": "Perintah yang tadi baru saja selesai, akan memulai perintah ini"
|
"warn_the_user_that_lock_is_acquired": "Perintah yang tadi baru saja selesai, akan memulai perintah ini",
|
||||||
|
"server_already_running": "Sebuah peladen telah berjalan di porta tersebut",
|
||||||
|
"unknown_group": "Kelompok '{group}' tidak diketahui",
|
||||||
|
"unknown_user": "Pengguna '{user}' tidak diketahui",
|
||||||
|
"values_mismatch": "Tidak sama",
|
||||||
|
"cannot_write_file": "Tidak dapat menyimpan berkas {file} (alasan: {error})",
|
||||||
|
"unknown_error_reading_file": "Galat yang tidak diketahui ketika membaca berkas {file} (alasan: {error})",
|
||||||
|
"invalid_url": "Gagal terhubung dengan {url} … mungkin layanan tersebut sedang tidak berjalan atau Anda tidak terhubung ke internet di IPv4/IPv6.",
|
||||||
|
"download_timeout": "{url} memakan waktu yang lama untuk menjawab, menyerah.",
|
||||||
|
"download_unknown_error": "Galat ketika mengunduh data dari {url}: {error}",
|
||||||
|
"download_bad_status_code": "{url} menjawab dengan kode status {code}",
|
||||||
|
"confirm": "Konfirmasi {prompt}",
|
||||||
|
"edit_text_question": "{}. Sunting teks ini ? [yN]: ",
|
||||||
|
"error": "Galat:",
|
||||||
|
"file_not_exist": "Berkas tidak ada: '{path}'",
|
||||||
|
"folder_exists": "Berkas sudah ada: '{path}'",
|
||||||
|
"invalid_argument": "Argumen tidak valid '{argument}': {error}",
|
||||||
|
"invalid_usage": "Tidak valid, ikutkan --help untuk melihat bantuan",
|
||||||
|
"not_logged_in": "Anda belum masuk",
|
||||||
|
"operation_interrupted": "Tindakan terganggu",
|
||||||
|
"error_writing_file": "Galat ketika menyimpan berkas {file}: {error}",
|
||||||
|
"error_changing_file_permissions": "Galat ketika mengubah izin untuk {path}: {error}",
|
||||||
|
"download_ssl_error": "Galat SSL ketika menghubungi {url}",
|
||||||
|
"pattern_not_match": "Tidak cocok dengan pola",
|
||||||
|
"root_required": "Anda harus berada di root untuk melakukan tindakan ini",
|
||||||
|
"corrupted_yaml": "Pembacaan rusak untuk YAML {ressource} (alasan: {error})",
|
||||||
|
"corrupted_toml": "Pembacaan rusak untuk TOML {ressource} (alasan: {error})",
|
||||||
|
"corrupted_json": "Pembacaan rusak untuk JSON {ressource} (alasan: {error})"
|
||||||
}
|
}
|
|
@ -33,14 +33,14 @@
|
||||||
"error_writing_file": "Errore durante la scrittura del file {file}: {error}",
|
"error_writing_file": "Errore durante la scrittura del file {file}: {error}",
|
||||||
"error_removing": "Errore durante la rimozione {path}: {error}",
|
"error_removing": "Errore durante la rimozione {path}: {error}",
|
||||||
"error_changing_file_permissions": "Errore durante il cambio di permessi per {path}: {error}",
|
"error_changing_file_permissions": "Errore durante il cambio di permessi per {path}: {error}",
|
||||||
"invalid_url": "Fallita connessione a {url}... magari il servizio è down, o non sei connesso correttamente ad internet con IPv4/IPv6.",
|
"invalid_url": "Fallita connessione a {url}… magari il servizio è down, o non sei connesso correttamente ad internet con IPv4/IPv6.",
|
||||||
"download_ssl_error": "Errore SSL durante la connessione a {url}",
|
"download_ssl_error": "Errore SSL durante la connessione a {url}",
|
||||||
"download_timeout": "{url} ci ha messo troppo a rispondere, abbandonato.",
|
"download_timeout": "{url} ci ha messo troppo a rispondere, abbandonato.",
|
||||||
"download_unknown_error": "Errore durante il download di dati da {url} : {error}",
|
"download_unknown_error": "Errore durante il download di dati da {url} : {error}",
|
||||||
"download_bad_status_code": "{url} ha restituito il codice di stato {code}",
|
"download_bad_status_code": "{url} ha restituito il codice di stato {code}",
|
||||||
"info": "Info:",
|
"info": "Info:",
|
||||||
"warn_the_user_that_lock_is_acquired": "L'altro comando è appena completato, ora avvio questo comando",
|
"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_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",
|
"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})",
|
"corrupted_toml": "TOML corrotto da {ressource} (motivo: {error})",
|
||||||
"edit_text_question": "{}. Modificare il testo? [yN]: "
|
"edit_text_question": "{}. Modificare il testo? [yN]: "
|
||||||
|
|
47
locales/ja.json
Normal file
47
locales/ja.json
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"logged_out": "ログアウトしました",
|
||||||
|
"password": "パスワード",
|
||||||
|
"argument_required": "引数 '{argument}' が必要です",
|
||||||
|
"authentication_required": "認証が必要",
|
||||||
|
"confirm": "{prompt}の確認",
|
||||||
|
"deprecated_command": "'{prog} {command}' は非推奨であり、将来削除される予定です",
|
||||||
|
"deprecated_command_alias": "'{prog} {old}' は非推奨であり、今後削除される予定です。代わりに '{prog} {new}' を使用してください",
|
||||||
|
"edit_text_question": "{}.このテキストを編集しますか?[yN]: ",
|
||||||
|
"error": "エラー:",
|
||||||
|
"info": "インフォメーション:",
|
||||||
|
"download_unknown_error": "{url}からデータをダウンロードする際のエラー:{error}",
|
||||||
|
"download_bad_status_code": "{url}は状態コード {code} を返しました",
|
||||||
|
"warn_the_user_about_waiting_lock": "別のYunoHostコマンドが現在実行されているため、完了するのを待っています",
|
||||||
|
"warn_the_user_about_waiting_lock_again": "まだ待っています…",
|
||||||
|
"warn_the_user_that_lock_is_acquired": "他のコマンドが完了しました。このコマンドが開始されました",
|
||||||
|
"file_not_exist": "ファイルが存在しません: '{path}'",
|
||||||
|
"folder_exists": "フォルダは既に存在します: '{path}'",
|
||||||
|
"instance_already_running": "YunoHost 操作が既に実行されています。他の操作が完了するのを待ってください。",
|
||||||
|
"invalid_argument": "無効な引数 '{argument}': {error}",
|
||||||
|
"invalid_usage": "無効な使用法です。--help を渡してヘルプを表示します",
|
||||||
|
"logged_in": "ログイン済み",
|
||||||
|
"not_logged_in": "ログインしていません",
|
||||||
|
"operation_interrupted": "操作が中断されました",
|
||||||
|
"pattern_not_match": "パターンと一致しない",
|
||||||
|
"root_required": "このアクションを実行するには、rootである必要があります",
|
||||||
|
"server_already_running": "サーバーはそのポートで既に実行されています",
|
||||||
|
"success": "成功!",
|
||||||
|
"unable_authenticate": "認証できません",
|
||||||
|
"unknown_group": "不明な '{group}' グループ",
|
||||||
|
"unknown_user": "不明な '{user}' ユーザー",
|
||||||
|
"values_mismatch": "値が一致しない",
|
||||||
|
"warning": "警告:",
|
||||||
|
"websocket_request_expected": "WebSocket 要求が必要です",
|
||||||
|
"cannot_open_file": "ファイル{file}を開けませんでした(理由:{error})",
|
||||||
|
"cannot_write_file": "ファイル {file}を書き込めませんでした (理由: {error})",
|
||||||
|
"unknown_error_reading_file": "ファイル{file}を読み取ろうとしているときに不明なエラーが発生しました(理由:{error})",
|
||||||
|
"corrupted_json": "{ressource}から読み取られたJSONは破損していました(理由:{error})",
|
||||||
|
"corrupted_yaml": "破損した YAML が{ressource}から読み取られました (理由: {error})",
|
||||||
|
"corrupted_toml": "破損した TOML が{ressource}から読み取られました (理由: {error})",
|
||||||
|
"error_writing_file": "ファイル{file}書き込み時のエラー:{error}",
|
||||||
|
"error_removing": "{path}を削除するときのエラー:{error}",
|
||||||
|
"error_changing_file_permissions": "{path}のアクセス許可変更時のエラー: {error}",
|
||||||
|
"invalid_url": "{url}に接続できませんでした…サービスがダウンしているか、IPv4 / IPv6でインターネットに正しく接続されていない可能性があります。",
|
||||||
|
"download_ssl_error": "{url}への接続時のSSLエラー",
|
||||||
|
"download_timeout": "{url}は応答に時間がかかりすぎたため、あきらめました。"
|
||||||
|
}
|
1
locales/ko.json
Normal file
1
locales/ko.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -7,7 +7,7 @@
|
||||||
"unknown_user": "Ukjent '{user}' bruker",
|
"unknown_user": "Ukjent '{user}' bruker",
|
||||||
"unknown_group": "Ukjent '{group}' gruppe",
|
"unknown_group": "Ukjent '{group}' gruppe",
|
||||||
"unable_authenticate": "Kunne ikke identitetsbekrefte",
|
"unable_authenticate": "Kunne ikke identitetsbekrefte",
|
||||||
"success": "Vellykket.",
|
"success": "Vellykket!",
|
||||||
"operation_interrupted": "Operasjon forstyrret",
|
"operation_interrupted": "Operasjon forstyrret",
|
||||||
"not_logged_in": "Du er ikke innlogget",
|
"not_logged_in": "Du er ikke innlogget",
|
||||||
"logged_in": "Innlogget",
|
"logged_in": "Innlogget",
|
||||||
|
|
|
@ -32,13 +32,13 @@
|
||||||
"error_writing_file": "Fout tijdens het schrijven van bestand {file}: {error}",
|
"error_writing_file": "Fout tijdens het schrijven van bestand {file}: {error}",
|
||||||
"error_removing": "Fout tijdens het verwijderen van {path}: {error}",
|
"error_removing": "Fout tijdens het verwijderen van {path}: {error}",
|
||||||
"error_changing_file_permissions": "Fout tijdens het veranderen van machtiging voor {path}: {error}",
|
"error_changing_file_permissions": "Fout tijdens het veranderen van machtiging voor {path}: {error}",
|
||||||
"invalid_url": "Kon niet verbinden met {url} ... misschien is de dienst uit de lucht, of ben je niet goed verbonden via IPv4 of IPv6.",
|
"invalid_url": "Kon niet verbinden met {url}… misschien is de dienst uit de lucht, of ben je niet goed verbonden via IPv4 of IPv6.",
|
||||||
"download_ssl_error": "SSL fout gedurende verbinding met {url}",
|
"download_ssl_error": "SSL fout gedurende verbinding met {url}",
|
||||||
"download_timeout": "{url} neemt te veel tijd om te antwoorden, we geven het op.",
|
"download_timeout": "{url} neemt te veel tijd om te antwoorden, we geven het op.",
|
||||||
"download_unknown_error": "Fout tijdens het downloaden van data van {url}: {error}",
|
"download_unknown_error": "Fout tijdens het downloaden van data van {url}: {error}",
|
||||||
"download_bad_status_code": "{url} stuurt status code {code}",
|
"download_bad_status_code": "{url} stuurt status code {code}",
|
||||||
"warn_the_user_that_lock_is_acquired": "De andere opdracht is zojuist voltooid; nu wordt nu deze opdracht gestart",
|
"warn_the_user_that_lock_is_acquired": "De andere opdracht is zojuist voltooid; nu wordt nu deze opdracht gestart",
|
||||||
"warn_the_user_about_waiting_lock_again": "Nog steeds aan het wachten...",
|
"warn_the_user_about_waiting_lock_again": "Nog steeds aan het wachten…",
|
||||||
"warn_the_user_about_waiting_lock": "Een ander YunoHost commando wordt uitgevoerd, we wachten tot het gedaan is alovrens dit te starten",
|
"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 van {ressource} (reason: {error})",
|
"corrupted_toml": "Ongeldige TOML werd gelezen van {ressource} (reason: {error})",
|
||||||
"corrupted_yaml": "Ongeldig YAML bestand op {ressource} (reden: {error})",
|
"corrupted_yaml": "Ongeldig YAML bestand op {ressource} (reden: {error})",
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
"logged_out": "Wylogowano",
|
"logged_out": "Wylogowano",
|
||||||
"password": "Hasło",
|
"password": "Hasło",
|
||||||
"warn_the_user_that_lock_is_acquired": "Inne polecenie właśnie się zakończyło, teraz uruchamiam to polecenie",
|
"warn_the_user_that_lock_is_acquired": "Inne polecenie właśnie się zakończyło, teraz uruchamiam to polecenie",
|
||||||
"warn_the_user_about_waiting_lock_again": "Wciąż czekam...",
|
"warn_the_user_about_waiting_lock_again": "Wciąż czekam…",
|
||||||
"warn_the_user_about_waiting_lock": "Kolejne polecenie YunoHost jest obecnie uruchomione, czekamy na jego zakończenie przed uruchomieniem tego",
|
"warn_the_user_about_waiting_lock": "Kolejne polecenie YunoHost jest obecnie uruchomione, czekamy na jego zakończenie przed uruchomieniem tego",
|
||||||
"download_bad_status_code": "{url} zwrócił kod stanu {code}",
|
"download_bad_status_code": "{url} zwrócił kod stanu {code}",
|
||||||
"download_unknown_error": "Błąd podczas pobierania danych z {url}: {error}",
|
"download_unknown_error": "Błąd podczas pobierania danych z {url}: {error}",
|
||||||
"download_timeout": "{url} potrzebował zbyt dużo czasu na odpowiedź, rezygnacja.",
|
"download_timeout": "{url} potrzebował zbyt dużo czasu na odpowiedź, rezygnacja.",
|
||||||
"download_ssl_error": "Błąd SSL podczas łączenia z {url}",
|
"download_ssl_error": "Błąd SSL podczas łączenia z {url}",
|
||||||
"invalid_url": "Nie udało się połączyć z {url}... być może strona nie jest dostępna, lub nie jesteś prawidłowo połączony z Internetem po IPv4/IPv6.",
|
"invalid_url": "Nie udało się połączyć z {url}… być może strona nie jest dostępna, lub nie jesteś prawidłowo połączony z Internetem po IPv4/IPv6.",
|
||||||
"error_changing_file_permissions": "Błąd podczas zmiany uprawnień dla {path}: {error}",
|
"error_changing_file_permissions": "Błąd podczas zmiany uprawnień dla {path}: {error}",
|
||||||
"error_removing": "Błąd podczas usuwania {path}: {error}",
|
"error_removing": "Błąd podczas usuwania {path}: {error}",
|
||||||
"error_writing_file": "Błąd podczas zapisywania pliku {file}: {error}",
|
"error_writing_file": "Błąd podczas zapisywania pliku {file}: {error}",
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"corrupted_json": "JSON corrompido lido do {ressource} (motivo: {error})",
|
"corrupted_json": "JSON corrompido lido do {ressource} (motivo: {error})",
|
||||||
"corrupted_yaml": "YAML corrompido lido do {ressource} (motivo: {error})",
|
"corrupted_yaml": "YAML corrompido lido do {ressource} (motivo: {error})",
|
||||||
"warn_the_user_that_lock_is_acquired": "O outro comando acabou de concluir, agora iniciando este comando",
|
"warn_the_user_that_lock_is_acquired": "O outro comando acabou de concluir, agora iniciando este comando",
|
||||||
"warn_the_user_about_waiting_lock_again": "Ainda esperando...",
|
"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",
|
"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})",
|
"corrupted_toml": "TOML corrompido lido em {ressource} (motivo: {error})",
|
||||||
"info": "Informações:",
|
"info": "Informações:",
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
"corrupted_yaml": "Повреждённой YAML получен от {ressource} (причина: {error})",
|
"corrupted_yaml": "Повреждённой YAML получен от {ressource} (причина: {error})",
|
||||||
"error_writing_file": "Ошибка при записи файла {file}: {error}",
|
"error_writing_file": "Ошибка при записи файла {file}: {error}",
|
||||||
"error_removing": "Ошибка при удалении {path}: {error}",
|
"error_removing": "Ошибка при удалении {path}: {error}",
|
||||||
"invalid_url": "Не удалось подключиться к {url} ... возможно этот сервис недоступен или вы не подключены к Интернету через IPv4/IPv6.",
|
"invalid_url": "Не удалось подключиться к {url}… возможно этот сервис недоступен или вы не подключены к Интернету через IPv4/IPv6.",
|
||||||
"download_ssl_error": "Ошибка SSL при соединении с {url}",
|
"download_ssl_error": "Ошибка SSL при соединении с {url}",
|
||||||
"download_timeout": "Превышено время ожидания ответа от {url}.",
|
"download_timeout": "Превышено время ожидания ответа от {url}.",
|
||||||
"download_unknown_error": "Ошибка при загрузке данных с {url} : {error}",
|
"download_unknown_error": "Ошибка при загрузке данных с {url} : {error}",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
"root_required": "Чтобы выполнить это действие, вы должны иметь права root",
|
"root_required": "Чтобы выполнить это действие, вы должны иметь права root",
|
||||||
"corrupted_json": "Повреждённый json получен от {ressource} (причина: {error})",
|
"corrupted_json": "Повреждённый json получен от {ressource} (причина: {error})",
|
||||||
"warn_the_user_that_lock_is_acquired": "Другая команда только что завершилась, теперь запускается эта команда",
|
"warn_the_user_that_lock_is_acquired": "Другая команда только что завершилась, теперь запускается эта команда",
|
||||||
"warn_the_user_about_waiting_lock_again": "Все еще жду...",
|
"warn_the_user_about_waiting_lock_again": "Все еще жду…",
|
||||||
"warn_the_user_about_waiting_lock": "Сейчас запускается еще одна команда YunoHost, мы ждем ее завершения, прежде чем запустить эту",
|
"warn_the_user_about_waiting_lock": "Сейчас запускается еще одна команда YunoHost, мы ждем ее завершения, прежде чем запустить эту",
|
||||||
"download_bad_status_code": "{url} вернул код состояния {code}",
|
"download_bad_status_code": "{url} вернул код состояния {code}",
|
||||||
"error_changing_file_permissions": "Ошибка при изменении разрешений для {path}: {error}",
|
"error_changing_file_permissions": "Ошибка при изменении разрешений для {path}: {error}",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"warn_the_user_about_waiting_lock_again": "Väntar fortfarande …",
|
"warn_the_user_about_waiting_lock_again": "Väntar fortfarande…",
|
||||||
"download_bad_status_code": "{url} svarade med statuskod {code}",
|
"download_bad_status_code": "{url} svarade med statuskod {code}",
|
||||||
"download_timeout": "Gav upp eftersom {url} tog för lång tid på sig att svara.",
|
"download_timeout": "Gav upp eftersom {url} tog för lång tid på sig att svara.",
|
||||||
"download_ssl_error": "Ett SSL-fel påträffades vid anslutning till {url}",
|
"download_ssl_error": "Ett SSL-fel påträffades vid anslutning till {url}",
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
"warning": "Uyarı:",
|
"warning": "Uyarı:",
|
||||||
"websocket_request_expected": "WebSocket isteği gerekli",
|
"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_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",
|
"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}",
|
"download_bad_status_code": "{url} döndürülen durum kodu {code}",
|
||||||
"download_unknown_error": "{url} adresinden veri indirilirken hata oluştu: {error}",
|
"download_unknown_error": "{url} adresinden veri indirilirken hata oluştu: {error}",
|
||||||
"download_timeout": "{url} yanıtlaması çok uzun sürdü, pes etti.",
|
"download_timeout": "{url} yanıtlaması çok uzun sürdü, pes etti.",
|
||||||
"download_ssl_error": "{url} ağına bağlanırken SSL hatası",
|
"download_ssl_error": "{url} ağına bağlanırken SSL hatası",
|
||||||
"invalid_url": "{url} bağlanılamadı... URL çalışmıyor olabilir veya IPv4/IPv6 internete düzgün bir şekilde bağlı değilsiniz.",
|
"invalid_url": "{url} bağlanılamadı… URL çalışmıyor olabilir veya IPv4/IPv6 internete düzgün bir şekilde bağlı değilsiniz.",
|
||||||
"error_changing_file_permissions": "{path} için izinler değiştirilirken hata oluştu: {error}",
|
"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_removing": "{path} kaldırılırken hata oluştu: {error}",
|
||||||
"error_writing_file": "{file} dosyası yazılırken hata oluştu: {error}",
|
"error_writing_file": "{file} dosyası yazılırken hata oluştu: {error}",
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"logged_out": "Ви вийшли з системи",
|
"logged_out": "Ви вийшли з системи",
|
||||||
"invalid_url": "Помилка з'єднання із {url}... можливо, служба не працює, або ви неправильно під'єднані до Інтернету в IPv4/IPv6.",
|
"invalid_url": "Помилка з'єднання із {url}… можливо, служба не працює, або ви неправильно під'єднані до Інтернету в IPv4/IPv6.",
|
||||||
"warn_the_user_that_lock_is_acquired": "Інша команда щойно завершилася, тепер запускаємо цю команду",
|
"warn_the_user_that_lock_is_acquired": "Інша команда щойно завершилася, тепер запускаємо цю команду",
|
||||||
"warn_the_user_about_waiting_lock_again": "Досі очікуємо...",
|
"warn_the_user_about_waiting_lock_again": "Досі очікуємо…",
|
||||||
"warn_the_user_about_waiting_lock": "Зараз запускається ще одна команда YunoHost, ми чекаємо її завершення, перш ніж запустити цю",
|
"warn_the_user_about_waiting_lock": "Зараз запускається ще одна команда YunoHost, ми чекаємо її завершення, перш ніж запустити цю",
|
||||||
"download_bad_status_code": "{url} повернув код стану {code}",
|
"download_bad_status_code": "{url} повернув код стану {code}",
|
||||||
"download_unknown_error": "Помилка під час завантаження даних з {url}: {error}",
|
"download_unknown_error": "Помилка під час завантаження даних з {url}: {error}",
|
||||||
|
|
37
maintenance/make_changelog.sh
Normal file
37
maintenance/make_changelog.sh
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
VERSION="11.2.1"
|
||||||
|
RELEASE="stable"
|
||||||
|
REPO=$(basename $(git rev-parse --show-toplevel))
|
||||||
|
REPO_URL=$(git remote get-url origin)
|
||||||
|
ME=$(git config --get user.name)
|
||||||
|
EMAIL=$(git config --get user.email)
|
||||||
|
|
||||||
|
LAST_RELEASE=$(git tag --list 'debian/11.*' --sort="v:refname" | tail -n 1)
|
||||||
|
|
||||||
|
echo "$REPO ($VERSION) $RELEASE; urgency=low"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
git log $LAST_RELEASE.. -n 10000 --first-parent --pretty=tformat:' - %b%s (%h)' \
|
||||||
|
| sed -E "s&Merge .*#([0-9]+).*\$& \([#\1]\(http://github.com/YunoHost/$REPO/pull/\1\)\)&g" \
|
||||||
|
| sed -E "/Co-authored-by: .* <.*>/d" \
|
||||||
|
| grep -v "Translations update from Weblate" \
|
||||||
|
| tac
|
||||||
|
|
||||||
|
TRANSLATIONS=$(git log $LAST_RELEASE... -n 10000 --pretty=format:"%s" \
|
||||||
|
| grep "Translated using Weblate" \
|
||||||
|
| sed -E "s/Translated using Weblate \((.*)\)/\1/g" \
|
||||||
|
| sort | uniq | tr '\n' ', ' | sed -e 's/,$//g' -e 's/,/, /g')
|
||||||
|
[[ -z "$TRANSLATIONS" ]] || echo " - [i18n] Translations updated for $TRANSLATIONS"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
CONTRIBUTORS=$(git log -n10 --pretty=format:'%Cred%h%Creset %C(bold blue)(%an) %Creset%Cgreen(%cr)%Creset - %s %C(yellow)%d%Creset' --abbrev-commit $LAST_RELEASE... -n 10000 --pretty=format:"%an" \
|
||||||
|
| sort | uniq | grep -v "$ME" | grep -v 'yunohost-bot' | grep -vi 'weblate' \
|
||||||
|
| tr '\n' ', ' | sed -e 's/,$//g' -e 's/,/, /g')
|
||||||
|
[[ -z "$CONTRIBUTORS" ]] || echo " Thanks to all contributors <3 ! ($CONTRIBUTORS)"
|
||||||
|
echo ""
|
||||||
|
echo " -- $ME <$EMAIL> $(date -R)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PR links can be converted to regular texts using : sed -E 's@\[(#[0-9]*)\]\([^ )]*\)@\1@g'
|
||||||
|
# Or readded with sed -E 's@#([0-9]*)@[YunoHost#\1](https://github.com/yunohost/yunohost/pull/\1)@g' | sed -E 's@\((\w+)\)@([YunoHost/\1](https://github.com/yunohost/yunohost/commit/\1))@g'
|
|
@ -53,7 +53,7 @@ class Moulinette:
|
||||||
|
|
||||||
|
|
||||||
# Easy access to interfaces
|
# Easy access to interfaces
|
||||||
def api(host="localhost", port=80, routes={}, actionsmap=None, locales_dir=None):
|
def api(host="localhost", port=80, routes={}, actionsmap=None, locales_dir=None, allowed_cors_origins=[]):
|
||||||
"""Web server (API) interface
|
"""Web server (API) interface
|
||||||
|
|
||||||
Run a HTTP server with the moulinette for an API usage.
|
Run a HTTP server with the moulinette for an API usage.
|
||||||
|
@ -73,6 +73,7 @@ def api(host="localhost", port=80, routes={}, actionsmap=None, locales_dir=None)
|
||||||
Api(
|
Api(
|
||||||
routes=routes,
|
routes=routes,
|
||||||
actionsmap=actionsmap,
|
actionsmap=actionsmap,
|
||||||
|
allowed_cors_origins=allowed_cors_origins,
|
||||||
).run(host, port)
|
).run(host, port)
|
||||||
except MoulinetteError as e:
|
except MoulinetteError as e:
|
||||||
import logging
|
import logging
|
||||||
|
|
|
@ -19,7 +19,6 @@ from moulinette.core import (
|
||||||
MoulinetteValidationError,
|
MoulinetteValidationError,
|
||||||
)
|
)
|
||||||
from moulinette.interfaces import BaseActionsMapParser
|
from moulinette.interfaces import BaseActionsMapParser
|
||||||
from moulinette.utils.log import start_action_logging
|
|
||||||
from moulinette.utils.filesystem import read_yaml
|
from moulinette.utils.filesystem import read_yaml
|
||||||
|
|
||||||
logger = logging.getLogger("moulinette.actionsmap")
|
logger = logging.getLogger("moulinette.actionsmap")
|
||||||
|
@ -107,7 +106,6 @@ class CommentParameter(_ExtraParameter):
|
||||||
|
|
||||||
|
|
||||||
class AskParameter(_ExtraParameter):
|
class AskParameter(_ExtraParameter):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Ask for the argument value if possible and needed.
|
Ask for the argument value if possible and needed.
|
||||||
|
|
||||||
|
@ -146,7 +144,6 @@ class AskParameter(_ExtraParameter):
|
||||||
|
|
||||||
|
|
||||||
class PasswordParameter(AskParameter):
|
class PasswordParameter(AskParameter):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Ask for the password argument value if possible and needed.
|
Ask for the password argument value if possible and needed.
|
||||||
|
|
||||||
|
@ -169,7 +166,6 @@ class PasswordParameter(AskParameter):
|
||||||
|
|
||||||
|
|
||||||
class PatternParameter(_ExtraParameter):
|
class PatternParameter(_ExtraParameter):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Check if the argument value match a pattern.
|
Check if the argument value match a pattern.
|
||||||
|
|
||||||
|
@ -222,7 +218,6 @@ class PatternParameter(_ExtraParameter):
|
||||||
|
|
||||||
|
|
||||||
class RequiredParameter(_ExtraParameter):
|
class RequiredParameter(_ExtraParameter):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Check if a required argument is defined or not.
|
Check if a required argument is defined or not.
|
||||||
|
|
||||||
|
@ -262,7 +257,6 @@ extraparameters_list = [
|
||||||
|
|
||||||
|
|
||||||
class ExtraArgumentParser:
|
class ExtraArgumentParser:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Argument validator and parser for the extra parameters.
|
Argument validator and parser for the extra parameters.
|
||||||
|
|
||||||
|
@ -374,7 +368,6 @@ class ExtraArgumentParser:
|
||||||
|
|
||||||
|
|
||||||
class ActionsMap:
|
class ActionsMap:
|
||||||
|
|
||||||
"""Validate and process actions defined into an actions map
|
"""Validate and process actions defined into an actions map
|
||||||
|
|
||||||
The actions map defines the features - and their usage - of an
|
The actions map defines the features - and their usage - of an
|
||||||
|
@ -398,8 +391,6 @@ class ActionsMap:
|
||||||
|
|
||||||
self.from_cache = False
|
self.from_cache = False
|
||||||
|
|
||||||
logger.debug("loading actions map")
|
|
||||||
|
|
||||||
actionsmap_yml_dir = os.path.dirname(actionsmap_yml)
|
actionsmap_yml_dir = os.path.dirname(actionsmap_yml)
|
||||||
actionsmap_yml_file = os.path.basename(actionsmap_yml)
|
actionsmap_yml_file = os.path.basename(actionsmap_yml)
|
||||||
actionsmap_yml_stat = os.stat(actionsmap_yml)
|
actionsmap_yml_stat = os.stat(actionsmap_yml)
|
||||||
|
@ -467,7 +458,7 @@ class ActionsMap:
|
||||||
|
|
||||||
# Load and initialize the authenticator module
|
# Load and initialize the authenticator module
|
||||||
auth_module = f"{self.namespace}.authenticators.{auth_method}"
|
auth_module = f"{self.namespace}.authenticators.{auth_method}"
|
||||||
logger.debug(f"Loading auth module {auth_module}")
|
#logger.debug(f"Loading auth module {auth_module}")
|
||||||
try:
|
try:
|
||||||
mod = import_module(auth_module)
|
mod = import_module(auth_module)
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
|
@ -562,17 +553,7 @@ class ActionsMap:
|
||||||
logger.exception(error_message)
|
logger.exception(error_message)
|
||||||
raise MoulinetteError(error_message, raw_msg=True)
|
raise MoulinetteError(error_message, raw_msg=True)
|
||||||
else:
|
else:
|
||||||
log_id = start_action_logging()
|
logger.debug("processing action '%s'", full_action_name)
|
||||||
if logger.isEnabledFor(logging.DEBUG):
|
|
||||||
# Log arguments in debug mode only for safety reasons
|
|
||||||
logger.debug(
|
|
||||||
"processing action [%s]: %s with args=%s",
|
|
||||||
log_id,
|
|
||||||
full_action_name,
|
|
||||||
arguments,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.debug("processing action [%s]: %s", log_id, full_action_name)
|
|
||||||
|
|
||||||
# Load translation and process the action
|
# Load translation and process the action
|
||||||
start = time()
|
start = time()
|
||||||
|
@ -580,7 +561,7 @@ class ActionsMap:
|
||||||
return func(**arguments)
|
return func(**arguments)
|
||||||
finally:
|
finally:
|
||||||
stop = time()
|
stop = time()
|
||||||
logger.debug("action [%s] executed in %.3fs", log_id, stop - start)
|
logger.debug("action executed in %.3fs", stop - start)
|
||||||
|
|
||||||
# Private methods
|
# Private methods
|
||||||
|
|
||||||
|
@ -598,9 +579,6 @@ class ActionsMap:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug("building parser...")
|
|
||||||
start = time()
|
|
||||||
|
|
||||||
interface_type = top_parser.interface
|
interface_type = top_parser.interface
|
||||||
|
|
||||||
# If loading from cache, extra were already checked when cache was
|
# If loading from cache, extra were already checked when cache was
|
||||||
|
@ -717,5 +695,4 @@ class ActionsMap:
|
||||||
else:
|
else:
|
||||||
action_parser.want_to_take_lock = True
|
action_parser.want_to_take_lock = True
|
||||||
|
|
||||||
logger.debug("building parser took %.3fs", time() - start)
|
|
||||||
return top_parser
|
return top_parser
|
||||||
|
|
|
@ -11,7 +11,6 @@ logger = logging.getLogger("moulinette.authenticator")
|
||||||
|
|
||||||
|
|
||||||
class BaseAuthenticator:
|
class BaseAuthenticator:
|
||||||
|
|
||||||
"""Authenticator base representation
|
"""Authenticator base representation
|
||||||
|
|
||||||
Each authenticators must implement an Authenticator class derived
|
Each authenticators must implement an Authenticator class derived
|
||||||
|
|
|
@ -18,7 +18,6 @@ def during_unittests_run():
|
||||||
|
|
||||||
|
|
||||||
class Translator:
|
class Translator:
|
||||||
|
|
||||||
"""Internationalization class
|
"""Internationalization class
|
||||||
|
|
||||||
Provide an internationalization mechanism based on JSON files to
|
Provide an internationalization mechanism based on JSON files to
|
||||||
|
@ -173,7 +172,6 @@ class Translator:
|
||||||
|
|
||||||
|
|
||||||
class Moulinette18n:
|
class Moulinette18n:
|
||||||
|
|
||||||
"""Internationalization service for the moulinette
|
"""Internationalization service for the moulinette
|
||||||
|
|
||||||
Manage internationalization and access to the proper keys translation
|
Manage internationalization and access to the proper keys translation
|
||||||
|
@ -270,7 +268,6 @@ class MoulinetteAuthenticationError(MoulinetteError):
|
||||||
|
|
||||||
|
|
||||||
class MoulinetteLock:
|
class MoulinetteLock:
|
||||||
|
|
||||||
"""Locker for a moulinette instance
|
"""Locker for a moulinette instance
|
||||||
|
|
||||||
It provides a lock mechanism for a given moulinette instance. It can
|
It provides a lock mechanism for a given moulinette instance. It can
|
||||||
|
|
|
@ -18,7 +18,6 @@ logger = logging.getLogger("moulinette.interface")
|
||||||
|
|
||||||
|
|
||||||
class BaseActionsMapParser:
|
class BaseActionsMapParser:
|
||||||
|
|
||||||
"""Actions map's base Parser
|
"""Actions map's base Parser
|
||||||
|
|
||||||
Each interfaces must implement an ActionsMapParser class derived
|
Each interfaces must implement an ActionsMapParser class derived
|
||||||
|
@ -33,8 +32,7 @@ class BaseActionsMapParser:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent=None, **kwargs):
|
def __init__(self, parent=None, **kwargs):
|
||||||
if not parent:
|
pass
|
||||||
logger.debug("initializing base actions map parser for %s", self.interface)
|
|
||||||
|
|
||||||
# Virtual properties
|
# Virtual properties
|
||||||
# Each parser classes must implement these properties.
|
# Each parser classes must implement these properties.
|
||||||
|
@ -148,7 +146,6 @@ class BaseActionsMapParser:
|
||||||
|
|
||||||
|
|
||||||
class _ExtendedSubParsersAction(argparse._SubParsersAction):
|
class _ExtendedSubParsersAction(argparse._SubParsersAction):
|
||||||
|
|
||||||
"""Subparsers with extended properties for argparse
|
"""Subparsers with extended properties for argparse
|
||||||
|
|
||||||
It provides the following additional properties at initialization,
|
It provides the following additional properties at initialization,
|
||||||
|
@ -448,7 +445,6 @@ class PositionalsFirstHelpFormatter(argparse.HelpFormatter):
|
||||||
|
|
||||||
|
|
||||||
class JSONExtendedEncoder(JSONEncoder):
|
class JSONExtendedEncoder(JSONEncoder):
|
||||||
|
|
||||||
"""Extended JSON encoder
|
"""Extended JSON encoder
|
||||||
|
|
||||||
Extend default JSON encoder to recognize more types and classes. It will
|
Extend default JSON encoder to recognize more types and classes. It will
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys
|
||||||
import re
|
import re
|
||||||
import errno
|
import errno
|
||||||
import logging
|
import logging
|
||||||
|
@ -30,7 +30,7 @@ from moulinette.interfaces import (
|
||||||
)
|
)
|
||||||
from moulinette.utils import log
|
from moulinette.utils import log
|
||||||
|
|
||||||
logger = log.getLogger("moulinette.interface.api")
|
logger = logging.getLogger("moulinette.interface.api")
|
||||||
|
|
||||||
|
|
||||||
# API helpers ----------------------------------------------------------
|
# API helpers ----------------------------------------------------------
|
||||||
|
@ -66,7 +66,6 @@ def filter_csrf(callback):
|
||||||
|
|
||||||
|
|
||||||
class _HTTPArgumentParser:
|
class _HTTPArgumentParser:
|
||||||
|
|
||||||
"""Argument parser for HTTP requests
|
"""Argument parser for HTTP requests
|
||||||
|
|
||||||
Object for parsing HTTP requests into Python objects. It is based
|
Object for parsing HTTP requests into Python objects. It is based
|
||||||
|
@ -267,6 +266,9 @@ class _ActionsMapPlugin:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
|
if request.get_header("Content-Type") == "application/json":
|
||||||
|
return callback((request.method, context.rule), request.json)
|
||||||
|
|
||||||
params = kwargs
|
params = kwargs
|
||||||
# Format boolean params
|
# Format boolean params
|
||||||
for a in args:
|
for a in args:
|
||||||
|
@ -308,11 +310,18 @@ class _ActionsMapPlugin:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if request.get_header("Content-Type") == "application/json":
|
||||||
|
if "credentials" not in request.json:
|
||||||
|
raise HTTPResponse("Missing credentials parameter", 400)
|
||||||
|
credentials = request.json["credentials"]
|
||||||
|
profile = request.json.get("profile", self.actionsmap.default_authentication)
|
||||||
|
else:
|
||||||
if "credentials" not in request.params:
|
if "credentials" not in request.params:
|
||||||
raise HTTPResponse("Missing credentials parameter", 400)
|
raise HTTPResponse("Missing credentials parameter", 400)
|
||||||
credentials = request.params["credentials"]
|
credentials = request.params["credentials"]
|
||||||
|
|
||||||
profile = request.params.get("profile", self.actionsmap.default_authentication)
|
profile = request.params.get("profile", self.actionsmap.default_authentication)
|
||||||
|
|
||||||
authenticator = self.actionsmap.get_authenticator(profile)
|
authenticator = self.actionsmap.get_authenticator(profile)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -332,6 +341,7 @@ class _ActionsMapPlugin:
|
||||||
try:
|
try:
|
||||||
session_infos = authenticator.get_session_cookie()
|
session_infos = authenticator.get_session_cookie()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
authenticator.delete_session_cookie()
|
||||||
msg = m18n.g("authentication_required")
|
msg = m18n.g("authentication_required")
|
||||||
raise HTTPResponse(msg, 401)
|
raise HTTPResponse(msg, 401)
|
||||||
|
|
||||||
|
@ -343,7 +353,7 @@ class _ActionsMapPlugin:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
authenticator.get_session_cookie()
|
authenticator.get_session_cookie()
|
||||||
except KeyError:
|
except Exception:
|
||||||
raise HTTPResponse(m18n.g("not_logged_in"), 401)
|
raise HTTPResponse(m18n.g("not_logged_in"), 401)
|
||||||
else:
|
else:
|
||||||
# Delete cookie and clean the session
|
# Delete cookie and clean the session
|
||||||
|
@ -405,6 +415,7 @@ class _ActionsMapPlugin:
|
||||||
|
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
logs = {"route": _route, "arguments": arguments, "traceback": tb}
|
logs = {"route": _route, "arguments": arguments, "traceback": tb}
|
||||||
|
print(tb, file=sys.stderr)
|
||||||
return HTTPResponse(json_encode(logs), 500)
|
return HTTPResponse(json_encode(logs), 500)
|
||||||
else:
|
else:
|
||||||
return format_for_response(ret)
|
return format_for_response(ret)
|
||||||
|
@ -464,7 +475,6 @@ def format_for_response(content):
|
||||||
|
|
||||||
|
|
||||||
class ActionsMapParser(BaseActionsMapParser):
|
class ActionsMapParser(BaseActionsMapParser):
|
||||||
|
|
||||||
"""Actions map's Parser for the API
|
"""Actions map's Parser for the API
|
||||||
|
|
||||||
Provide actions map parsing methods for a CLI usage. The parser for
|
Provide actions map parsing methods for a CLI usage. The parser for
|
||||||
|
@ -608,7 +618,6 @@ class ActionsMapParser(BaseActionsMapParser):
|
||||||
|
|
||||||
|
|
||||||
class Interface:
|
class Interface:
|
||||||
|
|
||||||
"""Application Programming Interface for the moulinette
|
"""Application Programming Interface for the moulinette
|
||||||
|
|
||||||
Initialize a HTTP server which serves the API connected to a given
|
Initialize a HTTP server which serves the API connected to a given
|
||||||
|
@ -621,17 +630,30 @@ class Interface:
|
||||||
|
|
||||||
type = "api"
|
type = "api"
|
||||||
|
|
||||||
def __init__(self, routes={}, actionsmap=None):
|
def __init__(self, routes={}, actionsmap=None, allowed_cors_origins=[]):
|
||||||
actionsmap = ActionsMap(actionsmap, ActionsMapParser())
|
actionsmap = ActionsMap(actionsmap, ActionsMapParser())
|
||||||
|
|
||||||
|
self.allowed_cors_origins = allowed_cors_origins
|
||||||
|
|
||||||
# TODO: Return OK to 'OPTIONS' xhr requests (l173)
|
# TODO: Return OK to 'OPTIONS' xhr requests (l173)
|
||||||
app = Bottle(autojson=True)
|
app = Bottle(autojson=True)
|
||||||
|
|
||||||
# Wrapper which sets proper header
|
def cors(callback):
|
||||||
def apiheader(callback):
|
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
response.set_header("Access-Control-Allow-Origin", "*")
|
try:
|
||||||
return callback(*args, **kwargs)
|
r = callback(*args, **kwargs)
|
||||||
|
except HTTPResponse as e:
|
||||||
|
r = e
|
||||||
|
|
||||||
|
origin = request.headers.environ.get("HTTP_ORIGIN", "")
|
||||||
|
if origin and origin in self.allowed_cors_origins:
|
||||||
|
resp = r if isinstance(r, HTTPResponse) else response
|
||||||
|
resp.headers['Access-Control-Allow-Origin'] = origin
|
||||||
|
resp.headers['Access-Control-Allow-Methods'] = 'GET, HEAD, POST, PUT, OPTIONS, DELETE'
|
||||||
|
resp.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
|
||||||
|
resp.headers['Access-Control-Allow-Credentials'] = 'true'
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
@ -640,7 +662,7 @@ class Interface:
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
locale = request.params.pop("locale")
|
locale = request.params.pop("locale")
|
||||||
except KeyError:
|
except (KeyError, ValueError):
|
||||||
locale = m18n.default_locale
|
locale = m18n.default_locale
|
||||||
m18n.set_locale(locale)
|
m18n.set_locale(locale)
|
||||||
return callback(*args, **kwargs)
|
return callback(*args, **kwargs)
|
||||||
|
@ -649,7 +671,7 @@ class Interface:
|
||||||
|
|
||||||
# Install plugins
|
# Install plugins
|
||||||
app.install(filter_csrf)
|
app.install(filter_csrf)
|
||||||
app.install(apiheader)
|
app.install(cors)
|
||||||
app.install(api18n)
|
app.install(api18n)
|
||||||
actionsmapplugin = _ActionsMapPlugin(actionsmap)
|
actionsmapplugin = _ActionsMapPlugin(actionsmap)
|
||||||
app.install(actionsmapplugin)
|
app.install(actionsmapplugin)
|
||||||
|
@ -658,6 +680,12 @@ class Interface:
|
||||||
self.display = actionsmapplugin.display
|
self.display = actionsmapplugin.display
|
||||||
self.prompt = actionsmapplugin.prompt
|
self.prompt = actionsmapplugin.prompt
|
||||||
|
|
||||||
|
def handle_options():
|
||||||
|
return HTTPResponse("", 204)
|
||||||
|
|
||||||
|
app.route('/<:re:.*>', method="OPTIONS",
|
||||||
|
callback=handle_options, skip=["actionsmap"])
|
||||||
|
|
||||||
# Append additional routes
|
# Append additional routes
|
||||||
# TODO: Add optional authentication to those routes?
|
# TODO: Add optional authentication to those routes?
|
||||||
for (m, p), c in routes.items():
|
for (m, p), c in routes.items():
|
||||||
|
|
|
@ -50,7 +50,7 @@ def monkey_get_action_name(argument):
|
||||||
|
|
||||||
argparse._get_action_name = monkey_get_action_name
|
argparse._get_action_name = monkey_get_action_name
|
||||||
|
|
||||||
logger = log.getLogger("moulinette.cli")
|
logger = logging.getLogger("moulinette.cli")
|
||||||
|
|
||||||
|
|
||||||
# CLI helpers ----------------------------------------------------------
|
# CLI helpers ----------------------------------------------------------
|
||||||
|
@ -208,7 +208,6 @@ def get_locale():
|
||||||
|
|
||||||
|
|
||||||
class TTYHandler(logging.StreamHandler):
|
class TTYHandler(logging.StreamHandler):
|
||||||
|
|
||||||
"""TTY log handler
|
"""TTY log handler
|
||||||
|
|
||||||
A handler class which prints logging records for a tty. The record is
|
A handler class which prints logging records for a tty. The record is
|
||||||
|
@ -235,28 +234,26 @@ class TTYHandler(logging.StreamHandler):
|
||||||
log.CRITICAL: "red",
|
log.CRITICAL: "red",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, message_key="fmessage"):
|
def __init__(self, message_key="message_with_color"):
|
||||||
logging.StreamHandler.__init__(self)
|
logging.StreamHandler.__init__(self)
|
||||||
self.message_key = message_key
|
self.message_key = message_key
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
"""Enhance message with level and colors if supported."""
|
"""Enhance message with level and colors if supported."""
|
||||||
msg = record.getMessage()
|
msg = record.getMessage()
|
||||||
|
level = record.levelname
|
||||||
|
level_with_color = level
|
||||||
if self.supports_color():
|
if self.supports_color():
|
||||||
level = ""
|
if self.level > log.DEBUG and record.levelname in ["SUCCESS", "WARNING", "ERROR", "INFO"]:
|
||||||
if self.level <= log.DEBUG:
|
level = m18n.g(record.levelname.lower())
|
||||||
# add level name before message
|
|
||||||
level = "%s " % record.levelname
|
|
||||||
elif record.levelname in ["SUCCESS", "WARNING", "ERROR", "INFO"]:
|
|
||||||
# add translated level name before message
|
|
||||||
level = "%s " % m18n.g(record.levelname.lower())
|
|
||||||
color = self.LEVELS_COLOR.get(record.levelno, "white")
|
color = self.LEVELS_COLOR.get(record.levelno, "white")
|
||||||
msg = "{}{}{}{}".format(colors_codes[color], level, END_CLI_COLOR, msg)
|
level_with_color = f"{colors_codes[color]}{level}{END_CLI_COLOR}"
|
||||||
|
if self.level == log.DEBUG:
|
||||||
|
level_with_color = level_with_color + " " * max(0, 7 - len(level))
|
||||||
if self.formatter:
|
if self.formatter:
|
||||||
# use user-defined formatter
|
record.__dict__["level_with_color"] = level_with_color
|
||||||
record.__dict__[self.message_key] = msg
|
|
||||||
return self.formatter.format(record)
|
return self.formatter.format(record)
|
||||||
return msg
|
return level_with_color + " " + msg
|
||||||
|
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
# set proper stream first
|
# set proper stream first
|
||||||
|
@ -274,7 +271,6 @@ class TTYHandler(logging.StreamHandler):
|
||||||
|
|
||||||
|
|
||||||
class ActionsMapParser(BaseActionsMapParser):
|
class ActionsMapParser(BaseActionsMapParser):
|
||||||
|
|
||||||
"""Actions map's Parser for the CLI
|
"""Actions map's Parser for the CLI
|
||||||
|
|
||||||
Provide actions map parsing methods for a CLI usage. The parser for
|
Provide actions map parsing methods for a CLI usage. The parser for
|
||||||
|
@ -446,7 +442,6 @@ class ActionsMapParser(BaseActionsMapParser):
|
||||||
|
|
||||||
|
|
||||||
class Interface:
|
class Interface:
|
||||||
|
|
||||||
"""Command-line Interface for the moulinette
|
"""Command-line Interface for the moulinette
|
||||||
|
|
||||||
Initialize an interface connected to the standard input/output
|
Initialize an interface connected to the standard input/output
|
||||||
|
|
|
@ -6,7 +6,6 @@ from logging import (
|
||||||
addLevelName,
|
addLevelName,
|
||||||
setLoggerClass,
|
setLoggerClass,
|
||||||
Logger,
|
Logger,
|
||||||
getLogger,
|
|
||||||
NOTSET, # noqa
|
NOTSET, # noqa
|
||||||
DEBUG,
|
DEBUG,
|
||||||
INFO,
|
INFO,
|
||||||
|
@ -84,7 +83,6 @@ def getHandlersByClass(classinfo, limit=0):
|
||||||
|
|
||||||
|
|
||||||
class MoulinetteLogger(Logger):
|
class MoulinetteLogger(Logger):
|
||||||
|
|
||||||
"""Custom logger class
|
"""Custom logger class
|
||||||
|
|
||||||
Extend base Logger class to provide the SUCCESS custom log level with
|
Extend base Logger class to provide the SUCCESS custom log level with
|
||||||
|
@ -109,94 +107,18 @@ class MoulinetteLogger(Logger):
|
||||||
f = currentframe()
|
f = currentframe()
|
||||||
if f is not None:
|
if f is not None:
|
||||||
f = f.f_back
|
f = f.f_back
|
||||||
rv = "(unknown file)", 0, "(unknown function)"
|
rv = "(unknown file)", 0, "(unknown function)", None
|
||||||
while hasattr(f, "f_code"):
|
while hasattr(f, "f_code"):
|
||||||
co = f.f_code
|
co = f.f_code
|
||||||
filename = os.path.normcase(co.co_filename)
|
filename = os.path.normcase(co.co_filename)
|
||||||
if filename == _srcfile or filename == __file__:
|
if filename == _srcfile or filename == __file__:
|
||||||
f = f.f_back
|
f = f.f_back
|
||||||
continue
|
continue
|
||||||
rv = (co.co_filename, f.f_lineno, co.co_name)
|
rv = (co.co_filename, f.f_lineno, co.co_name, None)
|
||||||
break
|
break
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def _log(self, *args, **kwargs):
|
|
||||||
"""Append action_id if available to the extra."""
|
|
||||||
if self.action_id is not None:
|
|
||||||
extra = kwargs.get("extra", {})
|
|
||||||
if "action_id" not in extra:
|
|
||||||
# FIXME: Get real action_id instead of logger/current one
|
|
||||||
extra["action_id"] = _get_action_id()
|
|
||||||
kwargs["extra"] = extra
|
|
||||||
return super()._log(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
# Action logging -------------------------------------------------------
|
|
||||||
|
|
||||||
pid = os.getpid()
|
|
||||||
action_id = 0
|
|
||||||
|
|
||||||
|
|
||||||
def _get_action_id():
|
|
||||||
return "%d.%d" % (pid, action_id)
|
|
||||||
|
|
||||||
|
|
||||||
def start_action_logging():
|
|
||||||
"""Configure logging for a new action
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The new action id
|
|
||||||
|
|
||||||
"""
|
|
||||||
global action_id
|
|
||||||
action_id += 1
|
|
||||||
|
|
||||||
return _get_action_id()
|
|
||||||
|
|
||||||
|
|
||||||
def getActionLogger(name=None, logger=None, action_id=None):
|
|
||||||
"""Get the logger adapter for an action
|
|
||||||
|
|
||||||
Return a logger for the specified name - or use given logger - and
|
|
||||||
optionally for a given action id, retrieving it if necessary.
|
|
||||||
|
|
||||||
Either a name or a logger must be specified.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not name and not logger:
|
|
||||||
raise ValueError("Either a name or a logger must be specified")
|
|
||||||
|
|
||||||
logger = logger or getLogger(name)
|
|
||||||
logger.action_id = action_id if action_id else _get_action_id()
|
|
||||||
return logger
|
|
||||||
|
|
||||||
|
|
||||||
class ActionFilter:
|
|
||||||
|
|
||||||
"""Extend log record for an optionnal action
|
|
||||||
|
|
||||||
Filter a given record and look for an `action_id` key. If it is not found
|
|
||||||
and `strict` is True, the record will not be logged. Otherwise, the key
|
|
||||||
specified by `message_key` will be added to the record, containing the
|
|
||||||
message formatted for the action or just the original one.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, message_key="fmessage", strict=False):
|
|
||||||
self.message_key = message_key
|
|
||||||
self.strict = strict
|
|
||||||
|
|
||||||
def filter(self, record):
|
|
||||||
msg = record.getMessage()
|
|
||||||
action_id = record.__dict__.get("action_id", None)
|
|
||||||
if action_id is not None:
|
|
||||||
msg = "[{:s}] {:s}".format(action_id, msg)
|
|
||||||
elif self.strict:
|
|
||||||
return False
|
|
||||||
record.__dict__[self.message_key] = msg
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
# Log broadcasting via the broker -----------------------------------------------
|
# Log broadcasting via the broker -----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -209,6 +131,7 @@ if not os.path.isdir("/var/run/yunohost"):
|
||||||
os.chown("/var/run/yunohost", 0, 0)
|
os.chown("/var/run/yunohost", 0, 0)
|
||||||
os.chmod("/var/run/yunohost", 0o700)
|
os.chmod("/var/run/yunohost", 0o700)
|
||||||
|
|
||||||
|
|
||||||
def start_log_broker():
|
def start_log_broker():
|
||||||
|
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
|
|
|
@ -82,7 +82,7 @@ def call_async_output(args, callback, **kwargs):
|
||||||
while p.poll() is None:
|
while p.poll() is None:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
callback, message = log_queue.get(True, 1)
|
callback, message = log_queue.get(True, 0.1)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -60,7 +60,7 @@ setup(
|
||||||
license="AGPL",
|
license="AGPL",
|
||||||
packages=find_packages(exclude=["test"]),
|
packages=find_packages(exclude=["test"]),
|
||||||
data_files=[("/usr/share/moulinette/locales", locale_files)],
|
data_files=[("/usr/share/moulinette/locales", locale_files)],
|
||||||
python_requires=">=3.7.0,<3.10",
|
python_requires=">=3.11.0,<3.12",
|
||||||
install_requires=install_deps,
|
install_requires=install_deps,
|
||||||
tests_require=test_deps,
|
tests_require=test_deps,
|
||||||
extras_require=extras,
|
extras_require=extras,
|
||||||
|
|
|
@ -46,7 +46,7 @@ def logging_configuration(moulinette):
|
||||||
"format": "%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s" # noqa
|
"format": "%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s" # noqa
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"filters": {"action": {"()": "moulinette.utils.log.ActionFilter"}},
|
"filters": {},
|
||||||
"handlers": {
|
"handlers": {
|
||||||
"api": {
|
"api": {
|
||||||
"level": level,
|
"level": level,
|
||||||
|
|
|
@ -24,8 +24,8 @@ godamn_spaces_of_hell = [
|
||||||
"\u2008",
|
"\u2008",
|
||||||
"\u2009",
|
"\u2009",
|
||||||
"\u200A",
|
"\u200A",
|
||||||
"\u202f",
|
# "\u202f",
|
||||||
"\u202F",
|
# "\u202F",
|
||||||
"\u3000",
|
"\u3000",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ session_secret = random_ascii()
|
||||||
|
|
||||||
|
|
||||||
class Authenticator(BaseAuthenticator):
|
class Authenticator(BaseAuthenticator):
|
||||||
|
|
||||||
"""Dummy authenticator used for tests"""
|
"""Dummy authenticator used for tests"""
|
||||||
|
|
||||||
name = "dummy"
|
name = "dummy"
|
||||||
|
|
|
@ -13,7 +13,6 @@ session_secret = random_ascii()
|
||||||
|
|
||||||
|
|
||||||
class Authenticator(BaseAuthenticator):
|
class Authenticator(BaseAuthenticator):
|
||||||
|
|
||||||
"""Dummy authenticator used for tests"""
|
"""Dummy authenticator used for tests"""
|
||||||
|
|
||||||
name = "yoloswag"
|
name = "yoloswag"
|
||||||
|
|
|
@ -180,7 +180,7 @@ class TestAuthAPI:
|
||||||
|
|
||||||
def test_request_arg_without_action(self, moulinette_webapi, caplog, mocker):
|
def test_request_arg_without_action(self, moulinette_webapi, caplog, mocker):
|
||||||
self.login(moulinette_webapi)
|
self.login(moulinette_webapi)
|
||||||
moulinette_webapi.get("/test-auth", status=404)
|
moulinette_webapi.get("/test-auth", status=405)
|
||||||
|
|
||||||
|
|
||||||
class TestAuthCLI:
|
class TestAuthCLI:
|
||||||
|
|
21
tox.ini
21
tox.ini
|
@ -1,6 +1,6 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist =
|
envlist =
|
||||||
py{37,39}-{pytest,lint,invalidcode,mypy}
|
py311-{pytest,lint,invalidcode,mypy}
|
||||||
format
|
format
|
||||||
format-check
|
format-check
|
||||||
docs
|
docs
|
||||||
|
@ -11,20 +11,19 @@ usedevelop = True
|
||||||
passenv = *
|
passenv = *
|
||||||
extras = tests
|
extras = tests
|
||||||
deps =
|
deps =
|
||||||
py{37,39}-pytest: .[tests]
|
py311-pytest: .[tests]
|
||||||
py{37,39}-lint: flake8
|
py311-lint: flake8
|
||||||
py{37,39}-invalidcode: flake8
|
py311-invalidcode: flake8
|
||||||
py{37,39}-mypy: mypy >= 0.761
|
py311-mypy: mypy >= 0.761
|
||||||
commands =
|
commands =
|
||||||
py{37,39}-pytest: pytest {posargs} -c pytest.ini
|
py311-pytest: pytest {posargs} -c pytest.ini
|
||||||
py{37,39}-lint: flake8 moulinette test
|
py311-lint: flake8 moulinette test
|
||||||
py{37,39}-invalidcode: flake8 moulinette test --select F
|
py311-invalidcode: flake8 moulinette test --select F
|
||||||
py{37,39}-mypy: mypy --ignore-missing-imports --install-types --non-interactive moulinette/
|
py311-mypy: mypy --ignore-missing-imports --install-types --non-interactive moulinette/
|
||||||
|
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
python =
|
python =
|
||||||
3.7: py37
|
3.11: py311
|
||||||
3.9: py39
|
|
||||||
|
|
||||||
[testenv:format]
|
[testenv:format]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
|
|
Loading…
Reference in a new issue