# -*- coding: utf-8 -*- """ test_intl ~~~~~~~~~ Test message patching for internationalization purposes. Runs the text builder in the test root. :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import os import re from StringIO import StringIO from subprocess import Popen, PIPE from xml.etree import ElementTree from sphinx.util.pycompat import relpath from util import test_roots, path, with_app, SkipTest warnfile = StringIO() root = test_roots / 'test-intl' doctreedir = root / '_build' / 'doctree' def with_intl_app(*args, **kw): default_kw = { 'srcdir': root, 'doctreedir': doctreedir, 'confoverrides': { 'language': 'xx', 'locale_dirs': ['.'], 'gettext_compact': False, }, } default_kw.update(kw) return with_app(*args, **default_kw) def setup_module(): # Delete remnants left over after failed build (root / 'xx').rmtree(True) (root / 'xx' / 'LC_MESSAGES').makedirs() # Compile all required catalogs into binary format (*.mo). for dirpath, dirs, files in os.walk(root): dirpath = path(dirpath) for f in [f for f in files if f.endswith('.po')]: po = dirpath / f mo = root / 'xx' / 'LC_MESSAGES' / ( relpath(po[:-3], root) + '.mo') if not mo.parent.exists(): mo.parent.makedirs() try: p = Popen(['msgfmt', po, '-o', mo], stdout=PIPE, stderr=PIPE) except OSError: raise SkipTest # most likely msgfmt was not found else: stdout, stderr = p.communicate() if p.returncode != 0: print stdout print stderr assert False, \ 'msgfmt exited with return code %s' % p.returncode assert mo.isfile(), 'msgfmt failed' def teardown_module(): (root / '_build').rmtree(True) (root / 'xx').rmtree(True) def elem_gettexts(elem): def itertext(self): # this function copied from Python-2.7 'ElementTree.itertext'. # for compatibility to Python-2.5, 2.6, 3.1 tag = self.tag if not isinstance(tag, basestring) and tag is not None: return if self.text: yield self.text for e in self: for s in itertext(e): yield s if e.tail: yield e.tail return filter(None, [s.strip() for s in itertext(elem)]) def elem_getref(elem): return elem.attrib.get('refid') or elem.attrib.get('refuri') def assert_elem(elem, texts=None, refs=None, names=None): if texts is not None: _texts = elem_gettexts(elem) assert _texts == texts if refs is not None: _refs = map(elem_getref, elem.findall('reference')) assert _refs == refs if names is not None: _names = elem.attrib.get('names').split() assert _names == names @with_intl_app(buildername='text') def test_simple(app): app.builder.build(['bom']) result = (app.outdir / 'bom.txt').text(encoding='utf-8') expect = (u"\nDatei mit UTF-8" u"\n***************\n" # underline matches new translation u"\nThis file has umlauts: äöü.\n") assert result == expect @with_intl_app(buildername='text') def test_subdir(app): app.builder.build(['subdir/contents']) result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8') assert result.startswith(u"\nsubdir contents\n***************\n") @with_intl_app(buildername='text', warning=warnfile) def test_i18n_warnings_in_translation(app): app.builddir.rmtree(True) app.builder.build(['warnings']) result = (app.outdir / 'warnings.txt').text(encoding='utf-8') expect = (u"\nI18N WITH REST WARNINGS" u"\n***********************\n" u"\nLINE OF >>``<reference') assert len(re.findall(expected_expr, result)) == 2 expected_expr = ('reference') assert len(re.findall(expected_expr, result)) == 0 expected_expr = ('I18N WITH ' 'REFS INCONSISTENCY') assert len(re.findall(expected_expr, result)) == 1 @with_intl_app(buildername='xml', cleanenv=True) def test_i18n_keep_external_links(app): # regression test for #1044 app.builder.build(['external_links']) et = ElementTree.parse(app.outdir / 'external_links.xml') secs = et.findall('section') para0 = secs[0].findall('paragraph') # external link check assert_elem( para0[0], texts=['EXTERNAL LINK TO', 'Python', '.'], refs=['http://python.org/index.html']) # internal link check assert_elem( para0[1], texts=['EXTERNAL LINKS', 'IS INTERNAL LINK.'], refs=['i18n-with-external-links']) # inline link check assert_elem( para0[2], texts=['INLINE LINK BY', 'THE SPHINX SITE', '.'], refs=['http://sphinx-doc.org']) # unnamed link check assert_elem( para0[3], texts=['UNNAMED', 'LINK', '.'], refs=['http://google.com']) # link target swapped translation para1 = secs[1].findall('paragraph') assert_elem( para1[0], texts=['LINK TO', 'external2', 'AND', 'external1', '.'], refs=['http://example.com/external2', 'http://example.com/external1']) assert_elem( para1[1], texts=['LINK TO', 'THE PYTHON SITE', 'AND', 'THE SPHINX SITE', '.'], refs=['http://python.org', 'http://sphinx-doc.org']) # multiple references in the same line para2 = secs[2].findall('paragraph') assert_elem( para2[0], texts=['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',', 'THE SPHINX SITE', ',', 'UNNAMED', 'AND', 'THE PYTHON SITE', '.'], refs=['i18n-with-external-links', 'http://python.org/index.html', 'http://sphinx-doc.org', 'http://google.com', 'http://python.org']) @with_intl_app(buildername='text', warning=warnfile, cleanenv=True) def test_i18n_literalblock_warning(app): app.builddir.rmtree(True) #for warnings acceleration app.builder.build(['literalblock']) result = (app.outdir / 'literalblock.txt').text(encoding='utf-8') expect = (u"\nI18N WITH LITERAL BLOCK" u"\n***********************\n" u"\nCORRECT LITERAL BLOCK:\n" u"\n this is" u"\n literal block\n" u"\nMISSING LITERAL BLOCK:\n" u"\n\n*(.*?)' % name, result, re.DOTALL) if matched: return matched.group(1) else: return '' expect1 = ( u"""

Deprecated since version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF DEPRECATED.

\n""" u"""

THIS IS THE SECOND PARAGRAPH OF DEPRECATED.

\n""") matched_content = get_content(result, "deprecated") assert expect1 == matched_content expect2 = ( u"""

New in version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF VERSIONADDED.

\n""") matched_content = get_content(result, "versionadded") assert expect2 == matched_content expect3 = ( u"""

Changed in version 1.0: """ u"""THIS IS THE FIRST PARAGRAPH OF VERSIONCHANGED.

\n""") matched_content = get_content(result, "versionchanged") assert expect3 == matched_content @with_intl_app(buildername='text', cleanenv=True) def test_i18n_docfields(app): app.builder.build(['docfields']) result = (app.outdir / 'docfields.txt').text(encoding='utf-8') expect = (u"\nI18N WITH DOCFIELDS" u"\n*******************\n" u"\nclass class Cls1\n" u"\n Parameters:" u"\n **param** -- DESCRIPTION OF PARAMETER param\n" u"\nclass class Cls2\n" u"\n Parameters:" u"\n * **foo** -- DESCRIPTION OF PARAMETER foo\n" u"\n * **bar** -- DESCRIPTION OF PARAMETER bar\n" u"\nclass class Cls3(values)\n" u"\n Raises ValueError:" u"\n IF THE VALUES ARE OUT OF RANGE\n" u"\nclass class Cls4(values)\n" u"\n Raises:" u"\n * **TypeError** -- IF THE VALUES ARE NOT VALID\n" u"\n * **ValueError** -- IF THE VALUES ARE OUT OF RANGE\n" u"\nclass class Cls5\n" u"\n Returns:" u'\n A NEW "Cls3" INSTANCE\n') assert result == expect @with_intl_app(buildername='text', cleanenv=True) def test_i18n_admonitions(app): # #1206: gettext did not translate admonition directive's title # seealso: http://docutils.sourceforge.net/docs/ref/rst/directives.html#admonitions app.builder.build(['admonitions']) result = (app.outdir / 'admonitions.txt').text(encoding='utf-8') directives = ( "attention", "caution", "danger", "error", "hint", "important", "note", "tip", "warning", "admonition",) for d in directives: assert d.upper() + " TITLE" in result assert d.upper() + " BODY" in result @with_intl_app(buildername='html', cleanenv=True) def test_i18n_docfields_html(app): app.builder.build(['docfields']) result = (app.outdir / 'docfields.html').text(encoding='utf-8') # expect no error by build @with_intl_app(buildername='html') def test_gettext_template(app): app.builder.build_all() result = (app.outdir / 'index.html').text(encoding='utf-8') assert "WELCOME" in result assert "SPHINX 2013.120" in result @with_intl_app(buildername='html') def test_rebuild_by_mo_mtime(app): app.builder.build_update() _, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app) assert count == 0 mo = (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').bytes() (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').write_bytes(mo) _, count, _ = app.env.update(app.config, app.srcdir, app.doctreedir, app) assert count == 1