# 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
<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:**
```html
<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:**
```html
<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:**
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/<lang>`

**Example**:
```html
<a href="{{ url_for('set_language', lang='en') }}">English</a>
<a href="{{ url_for('set_language', lang='it') }}">Italiano</a>
```

**With Alpine.js**:
```html
<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:
```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/<lang>`: 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
