From d7ca2fa0304ff5200c465dd5eadb7f312c775e1e Mon Sep 17 00:00:00 2001 From: James Knight Date: Sat, 18 Jun 2022 14:56:52 -0400 Subject: [PATCH 1/5] tests: Sanity check line-width option Provide a unit test to validate the usage of the `--line-width` option. Signed-off-by: James Knight --- tests/test_cmd_pot.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_cmd_pot.py b/tests/test_cmd_pot.py index 865a3b0..e1dfe55 100644 --- a/tests/test_cmd_pot.py +++ b/tests/test_cmd_pot.py @@ -8,6 +8,9 @@ :copyright: Copyright 2019 by Takayuki SHIMIZUKAWA. :license: BSD, see LICENSE for details. """ + +import os + from click.testing import CliRunner from sphinx_intl import commands @@ -67,6 +70,38 @@ def test_update_difference_detect(temp): assert r4.output.count('Not Changed:') == 1 +def test_update_line_width(temp): + with open('_build/locale/README.pot', 'r') as f: + template = f.read() + + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar identifier1"\nmsgstr ""\n') + + po_dir = os.path.join('locale', 'ja', 'LC_MESSAGES') + po_file = os.path.join(po_dir, 'README.po') + + r1 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-l', 'ja']) + assert r1.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '"foorbar identifier1"\n' in contents + + # change the identifier to trigger an update and impose a lower line-width count + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar identifier2"\nmsgstr ""\n') + + r2 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-w', '1']) + assert r2.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '"foorbar"\n' in contents + assert '"identifier2"\n' in contents + + def test_stat(temp): r1 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-l', 'ja']) assert r1.exit_code == 0 From 79e603abcefd5d6feddbd08260c3ce7ba572222d Mon Sep 17 00:00:00 2001 From: James Knight Date: Sat, 18 Jun 2022 14:58:04 -0400 Subject: [PATCH 2/5] Adjust catalog commands to support forwarding options to babel The following tweaks the catalog wrapper commands used to call babel's `pofile`/`mofile` functions to support forwarding any keyword argument. This can provide flexibility to callers which may wish to utilize additional options provided by babel, without needing to add an explicit option in the catalog wrapper calls. A compatibility adjustment was added to `dump_po` to help map an original argument `line_width` to babel's `width` argument. This is to ensure callers which still use the `line_width` argument can appropriately configure the babel call to impose the provided width. Signed-off-by: James Knight --- sphinx_intl/basic.py | 4 ++-- sphinx_intl/catalog.py | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/sphinx_intl/basic.py b/sphinx_intl/basic.py index 6d30c99..832052c 100644 --- a/sphinx_intl/basic.py +++ b/sphinx_intl/basic.py @@ -61,7 +61,7 @@ def update(locale_dir, pot_dir, languages, line_width=76): status['update'] += 1 click.echo('Update: {0} +{1}, -{2}'.format( po_file, len(added), len(deleted))) - c.dump_po(po_file, cat, line_width) + c.dump_po(po_file, cat, width=line_width) else: status['notchanged'] += 1 click.echo('Not Changed: {0}'.format(po_file)) @@ -69,7 +69,7 @@ def update(locale_dir, pot_dir, languages, line_width=76): status['create'] += 1 click.echo('Create: {0}'.format(po_file)) cat_pot.locale = lang - c.dump_po(po_file, cat_pot, line_width) + c.dump_po(po_file, cat_pot, width=line_width) return status diff --git a/sphinx_intl/catalog.py b/sphinx_intl/catalog.py index 714416c..f8642a3 100644 --- a/sphinx_intl/catalog.py +++ b/sphinx_intl/catalog.py @@ -6,10 +6,11 @@ from babel.messages import pofile, mofile -def load_po(filename): +def load_po(filename, **kwargs): """read po/pot file and return catalog object :param unicode filename: path to po/pot file + :param kwargs: keyword arguments to forward to babel's read_po call :return: catalog object """ # pre-read to get charset @@ -20,27 +21,36 @@ def load_po(filename): # To decode lines by babel, read po file as binary mode and specify charset for # read_po function. with io.open(filename, 'rb') as f: # FIXME: encoding VS charset - return pofile.read_po(f, charset=charset) + return pofile.read_po(f, charset=charset, **kwargs) -def dump_po(filename, catalog, line_width=76): +def dump_po(filename, catalog, **kwargs): """write po/pot file from catalog object :param unicode filename: path to po file :param catalog: catalog object - :param line_width: maximum line wdith of po files + :param kwargs: keyword arguments to forward to babel's write_po call; also + accepts a deprecated `line_width` option to forward to + write_po's `width` option :return: None """ dirname = os.path.dirname(filename) if not os.path.exists(dirname): os.makedirs(dirname) + # (compatibility) line_width was the original argument used to forward + # line width hints into write_po's `width` argument; if provided, + # set/override the width value + if 'line_width' in kwargs: + kwargs['width'] = kwargs['line_width'] + del kwargs['line_width'] + # Because babel automatically encode strings, file should be open as binary mode. with io.open(filename, 'wb') as f: - pofile.write_po(f, catalog, line_width) + pofile.write_po(f, catalog, **kwargs) -def write_mo(filename, catalog): +def write_mo(filename, catalog, **kwargs): """write mo file from catalog object :param unicode filename: path to mo file @@ -51,7 +61,7 @@ def write_mo(filename, catalog): if not os.path.exists(dirname): os.makedirs(dirname) with io.open(filename, 'wb') as f: - mofile.write_mo(f, catalog) + mofile.write_mo(f, catalog, **kwargs) def translated_entries(catalog): From 6f525a90c60dfc1f3368601b835a3de1f4cd14b6 Mon Sep 17 00:00:00 2001 From: James Knight Date: Sat, 18 Jun 2022 14:58:57 -0400 Subject: [PATCH 3/5] Correct docstring spelling Signed-off-by: James Knight --- sphinx_intl/basic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx_intl/basic.py b/sphinx_intl/basic.py index 832052c..c7c5cc7 100644 --- a/sphinx_intl/basic.py +++ b/sphinx_intl/basic.py @@ -29,7 +29,7 @@ def update(locale_dir, pot_dir, languages, line_width=76): :param unicode locale_dir: path for locale directory :param unicode pot_dir: path for pot directory :param tuple languages: languages to update po files - :param number line_width: maximum line wdith of po files + :param number line_width: maximum line width of po files :return: {'create': 0, 'update': 0, 'notchanged': 0} :rtype: dict """ From 6ed5aa7cf93cd9ec804060de9e7da49a957d14d7 Mon Sep 17 00:00:00 2001 From: James Knight Date: Sat, 18 Jun 2022 14:59:21 -0400 Subject: [PATCH 4/5] Provide no-obsolete option Adds support for a `--no-obsolete` option to allow the automatic exclusion of obsolete messages (`#~ `) from updated sources. This utilizes babel's `ignore_obsolete` option to not produce obsolete in a final pot file. The `--no-obsolete` argument was chosen to mimic the same argument naming as seen in `msgattrib` [1]. [1]: https://www.gnu.org/software/gettext/manual/html_node/msgattrib-Invocation.html Signed-off-by: James Knight --- sphinx_intl/basic.py | 9 ++++++--- sphinx_intl/commands.py | 11 +++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sphinx_intl/basic.py b/sphinx_intl/basic.py index c7c5cc7..fa035e3 100644 --- a/sphinx_intl/basic.py +++ b/sphinx_intl/basic.py @@ -22,7 +22,7 @@ def get_lang_dirs(path): # ================================== # commands -def update(locale_dir, pot_dir, languages, line_width=76): +def update(locale_dir, pot_dir, languages, line_width=76, ignore_obsolete=False): """ Update specified language's po files from pot. @@ -30,6 +30,7 @@ def update(locale_dir, pot_dir, languages, line_width=76): :param unicode pot_dir: path for pot directory :param tuple languages: languages to update po files :param number line_width: maximum line width of po files + :param bool ignore_obsolete: ignore obsolete entries in po files :return: {'create': 0, 'update': 0, 'notchanged': 0} :rtype: dict """ @@ -61,7 +62,8 @@ def update(locale_dir, pot_dir, languages, line_width=76): status['update'] += 1 click.echo('Update: {0} +{1}, -{2}'.format( po_file, len(added), len(deleted))) - c.dump_po(po_file, cat, width=line_width) + c.dump_po(po_file, cat, width=line_width, + ignore_obsolete=ignore_obsolete) else: status['notchanged'] += 1 click.echo('Not Changed: {0}'.format(po_file)) @@ -69,7 +71,8 @@ def update(locale_dir, pot_dir, languages, line_width=76): status['create'] += 1 click.echo('Create: {0}'.format(po_file)) cat_pot.locale = lang - c.dump_po(po_file, cat_pot, width=line_width) + c.dump_po(po_file, cat_pot, width=line_width, + ignore_obsolete=ignore_obsolete) return status diff --git a/sphinx_intl/commands.py b/sphinx_intl/commands.py index fb378f1..3567cc5 100644 --- a/sphinx_intl/commands.py +++ b/sphinx_intl/commands.py @@ -130,6 +130,12 @@ def convert(self, value, param, ctx): help='The maximum line width for the po files, 0 or a negative number ' 'disable line wrapping') +option_no_obsolete = click.option( + '--no-obsolete', + envvar=ENVVAR_PREFIX + '_NO_OBSOLETE', + is_flag=True, default=False, + help='Remove obsolete #~ messages.') + option_transifex_token = click.option( '--transifex-token', envvar=ENVVAR_PREFIX + '_TRANSIFEX_TOKEN', @@ -234,7 +240,8 @@ def main(ctx, config, tag): @option_pot_dir @option_language @option_line_width -def update(locale_dir, pot_dir, language, line_width): +@option_no_obsolete +def update(locale_dir, pot_dir, language, line_width, no_obsolete): """ Update specified language's po files from pot. @@ -260,7 +267,7 @@ def update(locale_dir, pot_dir, language, line_width): % locals()) raise click.BadParameter(msg, param_hint='language') - basic.update(locale_dir, pot_dir, languages, line_width) + basic.update(locale_dir, pot_dir, languages, line_width, ignore_obsolete=no_obsolete) @main.command() From dee5115da30e9b171ebee91c77dde94de14678d0 Mon Sep 17 00:00:00 2001 From: James Knight Date: Sat, 18 Jun 2022 14:59:31 -0400 Subject: [PATCH 5/5] tests: Validate newly added no-obsolete option Signed-off-by: James Knight --- tests/test_cmd_pot.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_cmd_pot.py b/tests/test_cmd_pot.py index e1dfe55..2ef062d 100644 --- a/tests/test_cmd_pot.py +++ b/tests/test_cmd_pot.py @@ -102,6 +102,51 @@ def test_update_line_width(temp): assert '"identifier2"\n' in contents +def test_update_no_obsolete(temp): + with open('_build/locale/README.pot', 'r') as f: + template = f.read() + + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar1"\nmsgstr ""\n') + f.write('\nmsgid "foorbar2"\nmsgstr ""\n') + + po_dir = os.path.join('locale', 'ja', 'LC_MESSAGES') + po_file = os.path.join(po_dir, 'README.po') + + r1 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-l', 'ja']) + assert r1.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '\nmsgid "foorbar1"\n' in contents + assert '\nmsgid "foorbar2"\n' in contents + + # remove the foorbar2 and verify we can see the obsolete entry + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar1"\nmsgstr ""\n') + + r2 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale']) + assert r2.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '\n#~ msgid "foorbar2"\n' in contents + + # remove the foorbar1 and verify we can no longer see any obsolete entry + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + + r3 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '--no-obsolete']) + assert r3.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert 'msgid "foorbar1"' not in contents + assert 'msgid "foorbar2"' not in contents + + def test_stat(temp): r1 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-l', 'ja']) assert r1.exit_code == 0