ttsfm / i18n.py
NitinBot001's picture
Upload 20 files
bf90fc9 verified
"""
Internationalization (i18n) support for TTSFM Web Application
This module provides multi-language support for the Flask web application,
including language detection, translation management, and template functions.
"""
import json
import os
from typing import Dict, Any, Optional
from flask import request, session, current_app
class LanguageManager:
"""Manages language detection, translation loading, and text translation."""
def __init__(self, app=None, translations_dir: str = "translations"):
"""
Initialize the LanguageManager.
Args:
app: Flask application instance
translations_dir: Directory containing translation files
"""
self.translations_dir = translations_dir
self.translations: Dict[str, Dict[str, Any]] = {}
self.supported_languages = ['en', 'zh']
self.default_language = 'en'
if app is not None:
self.init_app(app)
def init_app(self, app):
"""Initialize the Flask application with i18n support."""
app.config.setdefault('LANGUAGES', self.supported_languages)
app.config.setdefault('DEFAULT_LANGUAGE', self.default_language)
# Load translations
self.load_translations()
# Register template functions
app.jinja_env.globals['_'] = self.translate
app.jinja_env.globals['get_locale'] = self.get_locale
app.jinja_env.globals['get_supported_languages'] = self.get_supported_languages
# Store reference to this instance
app.language_manager = self
def load_translations(self):
"""Load all translation files from the translations directory."""
translations_path = os.path.join(
os.path.dirname(__file__),
self.translations_dir
)
if not os.path.exists(translations_path):
print(f"Warning: Translations directory not found: {translations_path}")
return
for lang_code in self.supported_languages:
file_path = os.path.join(translations_path, f"{lang_code}.json")
if os.path.exists(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as f:
self.translations[lang_code] = json.load(f)
print(f"Info: Loaded translations for language: {lang_code}")
except Exception as e:
print(f"Error: Failed to load translations for {lang_code}: {e}")
else:
print(f"Warning: Translation file not found: {file_path}")
def get_locale(self) -> str:
"""
Get the current locale based on user preference, session, or browser settings.
Returns:
Language code (e.g., 'en', 'zh')
"""
# 1. Check URL parameter (for language switching)
if 'lang' in request.args:
lang = request.args.get('lang')
if lang in self.supported_languages:
session['language'] = lang
return lang
# 2. Check session (user's previous choice)
if 'language' in session:
lang = session['language']
if lang in self.supported_languages:
return lang
# 3. Check browser's Accept-Language header
if request.headers.get('Accept-Language'):
browser_langs = request.headers.get('Accept-Language').split(',')
for browser_lang in browser_langs:
# Extract language code (e.g., 'zh-CN' -> 'zh')
lang_code = browser_lang.split(';')[0].split('-')[0].strip().lower()
if lang_code in self.supported_languages:
session['language'] = lang_code
return lang_code
# 4. Fall back to default language
return self.default_language
def set_locale(self, lang_code: str) -> bool:
"""
Set the current locale.
Args:
lang_code: Language code to set
Returns:
True if successful, False if language not supported
"""
if lang_code in self.supported_languages:
session['language'] = lang_code
return True
return False
def translate(self, key: str, **kwargs) -> str:
"""
Translate a text key to the current locale.
Args:
key: Translation key in dot notation (e.g., 'nav.home')
**kwargs: Variables for string formatting
Returns:
Translated text or the key if translation not found
"""
locale = self.get_locale()
# Get translation for current locale
translation = self._get_nested_value(
self.translations.get(locale, {}),
key
)
# Fall back to default language if not found
if translation is None and locale != self.default_language:
translation = self._get_nested_value(
self.translations.get(self.default_language, {}),
key
)
# Fall back to key if still not found
if translation is None:
translation = key
# Format with variables if provided
if kwargs and isinstance(translation, str):
try:
translation = translation.format(**kwargs)
except (KeyError, ValueError):
pass # Ignore formatting errors
return translation
def _get_nested_value(self, data: Dict[str, Any], key: str) -> Optional[str]:
"""
Get a nested value from a dictionary using dot notation.
Args:
data: Dictionary to search in
key: Dot-separated key (e.g., 'nav.home')
Returns:
Value if found, None otherwise
"""
keys = key.split('.')
current = data
for k in keys:
if isinstance(current, dict) and k in current:
current = current[k]
else:
return None
return current if isinstance(current, str) else None
def get_supported_languages(self) -> Dict[str, str]:
"""
Get a dictionary of supported languages with their display names.
Returns:
Dictionary mapping language codes to display names
"""
return {
'en': 'English',
'zh': '中文'
}
def get_language_info(self, lang_code: str) -> Dict[str, str]:
"""
Get information about a specific language.
Args:
lang_code: Language code
Returns:
Dictionary with language information
"""
language_names = {
'en': {'name': 'English', 'native': 'English'},
'zh': {'name': 'Chinese', 'native': '中文'}
}
return language_names.get(lang_code, {
'name': lang_code.upper(),
'native': lang_code.upper()
})
# Global instance
language_manager = LanguageManager()
def init_i18n(app):
"""Initialize i18n support for the Flask application."""
language_manager.init_app(app)
return language_manager
# Template helper functions
def _(key: str, **kwargs) -> str:
"""Shorthand translation function for use in templates and code."""
return language_manager.translate(key, **kwargs)
def get_locale() -> str:
"""Get the current locale."""
return language_manager.get_locale()
def set_locale(lang_code: str) -> bool:
"""Set the current locale."""
return language_manager.set_locale(lang_code)