Compare commits

...
13 Commits
Author SHA1 Message Date
devx-ci-bot ba002c2e72 release: v0.12.2 [skip ci] 2026-06-25 21:30:48 +02:00
emil af610d22ec DEVX-49: fix: remove auto-rebase from auto-merge to prevent CI feedback loop
Post-merge / detect-type (push) Successful in 10s
Post-merge / validate-commit-msg (push) Successful in 16s
Post-merge / configure-repo (push) Successful in 17s
Post-merge / release (push) Successful in 1m13s
Post-merge / vikunja (push) Successful in 14s
Post-merge / badges (push) Successful in 57s
Post-merge / sync-wiki (push) Successful in 1m5s
2026-06-25 19:29:45 +00:00
gitea-actions-bot 69a585db2f chore: update badge URLs to commit 1b6a8d99 [skip ci] 2026-06-25 19:14:36 +02:00
devx-ci-bot 700df828ba release: v0.12.1 [skip ci] 2026-06-25 19:13:16 +02:00
emil 4df0602157 DEVX-48: fix: use heredoc syntax for multi-line $GITHUB_ENV values
Post-merge / detect-type (push) Successful in 6s
Post-merge / configure-repo (push) Successful in 7s
Post-merge / validate-commit-msg (push) Successful in 15s
Post-merge / release (push) Successful in 52s
Post-merge / vikunja (push) Successful in 14s
Post-merge / sync-wiki (push) Successful in 49s
Post-merge / badges (push) Successful in 1m8s
2026-06-25 17:12:20 +00:00
gitea-actions-bot d7d90fe165 chore: update badge URLs to commit 9a9a53ef [skip ci] 2026-06-25 01:06:19 +02:00
devx-ci-bot 22c2d7c925 release: v0.12.0 [skip ci] 2026-06-24 23:05:23 +00:00
emil b4350751f1 DEVX-47: feat: add Polish as officially supported language
Post-merge / detect-type (push) Successful in 10s
Post-merge / configure-repo (push) Successful in 7s
Post-merge / validate-commit-msg (push) Successful in 14s
Post-merge / release (push) Failing after 58s
Post-merge / sync-wiki (push) Has been skipped
Post-merge / vikunja (push) Has been skipped
Post-merge / badges (push) Successful in 45s
2026-06-24 23:04:20 +00:00
gitea-actions-bot a987b63da7 chore: update badge URLs to commit 85b1ae90 [skip ci] 2026-06-24 22:50:19 +00:00
emil 82d613e23b DEVX-46: docs: add pyproject.toml dependency and pip.conf instructions for devx
Post-merge / detect-type (push) Successful in 7s
Post-merge / validate-commit-msg (push) Successful in 6s
Post-merge / configure-repo (push) Successful in 13s
Post-merge / release (push) Successful in 1m1s
Post-merge / vikunja (push) Successful in 15s
Post-merge / sync-wiki (push) Successful in 1m2s
Post-merge / badges (push) Successful in 1m1s
2026-06-24 22:48:06 +00:00
gitea-actions-bot 6b6c9d40f1 chore: update badge URLs to commit 90c2559f [skip ci] 2026-06-25 00:35:25 +02:00
emil d17854296a DEVX-45: docs: remove stale .taskid file fallback references
Post-merge / detect-type (push) Successful in 12s
Post-merge / validate-commit-msg (push) Successful in 7s
Post-merge / configure-repo (push) Successful in 13s
Post-merge / release (push) Successful in 43s
Post-merge / vikunja (push) Successful in 7s
Post-merge / sync-wiki (push) Successful in 43s
Post-merge / badges (push) Successful in 50s
2026-06-24 22:33:35 +00:00
gitea-actions-bot 2fff7ed271 chore: update badge URLs to commit c2065ac1 [skip ci] 2026-06-25 00:28:01 +02:00
16 changed files with 422 additions and 142 deletions
+1 -2
View File
@@ -120,8 +120,7 @@ jobs:
auto-merge:
# Auto-merge runs after all CI checks pass. It reads the task ID
# from the branch name (falling back to .taskid file), validates
# the PR title, and squash-merges.
# from the branch name, validates the PR title, and squash-merges.
needs: [quality, detect-changes, pr-review]
if: github.event_name == 'pull_request'
runs-on: docker
+18
View File
@@ -2,6 +2,24 @@
All notable changes to this project will be documented in this file.
## [0.12.2] - 2026-06-25
### Bug Fixes
- Remove auto-rebase from auto-merge to prevent CI feedback loop
## [0.12.1] - 2026-06-25
### Bug Fixes
- Use heredoc syntax for multi-line $GITHUB_ENV values
## [0.12.0] - 2026-06-24
### Features
- Add Polish as officially supported language
## [0.11.1] - 2026-06-24
### Bug Fixes
+46 -13
View File
@@ -16,12 +16,12 @@ quality badges.
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/python.svg)](https://www.python.org/downloads/)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/python.svg)](https://www.python.org/downloads/)
## Why devx?
@@ -55,28 +55,61 @@ up by bumping their devx dependency.
- **Developer tools** — environment setup, CI tool installation, test speed
enforcement, repository configuration.
- **i18n** — built-in translations for English, Bulgarian, German, Russian,
and Chinese; projects can extend with their own keys.
Chinese, and Polish; projects can extend with their own keys.
## Installation
Install from the Gitea PyPI registry:
devx is published to the Gitea PyPI registry at
`https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple`.
The registry is publicly readable — no authentication required to install.
### Quick install (one-off)
```bash
pip install devx --index-url https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
Or add the registry to your `pip.conf` / `pyproject.toml` and install normally:
### Persistent configuration (recommended)
Add the registry to `~/.pip/pip.conf` so `pip install devx` works without
specifying `--index-url` every time:
```ini
[global]
extra-index-url = https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
### As a dependency in another project
To use devx as a dependency in your `pyproject.toml`, add the registry as an
extra index and list devx in your dependencies:
```toml
[project]
dependencies = [
"devx>=0.11.1",
]
[tool.pip]
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
```
Then install normally:
```bash
pip install devx
pip install -e .
```
> **Note:** If your project requires a specific devx version, pin it in
> `dependencies` (e.g., `"devx==0.11.1"`) or use a version constraint
> (e.g., `"devx>=0.11.1,<0.12"`).
### Optional extras
devx ships optional dependency groups for different use cases:
```bash
pip install "devx[ci,lint]" # CI runners and linting (pytest, ruff, pyright, bandit)
pip install "devx[ci,lint]" # CI runners and linting (pytest, ruff, pyright, bandit, build, twine)
pip install "devx[molecule]" # Molecule testing for Ansible projects
pip install "devx[dev]" # Full local development (ci + lint + build + twine)
```
@@ -291,7 +324,7 @@ The config system loads `.env` automatically via `python-dotenv`.
| `DEVX_REPO_NAME` | **(none — must be set)** | Repository name (or `owner/repo`) |
| `DEVX_TASK_PREFIX` | `DEVX` | Task ID prefix (GRM, OBL-INFRA, etc.) |
| `DEVX_VIKUNJA_PROJECT_ID` | `6` | Vikunja project ID |
| `DEVX_LANG` | `en` | Language for i18n (en, bg, de, ru, zh) |
| `DEVX_LANG` | `en` | Language for i18n (en, bg, de, ru, zh, pl) |
| `DEVX_TRANSLATIONS_PATH` | — | Path to a custom JSON translations file |
| `DEVX_VERSION_FILE` | `src/devx/__init__.py` | Version source file (used by release) |
| `DEVX_DOCS_DIR` | `docs` | Documentation directory (used by sync_wiki) |
@@ -398,7 +431,7 @@ src/devx/
├── i18n.py # Translation system (gettext-based, translations.json)
├── exceptions.py # Custom exception types (DevxError, APIError)
├── opentofu.py # OpenTofu output helpers
├── translations.json # Translation strings (en, bg, de, ru, zh)
├── translations.json # Translation strings (en, bg, de, ru, zh, pl)
├── ci/ # CI/CD automation modules (run by workflows)
├── tools/ # Developer tooling modules (run locally or by CI)
└── molecule/ # Optional molecule testing helpers (for Ansible projects)
+43 -12
View File
@@ -12,12 +12,12 @@ project to be reusable across all oblachno-oss repositories.
[![CI](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions/workflows/ci.yml/badge.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/src/branch/master/LICENSE)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/389da2176541841d5dcf3f75f3bd03cbf9403830/python.svg)](https://www.python.org/downloads/)
[![Coverage](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/coverage.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Tests](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/tests.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Docs](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/docs.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/wiki)
[![Code Quality](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/quality.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/actions)
[![Version](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/version.svg)](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/releases)
[![Python](https://git.oblachno.oblachno.fyi/oblachno-oss/devx/raw/commit/1b6a8d99cf3c1916b9ab5aebd0d9900f3af09e6f/python.svg)](https://www.python.org/downloads/)
## Overview
@@ -42,22 +42,53 @@ via environment variables and `pyproject.toml`, and inherit:
- **Developer tools** — environment setup, CI tool installation, test speed
enforcement, repository configuration.
- **i18n** — built-in translations for English, Bulgarian, German, Russian,
and Chinese; projects can extend with their own keys.
Chinese, and Polish; projects can extend with their own keys.
## Installation
Install from the Gitea PyPI registry:
devx is published to the Gitea PyPI registry at
`https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple`.
The registry is publicly readable — no authentication required to install.
### Quick install (one-off)
```bash
pip install devx --index-url https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
Optional extras:
### Persistent configuration (recommended)
Add the registry to `~/.pip/pip.conf`:
```ini
[global]
extra-index-url = https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple
```
Then `pip install devx` works without specifying `--index-url`.
### As a dependency in another project
Add devx to your `pyproject.toml` dependencies and configure the registry:
```toml
[project]
dependencies = [
"devx>=0.11.1",
]
[tool.pip]
extra-index-url = "https://git.oblachno.oblachno.fyi/api/packages/oblachno-oss/pypi/simple"
```
Pin a specific version if needed: `"devx==0.11.1"` or `"devx>=0.11.1,<0.12"`.
### Optional extras
```bash
pip install "devx[ci,lint]" # CI runners and linting
pip install "devx[ci,lint]" # CI runners and linting (pytest, ruff, pyright, bandit, build, twine)
pip install "devx[molecule]" # Molecule testing for Ansible projects
pip install "devx[dev]" # Full local development
pip install "devx[dev]" # Full local development (ci + lint + build + twine)
```
## Architecture
@@ -117,7 +148,7 @@ fallback. Key variables:
| `DEVX_REPO_OWNER` | **(must be set)** | Repository owner |
| `DEVX_REPO_NAME` | **(must be set)** | Repository name |
| `DEVX_TASK_PREFIX` | `DEVX` | Task ID prefix (GRM, OBL-INFRA, etc.) |
| `DEVX_LANG` | `en` | Language for i18n (en, bg, de, ru, zh) |
| `DEVX_LANG` | `en` | Language for i18n (en, bg, de, ru, zh, pl) |
| `REPO_TOKEN` | — | Gitea API token |
| `VIKUNJA_TOKEN` | — | Vikunja API token |
+2 -2
View File
@@ -16,7 +16,7 @@ src/devx/
├── i18n.py # Translation system (JSON-based, translations.json)
├── exceptions.py # Custom exception types (DevxError, APIError)
├── opentofu.py # OpenTofu output helpers
├── translations.json # Translation strings (en, bg, de, ru, zh)
├── translations.json # Translation strings (en, bg, de, ru, zh, pl)
├── ci/ # CI/CD automation modules (run by workflows)
│ ├── __init__.py
│ ├── _shared.py # Shared utilities (get_latest_tag)
@@ -188,7 +188,7 @@ repos that only need a Gitea release.
### `auto_merge.py`
Auto-merges a PR when all CI checks pass. Reads the task ID from the branch
name (falling back to `.taskid` file), validates the PR title format against
name, validates the PR title format against
the Vikunja task title, extracts the conventional commit message from PR
commits, and squash-merges with title `{PREFIX}-N <conventional commit>`.
+2 -4
View File
@@ -93,8 +93,7 @@ Depends on `quality`, `detect-changes`, and `pr-review`. The final job in the
CI workflow. Runs `python -m devx.ci.auto_merge` with the branch name, PR
title, repository, and PR number:
1. **Read task ID** from branch name (e.g., `DEVX-12-fix-foo``DEVX-12`),
falling back to `.taskid` file for branches without a task ID prefix
1. **Read task ID** from branch name (e.g., `DEVX-12-fix-foo``DEVX-12`)
2. **Validate PR title format** — must be `{PREFIX}-N: <vikunja task title>`
3. **Validate PR title matches Vikunja task** — fetches the Vikunja task and
compares the title
@@ -330,8 +329,7 @@ On failure, the `notify_failure` step creates a Gitea issue.
### `auto_merge.py`
Auto-merge PR when all CI checks pass. Reads task ID from the branch name
(e.g., `DEVX-12-fix-foo``DEVX-12`), falling back to `.taskid` file for
branches without a task ID prefix. Validates PR title format, checks the
(e.g., `DEVX-12-fix-foo``DEVX-12`). Validates PR title format, checks the
Vikunja task exists and the title matches, extracts the conventional commit
message from PR commits, and squash-merges with
`{PREFIX}-N <conventional commit>` title.
+3 -3
View File
@@ -18,7 +18,7 @@ devx molecule --help # show molecule commands
### `devx ci auto-merge`
Auto-merge a PR when all CI checks pass. Reads the task ID from the branch
name (falling back to `.taskid` file), validates the PR title format against
name, validates the PR title format against
the Vikunja task title, extracts the conventional commit message from PR
commits, and squash-merges with `{PREFIX}-N <conventional commit>` title.
@@ -40,8 +40,8 @@ checks `src/devx/translations.json` against `src/devx/**/*.py`.
Checks performed:
- **Missing keys** — a `_()` call in code has no entry in the translations file
- **Dead keys** — a key in the translations file is not used in any code
- **Missing languages** — a key exists but is missing one of the five
supported languages (en, bg, de, ru, zh)
- **Missing languages** — a key exists but is missing one of the six
supported languages (en, bg, de, ru, zh, pl)
```bash
devx ci check-translations
+1 -1
View File
@@ -1,3 +1,3 @@
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
__version__ = "0.11.1"
__version__ = "0.12.2"
+11 -17
View File
@@ -237,23 +237,17 @@ def main(branch: str, pr_title: str, repo: str, pr_number: str) -> None:
client.merge_pr(pr_num, merge_title)
except APIError as e:
if e.status == 405 and "behind" in e.message.lower():
# Head branch is behind master — pull master and rebase, then retry
click.echo(_("Head branch is behind master. Pulling and rebasing..."))
try:
run_cmd(["git", "config", "user.name", "devx-ci-bot"])
run_cmd(["git", "config", "user.email", "devx-ci-bot@oblachno.fyi"])
run_cmd(["git", "fetch", "origin", "master"])
run_cmd(["git", "rebase", "origin/master"])
run_cmd(["git", "push", "--force-with-lease", "origin", f"HEAD:{branch}"])
click.echo(_("Rebased and pushed. Retrying merge..."))
client.merge_pr(pr_num, merge_title)
except (APIError, Exception) as retry_err:
raise click.ClickException(
_(
"Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
error=str(retry_err),
)
) from None
# Head branch is behind master — do NOT auto-rebase.
# Auto-rebasing creates a feedback loop: the force-push triggers
# a new pull_request synchronize event, which starts a new CI run,
# which runs auto-merge again, which rebases again, etc.
raise click.ClickException(
_(
"Branch is behind master. Rebase manually:\n"
" git fetch origin master && git rebase origin/master && git push --force-with-lease\n"
"Then re-add the ready-to-merge label.",
)
) from None
else:
raise click.ClickException(
_(
+6 -6
View File
@@ -11,8 +11,8 @@ Checks performed (all fail with exit code 1 on error):
- **Missing keys**: a ``_()`` call in code has no entry in the corresponding
translations file.
- **Dead keys**: a key in a translations file is not used in any code.
- **Missing languages**: a key exists but is missing one of the 5 supported
languages (en, bg, de, ru, zh). This is an error all supported languages
- **Missing languages**: a key exists but is missing one of the 6 supported
languages (en, bg, de, ru, zh, pl). This is an error all supported languages
must have translations for every key.
Usage::
@@ -33,7 +33,7 @@ import click
REPO_ROOT = Path(__file__).resolve().parent.parent.parent.parent
SUPPORTED_LANGS = ("en", "bg", "de", "ru", "zh")
SUPPORTED_LANGS = ("en", "bg", "de", "ru", "zh", "pl")
# Default translation set: devx package itself
DEFAULT_TRANS_FILE = REPO_ROOT / "src" / "devx" / "translations.json"
@@ -101,9 +101,9 @@ def collect_keys(src_dir: Path) -> set[str]:
if pyfile.name == "i18n.py":
continue
keys |= extract_keys(pyfile)
# Add dynamic keys for the default source directory
if src_dir == DEFAULT_SRC_DIR:
keys |= DYNAMIC_KEYS
# Dynamic keys are common status strings used via _(variable) that
# can't be detected by AST scanning. Include them for all projects.
keys |= DYNAMIC_KEYS
return keys
+6 -1
View File
@@ -55,7 +55,12 @@ def _write_github_env(key: str, value: str) -> None:
if not gh_env:
raise click.ClickException("GITHUB_ENV environment variable is not set")
with open(gh_env, "a") as f: # noqa: PTH123
f.write(f"{key}={value}\n")
if "\n" in value:
# Multi-line values require the heredoc syntax in $GITHUB_ENV.
delimiter = "EOF"
f.write(f"{key}<<{delimiter}\n{value}\n{delimiter}\n")
else:
f.write(f"{key}={value}\n")
@click.command()
+2 -2
View File
@@ -1,7 +1,7 @@
"""Simple i18n for devx scripts and tools.
Set DEVX_LANG environment variable to override the default English.
Supported: en, bg, de, ru, zh.
Supported: en, bg, de, ru, zh, pl.
Projects can extend translations by setting DEVX_TRANSLATIONS_PATH to a
JSON file with additional keys. Keys from the project's file are merged
@@ -45,7 +45,7 @@ def _(key: str, **kwargs: object) -> str:
If unset, English is always returned regardless of system locale.
"""
lang = os.getenv("DEVX_LANG", "en")
if lang not in ("en", "bg", "de", "ru", "zh"):
if lang not in ("en", "bg", "de", "ru", "zh", "pl"):
lang = "en"
template = TRANSLATIONS.get(key, {}).get(lang, key)
return template.format(**kwargs)
+213 -49
View File
@@ -3,6 +3,7 @@
"bg": "\n=== Summary ===",
"de": "\n=== Summary ===",
"en": "\n=== Summary ===",
"pl": "\n=== Podsumowanie ===",
"ru": "\n=== Summary ===",
"zh": "\n=== Summary ==="
},
@@ -10,6 +11,7 @@
"bg": "\nAll documentation coverage checks passed!",
"de": "\nAll documentation coverage checks passed!",
"en": "\nAll documentation coverage checks passed!",
"pl": "\nWszystkie kontrole pokrycia dokumentacji zakończone pomyślnie!",
"ru": "\nAll documentation coverage checks passed!",
"zh": "\nAll documentation coverage checks passed!"
},
@@ -17,6 +19,7 @@
"bg": "\nCHANGELOG version ordering:",
"de": "\nCHANGELOG version ordering:",
"en": "\nCHANGELOG version ordering:",
"pl": "\nKolejność wersji w CHANGELOG:",
"ru": "\nCHANGELOG version ordering:",
"zh": "\nCHANGELOG version ordering:"
},
@@ -24,6 +27,7 @@
"bg": "\nChecking CI script documentation in ci-cd-workflow.md...",
"de": "\nChecking CI script documentation in ci-cd-workflow.md...",
"en": "\nChecking CI script documentation in ci-cd-workflow.md...",
"pl": "\nSprawdzanie dokumentacji skryptów CI w ci-cd-workflow.md...",
"ru": "\nChecking CI script documentation in ci-cd-workflow.md...",
"zh": "\nChecking CI script documentation in ci-cd-workflow.md..."
},
@@ -31,6 +35,7 @@
"bg": "\nChecking module documentation in architecture.md...",
"de": "\nChecking module documentation in architecture.md...",
"en": "\nChecking module documentation in architecture.md...",
"pl": "\nSprawdzanie dokumentacji modułów w architecture.md...",
"ru": "\nChecking module documentation in architecture.md...",
"zh": "\nChecking module documentation in architecture.md..."
},
@@ -38,6 +43,7 @@
"bg": "\nDoc coverage: {covered}/{total} ({pct}%)",
"de": "\nDoc coverage: {covered}/{total} ({pct}%)",
"en": "\nDoc coverage: {covered}/{total} ({pct}%)",
"pl": "\nPokrycie dokumentacji: {covered}/{total} ({pct}%)",
"ru": "\nDoc coverage: {covered}/{total} ({pct}%)",
"zh": "\nDoc coverage: {covered}/{total} ({pct}%)"
},
@@ -45,6 +51,7 @@
"bg": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"de": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"en": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"pl": "\nGotowe! Utworzono: {created}, Zaktualizowano: {updated}, Pominięto: {skipped}",
"ru": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}",
"zh": "\nDone! Created: {created}, Updated: {updated}, Skipped: {skipped}"
},
@@ -52,6 +59,7 @@
"bg": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"de": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"en": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"pl": "\nBŁĄD: Pokrycie dokumentacji nie wynosi 100%. Użyj --fail-on-missing, aby to wymusić.",
"ru": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce.",
"zh": "\nERROR: Documentation coverage is not 100%. Use --fail-on-missing to enforce."
},
@@ -59,6 +67,7 @@
"bg": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"de": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"en": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"pl": "\nNapraw niezgodne tagi przed utworzeniem nowych wydań. Uruchom 'python3 -m devx.ci.release --verify', aby uzyskać pełny raport.",
"ru": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report.",
"zh": "\nFix the misaligned tags before creating new releases. Run 'python3 -m devx.ci.release --verify' for a full report."
},
@@ -66,6 +75,7 @@
"bg": "\nIntegrity check FAILED ({count} issues):",
"de": "\nIntegrity check FAILED ({count} issues):",
"en": "\nIntegrity check FAILED ({count} issues):",
"pl": "\nKontrola integralności NIEUDANA ({count} problemów):",
"ru": "\nIntegrity check FAILED ({count} issues):",
"zh": "\nIntegrity check FAILED ({count} issues):"
},
@@ -73,6 +83,7 @@
"bg": "\nIntegrity check passed — all {count} pages verified.",
"de": "\nIntegrity check passed — all {count} pages verified.",
"en": "\nIntegrity check passed — all {count} pages verified.",
"pl": "\nKontrola integralności zakończona pomyślnie — wszystkie {count} stron zweryfikowane.",
"ru": "\nIntegrity check passed — all {count} pages verified.",
"zh": "\nIntegrity check passed — all {count} pages verified."
},
@@ -80,6 +91,7 @@
"bg": "\nLatest tag: {tag}",
"de": "\nLatest tag: {tag}",
"en": "\nLatest tag: {tag}",
"pl": "\nNajnowszy tag: {tag}",
"ru": "\nLatest tag: {tag}",
"zh": "\nLatest tag: {tag}"
},
@@ -87,6 +99,7 @@
"bg": "\nMissing documentation:",
"de": "\nMissing documentation:",
"en": "\nMissing documentation:",
"pl": "\nBrakująca dokumentacja:",
"ru": "\nMissing documentation:",
"zh": "\nMissing documentation:"
},
@@ -94,6 +107,7 @@
"bg": "\nResult: {status}",
"de": "\nResult: {status}",
"en": "\nResult: {status}",
"pl": "\nWynik: {status}",
"ru": "\nResult: {status}",
"zh": "\nResult: {status}"
},
@@ -101,6 +115,7 @@
"bg": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"de": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"en": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"pl": "\nRecenzja #{review_id} opublikowana na PR #{pr_number} ze zdarzeniem '{event}' ({num_comments} komentarzy w tekście).",
"ru": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments).",
"zh": "\nReview #{review_id} posted on PR #{pr_number} with event '{event}' ({num_comments} inline comments)."
},
@@ -108,6 +123,7 @@
"bg": "\nRunning full wiki integrity check...",
"de": "\nRunning full wiki integrity check...",
"en": "\nRunning full wiki integrity check...",
"pl": "\nUruchamianie pełnej kontroli integralności wiki...",
"ru": "\nRunning full wiki integrity check...",
"zh": "\nRunning full wiki integrity check..."
},
@@ -115,6 +131,7 @@
"bg": "\nTag → Commit alignment:",
"de": "\nTag → Commit alignment:",
"en": "\nTag → Commit alignment:",
"pl": "\nTag → Commit: zgodność:",
"ru": "\nTag → Commit alignment:",
"zh": "\nTag → Commit alignment:"
},
@@ -122,6 +139,7 @@
"bg": "\nUntagged release commits:",
"de": "\nUntagged release commits:",
"en": "\nUntagged release commits:",
"pl": "\nCommity wydania bez tagu:",
"ru": "\nUntagged release commits:",
"zh": "\nUntagged release commits:"
},
@@ -129,6 +147,7 @@
"bg": "\nUser-facing changes ({count}):",
"de": "\nUser-facing changes ({count}):",
"en": "\nUser-facing changes ({count}):",
"pl": "\nZmiany widoczne dla użytkownika ({count}):",
"ru": "\nUser-facing changes ({count}):",
"zh": "\nUser-facing changes ({count}):"
},
@@ -136,6 +155,7 @@
"bg": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"de": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"en": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"pl": "\nWeryfikacja NIEUDANA: {failures} strona(y) ma pustą lub niezgodną treść!",
"ru": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!",
"zh": "\nVerification FAILED: {failures} page(s) have empty or mismatched content!"
},
@@ -143,6 +163,7 @@
"bg": "\nVerification passed — all wiki pages have correct content.",
"de": "\nVerification passed — all wiki pages have correct content.",
"en": "\nVerification passed — all wiki pages have correct content.",
"pl": "\nWeryfikacja zakończona pomyślnie — wszystkie strony wiki mają poprawną treść.",
"ru": "\nVerification passed — all wiki pages have correct content.",
"zh": "\nVerification passed — all wiki pages have correct content."
},
@@ -150,6 +171,7 @@
"bg": "\nVerifying wiki pages have content...",
"de": "\nVerifying wiki pages have content...",
"en": "\nVerifying wiki pages have content...",
"pl": "\nWeryfikowanie, czy strony wiki mają treść...",
"ru": "\nVerifying wiki pages have content...",
"zh": "\nVerifying wiki pages have content..."
},
@@ -157,6 +179,7 @@
"bg": "\nWorkflow-only changes ({count}):",
"de": "\nWorkflow-only changes ({count}):",
"en": "\nWorkflow-only changes ({count}):",
"pl": "\nZmiany tylko w workflow ({count}):",
"ru": "\nWorkflow-only changes ({count}):",
"zh": "\nWorkflow-only changes ({count}):"
},
@@ -164,6 +187,7 @@
"bg": "\n[dry-run] Changelog:\n{changelog}",
"de": "\n[dry-run] Changelog:\n{changelog}",
"en": "\n[dry-run] Changelog:\n{changelog}",
"pl": "\n[dry-run] Changelog:\n{changelog}",
"ru": "\n[dry-run] Changelog:\n{changelog}",
"zh": "\n[dry-run] Changelog:\n{changelog}"
},
@@ -171,6 +195,7 @@
"bg": "\n{label} files changed ({count}):",
"de": "\n{label} files changed ({count}):",
"en": "\n{label} files changed ({count}):",
"pl": "\n{label} plików zmienionych ({count}):",
"ru": "\n{label} files changed ({count}):",
"zh": "\n{label} files changed ({count}):"
},
@@ -178,6 +203,7 @@
"bg": "\n{tag} files ({count}):",
"de": "\n{tag} files ({count}):",
"en": "\n{tag} files ({count}):",
"pl": "\nPliki {tag} ({count}):",
"ru": "\n{tag} files ({count}):",
"zh": "\n{tag} files ({count}):"
},
@@ -185,6 +211,7 @@
"bg": " - Автоматично изтриване на клон след сливане: да",
"de": " - Branch nach Merge automatisch löschen: ja",
"en": " - Auto-delete branch after merge: yes",
"pl": " - Auto-usuwanie gałęzi po scaleniu: tak",
"ru": " - Автоудаление ветки после слияния: да",
"zh": " - 合并后自动删除分支: 是"
},
@@ -192,6 +219,7 @@
"bg": " - Блокиране на остарели клонове: да",
"de": " - Veraltete Branches blockieren: ja",
"en": " - Block outdated branches: yes",
"pl": " - Blokowanie nieaktualnych gałęzi: tak",
"ru": " - Блокировать устаревшие ветки: да",
"zh": " - 阻止过时分支: 是"
},
@@ -199,6 +227,7 @@
"bg": " - Блокиране на отхвърлени рецензии: да",
"de": " - Abgelehnte Reviews blockieren: ja",
"en": " - Block rejected reviews: yes",
"pl": " - Blokowanie odrzuconych recenzji: tak",
"ru": " - Блокировать отклонённые ревью: да",
"zh": " - 阻止被拒绝的审查: 是"
},
@@ -206,6 +235,7 @@
"bg": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"de": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"en": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"pl": " - Bezpośrednie push-e: ZABLOKOWANE (wymagają PR, użytkownicy z białej listy mogą pushować)",
"ru": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)",
"zh": " - Direct pushes: BLOCKED (require PR, whitelisted users can push)"
},
@@ -213,6 +243,7 @@
"bg": " - Анулиране на остарели одобрения: да",
"de": " - Veraltete Genehmigungen ablehnen: ja",
"en": " - Dismiss stale approvals: yes",
"pl": " - Odrzucanie nieaktualnych zatwierdzeń: tak",
"ru": " - Отклонять устаревшие одобрения: да",
"zh": " - 忽略过时审批: 是"
},
@@ -220,6 +251,7 @@
"bg": " - Необходими одобрения: {count}",
"de": " - Erforderliche Genehmigungen: {count}",
"en": " - Required approvals: {count}",
"pl": " - Wymagane zatwierdzenia: {count}",
"ru": " - Требуемые одобрения: {count}",
"zh": " - 必需审批数: {count}"
},
@@ -227,6 +259,7 @@
"bg": " - Необходими проверки на състоянието: {checks}",
"de": " - Erforderliche Status-Checks: {checks}",
"en": " - Required status checks: {checks}",
"pl": " - Wymagane kontrole statusu: {checks}",
"ru": " - Требуемые проверки статуса: {checks}",
"zh": " - 必需状态检查: {checks}"
},
@@ -234,6 +267,7 @@
"bg": " Created: {title}",
"de": " Created: {title}",
"en": " Created: {title}",
"pl": " Utworzono: {title}",
"ru": " Created: {title}",
"zh": " Created: {title}"
},
@@ -241,6 +275,7 @@
"bg": " FAIL: {title} — content mismatch or empty!",
"de": " FAIL: {title} — content mismatch or empty!",
"en": " FAIL: {title} — content mismatch or empty!",
"pl": " BŁĄD: {title} — treść niezgodna lub pusta!",
"ru": " FAIL: {title} — content mismatch or empty!",
"zh": " FAIL: {title} — content mismatch or empty!"
},
@@ -248,6 +283,7 @@
"bg": " ЛИПСВА: devx {cmd}",
"de": " FEHLT: devx {cmd}",
"en": " MISSING: devx {cmd}",
"pl": " BRAK: devx {cmd}",
"ru": " ОТСУТСТВУЕТ: devx {cmd}",
"zh": " 缺失: devx {cmd}"
},
@@ -255,6 +291,7 @@
"bg": " MISSING: {module}",
"de": " MISSING: {module}",
"en": " MISSING: {module}",
"pl": " BRAK: {module}",
"ru": " MISSING: {module}",
"zh": " MISSING: {module}"
},
@@ -262,6 +299,7 @@
"bg": " MISSING: {script}",
"de": " MISSING: {script}",
"en": " MISSING: {script}",
"pl": " BRAK: {script}",
"ru": " MISSING: {script}",
"zh": " MISSING: {script}"
},
@@ -269,6 +307,7 @@
"bg": " ОК: devx {cmd}",
"de": " OK: devx {cmd}",
"en": " OK: devx {cmd}",
"pl": " OK: devx {cmd}",
"ru": " ОК: devx {cmd}",
"zh": " 正常: devx {cmd}"
},
@@ -276,6 +315,7 @@
"bg": " OK: {module}",
"de": " OK: {module}",
"en": " OK: {module}",
"pl": " OK: {module}",
"ru": " OK: {module}",
"zh": " OK: {module}"
},
@@ -283,6 +323,7 @@
"bg": " OK: {script}",
"de": " OK: {script}",
"en": " OK: {script}",
"pl": " OK: {script}",
"ru": " OK: {script}",
"zh": " OK: {script}"
},
@@ -290,6 +331,7 @@
"bg": " OK: {title} ({chars} chars)",
"de": " OK: {title} ({chars} chars)",
"en": " OK: {title} ({chars} chars)",
"pl": " OK: {title} ({chars} znaków)",
"ru": " OK: {title} ({chars} chars)",
"zh": " OK: {title} ({chars} chars)"
},
@@ -297,6 +339,7 @@
"bg": " Updated: {title}",
"de": " Updated: {title}",
"en": " Updated: {title}",
"pl": " Zaktualizowano: {title}",
"ru": " Updated: {title}",
"zh": " Updated: {title}"
},
@@ -304,6 +347,7 @@
"bg": "--skip-build: skipping package build and PyPI publish.",
"de": "--skip-build: skipping package build and PyPI publish.",
"en": "--skip-build: skipping package build and PyPI publish.",
"pl": "--skip-build: pomijanie budowania pakietu i publikacji PyPI.",
"ru": "--skip-build: skipping package build and PyPI publish.",
"zh": "--skip-build: skipping package build and PyPI publish."
},
@@ -311,6 +355,7 @@
"bg": "=== Release Alignment Verification ===\n",
"de": "=== Release Alignment Verification ===\n",
"en": "=== Release Alignment Verification ===\n",
"pl": "=== Weryfikacja zgodności wydań ===\n",
"ru": "=== Release Alignment Verification ===\n",
"zh": "=== Release Alignment Verification ===\n"
},
@@ -318,6 +363,7 @@
"bg": "API poll warning: {exc}",
"de": "API poll warning: {exc}",
"en": "API poll warning: {exc}",
"pl": "Ostrzeżenie sondowania API: {exc}",
"ru": "API poll warning: {exc}",
"zh": "API poll warning: {exc}"
},
@@ -325,6 +371,7 @@
"bg": "All molecule tests passed.",
"de": "All molecule tests passed.",
"en": "All molecule tests passed.",
"pl": "Wszystkie testy molecule zakończone pomyślnie.",
"ru": "All molecule tests passed.",
"zh": "All molecule tests passed."
},
@@ -332,13 +379,23 @@
"bg": "Another molecule runner failed. Stopping this runner early.",
"de": "Another molecule runner failed. Stopping this runner early.",
"en": "Another molecule runner failed. Stopping this runner early.",
"pl": "Inny runner molecule zakończył się niepowodzeniem. Wczesne zatrzymanie tego runnera.",
"ru": "Another molecule runner failed. Stopping this runner early.",
"zh": "Another molecule runner failed. Stopping this runner early."
},
"Branch is behind master. Rebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.": {
"bg": "Branch is behind master. Rebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
"de": "Branch is behind master. Rebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
"en": "Branch is behind master. Rebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
"pl": "Gałąź jest w tyle za master. Wykonaj rebase ręcznie:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nNastępnie dodaj ponownie etykietę ready-to-merge.",
"ru": "Branch is behind master. Rebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label.",
"zh": "Branch is behind master. Rebase manually:\n git fetch origin master && git rebase origin/master && git push --force-with-lease\nThen re-add the ready-to-merge label."
},
"Bumping version: {current} -> v{new_version}": {
"bg": "Bumping version: {current} -> v{new_version}",
"de": "Bumping version: {current} -> v{new_version}",
"en": "Bumping version: {current} -> v{new_version}",
"pl": "Zmiana wersji: {current} -> v{new_version}",
"ru": "Bumping version: {current} -> v{new_version}",
"zh": "Bumping version: {current} -> v{new_version}"
},
@@ -346,6 +403,7 @@
"bg": "Checking CLI command documentation...",
"de": "Checking CLI command documentation...",
"en": "Checking CLI command documentation...",
"pl": "Sprawdzanie dokumentacji poleceń CLI...",
"ru": "Checking CLI command documentation...",
"zh": "Checking CLI command documentation..."
},
@@ -353,6 +411,7 @@
"bg": "Command failed ({cmd}): {stderr}",
"de": "Command failed ({cmd}): {stderr}",
"en": "Command failed ({cmd}): {stderr}",
"pl": "Polecenie nie powiodło się ({cmd}): {stderr}",
"ru": "Command failed ({cmd}): {stderr}",
"zh": "Command failed ({cmd}): {stderr}"
},
@@ -360,6 +419,7 @@
"bg": "Comparing {base}..{head} ({count} files changed)",
"de": "Comparing {base}..{head} ({count} files changed)",
"en": "Comparing {base}..{head} ({count} files changed)",
"pl": "Porównywanie {base}..{head} ({count} zmienionych plików)",
"ru": "Comparing {base}..{head} ({count} files changed)",
"zh": "Comparing {base}..{head} ({count} files changed)"
},
@@ -367,6 +427,7 @@
"bg": "Конфигуриране на защита на клона {branch}...",
"de": "Konfiguriere Branch-Schutz für {branch}...",
"en": "Configuring branch protection for {branch}...",
"pl": "Konfigurowanie ochrony gałęzi dla {branch}...",
"ru": "Настройка защиты ветки {branch}...",
"zh": "正在配置 {branch} 的分支保护..."
},
@@ -374,6 +435,7 @@
"bg": "Конфигуриране на настройките на хранилището...",
"de": "Repository-Einstellungen konfigurieren...",
"en": "Configuring repository settings...",
"pl": "Konfigurowanie ustawień repozytorium...",
"ru": "Настройка параметров репозитория...",
"zh": "正在配置仓库设置..."
},
@@ -381,6 +443,7 @@
"bg": "Could not extract conventional commit message from PR commits.",
"de": "Could not extract conventional commit message from PR commits.",
"en": "Could not extract conventional commit message from PR commits.",
"pl": "Nie udało się wyodrębnić konwencjonalnej wiadomości commit z commitów PR.",
"ru": "Could not extract conventional commit message from PR commits.",
"zh": "Could not extract conventional commit message from PR commits."
},
@@ -388,6 +451,7 @@
"bg": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"de": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"en": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"pl": "Nie znaleziono zadania Vikunja {task_id} w projekcie {project_id}. Każdy PR musi mieć odpowiadające zadanie Vikunja.",
"ru": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task.",
"zh": "Could not find Vikunja task {task_id} in project {project_id}. Every PR must have a corresponding Vikunja task."
},
@@ -395,6 +459,7 @@
"bg": "Could not find __version__ in {file}",
"de": "Could not find __version__ in {file}",
"en": "Could not find __version__ in {file}",
"pl": "Nie znaleziono __version__ w {file}",
"ru": "Could not find __version__ in {file}",
"zh": "Could not find __version__ in {file}"
},
@@ -402,6 +467,7 @@
"bg": "Could not parse test execution time from output.",
"de": "Could not parse test execution time from output.",
"en": "Could not parse test execution time from output.",
"pl": "Nie udało się przeanalizować czasu wykonania testu z wyjścia.",
"ru": "Could not parse test execution time from output.",
"zh": "Could not parse test execution time from output."
},
@@ -409,6 +475,7 @@
"bg": "Created issue #{issue_id}: {title}",
"de": "Created issue #{issue_id}: {title}",
"en": "Created issue #{issue_id}: {title}",
"pl": "Utworzono zgłoszenie #{issue_id}: {title}",
"ru": "Created issue #{issue_id}: {title}",
"zh": "Created issue #{issue_id}: {title}"
},
@@ -416,6 +483,7 @@
"bg": "Created release commit.",
"de": "Created release commit.",
"en": "Created release commit.",
"pl": "Utworzono commit wydania.",
"ru": "Created release commit.",
"zh": "Created release commit."
},
@@ -423,6 +491,7 @@
"bg": "Докер демонът вече работи",
"de": "Docker-Daemon läuft bereits",
"en": "Docker daemon already running",
"pl": "Demon Docker już uruchomiony",
"ru": "Демон Docker уже работает",
"zh": "Docker 守护进程已在运行"
},
@@ -430,6 +499,7 @@
"bg": "Docker daemon failed to start",
"de": "Docker-Daemon konnte nicht gestartet werden",
"en": "Docker daemon failed to start",
"pl": "Nie udało się uruchomić demona Docker",
"ru": "Не удалось запустить Docker-демон",
"zh": "Docker 守护进程启动失败"
},
@@ -437,6 +507,7 @@
"bg": "Docker daemon started",
"de": "Docker-Daemon gestartet",
"en": "Docker daemon started",
"pl": "Demon Docker uruchomiony",
"ru": "Docker-демон запущен",
"zh": "Docker 守护进程已启动"
},
@@ -444,6 +515,7 @@
"bg": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"de": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"en": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"pl": "Tryb dry-run: na gałęzi '{branch}' (nie master). Niektóre kontrole mogą zachowywać się inaczej.",
"ru": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently.",
"zh": "Dry-run mode: on branch '{branch}' (not master). Some checks may behave differently."
},
@@ -451,6 +523,7 @@
"bg": "ГРЕШКА: REPO_TOKEN не е зададен.",
"de": "FEHLER: REPO_TOKEN ist nicht gesetzt.",
"en": "ERROR: REPO_TOKEN is not set.",
"pl": "BŁĄD: REPO_TOKEN nie jest ustawiony.",
"ru": "ОШИБКА: REPO_TOKEN не задан.",
"zh": "错误:未设置 REPO_TOKEN。"
},
@@ -458,6 +531,7 @@
"bg": "ГРЕШКА: Името на хранилището не е указано. Използвайте --repo или задайте DEVX_REPO_NAME.",
"de": "FEHLER: Repository-Name nicht angegeben. Verwenden Sie --repo oder setzen Sie DEVX_REPO_NAME.",
"en": "ERROR: Repository name not specified. Use --repo or set DEVX_REPO_NAME.",
"pl": "BŁĄD: Nazwa repozytorium nie jest określona. Użyj --repo lub ustaw DEVX_REPO_NAME.",
"ru": "ОШИБКА: Имя репозитория не указано. Используйте --repo или задайте DEVX_REPO_NAME.",
"zh": "错误:未指定仓库名称。请使用 --repo 或设置 DEVX_REPO_NAME。"
},
@@ -465,6 +539,7 @@
"bg": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"de": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"en": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"pl": "BŁĄD: Kontrola zgodności tagów nie powiodła się. Istniejące tagi są niezgodne:",
"ru": "ERROR: Tag consistency check failed. Existing tags are misaligned:",
"zh": "ERROR: Tag consistency check failed. Existing tags are misaligned:"
},
@@ -472,6 +547,7 @@
"bg": "ГРЕШКА: VIKUNJA_TOKEN не е зададен.",
"de": "FEHLER: VIKUNJA_TOKEN ist nicht gesetzt.",
"en": "ERROR: VIKUNJA_TOKEN is not set.",
"pl": "BŁĄD: VIKUNJA_TOKEN nie jest ustawiony.",
"ru": "ОШИБКА: VIKUNJA_TOKEN не задан.",
"zh": "错误:未设置 VIKUNJA_TOKEN。"
},
@@ -479,6 +555,7 @@
"bg": "ERROR: mapping.json not found at {path}",
"de": "ERROR: mapping.json not found at {path}",
"en": "ERROR: mapping.json not found at {path}",
"pl": "BŁĄD: mapping.json nie znaleziono w {path}",
"ru": "ERROR: mapping.json not found at {path}",
"zh": "ERROR: mapping.json not found at {path}"
},
@@ -486,6 +563,7 @@
"bg": "FAILED: {pair} exited with code {code}",
"de": "FAILED: {pair} exited with code {code}",
"en": "FAILED: {pair} exited with code {code}",
"pl": "NIEUDANE: {pair} zakończone kodem {code}",
"ru": "FAILED: {pair} exited with code {code}",
"zh": "FAILED: {pair} exited with code {code}"
},
@@ -493,6 +571,7 @@
"bg": "Failed to create issue via tea: {error}",
"de": "Failed to create issue via tea: {error}",
"en": "Failed to create issue via tea: {error}",
"pl": "Nie udało się utworzyć zgłoszenia przez tea: {error}",
"ru": "Failed to create issue via tea: {error}",
"zh": "Failed to create issue via tea: {error}"
},
@@ -500,6 +579,7 @@
"bg": "Found {count} existing wiki pages.",
"de": "Found {count} existing wiki pages.",
"en": "Found {count} existing wiki pages.",
"pl": "Znaleziono {count} istniejących stron wiki.",
"ru": "Found {count} existing wiki pages.",
"zh": "Found {count} existing wiki pages."
},
@@ -507,6 +587,7 @@
"bg": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"de": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"en": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"pl": "GITEA_URL/REPO_TOKEN/RUN_ID nie ustawione; uruchamianie bez anulowania między runnerami.",
"ru": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation.",
"zh": "GITEA_URL/REPO_TOKEN/RUN_ID not set; running without cross-runner cancellation."
},
@@ -514,13 +595,23 @@
"bg": "Generated {file} with prefix '{prefix}'.",
"de": "Generated {file} with prefix '{prefix}'.",
"en": "Generated {file} with prefix '{prefix}'.",
"pl": "Wygenerowano {file} z prefiksem '{prefix}'.",
"ru": "Generated {file} with prefix '{prefix}'.",
"zh": "Generated {file} with prefix '{prefix}'."
},
"Gitea release {tag} already exists — skipping creation.": {
"bg": "Gitea release {tag} вече съществува — прескачане на създаването.",
"de": "Gitea-Release {tag} existiert bereits — Erstellung übersprungen.",
"en": "Gitea release {tag} already exists — skipping creation.",
"pl": "Wydanie Gitea {tag} już istnieje — pomijanie tworzenia.",
"ru": "Gitea release {tag} уже существует — пропуск создания.",
"zh": "Gitea release {tag} 已存在 — 跳过创建。"
},
"HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.": {
"bg": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"de": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"en": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"pl": "HEAD jest commitem wydania ('{msg}') ale tag {tag} brakuje. Naprawa przez utworzenie tagu.",
"ru": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag.",
"zh": "HEAD is a release commit ('{msg}') but tag {tag} is missing. Recovering by creating tag."
},
@@ -528,6 +619,7 @@
"bg": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"de": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"en": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"pl": "HEAD jest commitem wydania dla v{version} ale tag {tag} wskazuje na inny commit ({tag_commit} vs HEAD {head_commit}). Wskazuje to na niezgodność tag/commit.",
"ru": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment.",
"zh": "HEAD is a release commit for v{version} but tag {tag} points to a different commit ({tag_commit} vs HEAD {head_commit}). This indicates a tag/commit misalignment."
},
@@ -535,6 +627,7 @@
"bg": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"de": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"en": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"pl": "HEAD jest już commitem wydania ('{msg}') a tag {tag} wskazuje na HEAD. Pomijanie.",
"ru": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping.",
"zh": "HEAD is already a release commit ('{msg}') and tag {tag} points to HEAD. Skipping."
},
@@ -542,6 +635,7 @@
"bg": "HTTP грешка: {status} — {message}",
"de": "HTTP-Fehler: {status} — {message}",
"en": "HTTP error: {status} — {message}",
"pl": "Błąd HTTP: {status} — {message}",
"ru": "Ошибка HTTP: {status} — {message}",
"zh": "HTTP 错误: {status} — {message}"
},
@@ -549,20 +643,15 @@
"bg": "HTTP {status} Забранено — вашият токен няма администраторски права.\nУверете се, че токенът принадлежи на собственик на хранилище или администратор на организация.\nАлтернативно, конфигурирайте защитата на клона ръчно в Настройки → Клонове.",
"de": "HTTP {status} Verboten — Ihr Token hat keine Admin-Rechte.\nStellen Sie sicher, dass das Token einem Repository-Besitzer oder Organisations-Admin gehört.\nAlternativ können Sie den Branch-Schutz manuell unter Einstellungen → Branches konfigurieren.",
"en": "HTTP {status} Forbidden — your token lacks admin rights.\nMake sure the token belongs to a repo owner or organisation admin.\nAlternatively, configure branch protection manually in Settings → Branches.",
"pl": "HTTP {status} Forbidden — twój token nie ma uprawnień administratora.\nUpewnij się, że token należy do właściciela repozytorium lub administratora organizacji.\nAlternatywnie skonfiguruj ochronę gałęzi ręcznie w Ustawienia → Gałęzie.",
"ru": "HTTP {status} Запрещено — у вашего токена нет прав администратора.\nУбедитесь, что токен принадлежит владельцу репозитория или администратору организации.\nЛибо настройте защиту ветки вручную в разделе Настройки → Ветки.",
"zh": "HTTP {status} 禁止访问 — 您的令牌缺少管理员权限。\n请确保令牌属于仓库所有者或组织管理员。\n或者,您可以在 设置 → 分支 中手动配置分支保护。"
},
"Head branch is behind master. Pulling and rebasing...": {
"bg": "Head branch is behind master. Pulling and rebasing...",
"de": "Head branch is behind master. Pulling and rebasing...",
"en": "Head branch is behind master. Pulling and rebasing...",
"ru": "Head branch is behind master. Pulling and rebasing...",
"zh": "Head branch is behind master. Pulling and rebasing..."
},
"Host Docker not available, starting local dockerd...": {
"bg": "Хост Docker не е наличен, стартиране на локален dockerd...",
"de": "Host-Docker nicht verfügbar, lokaler dockerd wird gestartet...",
"en": "Host Docker not available, starting local dockerd...",
"pl": "Host Docker niedostępny, uruchamianie lokalnego dockerd...",
"ru": "Хост Docker недоступен, запускается локальный dockerd...",
"zh": "主机 Docker 不可用,正在启动本地 dockerd..."
},
@@ -570,6 +659,7 @@
"bg": "Инфраструктурен commit (без идентификатор на задача DEVX-N), пропускаме обновяването на Vikunja: {msg}",
"de": "Infrastruktur-Commit (keine DEVX-N Task-ID), Vikunja-Update wird übersprungen: {msg}",
"en": "Infrastructure commit (no DEVX-N task ID), skipping Vikunja update: {msg}",
"pl": "Commit infrastruktury (bez ID zadania DEVX-N), pomijanie aktualizacji Vikunja: {msg}",
"ru": "Инфраструктурный коммит (без ID задачи DEVX-N), пропуск обновления Vikunja: {msg}",
"zh": "基础设施提交(无 DEVX-N 任务 ID),跳过 Vikunja 更新: {msg}"
},
@@ -577,6 +667,7 @@
"bg": "Integration tests cancelled — another runner failed.",
"de": "Integration tests cancelled — another runner failed.",
"en": "Integration tests cancelled — another runner failed.",
"pl": "Testy integracyjne anulowane — inny runner zakończył się niepowodzeniem.",
"ru": "Integration tests cancelled — another runner failed.",
"zh": "Integration tests cancelled — another runner failed."
},
@@ -584,6 +675,7 @@
"bg": "Integration tests failed with exit code {code}",
"de": "Integration tests failed with exit code {code}",
"en": "Integration tests failed with exit code {code}",
"pl": "Testy integracyjne zakończone niepowodzeniem z kodem {code}",
"ru": "Integration tests failed with exit code {code}",
"zh": "Integration tests failed with exit code {code}"
},
@@ -591,6 +683,7 @@
"bg": "Integration tests passed.",
"de": "Integration tests passed.",
"en": "Integration tests passed.",
"pl": "Testy integracyjne zakończone pomyślnie.",
"ru": "Integration tests passed.",
"zh": "Integration tests passed."
},
@@ -598,6 +691,7 @@
"bg": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"de": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"en": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"pl": "Lint nie powiódł się — odmowa wydania. Najpierw napraw błędy lint.\n{stderr}",
"ru": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}",
"zh": "Lint failed — refusing to release. Fix lint errors first.\n{stderr}"
},
@@ -605,6 +699,7 @@
"bg": "Lint passed.",
"de": "Lint passed.",
"en": "Lint passed.",
"pl": "Lint zakończony pomyślnie.",
"ru": "Lint passed.",
"zh": "Lint passed."
},
@@ -612,6 +707,7 @@
"bg": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"de": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"en": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"pl": "Mapowany plik {file} jest pusty. Zaktualizuj treść lub usuń z mapping.json.",
"ru": "Mapped file {file} is empty. Update the content or remove from mapping.json.",
"zh": "Mapped file {file} is empty. Update the content or remove from mapping.json."
},
@@ -619,20 +715,15 @@
"bg": "Mapped file {file} not found. Update mapping.json or create the file.",
"de": "Mapped file {file} not found. Update mapping.json or create the file.",
"en": "Mapped file {file} not found. Update mapping.json or create the file.",
"pl": "Mapowany plik {file} nie znaleziony. Zaktualizuj mapping.json lub utwórz plik.",
"ru": "Mapped file {file} not found. Update mapping.json or create the file.",
"zh": "Mapped file {file} not found. Update mapping.json or create the file."
},
"Merge failed after rebase retry: {error}\nPlease rebase the PR manually.": {
"bg": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"de": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"en": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"ru": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually.",
"zh": "Merge failed after rebase retry: {error}\nPlease rebase the PR manually."
},
"Merge failed with HTTP {status}: {message}\nPlease check the PR is ready and you have merge rights.": {
"bg": "Сливането неуспешно с HTTP {status}: {message}\nПроверете дали PR е готов и имате права за сливане.",
"de": "Merge fehlgeschlagen mit HTTP {status}: {message}\nBitte prüfen Sie, ob der PR bereit ist und Sie Merge-Rechte haben.",
"en": "Merge failed with HTTP {status}: {message}\nPlease check the PR is ready and you have merge rights.",
"pl": "Scalanie nie powiodło się z HTTP {status}: {message}\nSprawdź czy PR jest gotowy i masz uprawnienia do scalania.",
"ru": "Слияние не удалось: HTTP {status}: {message}\nПроверьте, что PR готов и у вас есть права на слияние.",
"zh": "合并失败: HTTP {status}: {message}\n请检查 PR 是否准备就绪且您具有合并权限。"
},
@@ -640,6 +731,7 @@
"bg": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"de": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"en": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"pl": "Scalono {count} raportów: {tests} testów, {failures} niepowodzeń → {output}",
"ru": "Merged {count} reports: {tests} tests, {failures} failures → {output}",
"zh": "Merged {count} reports: {tests} tests, {failures} failures → {output}"
},
@@ -647,6 +739,7 @@
"bg": "Модул {mod} няма функция main()",
"de": "Modul {mod} hat keine main()-Funktion",
"en": "Module {mod} has no main() function",
"pl": "Moduł {mod} nie ma funkcji main()",
"ru": "Модуль {mod} не имеет функции main()",
"zh": "模块 {mod} 没有 main() 函数"
},
@@ -654,20 +747,15 @@
"bg": "Директорията на molecule не е намерена: {path}",
"de": "Molecule-Verzeichnis nicht gefunden: {path}",
"en": "Molecule directory not found: {path}",
"pl": "Katalog molecule nie znaleziony: {path}",
"ru": "Директория molecule не найдена: {path}",
"zh": "未找到 molecule 目录: {path}"
},
"Gitea release {tag} already exists — skipping creation.": {
"bg": "Gitea release {tag} вече съществува — прескачане на създаването.",
"de": "Gitea-Release {tag} existiert bereits — Erstellung übersprungen.",
"en": "Gitea release {tag} already exists — skipping creation.",
"ru": "Gitea release {tag} уже существует — пропуск создания.",
"zh": "Gitea release {tag} 已存在 — 跳过创建。"
},
"Nice! Gitea release {tag} created.": {
"bg": "Отлично! Gitea release {tag} е създаден.",
"de": "Prima! Gitea-Release {tag} erstellt.",
"en": "Nice! Gitea release {tag} created.",
"pl": "Świetnie! Wydanie Gitea {tag} utworzone.",
"ru": "Отлично! Gitea release {tag} создан.",
"zh": "不错!Gitea release {tag} 已创建。"
},
@@ -675,6 +763,7 @@
"bg": "Отлично! PR #{pr_number} е squash-merge-нат със заглавие: {merge_title}",
"de": "Prima! PR #{pr_number} wurde mit Titel {merge_title} squash-gemergt.",
"en": "Nice! PR #{pr_number} squash-merged with title: {merge_title}",
"pl": "Świetnie! PR #{pr_number} squash-merged z tytułem: {merge_title}",
"ru": "Отлично! PR #{pr_number} squash-merge с заголовком: {merge_title}",
"zh": "不错!PR #{pr_number} 已 squash 合并,标题: {merge_title}"
},
@@ -682,6 +771,7 @@
"bg": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"de": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"en": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"pl": "Świetnie! Wydanie v{version} otagowane i wypchnięte. Workflow publikacji zostanie uruchomiony.",
"ru": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered.",
"zh": "Nice! Release v{version} tagged and pushed. The publish workflow will be triggered."
},
@@ -689,6 +779,7 @@
"bg": "Отлично! Задача Vikunja {task_id} (ID {vikunja_id}) е обновена и маркирана като готова.",
"de": "Prima! Vikunja-Aufgabe {task_id} (ID {vikunja_id}) aktualisiert und als erledigt markiert.",
"en": "Nice! Vikunja task {task_id} (ID {vikunja_id}) updated and marked done.",
"pl": "Świetnie! Zadanie Vikunja {task_id} (ID {vikunja_id}) zaktualizowane i oznaczone jako ukończone.",
"ru": "Отлично! Задача Vikunja {task_id} (ID {vikunja_id}) обновлена и отмечена как выполненная.",
"zh": "不错!Vikunja 任务 {task_id} (ID {vikunja_id}) 已更新并标记为完成。"
},
@@ -696,6 +787,7 @@
"bg": "No JUnit reports found matching {pattern} — skipping merge.",
"de": "No JUnit reports found matching {pattern} — skipping merge.",
"en": "No JUnit reports found matching {pattern} — skipping merge.",
"pl": "Nie znaleziono raportów JUnit pasujących do {pattern} — pomijanie scalania.",
"ru": "No JUnit reports found matching {pattern} — skipping merge.",
"zh": "No JUnit reports found matching {pattern} — skipping merge."
},
@@ -703,6 +795,7 @@
"bg": "No changes between {base} and {head}.",
"de": "No changes between {base} and {head}.",
"en": "No changes between {base} and {head}.",
"pl": "Brak zmian między {base} i {head}.",
"ru": "No changes between {base} and {head}.",
"zh": "No changes between {base} and {head}."
},
@@ -710,6 +803,7 @@
"bg": "No staged changes — version and changelog already up to date.",
"de": "No staged changes — version and changelog already up to date.",
"en": "No staged changes — version and changelog already up to date.",
"pl": "Brak zmian w staging — wersja i changelog są już aktualne.",
"ru": "No staged changes — version and changelog already up to date.",
"zh": "No staged changes — version and changelog already up to date."
},
@@ -717,6 +811,7 @@
"bg": "No tags found — treating all changes as user-facing.",
"de": "No tags found — treating all changes as user-facing.",
"en": "No tags found — treating all changes as user-facing.",
"pl": "Nie znaleziono tagów — traktowanie wszystkich zmian jako widocznych dla użytkownika.",
"ru": "No tags found — treating all changes as user-facing.",
"zh": "No tags found — treating all changes as user-facing."
},
@@ -724,6 +819,7 @@
"bg": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"de": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"en": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"pl": "Nie znaleziono ID zadania ({prefix}-N) w wiadomości commit: {msg}. Każdy commit nie-infrastrukturalny musi mieć ID zadania.",
"ru": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID.",
"zh": "No task ID ({prefix}-N) found in commit message: {msg}. Every non-infrastructure commit must have a task ID."
},
@@ -731,6 +827,7 @@
"bg": "No unreleased changes found. Nothing to release.",
"de": "No unreleased changes found. Nothing to release.",
"en": "No unreleased changes found. Nothing to release.",
"pl": "Nie znaleziono nieopublikowanych zmian. Nic do wydania.",
"ru": "No unreleased changes found. Nothing to release.",
"zh": "No unreleased changes found. Nothing to release."
},
@@ -738,6 +835,7 @@
"bg": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"de": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"en": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"pl": "Brak zmian widocznych dla użytkownika od {tag} — tylko pliki workflow/infrastruktury uległy zmianie. Pomijanie wydania.",
"ru": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release.",
"zh": "No user-facing changes since {tag} — only workflow/infrastructure files changed. Skipping release."
},
@@ -745,6 +843,7 @@
"bg": "Note: Self-approval not allowed. Posting COMMENT instead.",
"de": "Note: Self-approval not allowed. Posting COMMENT instead.",
"en": "Note: Self-approval not allowed. Posting COMMENT instead.",
"pl": "Uwaga: Samo-zatwierdzenie niedozwolone. Publikowanie COMMENT zamiast tego.",
"ru": "Note: Self-approval not allowed. Posting COMMENT instead.",
"zh": "Note: Self-approval not allowed. Posting COMMENT instead."
},
@@ -752,6 +851,7 @@
"bg": "Опа! Съобщението за commit трябва да следва конвенционален формат.\n Очаква се: <type>: <description>\n Получено: {subject}\n Разрешени типове: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"de": "Ups! Commit-Nachricht muss dem konventionellen Commit-Format folgen.\n Erwartet: <type>: <description>\n Erhalten: {subject}\n Erlaubte Typen: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"en": "Oops! Commit message must follow conventional commit format.\n Expected: <type>: <description>\n Got: {subject}\n Allowed types: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"pl": "Ups! Wiadomość commit musi być w formacie conventional commit.\n Oczekiwano: <typ>: <opis>\n Otrzymano: {subject}\n Dozwolone typy: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"ru": "Ой! Сообщение коммита должно соответствовать формату conventional commit.\n Ожидается: <type>: <description>\n Получено: {subject}\n Допустимые типы: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE",
"zh": "哎呀!提交消息必须遵循 conventional commit 格式。\n 预期格式: <type>: <description>\n 实际: {subject}\n 允许的类型: feat, fix, chore, docs, style, refactor,\n perf, test, ci, build, revert, BREAKING CHANGE"
},
@@ -759,6 +859,7 @@
"bg": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"de": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"en": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"pl": "Ups! Nie dołączaj ID zadania ({prefix}-N) w commitach gałęzi feature.\n ID zadania zostanie dodane automatycznie przy scaleniu przez CI.",
"ru": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI.",
"zh": "Oops! Do not include task ID ({prefix}-N) in feature branch commits.\n The task ID will be added automatically on merge via CI."
},
@@ -766,6 +867,7 @@
"bg": "Опа! Публикуването в Gitea PyPI registry неуспешно:\n{stderr}",
"de": "Ups! Veröffentlichung in der Gitea PyPI-Registry fehlgeschlagen:\n{stderr}",
"en": "Oops! Gitea PyPI registry publish failed:\n{stderr}",
"pl": "Ups! Publikacja w rejestrze Gitea PyPI nie powiodła się:\n{stderr}",
"ru": "Ой! Публикация в Gitea PyPI registry не удалась:\n{stderr}",
"zh": "哎呀!Gitea PyPI registry 发布失败:\n{stderr}"
},
@@ -773,6 +875,7 @@
"bg": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"de": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"en": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"pl": "Ups! Commit gałęzi master musi być w formacie conventional po ID zadania.\n Oczekiwano: {prefix}-N: <typ>: <opis>\n Otrzymano: {subject}",
"ru": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}",
"zh": "Oops! Master branch commit must follow conventional format after task ID.\n Expected: {prefix}-N: <type>: <description>\n Got: {subject}"
},
@@ -780,13 +883,23 @@
"bg": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"de": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"en": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"pl": "Ups! Commity gałęzi master muszą zaczynać się od ID zadania.\n Oczekiwano: {prefix}-N: <conwencjonalna wiadomość commit>\n Otrzymano: {subject}",
"ru": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"zh": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}"
},
"Oops! No task ID found in branch name '{branch}'. Branch names must include the task ID prefix (e.g., DEVX-31-fix-bug).": {
"bg": "Ой! Не е намерен ID на задача в името на клона '{branch}'. Имената на клонове трябва да включват префикса за ID на задача (напр. DEVX-31-fix-bug).",
"de": "Hoppla! Keine Task-ID im Branch-Namen '{branch}' gefunden. Branch-Namen müssen das Task-ID-Präfix enthalten (z.B. DEVX-31-fix-bug).",
"en": "Oops! No task ID found in branch name '{branch}'. Branch names must include the task ID prefix (e.g., DEVX-31-fix-bug).",
"pl": "Ups! Nie znaleziono ID zadania w nazwie gałęzi '{branch}'. Nazwy gałęzi muszą zawierać prefiks ID zadania (np., DEVX-31-fix-bug).",
"ru": "Ой! ID задачи не найден в имени ветки '{branch}'. Имена веток должны включать префикс ID задачи (например, DEVX-31-fix-bug).",
"zh": "哎呀!在分支名称 '{branch}' 中未找到任务 ID。分支名称必须包含任务 ID 前缀(例如 DEVX-31-fix-bug)。"
},
"Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}": {
"bg": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"de": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"en": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"pl": "Ups! Tytuł PR musi być w formacie '{prefix}-N: <tytuł zadania>'.\n Oczekiwano: {task_id}: <tytuł zadania>\n Otrzymano: {pr_title}",
"ru": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"zh": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}"
},
@@ -794,6 +907,7 @@
"bg": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"de": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"en": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"pl": "Ups! Niezgodność ID zadania w tytule PR.\n ID zadania z gałęzi: {task_id}\n Tytuł PR: {pr_title}",
"ru": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}",
"zh": "Oops! PR title task ID mismatch.\n Branch task ID: {task_id}\n PR title: {pr_title}"
},
@@ -801,6 +915,7 @@
"bg": "Опа! Сборката на пакета неуспешна:\n{stderr}",
"de": "Ups! Paket-Build fehlgeschlagen:\n{stderr}",
"en": "Oops! Package build failed:\n{stderr}",
"pl": "Ups! Budowanie pakietu nie powiodło się:\n{stderr}",
"ru": "Ой! Сборка пакета не удалась:\n{stderr}",
"zh": "哎呀!包构建失败:\n{stderr}"
},
@@ -808,20 +923,15 @@
"bg": "Опа! Публикуването в PyPI неуспешно:\n{stderr}",
"de": "Ups! PyPI-Veröffentlichung fehlgeschlagen:\n{stderr}",
"en": "Oops! PyPI publish failed:\n{stderr}",
"pl": "Ups! Publikacja PyPI nie powiodła się:\n{stderr}",
"ru": "Ой! Публикация в PyPI не удалась:\n{stderr}",
"zh": "哎呀!PyPI 发布失败:\n{stderr}"
},
"Parsed owner={owner}, repo={repo} from DEVX_REPO_NAME": {
"bg": "Разбор на owner={owner}, repo={repo} от DEVX_REPO_NAME",
"de": "Owner={owner}, repo={repo} aus DEVX_REPO_NAME analysiert",
"en": "Parsed owner={owner}, repo={repo} from DEVX_REPO_NAME",
"ru": "Извлечён owner={owner}, repo={repo} из DEVX_REPO_NAME",
"zh": "从 DEVX_REPO_NAME 解析 owner={owner}, repo={repo}"
},
"PASSED: {pair}": {
"bg": "PASSED: {pair}",
"de": "PASSED: {pair}",
"en": "PASSED: {pair}",
"pl": "UDANE: {pair}",
"ru": "PASSED: {pair}",
"zh": "PASSED: {pair}"
},
@@ -829,6 +939,7 @@
"bg": "PR number must be an integer, got: {pr_number}",
"de": "PR number must be an integer, got: {pr_number}",
"en": "PR number must be an integer, got: {pr_number}",
"pl": "Numer PR musi być liczbą całkowitą, otrzymano: {pr_number}",
"ru": "PR number must be an integer, got: {pr_number}",
"zh": "PR number must be an integer, got: {pr_number}"
},
@@ -836,6 +947,7 @@
"bg": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"de": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"en": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"pl": "Tytuł PR nie pasuje do tytułu zadania Vikunja.\n Oczekiwano: {expected}\n Otrzymano: {pr_title}",
"ru": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}",
"zh": "PR title does not match Vikunja task title.\n Expected: {expected}\n Got: {pr_title}"
},
@@ -843,13 +955,23 @@
"bg": "PYPI_TOKEN не е зададен и няма конфигуриран URL на registry — пропускаме публикуването в PyPI. Без притеснения, просто ще създадем Gitea release.",
"de": "PYPI_TOKEN nicht gesetzt und keine Registry-URL konfiguriert — PyPI-Veröffentlichung wird übersprungen. Keine Sorge, wir erstellen einfach das Gitea-Release.",
"en": "PYPI_TOKEN not set and no registry URL configured — skipping PyPI publish. No worries, we'll just create the Gitea release.",
"pl": "PYPI_TOKEN nie ustawiony i brak URL rejestru — pomijanie publikacji PyPI. Bez obaw, utworzymy tylko wydanie Gitea.",
"ru": "PYPI_TOKEN не задан и URL registry не настроен — пропускаем публикацию в PyPI. Не беспокойтесь, мы просто создадим Gitea release.",
"zh": "未设置 PYPI_TOKEN 且未配置 registry URL — 跳过 PyPI 发布。别担心,我们直接创建 Gitea release。"
},
"Parsed owner={owner}, repo={repo} from DEVX_REPO_NAME": {
"bg": "Разбор на owner={owner}, repo={repo} от DEVX_REPO_NAME",
"de": "Owner={owner}, repo={repo} aus DEVX_REPO_NAME analysiert",
"en": "Parsed owner={owner}, repo={repo} from DEVX_REPO_NAME",
"pl": "Przeanalizowano owner={owner}, repo={repo} z DEVX_REPO_NAME",
"ru": "Извлечён owner={owner}, repo={repo} из DEVX_REPO_NAME",
"zh": "从 DEVX_REPO_NAME 解析 owner={owner}, repo={repo}"
},
"Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.": {
"bg": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"de": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"en": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"pl": "Kontrola szybkości pojedynczego testu NIEUDANA: {count} test(ów) przekracza limit {limit}s.",
"ru": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit.",
"zh": "Per-test speed check FAILED: {count} test(s) exceed {limit}s limit."
},
@@ -857,6 +979,7 @@
"bg": "Публикувано в Gitea PyPI registry.",
"de": "In der Gitea PyPI-Registry veröffentlicht.",
"en": "Published to Gitea PyPI registry.",
"pl": "Opublikowano w rejestrze Gitea PyPI.",
"ru": "Опубликовано в Gitea PyPI registry.",
"zh": "已发布到 Gitea PyPI registry。"
},
@@ -864,6 +987,7 @@
"bg": "Публикувано в PyPI.",
"de": "In PyPI veröffentlicht.",
"en": "Published to PyPI.",
"pl": "Opublikowano w PyPI.",
"ru": "Опубликовано в PyPI.",
"zh": "已发布到 PyPI。"
},
@@ -871,20 +995,15 @@
"bg": "Pushed release commit to master.",
"de": "Pushed release commit to master.",
"en": "Pushed release commit to master.",
"pl": "Wypchnięto commit wydania do master.",
"ru": "Pushed release commit to master.",
"zh": "Pushed release commit to master."
},
"Rebased and pushed. Retrying merge...": {
"bg": "Rebased and pushed. Retrying merge...",
"de": "Rebased and pushed. Retrying merge...",
"en": "Rebased and pushed. Retrying merge...",
"ru": "Rebased and pushed. Retrying merge...",
"zh": "Rebased and pushed. Retrying merge..."
},
"Release creation failed: {error}": {
"bg": "Release creation failed: {error}",
"de": "Release creation failed: {error}",
"en": "Release creation failed: {error}",
"pl": "Tworzenie wydania nie powiodło się: {error}",
"ru": "Release creation failed: {error}",
"zh": "Release creation failed: {error}"
},
@@ -892,6 +1011,7 @@
"bg": "Release must be run on master, currently on '{branch}'.",
"de": "Release must be run on master, currently on '{branch}'.",
"en": "Release must be run on master, currently on '{branch}'.",
"pl": "Wydanie musi być uruchomione na master, obecnie na '{branch}'.",
"ru": "Release must be run on master, currently on '{branch}'.",
"zh": "Release must be run on master, currently on '{branch}'."
},
@@ -899,6 +1019,7 @@
"bg": "Repo must be in 'owner/name' format, got: {repo}",
"de": "Repo must be in 'owner/name' format, got: {repo}",
"en": "Repo must be in 'owner/name' format, got: {repo}",
"pl": "Repo musi być w formacie 'owner/name', otrzymano: {repo}",
"ru": "Repo must be in 'owner/name' format, got: {repo}",
"zh": "Repo must be in 'owner/name' format, got: {repo}"
},
@@ -906,6 +1027,7 @@
"bg": "Конфигурирането на хранилището е завършено.",
"de": "Repository-Konfiguration abgeschlossen.",
"en": "Repository configuration complete.",
"pl": "Konfiguracja repozytorium zakończona.",
"ru": "Конфигурация репозитория завершена.",
"zh": "仓库配置完成。"
},
@@ -913,6 +1035,7 @@
"bg": "Roles directory not found: {path}",
"de": "Roles directory not found: {path}",
"en": "Roles directory not found: {path}",
"pl": "Katalog ról nie znaleziony: {path}",
"ru": "Roles directory not found: {path}",
"zh": "Roles directory not found: {path}"
},
@@ -920,6 +1043,7 @@
"bg": "Индексът на runner {index} е извън диапазона (0..{max})",
"de": "Runner-Index {index} außerhalb des Bereichs (0..{max})",
"en": "Runner index {index} out of range (0..{max})",
"pl": "Indeks runnera {index} poza zakresem (0..{max})",
"ru": "Индекс runner {index} вне диапазона (0..{max})",
"zh": "Runner 索引 {index} 超出范围 (0..{max})"
},
@@ -927,6 +1051,7 @@
"bg": "Running lint checks...",
"de": "Running lint checks...",
"en": "Running lint checks...",
"pl": "Uruchamianie kontroli lint...",
"ru": "Running lint checks...",
"zh": "Running lint checks..."
},
@@ -934,6 +1059,7 @@
"bg": "Running tests...",
"de": "Running tests...",
"en": "Running tests...",
"pl": "Uruchamianie testów...",
"ru": "Running tests...",
"zh": "Running tests..."
},
@@ -941,6 +1067,7 @@
"bg": "Running: {scenario} on {platform}",
"de": "Running: {scenario} on {platform}",
"en": "Running: {scenario} on {platform}",
"pl": "Uruchamianie: {scenario} na {platform}",
"ru": "Running: {scenario} on {platform}",
"zh": "Running: {scenario} on {platform}"
},
@@ -948,6 +1075,7 @@
"bg": "Skipping commit push — no staged changes.",
"de": "Skipping commit push — no staged changes.",
"en": "Skipping commit push — no staged changes.",
"pl": "Pomijanie wypchnięcia commit — brak zmian w staging.",
"ru": "Skipping commit push — no staged changes.",
"zh": "Skipping commit push — no staged changes."
},
@@ -955,6 +1083,7 @@
"bg": "Syncing {count} documentation pages to wiki...",
"de": "Syncing {count} documentation pages to wiki...",
"en": "Syncing {count} documentation pages to wiki...",
"pl": "Synchronizowanie {count} stron dokumentacji do wiki...",
"ru": "Syncing {count} documentation pages to wiki...",
"zh": "Syncing {count} documentation pages to wiki..."
},
@@ -962,6 +1091,7 @@
"bg": "Tag consistency check failed.",
"de": "Tag consistency check failed.",
"en": "Tag consistency check failed.",
"pl": "Kontrola zgodności tagów nie powiodła się.",
"ru": "Tag consistency check failed.",
"zh": "Tag consistency check failed."
},
@@ -969,6 +1099,7 @@
"bg": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"de": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"en": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"pl": "Tag v{version} już istniał. Workflow publikacji powinien już być uruchomiony.",
"ru": "Tag v{version} already existed. Publish workflow should already have been triggered.",
"zh": "Tag v{version} already existed. Publish workflow should already have been triggered."
},
@@ -976,6 +1107,7 @@
"bg": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"de": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"en": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"pl": "Tag {tag} już istnieje i wskazuje na HEAD. Pomijanie tworzenia.",
"ru": "Tag {tag} already exists and points to HEAD. Skipping creation.",
"zh": "Tag {tag} already exists and points to HEAD. Skipping creation."
},
@@ -983,6 +1115,7 @@
"bg": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"de": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"en": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"pl": "Tag {tag} już istnieje ale wskazuje na {tag_commit} (oczekiwano HEAD {head_commit}). Wskazuje to na niezgodność tag/commit. Uruchom 'python3 -m devx.ci.release --verify', aby uzyskać szczegóły.",
"ru": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details.",
"zh": "Tag {tag} already exists but points to {tag_commit} (expected HEAD {head_commit}). This indicates a tag/commit misalignment. Run 'python3 -m devx.ci.release --verify' for details."
},
@@ -990,6 +1123,7 @@
"bg": "Task ID: {task_id}",
"de": "Task ID: {task_id}",
"en": "Task ID: {task_id}",
"pl": "ID zadania: {task_id}",
"ru": "Task ID: {task_id}",
"zh": "Task ID: {task_id}"
},
@@ -997,6 +1131,7 @@
"bg": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"de": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"en": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"pl": "Test '{name}' trwał {elapsed:.2f}s (limit: {limit}s). Optymalizuj: użyj lżejszych fixtures, zmniejsz I/O, lub mockuj zewnętrzne wywołania.",
"ru": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"zh": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls."
},
@@ -1004,6 +1139,7 @@
"bg": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"de": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"en": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"pl": "Testy nie powiodły się — odmowa wydania. Najpierw napraw niepowodzenia testów.\n{stderr}",
"ru": "Tests failed — refusing to release. Fix test failures first.\n{stderr}",
"zh": "Tests failed — refusing to release. Fix test failures first.\n{stderr}"
},
@@ -1011,6 +1147,7 @@
"bg": "Tests passed.",
"de": "Tests passed.",
"en": "Tests passed.",
"pl": "Testy zakończone pomyślnie.",
"ru": "Tests passed.",
"zh": "Tests passed."
},
@@ -1018,6 +1155,7 @@
"bg": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"de": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"en": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"pl": "Testy jednostkowe zakończone pomyślnie w {duration:.2f}s (poniżej limitu {max}s, wszystkie testy poniżej limitu {single}s na test).",
"ru": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit).",
"zh": "Unit tests passed in {duration:.2f}s (under {max}s limit, all tests under {single}s per-test limit)."
},
@@ -1025,6 +1163,7 @@
"bg": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"de": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"en": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"pl": "Testy jednostkowe zbyt wolne: {duration:.2f}s (maks. dozwolone: {max}s).\n Naprawa: uruchom 'make pytest-cov' do profilowania, następnie zoptymalizuj wolne testy.\n Wskazówka: unikaj niepotrzebnych importów, użyj lżejszych mocków, lub buforuj fixtures.",
"ru": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
"zh": "Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures."
},
@@ -1032,6 +1171,7 @@
"bg": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"de": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"en": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"pl": "Nieznana kategoria kontroli '{check}'. Dostępne: all, user-facing{tags}",
"ru": "Unknown check category '{check}'. Available: all, user-facing{tags}",
"zh": "Unknown check category '{check}'. Available: all, user-facing{tags}"
},
@@ -1039,6 +1179,7 @@
"bg": "Updated version in {init}",
"de": "Updated version in {init}",
"en": "Updated version in {init}",
"pl": "Zaktualizowano wersję w {init}",
"ru": "Updated version in {init}",
"zh": "Updated version in {init}"
},
@@ -1046,6 +1187,7 @@
"bg": "Updated {changelog_file}",
"de": "Updated {changelog_file}",
"en": "Updated {changelog_file}",
"pl": "Zaktualizowano {changelog_file}",
"ru": "Updated {changelog_file}",
"zh": "Updated {changelog_file}"
},
@@ -1053,6 +1195,7 @@
"bg": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"de": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"en": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"pl": "VIKUNJA_TOKEN nie jest ustawiony. Jest to wymagane w CI do walidacji tytułów PR.",
"ru": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles.",
"zh": "VIKUNJA_TOKEN is not set. This is required in CI to validate PR titles."
},
@@ -1060,6 +1203,7 @@
"bg": "Version file: {file}",
"de": "Version file: {file}",
"en": "Version file: {file}",
"pl": "Plik wersji: {file}",
"ru": "Version file: {file}",
"zh": "Version file: {file}"
},
@@ -1067,6 +1211,7 @@
"bg": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"de": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"en": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"pl": "Błąd API Vikunja (HTTP {status}): {message}. Zadanie {task_id} NIE zostało zaktualizowane. Scalenie powiodło się ale zadanie Vikunja wymaga ręcznej aktualizacji.",
"ru": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update.",
"zh": "Vikunja API error (HTTP {status}): {message}. Task {task_id} was NOT updated. The merge succeeded but the Vikunja task needs manual update."
},
@@ -1074,13 +1219,23 @@
"bg": "WARNING: --skip-tests passed — skipping test verification.",
"de": "WARNING: --skip-tests passed — skipping test verification.",
"en": "WARNING: --skip-tests passed — skipping test verification.",
"pl": "OSTRZEŻENIE: --skip-tests przekazane — pomijanie weryfikacji testów.",
"ru": "WARNING: --skip-tests passed — skipping test verification.",
"zh": "WARNING: --skip-tests passed — skipping test verification."
},
"WARNING: .taskid file ({file_id}) is deprecated and disagrees with branch name ({branch_id}). Delete .taskid from the repo — branch name is the sole source of truth.": {
"bg": "ВНИМАНИЕ: Файлът .taskid ({file_id}) е остарял и не съвпада с името на клона ({branch_id}). Изтрийте .taskid от хранилището — името на клона е единственият източник на истината.",
"de": "WARNUNG: Die Datei .taskid ({file_id}) ist veraltet und stimmt nicht mit dem Branch-Namen ({branch_id}) überein. Löschen Sie .taskid aus dem Repo — der Branch-Name ist die einzige Wahrheitsquelle.",
"en": "WARNING: .taskid file ({file_id}) is deprecated and disagrees with branch name ({branch_id}). Delete .taskid from the repo — branch name is the sole source of truth.",
"pl": "OSTRZEŻENIE: plik .taskid ({file_id}) jest przestarzały i niezgodny z nazwą gałęzi ({branch_id}). Usuń .taskid z repozytorium — nazwa gałęzi jest jedynym źródłem prawdy.",
"ru": "ВНИМАНИЕ: Файл .taskid ({file_id}) устарел и не совпадает с именем ветки ({branch_id}). Удалите .taskid из репозитория — имя ветки — единственный источник истины.",
"zh": "警告:.taskid 文件 ({file_id}) 已弃用,与分支名称 ({branch_id}) 不一致。请从仓库中删除 .taskid — 分支名称是唯一的真实来源。"
},
"Warning: could not fetch tags from origin.": {
"bg": "Warning: could not fetch tags from origin.",
"de": "Warning: could not fetch tags from origin.",
"en": "Warning: could not fetch tags from origin.",
"pl": "Ostrzeżenie: nie udało się pobrać tagów z origin.",
"ru": "Warning: could not fetch tags from origin.",
"zh": "Warning: could not fetch tags from origin."
},
@@ -1088,6 +1243,7 @@
"bg": "Wiki integrity check failed — {count} issue(s)",
"de": "Wiki integrity check failed — {count} issue(s)",
"en": "Wiki integrity check failed — {count} issue(s)",
"pl": "Kontrola integralności wiki nie powiodła się — {count} problem(ów)",
"ru": "Wiki integrity check failed — {count} issue(s)",
"zh": "Wiki integrity check failed — {count} issue(s)"
},
@@ -1095,6 +1251,7 @@
"bg": "Wiki verification failed — {failures} page(s) empty or mismatched",
"de": "Wiki verification failed — {failures} page(s) empty or mismatched",
"en": "Wiki verification failed — {failures} page(s) empty or mismatched",
"pl": "Weryfikacja wiki nie powiodła się — {failures} strona(y) pusta lub niezgodna",
"ru": "Wiki verification failed — {failures} page(s) empty or mismatched",
"zh": "Wiki verification failed — {failures} page(s) empty or mismatched"
},
@@ -1102,6 +1259,7 @@
"bg": "[dry-run] Would commit: release: v{version}",
"de": "[dry-run] Would commit: release: v{version}",
"en": "[dry-run] Would commit: release: v{version}",
"pl": "[dry-run] Utworzono by commit: release: v{version}",
"ru": "[dry-run] Would commit: release: v{version}",
"zh": "[dry-run] Would commit: release: v{version}"
},
@@ -1109,6 +1267,7 @@
"bg": "[dry-run] Would create tag: v{version}",
"de": "[dry-run] Would create tag: v{version}",
"en": "[dry-run] Would create tag: v{version}",
"pl": "[dry-run] Utworzono by tag: v{version}",
"ru": "[dry-run] Would create tag: v{version}",
"zh": "[dry-run] Would create tag: v{version}"
},
@@ -1116,6 +1275,7 @@
"bg": "[dry-run] Would create tag: {tag}",
"de": "[dry-run] Would create tag: {tag}",
"en": "[dry-run] Would create tag: {tag}",
"pl": "[dry-run] Utworzono by tag: {tag}",
"ru": "[dry-run] Would create tag: {tag}",
"zh": "[dry-run] Would create tag: {tag}"
},
@@ -1123,6 +1283,7 @@
"bg": "[dry-run] Would push commit to master",
"de": "[dry-run] Would push commit to master",
"en": "[dry-run] Would push commit to master",
"pl": "[dry-run] Wypchnięto by commit do master",
"ru": "[dry-run] Would push commit to master",
"zh": "[dry-run] Would push commit to master"
},
@@ -1130,6 +1291,7 @@
"bg": "[dry-run] Would sync page: {title} ({chars} chars)",
"de": "[dry-run] Would sync page: {title} ({chars} chars)",
"en": "[dry-run] Would sync page: {title} ({chars} chars)",
"pl": "[dry-run] Zsynchronizowano by stronę: {title} ({chars} znaków)",
"ru": "[dry-run] Would sync page: {title} ({chars} chars)",
"zh": "[dry-run] Would sync page: {title} ({chars} chars)"
},
@@ -1137,6 +1299,7 @@
"bg": "[dry-run] Would update {changelog_file}",
"de": "[dry-run] Would update {changelog_file}",
"en": "[dry-run] Would update {changelog_file}",
"pl": "[dry-run] Zaktualizowano by {changelog_file}",
"ru": "[dry-run] Would update {changelog_file}",
"zh": "[dry-run] Would update {changelog_file}"
},
@@ -1144,6 +1307,7 @@
"bg": "[dry-run] Would update {init}",
"de": "[dry-run] Would update {init}",
"en": "[dry-run] Would update {init}",
"pl": "[dry-run] Zaktualizowano by {init}",
"ru": "[dry-run] Would update {init}",
"zh": "[dry-run] Would update {init}"
},
@@ -1151,6 +1315,7 @@
"bg": "активен",
"de": "aktiv",
"en": "active",
"pl": "aktywny",
"ru": "активен",
"zh": "活跃"
},
@@ -1158,6 +1323,7 @@
"bg": "завършен",
"de": "abgeschlossen",
"en": "completed",
"pl": "ukończony",
"ru": "завершён",
"zh": "已完成"
},
@@ -1165,6 +1331,7 @@
"bg": "неуспешен",
"de": "fehlgeschlagen",
"en": "failed",
"pl": "nieudany",
"ru": "неудачный",
"zh": "失败"
},
@@ -1172,6 +1339,7 @@
"bg": "git command failed ({cmd}): {stderr}",
"de": "git command failed ({cmd}): {stderr}",
"en": "git command failed ({cmd}): {stderr}",
"pl": "polecenie git nie powiodło się ({cmd}): {stderr}",
"ru": "git command failed ({cmd}): {stderr}",
"zh": "git command failed ({cmd}): {stderr}"
},
@@ -1179,6 +1347,7 @@
"bg": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"de": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"en": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"pl": "git-cliff wygenerował pusty changelog dla v{version}. Sprawdź cliff.toml i historię commitów.",
"ru": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history.",
"zh": "git-cliff generated empty changelog for v{version}. Check cliff.toml and commit history."
},
@@ -1186,6 +1355,7 @@
"bg": "git-cliff returned empty version.",
"de": "git-cliff returned empty version.",
"en": "git-cliff returned empty version.",
"pl": "git-cliff zwrócił pustą wersję.",
"ru": "git-cliff returned empty version.",
"zh": "git-cliff returned empty version."
},
@@ -1193,6 +1363,7 @@
"bg": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"de": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"en": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"pl": "git-cliff zwrócił nieprawidłowy format wersji: {version}. Oczekiwano semver (np., 0.4.1).",
"ru": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1).",
"zh": "git-cliff returned invalid version format: {version}. Expected semver (e.g., 0.4.1)."
},
@@ -1200,6 +1371,7 @@
"bg": "в процес",
"de": "in Bearbeitung",
"en": "in progress",
"pl": "w toku",
"ru": "в процессе",
"zh": "进行中"
},
@@ -1207,6 +1379,7 @@
"bg": "неактивен",
"de": "inaktiv",
"en": "inactive",
"pl": "nieaktywny",
"ru": "неактивен",
"zh": "未激活"
},
@@ -1214,6 +1387,7 @@
"bg": "mapping.json keys and values must be strings, got {k}={v}",
"de": "mapping.json keys and values must be strings, got {k}={v}",
"en": "mapping.json keys and values must be strings, got {k}={v}",
"pl": "klucze i wartości mapping.json muszą być ciągami znaków, otrzymano {k}={v}",
"ru": "mapping.json keys and values must be strings, got {k}={v}",
"zh": "mapping.json keys and values must be strings, got {k}={v}"
},
@@ -1221,6 +1395,7 @@
"bg": "mapping.json must be a dict of file-path -> page-title, got {type}",
"de": "mapping.json must be a dict of file-path -> page-title, got {type}",
"en": "mapping.json must be a dict of file-path -> page-title, got {type}",
"pl": "mapping.json musi być słownikiem ścieżka-pliku -> tytuł-strony, otrzymano {type}",
"ru": "mapping.json must be a dict of file-path -> page-title, got {type}",
"zh": "mapping.json must be a dict of file-path -> page-title, got {type}"
},
@@ -1228,6 +1403,7 @@
"bg": "в очакване",
"de": "ausstehend",
"en": "pending",
"pl": "oczekujący",
"ru": "ожидает",
"zh": "待处理"
},
@@ -1235,6 +1411,7 @@
"bg": "неизвестен",
"de": "unbekannt",
"en": "unknown",
"pl": "nieznany",
"ru": "неизвестно",
"zh": "未知"
},
@@ -1242,21 +1419,8 @@
"bg": "{file} already exists. Use --force to overwrite.",
"de": "{file} already exists. Use --force to overwrite.",
"en": "{file} already exists. Use --force to overwrite.",
"pl": "{file} już istnieje. Użyj --force, aby nadpisać.",
"ru": "{file} already exists. Use --force to overwrite.",
"zh": "{file} already exists. Use --force to overwrite."
},
"Oops! No task ID found in branch name '{branch}'. Branch names must include the task ID prefix (e.g., DEVX-31-fix-bug).": {
"en": "Oops! No task ID found in branch name '{branch}'. Branch names must include the task ID prefix (e.g., DEVX-31-fix-bug).",
"bg": "Ой! Не е намерен ID на задача в името на клона '{branch}'. Имената на клонове трябва да включват префикса за ID на задача (напр. DEVX-31-fix-bug).",
"de": "Hoppla! Keine Task-ID im Branch-Namen '{branch}' gefunden. Branch-Namen müssen das Task-ID-Präfix enthalten (z.B. DEVX-31-fix-bug).",
"ru": "Ой! ID задачи не найден в имени ветки '{branch}'. Имена веток должны включать префикс ID задачи (например, DEVX-31-fix-bug).",
"zh": "哎呀!在分支名称 '{branch}' 中未找到任务 ID。分支名称必须包含任务 ID 前缀(例如 DEVX-31-fix-bug)。"
},
"WARNING: .taskid file ({file_id}) is deprecated and disagrees with branch name ({branch_id}). Delete .taskid from the repo — branch name is the sole source of truth.": {
"en": "WARNING: .taskid file ({file_id}) is deprecated and disagrees with branch name ({branch_id}). Delete .taskid from the repo — branch name is the sole source of truth.",
"bg": "ВНИМАНИЕ: Файлът .taskid ({file_id}) е остарял и не съвпада с името на клона ({branch_id}). Изтрийте .taskid от хранилището — името на клона е единственият източник на истината.",
"de": "WARNUNG: Die Datei .taskid ({file_id}) ist veraltet und stimmt nicht mit dem Branch-Namen ({branch_id}) überein. Löschen Sie .taskid aus dem Repo — der Branch-Name ist die einzige Wahrheitsquelle.",
"ru": "ВНИМАНИЕ: Файл .taskid ({file_id}) устарел и не совпадает с именем ветки ({branch_id}). Удалите .taskid из репозитория — имя ветки — единственный источник истины.",
"zh": "警告:.taskid 文件 ({file_id}) 已弃用,与分支名称 ({branch_id}) 不一致。请从仓库中删除 .taskid — 分支名称是唯一的真实来源。"
}
}
+22 -19
View File
@@ -253,31 +253,34 @@ class TestMain:
@patch.dict("os.environ", {"REPO_TOKEN": "tok", "VIKUNJA_TOKEN": "tok"}, clear=True)
@patch("devx.ci.auto_merge.validate_pr_title_matches_vikunja")
@patch("devx.ci.auto_merge.GiteaClient")
def test_merge_behind_master_rebases(
def test_merge_behind_master_raises_no_rebase(
self, mock_client_cls: MagicMock, _mock_validate: MagicMock, tmp_path, monkeypatch
) -> None: # type: ignore[no-untyped-def]
"""When branch is behind master, auto-merge should NOT rebase.
Auto-rebasing creates a feedback loop: the force-push triggers a new
pull_request synchronize event, which starts a new CI run, which runs
auto-merge again, which rebases again, etc.
"""
monkeypatch.chdir(tmp_path)
mock_client = MagicMock()
mock_client.get_pr_commits.return_value = [
{"commit": {"message": "fix: resolve timeout"}},
]
mock_client.merge_pr.side_effect = [
APIError(405, "HEAD branch is behind master"),
None, # Second call succeeds
]
mock_client.merge_pr.side_effect = APIError(405, "HEAD branch is behind master")
mock_client_cls.return_value = mock_client
with patch("devx.ci.auto_merge.run_cmd") as mock_run:
runner = CliRunner()
result = runner.invoke(
main,
["DEVX-19-fix-bug", "DEVX-19: Fix timeout", "owner/repo", "7"],
)
assert result.exit_code == 0, result.output
assert mock_client.merge_pr.call_count == 2
# Should have fetched, rebased, and pushed
assert mock_run.call_count == 5 # config name, config email, fetch, rebase, push
runner = CliRunner()
result = runner.invoke(
main,
["DEVX-19-fix-bug", "DEVX-19: Fix timeout", "owner/repo", "7"],
)
assert result.exit_code != 0
assert "behind master" in result.output.lower()
assert "rebase manually" in result.output.lower()
# Must NOT have called merge_pr twice (no retry after rebase)
assert mock_client.merge_pr.call_count == 1
@patch.dict("os.environ", {"REPO_TOKEN": "tok", "VIKUNJA_TOKEN": "tok"}, clear=True)
@patch("devx.ci.auto_merge.validate_pr_title_matches_vikunja")
@@ -344,10 +347,10 @@ class TestMain:
@patch.dict("os.environ", {"REPO_TOKEN": "tok", "VIKUNJA_TOKEN": "tok"}, clear=True)
@patch("devx.ci.auto_merge.validate_pr_title_matches_vikunja")
@patch("devx.ci.auto_merge.GiteaClient")
def test_rebase_retry_failure_raises(
def test_merge_behind_master_does_not_force_push(
self, mock_client_cls: MagicMock, _mock_validate: MagicMock, tmp_path, monkeypatch
) -> None: # type: ignore[no-untyped-def]
"""When rebase retry also fails, raises with helpful message."""
"""Verify no git commands are run when branch is behind master."""
monkeypatch.chdir(tmp_path)
mock_client = MagicMock()
@@ -358,14 +361,14 @@ class TestMain:
mock_client_cls.return_value = mock_client
with patch("devx.ci.auto_merge.run_cmd") as mock_run:
mock_run.side_effect = click.ClickException("git rebase failed")
runner = CliRunner()
result = runner.invoke(
main,
["DEVX-19-fix-bug", "DEVX-19: Fix timeout", "owner/repo", "7"],
)
assert result.exit_code != 0
assert "rebase" in result.output.lower()
# No git commands should be run (no rebase, no push)
mock_run.assert_not_called()
def test_main_module_block() -> None:
+29 -11
View File
@@ -41,9 +41,12 @@ class TestCheckTranslationSet:
src_dir.mkdir()
(src_dir / "mod.py").write_text('_("Hello")\n')
trans_file = tmp_path / "translations.json"
trans_file.write_text(
json.dumps({"Hello": {"en": "Hello", "bg": "Здравей", "de": "Hallo", "ru": "Привет", "zh": "你好"}})
)
all_langs = {"en": "Hello", "bg": "Здравей", "de": "Hallo", "ru": "Привет", "zh": "你好", "pl": "Cześć"}
# Include dynamic keys since collect_keys now adds them for all dirs
data = {"Hello": all_langs}
for dk in check_translations.DYNAMIC_KEYS:
data[dk] = all_langs
trans_file.write_text(json.dumps(data))
result = check_translations.check_translation_set("test", src_dir, trans_file)
assert not result.errors
@@ -170,9 +173,11 @@ class TestMain:
def test_translations_flag(self, tmp_path: Path) -> None:
"""--translations flag should check a specific file."""
trans_file = tmp_path / "translations.json"
trans_file.write_text(
json.dumps({"Hello": {"en": "Hello", "bg": "Здравей", "de": "Hallo", "ru": "Привет", "zh": "你好"}})
)
all_langs = {"en": "Hello", "bg": "Здравей", "de": "Hallo", "ru": "Привет", "zh": "你好", "pl": "Cześć"}
data = {"Hello": all_langs}
for dk in check_translations.DYNAMIC_KEYS:
data[dk] = all_langs
trans_file.write_text(json.dumps(data))
(tmp_path / "mod.py").write_text('_("Hello")\n')
runner = CliRunner()
@@ -251,6 +256,19 @@ class TestDevxI18n:
monkeypatch.delenv("DEVX_LANG", raising=False)
importlib.reload(devx.i18n)
def test_polish_translation(self, monkeypatch: pytest.MonkeyPatch) -> None:
import importlib
monkeypatch.setenv("DEVX_LANG", "pl")
import devx.i18n
importlib.reload(devx.i18n)
result = devx.i18n._("Running tests...")
assert "Uruchamianie testów" in result
monkeypatch.delenv("DEVX_LANG", raising=False)
importlib.reload(devx.i18n)
def test_unsupported_lang_fallback(self, monkeypatch: pytest.MonkeyPatch) -> None:
import importlib
@@ -290,14 +308,14 @@ class TestCollectKeys:
assert "should_appear" in keys
assert "should_not_appear" not in keys
def test_non_default_dir_no_dynamic_keys(self, tmp_path: Path) -> None:
"""Non-default source dirs should not include DYNAMIC_KEYS."""
def test_non_default_dir_includes_dynamic_keys(self, tmp_path: Path) -> None:
"""Non-default source dirs should also include DYNAMIC_KEYS."""
(tmp_path / "mod.py").write_text('_("mykey")\n')
keys = check_translations.collect_keys(tmp_path)
assert "mykey" in keys
# Dynamic keys should NOT be present for non-default dirs
assert "completed" not in keys
assert "pending" not in keys
# Dynamic keys should be present for all dirs
assert "completed" in keys
assert "pending" in keys
def test_default_dir_includes_dynamic_keys(self) -> None:
"""The default source dir should include DYNAMIC_KEYS."""
+17
View File
@@ -101,6 +101,23 @@ class TestCli:
assert "ASSIGNED_FILES=" in content
assert "SKIP=false" in content
def test_github_env_multiline_uses_heredoc(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
gh_file = tmp_path / "env.txt"
monkeypatch.setenv("GITHUB_ENV", str(gh_file))
for i in range(6):
(tmp_path / f"test_{i}.py").write_text("")
runner = CliRunner()
result = runner.invoke(
main,
["--pattern", str(tmp_path / "test_*.py"), "--runner-index", "1", "--max-runners", "2", "--github-env"],
)
assert result.exit_code == 0
content = gh_file.read_text()
# Multi-line values must use heredoc syntax to avoid corrupting $GITHUB_ENV
assert "ASSIGNED_FILES<<EOF" in content
assert content.count("EOF") >= 2
assert "SKIP=false" in content
def test_skip_if_excess(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
gh_file = tmp_path / "env.txt"
monkeypatch.setenv("GITHUB_ENV", str(gh_file))