Refactor translation handling and remove unused language support in templates

This commit is contained in:
Christian Krakau-Louis
2025-04-14 03:27:24 +02:00
parent d595b468e9
commit 2af9cd17ea
6 changed files with 98 additions and 194 deletions
+4 -27
View File
@@ -1,16 +1,8 @@
import gettext import gettext
import os import os
from typing import Dict, List, Callable, Any from fastapi import Request
from fastapi import Request, Depends
from babel.support import Translations from babel.support import Translations
from functools import lru_cache from functools import lru_cache
from gettext import gettext as _ # Ensure `_` is imported for translations
# Define supported languages
SUPPORTED_LANGUAGES = {
'en': 'English',
'de': 'Deutsch',
}
DEFAULT_LANGUAGE = 'de' # Default is German DEFAULT_LANGUAGE = 'de' # Default is German
@@ -40,12 +32,12 @@ def get_locale_from_request(request: Request) -> str:
""" """
# Check URL parameter # Check URL parameter
lang_param = request.query_params.get('lang') lang_param = request.query_params.get('lang')
if lang_param in SUPPORTED_LANGUAGES: if lang_param:
return lang_param return lang_param
# Check session # Check session
session = request.session.get('language') session = request.session.get('language')
if session in SUPPORTED_LANGUAGES: if session:
return session return session
# Check Accept-Language header # Check Accept-Language header
@@ -54,21 +46,6 @@ def get_locale_from_request(request: Request) -> str:
for lang in accept_language.split(','): for lang in accept_language.split(','):
lang_code = lang.split(';')[0].strip().lower() lang_code = lang.split(';')[0].strip().lower()
lang_code = lang_code.split('-')[0] # Convert 'en-US' to 'en' lang_code = lang_code.split('-')[0] # Convert 'en-US' to 'en'
if lang_code in SUPPORTED_LANGUAGES: return lang_code
return lang_code
return DEFAULT_LANGUAGE return DEFAULT_LANGUAGE
def get_translator(locale: str = Depends(get_locale_from_request)):
"""
Return a FastAPI dependency that provides the translation function
"""
translations = get_translation(locale)
gettext_func = translations.gettext
# Make the gettext function available with both _ and gettext names
return {
"_": gettext_func,
"gettext": gettext_func,
"locale": locale
}
+2 -27
View File
@@ -12,7 +12,6 @@ from . import models
from .templates_config import templates from .templates_config import templates
from .views import qr, redeem, teams, admin, leaderboard, dashboard, static, pages from .views import qr, redeem, teams, admin, leaderboard, dashboard, static, pages
from .db_init import seed_db from .db_init import seed_db
from app.i18n import get_translator, SUPPORTED_LANGUAGES, _ # Ensure `_` is correctly imported
# Create tables on startup # Create tables on startup
init_db() init_db()
@@ -35,8 +34,6 @@ static.configure_static_files(app)
# Setup Jinja2 templates # Setup Jinja2 templates
templates = Jinja2Templates(directory="app/templates") templates = Jinja2Templates(directory="app/templates")
templates.env.globals["SUPPORTED_LANGUAGES"] = SUPPORTED_LANGUAGES
templates.env.globals["_"] = _
# User context middleware to make template globals available # User context middleware to make template globals available
@app.middleware("http") @app.middleware("http")
@@ -52,35 +49,13 @@ async def add_template_globals(request: Request, call_next):
response = await call_next(request) response = await call_next(request)
return response return response
# Add translation context processor to Jinja templates
@app.middleware("http")
async def add_translation_context(request: Request, call_next):
response = await call_next(request)
return response
@app.get("/", response_class=HTMLResponse) @app.get("/", response_class=HTMLResponse)
async def read_root(request: Request, i18n: dict = Depends(get_translator)): async def read_root(request: Request):
return templates.TemplateResponse( return templates.TemplateResponse(
"index.html", "index.html",
{"request": request, "user": None, **i18n} {"request": request, "user": None}
) )
@app.api_route("/set-language/{language_code}", methods=["GET", "POST"])
async def set_language(request: Request, language_code: str):
if request.method == "POST":
form = await request.form()
language = form.get("language", "en")
else:
language = language_code
if language in SUPPORTED_LANGUAGES:
request.session["language"] = language
response = RedirectResponse(url=request.headers.get("referer", "/"))
response.set_cookie(key="language", value=language, max_age=31536000) # 1 year
return response
return {"message": f"Invalid language code: {language}"}
# Routers # Routers
app.include_router(pages.router, tags=["Pages"]) # Pages router for index and static pages app.include_router(pages.router, tags=["Pages"]) # Pages router for index and static pages
app.include_router(qr.router, prefix="/qr", tags=["QR"]) app.include_router(qr.router, prefix="/qr", tags=["QR"])
+5 -37
View File
@@ -1,61 +1,29 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<div class="container mx-auto p-4"> <div class="container mx-auto p-4">
<h1 class="text-2xl font-bold text-irish-green mb-4">{{ _("About LeagueLedger") }}</h1> <h1 class="text-2xl font-bold text-irish-green mb-4">About LeagueLedger</h1>
<p class="mb-4"> <p class="mb-4">
{% if locale == "de" %}
LeagueLedger ist Ihr ultimativer Begleiter für die Verfolgung von Pub-Quiz-Team-Erfolgen. Wir möchten eine unterhaltsame und ansprechende Plattform für Quiz-Enthusiasten bieten, um sich zu vernetzen, zu wetteifern und ihr Wissen zu feiern.
{% else %}
LeagueLedger is your ultimate companion for tracking pub quiz team achievements. We aim to provide a fun and engaging platform for quiz enthusiasts to connect, compete, and celebrate their knowledge. LeagueLedger is your ultimate companion for tracking pub quiz team achievements. We aim to provide a fun and engaging platform for quiz enthusiasts to connect, compete, and celebrate their knowledge.
{% endif %}
</p> </p>
<p class="mb-4"> <p class="mb-4">
{% if locale == "de" %}
Als Pub-Quiz-Meister können Sie QR-Codes für Ihre bestplatzierten Teams generieren, diese verteilen und Teams diese auf unserer Website für Punkte einlösen lassen.
{% else %}
As a pub quiz master, you can generate printout QR codes for your top-ranking teams, distribute them, and let teams redeem them for points on our website. As a pub quiz master, you can generate printout QR codes for your top-ranking teams, distribute them, and let teams redeem them for points on our website.
{% endif %}
</p> </p>
<p class="mb-4"> <p class="mb-4">
{% if locale == "de" %}
Als Pub-Quiz-Teammitglied können Sie QR-Codes einlösen, einen Teamnamen erstellen, andere Mitglieder einladen und Social-Logins für einen einfachen Zugang nutzen.
{% else %}
As a pub quiz team member, you can redeem QR codes, create a team name, invite other members, and use social logins for easy access. As a pub quiz team member, you can redeem QR codes, create a team name, invite other members, and use social logins for easy access.
{% endif %}
</p> </p>
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2"> <h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Our Mission</h2>
{% if locale == "de" %}Unsere Mission{% else %}Our Mission{% endif %}
</h2>
<p class="mb-4"> <p class="mb-4">
{% if locale == "de" %}
Die Pub-Quiz-Erfahrung zu verbessern, indem wir eine nahtlose und intuitive Plattform für die Verfolgung des Teamfortschritts bieten, freundlichen Wettbewerb fördern und den Geist des Quiz feiern.
{% else %}
To enhance the pub quiz experience by providing a seamless and intuitive platform for tracking team progress, fostering friendly competition, and celebrating the spirit of trivia. To enhance the pub quiz experience by providing a seamless and intuitive platform for tracking team progress, fostering friendly competition, and celebrating the spirit of trivia.
{% endif %}
</p> </p>
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2"> <h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">Our Team</h2>
{% if locale == "de" %}Unser Team{% else %}Our Team{% endif %}
</h2>
<p class="mb-4"> <p class="mb-4">
{% if locale == "de" %}
LeagueLedger ist eine Open-Source-Initiative und Teil der KaufDeinQuiz-Plattform. Es wird von Christian Louis IT Beratung und Medienproduktion entwickelt, unter der Leitung von Christian Krakau-Louis, einem Team von engagierten Quiz-Enthusiasten und Softwareentwicklern, die leidenschaftlich daran arbeiten, innovative Lösungen für die Pub-Quiz-Community zu schaffen.
{% else %}
LeagueLedger is an Open-Source initiative and part of the KaufDeinQuiz platform. It is brought to you by Christian Louis IT Beratung und Medienproduktion, led by Christian Krakau-Louis, a team of dedicated quiz enthusiasts and software developers passionate about creating innovative solutions for the pub quiz community. LeagueLedger is an Open-Source initiative and part of the KaufDeinQuiz platform. It is brought to you by Christian Louis IT Beratung und Medienproduktion, led by Christian Krakau-Louis, a team of dedicated quiz enthusiasts and software developers passionate about creating innovative solutions for the pub quiz community.
{% endif %}
</p> </p>
<h2 class="text-xl font-semibold text-irish-green mt-6 mb-2"> <h2 class="text-xl font-semibold text-irish-green mt-6 mb-2">License</h2>
{% if locale == "de" %}Lizenz{% else %}License{% endif %}
</h2>
<p class="mb-4"> <p class="mb-4">
{% if locale == "de" %}
LeagueLedger ist unter der Apache License 2.0 lizenziert. Die vollständige Lizenz finden Sie in unserer GitHub-Repository oder auf Anfrage.
{% else %}
LeagueLedger is licensed under the Apache License 2.0. You can find the full license in our GitHub repository or upon request. LeagueLedger is licensed under the Apache License 2.0. You can find the full license in our GitHub repository or upon request.
{% endif %}
</p> </p>
<p class="mt-8 text-sm text-gray-600"> <p class="mt-8 text-sm text-gray-600">Last updated: April 2025</p>
{% if locale == "de" %}Letzte Aktualisierung: April 2025{% else %}Last updated: April 2025{% endif %}
</p>
</div> </div>
{% endblock %} {% endblock %}
+39 -76
View File
@@ -1,9 +1,9 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ locale }}"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}LeagueLedger - Pub Quiz Tracking{% endblock %}</title> <title>LeagueLedger - Pub Quiz Tracking</title>
<!-- Favicon --> <!-- Favicon -->
@@ -65,26 +65,10 @@
<!-- Desktop Navigation --> <!-- Desktop Navigation -->
<div class="hidden md:flex space-x-6 items-center"> <div class="hidden md:flex space-x-6 items-center">
<a href="/" class="hover:text-golden-ale transition">{{ _("Home") }}</a> <a href="/" class="hover:text-golden-ale transition">Home</a>
<a href="/teams" class="hover:text-golden-ale transition">{{ _("Teams") }}</a> <a href="/teams" class="hover:text-golden-ale transition">Teams</a>
<a href="/leaderboard" class="hover:text-golden-ale transition">{{ _("Leaderboard") }}</a> <a href="/leaderboard" class="hover:text-golden-ale transition">Leaderboard</a>
<a href="/scan" class="hover:text-golden-ale transition">{{ _("Scan QR Code") }}</a> <a href="/scan" class="hover:text-golden-ale transition">Scan QR Code</a>
<!-- Language Selector -->
<div class="relative dropdown">
<button class="dropdown-toggle flex items-center space-x-1 hover:text-golden-ale transition">
<i class="fas fa-globe text-lg"></i>
<span>{{ SUPPORTED_LANGUAGES[locale] }}</span>
<i class="fas fa-chevron-down text-xs"></i>
</button>
<div class="dropdown-menu hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50">
{% for code, name in SUPPORTED_LANGUAGES.items() %}
<a href="/set-language/{{ code }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 {% if locale == code %}font-bold{% endif %}">
{{ name }}
</a>
{% endfor %}
</div>
</div>
{% if user %} {% if user %}
<div class="relative dropdown"> <div class="relative dropdown">
@@ -95,43 +79,29 @@
</button> </button>
<div class="dropdown-menu hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50"> <div class="dropdown-menu hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50">
<a href="/profile" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"> <a href="/profile" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-user mr-2"></i> {{ _("Profile") }} <i class="fas fa-user mr-2"></i> Profile
</a> </a>
<a href="/dashboard" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"> <a href="/dashboard" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-tachometer-alt mr-2"></i> {{ _("Dashboard") }} <i class="fas fa-tachometer-alt mr-2"></i> Dashboard
</a> </a>
{% if user.is_admin %} {% if user.is_admin %}
<a href="/admin" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"> <a href="/admin" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-cog mr-2"></i> {{ _("Admin") }} <i class="fas fa-cog mr-2"></i> Admin
</a> </a>
{% endif %} {% endif %}
<div class="border-t border-gray-100 my-1"></div> <div class="border-t border-gray-100 my-1"></div>
<a href="/logout" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"> <a href="/logout" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-sign-out-alt mr-2"></i> {{ _("Logout") }} <i class="fas fa-sign-out-alt mr-2"></i> Logout
</a> </a>
</div> </div>
</div> </div>
{% else %} {% else %}
<a href="/login" class="bg-golden-ale hover:bg-opacity-90 text-black-stout px-4 py-2 rounded-md transition">{{ _("Sign In") }}</a> <a href="/login" class="bg-golden-ale hover:bg-opacity-90 text-black-stout px-4 py-2 rounded-md transition">Sign In</a>
{% endif %} {% endif %}
</div> </div>
<!-- Mobile menu button --> <!-- Mobile menu button -->
<div class="md:hidden flex items-center space-x-4"> <div class="md:hidden flex items-center space-x-4">
<!-- Mobile Language Selector -->
<div class="relative dropdown">
<button class="dropdown-toggle flex items-center hover:text-golden-ale transition">
<i class="fas fa-globe text-lg"></i>
</button>
<div class="dropdown-menu hidden absolute right-0 mt-2 w-32 bg-white rounded-md shadow-lg py-1 z-50">
{% for code, name in SUPPORTED_LANGUAGES.items() %}
<a href="/set-language/{{ code }}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 {% if locale == code %}font-bold{% endif %}">
{{ name }}
</a>
{% endfor %}
</div>
</div>
<button id="mobile-menu-button" class="text-white hover:text-golden-ale transition"> <button id="mobile-menu-button" class="text-white hover:text-golden-ale transition">
<i class="fas fa-bars text-2xl"></i> <i class="fas fa-bars text-2xl"></i>
</button> </button>
@@ -141,19 +111,19 @@
<!-- Mobile Navigation --> <!-- Mobile Navigation -->
<div id="mobile-menu" class="hidden md:hidden mt-3 pb-3 border-t border-irish-green border-opacity-30"> <div id="mobile-menu" class="hidden md:hidden mt-3 pb-3 border-t border-irish-green border-opacity-30">
<div class="flex flex-col space-y-2 mt-3"> <div class="flex flex-col space-y-2 mt-3">
<a href="/" class="hover:text-golden-ale transition py-2">{{ _("Home") }}</a> <a href="/" class="hover:text-golden-ale transition py-2">Home</a>
<a href="/teams" class="hover:text-golden-ale transition py-2">{{ _("Teams") }}</a> <a href="/teams" class="hover:text-golden-ale transition py-2">Teams</a>
<a href="/leaderboard" class="hover:text-golden-ale transition py-2">{{ _("Leaderboard") }}</a> <a href="/leaderboard" class="hover:text-golden-ale transition py-2">Leaderboard</a>
<a href="/scan" class="hover:text-golden-ale transition py-2">{{ _("Scan QR Code") }}</a> <a href="/scan" class="hover:text-golden-ale transition py-2">Scan QR Code</a>
{% if user %} {% if user %}
<a href="/profile" class="hover:text-golden-ale transition py-2">{{ _("Profile") }}</a> <a href="/profile" class="hover:text-golden-ale transition py-2">Profile</a>
<a href="/dashboard" class="hover:text-golden-ale transition py-2">{{ _("Dashboard") }}</a> <a href="/dashboard" class="hover:text-golden-ale transition py-2">Dashboard</a>
{% if user.is_admin %} {% if user.is_admin %}
<a href="/admin" class="hover:text-golden-ale transition py-2">{{ _("Admin") }}</a> <a href="/admin" class="hover:text-golden-ale transition py-2">Admin</a>
{% endif %} {% endif %}
<a href="/logout" class="hover:text-golden-ale transition py-2">{{ _("Logout") }}</a> <a href="/logout" class="hover:text-golden-ale transition py-2">Logout</a>
{% else %} {% else %}
<a href="/login" class="bg-golden-ale hover:bg-opacity-90 text-black-stout px-4 py-2 rounded-md transition text-center">{{ _("Sign In") }}</a> <a href="/login" class="bg-golden-ale hover:bg-opacity-90 text-black-stout px-4 py-2 rounded-md transition text-center">Sign In</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@@ -174,53 +144,46 @@
<img src="{{ url_for('static', path='images/logos/monogram.png') }}" alt="LeagueLedger Logo" class="h-12"> <img src="{{ url_for('static', path='images/logos/monogram.png') }}" alt="LeagueLedger Logo" class="h-12">
<span class="ml-2 text-xl font-bold">LeagueLedger</span> <span class="ml-2 text-xl font-bold">LeagueLedger</span>
</div> </div>
<p class="text-sm">{{ _("Track your pub quiz team's progress.") }}<br>{{ _("Scan QR codes to earn points.") }}</p> <p class="text-sm">Track your pub quiz team's progress.<br>Scan QR codes to earn points.</p>
</div> </div>
<div class="grid grid-cols-2 md:grid-cols-3 gap-8"> <div class="grid grid-cols-2 md:grid-cols-3 gap-8">
<div> <div>
<h3 class="text-golden-ale font-bold mb-4">{{ _("Navigation") }}</h3> <h3 class="text-golden-ale font-bold mb-4">Navigation</h3>
<ul class="space-y-2"> <ul class="space-y-2">
<li><a href="/" class="hover:text-golden-ale transition">{{ _("Home") }}</a></li> <li><a href="/" class="hover:text-golden-ale transition">Home</a></li>
<li><a href="/teams" class="hover:text-golden-ale transition">{{ _("Teams") }}</a></li> <li><a href="/teams" class="hover:text-golden-ale transition">Teams</a></li>
<li><a href="/leaderboard" class="hover:text-golden-ale transition">{{ _("Leaderboard") }}</a></li> <li><a href="/leaderboard" class="hover:text-golden-ale transition">Leaderboard</a></li>
<li><a href="/scan" class="hover:text-golden-ale transition">{{ _("Scan QR Code") }}</a></li> <li><a href="/scan" class="hover:text-golden-ale transition">Scan QR Code</a></li>
</ul> </ul>
</div> </div>
<div> <div>
<h3 class="text-golden-ale font-bold mb-4">{{ _("Account") }}</h3> <h3 class="text-golden-ale font-bold mb-4">Account</h3>
<ul class="space-y-2"> <ul class="space-y-2">
<li><a href="/login" class="hover:text-golden-ale transition">{{ _("Sign In") }}</a></li> <li><a href="/login" class="hover:text-golden-ale transition">Sign In</a></li>
<li><a href="/register" class="hover:text-golden-ale transition">{{ _("Register") }}</a></li> <li><a href="/register" class="hover:text-golden-ale transition">Register</a></li>
<li><a href="/profile" class="hover:text-golden-ale transition">{{ _("Profile") }}</a></li> <li><a href="/profile" class="hover:text-golden-ale transition">Profile</a></li>
<li><a href="/dashboard" class="hover:text-golden-ale transition">{{ _("Dashboard") }}</a></li> <li><a href="/dashboard" class="hover:text-golden-ale transition">Dashboard</a></li>
</ul> </ul>
</div> </div>
<div> <div>
<h3 class="text-golden-ale font-bold mb-4">{{ _("Legal") }}</h3> <h3 class="text-golden-ale font-bold mb-4">Legal</h3>
<ul class="space-y-2"> <ul class="space-y-2">
<li><a href="/about" class="hover:text-golden-ale transition">{{ _("About") }}</a></li> <li><a href="/about" class="hover:text-golden-ale transition">About</a></li>
<li><a href="/contact" class="hover:text-golden-ale transition">{{ _("Contact") }}</a></li> <li><a href="/contact" class="hover:text-golden-ale transition">Contact</a></li>
<li><a href="/terms" class="hover:text-golden-ale transition">{{ _("Terms of Service") }}</a></li> <li><a href="/terms" class="hover:text-golden-ale transition">Terms of Service</a></li>
<li><a href="/privacy" class="hover:text-golden-ale transition">{{ _("Privacy Policy") }}</a></li> <li><a href="/privacy" class="hover:text-golden-ale transition">Privacy Policy</a></li>
<li><a href="/cookies" class="hover:text-golden-ale transition">{{ _("Cookie Policy") }}</a></li> <li><a href="/cookies" class="hover:text-golden-ale transition">Cookie Policy</a></li>
<li><a href="/impressum" class="hover:text-golden-ale transition">{{ _("Imprint") }}</a></li> <li><a href="/impressum" class="hover:text-golden-ale transition">Imprint</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<div class="border-t border-gray-800 mt-8 pt-8 flex flex-col md:flex-row justify-between items-center"> <div class="border-t border-gray-800 mt-8 pt-8 flex flex-col md:flex-row justify-between items-center">
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<p class="text-sm">&copy; 2025 LeagueLedger. {{ _("Licensed under Apache License 2.0") }}</p> <p class="text-sm">&copy; 2025 LeagueLedger. Licensed under Apache License 2.0</p>
</div>
<div class="flex space-x-4 mt-4 md:mt-0">
{% for code, name in SUPPORTED_LANGUAGES.items() %}
<a href="/set-language/{{ code }}" class="hover:text-golden-ale transition {% if locale == code %}font-bold{% endif %}">
{{ name }}
</a>
{% endfor %}
</div> </div>
</div> </div>
</div> </div>
+47
View File
@@ -0,0 +1,47 @@
version: '3.8'
services:
app:
image: leagueledger:latest
deploy:
replicas: 3
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
networks:
- traefik-public
environment:
- DATABASE_URL=postgresql://user:password@db:5432/leagueledger
- SECRET_KEY=your-very-secret-session-key
- TRAEFIK_ENTRYPOINT=http
labels:
- "traefik.enable=true"
- "traefik.http.routers.leagueledger.rule=Host(`yourdomain.com`)"
- "traefik.http.services.leagueledger.loadbalancer.server.port=8000"
volumes:
- ./app:/app
ports:
- "8000:8000"
db:
image: postgres:13
deploy:
restart_policy:
condition: on-failure
networks:
- traefik-public
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: leagueledger
volumes:
- db_data:/var/lib/postgresql/data
networks:
traefik-public:
external: true
volumes:
db_data:
+1 -27
View File
@@ -4,31 +4,6 @@ from pathlib import Path
# Define base directory # Define base directory
BASE_DIR = Path(__file__).parent.parent BASE_DIR = Path(__file__).parent.parent
LOCALE_DIR = BASE_DIR / "app" / "i18n" / "locales"
def compile_all_translations():
"""Compile .po files into .mo files for all languages"""
print("Compiling translations...")
for lang_dir in LOCALE_DIR.iterdir():
if lang_dir.is_dir():
lang_code = lang_dir.name
po_file = lang_dir / "LC_MESSAGES" / "messages.po"
if po_file.exists():
print(f"Compiling {lang_code} translations...")
try:
subprocess.run([
"pybabel", "compile",
"-f", "-i", str(po_file),
"-o", str(po_file.parent / "messages.mo"),
"--statistics"
], check=True)
print(f"Successfully compiled {lang_code} translations")
except subprocess.CalledProcessError as e:
print(f"Error compiling {lang_code} translations: {e}")
else:
print(f"No .po file found for {lang_code}")
def update_pot_file(): def update_pot_file():
"""Extract translatable strings from templates and create a POT file""" """Extract translatable strings from templates and create a POT file"""
@@ -54,7 +29,7 @@ def update_po_files():
pot_file = BASE_DIR / "app" / "i18n" / "messages.pot" pot_file = BASE_DIR / "app" / "i18n" / "messages.pot"
for lang_dir in LOCALE_DIR.iterdir(): for lang_dir in (BASE_DIR / "app" / "i18n" / "locales").iterdir():
if lang_dir.is_dir(): if lang_dir.is_dir():
lang_code = lang_dir.name lang_code = lang_dir.name
po_file = lang_dir / "LC_MESSAGES" / "messages.po" po_file = lang_dir / "LC_MESSAGES" / "messages.po"
@@ -97,4 +72,3 @@ if __name__ == "__main__":
update_pot_file() update_pot_file()
update_po_files() update_po_files()
compile_all_translations()