dd2ebf863a
Security hardening: CORS lockdown, rate limiting middleware con sliding window e eviction IP stale, security headers (CSP, HSTS, X-Frame-Options), session cookie hardening, filename sanitization upload. i18n completion: internazionalizzati barcode.js e csv-export.js con bridge window.BARCODE_I18N/CSV_I18N, aggiornati .po IT/EN con 27 nuove stringhe. Tablet UX: touch target 44px per dispositivi coarse pointer. Test suite: 101 test totali (76 server + 25 client), copertura completa di tutti i router API, autenticazione, ruoli, CRUD, SPC, file upload, security integration. Infrastruttura SQLite async in-memory con fixtures. Fix critici: MissingGreenlet in recipe_service (selectinload eager), route ordering tasks.py, auth_service bcrypt diretto, Measurement.id Integer per SQLite. Documentazione: API.md (riferimento completo 40+ endpoint), DEPLOYMENT.md (guida produzione con Docker/Nginx/SSL), USER_GUIDE.md (manuale utente per ruolo). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.0 KiB
7.0 KiB
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
client/
├── 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
- User session: Language stored in
session["language"](persistent) - Browser fallback: Uses
Accept-Languageheader if no session - Default: Italian (IT)
Server-Side (Flask-Babel)
In Python code:
from flask_babel import gettext as _
flash(_("Profilo aggiornato con successo"))
In Jinja2 templates:
<h1>{{ _('Accedi al sistema') }}</h1>
{# With variables #}
<p>{{ _('Benvenuto, %(name)s!', name=user.display_name) }}</p>
Client-Side (Alpine.js i18n)
Load i18n in base template:
<script src="/static/js/lib/alpinejs-i18n.min.js"></script>
<script>
// Initialize i18n with current language
Alpine.plugin(AlpineI18n);
Alpine.store('i18n', {
locale: '{{ current_language }}',
fallbackLocale: 'it',
messages: {}
});
// Load locale files
fetch('/static/js/locales/{{ current_language }}.json')
.then(r => r.json())
.then(data => Alpine.store('i18n').messages['{{ current_language }}'] = data);
</script>
Use in templates:
<button @click="submit" x-text="$t('numpad.submit')"></button>
<p x-text="$t('auth.welcome', {name: userName})"></p>
Workflow
1. Add New Strings
Python/Templates:
- Wrap strings with
_()or{{ _() }} - Extract strings:
pybabel extract -F translations/babel.cfg -o translations/messages.pot . - Update catalogs:
pybabel update -i translations/messages.pot -d translations - Edit
translations/*/LC_MESSAGES/messages.pofiles - Compile:
python compile_translations.py
JavaScript/Alpine:
- Add keys to
static/js/locales/it.json(Italian strings) - Add translations to
static/js/locales/en.json - No compilation needed (loaded at runtime)
2. Compile Translations
After editing .po files:
cd client
python compile_translations.py
This generates .mo binary files that Flask-Babel uses at runtime.
3. Language Switching
Endpoint: GET /set-language/<lang>
Example:
<a href="{{ url_for('set_language', lang='en') }}">English</a>
<a href="{{ url_for('set_language', lang='it') }}">Italiano</a>
With Alpine.js:
<button @click="$store.i18n.locale = 'en'; window.location.href='/set-language/en'">
English
</button>
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:
{
"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:
BABEL_DEFAULT_LOCALE = 'it'
BABEL_TRANSLATION_DIRECTORIES = 'translations'
LANGUAGES = {
'it': 'Italiano',
'en': 'English'
}
app.py provides:
get_locale(): Language selector function/set-language/<lang>: Language switch endpointinject_globals(): Exposescurrent_languageandlanguagesto templates
Best Practices
- Always use Italian as msgid: Italian strings are the canonical keys
- Keep client/server keys synced: Use similar naming across .po and .json
- Test both languages: Switch languages and verify all pages
- Recompile after editing: Run
compile_translations.pyafter changing .po files - Use variables for dynamic content:
_('Welcome, %(name)s!', name=user)or$t('auth.welcome', {name}) - Avoid hardcoded strings: Always use translation functions
Troubleshooting
Translations not appearing:
- Verify
.mofiles 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
# 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):
-
Server-side:
pybabel init -i translations/messages.pot -d translations -l de # Edit translations/de/LC_MESSAGES/messages.po python compile_translations.py -
Client-side:
- Create
static/js/locales/de.json - Add to config:
LANGUAGES = {..., 'de': 'Deutsch'}
- Create
-
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