Perché internazionalizzare l'app

Se vuoi pubblicare la tua app su mercati diversi, supportare più lingue non è un optional. Flutter offre un sistema di internazionalizzazione (i18n) integrato e ufficiale basato sui pacchetti flutter_localizations e intl, che permette di gestire traduzioni, formattazione di date, numeri e valute in modo strutturato.

In questa guida vedremo come configurare la localizzazione, generare automaticamente le classi di traduzione a partire dai file ARB e gestire la formattazione locale-aware.

Configurazione iniziale

Aggiungi le dipendenze al tuo pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any

flutter:
  generate: true

La riga generate: true abilita la generazione automatica del codice di localizzazione da parte di Flutter.

Il file di configurazione l10n.yaml

Crea un file l10n.yaml nella root del progetto:

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations

Questo dice a Flutter dove trovare i file di traduzione e come chiamare la classe generata.

Creare i file ARB

I file ARB (Application Resource Bundle) sono semplici file JSON che contengono le coppie chiave-valore delle traduzioni. Crea lib/l10n/app_en.arb:

{
  "appTitle": "My Application",
  "@appTitle": {
    "description": "Il titolo dell'applicazione"
  },
  "welcome": "Welcome, {name}!",
  "@welcome": {
    "description": "Messaggio di benvenuto",
    "placeholders": {
      "name": {
        "type": "String"
      }
    }
  },
  "itemCount": "{count, plural, =0{Nessun elemento} =1{1 elemento} other{{count} elementi}}",
  "@itemCount": {
    "placeholders": {
      "count": {
        "type": "int"
      }
    }
  }
}

E la versione italiana in lib/l10n/app_it.arb:

{
  "appTitle": "La mia applicazione",
  "welcome": "Benvenuto, {name}!",
  "itemCount": "{count, plural, =0{Nessun elemento} =1{1 elemento} other{{count} elementi}}"
}

Nota la sintassi ICU per i plurali: gestisce automaticamente le forme singolare/plurale in base al valore numerico.

Generare le classi

Le classi vengono generate automaticamente al primo flutter run o flutter build. In alternativa puoi forzare la generazione con:

flutter gen-l10n

Verrà creata la classe AppLocalizations con un getter tipizzato per ogni chiave.

Configurare MaterialApp

Ora colleghiamo tutto nel widget radice:

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'l10n/app_localizations.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      onGenerateTitle: (context) =>
          AppLocalizations.of(context)!.appTitle,
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en'),
        Locale('it'),
      ],
      home: const HomePage(),
    );
  }
}

Usare le traduzioni nei widget

Accedere alle stringhe è semplice e completamente tipizzato:

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context)!;
    return Scaffold(
      appBar: AppBar(title: Text(l10n.appTitle)),
      body: Column(
        children: [
          Text(l10n.welcome('Marco')),
          Text(l10n.itemCount(3)),
        ],
      ),
    );
  }
}

Il vantaggio rispetto alle stringhe sparse nel codice è enorme: l'IDE ti suggerisce le chiavi disponibili e gli errori vengono segnalati a tempo di compilazione.

Formattare date, numeri e valute

Il pacchetto intl permette di formattare valori rispettando la locale corrente:

import 'package:intl/intl.dart';

final locale = Localizations.localeOf(context).toString();

// Data: 8 febbraio 2025 in italiano
final data = DateFormat.yMMMMd(locale).format(DateTime.now());

// Valuta: € 1.234,56
final prezzo = NumberFormat.currency(
  locale: locale,
  symbol: '€',
).format(1234.56);

Cambiare lingua a runtime

Per permettere all'utente di scegliere la lingua, puoi gestire il locale con uno state management (ad esempio un ValueNotifier o Riverpod) e passarlo a MaterialApp:

MaterialApp(
  locale: selectedLocale, // null = lingua di sistema
  // ... resto della configurazione
)

Quando selectedLocale cambia, Flutter ricostruisce l'interfaccia con le nuove traduzioni.

Best practice

  • Mantieni il file ARB template (app_en.arb) sempre completo: è la fonte di verità per le chiavi.
  • Usa sempre i metadati @chiave con la descrizione per aiutare i traduttori.
  • Sfrutta i plurali e i placeholder ICU invece di concatenare stringhe a mano.
  • Aggiungi i delegate Global*Localizations per tradurre anche i widget di sistema (date picker, menu, ecc.).

Conclusione

Il sistema di internazionalizzazione di Flutter è robusto, ufficiale e completamente integrato nel processo di build. Investire qualche ora nella sua configurazione iniziale ti ripaga ampiamente quando devi espandere la tua app verso nuovi mercati, mantenendo il codice pulito e type-safe.