diff --git a/CHANGES b/CHANGES index 36e5cc77b4b..7a44cde27d9 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,8 @@ Dependencies Incompatible changes -------------------- +* #7784: i18n: The msgid for alt text of image is changed + Deprecated ---------- @@ -26,6 +28,8 @@ Features added * #7745: html: inventory is broken if the docname contains a space * #7902: html theme: Add a new option :confval:`globaltoc_maxdepth` to control the behavior of globaltoc in sidebar +* #7784: i18n: The alt text for image is translated by default (without + :confval:`gettext_additional_targets` setting) * #7052: add ``:noindexentry:`` to the Python, C, C++, and Javascript domains. Update the documentation to better reflect the relationship between this option and the ``:noindex:`` option. diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index eba6904c40c..c962f1f3c90 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -802,13 +802,16 @@ documentation on :ref:`intl` for details. :literal-block: literal blocks (``::`` annotation and ``code-block`` directive) :doctest-block: doctest block :raw: raw content - :image: image/figure uri and alt + :image: image/figure uri For example: ``gettext_additional_targets = ['literal-block', 'image']``. The default is ``[]``. .. versionadded:: 1.3 + .. versionchanged:: 3.2 + + The alt text for image is translated by default. .. confval:: figure_language_filename diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index 34d5b13687d..eeeb3cb4f3a 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -238,6 +238,10 @@ def apply(self, **kwargs: Any) -> None: node.details['nodes'][0]['content'] = msgstr continue + if isinstance(node, nodes.image) and node.get('alt') == msg: + node['alt'] = msgstr + continue + # Avoid "Literal block expected; none found." warnings. # If msgstr ends with '::' then it cause warning message at # parser.parse() processing. @@ -441,8 +445,9 @@ def get_ref_key(node: addnodes.pending_xref) -> Tuple[str, str, str]: if isinstance(node, LITERAL_TYPE_NODES): node.rawsource = node.astext() - if isinstance(node, IMAGE_TYPE_NODES): - node.update_all_atts(patch) + if isinstance(node, nodes.image) and node.get('alt') != msg: + node['uri'] = patch['uri'] + continue # do not mark translated node['translated'] = True # to avoid double translation diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index b4d796f6184..725f4037915 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -201,6 +201,10 @@ def is_translatable(node: Node) -> bool: if isinstance(node, addnodes.translatable): return True + # image node marked as translatable or having alt text + if isinstance(node, nodes.image) and (node.get('translatable') or node.get('alt')): + return True + if isinstance(node, nodes.Inline) and 'translatable' not in node: # type: ignore # inline node must not be translated if 'translatable' is not set return False @@ -228,9 +232,6 @@ def is_translatable(node: Node) -> bool: return False return True - if isinstance(node, nodes.image) and node.get('translatable'): - return True - if isinstance(node, addnodes.meta): return True if is_pending_meta(node): @@ -264,10 +265,13 @@ def extract_messages(doctree: Element) -> Iterable[Tuple[Element, str]]: msg = node.rawsource if not msg: msg = node.astext() - elif isinstance(node, IMAGE_TYPE_NODES): - msg = '.. image:: %s' % node['uri'] + elif isinstance(node, nodes.image): if node.get('alt'): - msg += '\n :alt: %s' % node['alt'] + yield node, node['alt'] + if node.get('translatable'): + msg = '.. image:: %s' % node['uri'] + else: + msg = None elif isinstance(node, META_TYPE_NODES): msg = node.rawcontent elif isinstance(node, nodes.pending) and is_pending_meta(node): diff --git a/tests/roots/test-intl/xx/LC_MESSAGES/figure.po b/tests/roots/test-intl/xx/LC_MESSAGES/figure.po index 449b15e3f3e..64bbdf763db 100644 --- a/tests/roots/test-intl/xx/LC_MESSAGES/figure.po +++ b/tests/roots/test-intl/xx/LC_MESSAGES/figure.po @@ -37,19 +37,17 @@ msgstr "BLOCK" msgid "image url and alt" msgstr "IMAGE URL AND ALT" -msgid "" -".. image:: img.png\n" -" :alt: img" -msgstr "" -".. image:: i18n.png\n" -" :alt: IMG -> I18N" +msgid "img" +msgstr "IMG -> I18N" -msgid "" -".. image:: i18n.png\n" -" :alt: i18n" -msgstr "" -".. image:: img.png\n" -" :alt: I18N -> IMG" +msgid ".. image:: img.png" +msgstr ".. image:: i18n.png" + +msgid "i18n" +msgstr "I18N -> IMG" + +msgid ".. image:: i18n.png" +msgstr ".. image:: img.png" msgid "image on substitution" msgstr "IMAGE ON SUBSTITUTION" diff --git a/tests/test_intl.py b/tests/test_intl.py index d0c64b589df..d9701343ef9 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -345,9 +345,9 @@ def test_text_figure_captions(app): "14.2. IMAGE URL AND ALT\n" "=======================\n" "\n" - "[image: i18n][image]\n" + "[image: I18N -> IMG][image]\n" "\n" - " [image: img][image]\n" + " [image: IMG -> I18N][image]\n" "\n" "\n" "14.3. IMAGE ON SUBSTITUTION\n" @@ -1102,12 +1102,12 @@ def test_additional_targets_should_not_be_translated(app): result = (app.outdir / 'figure.html').read_text() - # alt and src for image block should not be translated - expected_expr = """i18n""" + # src for image block should not be translated (alt is translated) + expected_expr = """I18N -> IMG""" assert_count(expected_expr, result, 1) - # alt and src for figure block should not be translated - expected_expr = """img""" + # src for figure block should not be translated (alt is translated) + expected_expr = """IMG -> I18N""" assert_count(expected_expr, result, 1)