Skip to content

Add translation completeness test for Satellite locales - #22334

Open
pnovotny wants to merge 3 commits into
SatelliteQE:masterfrom
pnovotny:translations
Open

Add translation completeness test for Satellite locales#22334
pnovotny wants to merge 3 commits into
SatelliteQE:masterfrom
pnovotny:translations

Conversation

@pnovotny

@pnovotny pnovotny commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Problem Statement

There was no automated test to verify that Satellite's translation files (.po) are complete — untranslated messages and missing template strings could ship undetected.

Solution

  • Add test_positive_check_missing_translations parametrized over supported languages (fr, ja, ko, zh_CN) that checks for untranslated messages via msgfmt/msgattrib and compares .po files against .pot templates via msgcmp
  • Add xfail handling for languages with known open translation issues (SAT-48297, SAT-48299, SAT-48300, SAT-48301)
  • Add new test file to CODEOWNERS

Related Issues

https://redhat.atlassian.net/browse/SAT-32747

PRT test Cases example

trigger: test-robottelo
pytest: tests/foreman/cli/test_translation.py -k test_positive_check_missing_translations

Summary by Sourcery

Add automated CLI test to ensure Satellite translation files are fully translated and complete relative to their templates for selected locales.

Enhancements:

  • Expose SUPPORTED_LANGUAGES constant for reuse in translation-related tests.

Tests:

  • Add parametrized translation completeness test over supported locales (fr, ja, ko, zh_CN) using msgfmt/msgattrib/msgcmp to detect untranslated or missing strings.
  • Introduce expected-fail handling for locales with known open translation issues to avoid breaking the suite while tracking gaps.

Chores:

  • Add ownership of the new translation test file in CODEOWNERS.

New test `test_positive_check_missing_translations` for untranslated messages and missing template strings.
Supported languages: fr, ja, ko, zh_CN
@pnovotny
pnovotny requested a review from a team as a code owner August 3, 2026 12:19
@pnovotny pnovotny added the Stream Introduced in or relating directly to Satellite Stream/Master label Aug 3, 2026
@pnovotny pnovotny self-assigned this Aug 3, 2026
@pnovotny
pnovotny requested a review from a team August 3, 2026 12:19
@sourcery-ai

sourcery-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds a parametrized pytest that enforces translation completeness for selected Satellite locales using gettext utilities, introduces a SUPPORTED_LANGUAGES constant for those locales, and wires the new test file into CODEOWNERS, with xfail handling for known open translation issues.

Sequence diagram for translation completeness pytest

sequenceDiagram
    actor Developer
    participant Pytest
    participant test_translation
    participant GettextTools

    Developer->>Pytest: run tests with test_positive_check_missing_translations
    Pytest->>test_translation: test_positive_check_missing_translations(SUPPORTED_LANGUAGES)
    loop for each language in SUPPORTED_LANGUAGES
        test_translation->>GettextTools: msgfmt
        test_translation->>GettextTools: msgattrib
        test_translation->>GettextTools: msgcmp
        alt [known translation issue]
            test_translation->>Pytest: pytest.xfail
        else [no known issue]
            Pytest-->>Developer: assert no missing or untranslated strings
        end
    end
Loading

File-Level Changes

Change Details Files
Introduce SUPPORTED_LANGUAGES constant used to parameterize translation completeness tests over specific locales.
  • Define SUPPORTED_LANGUAGES tuple containing ('fr', 'ja', 'ko', 'zh_CN').
  • Export SUPPORTED_LANGUAGES from robottelo.constants for reuse in tests.
robottelo/constants/__init__.py
Add CLI translation completeness test that validates .po files are fully translated and complete against their .pot templates, with conditional xfail for known open issues.
  • Create tests/foreman/cli/test_translation.py containing a parametrized pytest over SUPPORTED_LANGUAGES using a fixture.
  • Implement filesystem discovery of locale directories and .po/.pot files on the target Satellite using find and shell commands.
  • Use msgfmt/msgattrib to detect untranslated messages and build a structured untranslated_report for failing files.
  • Use msgcmp combined with sed and regex parsing to detect missing translations relative to .pot templates and aggregate template_failures.
  • Aggregate errors into human-readable messages and either xfail when a known SAT issue is still open for the language or assert to fail the test otherwise.
  • Integrate is_open issue handler logic to dynamically check Jira issue state per language.
tests/foreman/cli/test_translation.py
Ensure new translation test file has explicit ownership in CODEOWNERS.
  • Add tests/foreman/cli/test_translation.py to .github/CODEOWNERS with appropriate team ownership (exact entry not shown in diff).
.github/CODEOWNERS

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@pnovotny

pnovotny commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

trigger: test-robottelo
pytest: tests/foreman/cli/test_translation.py -k test_positive_check_missing_translations

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • The FIND_POT command relies on shell brace expansion ({katello,foreman,hammer_cli}), which may not be portable across different default shells; consider expanding this into explicit paths or building the list programmatically to avoid shell-specific behavior.
  • In the untranslated_report summary, the aggregated error message does not include the .po filenames, only statistics and messages; add the corresponding file path to each section so failures can be traced back to the specific translation file.
  • When mapping .pot files to related .po files, pot_name in po can overmatch (e.g., when one template name is a substring of another); consider matching against the .po basename stem instead of a substring to avoid incorrect associations.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `FIND_POT` command relies on shell brace expansion (`{katello,foreman,hammer_cli}`), which may not be portable across different default shells; consider expanding this into explicit paths or building the list programmatically to avoid shell-specific behavior.
- In the `untranslated_report` summary, the aggregated error message does not include the `.po` filenames, only statistics and messages; add the corresponding file path to each section so failures can be traced back to the specific translation file.
- When mapping `.pot` files to related `.po` files, `pot_name in po` can overmatch (e.g., when one template name is a substring of another); consider matching against the `.po` basename stem instead of a substring to avoid incorrect associations.

## Individual Comments

### Comment 1
<location path="tests/foreman/cli/test_translation.py" line_range="78-82" />
<code_context>
+            }
+
+    # Step 4: Compare .po files against their .pot templates
+    result = target_sat.execute(FIND_POT)
+    assert result.status == 0, f'Failed to search for .pot files: {result.stderr}'
+    pot_files = result.stdout.strip().splitlines()
+
+    template_failures = {}
</code_context>
<issue_to_address>
**suggestion (testing):** Consider asserting that at least one .pot template is found to fully match the documented expected results

The docstring states that all translation files are found on the Satellite, but this test only asserts that `find` succeeds and never verifies that any `.pot` files were actually discovered. If `FIND_POT` matches nothing, the template checks are silently skipped and the test can still pass. To ensure the test enforces its documented behavior and catches packaging/path issues, add an assertion that `pot_files` is non-empty (e.g. `assert pot_files, 'No .pot template files found on the Satellite'`).

```suggestion
    result = target_sat.execute(FIND_POT)
    assert result.status == 0, f'Failed to search for .pot files: {result.stderr}'
    pot_files = result.stdout.strip().splitlines()
    assert pot_files, 'No .pot template files found on the Satellite'

    template_failures = {}
```
</issue_to_address>

### Comment 2
<location path="tests/foreman/cli/test_translation.py" line_range="66-68" />
<code_context>
+
+    # Step 2 & 3: Check each .po file for untranslated messages
+    untranslated_report = {}
+    for po_file in po_files:
+        stats = target_sat.execute(f'msgfmt -v --statistics -o /dev/null {po_file}')
+        if 'untranslated' in stats.stderr:
+            details = target_sat.execute(f'msgattrib --untranslated --indent --no-wrap {po_file}')
+            untranslated_report[po_file] = {
</code_context>
<issue_to_address>
**issue (testing):** Add assertions on `msgfmt` execution status to avoid silently treating tool failures as translation issues

The loop only checks `stats.stderr` for `untranslated` and ignores `stats.status`, so a failed `msgfmt` run (missing binary, malformed `.po`, permission issues, etc.) could be misread as valid statistics or missed entirely. Please assert `stats.status == 0` before inspecting stderr, or add a separate failure branch that fails the test and surfaces `stats.stderr` when the status is non‑zero.
</issue_to_address>

### Comment 3
<location path="tests/foreman/cli/test_translation.py" line_range="88-90" />
<code_context>
+        pot_name = os.path.splitext(os.path.basename(pot_file))[0]
+
+        related_pos = [po for po in po_files if po.startswith(locale_dir) and pot_name in po]
+        for po_file in related_pos:
+            cmp_result = target_sat.execute(f'msgcmp {po_file} {pot_file}')
+            not_defined = []
+            for line in cmp_result.stderr.splitlines():
+                if f'not defined in {po_file}' in line:
</code_context>
<issue_to_address>
**issue (testing):** Check `msgcmp` return status to distinguish missing translations from tool or environment failures

`msgcmp` stderr is parsed to find missing translations, but `cmp_result.status` is never checked. If `msgcmp` fails (missing binary, bad input, etc.), stderr may not match the expected format and the test could silently misclassify or miss the failure. Please assert on `cmp_result.status` (e.g. `assert cmp_result.status == 0`) or otherwise handle non‑zero exit codes separately to keep failures from the comparison tool distinct and clearly reported.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread tests/foreman/cli/test_translation.py
Comment thread tests/foreman/cli/test_translation.py
Comment thread tests/foreman/cli/test_translation.py
@pnovotny pnovotny added the No-CherryPick PR doesnt need CherryPick to previous branches label Aug 3, 2026
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16313
Build Status: SUCCESS
PRT Comment: pytest tests/foreman/cli/test_translation.py -k test_positive_check_missing_translations --external-logging
Test Result : ================= 4 xfailed, 27 warnings in 955.10s (0:15:55) ==================

@Satellite-QE Satellite-QE added the PRT-Passed Indicates that latest PRT run is passed for the PR label Aug 3, 2026
@pnovotny

pnovotny commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

trigger: test-robottelo
pytest: tests/foreman/cli/test_translation.py -k test_positive_check_missing_translations

@pnovotny
pnovotny requested a review from a team August 3, 2026 12:50
@pnovotny pnovotny removed the PRT-Passed Indicates that latest PRT run is passed for the PR label Aug 3, 2026
@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16315
Build Status: UNSTABLE
PRT Comment: pytest tests/foreman/cli/test_translation.py -k test_positive_check_missing_translations --external-logging
Test Result : ================== 4 failed, 30 warnings in 956.20s (0:15:56) ==================

@Satellite-QE Satellite-QE added the PRT-Failed Indicates that latest PRT run is failed for the PR label Aug 3, 2026
The command returns RC 1 also for missing template strings, which we want to catch.
@pnovotny

pnovotny commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

trigger: test-robottelo
pytest: tests/foreman/cli/test_translation.py -k test_positive_check_missing_translations

@Satellite-QE

Copy link
Copy Markdown
Collaborator

PRT Result

Build Number: 16316
Build Status: SUCCESS
PRT Comment: pytest tests/foreman/cli/test_translation.py -k test_positive_check_missing_translations --external-logging
Test Result : ================= 4 xfailed, 27 warnings in 988.71s (0:16:28) ==================

@Satellite-QE Satellite-QE added PRT-Passed Indicates that latest PRT run is passed for the PR and removed PRT-Failed Indicates that latest PRT run is failed for the PR labels Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

No-CherryPick PR doesnt need CherryPick to previous branches PRT-Passed Indicates that latest PRT run is passed for the PR Stream Introduced in or relating directly to Satellite Stream/Master

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants