translations.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. # ©️ Dan Gazizullin, 2021-2023
  2. # This file is a part of Hikka Userbot
  3. # 🌐 https://github.com/hikariatama/Hikka
  4. # You can redistribute it and/or modify it under the terms of the GNU AGPLv3
  5. # 🔑 https://www.gnu.org/licenses/agpl-3.0.html
  6. import json
  7. import logging
  8. import os
  9. import typing
  10. import requests
  11. from . import utils
  12. from .database import Database
  13. from .tl_cache import CustomTelegramClient
  14. from .types import Module
  15. logger = logging.getLogger(__name__)
  16. class Translator:
  17. def __init__(self, client: CustomTelegramClient, db: Database):
  18. self._client = client
  19. self.db = db
  20. async def init(self) -> bool:
  21. self._data = {}
  22. if not (lang := self.db.get(__name__, "lang", False)):
  23. return False
  24. for language in lang.split(" "):
  25. if utils.check_url(language):
  26. try:
  27. ndata = (await utils.run_sync(requests.get, lang)).json()
  28. except Exception:
  29. logger.exception("Unable to decode %s", lang)
  30. continue
  31. data = ndata.get("data", ndata)
  32. if any(not isinstance(i, str) for i in data.values()):
  33. logger.exception(
  34. "Translation pack format is not valid (typecheck failed)"
  35. )
  36. continue
  37. self._data.update(data)
  38. continue
  39. possible_pack_path = os.path.join(
  40. utils.get_base_dir(),
  41. f"langpacks/{language}.json",
  42. )
  43. if os.path.isfile(possible_pack_path):
  44. with open(possible_pack_path, "r") as f:
  45. self._data.update(json.load(f))
  46. return True
  47. return True
  48. def getkey(self, key: str) -> typing.Any:
  49. return self._data.get(key, False)
  50. def gettext(self, text: str) -> typing.Any:
  51. return self.getkey(text) or text
  52. class Strings:
  53. def __init__(self, mod: Module, translator: Translator):
  54. self._mod = mod
  55. self._translator = translator
  56. if not translator:
  57. logger.debug("Module %s got empty translator %s", mod, translator)
  58. self._base_strings = mod.strings # Back 'em up, bc they will get replaced
  59. def __getitem__(self, key: str) -> str:
  60. return (
  61. self._translator.getkey(f"{self._mod.__module__}.{key}")
  62. if self._translator is not None
  63. else False
  64. ) or (
  65. getattr(
  66. self._mod,
  67. next(
  68. (
  69. f"strings_{lang}"
  70. for lang in self._translator.db.get(
  71. __name__,
  72. "lang",
  73. "en",
  74. ).split(" ")
  75. if hasattr(self._mod, f"strings_{lang}")
  76. and isinstance(getattr(self._mod, f"strings_{lang}"), dict)
  77. and key in getattr(self._mod, f"strings_{lang}")
  78. ),
  79. utils.rand(32),
  80. ),
  81. self._base_strings,
  82. )
  83. if self._translator is not None
  84. else self._base_strings
  85. ).get(
  86. key,
  87. self._base_strings.get(key, "Unknown strings"),
  88. )
  89. def __call__(
  90. self,
  91. key: str,
  92. _: typing.Optional[typing.Any] = None, # Compatibility tweak for FTG\GeekTG
  93. ) -> str:
  94. return self.__getitem__(key)
  95. def __iter__(self):
  96. return self._base_strings.__iter__()