1""" 2 test_intl 3 ~~~~~~~~~ 4 5 Test message patching for internationalization purposes. Runs the text 6 builder in the test root. 7 8 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 9 :license: BSD, see LICENSE for details. 10""" 11 12import os 13import re 14 15import pytest 16from babel.messages import mofile, pofile 17from babel.messages.catalog import Catalog 18from docutils import nodes 19 20from sphinx import locale 21from sphinx.testing.util import (assert_node, assert_not_re_search, assert_re_search, 22 assert_startswith, etree_parse, path, strip_escseq) 23 24sphinx_intl = pytest.mark.sphinx( 25 testroot='intl', 26 confoverrides={ 27 'language': 'xx', 'locale_dirs': ['.'], 28 'gettext_compact': False, 29 }, 30) 31 32 33def read_po(pathname): 34 with pathname.open() as f: 35 return pofile.read_po(f) 36 37 38def write_mo(pathname, po): 39 with pathname.open('wb') as f: 40 return mofile.write_mo(f, po) 41 42 43@pytest.fixture(autouse=True) 44def setup_intl(app_params): 45 srcdir = path(app_params.kwargs['srcdir']) 46 for dirpath, dirs, files in os.walk(srcdir): 47 dirpath = path(dirpath) 48 for f in [f for f in files if f.endswith('.po')]: 49 po = dirpath / f 50 mo = srcdir / 'xx' / 'LC_MESSAGES' / ( 51 os.path.relpath(po[:-3], srcdir) + '.mo') 52 if not mo.parent.exists(): 53 mo.parent.makedirs() 54 55 if not mo.exists() or mo.stat().st_mtime < po.stat().st_mtime: 56 # compile .mo file only if needed 57 write_mo(mo, read_po(po)) 58 59 60@pytest.fixture(autouse=True) 61def _info(app): 62 yield 63 print('# language:', app.config.language) 64 print('# locale_dirs:', app.config.locale_dirs) 65 66 67def elem_gettexts(elem): 68 return [_f for _f in [s.strip() for s in elem.itertext()] if _f] 69 70 71def elem_getref(elem): 72 return elem.attrib.get('refid') or elem.attrib.get('refuri') 73 74 75def assert_elem(elem, texts=None, refs=None, names=None): 76 if texts is not None: 77 _texts = elem_gettexts(elem) 78 assert _texts == texts 79 if refs is not None: 80 _refs = [elem_getref(x) for x in elem.findall('reference')] 81 assert _refs == refs 82 if names is not None: 83 _names = elem.attrib.get('names').split() 84 assert _names == names 85 86 87def assert_count(expected_expr, result, count): 88 find_pair = (expected_expr, result) 89 assert len(re.findall(*find_pair)) == count, find_pair 90 91 92@sphinx_intl 93@pytest.mark.sphinx('text') 94@pytest.mark.test_params(shared_result='test_intl_basic') 95def test_text_emit_warnings(app, warning): 96 app.build() 97 # test warnings in translation 98 warnings = getwarning(warning) 99 warning_expr = ('.*/warnings.txt:4:<translated>:1: ' 100 'WARNING: Inline literal start-string without end-string.\n') 101 assert_re_search(warning_expr, warnings) 102 103 104@sphinx_intl 105@pytest.mark.sphinx('text') 106@pytest.mark.test_params(shared_result='test_intl_basic') 107def test_text_warning_node(app): 108 app.build() 109 # test warnings in translation 110 result = (app.outdir / 'warnings.txt').read_text() 111 expect = ("3. I18N WITH REST WARNINGS" 112 "\n**************************\n" 113 "\nLINE OF >>``<<BROKEN LITERAL MARKUP.\n") 114 assert result == expect 115 116 117@sphinx_intl 118@pytest.mark.sphinx('text') 119@pytest.mark.test_params(shared_result='test_intl_basic') 120@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows") 121def test_text_title_underline(app): 122 app.build() 123 # --- simple translation; check title underlines 124 result = (app.outdir / 'bom.txt').read_text() 125 expect = ("2. Datei mit UTF-8" 126 "\n******************\n" # underline matches new translation 127 "\nThis file has umlauts: äöü.\n") 128 assert result == expect 129 130 131@sphinx_intl 132@pytest.mark.sphinx('text') 133@pytest.mark.test_params(shared_result='test_intl_basic') 134def test_text_subdirs(app): 135 app.build() 136 # --- check translation in subdirs 137 result = (app.outdir / 'subdir' / 'index.txt').read_text() 138 assert_startswith(result, "1. subdir contents\n******************\n") 139 140 141@sphinx_intl 142@pytest.mark.sphinx('text') 143@pytest.mark.test_params(shared_result='test_intl_basic') 144def test_text_inconsistency_warnings(app, warning): 145 app.build() 146 # --- check warnings for inconsistency in number of references 147 result = (app.outdir / 'refs_inconsistency.txt').read_text() 148 expect = ("8. I18N WITH REFS INCONSISTENCY" 149 "\n*******************************\n" 150 "\n* FOR CITATION [ref3].\n" 151 "\n* reference FOR reference.\n" 152 "\n* ORPHAN REFERENCE: I18N WITH REFS INCONSISTENCY.\n" 153 "\n[1] THIS IS A AUTO NUMBERED FOOTNOTE.\n" 154 "\n[ref2] THIS IS A CITATION.\n" 155 "\n[100] THIS IS A NUMBERED FOOTNOTE.\n") 156 assert result == expect 157 158 warnings = getwarning(warning) 159 warning_fmt = ('.*/refs_inconsistency.txt:\\d+: ' 160 'WARNING: inconsistent %(reftype)s in translated message.' 161 ' original: %(original)s, translated: %(translated)s\n') 162 expected_warning_expr = ( 163 warning_fmt % { 164 'reftype': 'footnote references', 165 'original': "\\['\\[#\\]_'\\]", 166 'translated': "\\[\\]" 167 } + 168 warning_fmt % { 169 'reftype': 'footnote references', 170 'original': "\\['\\[100\\]_'\\]", 171 'translated': "\\[\\]" 172 } + 173 warning_fmt % { 174 'reftype': 'references', 175 'original': "\\['reference_'\\]", 176 'translated': "\\['reference_', 'reference_'\\]" 177 } + 178 warning_fmt % { 179 'reftype': 'references', 180 'original': "\\[\\]", 181 'translated': "\\['`I18N WITH REFS INCONSISTENCY`_'\\]" 182 }) 183 assert_re_search(expected_warning_expr, warnings) 184 185 expected_citation_warning_expr = ( 186 '.*/refs_inconsistency.txt:\\d+: WARNING: Citation \\[ref2\\] is not referenced.\n' + 187 '.*/refs_inconsistency.txt:\\d+: WARNING: citation not found: ref3') 188 assert_re_search(expected_citation_warning_expr, warnings) 189 190 191@sphinx_intl 192@pytest.mark.sphinx('text') 193@pytest.mark.test_params(shared_result='test_intl_basic') 194def test_text_literalblock_warnings(app, warning): 195 app.build() 196 # --- check warning for literal block 197 result = (app.outdir / 'literalblock.txt').read_text() 198 expect = ("9. I18N WITH LITERAL BLOCK" 199 "\n**************************\n" 200 "\nCORRECT LITERAL BLOCK:\n" 201 "\n this is" 202 "\n literal block\n" 203 "\nMISSING LITERAL BLOCK:\n" 204 "\n<SYSTEM MESSAGE:") 205 assert_startswith(result, expect) 206 207 warnings = getwarning(warning) 208 expected_warning_expr = ('.*/literalblock.txt:\\d+: ' 209 'WARNING: Literal block expected; none found.') 210 assert_re_search(expected_warning_expr, warnings) 211 212 213@sphinx_intl 214@pytest.mark.sphinx('text') 215@pytest.mark.test_params(shared_result='test_intl_basic') 216def test_text_definition_terms(app): 217 app.build() 218 # --- definition terms: regression test for #975, #2198, #2205 219 result = (app.outdir / 'definition_terms.txt').read_text() 220 expect = ("13. I18N WITH DEFINITION TERMS" 221 "\n******************************\n" 222 "\nSOME TERM" 223 "\n THE CORRESPONDING DEFINITION\n" 224 "\nSOME *TERM* WITH LINK" 225 "\n THE CORRESPONDING DEFINITION #2\n" 226 "\nSOME **TERM** WITH : CLASSIFIER1 : CLASSIFIER2" 227 "\n THE CORRESPONDING DEFINITION\n" 228 "\nSOME TERM WITH : CLASSIFIER[]" 229 "\n THE CORRESPONDING DEFINITION\n") 230 assert result == expect 231 232 233@sphinx_intl 234@pytest.mark.sphinx('text') 235@pytest.mark.test_params(shared_result='test_intl_basic') 236def test_text_glossary_term(app, warning): 237 app.build() 238 # --- glossary terms: regression test for #1090 239 result = (app.outdir / 'glossary_terms.txt').read_text() 240 expect = ("18. I18N WITH GLOSSARY TERMS" 241 "\n****************************\n" 242 "\nSOME NEW TERM" 243 "\n THE CORRESPONDING GLOSSARY\n" 244 "\nSOME OTHER NEW TERM" 245 "\n THE CORRESPONDING GLOSSARY #2\n" 246 "\nLINK TO *SOME NEW TERM*.\n") 247 assert result == expect 248 warnings = getwarning(warning) 249 assert 'term not in glossary' not in warnings 250 251 252@sphinx_intl 253@pytest.mark.sphinx('text') 254@pytest.mark.test_params(shared_result='test_intl_basic') 255def test_text_glossary_term_inconsistencies(app, warning): 256 app.build() 257 # --- glossary term inconsistencies: regression test for #1090 258 result = (app.outdir / 'glossary_terms_inconsistency.txt').read_text() 259 expect = ("19. I18N WITH GLOSSARY TERMS INCONSISTENCY" 260 "\n******************************************\n" 261 "\n1. LINK TO *SOME NEW TERM*.\n") 262 assert result == expect 263 264 warnings = getwarning(warning) 265 expected_warning_expr = ( 266 '.*/glossary_terms_inconsistency.txt:\\d+: ' 267 'WARNING: inconsistent term references in translated message.' 268 " original: \\[':term:`Some term`', ':term:`Some other term`'\\]," 269 " translated: \\[':term:`SOME NEW TERM`'\\]\n") 270 assert_re_search(expected_warning_expr, warnings) 271 272 273@sphinx_intl 274@pytest.mark.sphinx('gettext') 275@pytest.mark.test_params(shared_result='test_intl_gettext') 276def test_gettext_section(app): 277 app.build() 278 # --- section 279 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'section.po') 280 actual = read_po(app.outdir / 'section.pot') 281 for expect_msg in [m for m in expect if m.id]: 282 assert expect_msg.id in [m.id for m in actual if m.id] 283 284 285@sphinx_intl 286@pytest.mark.sphinx('text') 287@pytest.mark.test_params(shared_result='test_intl_basic') 288def test_text_section(app): 289 app.build() 290 # --- section 291 result = (app.outdir / 'section.txt').read_text() 292 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'section.po') 293 for expect_msg in [m for m in expect if m.id]: 294 assert expect_msg.string in result 295 296 297@sphinx_intl 298@pytest.mark.sphinx('text') 299@pytest.mark.test_params(shared_result='test_intl_basic') 300def test_text_seealso(app): 301 app.build() 302 # --- seealso 303 result = (app.outdir / 'seealso.txt').read_text() 304 expect = ("12. I18N WITH SEEALSO" 305 "\n*********************\n" 306 "\nSee also: SHORT TEXT 1\n" 307 "\nSee also: LONG TEXT 1\n" 308 "\nSee also:\n" 309 "\n SHORT TEXT 2\n" 310 "\n LONG TEXT 2\n") 311 assert result == expect 312 313 314@sphinx_intl 315@pytest.mark.sphinx('text') 316@pytest.mark.test_params(shared_result='test_intl_basic') 317def test_text_figure_captions(app): 318 app.build() 319 # --- figure captions: regression test for #940 320 result = (app.outdir / 'figure.txt').read_text() 321 expect = ("14. I18N WITH FIGURE CAPTION" 322 "\n****************************\n" 323 "\n [image]MY CAPTION OF THE FIGURE\n" 324 "\n MY DESCRIPTION PARAGRAPH1 OF THE FIGURE.\n" 325 "\n MY DESCRIPTION PARAGRAPH2 OF THE FIGURE.\n" 326 "\n" 327 "\n14.1. FIGURE IN THE BLOCK" 328 "\n=========================\n" 329 "\nBLOCK\n" 330 "\n [image]MY CAPTION OF THE FIGURE\n" 331 "\n MY DESCRIPTION PARAGRAPH1 OF THE FIGURE.\n" 332 "\n MY DESCRIPTION PARAGRAPH2 OF THE FIGURE.\n" 333 "\n" 334 "\n" 335 "14.2. IMAGE URL AND ALT\n" 336 "=======================\n" 337 "\n" 338 "[image: i18n][image]\n" 339 "\n" 340 " [image: img][image]\n" 341 "\n" 342 "\n" 343 "14.3. IMAGE ON SUBSTITUTION\n" 344 "===========================\n" 345 "\n" 346 "\n" 347 "14.4. IMAGE UNDER NOTE\n" 348 "======================\n" 349 "\n" 350 "Note:\n" 351 "\n" 352 " [image: i18n under note][image]\n" 353 "\n" 354 " [image: img under note][image]\n") 355 assert result == expect 356 357 358@sphinx_intl 359@pytest.mark.sphinx('text') 360@pytest.mark.test_params(shared_result='test_intl_basic') 361def test_text_rubric(app): 362 app.build() 363 # --- rubric: regression test for pull request #190 364 result = (app.outdir / 'rubric.txt').read_text() 365 expect = ("I18N WITH RUBRIC" 366 "\n****************\n" 367 "\n-[ RUBRIC TITLE ]-\n" 368 "\n" 369 "\nRUBRIC IN THE BLOCK" 370 "\n===================\n" 371 "\nBLOCK\n" 372 "\n -[ RUBRIC TITLE ]-\n") 373 assert result == expect 374 375 376@sphinx_intl 377@pytest.mark.sphinx('text') 378@pytest.mark.test_params(shared_result='test_intl_basic') 379def test_text_docfields(app): 380 app.build() 381 # --- docfields 382 result = (app.outdir / 'docfields.txt').read_text() 383 expect = ("21. I18N WITH DOCFIELDS" 384 "\n***********************\n" 385 "\nclass Cls1\n" 386 "\n Parameters:" 387 "\n **param** -- DESCRIPTION OF PARAMETER param\n" 388 "\nclass Cls2\n" 389 "\n Parameters:" 390 "\n * **foo** -- DESCRIPTION OF PARAMETER foo\n" 391 "\n * **bar** -- DESCRIPTION OF PARAMETER bar\n" 392 "\nclass Cls3(values)\n" 393 "\n Raises:" 394 "\n **ValueError** -- IF THE VALUES ARE OUT OF RANGE\n" 395 "\nclass Cls4(values)\n" 396 "\n Raises:" 397 "\n * **TypeError** -- IF THE VALUES ARE NOT VALID\n" 398 "\n * **ValueError** -- IF THE VALUES ARE OUT OF RANGE\n" 399 "\nclass Cls5\n" 400 "\n Returns:" 401 '\n A NEW "Cls3" INSTANCE\n') 402 assert result == expect 403 404 405@sphinx_intl 406@pytest.mark.sphinx('text') 407@pytest.mark.test_params(shared_result='test_intl_basic') 408def test_text_admonitions(app): 409 app.build() 410 # --- admonitions 411 # #1206: gettext did not translate admonition directive's title 412 # seealso: http://docutils.sourceforge.net/docs/ref/rst/directives.html#admonitions 413 result = (app.outdir / 'admonitions.txt').read_text() 414 directives = ( 415 "attention", "caution", "danger", "error", "hint", 416 "important", "note", "tip", "warning", "admonition") 417 for d in directives: 418 assert d.upper() + " TITLE" in result 419 assert d.upper() + " BODY" in result 420 421 # for #4938 `1. ` prefixed admonition title 422 assert "1. ADMONITION TITLE" in result 423 424 425@sphinx_intl 426@pytest.mark.sphinx('gettext') 427@pytest.mark.test_params(shared_result='test_intl_gettext') 428def test_gettext_toctree(app): 429 app.build() 430 # --- toctree (index.rst) 431 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'index.po') 432 actual = read_po(app.outdir / 'index.pot') 433 for expect_msg in [m for m in expect if m.id]: 434 assert expect_msg.id in [m.id for m in actual if m.id] 435 # --- toctree (toctree.rst) 436 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'toctree.po') 437 actual = read_po(app.outdir / 'toctree.pot') 438 for expect_msg in [m for m in expect if m.id]: 439 assert expect_msg.id in [m.id for m in actual if m.id] 440 441 442@sphinx_intl 443@pytest.mark.sphinx('gettext') 444@pytest.mark.test_params(shared_result='test_intl_gettext') 445def test_gettext_table(app): 446 app.build() 447 # --- toctree 448 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'table.po') 449 actual = read_po(app.outdir / 'table.pot') 450 for expect_msg in [m for m in expect if m.id]: 451 assert expect_msg.id in [m.id for m in actual if m.id] 452 453 454@sphinx_intl 455@pytest.mark.sphinx('text') 456@pytest.mark.test_params(shared_result='test_intl_basic') 457def test_text_table(app): 458 app.build() 459 # --- toctree 460 result = (app.outdir / 'table.txt').read_text() 461 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'table.po') 462 for expect_msg in [m for m in expect if m.id]: 463 assert expect_msg.string in result 464 465 466@sphinx_intl 467@pytest.mark.sphinx('text') 468@pytest.mark.test_params(shared_result='test_intl_basic') 469def test_text_toctree(app): 470 app.build() 471 # --- toctree (index.rst) 472 # Note: index.rst contains contents that is not shown in text. 473 result = (app.outdir / 'index.txt').read_text() 474 assert 'CONTENTS' in result 475 assert 'TABLE OF CONTENTS' in result 476 # --- toctree (toctree.rst) 477 result = (app.outdir / 'toctree.txt').read_text() 478 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'toctree.po') 479 for expect_msg in [m for m in expect if m.id]: 480 assert expect_msg.string in result 481 482 483@sphinx_intl 484@pytest.mark.sphinx('gettext') 485@pytest.mark.test_params(shared_result='test_intl_gettext') 486def test_gettext_topic(app): 487 app.build() 488 # --- topic 489 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'topic.po') 490 actual = read_po(app.outdir / 'topic.pot') 491 for expect_msg in [m for m in expect if m.id]: 492 assert expect_msg.id in [m.id for m in actual if m.id] 493 494 495@sphinx_intl 496@pytest.mark.sphinx('text') 497@pytest.mark.test_params(shared_result='test_intl_basic') 498def test_text_topic(app): 499 app.build() 500 # --- topic 501 result = (app.outdir / 'topic.txt').read_text() 502 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'topic.po') 503 for expect_msg in [m for m in expect if m.id]: 504 assert expect_msg.string in result 505 506 507@sphinx_intl 508@pytest.mark.sphinx('gettext') 509@pytest.mark.test_params(shared_result='test_intl_gettext') 510def test_gettext_definition_terms(app): 511 app.build() 512 # --- definition terms: regression test for #2198, #2205 513 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'definition_terms.po') 514 actual = read_po(app.outdir / 'definition_terms.pot') 515 for expect_msg in [m for m in expect if m.id]: 516 assert expect_msg.id in [m.id for m in actual if m.id] 517 518 519@sphinx_intl 520@pytest.mark.sphinx('gettext') 521@pytest.mark.test_params(shared_result='test_intl_gettext') 522def test_gettext_glossary_terms(app, warning): 523 app.build() 524 # --- glossary terms: regression test for #1090 525 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'glossary_terms.po') 526 actual = read_po(app.outdir / 'glossary_terms.pot') 527 for expect_msg in [m for m in expect if m.id]: 528 assert expect_msg.id in [m.id for m in actual if m.id] 529 warnings = warning.getvalue().replace(os.sep, '/') 530 assert 'term not in glossary' not in warnings 531 532 533@sphinx_intl 534@pytest.mark.sphinx('gettext') 535@pytest.mark.test_params(shared_result='test_intl_gettext') 536def test_gettext_glossary_term_inconsistencies(app): 537 app.build() 538 # --- glossary term inconsistencies: regression test for #1090 539 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'glossary_terms_inconsistency.po') 540 actual = read_po(app.outdir / 'glossary_terms_inconsistency.pot') 541 for expect_msg in [m for m in expect if m.id]: 542 assert expect_msg.id in [m.id for m in actual if m.id] 543 544 545@sphinx_intl 546@pytest.mark.sphinx('gettext') 547@pytest.mark.test_params(shared_result='test_intl_gettext') 548def test_gettext_literalblock(app): 549 app.build() 550 # --- gettext builder always ignores ``only`` directive 551 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'literalblock.po') 552 actual = read_po(app.outdir / 'literalblock.pot') 553 for expect_msg in [m for m in expect if m.id]: 554 if len(expect_msg.id.splitlines()) == 1: 555 # compare tranlsations only labels 556 assert expect_msg.id in [m.id for m in actual if m.id] 557 else: 558 pass # skip code-blocks and literalblocks 559 560 561@sphinx_intl 562@pytest.mark.sphinx('gettext') 563@pytest.mark.test_params(shared_result='test_intl_gettext') 564def test_gettext_buildr_ignores_only_directive(app): 565 app.build() 566 # --- gettext builder always ignores ``only`` directive 567 expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'only.po') 568 actual = read_po(app.outdir / 'only.pot') 569 for expect_msg in [m for m in expect if m.id]: 570 assert expect_msg.id in [m.id for m in actual if m.id] 571 572 573@sphinx_intl 574# use individual shared_result directory to avoid "incompatible doctree" error 575@pytest.mark.sphinx(testroot='builder-gettext-dont-rebuild-mo') 576def test_gettext_dont_rebuild_mo(make_app, app_params): 577 # --- don't rebuild by .mo mtime 578 def get_number_of_update_targets(app_): 579 app_.env.find_files(app_.config, app_.builder) 580 _, updated, _ = app_.env.get_outdated_files(config_changed=False) 581 return len(updated) 582 583 args, kwargs = app_params 584 585 # phase1: build document with non-gettext builder and generate mo file in srcdir 586 app0 = make_app('dummy', *args, **kwargs) 587 app0.build() 588 assert (app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').exists() 589 # Since it is after the build, the number of documents to be updated is 0 590 assert get_number_of_update_targets(app0) == 0 591 # When rewriting the timestamp of mo file, the number of documents to be 592 # updated will be changed. 593 mtime = (app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').stat().st_mtime 594 (app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime((mtime + 5, mtime + 5)) 595 assert get_number_of_update_targets(app0) == 1 596 597 # Because doctree for gettext builder can not be shared with other builders, 598 # erase doctreedir before gettext build. 599 app0.doctreedir.rmtree() 600 601 # phase2: build document with gettext builder. 602 # The mo file in the srcdir directory is retained. 603 app = make_app('gettext', *args, **kwargs) 604 app.build() 605 # Since it is after the build, the number of documents to be updated is 0 606 assert get_number_of_update_targets(app) == 0 607 # Even if the timestamp of the mo file is updated, the number of documents 608 # to be updated is 0. gettext builder does not rebuild because of mo update. 609 (app0.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime((mtime + 10, mtime + 10)) 610 assert get_number_of_update_targets(app) == 0 611 612 613@sphinx_intl 614@pytest.mark.sphinx('html') 615@pytest.mark.test_params(shared_result='test_intl_basic') 616def test_html_meta(app): 617 app.build() 618 # --- test for meta 619 result = (app.outdir / 'index.html').read_text() 620 expected_expr = '<meta content="TESTDATA FOR I18N" name="description" />' 621 assert expected_expr in result 622 expected_expr = '<meta content="I18N, SPHINX, MARKUP" name="keywords" />' 623 assert expected_expr in result 624 expected_expr = '<p class="caption"><span class="caption-text">HIDDEN TOC</span></p>' 625 assert expected_expr in result 626 627 628@sphinx_intl 629@pytest.mark.sphinx('html') 630@pytest.mark.test_params(shared_result='test_intl_basic') 631def test_html_footnotes(app): 632 app.build() 633 # --- test for #955 cant-build-html-with-footnotes-when-using 634 # expect no error by build 635 (app.outdir / 'footnote.html').read_text() 636 637 638@sphinx_intl 639@pytest.mark.sphinx('html') 640@pytest.mark.test_params(shared_result='test_intl_basic') 641def test_html_undefined_refs(app): 642 app.build() 643 # --- links to undefined reference 644 result = (app.outdir / 'refs_inconsistency.html').read_text() 645 646 expected_expr = ('<a class="reference external" ' 647 'href="http://www.example.com">reference</a>') 648 assert len(re.findall(expected_expr, result)) == 2 649 650 expected_expr = ('<a class="reference internal" ' 651 'href="#reference">reference</a>') 652 assert len(re.findall(expected_expr, result)) == 0 653 654 expected_expr = ('<a class="reference internal" ' 655 'href="#i18n-with-refs-inconsistency">I18N WITH ' 656 'REFS INCONSISTENCY</a>') 657 assert len(re.findall(expected_expr, result)) == 1 658 659 660@sphinx_intl 661@pytest.mark.sphinx('html') 662@pytest.mark.test_params(shared_result='test_intl_basic') 663def test_html_index_entries(app): 664 app.build() 665 # --- index entries: regression test for #976 666 result = (app.outdir / 'genindex.html').read_text() 667 668 def wrap(tag, keyword): 669 start_tag = "<%s[^>]*>" % tag 670 end_tag = "</%s>" % tag 671 return r"%s\s*%s\s*%s" % (start_tag, keyword, end_tag) 672 673 def wrap_nest(parenttag, childtag, keyword): 674 start_tag1 = "<%s[^>]*>" % parenttag 675 start_tag2 = "<%s[^>]*>" % childtag 676 return r"%s\s*%s\s*%s" % (start_tag1, keyword, start_tag2) 677 expected_exprs = [ 678 wrap('a', 'NEWSLETTER'), 679 wrap('a', 'MAILING LIST'), 680 wrap('a', 'RECIPIENTS LIST'), 681 wrap('a', 'FIRST SECOND'), 682 wrap('a', 'SECOND THIRD'), 683 wrap('a', 'THIRD, FIRST'), 684 wrap_nest('li', 'ul', 'ENTRY'), 685 wrap_nest('li', 'ul', 'SEE'), 686 wrap('a', 'MODULE'), 687 wrap('a', 'KEYWORD'), 688 wrap('a', 'OPERATOR'), 689 wrap('a', 'OBJECT'), 690 wrap('a', 'EXCEPTION'), 691 wrap('a', 'STATEMENT'), 692 wrap('a', 'BUILTIN'), 693 ] 694 for expr in expected_exprs: 695 assert_re_search(expr, result, re.M) 696 697 698@sphinx_intl 699@pytest.mark.sphinx('html') 700@pytest.mark.test_params(shared_result='test_intl_basic') 701def test_html_versionchanges(app): 702 app.build() 703 # --- versionchanges 704 result = (app.outdir / 'versionchange.html').read_text() 705 706 def get_content(result, name): 707 matched = re.search(r'<div class="%s">\n*(.*?)</div>' % name, 708 result, re.DOTALL) 709 if matched: 710 return matched.group(1) 711 else: 712 return '' 713 714 expect1 = ( 715 """<p><span class="versionmodified deprecated">Deprecated since version 1.0: </span>""" 716 """THIS IS THE <em>FIRST</em> PARAGRAPH OF DEPRECATED.</p>\n""" 717 """<p>THIS IS THE <em>SECOND</em> PARAGRAPH OF DEPRECATED.</p>\n""") 718 matched_content = get_content(result, "deprecated") 719 assert expect1 == matched_content 720 721 expect2 = ( 722 """<p><span class="versionmodified added">New in version 1.0: </span>""" 723 """THIS IS THE <em>FIRST</em> PARAGRAPH OF VERSIONADDED.</p>\n""") 724 matched_content = get_content(result, "versionadded") 725 assert expect2 == matched_content 726 727 expect3 = ( 728 """<p><span class="versionmodified changed">Changed in version 1.0: </span>""" 729 """THIS IS THE <em>FIRST</em> PARAGRAPH OF VERSIONCHANGED.</p>\n""") 730 matched_content = get_content(result, "versionchanged") 731 assert expect3 == matched_content 732 733 734@sphinx_intl 735@pytest.mark.sphinx('html') 736@pytest.mark.test_params(shared_result='test_intl_basic') 737def test_html_docfields(app): 738 app.build() 739 # --- docfields 740 # expect no error by build 741 (app.outdir / 'docfields.html').read_text() 742 743 744@sphinx_intl 745@pytest.mark.sphinx('html') 746@pytest.mark.test_params(shared_result='test_intl_basic') 747def test_html_template(app): 748 app.build() 749 # --- gettext template 750 result = (app.outdir / 'contents.html').read_text() 751 assert "WELCOME" in result 752 assert "SPHINX 2013.120" in result 753 754 755@sphinx_intl 756@pytest.mark.sphinx('html') 757@pytest.mark.test_params(shared_result='test_intl_basic') 758def test_html_rebuild_mo(app): 759 app.build() 760 # --- rebuild by .mo mtime 761 app.builder.build_update() 762 app.env.find_files(app.config, app.builder) 763 _, updated, _ = app.env.get_outdated_files(config_changed=False) 764 assert len(updated) == 0 765 766 mtime = (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').stat().st_mtime 767 (app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime((mtime + 5, mtime + 5)) 768 app.env.find_files(app.config, app.builder) 769 _, updated, _ = app.env.get_outdated_files(config_changed=False) 770 assert len(updated) == 1 771 772 773@sphinx_intl 774@pytest.mark.sphinx('xml') 775@pytest.mark.test_params(shared_result='test_intl_basic') 776def test_xml_footnotes(app, warning): 777 app.build() 778 # --- footnotes: regression test for fix #955, #1176 779 et = etree_parse(app.outdir / 'footnote.xml') 780 secs = et.findall('section') 781 782 para0 = secs[0].findall('paragraph') 783 assert_elem( 784 para0[0], 785 ['I18N WITH FOOTNOTE', 'INCLUDE THIS CONTENTS', 786 '2', '[ref]', '1', '100', '*', '. SECOND FOOTNOTE_REF', '100', '.'], 787 ['i18n-with-footnote', 'ref']) 788 789 # check node_id for footnote_references which refer same footnote (refs: #3002) 790 assert para0[0][4].text == para0[0][6].text == '100' 791 assert para0[0][4].attrib['ids'] != para0[0][6].attrib['ids'] 792 793 footnote0 = secs[0].findall('footnote') 794 assert_elem( 795 footnote0[0], 796 ['1', 'THIS IS A AUTO NUMBERED FOOTNOTE.'], 797 None, 798 ['1']) 799 assert_elem( 800 footnote0[1], 801 ['100', 'THIS IS A NUMBERED FOOTNOTE.'], 802 None, 803 ['100']) 804 assert_elem( 805 footnote0[2], 806 ['2', 'THIS IS A AUTO NUMBERED NAMED FOOTNOTE.'], 807 None, 808 ['named']) 809 assert_elem( 810 footnote0[3], 811 ['*', 'THIS IS A AUTO SYMBOL FOOTNOTE.'], 812 None, 813 None) 814 815 citation0 = secs[0].findall('citation') 816 assert_elem( 817 citation0[0], 818 ['ref', 'THIS IS A NAMED FOOTNOTE.'], 819 None, 820 ['ref']) 821 822 warnings = getwarning(warning) 823 warning_expr = '.*/footnote.xml:\\d*: SEVERE: Duplicate ID: ".*".\n' 824 assert_not_re_search(warning_expr, warnings) 825 826 827@sphinx_intl 828@pytest.mark.sphinx('xml') 829@pytest.mark.test_params(shared_result='test_intl_basic') 830def test_xml_footnote_backlinks(app): 831 app.build() 832 # --- footnote backlinks: i18n test for #1058 833 et = etree_parse(app.outdir / 'footnote.xml') 834 secs = et.findall('section') 835 836 para0 = secs[0].findall('paragraph') 837 refs0 = para0[0].findall('footnote_reference') 838 refid2id = {r.attrib.get('refid'): r.attrib.get('ids') for r in refs0} 839 840 footnote0 = secs[0].findall('footnote') 841 for footnote in footnote0: 842 ids = footnote.attrib.get('ids') 843 backrefs = footnote.attrib.get('backrefs').split() 844 assert refid2id[ids] in backrefs 845 846 847@sphinx_intl 848@pytest.mark.sphinx('xml') 849@pytest.mark.test_params(shared_result='test_intl_basic') 850def test_xml_refs_in_python_domain(app): 851 app.build() 852 # --- refs in the Python domain 853 et = etree_parse(app.outdir / 'refs_python_domain.xml') 854 secs = et.findall('section') 855 856 # regression test for fix #1363 857 para0 = secs[0].findall('paragraph') 858 assert_elem( 859 para0[0], 860 ['SEE THIS DECORATOR:', 'sensitive_variables()', '.'], 861 ['sensitive.sensitive_variables']) 862 863 864@sphinx_intl 865@pytest.mark.sphinx('xml') 866@pytest.mark.test_params(shared_result='test_intl_basic') 867def test_xml_keep_external_links(app): 868 app.build() 869 # --- keep external links: regression test for #1044 870 et = etree_parse(app.outdir / 'external_links.xml') 871 secs = et.findall('section') 872 873 para0 = secs[0].findall('paragraph') 874 # external link check 875 assert_elem( 876 para0[0], 877 ['EXTERNAL LINK TO', 'Python', '.'], 878 ['http://python.org/index.html']) 879 880 # internal link check 881 assert_elem( 882 para0[1], 883 ['EXTERNAL LINKS', 'IS INTERNAL LINK.'], 884 ['i18n-with-external-links']) 885 886 # inline link check 887 assert_elem( 888 para0[2], 889 ['INLINE LINK BY', 'THE SPHINX SITE', '.'], 890 ['http://sphinx-doc.org']) 891 892 # unnamed link check 893 assert_elem( 894 para0[3], 895 ['UNNAMED', 'LINK', '.'], 896 ['http://google.com']) 897 898 # link target swapped translation 899 para1 = secs[1].findall('paragraph') 900 assert_elem( 901 para1[0], 902 ['LINK TO', 'external2', 'AND', 'external1', '.'], 903 ['https://www.google.com/external2', 904 'https://www.google.com/external1']) 905 assert_elem( 906 para1[1], 907 ['LINK TO', 'THE PYTHON SITE', 'AND', 'THE SPHINX SITE', '.'], 908 ['http://python.org', 'http://sphinx-doc.org']) 909 910 # multiple references in the same line 911 para2 = secs[2].findall('paragraph') 912 assert_elem( 913 para2[0], 914 ['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',', 915 'THE SPHINX SITE', ',', 'UNNAMED', 'AND', 916 'THE PYTHON SITE', '.'], 917 ['i18n-with-external-links', 'http://python.org/index.html', 918 'http://sphinx-doc.org', 'http://google.com', 919 'http://python.org']) 920 921 922@sphinx_intl 923@pytest.mark.sphinx('xml') 924@pytest.mark.test_params(shared_result='test_intl_basic') 925def test_xml_role_xref(app): 926 app.build() 927 # --- role xref: regression test for #1090, #1193 928 et = etree_parse(app.outdir / 'role_xref.xml') 929 sec1, sec2 = et.findall('section') 930 931 para1, = sec1.findall('paragraph') 932 assert_elem( 933 para1, 934 ['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',', 935 'SOME NEW TERM', '.'], 936 ['i18n-role-xref', 'index', 937 'glossary_terms#term-Some-term']) 938 939 para2 = sec2.findall('paragraph') 940 assert_elem( 941 para2[0], 942 ['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM', '.'], 943 ['glossary_terms#term-Some-other-term', 944 'glossary_terms#term-Some-term']) 945 assert_elem( 946 para2[1], 947 ['LINK TO', 'LABEL', 'AND', 948 'SAME TYPE LINKS', 'AND', 'SAME TYPE LINKS', '.'], 949 ['i18n-role-xref', 'same-type-links', 'same-type-links']) 950 assert_elem( 951 para2[2], 952 ['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'], 953 ['glossary_terms', 'index']) 954 assert_elem( 955 para2[3], 956 ['LINK TO', '--module', 'AND', '-m', '.'], 957 ['cmdoption-module', 'cmdoption-m']) 958 assert_elem( 959 para2[4], 960 ['LINK TO', 'env2', 'AND', 'env1', '.'], 961 ['envvar-env2', 'envvar-env1']) 962 assert_elem( 963 para2[5], 964 ['LINK TO', 'token2', 'AND', 'token1', '.'], 965 []) # TODO: how do I link token role to productionlist? 966 assert_elem( 967 para2[6], 968 ['LINK TO', 'same-type-links', 'AND', "i18n-role-xref", '.'], 969 ['same-type-links', 'i18n-role-xref']) 970 971 972@sphinx_intl 973@pytest.mark.sphinx('xml') 974@pytest.mark.test_params(shared_result='test_intl_basic') 975def test_xml_warnings(app, warning): 976 app.build() 977 # warnings 978 warnings = getwarning(warning) 979 assert 'term not in glossary' not in warnings 980 assert 'undefined label' not in warnings 981 assert 'unknown document' not in warnings 982 983 984@sphinx_intl 985@pytest.mark.sphinx('xml') 986@pytest.mark.test_params(shared_result='test_intl_basic') 987def test_xml_label_targets(app): 988 app.build() 989 # --- label targets: regression test for #1193, #1265 990 et = etree_parse(app.outdir / 'label_target.xml') 991 secs = et.findall('section') 992 993 para0 = secs[0].findall('paragraph') 994 assert_elem( 995 para0[0], 996 ['X SECTION AND LABEL', 'POINT TO', 'implicit-target', 'AND', 997 'X SECTION AND LABEL', 'POINT TO', 'section-and-label', '.'], 998 ['implicit-target', 'section-and-label']) 999 1000 para1 = secs[1].findall('paragraph') 1001 assert_elem( 1002 para1[0], 1003 ['X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'AND', 1004 'X EXPLICIT-TARGET', 'POINT TO DUPLICATED ID LIKE', 'id1', 1005 '.'], 1006 ['explicit-target', 'id1']) 1007 1008 para2 = secs[2].findall('paragraph') 1009 assert_elem( 1010 para2[0], 1011 ['X IMPLICIT SECTION NAME', 'POINT TO', 1012 'implicit-section-name', '.'], 1013 ['implicit-section-name']) 1014 1015 sec2 = secs[2].findall('section') 1016 1017 para2_0 = sec2[0].findall('paragraph') 1018 assert_elem( 1019 para2_0[0], 1020 ['`X DUPLICATED SUB SECTION`_', 'IS BROKEN LINK.'], 1021 []) 1022 1023 para3 = secs[3].findall('paragraph') 1024 assert_elem( 1025 para3[0], 1026 ['X', 'bridge label', 1027 'IS NOT TRANSLATABLE BUT LINKED TO TRANSLATED ' + 1028 'SECTION TITLE.'], 1029 ['label-bridged-target-section']) 1030 assert_elem( 1031 para3[1], 1032 ['X', 'bridge label', 'POINT TO', 1033 'LABEL BRIDGED TARGET SECTION', 'AND', 'bridge label2', 1034 'POINT TO', 'SECTION AND LABEL', '. THE SECOND APPEARED', 1035 'bridge label2', 'POINT TO CORRECT TARGET.'], 1036 ['label-bridged-target-section', 1037 'section-and-label', 1038 'section-and-label']) 1039 1040 1041@sphinx_intl 1042@pytest.mark.sphinx('html') 1043@pytest.mark.test_params(shared_result='test_intl_basic') 1044def test_additional_targets_should_not_be_translated(app): 1045 app.build() 1046 # [literalblock.txt] 1047 result = (app.outdir / 'literalblock.html').read_text() 1048 1049 # title should be translated 1050 expected_expr = 'CODE-BLOCKS' 1051 assert_count(expected_expr, result, 2) 1052 1053 # ruby code block should not be translated but be highlighted 1054 expected_expr = """<span class="s1">'result'</span>""" 1055 assert_count(expected_expr, result, 1) 1056 1057 # C code block without lang should not be translated and *ruby* highlighted 1058 expected_expr = """<span class="c1">#include <stdlib.h></span>""" 1059 assert_count(expected_expr, result, 1) 1060 1061 # C code block with lang should not be translated but be *C* highlighted 1062 expected_expr = ("""<span class="cp">#include</span> """ 1063 """<span class="cpf"><stdio.h></span>""") 1064 assert_count(expected_expr, result, 1) 1065 1066 # literal block in list item should not be translated 1067 expected_expr = ("""<span class="n">literal</span>""" 1068 """<span class="o">-</span>""" 1069 """<span class="n">block</span>\n""" 1070 """<span class="k">in</span> """ 1071 """<span class="n">list</span>""") 1072 assert_count(expected_expr, result, 1) 1073 1074 # doctest block should not be translated but be highlighted 1075 expected_expr = ( 1076 """<span class="gp">>>> </span>""" 1077 """<span class="kn">import</span> <span class="nn">sys</span> """ 1078 """<span class="c1"># sys importing</span>""") 1079 assert_count(expected_expr, result, 1) 1080 1081 # [raw.txt] 1082 1083 result = (app.outdir / 'raw.html').read_text() 1084 1085 # raw block should not be translated 1086 expected_expr = """<iframe src="http://sphinx-doc.org"></iframe></div>""" 1087 assert_count(expected_expr, result, 1) 1088 1089 # [figure.txt] 1090 1091 result = (app.outdir / 'figure.html').read_text() 1092 1093 # alt and src for image block should not be translated 1094 expected_expr = """<img alt="i18n" src="_images/i18n.png" />""" 1095 assert_count(expected_expr, result, 1) 1096 1097 # alt and src for figure block should not be translated 1098 expected_expr = """<img alt="img" src="_images/img.png" />""" 1099 assert_count(expected_expr, result, 1) 1100 1101 1102@sphinx_intl 1103@pytest.mark.sphinx( 1104 'html', 1105 srcdir='test_additional_targets_should_be_translated', 1106 confoverrides={ 1107 'language': 'xx', 'locale_dirs': ['.'], 1108 'gettext_compact': False, 1109 'gettext_additional_targets': [ 1110 'index', 1111 'literal-block', 1112 'doctest-block', 1113 'raw', 1114 'image', 1115 ], 1116 } 1117) 1118def test_additional_targets_should_be_translated(app): 1119 app.build() 1120 # [literalblock.txt] 1121 result = (app.outdir / 'literalblock.html').read_text() 1122 1123 # title should be translated 1124 expected_expr = 'CODE-BLOCKS' 1125 assert_count(expected_expr, result, 2) 1126 1127 # ruby code block should be translated and be highlighted 1128 expected_expr = """<span class="s1">'RESULT'</span>""" 1129 assert_count(expected_expr, result, 1) 1130 1131 # C code block without lang should be translated and *ruby* highlighted 1132 expected_expr = """<span class="c1">#include <STDLIB.H></span>""" 1133 assert_count(expected_expr, result, 1) 1134 1135 # C code block with lang should be translated and be *C* highlighted 1136 expected_expr = ("""<span class="cp">#include</span> """ 1137 """<span class="cpf"><STDIO.H></span>""") 1138 assert_count(expected_expr, result, 1) 1139 1140 # literal block in list item should be translated 1141 expected_expr = ("""<span class="no">LITERAL</span>""" 1142 """<span class="o">-</span>""" 1143 """<span class="no">BLOCK</span>\n""" 1144 """<span class="no">IN</span> """ 1145 """<span class="no">LIST</span>""") 1146 assert_count(expected_expr, result, 1) 1147 1148 # doctest block should not be translated but be highlighted 1149 expected_expr = ( 1150 """<span class="gp">>>> </span>""" 1151 """<span class="kn">import</span> <span class="nn">sys</span> """ 1152 """<span class="c1"># SYS IMPORTING</span>""") 1153 assert_count(expected_expr, result, 1) 1154 1155 # [raw.txt] 1156 1157 result = (app.outdir / 'raw.html').read_text() 1158 1159 # raw block should be translated 1160 expected_expr = """<iframe src="HTTP://SPHINX-DOC.ORG"></iframe></div>""" 1161 assert_count(expected_expr, result, 1) 1162 1163 # [figure.txt] 1164 1165 result = (app.outdir / 'figure.html').read_text() 1166 1167 # alt and src for image block should be translated 1168 expected_expr = """<img alt="I18N -> IMG" src="_images/img.png" />""" 1169 assert_count(expected_expr, result, 1) 1170 1171 # alt and src for figure block should be translated 1172 expected_expr = """<img alt="IMG -> I18N" src="_images/i18n.png" />""" 1173 assert_count(expected_expr, result, 1) 1174 1175 1176@sphinx_intl 1177@pytest.mark.sphinx('text') 1178@pytest.mark.test_params(shared_result='test_intl_basic') 1179def test_text_references(app, warning): 1180 app.builder.build_specific([app.srcdir / 'refs.txt']) 1181 1182 warnings = warning.getvalue().replace(os.sep, '/') 1183 warning_expr = 'refs.txt:\\d+: ERROR: Unknown target name:' 1184 assert_count(warning_expr, warnings, 0) 1185 1186 1187@pytest.mark.sphinx( 1188 'dummy', testroot='images', 1189 srcdir='test_intl_images', 1190 confoverrides={'language': 'xx'} 1191) 1192@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows") 1193def test_image_glob_intl(app): 1194 app.build() 1195 1196 # index.rst 1197 doctree = app.env.get_doctree('index') 1198 assert_node(doctree[0][1], nodes.image, uri='rimg.xx.png', 1199 candidates={'*': 'rimg.xx.png'}) 1200 1201 assert isinstance(doctree[0][2], nodes.figure) 1202 assert_node(doctree[0][2][0], nodes.image, uri='rimg.xx.png', 1203 candidates={'*': 'rimg.xx.png'}) 1204 1205 assert_node(doctree[0][3], nodes.image, uri='img.*', 1206 candidates={'application/pdf': 'img.pdf', 1207 'image/gif': 'img.gif', 1208 'image/png': 'img.png'}) 1209 1210 assert isinstance(doctree[0][4], nodes.figure) 1211 assert_node(doctree[0][4][0], nodes.image, uri='img.*', 1212 candidates={'application/pdf': 'img.pdf', 1213 'image/gif': 'img.gif', 1214 'image/png': 'img.png'}) 1215 1216 # subdir/index.rst 1217 doctree = app.env.get_doctree('subdir/index') 1218 assert_node(doctree[0][1], nodes.image, uri='subdir/rimg.xx.png', 1219 candidates={'*': 'subdir/rimg.xx.png'}) 1220 1221 assert_node(doctree[0][2], nodes.image, uri='subdir/svgimg.*', 1222 candidates={'application/pdf': 'subdir/svgimg.pdf', 1223 'image/svg+xml': 'subdir/svgimg.xx.svg'}) 1224 1225 assert isinstance(doctree[0][3], nodes.figure) 1226 assert_node(doctree[0][3][0], nodes.image, uri='subdir/svgimg.*', 1227 candidates={'application/pdf': 'subdir/svgimg.pdf', 1228 'image/svg+xml': 'subdir/svgimg.xx.svg'}) 1229 1230 1231@pytest.mark.sphinx( 1232 'dummy', testroot='images', 1233 srcdir='test_intl_images', 1234 confoverrides={ 1235 'language': 'xx', 1236 'figure_language_filename': '{root}{ext}.{language}', 1237 } 1238) 1239@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows") 1240def test_image_glob_intl_using_figure_language_filename(app): 1241 app.build() 1242 1243 # index.rst 1244 doctree = app.env.get_doctree('index') 1245 assert_node(doctree[0][1], nodes.image, uri='rimg.png.xx', 1246 candidates={'*': 'rimg.png.xx'}) 1247 1248 assert isinstance(doctree[0][2], nodes.figure) 1249 assert_node(doctree[0][2][0], nodes.image, uri='rimg.png.xx', 1250 candidates={'*': 'rimg.png.xx'}) 1251 1252 assert_node(doctree[0][3], nodes.image, uri='img.*', 1253 candidates={'application/pdf': 'img.pdf', 1254 'image/gif': 'img.gif', 1255 'image/png': 'img.png'}) 1256 1257 assert isinstance(doctree[0][4], nodes.figure) 1258 assert_node(doctree[0][4][0], nodes.image, uri='img.*', 1259 candidates={'application/pdf': 'img.pdf', 1260 'image/gif': 'img.gif', 1261 'image/png': 'img.png'}) 1262 1263 # subdir/index.rst 1264 doctree = app.env.get_doctree('subdir/index') 1265 assert_node(doctree[0][1], nodes.image, uri='subdir/rimg.png', 1266 candidates={'*': 'subdir/rimg.png'}) 1267 1268 assert_node(doctree[0][2], nodes.image, uri='subdir/svgimg.*', 1269 candidates={'application/pdf': 'subdir/svgimg.pdf', 1270 'image/svg+xml': 'subdir/svgimg.svg'}) 1271 1272 assert isinstance(doctree[0][3], nodes.figure) 1273 assert_node(doctree[0][3][0], nodes.image, uri='subdir/svgimg.*', 1274 candidates={'application/pdf': 'subdir/svgimg.pdf', 1275 'image/svg+xml': 'subdir/svgimg.svg'}) 1276 1277 1278def getwarning(warnings): 1279 return strip_escseq(warnings.getvalue().replace(os.sep, '/')) 1280 1281 1282@pytest.mark.sphinx('html', testroot='basic', confoverrides={'language': 'de'}) 1283def test_customize_system_message(make_app, app_params, sphinx_test_tempdir): 1284 try: 1285 # clear translators cache 1286 locale.translators.clear() 1287 1288 # prepare message catalog (.po) 1289 locale_dir = sphinx_test_tempdir / 'basic' / 'locales' / 'de' / 'LC_MESSAGES' 1290 locale_dir.makedirs() 1291 with (locale_dir / 'sphinx.po').open('wb') as f: 1292 catalog = Catalog() 1293 catalog.add('Quick search', 'QUICK SEARCH') 1294 pofile.write_po(f, catalog) 1295 1296 # construct application and convert po file to .mo 1297 args, kwargs = app_params 1298 app = make_app(*args, **kwargs) 1299 assert (locale_dir / 'sphinx.mo').exists() 1300 assert app.translator.gettext('Quick search') == 'QUICK SEARCH' 1301 1302 app.build() 1303 content = (app.outdir / 'index.html').read_text() 1304 assert 'QUICK SEARCH' in content 1305 finally: 1306 locale.translators.clear() 1307