translations.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. # Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com>
  2. # Permission is hereby granted, free of charge, to any person obtaining a copy
  3. # of this software and associated documentation files (the "Software"), to
  4. # deal in the Software without restriction, including without limitation the
  5. # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  6. # sell copies of the Software, and to permit persons to whom the Software is
  7. # furnished to do so, subject to the following conditions:
  8. # The above copyright notice and this permission notice shall be included in
  9. # all copies or substantial portions of the Software.
  10. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  11. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  12. # FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
  13. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  14. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  15. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  16. # IN THE SOFTWARE.
  17. import os
  18. import re
  19. from glob import iglob
  20. from mkdocs.config.defaults import MkDocsConfig
  21. from mkdocs.structure.pages import Page
  22. from urllib.parse import urlencode, urlparse
  23. # -----------------------------------------------------------------------------
  24. # Hooks
  25. # -----------------------------------------------------------------------------
  26. # Determine missing translations and render language overview in the setup
  27. # guide, including links to provide missing translations.
  28. def on_page_markdown(markdown: str, *, page: Page, config: MkDocsConfig, files):
  29. issue_url = "https://github.com/squidfunk/mkdocs-material/issues/new"
  30. if page.file.src_uri != "setup/changing-the-language.md":
  31. return
  32. # Collect all existing languages
  33. names: dict[str, str] = {}
  34. known: dict[str, dict[str, str]] = {}
  35. for path in iglob("src/templates/partials/languages/*.html"):
  36. with open(path, "r", encoding = "utf-8") as f:
  37. data = f.read()
  38. # Extract language code and name
  39. name, = re.findall(r"<!-- Translations: (.+) -->", data)
  40. code, _ = os.path.splitext(os.path.basename(path))
  41. # Map names and available translations
  42. names[code] = name
  43. known[code] = dict(re.findall(
  44. r"^ \"([^\"]+)\": \"([^\"]*)\"(?:,|$)?", data,
  45. re.MULTILINE
  46. ))
  47. # Remove technical stuff
  48. for key in [
  49. "direction",
  50. "search.config.pipeline",
  51. "search.config.lang",
  52. "search.config.separator"
  53. ]:
  54. if key in known[code]:
  55. del known[code][key]
  56. # Traverse all languages and compute missing translations
  57. languages = []
  58. reference = set(known["en"])
  59. for code, name in names.items():
  60. miss = reference - set(known[code])
  61. # Check each translations
  62. translations: list[str] = []
  63. for key, value in known["en"].items():
  64. if key in known[code]:
  65. translations.append(
  66. f" \"{key}\": \"{known[code][key]}\""
  67. )
  68. else:
  69. translations.append(
  70. f" \"{key}\": \"{value} ⬅️\""
  71. )
  72. # Assemble GitHub issue URL
  73. link = urlparse(issue_url)
  74. link = link._replace(query = urlencode({
  75. "template": "04-add-translations.yml",
  76. "title": f"Update {name} translations",
  77. "translations": "\n".join([
  78. "{% macro t(key) %}{{ {",
  79. ",\n".join(translations),
  80. "}[key] }}{% endmacro %}"
  81. ]),
  82. "country-flag": f":flag_{countries[code]}:"
  83. }))
  84. # Add translation
  85. languages.append({
  86. "flag": countries[code],
  87. "code": code,
  88. "name": name,
  89. "link": link.geturl(),
  90. "miss": miss
  91. })
  92. # Load template and render translations
  93. env = config.theme.get_env()
  94. template = env.get_template( "hooks/translations.html")
  95. translations = template.module.render(
  96. sorted(languages, key = lambda language: language["name"])
  97. )
  98. # Replace translation marker
  99. return markdown.replace(
  100. "<!-- hooks/translations.py -->", "\n".join(
  101. [line.lstrip() for line in translations.split("\n")
  102. ]
  103. ))
  104. # -----------------------------------------------------------------------------
  105. # Data
  106. # -----------------------------------------------------------------------------
  107. # Map ISO 639-1 (languages) to ISO 3166 (countries)
  108. countries = dict({
  109. "af": "za",
  110. "ar": "ae",
  111. "be": "by",
  112. "bg": "bg",
  113. "bn": "bd",
  114. "ca": "es",
  115. "cs": "cz",
  116. "da": "dk",
  117. "de": "de",
  118. "el": "gr",
  119. "en": "us",
  120. "eo": "eu",
  121. "es": "es",
  122. "et": "ee",
  123. "eu": "es",
  124. "fa": "ir",
  125. "fi": "fi",
  126. "fr": "fr",
  127. "gl": "es",
  128. "he": "il",
  129. "hi": "in",
  130. "hr": "hr",
  131. "hu": "hu",
  132. "hy": "am",
  133. "id": "id",
  134. "is": "is",
  135. "it": "it",
  136. "ja": "jp",
  137. "ka": "ge",
  138. "kn": "in",
  139. "ko": "kr",
  140. "ku-IQ": "iq",
  141. "lb": "lu",
  142. "lt": "lt",
  143. "lv": "lv",
  144. "mk": "mk",
  145. "mn": "mn",
  146. "ms": "my",
  147. "my": "mm",
  148. "nb": "no",
  149. "nl": "nl",
  150. "nn": "no",
  151. "pl": "pl",
  152. "pt-BR": "br",
  153. "pt": "pt",
  154. "ro": "ro",
  155. "ru": "ru",
  156. "sa": "in",
  157. "sh": "rs",
  158. "si": "lk",
  159. "sk": "sk",
  160. "sl": "si",
  161. "sr": "rs",
  162. "sv": "se",
  163. "te": "in",
  164. "th": "th",
  165. "tl": "ph",
  166. "tr": "tr",
  167. "uk": "ua",
  168. "ur": "pk",
  169. "uz": "uz",
  170. "vi": "vn",
  171. "zh": "cn",
  172. "zh-Hant": "cn",
  173. "zh-TW": "tw"
  174. })