class_docstr: Replace translatable nodes with their translated doctree.
class_docstr_tok: ["Replace","translatable","nodes","with","their","translated","doctree","."]
code: def apply(self, **kwargs: Any) -> None:
      settings, source = self.document.settings, self.document["source"]
      msgstr = ""
  
      textdomain = docname_to_domain(
          self.env.docname, self.config.gettext_compact
      )
  
      # fetch translations
      dirs = [
          path.join(self.env.srcdir, directory)
          for directory in self.config.locale_dirs
      ]
      catalog, has_catalog = init_locale(
          dirs, self.config.language, textdomain
      )
      if not has_catalog:
          return
  
      catalogues = [getattr(catalog, "_catalog", None)]
      while (catalog := catalog._fallback) is not None:  # type: ignore[attr-defined]
          catalogues.append(getattr(catalog, "_catalog", None))
      merged: dict[str, str] = {}
      for catalogue in filter(
          None, reversed(catalogues)
      ):  # type: dict[str, str]
          merged |= catalogue
  
      # phase1: replace reference ids with translated names
      for node, msg in extract_messages(self.document):
          msgstr = merged.get(msg, "")
  
          # There is no point in having #noqa on literal blocks because
          # they cannot contain references.  Recognizing it would just
          # completely prevent escaping the #noqa.  Outside of literal
          # blocks, one can always write \#noqa.
          if not isinstance(node, LITERAL_TYPE_NODES):
              msgstr, _ = parse_noqa(msgstr)
  
          if msgstr.strip() == "":
              # as-of-yet untranslated
              node["translated"] = False
              continue
          if msgstr == msg:
              # identical source and translated messages
              node["translated"] = True
              continue
  
          # Avoid "Literal block expected; none found." warnings.
          # If msgstr ends with '::' then it cause warning message at
          # parser.parse() processing.
          # literal-block-warning is only appear in avobe case.
          if msgstr.strip().endswith("::"):
              msgstr += "\n\n   dummy literal"
              # dummy literal node will discard by 'patch = patch[0]'
  
          # literalblock need literal block notation to avoid it become
          # paragraph.
          if isinstance(node, LITERAL_TYPE_NODES):
              msgstr = "::\n\n" + indent(msgstr, " " * 3)
  
          patch = publish_msgstr(
              self.app, msgstr, source, node.line, self.config, settings
          )
          # FIXME: no warnings about inconsistent references in this part
          # XXX doctest and other block markup
          if not isinstance(patch, nodes.paragraph):
              continue  # skip for now
  
          updater = _NodeUpdater(node, patch, self.document, noqa=False)
          processed = updater.update_title_mapping()
  
          # glossary terms update refid
          if isinstance(node, nodes.term):
              for _id in node["ids"]:
                  parts = split_term_classifiers(msgstr)
                  patch = publish_msgstr(
                      self.app,
                      parts[0] or "",
                      source,
                      node.line,
                      self.config,
                      settings,
                  )
                  updater.patch = make_glossary_term(
                      self.env,
                      patch,
                      parts[1] or "",
                      source,
                      node.line,
                      _id,
                      self.document,
                  )
                  processed = True
  
          # update leaves with processed nodes
          if processed:
              updater.update_leaves()
              node["translated"] = True  # to avoid double translation
          else:
              node["translated"] = False
  
      # phase2: translation
      for node, msg in extract_messages(self.document):
          if node.setdefault(
              "translated", False
          ):  # to avoid double translation
              continue  # skip if the node is already translated by phase1
  
          msgstr = merged.get(msg, "")
          noqa = False
  
          # See above.
          if not isinstance(node, LITERAL_TYPE_NODES):
              msgstr, noqa = parse_noqa(msgstr)
  
          if not msgstr or msgstr == msg:  # as-of-yet untranslated
              node["translated"] = False
              continue
  
          # update translatable nodes
          if isinstance(node, addnodes.translatable):
              node.apply_translated_message(msg, msgstr)  # type: ignore[attr-defined]
              continue
  
          # update meta nodes
          if isinstance(node, nodes.meta):  # type: ignore[attr-defined]
              node["content"] = msgstr
              node["translated"] = True
              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.
          # literal-block-warning is only appear in avobe case.
          if msgstr.strip().endswith("::"):
              msgstr += "\n\n   dummy literal"
              # dummy literal node will discard by 'patch = patch[0]'
  
          # literalblock need literal block notation to avoid it become
          # paragraph.
          if isinstance(node, LITERAL_TYPE_NODES):
              msgstr = "::\n\n" + indent(msgstr, " " * 3)
  
          # Structural Subelements phase1
          # There is a possibility that only the title node is created.
          # see: https://docutils.sourceforge.io/docs/ref/doctree.html#structural-subelements
          if isinstance(node, nodes.title):
              # This generates: <section ...><title>msgstr</title></section>
              msgstr = msgstr + "\n" + "=" * len(msgstr) * 2
  
          patch = publish_msgstr(
              self.app, msgstr, source, node.line, self.config, settings
          )
          # Structural Subelements phase2
          if isinstance(node, nodes.title):
              # get <title> node that placed as a first child
              patch = patch.next_node()
  
          # ignore unexpected markups in translation message
          unexpected: tuple[type[nodes.Element], ...] = (
              nodes.paragraph,  # expected form of translation
              nodes.title,  # generated by above "Subelements phase2"
          )
  
          # following types are expected if
          # config.gettext_additional_targets is configured
          unexpected += LITERAL_TYPE_NODES
          unexpected += IMAGE_TYPE_NODES
  
          if not isinstance(patch, unexpected):
              continue  # skip
  
          updater = _NodeUpdater(node, patch, self.document, noqa)
          updater.update_autofootnote_references()
          updater.update_refnamed_references()
          updater.update_refnamed_footnote_references()
          updater.update_citation_references()
          updater.update_pending_xrefs()
          updater.update_leaves()
  
          # for highlighting that expects .rawsource and .astext() are same.
          if isinstance(node, LITERAL_TYPE_NODES):
              node.rawsource = node.astext()
  
          if isinstance(node, nodes.image) and node.get("alt") != msg:
              node["uri"] = patch["uri"]
              node["translated"] = False
              continue  # do not mark translated
  
          node["translated"] = True  # to avoid double translation
  
      if "index" in self.config.gettext_additional_targets:
          # Extract and translate messages for index entries.
          for node, entries in traverse_translatable_index(
              self.document
          ):
              new_entries: list[
                  tuple[str, str, str, str, str | None]
              ] = []
              for (
                  entry_type,
                  value,
                  target_id,
                  main,
                  _category_key,
              ) in entries:
                  msg_parts = split_index_msg(entry_type, value)
                  msgstr_parts = []
                  for part in msg_parts:
                      msgstr = merged.get(part, "")
                      if not msgstr:
                          msgstr = part
                      msgstr_parts.append(msgstr)
  
                  new_entry = (
                      entry_type,
                      ";".join(msgstr_parts),
                      target_id,
                      main,
                      None,
                  )
                  new_entries.append(new_entry)
  
              node["raw_entries"] = entries
              node["entries"] = new_entries
  
imports: from __future__ import annotations
  import contextlib
  from os import path
  from re import DOTALL, match
  from textwrap import indent
  from typing import TYPE_CHECKING, Any, TypeVar
  from docutils import nodes
  from docutils.io import StringInput
  from sphinx import addnodes
  from sphinx.domains.std import make_glossary_term, split_term_classifiers
  from sphinx.errors import ConfigError
  from sphinx.locale import __
  from sphinx.locale import init
  from sphinx.transforms import SphinxTransform
  from sphinx.util import get_filetype, logging
  from sphinx.util.i18n import docname_to_domain
  from sphinx.util.index_entries import split_index_msg
  from sphinx.util.nodes import IMAGE_TYPE_NODES, LITERAL_TYPE_NODES, NodeMatcher, extract_messages, traverse_translatable_index
label_desc: Use image node_id 1 for calling the Locale obj's underlying member method code with example usage: obj.apply() without return types