Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions profiles/bisq-mobile/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ semantic_review:
reasoning_effort: "none"
auto_apply_error_suggestions: true
semantic_quality_rules:
- id: "it-trade-failure-terminology"
message: "Use 'scambio' for 'trade' in these reviewed Italian failure messages."
locales: ["it"]
keys:
- "bisqEasy.openTrades.failed"
- "bisqEasy.openTrades.failed.popup"
- "bisqEasy.openTrades.failedAtPeer"
- "bisqEasy.openTrades.failedAtPeer.popup"
source_regex: '\btrade\b'
required_target_regex: '\bscambio\b'
regex_ignorecase: true
severity: "error"
source: "bisq-mobile#1913"
- id: "trade-history-traders-label"
message: "Fully localize the Trade Details traders/role label; do not retain the English term 'Traders'."
locales: ["*"]
Expand Down
13 changes: 13 additions & 0 deletions profiles/bisq/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ semantic_review:
reasoning_effort: "none"
auto_apply_error_suggestions: true
semantic_quality_rules:
- id: "it-trade-failure-terminology"
message: "Use 'scambio' for 'trade' in these reviewed Italian failure messages."
locales: ["it"]
keys:
- "bisqEasy.openTrades.failed"
- "bisqEasy.openTrades.failed.popup"
- "bisqEasy.openTrades.failedAtPeer"
- "bisqEasy.openTrades.failedAtPeer.popup"
source_regex: '\btrade\b'
required_target_regex: '\bscambio\b'
Comment thread
hiciefte marked this conversation as resolved.
regex_ignorecase: true
severity: "error"
source: "bisq-mobile#1913"
- id: "trade-history-traders-label"
message: "Fully localize the Trade Details traders/role label; do not retain the English term 'Traders'."
locales: ["*"]
Expand Down
44 changes: 44 additions & 0 deletions tests/fixtures/semantic_rules/italian_trade_failures.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"provenance": "https://github.com/bisq-network/bisq-mobile/pull/1913",
"original_revision": "1f3539de70815bf2da48606f42f28e69d6406ec4",
"corrected_revision": "362e05c99222cfc0c4048d928b0c1a7e85e28344",
"note": "Source is unchanged by the PR. Original targets are from its parent; corrected targets and valid verb controls are from its head.",
"failures": [
{
"key": "bisqEasy.openTrades.failed",
"source": "The trade failed: ''{0}''",
"original": "La transazione è fallita: ''{0}''",
"corrected": "Lo scambio è fallito: ''{0}''"
},
{
"key": "bisqEasy.openTrades.failed.popup",
"source": "The trade failed: ''{0}''\\n\\nStack trace: {1}",
"original": "La transazione è fallita: ''{0}''\\n\\nTrace dello stack: {1}",
"corrected": "Lo scambio è fallito: ''{0}''\\n\\nTrace dello stack: {1}"
},
{
"key": "bisqEasy.openTrades.failedAtPeer",
"source": "The trade failed at your peer: ''{0}''",
"original": "La transazione è fallita presso il tuo peer: ''{0}''",
"corrected": "Lo scambio è fallito presso il tuo peer: ''{0}''"
},
{
"key": "bisqEasy.openTrades.failedAtPeer.popup",
"source": "The trade failed at your peer: ''{0}''\\n\\nStack trace: {1}",
"original": "La transazione è fallita presso il tuo peer: ''{0}''\\n\\nTraccia dello stack: {1}",
"corrected": "Lo scambio è fallito presso il tuo peer: ''{0}''\\n\\nTraccia dello stack: {1}"
}
],
"valid_verbs": [
{
"key": "bisqEasy.tradeWizard.amount.description.fixed",
"source": "{0} amount to trade",
"target": "{0} importo da scambiare"
},
{
"key": "bisqEasy.tradeWizard.amount.description.range",
"source": "Range of {0} amount to trade",
"target": "Intervallo di {0} importo da scambiare"
}
]
}
87 changes: 87 additions & 0 deletions tests/integration/test_italian_trade_failure_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""Replay reviewed Italian trade terminology without requiring every inflection."""

import json
import subprocess
from pathlib import Path

import pytest

from localize.semantic_quality import TranslationChange, evaluate_semantic_rules
from localize.translation_quality_gate import load_quality_gate_config, main

PROJECT_ROOT = Path(__file__).parents[2]
FIXTURE = json.loads(
(PROJECT_ROOT / "tests/fixtures/semantic_rules/italian_trade_failures.json").read_text(encoding="utf-8")
)
RULE_ID = "it-trade-failure-terminology"


def _write_properties(path, values):
"""Write the exact reviewed property values, including their placeholders."""
path.write_text("".join(f"{key}={value}\n" for key, value in values.items()), encoding="utf-8")


@pytest.mark.parametrize("profile", ["bisq", "bisq-mobile"])
@pytest.mark.parametrize("scope", ["changed", "all"])
def test_reviewed_italian_failures_block_and_corrections_pass(tmp_path, profile, scope):
"""Both profiles block four real defects and accept their reviewed corrections."""
resources = tmp_path / "resources"
resources.mkdir()
source = {item["key"]: item["source"] for item in FIXTURE["failures"] + FIXTURE["valid_verbs"]}
_write_properties(resources / "bisq_easy.properties", source)
target = resources / "bisq_easy_it.properties"
target.touch()

def git(*args):
"""Exercise the real staged-diff reader without modifying a live repository."""
subprocess.run(["git", *args], cwd=tmp_path, check=True, capture_output=True)

git("init", "-q")
git("add", "resources")
git("-c", "user.name=Test", "-c", "user.email=test@example.invalid",
"-c", "commit.gpgsign=false", "commit", "-qm", "Seed locale fixture")
verbs = {item["key"]: item["target"] for item in FIXTURE["valid_verbs"]}
original = {item["key"]: item["original"] for item in FIXTURE["failures"]}
_write_properties(target, {**original, **verbs})
git("add", "resources")
report_path = tmp_path / "report.json"
args = [
"--repo-root", str(tmp_path), "--input-folder", str(resources),
"--config", str(PROJECT_ROOT / "profiles" / profile / "config.yaml"),
"--validation-summary", str(tmp_path / "missing-summary.json"),
"--output-json", str(report_path),
"--output-markdown", str(tmp_path / "report.md"),
"--audit-scope", scope,
"--changed-files", "resources/bisq_easy_it.properties",
]
assert main(args) == 1
report = json.loads(report_path.read_text(encoding="utf-8"))
assert report["semantic_qa"]["errors_count"] == 4
assert {item["key"] for item in report["semantic_qa"]["examples"]} == set(original)
assert {item["rule_id"] for item in report["semantic_qa"]["examples"]} == {RULE_ID}
corrected = {item["key"]: item["corrected"] for item in FIXTURE["failures"]}
_write_properties(target, {**corrected, **verbs})
git("add", "resources")
assert main(args) == 0
report = json.loads(report_path.read_text(encoding="utf-8"))
assert report["semantic_qa"]["errors_count"] == 0


@pytest.mark.parametrize("profile", ["bisq", "bisq-mobile"])
def test_rule_keeps_source_key_and_locale_boundaries(profile):
"""Transactions, other locales and unreviewed keys remain outside this policy."""
_, _, _, rules = load_quality_gate_config(str(PROJECT_ROOT / "profiles" / profile / "config.yaml"))
scoped_rules = [rule for rule in rules if rule.id == RULE_ID]
assert len(scoped_rules) == 1
changes = []
for item in FIXTURE["failures"]:
for key, locale, source in [
(item["key"], "it", item["source"].replace("trade", "transaction")),
(item["key"], "fr", item["source"]),
(item["key"] + ".unreviewed", "it", item["source"]),
]:
changes.append(TranslationChange(
file=f"bisq_easy_{locale}.properties", locale_code=locale, key=key,
source_value=source, old_value=None, new_value=item["original"],
))
assert evaluate_semantic_rules(changes, scoped_rules) == []
Loading