# Localizzazione

<div id="introduction"></div>

## Introduzione

La localizzazione ti permette di fornire la tua app in più lingue. Nylo v7 rende facile localizzare il testo utilizzando file JSON di lingua.

Ecco un esempio rapido:

**lang/en.json**
``` json
{
  "welcome": "Welcome",
  "greeting": "Hello {{name}}"
}
```

**Nel tuo widget:**
``` dart
Text("welcome".tr())              // "Welcome"
Text("greeting".tr(arguments: {"name": "Anthony"}))  // "Hello Anthony"
```

<div id="configuration"></div>

## Configurazione

La localizzazione è configurata in `lib/config/localization.dart`:

``` dart
final class LocalizationConfig {
  // Codice lingua predefinito (corrisponde al tuo file JSON, es. 'en' per lang/en.json)
  static final String languageCode =
      getEnv('DEFAULT_LOCALE', defaultValue: "en");

  // LocaleType.device - Usa l'impostazione della lingua del dispositivo
  // LocaleType.asDefined - Usa il languageCode sopra
  static final LocaleType localeType =
      getEnv('LOCALE_TYPE', defaultValue: 'asDefined') == 'device'
          ? LocaleType.device
          : LocaleType.asDefined;

  // Directory contenente i file JSON delle lingue
  static const String assetsDirectory = 'lang/';

  // Elenco delle lingue supportate
  static const List<Locale> supportedLocales = [
    Locale('en'),
    Locale('es'),
    // Aggiungi altre lingue secondo necessita'
  ];

  // Fallback quando una chiave non viene trovata nella lingua attiva
  static const String fallbackLanguageCode = 'en';

  // Codici lingua RTL
  static const List<String> rtlLanguages = ['ar', 'he', 'fa', 'ur'];

  // Registra avvisi per le chiavi di traduzione mancanti
  static final bool debugMissingKeys =
      getEnv('DEBUG_TRANSLATIONS', defaultValue: 'false') == 'true';
}
```

<div id="adding-localized-files"></div>

## Aggiungere File Localizzati

Aggiungi i tuoi file JSON di lingua alla directory `lang/`:

```
lang/
├── en.json   # English
├── es.json   # Spanish
├── fr.json   # French
└── ...
```

**lang/en.json**
``` json
{
  "welcome": "Welcome",
  "settings": "Settings",
  "navigation": {
    "home": "Home",
    "profile": "Profile"
  }
}
```

**lang/es.json**
``` json
{
  "welcome": "Bienvenido",
  "settings": "Configuración",
  "navigation": {
    "home": "Inicio",
    "profile": "Perfil"
  }
}
```

### Registrazione nel pubspec.yaml

Assicurati che i tuoi file di lingua siano inclusi nel tuo `pubspec.yaml`:

``` yaml
flutter:
  assets:
    - lang/
```

<div id="localizing-text"></div>

## Localizzare il Testo

Usa l'estensione `.tr()` o l'helper `trans()` per tradurre le stringhe:

``` dart
// Usando l'estensione .tr()
"welcome".tr()

// Usando l'helper trans()
trans("welcome")
```

### Chiavi Annidate

Accedi alle chiavi JSON annidate utilizzando la notazione a punto:

**lang/en.json**
``` json
{
  "navigation": {
    "home": "Home",
    "profile": "Profile"
  }
}
```

``` dart
"navigation.home".tr()       // "Home"
trans("navigation.profile")  // "Profile"
```

<div id="arguments"></div>

### Argomenti

Passa valori dinamici nelle tue traduzioni utilizzando la sintassi `{{key}}`:

**lang/en.json**
``` json
{
  "greeting": "Hello {{name}}",
  "items_count": "You have {{count}} items"
}
```

``` dart
"greeting".tr(arguments: {"name": "Anthony"})
// "Hello Anthony"

trans("items_count", arguments: {"count": "5"})
// "You have 5 items"
```

<div id="styled-text-placeholders"></div>

### Segnaposto StyledText

Quando utilizzi `StyledText.template` con stringhe localizzate, puoi usare la sintassi `{{key:text}}`. Questo mantiene il **key** stabile in tutte le lingue (in modo che i tuoi stili e gestori di tap corrispondano sempre), mentre il **text** viene tradotto per ogni lingua.

**lang/it.json**
``` json
{
  "learn_skills": "Impara {{lang:Lingue}}, {{read:Lettura}} e {{speak:Parlato}}",
  "already_have_account": "Hai già un account? {{login:Accedi}}"
}
```

**lang/es.json**
``` json
{
  "learn_skills": "Aprende {{lang:Idiomas}}, {{read:Lectura}} y {{speak:Habla}}",
  "already_have_account": "¿Ya tienes una cuenta? {{login:Iniciar sesión}}"
}
```

**Nel tuo widget:**
``` dart
StyledText.template(
  "learn_skills".tr(),
  styles: {
    "lang|read|speak": TextStyle(fontWeight: FontWeight.bold),
  },
)
```

Le chiavi `lang`, `read` e `speak` sono le stesse in ogni file di lingua, quindi la mappa degli stili funziona per tutte le lingue. Il testo visualizzato dopo i `:` è ciò che l'utente vede — "Lingue" in italiano, "Idiomas" in spagnolo, ecc.

Puoi anche usare questo con `onTap`:

``` dart
StyledText.template(
  "already_have_account".tr(),
  styles: {
    "login": TextStyle(fontWeight: FontWeight.bold),
  },
  onTap: {
    "login": () => routeTo(LoginPage.path),
  },
)
```

> **Nota:** La sintassi `{{key}}` (con prefisso `@`) è per gli argomenti sostituiti da `.tr(arguments:)` al momento della traduzione. La sintassi `{{key:text}}` (senza `@`) è per i segnaposto `StyledText` analizzati al momento del rendering. Non confonderli — usa `{{}}` per valori dinamici e `@{{}}` per gli span stilizzati.

<div id="updating-the-locale"></div>

## Aggiornare la Lingua

Cambia la lingua dell'app in fase di esecuzione:

``` dart
// Usando NyLocalization direttamente
await NyLocalization.instance.setLanguage(
  context,
  language: 'es'  // Deve corrispondere al nome del file JSON (es.json)
);
```

Se il tuo widget estende `NyPage`, usa l'helper `changeLanguage`:

``` dart
class _SettingsPageState extends NyPage<SettingsPage> {

  @override
  Widget view(BuildContext context) {
    return ListView(
      children: [
        ListTile(
          title: Text("English"),
          onTap: () => changeLanguage('en'),
        ),
        ListTile(
          title: Text("Español"),
          onTap: () => changeLanguage('es'),
        ),
      ],
    );
  }
}
```

<div id="setting-a-default-locale"></div>

## Impostare una Lingua Predefinita

Imposta la lingua predefinita nel tuo file `.env`:

``` bash
DEFAULT_LOCALE="en"
```

Oppure usa la lingua del dispositivo impostando:

``` bash
LOCALE_TYPE="device"
```

Dopo aver modificato `.env`, rigenera la configurazione dell'ambiente:

``` bash
metro make:env
```

<div id="supported-locales"></div>

## Lingue Supportate

Definisci quali lingue supporta la tua app in `LocalizationConfig`:

``` dart
static const List<Locale> supportedLocales = [
  Locale('en'),
  Locale('es'),
  Locale('fr'),
  Locale('de'),
  Locale('ar'),
];
```

Questa lista viene utilizzata da `MaterialApp.supportedLocales` di Flutter.

<div id="fallback-language"></div>

## Lingua di Fallback

Quando una chiave di traduzione non viene trovata nella lingua attiva, Nylo la cerca automaticamente nella lingua di fallback prima di restituire la chiave grezza. La lingua di fallback e' configurata in `lib/config/localization.dart`:

``` dart
static const String fallbackLanguageCode = 'en';
```

Questa risoluzione in due fasi si applica sia alle chiavi di primo livello che alle chiavi annidate con notazione a punto:

1. Cerca la chiave nel file della lingua attiva.
2. Se non trovata, cercala nel file della lingua di fallback.
3. Se ancora non trovata, restituisce la stringa della chiave grezza.

Ad esempio, se il file della lingua francese manca della chiave `settings.privacy`, la logica di fallback cerca `settings.privacy` nel file della lingua inglese prima di restituire `"settings.privacy"` come tale.

Questo assicura che la tua app non mostri mai chiavi grezze se una traduzione e' solo parzialmente completa.

<div id="rtl-support"></div>

## Supporto RTL

Nylo v7 include il supporto integrato per le lingue da destra a sinistra (RTL):

``` dart
static const List<String> rtlLanguages = ['ar', 'he', 'fa', 'ur'];

// Verifica se la lingua corrente e' RTL
if (LocalizationConfig.isRtl(currentLanguageCode)) {
  // Gestisci il layout RTL
}
```

<div id="debug-missing-keys"></div>

## Debug Chiavi Mancanti

Abilita gli avvisi per le chiavi di traduzione mancanti durante lo sviluppo:

Nel tuo file `.env`:
``` bash
DEBUG_TRANSLATIONS="true"
```

Questo registra avvisi quando `.tr()` non riesce a trovare una chiave, aiutandoti a individuare stringhe non tradotte.

<div id="nylocalization-api"></div>

## API NyLocalization

`NyLocalization` è un singleton che gestisce tutta la localizzazione. Oltre al metodo base `translate()`, fornisce diversi metodi aggiuntivi:

### Verificare se una Traduzione Esiste

``` dart
bool exists = NyLocalization.instance.hasTranslation('welcome');
// true se la chiave esiste nel file della lingua corrente

// Funziona anche con le chiavi annidate
bool nestedExists = NyLocalization.instance.hasTranslation('navigation.home');
```

### Ottenere Tutte le Chiavi di Traduzione

Utile per il debug per vedere quali chiavi sono caricate:

``` dart
List<String> keys = NyLocalization.instance.getAllKeys();
// ['welcome', 'settings', 'navigation', ...]
```

### Cambiare Lingua Senza Riavvio

Se vuoi cambiare la lingua silenziosamente (senza riavviare l'app):

``` dart
await NyLocalization.instance.setLocale(locale: Locale('fr'));
```

Questo carica il nuovo file di lingua ma **non** riavvia l'app. Utile quando vuoi gestire gli aggiornamenti dell'interfaccia manualmente.

### Verificare la Direzione RTL

``` dart
bool isRtl = NyLocalization.instance.isDirectionRTL(context);
```

### Accedere alla Lingua Corrente

``` dart
// Ottieni il codice lingua corrente
String code = NyLocalization.instance.languageCode;  // es., 'en'

// Ottieni l'oggetto Locale corrente
Locale currentLocale = NyLocalization.instance.locale;

// Ottieni i delegati di localizzazione Flutter (usati in MaterialApp)
var delegates = NyLocalization.instance.delegates;
```

### Riferimento Completo dell'API

| Metodo / Proprietà | Restituisce | Descrizione |
|---------------------|-------------|-------------|
| `instance` | `NyLocalization` | Istanza singleton |
| `translate(key, [arguments])` | `String` | Traduce una chiave con argomenti opzionali |
| `hasTranslation(key)` | `bool` | Verifica se una chiave di traduzione esiste |
| `getAllKeys()` | `List<String>` | Ottiene tutte le chiavi di traduzione caricate |
| `setLanguage(context, {language, restart})` | `Future<void>` | Cambia lingua, opzionalmente riavvia |
| `setLocale({locale})` | `Future<void>` | Cambia lingua senza riavvio |
| `setDebugMissingKeys(enabled)` | `void` | Abilita/disabilita il log delle chiavi mancanti |
| `isDirectionRTL(context)` | `bool` | Verifica se la direzione corrente è RTL |
| `restart(context)` | `void` | Riavvia l'app |
| `languageCode` | `String` | Codice lingua corrente |
| `locale` | `Locale` | Oggetto Locale corrente |
| `delegates` | `Iterable<LocalizationsDelegate>` | Delegati di localizzazione Flutter |
| `setValuesForTesting({values, fallbackValues})` | `void` | Inietta mappe di traduzione direttamente per i test unitari |

<div id="nylocalehelper"></div>

## NyLocaleHelper

`NyLocaleHelper` è una classe utility statica per le operazioni sulle lingue. Fornisce metodi per rilevare la lingua corrente, verificare il supporto RTL e creare oggetti Locale.

``` dart
// Ottieni la lingua di sistema corrente
Locale locale = NyLocaleHelper.getCurrentLocale(context: context);

// Ottieni i codici lingua e paese
String langCode = NyLocaleHelper.getLanguageCode(context: context);  // 'en'
String? countryCode = NyLocaleHelper.getCountryCode(context: context);  // 'US' o null

// Verifica se la lingua corrente corrisponde
bool isEnglish = NyLocaleHelper.matchesLocale(context, 'en');
bool isUsEnglish = NyLocaleHelper.matchesLocale(context, 'en', 'US');

// Rilevamento RTL
bool isRtl = NyLocaleHelper.isRtlLanguage('ar');  // true
bool currentIsRtl = NyLocaleHelper.isCurrentLocaleRtl(context: context);

// Ottieni la direzione del testo
TextDirection direction = NyLocaleHelper.getTextDirection('ar');  // TextDirection.rtl
TextDirection currentDir = NyLocaleHelper.getCurrentTextDirection(context: context);

// Crea un Locale da stringhe
Locale newLocale = NyLocaleHelper.toLocale('en', 'US');
```

### Riferimento Completo dell'API

| Metodo | Restituisce | Descrizione |
|--------|-------------|-------------|
| `getCurrentLocale({context})` | `Locale` | Ottiene la lingua di sistema corrente |
| `getLanguageCode({context})` | `String` | Ottiene il codice lingua corrente |
| `getCountryCode({context})` | `String?` | Ottiene il codice paese corrente |
| `matchesLocale(context, languageCode, [countryCode])` | `bool` | Verifica se la lingua corrente corrisponde |
| `isRtlLanguage(languageCode)` | `bool` | Verifica se un codice lingua è RTL |
| `isCurrentLocaleRtl({context})` | `bool` | Verifica se la lingua corrente è RTL |
| `getTextDirection(languageCode)` | `TextDirection` | Ottiene la TextDirection per una lingua |
| `getCurrentTextDirection({context})` | `TextDirection` | Ottiene la TextDirection per la lingua corrente |
| `toLocale(languageCode, [countryCode])` | `Locale` | Crea un Locale da stringhe |

La costante `rtlLanguages` contiene: `ar`, `he`, `fa`, `ur`, `yi`, `ps`, `ku`, `sd`, `dv`.

<div id="changing-language-from-controller"></div>

## Cambiare Lingua da un Controller

Se utilizzi controller con le tue pagine, puoi cambiare la lingua da `NyController`:

``` dart
class SettingsController extends NyController {

  void switchToSpanish() {
    changeLanguage('es');
  }

  void switchToEnglishNoRestart() {
    changeLanguage('en', restartState: false);
  }
}
```

Il parametro `restartState` controlla se l'app si riavvia dopo aver cambiato la lingua. Impostalo su `false` se vuoi gestire l'aggiornamento dell'interfaccia da solo.
