From 831e122dd8661beca59c09256e5d9b0a0b3ed20b Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Thu, 4 Jun 2026 20:20:28 -0400 Subject: [PATCH] fix(localize): Allow empty xgettext extraction If xgettext produces no messages, x_extract now detects that the .pot/.po file wasn't created, prints a notice and returns early instead of attempting to rewrite the header. Adds a unit test to verify extraction succeeds when xgettext writes no messages and no template file is produced. This prevents errors when projects have no translatable strings. --- scripts/localize.py | 4 +++ tests/unit/test_localize.py | 68 ++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/scripts/localize.py b/scripts/localize.py index b36df45..7c2ab41 100644 --- a/scripts/localize.py +++ b/scripts/localize.py @@ -509,6 +509,10 @@ def x_extract(context: LocaleContext): ] run_command(command=command, root_dir=context.root_dir) + if not os.path.exists(pot_filepath): + print(f'No gettext messages found; {pot_filepath} was not generated.') + return + rewrite_pot_header(context=context, pot_filepath=pot_filepath) diff --git a/tests/unit/test_localize.py b/tests/unit/test_localize.py index 514f2a9..539d8b6 100644 --- a/tests/unit/test_localize.py +++ b/tests/unit/test_localize.py @@ -4,10 +4,25 @@ import datetime import os +# lib imports +import pytest + # local imports import scripts.localize as localize +@pytest.fixture +def clean_github_environment(monkeypatch): + """Remove GitHub runner variables that affect locale context defaults.""" + + for variable in [ + 'GITHUB_REPOSITORY', + 'GITHUB_REPOSITORY_OWNER', + 'GITHUB_SERVER_URL', + ]: + monkeypatch.delenv(variable, raising=False) + + def parse_args(*args): """Parse locale helper arguments for tests. @@ -362,13 +377,9 @@ def test_collect_source_files_scans_existing_source_directories(tmp_path): ] -def test_x_extract_builds_command_and_rewrites_header(monkeypatch, tmp_path): +def test_x_extract_builds_command_and_rewrites_header(clean_github_environment, monkeypatch, tmp_path): """Verify xgettext extraction builds command arguments and rewrites headers.""" - monkeypatch.delenv('GITHUB_REPOSITORY', raising=False) - monkeypatch.delenv('GITHUB_REPOSITORY_OWNER', raising=False) - monkeypatch.delenv('GITHUB_SERVER_URL', raising=False) - root_dir = str(tmp_path) os.makedirs(os.path.join(root_dir, 'src', 'nested')) with open(os.path.join(root_dir, 'src', 'main.cpp'), mode='w', encoding='utf-8') as file: @@ -426,6 +437,53 @@ def fake_check_output(args, cwd): ) +def test_x_extract_allows_empty_extraction(clean_github_environment, monkeypatch, tmp_path): + """Verify extraction succeeds when xgettext has no messages to write.""" + + root_dir = str(tmp_path) + os.makedirs(os.path.join(root_dir, 'src')) + with open(os.path.join(root_dir, 'src', 'main.cpp'), mode='w', encoding='utf-8') as file: + file.write('int main() { return 0; }\n') + + context = localize.build_context(args=parse_args('--root-dir', root_dir, '--project-name', 'Example')) + calls = [] + + def fake_check_output(args, cwd): + """Record xgettext calls without creating a template file.""" + + calls.append({ + 'args': args, + 'cwd': cwd, + }) + + monkeypatch.setattr(localize.subprocess, 'check_output', fake_check_output) + + localize.x_extract(context=context) + + assert calls == [ + { + 'args': [ + 'xgettext', + *[f'--keyword={keyword}' for keyword in localize.DEFAULT_KEYWORDS], + '--default-domain=example', + f'--output={os.path.join(context.locale_dir, "example.po")}', + '--language=C++', + '--boost', + '--from-code=utf-8', + '-F', + '--msgid-bugs-address=https://github.com/Example/Example', + '--copyright-holder=Example', + '--package-name=Example', + '--package-version=v0', + os.path.join('src', 'main.cpp'), + ], + 'cwd': root_dir, + }, + ] + assert os.path.isdir(context.locale_dir) + assert not os.path.exists(os.path.join(context.locale_dir, 'example.po')) + + def test_x_extract_requires_source_files(tmp_path): """Verify extraction fails clearly when no source files are found."""