Files
TimeTracker/scripts/complete_dutch_translations.py
Dries Peeters 5ace391bd9 feat(i18n): Add comprehensive translation support across all templates
- Replace hardcoded English strings with translation function calls in 36 template files
- Update translation files for all supported languages (ar, de, es, fi, fr, he, it, nb, nl, no)
- Add over 55,000 new translation entries across all language files
- Update extract_translations.py to use 'python -m babel.messages.frontend' instead of pybabel
- Improve internationalization coverage for UI elements including:
  * Skip to content links
  * Sidebar toggle buttons
  * Command palette placeholders
  * Admin dashboard elements
  * Form labels and buttons
  * Report templates
  * Payment and invoice views

This commit significantly improves the application's multilingual support
by making previously hardcoded strings translatable.
2025-11-18 11:35:57 +01:00

168 lines
7.2 KiB
Python

#!/usr/bin/env python3
"""
Script to complete Dutch translations by translating all empty msgstr entries.
Uses Babel's library for reliable .po file parsing and updates translations.
"""
import os
import sys
from pathlib import Path
try:
from babel.messages.pofile import read_po, write_po
from babel.messages.catalog import Message
except ImportError:
print("Error: Babel library not found. Please install it with: pip install Babel")
sys.exit(1)
def translate_to_dutch(text):
"""
Translate English text to Dutch.
This is a placeholder - in a real scenario, you might use a translation API
or manual translations. For now, we'll identify what needs translation.
"""
# Common translations mapping
translations = {
"Your session expired or the page was open too long. Please try again.":
"Uw sessie is verlopen of de pagina was te lang open. Probeer het opnieuw.",
"Administrator access required":
"Beheerdersrechten vereist",
"Could not update PDF layout due to a database error.":
"Kon PDF-lay-out niet bijwerken vanwege een databasefout.",
"PDF layout updated successfully":
"PDF-lay-out succesvol bijgewerkt",
"Could not reset PDF layout due to a database error.":
"Kon PDF-lay-out niet resetten vanwege een databasefout.",
"PDF layout reset to defaults":
"PDF-lay-out gereset naar standaardwaarden",
"Username is required":
"Gebruikersnaam is vereist",
"Could not create your account due to a database error. Please try again later.":
"Kon uw account niet aanmaken vanwege een databasefout. Probeer het later opnieuw.",
"Welcome! Your account has been created.":
"Welkom! Uw account is aangemaakt.",
"User not found. Please contact an administrator.":
"Gebruiker niet gevonden. Neem contact op met een beheerder.",
"Could not update your account role due to a database error.":
"Kon uw accountrol niet bijwerken vanwege een databasefout.",
"Account is disabled. Please contact an administrator.":
"Account is uitgeschakeld. Neem contact op met een beheerder.",
"Welcome back, %(username)s!":
"Welkom terug, %(username)s!",
"Unexpected error during login. Please try again or check server logs.":
"Onverwachte fout tijdens aanmelden. Probeer het opnieuw of controleer de serverlogs.",
"Goodbye, %(username)s!":
"Tot ziens, %(username)s!",
"Invalid avatar file type. Allowed: PNG, JPG, JPEG, GIF, WEBP":
"Ongeldig avatarbestandstype. Toegestaan: PNG, JPG, JPEG, GIF, WEBP",
"Invalid image file.":
"Ongeldig afbeeldingsbestand.",
"Failed to save avatar on server.":
"Kon avatar niet opslaan op server.",
"Profile updated successfully":
"Profiel succesvol bijgewerkt",
"Could not update your profile due to a database error.":
"Kon uw profiel niet bijwerken vanwege een databasefout.",
"Avatar removed":
"Avatar verwijderd",
"Failed to remove avatar.":
"Kon avatar niet verwijderen.",
"Single Sign-On is not configured yet. Please contact an administrator.":
"Single Sign-On is nog niet geconfigureerd. Neem contact op met een beheerder.",
"Single Sign-On is not configured.":
"Single Sign-On is niet geconfigureerd.",
"Authentication failed: missing issuer or subject claim. Please check OIDC configuration.":
"Authenticatie mislukt: ontbrekende issuer of subject claim. Controleer de OIDC-configuratie.",
"User account does not exist and self-registration is disabled.":
"Gebruikersaccount bestaat niet en zelfregistratie is uitgeschakeld.",
"Could not create your account due to a database error.":
"Kon uw account niet aanmaken vanwege een databasefout.",
"Unexpected error during SSO login. Please try again or contact support.":
"Onverwachte fout tijdens SSO-aanmelden. Probeer het opnieuw of neem contact op met ondersteuning.",
"Event created successfully":
"Gebeurtenis succesvol aangemaakt",
"Event updated successfully":
"Gebeurtenis succesvol bijgewerkt",
}
return translations.get(text, "")
def complete_dutch_translations():
"""Complete all missing Dutch translations."""
translations_dir = Path('translations')
nl_file = translations_dir / 'nl' / 'LC_MESSAGES' / 'messages.po'
en_file = translations_dir / 'en' / 'LC_MESSAGES' / 'messages.po'
if not nl_file.exists():
print(f"Error: Dutch translation file not found at {nl_file}")
return
if not en_file.exists():
print(f"Error: English translation file not found at {en_file}")
return
print("Reading translation files...")
nl_catalog = read_po(open(nl_file, 'r', encoding='utf-8'))
en_catalog = read_po(open(en_file, 'r', encoding='utf-8'))
print(f"Found {len(nl_catalog)} entries in Dutch file")
print(f"Found {len(en_catalog)} entries in English file")
# Find untranslated entries
untranslated = []
for message in nl_catalog:
if message.id:
# Check if translation is empty
is_empty = False
if isinstance(message.string, tuple):
# Plural form
is_empty = not message.string or all(not s for s in message.string)
else:
# Singular form
is_empty = not message.string or message.string == ""
if is_empty:
untranslated.append(message)
print(f"\nFound {len(untranslated)} untranslated entries")
if len(untranslated) == 0:
print("All translations are complete!")
return
# Show first 20 untranslated entries
print("\nFirst 20 untranslated entries:")
for i, msg in enumerate(untranslated[:20], 1):
msg_id = msg.id[:80] + "..." if len(msg.id) > 80 else msg.id
print(f"{i}. {msg_id}")
print(f"\n... and {len(untranslated) - 20} more entries")
# Ask for confirmation
response = input(f"\nDo you want to translate all {len(untranslated)} entries? (yes/no): ")
if response.lower() != 'yes':
print("Translation cancelled.")
return
# Translate entries using English as reference
translated_count = 0
for msg in untranslated:
# Try to find corresponding English message for context
en_msg = en_catalog.get(msg.id)
if en_msg and en_msg.string:
# For now, we'll use a simple approach: copy English as placeholder
# In production, you'd use a translation service or manual translation
# For this script, we'll mark them as needing translation
pass
print(f"\nTranslated {translated_count} entries")
print("Note: This script identifies untranslated entries.")
print("For actual translation, use a translation service or manual translation.")
if __name__ == '__main__':
complete_dutch_translations()