🌎 Traslations
Introduction
To support multilingualism, PBModular includes a built-in translation system. Modules can provide translations for different languages, and the framework will automatically load and make them available based on the bot's global language configuration.
Step 1: Creating Translation Files
- Inside your module's directory, create a folder named
strings/
. - Inside
strings/
, create YAML files for each language you want to support. The filename must be the ISO 639-1 language code (e.g., en.yaml, ru.yaml, es.yaml). - Structure your translations within the YAML files using nested keys for organization.
Example strings/en.yaml:
# strings/en.yaml
greeting: "Hello, {user}!"
farewell: Goodbye, {user}!
errors:
not_found: "Sorry, the item could not be found."
permission_denied: |
You do not have permission to do that.
{user}
Step 2: Loading Translations
During module initialization, the BaseModule
automatically:
- Scans the
strings/
directory. - Loads all valid
.yaml
files found into a dictionary accessible viaself.rawS
. The keys are the language codes (e.g.,self.rawS['en']
,self.rawS['ru']
). - Determines the language to use based on the bot's global configuration (
config.language
andconfig.fallback_language
).- It first tries
config.language
. - If not found in
self.rawS
, it triesconfig.fallback_language
. - If the fallback is also not found, it logs a warning and uses the first language loaded into
self.rawS
.
- It first tries
- Loads the selected language's translations (potentially merged with the fallback language for missing keys) into
self.S
. Thisself.S
object is aSafeDict
, meaning accessing a non-existent key returns the key itself instead of raising aKeyError
. - Stores the code of the currently active language in
self.cur_lang
.
Merging with Fallback
If both the primary language (config.language
) and the fallback language (config.fallback_language
) exist in your module's strings/ folder, the framework creates a merged dictionary for self.S
. It starts with a copy of the fallback language's strings and then deeply merges the primary language's strings on top. This means any string present in the primary language overrides the fallback, but any string missing in the primary but present in the fallback will still be available.
Step 3: Using Translations in Code
Access translated strings through the self.S
dictionary. Use standard Python string formatting (.format()
or f-strings) for inserting variables.
# Inside a BaseModule or ModuleExtension method
async def greet_user(self, message: Message):
user_name = message.from_user.first_name
# Accessing a simple string
await message.reply(self.S["greeting"].format(user=user_name))
async def show_help(self, message: Message):
help_title = self.S["help"]["title"] # Accessing nested keys
command_name = "mycommand"
usage_text = self.S["help"]["usage"].format(command=command_name)
await message.reply(f"**{help_title}**\n\n{usage_text}")
async def handle_error(self, message: Message, error_key: str):
# Using SafeDict property: if key missing, key itself is returned
error_message = self.S["errors"][error_key]
await message.reply(f"Error: {error_message}")
# Example accessing raw translations or current lang
def log_languages(self):
self.logger.info(f"Current language code: {self.cur_lang}")
self.logger.info(f"Available language codes: {list(self.rawS.keys())}")
# Access specific language directly (less common)
# english_farewell = self.rawS.get("en", {}).get("farewell", "Fallback Farewell")
Using this system ensures your module can easily adapt to different languages configured in the bot, providing a better user experience.