Takayuki Shimizukawa avatar Takayuki Shimizukawa committed cfe9e2d

Now gettext translates label target name. Fix i18n: crash when using a indirect target and translating a target section name. Closes #1265

Comments (0)

Files changed (7)

+Release 1.2 (in development)
+============================
+
+Features added
+--------------
+
+* i18n: gettext translates label target name.
+
+Incompatible changes
+--------------------
+
+
+Bugs fixed
+----------
+
+* #1265: Fix i18n: crash when using a indirect target and translating a target
+  section name.
+
+
 Release 1.2 beta3 (released Oct 3, 2013)
 ========================================
 

sphinx/builders/gettext.py

 
 from sphinx.builders import Builder
 from sphinx.util import split_index_msg
-from sphinx.util.nodes import extract_messages, traverse_translatable_index
+from sphinx.util.nodes import (
+    extract_messages,
+    traverse_translatable_target,
+    traverse_translatable_index,
+)
 from sphinx.util.osutil import safe_relpath, ensuredir, find_catalog, SEP
 from sphinx.util.console import darkgreen, purple, bold
 from sphinx.locale import pairindextypes
         for node, msg in extract_messages(doctree):
             catalog.add(msg, node)
 
+        for node, msg in traverse_translatable_target(doctree):
+            catalog.add(msg, node)
+
         # Extract translatable messages from index entries.
         for node, entries in traverse_translatable_index(doctree):
             for typ, msg, tid, main in entries:

sphinx/transforms.py

 from sphinx import addnodes
 from sphinx.locale import _, init as init_locale
 from sphinx.util import split_index_msg
-from sphinx.util.nodes import traverse_translatable_index, extract_messages
+from sphinx.util.nodes import (
+    traverse_translatable_target,
+    traverse_translatable_index,
+    extract_messages,
+)
 from sphinx.util.osutil import ustrftime, find_catalog
 from sphinx.util.compat import docutils_version
 from sphinx.util.pycompat import all
         parser = RSTParser()
 
         #phase1: replace reference ids with translated names
+        for node, msg in traverse_translatable_target(self.document):
+            msgstr = catalog.gettext(msg)
+            # XXX add marker to untranslated parts
+            if not msgstr or msgstr == msg or not msgstr.strip():
+                # as-of-yet untranslated
+                continue
+
+            patch = new_document(source, settings)
+            CustomLocaleReporter(node.source, node.line).set_reporter(patch)
+            parser.parse(msgstr, patch)
+            patch = patch[0]
+
+            # XXX doctest and other block markup
+            if not isinstance(patch, nodes.paragraph):
+                continue # skip for now
+
+            old_name = msg
+            new_name = nodes.fully_normalize_name(patch.astext())
+
+            if old_name in self.document.nameids:
+                self.document.nameids[new_name] = (
+                        self.document.nameids[old_name])
+                self.document.nametypes[new_name] = (
+                        self.document.nametypes[old_name])
+                self.document.refnames[new_name] = (
+                        self.document.refnames[old_name])
+
+        #phase2: replace reference ids with translated names
         for node, msg in extract_messages(self.document):
             msgstr = catalog.gettext(msg)
             # XXX add marker to untranslated parts
                         # _id was not duplicated.
                         # remove old_name entry from document ids database
                         # to reuse original _id.
-                        self.document.nameids.pop(old_name, None)
-                        self.document.nametypes.pop(old_name, None)
-                        self.document.ids.pop(_id, None)
+                        self.document.nameids.pop(old_name, None)  #supplemental
+                        self.document.nametypes.pop(old_name, None)  #supplemental
+                        self.document.ids.pop(_id, None)  # must remove
 
                     # re-entry with new named section node.
                     self.document.note_implicit_target(
                 node['translated'] = True
 
 
-        #phase2: translation
+        #phase3: translation
         for node, msg in extract_messages(self.document):
             if node.get('translated', False):
                 continue
             node.children = patch.children
             node['translated'] = True
 
-        # Extract and translate messages for index entries.
+        #phase4: Extract and translate messages for index entries.
         for node, entries in traverse_translatable_index(self.document):
             new_entries = []
             for type, msg, tid, main in entries:

sphinx/util/nodes.py

             yield node, msg
 
 
+def traverse_translatable_target(doctree):
+    """Extract translatable target from a document tree."""
+    for node in doctree.traverse(nodes.target):
+        if 'names' in node and node['names']:
+            yield node, node['names'][0]
+
+
 def traverse_translatable_index(doctree):
     """Traverse translatable index node from a document tree."""
     def is_block_index(node):

tests/roots/test-intl/label_target.po

 
 msgid ""
 ":ref:`explicit-target` point to ``explicit-target`` and `explicit-target`_"
-" point to duplicated id like ``id1``."
+" point to ``explicit-target`` too."
 msgstr ""
 ":ref:`explicit-target` POINT TO ``explicit-target`` AND `X EXPLICIT-TARGET`_"
-" POINT TO DUPLICATED ID LIKE ``id1``."
+" POINT TO ``explicit-target`` TOO."
 
 msgid "implicit section name"
 msgstr "X IMPLICIT SECTION NAME"
 msgid "label bridged target section"
 msgstr "X LABEL BRIDGED TARGET SECTION"
 
-msgid "`bridge label`_ is not translatable but linked to translated section title."
-msgstr "X `bridge label`_ IS NOT TRANSLATABLE BUT LINKED TO TRANSLATED SECTION TITLE."
+msgid "`bridge label`_ is also translatable and linked to translated section title."
+msgstr "X `Y BRIDGE LABEL`_ IS ALSO TRANSLATABLE AND LINKED TO TRANSLATED SECTION TITLE."
 
 msgid ""
 "`bridge label2`_ point to ``section and label`` and `bridge label`_ point to "
 "``label bridged target section``. The second appeared `bridge label2`_ point "
 "to correct target."
 msgstr ""
-"X `bridge label`_ POINT TO ``LABEL BRIDGED TARGET SECTION`` AND "
-"`bridge label2`_ POINT TO ``SECTION AND LABEL``. THE SECOND APPEARED "
-"`bridge label2`_ POINT TO CORRECT TARGET."
+"X `Y BRIDGE LABEL`_ POINT TO ``LABEL BRIDGED TARGET SECTION`` AND "
+"`Z BRIDGE LABEL2`_ POINT TO ``SECTION AND LABEL``. THE SECOND APPEARED "
+"`Z BRIDGE LABEL2`_ POINT TO CORRECT TARGET."
+
+msgid "bridge label"
+msgstr "Y BRIDGE LABEL"
+
+msgid "bridge label2"
+msgstr "Z BRIDGE LABEL2"
+

tests/roots/test-intl/label_target.txt

 .. This case, a duplicated target id is generated by docutils.
 
 :ref:`explicit-target` point to ``explicit-target`` and
-`explicit-target`_ point to duplicated id like ``id1``.
+`explicit-target`_ point to ``explicit-target`` too.
 
 
 implicit section name
 
 .. This section is targeted through label definition.
 
-`bridge label`_ is not translatable but linked to translated section title.
+`bridge label`_ is also translatable and linked to translated section title.
 
 `bridge label2`_ point to ``section and label`` and `bridge label`_ point to ``label bridged target section``. The second appeared `bridge label2`_ point to correct target.
 

tests/test_intl.py

     assert_elem(
             para1[0],
             texts=['X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'AND',
-                   'X EXPLICIT-TARGET', 'POINT TO DUPLICATED ID LIKE', 'id1',
-                   '.'],
-            refs=['explicit-target', 'id1'])
+                   'X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'TOO.'],
+            refs=['explicit-target', 'explicit-target'])
 
     para2 = secs[2].findall('paragraph')
     assert_elem(
     para3 = secs[3].findall('paragraph')
     assert_elem(
             para3[0],
-            texts=['X', 'bridge label',
-                   'IS NOT TRANSLATABLE BUT LINKED TO TRANSLATED ' +
+            texts=['X', 'Y BRIDGE LABEL',
+                   'IS ALSO TRANSLATABLE AND LINKED TO TRANSLATED ' +
                    'SECTION TITLE.'],
             refs=['label-bridged-target-section'])
     assert_elem(
             para3[1],
-            texts=['X', 'bridge label', 'POINT TO',
-                   'LABEL BRIDGED TARGET SECTION', 'AND', 'bridge label2',
+            texts=['X', 'Y BRIDGE LABEL', 'POINT TO',
+                   'LABEL BRIDGED TARGET SECTION', 'AND', 'Z BRIDGE LABEL2',
                    'POINT TO', 'SECTION AND LABEL', '. THE SECOND APPEARED',
-                   'bridge label2', 'POINT TO CORRECT TARGET.'],
+                   'Z BRIDGE LABEL2', 'POINT TO CORRECT TARGET.'],
             refs=['label-bridged-target-section',
                   'section-and-label',
                   'section-and-label'])
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.