# TieMeasureFlow i18n Setup Guide ## Overview TieMeasureFlow supports complete internationalization (i18n) with: - **Server-side**: Flask-Babel for Python/Jinja2 templates - **Client-side**: alpinejs-i18n for JavaScript/Alpine.js - **Supported languages**: Italian (IT - default) and English (EN) ## Directory Structure ``` src/frontend/flask_app/ ├── translations/ # Flask-Babel translations │ ├── babel.cfg # Extraction config │ ├── it/LC_MESSAGES/ │ │ ├── messages.po # Italian strings │ │ └── messages.mo # Compiled binary (generated) │ └── en/LC_MESSAGES/ │ ├── messages.po # English translations │ └── messages.mo # Compiled binary (generated) ├── static/js/locales/ # Alpine.js i18n │ ├── it.json # Italian client-side strings │ └── en.json # English client-side strings └── compile_translations.py # Utility to compile .po → .mo ``` ## How It Works ### Language Selection 1. **User session**: Language stored in `session["language"]` (persistent) 2. **Browser fallback**: Uses `Accept-Language` header if no session 3. **Default**: Italian (IT) ### Server-Side (Flask-Babel) **In Python code:** ```python from flask_babel import gettext as _ flash(_("Profilo aggiornato con successo")) ``` **In Jinja2 templates:** ```jinja2

{{ _('Accedi al sistema') }}

{# With variables #}

{{ _('Benvenuto, %(name)s!', name=user.display_name) }}

``` ### Client-Side (Alpine.js i18n) **Load i18n in base template:** ```html ``` **Use in templates:** ```html

``` ## Workflow ### 1. Add New Strings **Python/Templates:** 1. Wrap strings with `_()` or `{{ _() }}` 2. Extract strings: `pybabel extract -F translations/babel.cfg -o translations/messages.pot .` 3. Update catalogs: `pybabel update -i translations/messages.pot -d translations` 4. Edit `translations/*/LC_MESSAGES/messages.po` files 5. Compile: `python compile_translations.py` **JavaScript/Alpine:** 1. Add keys to `static/js/locales/it.json` (Italian strings) 2. Add translations to `static/js/locales/en.json` 3. No compilation needed (loaded at runtime) ### 2. Compile Translations After editing .po files: ```bash cd src/frontend/flask_app python compile_translations.py ``` This generates `.mo` binary files that Flask-Babel uses at runtime. ### 3. Language Switching **Endpoint**: `GET /set-language/` **Example**: ```html English Italiano ``` **With Alpine.js**: ```html ``` ## Translation Keys ### Server-Side (messages.po) 56 strings covering: - **Login**: "Accedi al sistema", "Credenziali non valide", etc. - **Navbar**: "Misure", "Ricette", "Statistiche", etc. - **Profile**: "Profilo Utente", "Salva Modifiche", etc. - **Flash messages**: "Benvenuto, %(name)s!", "Logout effettuato", etc. - **Common UI**: "Caricamento...", "Salva", "Annulla", "Conferma", etc. - **Measurements**: "Conforme", "Non Conforme", etc. ### Client-Side (locales/*.json) Structured keys: ```json { "numpad": { "title", "clear", "submit", "decimal", "close" }, "measurement": { "pass", "warning", "fail", "value", "next" }, "common": { "loading", "save", "cancel", "confirm", "delete", "edit", ... }, "theme": { "light", "dark", "toggle" }, "language": { "switch", "it", "en" }, "auth": { "login", "username", "password", "signIn", ... }, "nav": { "measurements", "recipes", "statistics", ... }, "profile": { "title", "displayName", "language", "theme", ... } } ``` ## Configuration **config.py** must define: ```python BABEL_DEFAULT_LOCALE = 'it' BABEL_TRANSLATION_DIRECTORIES = 'translations' LANGUAGES = { 'it': 'Italiano', 'en': 'English' } ``` **app.py** provides: - `get_locale()`: Language selector function - `/set-language/`: Language switch endpoint - `inject_globals()`: Exposes `current_language` and `languages` to templates ## Best Practices 1. **Always use Italian as msgid**: Italian strings are the canonical keys 2. **Keep client/server keys synced**: Use similar naming across .po and .json 3. **Test both languages**: Switch languages and verify all pages 4. **Recompile after editing**: Run `compile_translations.py` after changing .po files 5. **Use variables for dynamic content**: `_('Welcome, %(name)s!', name=user)` or `$t('auth.welcome', {name})` 6. **Avoid hardcoded strings**: Always use translation functions ## Troubleshooting **Translations not appearing:** - Verify `.mo` files exist: `find translations -name "*.mo"` - Check session language: `session["language"]` or browser console - Recompile: `python compile_translations.py` - Restart Flask server **Client-side i18n not working:** - Check browser console for fetch errors - Verify JSON files exist: `static/js/locales/{it,en}.json` - Check Alpine.js i18n plugin is loaded - Verify `Alpine.store('i18n')` is initialized **Missing translations show as keys:** - Add missing keys to .po or .json files - Recompile .po files if server-side - Reload page if client-side ## Commands Reference ```bash # Extract translatable strings from code pybabel extract -F translations/babel.cfg -o translations/messages.pot . # Initialize new language (first time) pybabel init -i translations/messages.pot -d translations -l en # Update existing catalogs with new strings pybabel update -i translations/messages.pot -d translations # Compile .po to .mo (required after editing) python compile_translations.py # Check translation statistics msgfmt --statistics translations/it/LC_MESSAGES/messages.po msgfmt --statistics translations/en/LC_MESSAGES/messages.po ``` ## Future Expansion To add a new language (e.g., German): 1. **Server-side**: ```bash pybabel init -i translations/messages.pot -d translations -l de # Edit translations/de/LC_MESSAGES/messages.po python compile_translations.py ``` 2. **Client-side**: - Create `static/js/locales/de.json` - Add to config: `LANGUAGES = {..., 'de': 'Deutsch'}` 3. **Update loader**: Modify base template to support new locale ## Resources - Flask-Babel: https://python-babel.github.io/flask-babel/ - Babel: http://babel.pocoo.org/ - Alpine.js i18n: https://github.com/alpine-collective/alpine-i18n - PO file format: https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html