diff --git a/sphinx_intl/basic.py b/sphinx_intl/basic.py index 6d30c99..fa035e3 100644 --- a/sphinx_intl/basic.py +++ b/sphinx_intl/basic.py @@ -22,14 +22,15 @@ 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. :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 + :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, 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, line_width) + c.dump_po(po_file, cat_pot, width=line_width, + ignore_obsolete=ignore_obsolete) 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): 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() diff --git a/tests/test_cmd_pot.py b/tests/test_cmd_pot.py index 865a3b0..2ef062d 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,83 @@ 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_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