Browse Source

Merge pull request #56162 from akien-mga/i18n-make_rst-more-translatable

i18n: Make more `make_rst.py` strings translatable
Rémi Verschelde 3 years ago
parent
commit
0905f81c0a
2 changed files with 65 additions and 26 deletions
  1. 49 23
      doc/tools/make_rst.py
  2. 16 3
      doc/translations/extract.py

+ 49 - 23
doc/tools/make_rst.py

@@ -19,10 +19,11 @@ GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$")
 MARKUP_ALLOWED_PRECEDENT = " -:/'\"<([{"
 MARKUP_ALLOWED_PRECEDENT = " -:/'\"<([{"
 MARKUP_ALLOWED_SUBSEQUENT = " -.,:;!?\\/'\")]}>"
 MARKUP_ALLOWED_SUBSEQUENT = " -.,:;!?\\/'\")]}>"
 
 
-# Used to translate the section headings when required with --lang argument.
-# The HEADINGS list should be synced with what we actually write with `make_heading`,
-# and also hardcoded in `doc/translations/extract.py`.
-HEADINGS = [
+# Used to translate section headings and other hardcoded strings when required with
+# the --lang argument. The BASE_STRINGS list should be synced with what we actually
+# write in this script (check `translate()` uses), and also hardcoded in
+# `doc/translations/extract.py` to include them in the source POT file.
+BASE_STRINGS = [
     "Description",
     "Description",
     "Tutorials",
     "Tutorials",
     "Properties",
     "Properties",
@@ -38,8 +39,21 @@ HEADINGS = [
     "Method Descriptions",
     "Method Descriptions",
     "Operator Descriptions",
     "Operator Descriptions",
     "Theme Property Descriptions",
     "Theme Property Descriptions",
+    "Inherits:",
+    "Inherited By:",
+    "(overrides %s)",
+    "Default",
+    "Setter",
+    "value",
+    "Getter",
+    "This method should typically be overridden by the user to have any effect.",
+    "This method has no side effects. It doesn't modify any of the instance's member variables.",
+    "This method accepts any number of arguments after the ones described here.",
+    "This method is used to construct a type.",
+    "This method doesn't need an instance to be called, so it can be called directly using the class name.",
+    "This method describes a valid operator to use with this type as left-hand operand.",
 ]
 ]
-headings_l10n = {}
+strings_l10n = {}
 
 
 
 
 def print_error(error, state):  # type: (str, State) -> None
 def print_error(error, state):  # type: (str, State) -> None
@@ -408,13 +422,13 @@ def main():  # type: () -> None
             try:
             try:
                 import polib
                 import polib
             except ImportError:
             except ImportError:
-                print("Section heading localization requires `polib`.")
+                print("Base template strings localization requires `polib`.")
                 exit(1)
                 exit(1)
 
 
             pofile = polib.pofile(lang_file)
             pofile = polib.pofile(lang_file)
             for entry in pofile.translated_entries():
             for entry in pofile.translated_entries():
-                if entry.msgid in HEADINGS:
-                    headings_l10n[entry.msgid] = entry.msgstr
+                if entry.msgid in BASE_STRINGS:
+                    strings_l10n[entry.msgid] = entry.msgstr
         else:
         else:
             print("No PO file at '{}' for language '{}'.".format(lang_file, args.lang))
             print("No PO file at '{}' for language '{}'.".format(lang_file, args.lang))
 
 
@@ -494,6 +508,14 @@ def main():  # type: () -> None
         exit(1)
         exit(1)
 
 
 
 
+def translate(string):  # type: (str) -> str
+    """Translate a string based on translations sourced from `doc/translations/*.po`
+    for a language if defined via the --lang command line argument.
+    Returns the original string if no translation exists.
+    """
+    return strings_l10n.get(string, string)
+
+
 def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, State, bool, str) -> None
 def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, State, bool, str) -> None
     class_name = class_def.name
     class_name = class_def.name
 
 
@@ -515,7 +537,7 @@ def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, S
     # Ascendants
     # Ascendants
     if class_def.inherits:
     if class_def.inherits:
         inh = class_def.inherits.strip()
         inh = class_def.inherits.strip()
-        f.write("**Inherits:** ")
+        f.write("**" + translate("Inherits:") + "** ")
         first = True
         first = True
         while inh in state.classes:
         while inh in state.classes:
             if not first:
             if not first:
@@ -538,7 +560,7 @@ def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, S
             inherited.append(c.name)
             inherited.append(c.name)
 
 
     if len(inherited):
     if len(inherited):
-        f.write("**Inherited By:** ")
+        f.write("**" + translate("Inherited By:") + "** ")
         for i, child in enumerate(inherited):
         for i, child in enumerate(inherited):
             if i > 0:
             if i > 0:
                 f.write(", ")
                 f.write(", ")
@@ -569,7 +591,8 @@ def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, S
             default = property_def.default_value
             default = property_def.default_value
             if default is not None and property_def.overrides:
             if default is not None and property_def.overrides:
                 ref = ":ref:`{1}<class_{1}_property_{0}>`".format(property_def.name, property_def.overrides)
                 ref = ":ref:`{1}<class_{1}_property_{0}>`".format(property_def.name, property_def.overrides)
-                ml.append((type_rst, property_def.name, default + " (overrides " + ref + ")"))
+                # Not using translate() for now as it breaks table formatting.
+                ml.append((type_rst, property_def.name, default + " " + "(overrides %s)" % ref))
             else:
             else:
                 ref = ":ref:`{0}<class_{1}_property_{0}>`".format(property_def.name, class_name)
                 ref = ":ref:`{0}<class_{1}_property_{0}>`".format(property_def.name, class_name)
                 ml.append((type_rst, ref, default))
                 ml.append((type_rst, ref, default))
@@ -687,12 +710,13 @@ def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, S
             f.write("- {} **{}**\n\n".format(property_def.type_name.to_rst(state), property_def.name))
             f.write("- {} **{}**\n\n".format(property_def.type_name.to_rst(state), property_def.name))
 
 
             info = []
             info = []
+            # Not using translate() for now as it breaks table formatting.
             if property_def.default_value is not None:
             if property_def.default_value is not None:
-                info.append(("*Default*", property_def.default_value))
+                info.append(("*" + "Default" + "*", property_def.default_value))
             if property_def.setter is not None and not property_def.setter.startswith("_"):
             if property_def.setter is not None and not property_def.setter.startswith("_"):
-                info.append(("*Setter*", property_def.setter + "(value)"))
+                info.append(("*" + "Setter" + "*", property_def.setter + "(" + "value" + ")"))
             if property_def.getter is not None and not property_def.getter.startswith("_"):
             if property_def.getter is not None and not property_def.getter.startswith("_"):
-                info.append(("*Getter*", property_def.getter + "()"))
+                info.append(("*" + "Getter" + "*", property_def.getter + "()"))
 
 
             if len(info) > 0:
             if len(info) > 0:
                 format_table(f, info)
                 format_table(f, info)
@@ -781,7 +805,8 @@ def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, S
 
 
             info = []
             info = []
             if theme_item_def.default_value is not None:
             if theme_item_def.default_value is not None:
-                info.append(("*Default*", theme_item_def.default_value))
+                # Not using translate() for now as it breaks table formatting.
+                info.append(("*" + "Default" + "*", theme_item_def.default_value))
 
 
             if len(info) > 0:
             if len(info) > 0:
                 format_table(f, info)
                 format_table(f, info)
@@ -1306,8 +1331,9 @@ def make_method_signature(
 
 
 def make_heading(title, underline, l10n=True):  # type: (str, str, bool) -> str
 def make_heading(title, underline, l10n=True):  # type: (str, str, bool) -> str
     if l10n:
     if l10n:
-        if title in headings_l10n:
-            title = headings_l10n.get(title)
+        new_title = translate(title)
+        if new_title != title:
+            title = new_title
             underline *= 2  # Double length to handle wide chars.
             underline *= 2  # Double length to handle wide chars.
     return title + "\n" + (underline * len(title)) + "\n\n"
     return title + "\n" + (underline * len(title)) + "\n\n"
 
 
@@ -1317,12 +1343,12 @@ def make_footer():  # type: () -> str
     # This way, we avoid bloating the generated rST with duplicate abbreviations.
     # This way, we avoid bloating the generated rST with duplicate abbreviations.
     # fmt: off
     # fmt: off
     return (
     return (
-        ".. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n"
-        ".. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n"
-        ".. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n"
-        ".. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n"
-        ".. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n"
-        ".. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n"
+        ".. |virtual| replace:: :abbr:`virtual (" + translate("This method should typically be overridden by the user to have any effect.") + ")`\n"
+        ".. |const| replace:: :abbr:`const (" + translate("This method has no side effects. It doesn't modify any of the instance's member variables.") + ")`\n"
+        ".. |vararg| replace:: :abbr:`vararg (" + translate("This method accepts any number of arguments after the ones described here.") + ")`\n"
+        ".. |constructor| replace:: :abbr:`constructor (" + translate("This method is used to construct a type.") + ")`\n"
+        ".. |static| replace:: :abbr:`static (" + translate("This method doesn't need an instance to be called, so it can be called directly using the class name.") + ")`\n"
+        ".. |operator| replace:: :abbr:`operator (" + translate("This method describes a valid operator to use with this type as left-hand operand.") + ")`\n"
     )
     )
     # fmt: on
     # fmt: on
 
 

+ 16 - 3
doc/translations/extract.py

@@ -26,7 +26,7 @@ msgstr ""
 """
 """
 # Some strings used by make_rst.py are normally part of the editor translations,
 # Some strings used by make_rst.py are normally part of the editor translations,
 # so we need to include them manually here for the online docs.
 # so we need to include them manually here for the online docs.
-HEADINGS = [
+BASE_STRINGS = [
     "Description",
     "Description",
     "Tutorials",
     "Tutorials",
     "Properties",
     "Properties",
@@ -42,6 +42,19 @@ HEADINGS = [
     "Method Descriptions",
     "Method Descriptions",
     "Operator Descriptions",
     "Operator Descriptions",
     "Theme Property Descriptions",
     "Theme Property Descriptions",
+    "Inherits:",
+    "Inherited By:",
+    "(overrides %s)",
+    "Default",
+    "Setter",
+    "value",
+    "Getter",
+    "This method should typically be overridden by the user to have any effect.",
+    "This method has no side effects. It doesn't modify any of the instance's member variables.",
+    "This method accepts any number of arguments after the ones described here.",
+    "This method is used to construct a type.",
+    "This method doesn't need an instance to be called, so it can be called directly using the class name.",
+    "This method describes a valid operator to use with this type as left-hand operand.",
 ]
 ]
 
 
 ## <xml-line-number-hack from="https://stackoverflow.com/a/36430270/10846399">
 ## <xml-line-number-hack from="https://stackoverflow.com/a/36430270/10846399">
@@ -229,12 +242,12 @@ def _make_translation_catalog(classes):
 def _generate_translation_catalog_file(unique_msgs, output, location_line=False):
 def _generate_translation_catalog_file(unique_msgs, output, location_line=False):
     with open(output, "w", encoding="utf8") as f:
     with open(output, "w", encoding="utf8") as f:
         f.write(HEADER)
         f.write(HEADER)
-        for msg in HEADINGS:
+        for msg in BASE_STRINGS:
             f.write("#: doc/tools/make_rst.py\n")
             f.write("#: doc/tools/make_rst.py\n")
             f.write('msgid "{}"\n'.format(msg))
             f.write('msgid "{}"\n'.format(msg))
             f.write('msgstr ""\n\n')
             f.write('msgstr ""\n\n')
         for msg in unique_msgs:
         for msg in unique_msgs:
-            if len(msg) == 0 or msg in HEADINGS:
+            if len(msg) == 0 or msg in BASE_STRINGS:
                 continue
                 continue
 
 
             f.write("#:")
             f.write("#:")