From d5deb5850b3b7d6024deee73972aaa584383ee08 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 5 Apr 2025 18:14:21 +0100 Subject: [PATCH 1/7] Add --statistics opt --- Lib/test/test_tools/test_msgfmt.py | 6 +++++ ...-04-05-18-13-00.gh-issue-77393.msgid28.rst | 1 + Tools/i18n/msgfmt.py | 22 ++++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2025-04-05-18-13-00.gh-issue-77393.msgid28.rst diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index ea10d4693df75a..2f396ea4598168 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -169,6 +169,12 @@ def test_no_input_file(self): def test_nonexistent_file(self): assert_python_failure(msgfmt, 'nonexistent.po') + def test_statistics(self): + with temp_cwd(): + res = assert_python_ok(msgfmt, '--statistics', data_dir / "general.po") + out = res.out.decode('utf-8').strip() + self.assertEqual('7 translated messages, 1 untranslated message.', out) + def update_catalog_snapshots(): for po_file in data_dir.glob('*.po'): diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-04-05-18-13-00.gh-issue-77393.msgid28.rst b/Misc/NEWS.d/next/Tools-Demos/2025-04-05-18-13-00.gh-issue-77393.msgid28.rst new file mode 100644 index 00000000000000..454dab452527d6 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2025-04-05-18-13-00.gh-issue-77393.msgid28.rst @@ -0,0 +1 @@ +Added ``--statistics`` option to :program:`msgfmt`. diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index 878d3e290778c6..c9b9f31d331fe8 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -23,6 +23,9 @@ -V --version Display version information and exit. + + --statistics + Print statistics about translations. """ import os @@ -229,11 +232,12 @@ def make(filename, outfile): def main(): try: opts, args = getopt.getopt(sys.argv[1:], 'hVo:', - ['help', 'version', 'output-file=']) + ['help', 'version', 'output-file=', 'statistics']) except getopt.error as msg: usage(1, msg) outfile = None + print_statistics = False # parse options for opt, arg in opts: if opt in ('-h', '--help'): @@ -243,6 +247,8 @@ def main(): sys.exit(0) elif opt in ('-o', '--output-file'): outfile = arg + elif opt in ('--statistics',): + print_statistics = True # do it if not args: print('No input file given', file=sys.stderr) @@ -252,6 +258,20 @@ def main(): for filename in args: make(filename, outfile) + if print_statistics: + strings = translated = 0 + for msgid, msgstr in MESSAGES.items(): + if msgid == b'': + continue + strings += 1 + if msgstr.strip() and msgstr != msgid: + translated += 1 + + print( + f"{translated} translated message{'s' if translated != 1 else ''}, " + f"{strings - translated} untranslated message{'s' if (strings - translated) != 1 else ''}." + ) + if __name__ == '__main__': main() From 3a9d5e2170115ba2dc9a20a3468139b041cadf1f Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 5 Apr 2025 18:19:53 +0100 Subject: [PATCH 2/7] Make it like GNU msgfmt's option --- Tools/i18n/msgfmt.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index c9b9f31d331fe8..5cba7cebc03f98 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -264,13 +264,15 @@ def main(): if msgid == b'': continue strings += 1 - if msgstr.strip() and msgstr != msgid: + if msgstr.strip(): translated += 1 - print( - f"{translated} translated message{'s' if translated != 1 else ''}, " - f"{strings - translated} untranslated message{'s' if (strings - translated) != 1 else ''}." - ) + untranslated = strings - translated + message = f"{translated} translated message{'s' if translated != 1 else ''}" + if untranslated > 0: + message += f", {untranslated} untranslated message{'s' if untranslated != 1 else ''}" + message += "." + print(message) if __name__ == '__main__': From e85a61a58bbfae5511dcaeff5d496184892b3edf Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 5 Apr 2025 18:39:23 +0100 Subject: [PATCH 3/7] Minor fix --- Tools/i18n/msgfmt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index 5cba7cebc03f98..399008b460af12 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -264,7 +264,7 @@ def main(): if msgid == b'': continue strings += 1 - if msgstr.strip(): + if msgstr.strip() and msgstr != msgid: translated += 1 untranslated = strings - translated From 898204b1c69df7ab8d2216710bed158a86644eda Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 5 Apr 2025 21:10:04 +0100 Subject: [PATCH 4/7] Test multiple input files, also edge case (and as a result issue 53950) covered --- Lib/test/test_tools/test_msgfmt.py | 4 ++++ Tools/i18n/msgfmt.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index 2f396ea4598168..3bad1cdb901e3b 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -174,6 +174,10 @@ def test_statistics(self): res = assert_python_ok(msgfmt, '--statistics', data_dir / "general.po") out = res.out.decode('utf-8').strip() self.assertEqual('7 translated messages, 1 untranslated message.', out) + # Multiple input files + res = assert_python_ok(msgfmt, '--statistics', '-o', 'temp.mo', data_dir / "general.po", data_dir / "fuzzy.po") + out = res.out.decode('utf-8').strip() + self.assertEqual('7 translated messages, 1 untranslated message.\n0 translated messages.', out) def update_catalog_snapshots(): diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index 399008b460af12..85269a6a80b86b 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -102,6 +102,10 @@ def generate(): def make(filename, outfile): + # see gh-issue: 53950 + global MESSAGES + MESSAGES.clear() + ID = 1 STR = 2 CTXT = 3 From 0b71e6e90116d04ccbcbf98a4ae3adfdfc226cf5 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 5 Apr 2025 21:27:43 +0100 Subject: [PATCH 5/7] Add filenames for multiple inputs and fix test for windows --- Lib/test/test_tools/test_msgfmt.py | 3 ++- Tools/i18n/msgfmt.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index 3bad1cdb901e3b..849c6fd003361d 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -177,7 +177,8 @@ def test_statistics(self): # Multiple input files res = assert_python_ok(msgfmt, '--statistics', '-o', 'temp.mo', data_dir / "general.po", data_dir / "fuzzy.po") out = res.out.decode('utf-8').strip() - self.assertEqual('7 translated messages, 1 untranslated message.\n0 translated messages.', out) + self.assertIn('general.po: 7 translated messages, 1 untranslated message.', out) + self.assertIn('fuzzy.po: 0 translated messages.', out) def update_catalog_snapshots(): diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index 85269a6a80b86b..ec20347f139420 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -272,7 +272,9 @@ def main(): translated += 1 untranslated = strings - translated - message = f"{translated} translated message{'s' if translated != 1 else ''}" + + message = (f"{os.path.basename(filename) + ': ' if len(args) > 1 else ''}" + f"{translated} translated message{'s' if translated != 1 else ''}") if untranslated > 0: message += f", {untranslated} untranslated message{'s' if untranslated != 1 else ''}" message += "." From 3c9fd26253591297243a6d622b28616f77f09267 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 5 Apr 2025 21:55:33 +0100 Subject: [PATCH 6/7] Fix empty translation counting --- Lib/test/test_tools/test_msgfmt.py | 4 ++-- Tools/i18n/msgfmt.py | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_tools/test_msgfmt.py b/Lib/test/test_tools/test_msgfmt.py index 849c6fd003361d..eb93f55b4a1286 100644 --- a/Lib/test/test_tools/test_msgfmt.py +++ b/Lib/test/test_tools/test_msgfmt.py @@ -173,11 +173,11 @@ def test_statistics(self): with temp_cwd(): res = assert_python_ok(msgfmt, '--statistics', data_dir / "general.po") out = res.out.decode('utf-8').strip() - self.assertEqual('7 translated messages, 1 untranslated message.', out) + self.assertEqual('8 translated messages, 1 untranslated message.', out) # Multiple input files res = assert_python_ok(msgfmt, '--statistics', '-o', 'temp.mo', data_dir / "general.po", data_dir / "fuzzy.po") out = res.out.decode('utf-8').strip() - self.assertIn('general.po: 7 translated messages, 1 untranslated message.', out) + self.assertIn('general.po: 8 translated messages, 1 untranslated message.', out) self.assertIn('fuzzy.po: 0 translated messages.', out) diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index ec20347f139420..57fa7db3f92982 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -41,6 +41,7 @@ MESSAGES = {} +empty_translations = 0 # Counter for empty translations for --statistics def usage(code, msg=''): @@ -52,12 +53,14 @@ def usage(code, msg=''): def add(ctxt, id, str, fuzzy): "Add a non-fuzzy translation to the dictionary." - global MESSAGES + global MESSAGES, empty_translations if not fuzzy and str: if ctxt is None: MESSAGES[id] = str else: MESSAGES[b"%b\x04%b" % (ctxt, id)] = str + elif not fuzzy and not str: + empty_translations += 1 def generate(): @@ -103,8 +106,9 @@ def generate(): def make(filename, outfile): # see gh-issue: 53950 - global MESSAGES + global MESSAGES, empty_translations MESSAGES.clear() + empty_translations = 0 ID = 1 STR = 2 @@ -263,20 +267,17 @@ def main(): make(filename, outfile) if print_statistics: - strings = translated = 0 + translated = 0 for msgid, msgstr in MESSAGES.items(): if msgid == b'': continue - strings += 1 - if msgstr.strip() and msgstr != msgid: + if msgstr.strip(): translated += 1 - untranslated = strings - translated - message = (f"{os.path.basename(filename) + ': ' if len(args) > 1 else ''}" f"{translated} translated message{'s' if translated != 1 else ''}") - if untranslated > 0: - message += f", {untranslated} untranslated message{'s' if untranslated != 1 else ''}" + if empty_translations > 0: + message += f", {empty_translations} untranslated message{'s' if empty_translations != 1 else ''}" message += "." print(message) From 3a97d4323cddc0c57eda19b04aa5bb392220307a Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Wed, 16 Apr 2025 13:05:16 +0100 Subject: [PATCH 7/7] Serhiy's suggestions --- Tools/i18n/msgfmt.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Tools/i18n/msgfmt.py b/Tools/i18n/msgfmt.py index 57fa7db3f92982..887cf6db22dd1c 100755 --- a/Tools/i18n/msgfmt.py +++ b/Tools/i18n/msgfmt.py @@ -267,19 +267,23 @@ def main(): make(filename, outfile) if print_statistics: - translated = 0 - for msgid, msgstr in MESSAGES.items(): - if msgid == b'': - continue - if msgstr.strip(): - translated += 1 - - message = (f"{os.path.basename(filename) + ': ' if len(args) > 1 else ''}" - f"{translated} translated message{'s' if translated != 1 else ''}") - if empty_translations > 0: - message += f", {empty_translations} untranslated message{'s' if empty_translations != 1 else ''}" - message += "." - print(message) + _print_statistics(filename, args) + +# Utility to print --statistics +def _print_statistics(filename, args): + translated = 0 + for msgid, msgstr in MESSAGES.items(): + if not msgid: + continue + if msgstr: + translated += 1 + + message = (f"{os.path.basename(filename) + ': ' if len(args) > 1 else ''}" + f"{translated} translated message{'s' if translated != 1 else ''}") + if empty_translations > 0: + message += f", {empty_translations} untranslated message{'s' if empty_translations != 1 else ''}" + message += "." + print(message) if __name__ == '__main__':