diff --git a/config/template_config.ini b/config/template_config.ini index 6f63b8e..0545ac4 100644 --- a/config/template_config.ini +++ b/config/template_config.ini @@ -8,14 +8,20 @@ version = 18 ; Set this to no when you are done editing the file is_template = yes -; Language code for string file -; Uses the https://en.wikipedia.org/wiki/IETF_language_tag name of the language + +# Language parameters +[Language] ; Available languages: -; it - Italian, by Steffo -; en - English, by https://github.com/DarrenWestwood (incomplete, please improve it!) +; it - Italian, by https://github.com/Steffo99 +; en - English, by https://github.com/DarrenWestwood ; uk - Ukrainian, by https://github.com/pzhuk ; ru - Russian, by https://github.com/pzhuk -language = it +; The lanugages that messages can be displayed in +enabled_languages = it | en | uk | ru +; The default language to be set for users whose language cannot be autodetected or whose language is not enabled +default_language = it +; The language to fallback to if a string is missing in a specific language +fallback_language = en # Telegram bot parameters [Telegram] diff --git a/core.py b/core.py index fb4a8e0..a318191 100644 --- a/core.py +++ b/core.py @@ -47,7 +47,8 @@ def main(): # Finding default language default_language = configloader.config["Config"]["language"] - default_loc = localization.Localization(default_language) + # Creating localization object + default_loc = localization.Localization(language=default_language, fallback=default_language) # Create a dictionary linking the chat ids to the Worker objects # {"1234": } diff --git a/database.py b/database.py index 90e0128..0745056 100644 --- a/database.py +++ b/database.py @@ -8,6 +8,9 @@ import telegram import requests import utils import importlib +import logging + +log = logging.getLogger(__name__) language = configloader.config["Config"]["language"] strings = importlib.import_module("strings." + language) @@ -31,6 +34,7 @@ class User(TableDeclarativeBase): first_name = Column(String, nullable=False) last_name = Column(String) username = Column(String) + language = Column(String, nullable=False) # Current wallet credit credit = Column(Integer, nullable=False) @@ -38,14 +42,15 @@ class User(TableDeclarativeBase): # Extra table parameters __tablename__ = "users" - def __init__(self, telegram_chat: telegram.Chat, **kwargs): + def __init__(self, telegram_user: telegram.User, **kwargs): # Initialize the super super().__init__(**kwargs) # Get the data from telegram - self.user_id = telegram_chat.id - self.first_name = telegram_chat.first_name - self.last_name = telegram_chat.last_name - self.username = telegram_chat.username + self.user_id = telegram_user.id + self.first_name = telegram_user.first_name + self.last_name = telegram_user.last_name + self.username = telegram_user.username + self.language = telegram_user.language_code if telegram_user.language_code else configloader.config["Language"]["default_language"] # The starting wallet value is 0 self.credit = 0 diff --git a/localization.py b/localization.py index 84f3516..ee02f87 100644 --- a/localization.py +++ b/localization.py @@ -1,4 +1,10 @@ +from typing import * import importlib +import types +import logging + + +log = logging.getLogger(__name__) class IgnoreDict(dict): @@ -8,22 +14,31 @@ class IgnoreDict(dict): class Localization: - def __init__(self, language, replacements=None): - self.language = language - self.module = importlib.import_module("strings." + language) - self.replacements = replacements if replacements else {} - - @staticmethod - def is_supported(language) -> bool: - try: - importlib.import_module("strings." + language) - except ImportError: - return False + def __init__(self, language: str, *, fallback: str, replacements: Dict[str, str] = None): + log.debug(f"Creating localization for {language}") + self.language: str = language + log.debug(f"Importing strings.{language}") + self.module: types.ModuleType = importlib.import_module(f"strings.{language}") + if language != fallback: + log.debug(f"Importing strings.{fallback} as fallback") + self.fallback_language: str = fallback + self.fallback_module = importlib.import_module(f"strings.{fallback}") if fallback else None else: - return True + log.debug("Language is the same as the default, not importing any fallback") + self.fallback_language = None + self.fallback_module = None + self.replacements: Dict[str, str] = replacements if replacements else {} - def get(self, key, **kwargs) -> str: - string = self.module.__getattribute__(key) + def get(self, key: str, **kwargs) -> str: + try: + log.debug(f"Getting localized string with key {key}") + string = self.module.__getattribute__(key) + except AttributeError: + if self.fallback_module: + log.warning(f"Missing localized string with key {key}, using default") + string = self.fallback_module.__getattribute__(key) + else: + raise assert isinstance(string, str) formatter = IgnoreDict(**self.replacements, **kwargs) return string.format_map(formatter) diff --git a/worker.py b/worker.py index afaf5b3..cc44590 100644 --- a/worker.py +++ b/worker.py @@ -78,7 +78,7 @@ class Worker(threading.Thread): # Check if there are other registered users: if there aren't any, the first user will be owner of the bot will_be_owner = (self.session.query(db.Admin).first() is None) # Create the new record - self.user = db.User(self.chat) + self.user = db.User(self.telegram_user) # Add the new record to the db self.session.add(self.user) # Flush the session to get an userid @@ -100,20 +100,15 @@ class Worker(threading.Thread): log.info(f"Created new user: {self.user}") if will_be_owner: log.warning(f"User was auto-promoted to Admin as no other admins existed: {self.user}") - # Detect language from Telegram metadata - default_language = configloader.config["Config"]["language"] - language = self.telegram_user.language_code - if language: - log.debug(f"Detected language: {language}") - if not localization.Localization.is_supported(language): - log.debug(f"Unsupported language, using default: {default_language}") - language = default_language - else: - log.debug(f"No language detected, using default: {default_language}") - language = default_language + # Check if the user's language is enabled; if it isn't, change it to the default + if self.user.language not in configloader.config["Language"]["enabled_languages"]: + log.debug(f"User's language '{self.user.language}' is not enabled, changing it to the default") + self.user.language = configloader.config["Language"]["default_language"] + self.session.commit() # Create a Localization object self.loc = localization.Localization( - language=language, + language=self.user.language, + fallback=configloader.config["Language"]["fallback_language"], replacements={ "user_string": str(self.user), "user_mention": self.user.mention(),