Extended Translation Handler with configurable settings and colored logging for Discord bots.
- One-Time Configuration: Set settings once, use everywhere
- Flexible Path: Configure your own translations folder
- Customizable Fallbacks: Configure multiple fallback languages
- Cache Control: Individually set TTL (Time-to-Live)
- Colorama Integration: Color-coded log outputs
- Multiple Log Levels: DEBUG, INFO, WARNING, ERROR
- Automatic Fallback: Works without Colorama too
- Clear Outputs: Easily recognizable translation logs
# Basic module
pip install pyyaml
# Optional: For colored logging
pip install coloramafrom translation_handler_extended import TranslationHandler
# In your main() function - once at bot startup
TranslationHandler.settings(
path="translation/messages", # Path to your YAML files
logging=True, # Enable logging
colored=True # Enable colored logging
)# Synchronous
text = TranslationHandler.get("de", "welcome.title", user="Max")
# Asynchronous
text = await TranslationHandler.get_async("en", "error.not_found")
# For specific user
text = await TranslationHandler.get_for_user(bot, user_id, "message.greeting")TranslationHandler.settings(
path="translation/messages", # Path to YAML files
fallback_langs=("en", "de"), # Fallback languages (Tuple)
default_lang="en", # Default language
cache_ttl=30, # Cache TTL in minutes
logging=True, # Enable/disable logging
colored=True, # Colored logging
log_level="INFO" # DEBUG, INFO, WARNING, ERROR
)| Parameter | Type | Default | Description |
|---|---|---|---|
path |
str/Path | "translation/messages" |
Folder with YAML files |
fallback_langs |
tuple | ("en", "de") |
Fallback languages in order |
default_lang |
str | "en" |
Default language |
cache_ttl |
int | 30 |
Cache validity in minutes |
logging |
bool | True |
Enable translation logs |
colored |
bool | True |
Colored console output |
log_level |
str | "INFO" |
Logging level |
[TRANSLATION] de.yaml has loaded # Green (Success)
[TRANSLATION] en.yaml loaded (fallback...) # Green (Info)
[TRANSLATION] Loaded de.yaml from cache # Cyan (Debug)
[TRANSLATION] File not found: es.yaml # Yellow (Warning)
[TRANSLATION] YAML parsing error... # Red (Error)
# DEBUG - Shows everything (including cache accesses)
TranslationHandler.settings(log_level="DEBUG")
# Output:
# [TRANSLATION] Loaded de.yaml from cache
# [TRANSLATION] Key not found: some.missing.key
# INFO - Standard (important events)
TranslationHandler.settings(log_level="INFO")
# Output:
# [TRANSLATION] de.yaml has loaded
# [TRANSLATION] Cache cleared for: de
# WARNING - Only warnings and errors
TranslationHandler.settings(log_level="WARNING")
# Output:
# [TRANSLATION] No translation file found for 'xx'
# ERROR - Only critical errors
TranslationHandler.settings(log_level="ERROR")
# Output:
# [TRANSLATION] YAML parsing error in de.yaml: ...import discord
from discord.ext import commands
from translation_handler_extended import TranslationHandler
# Bot Setup
bot = commands.Bot(command_prefix="!")
@bot.event
async def on_ready():
# Configure settings ONCE at startup
TranslationHandler.settings(
path="bot/translations/messages",
fallback_langs=("en", "de", "es"),
default_lang="en",
cache_ttl=60,
logging=True,
colored=True,
log_level="INFO"
)
print(f"{bot.user} is ready!")
# Output: [TRANSLATION] en.yaml has loaded
@bot.command()
async def hello(ctx):
# Automatic user language
greeting = await TranslationHandler.get_for_user(
bot,
ctx.author.id,
"commands.hello.message",
username=ctx.author.name
)
await ctx.send(greeting)
@bot.command()
async def error_demo(ctx):
# Manual language with placeholders
error_msg = TranslationHandler.get(
"de",
"errors.command_failed",
command="demo",
reason="Test"
)
await ctx.send(error_msg)
bot.run("TOKEN")# Development - Detailed logging
if DEBUG:
TranslationHandler.settings(
path="translations/messages",
logging=True,
colored=True,
log_level="DEBUG"
)
else:
# Production - Minimal logging
TranslationHandler.settings(
path="translations/messages",
logging=True,
colored=False, # No colors in logs
log_level="ERROR" # Only errors
)# Project structure:
# my_bot/
# βββ locales/
# β βββ lang/
# β βββ en.yaml
# β βββ de.yaml
# β βββ fr.yaml
# βββ bot.py
TranslationHandler.settings(
path="locales/lang", # Custom path
fallback_langs=("en", "de", "fr"),
default_lang="en"
)# Show cache stats
stats = TranslationHandler.get_cache_stats()
print(stats)
# Output: {'entries': 2, 'languages': ['de', 'en'], 'oldest_entry': ...}
# Clear cache for one language
await TranslationHandler.clear_cache("de")
# Output: [TRANSLATION] Cache cleared for: de
# Clear entire cache
await TranslationHandler.clear_cache()
# Output: [TRANSLATION] Cache cleared for all languages
# Show settings
settings = TranslationHandler.get_settings()
print(settings)TranslationHandler.settings(
fallback_langs=("en", "de", "es", "fr")
)
# If "it.yaml" is missing:
# 1. Tries it.yaml
# 2. Tries en.yaml (Fallback 1)
# 3. Tries de.yaml (Fallback 2)
# 4. Tries es.yaml (Fallback 3)
# 5. Tries fr.yaml (Fallback 4)
text = await TranslationHandler.load_messages("it")# Force reload from disk (ignores cache)
messages = await TranslationHandler.load_messages("de", force_reload=True)
# Output: [TRANSLATION] de.yaml has loaded# All current settings
current_settings = TranslationHandler.get_settings()
print(current_settings)
# {
# 'translation_path': 'translation/messages',
# 'fallback_languages': ('en', 'de'),
# 'default_language': 'en',
# 'cache_ttl_minutes': 30,
# 'logging_enabled': True,
# 'colored_logging': True,
# 'log_level': 'INFO',
# 'configured': True,
# 'colorama_available': True
# }# en.yaml
welcome:
title: "Welcome, {user}!"
message: "Hello and welcome to our server!"
commands:
hello:
message: "Hi {username}, how are you?"
errors:
command_failed: "Command '{command}' failed: {reason}"
not_found: "Item not found"
cooldown: "Please wait {seconds} seconds"
settings:
language_name: "English"# β
CORRECT - Once at bot startup
async def main():
TranslationHandler.settings(
path="translations/messages",
logging=True,
colored=True
)
bot = commands.Bot(...)
await bot.start(TOKEN)
# β WRONG - Not in every command
@bot.command()
async def test(ctx):
TranslationHandler.settings(...) # DON'T DO THIS!# β
CORRECT - In async functions
async def my_function():
text = await TranslationHandler.get_async("de", "key")
# β
ALSO OK - Sync wrapper for sync context
def sync_function():
text = TranslationHandler.get("de", "key") # Uses asyncio internallyimport os
log_level = "DEBUG" if os.getenv("DEBUG") else "ERROR"
TranslationHandler.settings(
logging=True,
log_level=log_level
)# β
CORRECT - Provide all placeholders
text = TranslationHandler.get(
"de",
"error.timeout",
seconds=30,
action="connect"
)
# β οΈ WARNING - Missing placeholders
text = TranslationHandler.get("de", "error.timeout")
# Output: [TRANSLATION] Missing placeholder in translation: 'seconds'# Automatic fallback to uncolored logging
# No action needed - works automatically
# Or explicitly disable:
TranslationHandler.settings(colored=False)# Check the path
settings = TranslationHandler.get_settings()
print(f"Searching in: {settings['translation_path']}")
# Check available languages
langs = TranslationHandler.get_available_languages()
print(f"Found: {langs}")# Clear cache completely and reload
await TranslationHandler.clear_cache()
messages = await TranslationHandler.load_messages("de", force_reload=True)- Cache: TTL-based, automatic cleanup
- File Watching: Only reload on changes (via mtime)
- Async Support: Fully asynchronous for best performance
- Memory: Efficient memory usage through TTL
# OLD
TRANSLATION_PATH = Path("translation") / "messages"
messages = MessagesHandler.load_messages("de")
# NEW
TranslationHandler.settings(path="translation/messages")
messages = await TranslationHandler.load_messages("de")
# Output: [TRANSLATION] de.yaml has loadedTranslationHandler.settings(...) # Set configuration
TranslationHandler.get_settings() # Get current configTranslationHandler.get(lang, path, **kwargs) # Sync
await TranslationHandler.get_async(lang, path, **kwargs) # Async
await TranslationHandler.get_for_user(bot, user_id, path) # User
await TranslationHandler.get_for_guild(bot, guild_id, path) # Guild
await TranslationHandler.get_all_translations(path, langs) # Multiawait TranslationHandler.load_messages(lang, force_reload) # Load
await TranslationHandler.clear_cache(lang) # Clear cache
TranslationHandler.get_cache_stats() # Cache stats
TranslationHandler.get_available_languages() # Available languages
await TranslationHandler.validate_translations(lang) # ValidationUse it as you want! π
Improvements welcome! Features:
- Hot-reload without force_reload
- JSON support in addition to YAML
- Pluralization support
- Context-based translations
Made with β€οΈ for the Discord bot community