Skip to content

Commit 0fa0aef

Browse files
committed
Add translation completeness test for Satellite locales
New test `test_positive_check_missing_translations` for untranslated messages and missing template strings. Supported languages: fr, ja, ko, zh_CN
1 parent 33ac75f commit 0fa0aef

3 files changed

Lines changed: 129 additions & 1 deletion

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ tests/foreman/cli/test_http_proxy.py @SatelliteQE/team-dragonfly
258258
tests/foreman/cli/test_logging.py @SatelliteQE/team-dragonfly
259259
tests/foreman/cli/test_reporttemplates.py @SatelliteQE/team-dragonfly
260260
tests/foreman/cli/test_settings.py @SatelliteQE/team-dragonfly
261+
tests/foreman/cli/test_translation.py @SatelliteQE/team-dragonfly
261262
tests/foreman/cli/test_usage_report.py @SatelliteQE/team-dragonfly
262263
tests/foreman/ui/test_audit.py @SatelliteQE/team-dragonfly
263264
tests/foreman/ui/test_bookmarks.py @SatelliteQE/team-dragonfly

robottelo/constants/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
'zh_CN',
3131
'zh_TW',
3232
)
33-
33+
SUPPORTED_LANGUAGES = ('fr', 'ja', 'ko', 'zh_CN')
3434

3535
DISTRO_DEFAULT = 'rhel7'
3636
DISTROS_SUPPORTED = ['rhel7', 'rhel8', 'rhel9', 'rhel10']
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
"""Tests for verifying translation completeness on Satellite.
2+
3+
:CaseAutomation: Automated
4+
5+
:CaseComponent: LocalizationInternationalization
6+
7+
:team: Dragonfly
8+
9+
"""
10+
11+
import os
12+
import re
13+
14+
import pytest
15+
16+
from robottelo.constants import SUPPORTED_LANGUAGES
17+
from robottelo.utils.issue_handlers import is_open
18+
19+
LOCALE_DIRS = (
20+
'/usr/share/foreman/locale/',
21+
'/usr/share/gems/gems/{katello,foreman,hammer_cli}*/locale/',
22+
)
23+
FIND_POT = f"find {' '.join(LOCALE_DIRS)} -name '*.pot'"
24+
25+
26+
@pytest.fixture(params=SUPPORTED_LANGUAGES)
27+
def supported_language(request):
28+
return request.param
29+
30+
31+
def test_positive_check_missing_translations(target_sat, supported_language):
32+
"""Verify that all translation files have 100% translated messages
33+
and are complete relative to their templates.
34+
35+
:id: f33c2ad0-02fd-4d50-b1a9-9b6c66a23700
36+
37+
:parametrized: yes
38+
39+
:Verifies: SAT-32747
40+
41+
:steps:
42+
1. Find all .po translation files on the Satellite.
43+
2. Run ``msgfmt --statistics`` on each file to detect untranslated messages.
44+
3. For files with untranslated messages, run ``msgattrib --untranslated``
45+
to list them.
46+
4. Find all .pot template files and compare each .po file against its
47+
template using ``msgcmp`` to detect missing translations.
48+
49+
:expectedresults:
50+
1. All translation files are found on the Satellite.
51+
2. No translation file contains untranslated messages.
52+
3. No translation file is missing strings defined in its template.
53+
54+
:CaseImportance: Medium
55+
"""
56+
# Step 1: Find all .po translation files for the given language
57+
po_search_dirs = ' '.join(os.path.join(dir_, supported_language, '') for dir_ in LOCALE_DIRS)
58+
find_po = f"find {po_search_dirs} -name '*.po'"
59+
result = target_sat.execute(find_po)
60+
assert result.status == 0, f'Failed to search for .po files: {result.stderr}'
61+
po_files = result.stdout.strip().splitlines()
62+
assert po_files, 'No .po translation files found on the Satellite'
63+
64+
# Step 2 & 3: Check each .po file for untranslated messages
65+
untranslated_report = {}
66+
for po_file in po_files:
67+
stats = target_sat.execute(f'msgfmt -v --statistics -o /dev/null {po_file}')
68+
if 'untranslated' in stats.stderr:
69+
details = target_sat.execute(f'msgattrib --untranslated --indent --no-wrap {po_file}')
70+
untranslated_report[po_file] = {
71+
'statistics': stats.stderr.strip(),
72+
'messages': '\n'.join(
73+
[line for line in details.stdout.splitlines() if not line.startswith('#')]
74+
),
75+
}
76+
77+
# Step 4: Compare .po files against their .pot templates
78+
result = target_sat.execute(FIND_POT)
79+
assert result.status == 0, f'Failed to search for .pot files: {result.stderr}'
80+
pot_files = result.stdout.strip().splitlines()
81+
82+
template_failures = {}
83+
for pot_file in pot_files:
84+
locale_dir = os.path.dirname(pot_file)
85+
pot_name = os.path.splitext(os.path.basename(pot_file))[0]
86+
87+
related_pos = [po for po in po_files if po.startswith(locale_dir) and pot_name in po]
88+
for po_file in related_pos:
89+
cmp_result = target_sat.execute(f'msgcmp {po_file} {pot_file}')
90+
not_defined = []
91+
for line in cmp_result.stderr.splitlines():
92+
if f'not defined in {po_file}' in line:
93+
match = re.search(r':(\d+):', line)
94+
line_number = int(match.group(1)) if match else 0
95+
pot_line = ''
96+
if line_number:
97+
get_line_result = target_sat.execute(
98+
f"sed -n '{line_number - 1}p' {pot_file}"
99+
)
100+
pot_line = get_line_result.stdout.strip()
101+
not_defined.append((pot_line, line.strip()))
102+
if not_defined:
103+
template_failures[po_file] = not_defined
104+
105+
errors = []
106+
if untranslated_report:
107+
summary = '\n'.join(
108+
f"{info['statistics']}\n{info['messages']}\n" for info in untranslated_report.values()
109+
)
110+
errors.append(f'Files with untranslated messages:\n{summary}')
111+
if template_failures:
112+
lines = []
113+
for po_file, entries in template_failures.items():
114+
lines.append(f'{po_file}:')
115+
for pot_line, msg in entries:
116+
lines.append(f' {msg}\n {pot_line}')
117+
errors.append('Files with missing translations relative to template:\n' + '\n'.join(lines))
118+
119+
# This code block can be removed once all translation issues are resolved.
120+
open_issues = {'fr': 'SAT-48297', 'ja': 'SAT-48299', 'ko': 'SAT-48300', 'zh_CN': 'SAT-48301'}
121+
if errors and open_issues.get(supported_language) and is_open(open_issues[supported_language]):
122+
pytest.xfail(
123+
f'Translation of language {supported_language} is still in progress ({open_issues[supported_language]}).\n'
124+
'Missing translations:\n' + '\n\n'.join(errors)
125+
)
126+
127+
assert not errors, '\n\n'.join(errors)

0 commit comments

Comments
 (0)