180 lines
5.7 KiB
Python
180 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple JSON-based Translation Manager for Kobelly Web Solutions
|
|
No compilation needed - just edit JSON files directly!
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import glob
|
|
from flask import request, session, url_for
|
|
from functools import wraps
|
|
|
|
# Supported languages
|
|
SUPPORTED_LANGUAGES = ['en', 'de', 'fr', 'nl']
|
|
|
|
# Default language
|
|
DEFAULT_LANGUAGE = 'en'
|
|
|
|
# Translation cache
|
|
_translations = {}
|
|
|
|
def load_translations():
|
|
"""Load all translation files into memory."""
|
|
global _translations
|
|
|
|
if not os.path.exists('translations'):
|
|
os.makedirs('translations')
|
|
|
|
for lang in SUPPORTED_LANGUAGES:
|
|
lang_file = f'translations/{lang}.json'
|
|
|
|
if os.path.exists(lang_file):
|
|
try:
|
|
with open(lang_file, 'r', encoding='utf-8') as f:
|
|
_translations[lang] = json.load(f)
|
|
print(f"✓ Loaded {lang} translations ({len(_translations[lang])} keys)")
|
|
except Exception as e:
|
|
print(f"✗ Error loading {lang} translations: {e}")
|
|
_translations[lang] = {}
|
|
else:
|
|
print(f"⚠ {lang} translation file not found, creating empty one")
|
|
_translations[lang] = {}
|
|
save_translations(lang, {})
|
|
|
|
def save_translations(lang, translations):
|
|
"""Save translations for a specific language."""
|
|
if not os.path.exists('translations'):
|
|
os.makedirs('translations')
|
|
|
|
lang_file = f'translations/{lang}.json'
|
|
try:
|
|
with open(lang_file, 'w', encoding='utf-8') as f:
|
|
json.dump(translations, f, indent=2, ensure_ascii=False)
|
|
_translations[lang] = translations
|
|
print(f"✓ Saved {lang} translations")
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ Error saving {lang} translations: {e}")
|
|
return False
|
|
|
|
def get_current_language():
|
|
"""Get the current language from session or request."""
|
|
# Check session first
|
|
if 'language' in session:
|
|
return session['language']
|
|
|
|
# Check request parameter
|
|
if request.args.get('lang') in SUPPORTED_LANGUAGES:
|
|
return request.args.get('lang')
|
|
|
|
# Check Accept-Language header
|
|
if request.accept_languages:
|
|
for lang in request.accept_languages:
|
|
if lang[0] in SUPPORTED_LANGUAGES:
|
|
return lang[0]
|
|
|
|
return DEFAULT_LANGUAGE
|
|
|
|
def set_language(lang):
|
|
"""Set the current language in session."""
|
|
if lang in SUPPORTED_LANGUAGES:
|
|
session['language'] = lang
|
|
print(f"✓ Language set to {lang}")
|
|
return True
|
|
print(f"✗ Invalid language: {lang}")
|
|
return False
|
|
|
|
def translate(key, lang=None, **kwargs):
|
|
"""Translate a key to the specified language."""
|
|
if lang is None:
|
|
lang = get_current_language()
|
|
|
|
# Get translation
|
|
translation = _translations.get(lang, {}).get(key, key)
|
|
|
|
# Format with kwargs if provided
|
|
if kwargs:
|
|
try:
|
|
translation = translation.format(**kwargs)
|
|
except (KeyError, ValueError):
|
|
# If formatting fails, return the key
|
|
translation = key
|
|
|
|
return translation
|
|
|
|
def extract_strings():
|
|
"""Extract all translatable strings from templates and create translation files."""
|
|
print("📤 Extracting translatable strings...")
|
|
|
|
# Find all template files
|
|
template_files = glob.glob('templates/**/*.html', recursive=True)
|
|
|
|
# Extract strings (this is a simplified version)
|
|
# In practice, you'd want to parse the templates more thoroughly
|
|
extracted_strings = set()
|
|
|
|
for template_file in template_files:
|
|
try:
|
|
with open(template_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
# Look for common patterns (this is simplified)
|
|
# You might want to use a proper template parser
|
|
lines = content.split('\n')
|
|
for line in lines:
|
|
line = line.strip()
|
|
if line and not line.startswith('{') and not line.startswith('<!--'):
|
|
# Simple extraction - you might want to improve this
|
|
if len(line) > 3 and not line.startswith('<'):
|
|
extracted_strings.add(line)
|
|
except Exception as e:
|
|
print(f"Error reading {template_file}: {e}")
|
|
|
|
# Create translation structure
|
|
translations = {}
|
|
for string in sorted(extracted_strings):
|
|
translations[string] = string
|
|
|
|
# Save for each language
|
|
for lang in SUPPORTED_LANGUAGES:
|
|
save_translations(lang, translations)
|
|
|
|
print(f"✓ Extracted {len(extracted_strings)} strings")
|
|
return translations
|
|
|
|
def create_language_selector():
|
|
"""Create HTML for language selector."""
|
|
current_lang = get_current_language()
|
|
|
|
html = '<div class="language-selector">'
|
|
for lang in SUPPORTED_LANGUAGES:
|
|
lang_name = {
|
|
'en': 'English',
|
|
'de': 'Deutsch',
|
|
'fr': 'Français',
|
|
'nl': 'Nederlands'
|
|
}.get(lang, lang.upper())
|
|
|
|
active_class = 'active' if lang == current_lang else ''
|
|
html += f'<a href="?lang={lang}" class="lang-link {active_class}">{lang_name}</a>'
|
|
html += '</div>'
|
|
|
|
return html
|
|
|
|
# Flask integration
|
|
def init_app(app):
|
|
"""Initialize the translation system with Flask app."""
|
|
load_translations()
|
|
|
|
@app.context_processor
|
|
def inject_translations():
|
|
return {
|
|
'translate': translate,
|
|
'get_current_language': get_current_language,
|
|
'create_language_selector': create_language_selector
|
|
}
|
|
|
|
# Convenience function for templates
|
|
def t(key, **kwargs):
|
|
"""Short alias for translate function."""
|
|
return translate(key, **kwargs) |