From fd1d0c28c26eaf7f3da59d322099cdc012dd73e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Verschelde?= Date: Wed, 22 Dec 2021 16:35:08 +0100 Subject: [PATCH] i18n: Make more `make_rst.py` strings translatable For now we leave out the strings which could break rst table formatting. --- doc/tools/make_rst.py | 72 +++++++++++++++++++++++++------------ doc/translations/extract.py | 19 ++++++++-- 2 files changed, 65 insertions(+), 26 deletions(-) diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 3b965f0dba6..6276c448449 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -19,10 +19,11 @@ GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") MARKUP_ALLOWED_PRECEDENT = " -:/'\"<([{" 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", "Tutorials", "Properties", @@ -38,8 +39,21 @@ HEADINGS = [ "Method Descriptions", "Operator 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 @@ -408,13 +422,13 @@ def main(): # type: () -> None try: import polib except ImportError: - print("Section heading localization requires `polib`.") + print("Base template strings localization requires `polib`.") exit(1) pofile = polib.pofile(lang_file) 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: print("No PO file at '{}' for language '{}'.".format(lang_file, args.lang)) @@ -494,6 +508,14 @@ def main(): # type: () -> None 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 class_name = class_def.name @@ -515,7 +537,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S # Ascendants if class_def.inherits: inh = class_def.inherits.strip() - f.write("**Inherits:** ") + f.write("**" + translate("Inherits:") + "** ") first = True while inh in state.classes: 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) if len(inherited): - f.write("**Inherited By:** ") + f.write("**" + translate("Inherited By:") + "** ") for i, child in enumerate(inherited): if i > 0: 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 if default is not None and property_def.overrides: ref = ":ref:`{1}`".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: ref = ":ref:`{0}`".format(property_def.name, class_name) 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)) info = [] + # Not using translate() for now as it breaks table formatting. 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("_"): - 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("_"): - info.append(("*Getter*", property_def.getter + "()")) + info.append(("*" + "Getter" + "*", property_def.getter + "()")) if len(info) > 0: format_table(f, info) @@ -781,7 +805,8 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S info = [] 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: format_table(f, info) @@ -1305,8 +1330,9 @@ def make_method_signature( def make_heading(title, underline, l10n=True): # type: (str, str, bool) -> str 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. return title + "\n" + (underline * len(title)) + "\n\n" @@ -1316,12 +1342,12 @@ def make_footer(): # type: () -> str # This way, we avoid bloating the generated rST with duplicate abbreviations. # fmt: off 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 diff --git a/doc/translations/extract.py b/doc/translations/extract.py index 214a756e64a..55eaeb50022 100644 --- a/doc/translations/extract.py +++ b/doc/translations/extract.py @@ -26,7 +26,7 @@ msgstr "" """ # 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. -HEADINGS = [ +BASE_STRINGS = [ "Description", "Tutorials", "Properties", @@ -42,6 +42,19 @@ HEADINGS = [ "Method Descriptions", "Operator 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.", ] ## @@ -229,12 +242,12 @@ def _make_translation_catalog(classes): def _generate_translation_catalog_file(unique_msgs, output, location_line=False): with open(output, "w", encoding="utf8") as f: f.write(HEADER) - for msg in HEADINGS: + for msg in BASE_STRINGS: f.write("#: doc/tools/make_rst.py\n") f.write('msgid "{}"\n'.format(msg)) f.write('msgstr ""\n\n') for msg in unique_msgs: - if len(msg) == 0 or msg in HEADINGS: + if len(msg) == 0 or msg in BASE_STRINGS: continue f.write("#:")