summaryrefslogtreecommitdiffstats
path: root/tests/test_builders/test_build_gettext.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_builders/test_build_gettext.py')
-rw-r--r--tests/test_builders/test_build_gettext.py235
1 files changed, 235 insertions, 0 deletions
diff --git a/tests/test_builders/test_build_gettext.py b/tests/test_builders/test_build_gettext.py
new file mode 100644
index 0000000..ddc6d30
--- /dev/null
+++ b/tests/test_builders/test_build_gettext.py
@@ -0,0 +1,235 @@
+"""Test the build process with gettext builder with the test root."""
+
+import gettext
+import os
+import re
+import subprocess
+import sys
+from subprocess import CalledProcessError
+
+import pytest
+
+from sphinx.builders.gettext import Catalog, MsgOrigin
+
+if sys.version_info[:2] >= (3, 11):
+ from contextlib import chdir
+else:
+ from sphinx.util.osutil import _chdir as chdir
+
+_MSGID_PATTERN = re.compile(r'msgid "(.*)"')
+
+
+def msgid_getter(msgid):
+ if m := _MSGID_PATTERN.search(msgid):
+ return m[1]
+ return None
+
+
+def test_Catalog_duplicated_message():
+ catalog = Catalog()
+ catalog.add('hello', MsgOrigin('/path/to/filename', 1))
+ catalog.add('hello', MsgOrigin('/path/to/filename', 1))
+ catalog.add('hello', MsgOrigin('/path/to/filename', 2))
+ catalog.add('hello', MsgOrigin('/path/to/yetanother', 1))
+ catalog.add('world', MsgOrigin('/path/to/filename', 1))
+
+ assert len(list(catalog)) == 2
+
+ msg1, msg2 = list(catalog)
+ assert msg1.text == 'hello'
+ assert msg1.locations == [('/path/to/filename', 1),
+ ('/path/to/filename', 2),
+ ('/path/to/yetanother', 1)]
+ assert msg2.text == 'world'
+ assert msg2.locations == [('/path/to/filename', 1)]
+
+
+@pytest.mark.sphinx('gettext', srcdir='root-gettext')
+def test_build_gettext(app):
+ # Generic build; should fail only when the builder is horribly broken.
+ app.build(force_all=True)
+
+ # Do messages end up in the correct location?
+ # top-level documents end up in a message catalog
+ assert (app.outdir / 'extapi.pot').is_file()
+ # directory items are grouped into sections
+ assert (app.outdir / 'subdir.pot').is_file()
+
+ # regression test for issue #960
+ catalog = (app.outdir / 'markup.pot').read_text(encoding='utf8')
+ assert 'msgid "something, something else, something more"' in catalog
+
+
+@pytest.mark.sphinx('gettext', srcdir='root-gettext')
+def test_msgfmt(app):
+ app.build(force_all=True)
+
+ (app.outdir / 'en' / 'LC_MESSAGES').mkdir(parents=True, exist_ok=True)
+ with chdir(app.outdir):
+ try:
+ args = ['msginit', '--no-translator', '-i', 'markup.pot', '--locale', 'en_US']
+ subprocess.run(args, capture_output=True, check=True)
+ except OSError:
+ pytest.skip() # most likely msginit was not found
+ except CalledProcessError as exc:
+ print(exc.stdout)
+ print(exc.stderr)
+ msg = f'msginit exited with return code {exc.returncode}'
+ raise AssertionError(msg) from exc
+
+ assert (app.outdir / 'en_US.po').is_file(), 'msginit failed'
+ try:
+ args = ['msgfmt', 'en_US.po',
+ '-o', os.path.join('en', 'LC_MESSAGES', 'test_root.mo')]
+ subprocess.run(args, capture_output=True, check=True)
+ except OSError:
+ pytest.skip() # most likely msgfmt was not found
+ except CalledProcessError as exc:
+ print(exc.stdout)
+ print(exc.stderr)
+ msg = f'msgfmt exited with return code {exc.returncode}'
+ raise AssertionError(msg) from exc
+
+ mo = app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo'
+ assert mo.is_file(), 'msgfmt failed'
+
+ _ = gettext.translation('test_root', app.outdir, languages=['en']).gettext
+ assert _("Testing various markup") == "Testing various markup"
+
+
+@pytest.mark.sphinx(
+ 'gettext', testroot='intl', srcdir='gettext',
+ confoverrides={'gettext_compact': False})
+def test_gettext_index_entries(app):
+ # regression test for #976
+ app.build(filenames=[app.srcdir / 'index_entries.txt'])
+
+ pot = (app.outdir / 'index_entries.pot').read_text(encoding='utf8')
+ msg_ids = list(filter(None, map(msgid_getter, pot.splitlines())))
+
+ assert msg_ids == [
+ "i18n with index entries",
+ "index target section",
+ "this is :index:`Newsletter` target paragraph.",
+ "various index entries",
+ "That's all.",
+ "Mailing List",
+ "Newsletter",
+ "Recipients List",
+ "First",
+ "Second",
+ "Third",
+ "Entry",
+ "See",
+ ]
+
+
+@pytest.mark.sphinx(
+ 'gettext', testroot='intl', srcdir='gettext',
+ confoverrides={'gettext_compact': False,
+ 'gettext_additional_targets': []})
+def test_gettext_disable_index_entries(app):
+ # regression test for #976
+ app.env._pickled_doctree_cache.clear() # clear cache
+ app.build(filenames=[app.srcdir / 'index_entries.txt'])
+
+ pot = (app.outdir / 'index_entries.pot').read_text(encoding='utf8')
+ msg_ids = list(filter(None, map(msgid_getter, pot.splitlines())))
+
+ assert msg_ids == [
+ "i18n with index entries",
+ "index target section",
+ "this is :index:`Newsletter` target paragraph.",
+ "various index entries",
+ "That's all.",
+ ]
+
+
+@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext')
+def test_gettext_template(app):
+ app.build(force_all=True)
+
+ assert (app.outdir / 'sphinx.pot').is_file()
+
+ result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8')
+ assert "Welcome" in result
+ assert "Sphinx %(version)s" in result
+
+
+@pytest.mark.sphinx('gettext', testroot='gettext-template')
+def test_gettext_template_msgid_order_in_sphinxpot(app):
+ app.build(force_all=True)
+ assert (app.outdir / 'sphinx.pot').is_file()
+
+ result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8')
+ assert re.search(
+ ('msgid "Template 1".*'
+ 'msgid "This is Template 1\\.".*'
+ 'msgid "Template 2".*'
+ 'msgid "This is Template 2\\.".*'),
+ result,
+ flags=re.DOTALL)
+
+
+@pytest.mark.sphinx(
+ 'gettext', srcdir='root-gettext',
+ confoverrides={'gettext_compact': 'documentation'})
+def test_build_single_pot(app):
+ app.build(force_all=True)
+
+ assert (app.outdir / 'documentation.pot').is_file()
+
+ result = (app.outdir / 'documentation.pot').read_text(encoding='utf8')
+ assert re.search(
+ ('msgid "Todo".*'
+ 'msgid "Like footnotes.".*'
+ 'msgid "The minute.".*'
+ 'msgid "Generated section".*'),
+ result,
+ flags=re.DOTALL)
+
+
+@pytest.mark.sphinx(
+ 'gettext',
+ testroot='intl_substitution_definitions',
+ srcdir='gettext-subst',
+ confoverrides={'gettext_compact': False,
+ 'gettext_additional_targets': ['image']})
+def test_gettext_prolog_epilog_substitution(app):
+ app.build(force_all=True)
+
+ assert (app.outdir / 'prolog_epilog_substitution.pot').is_file()
+ pot = (app.outdir / 'prolog_epilog_substitution.pot').read_text(encoding='utf8')
+ msg_ids = list(filter(None, map(msgid_getter, pot.splitlines())))
+
+ assert msg_ids == [
+ "i18n with prologue and epilogue substitutions",
+ "This is content that contains |subst_prolog_1|.",
+ "Substituted image |subst_prolog_2| here.",
+ "subst_prolog_2",
+ ".. image:: /img.png",
+ "This is content that contains |subst_epilog_1|.",
+ "Substituted image |subst_epilog_2| here.",
+ "subst_epilog_2",
+ ".. image:: /i18n.png",
+ ]
+
+
+@pytest.mark.sphinx(
+ 'gettext',
+ testroot='intl_substitution_definitions',
+ srcdir='gettext-subst',
+ confoverrides={'gettext_compact': False,
+ 'gettext_additional_targets': ['image']})
+def test_gettext_prolog_epilog_substitution_excluded(app):
+ # regression test for #9428
+ app.build(force_all=True)
+
+ assert (app.outdir / 'prolog_epilog_substitution_excluded.pot').is_file()
+ pot = (app.outdir / 'prolog_epilog_substitution_excluded.pot').read_text(encoding='utf8')
+ msg_ids = list(filter(None, map(msgid_getter, pot.splitlines())))
+
+ assert msg_ids == [
+ "i18n without prologue and epilogue substitutions",
+ "This is content that does not include prologue and epilogue substitutions.",
+ ]