CultivaShop — Flutter i18n Setup

Guía de implementación de internacionalización (i18n) para la app móvil de e-commerce agrícola

Flutter 3.x Material 3 flutter_localizations intl: any 3 idiomas: ES · EN · CA
Checklist de configuración inicial
1
pubspec.yaml — dependencias y flag generate
Sección relevante de pubspec.yaml pubspec.yaml
name: cultivashop
description: App móvil de e-commerce agrícola local
version: 1.0.0+1

environment:
  sdk: ^3.5.0

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:     # paquete oficial del SDK
    sdk: flutter
  intl: any               # 'any' delega la versión al SDK

flutter:
  generate: true           # activa generación automática de AppLocalizations
  uses-material-design: true
2
l10n.yaml — configuración del generador
Archivo de configuración de localización l10n.yaml
arb-dir: lib/l10n              # directorio de los archivos .arb
template-arb-file: app_es.arb    # ES como idioma base (template)
output-localization-file: app_localizations.dart
synthetic-package: true          # genera en flutter_gen (recomendado)
use-escaping: true              # activa escape con '' en mensajes ICU
nullable-getter: false          # AppLocalizations.of(context) no nullable
3
Catálogo de strings — 7 claves ARB de CultivaShop
Clave ARB
Tipo
Descripción
appTitle
estático
Nombre de la app en la barra superior
welcomeUser
placeholder
Saludo personalizado con userName
cartItemCount
plural
0 artículos / 1 artículo / N artículos
userGreeting
select
Tratamiento formal según género del usuario
errorNetwork
estático
Sin conexión a internet
errorOutOfStock
estático
Producto sin stock disponible
errorSessionExpired
estático
Sesión expirada, requiere relogin
4
Archivos ARB — los 3 idiomas
🇪🇸 app_es.arb (template) 🇬🇧 app_en.arb 🏴󠁥󠁳󠁣󠁴󠁿 app_ca.arb
{
  "@@locale": "es",

  "appTitle": "CultivaShop",
  "@appTitle": {
    "description": "Nombre de la aplicación"
  },

  "welcomeUser": "¡Bienvenido/a, {userName}!",
  "@welcomeUser": {
    "description": "Saludo en la pantalla de inicio",
    "placeholders": {
      "userName": { "type": "String", "example": "María" }
    }
  },

  "cartItemCount": "{count, plural, =0{Sin artículos} =1{1 artículo} other{{count} artículos}}",
  "@cartItemCount": {
    "description": "Cantidad de artículos en el carrito",
    "placeholders": {
      "count": { "type": "num", "format": "compact" }
    }
  },

  "userGreeting": "{gender, select, male{Bienvenido} female{Bienvenida} other{Bienvenide}}",
  "@userGreeting": {
    "description": "Saludo formal según género",
    "placeholders": {
      "gender": { "type": "String" }
    }
  },

  "errorNetwork": "Sin conexión. Comprueba tu red e inténtalo de nuevo.",
  "@errorNetwork": { "description": "Error de conectividad" },

  "errorOutOfStock": "Este producto está agotado. Te avisaremos cuando vuelva.",
  "@errorOutOfStock": { "description": "Stock agotado" },

  "errorSessionExpired": "Tu sesión ha expirado. Por favor, inicia sesión de nuevo.",
  "@errorSessionExpired": { "description": "Sesión expirada" }
}
Inglés app_en.arb
{
  "@@locale": "en",
  "appTitle": "CultivaShop",
  "welcomeUser": "Welcome, {userName}!",
  "cartItemCount": "{count, plural, =0{No items} =1{1 item} other{{count} items}}",
  "userGreeting": "{gender, select, male{Welcome, sir} female{Welcome, ma'am} other{Welcome}}",
  "errorNetwork": "No connection. Please check your network and try again.",
  "errorOutOfStock": "This product is out of stock. We'll notify you when it's back.",
  "errorSessionExpired": "Your session has expired. Please log in again."
}
Català app_ca.arb
{
  "@@locale": "ca",
  "appTitle": "CultivaShop",
  "welcomeUser": "Benvingut/da, {userName}!",
  "cartItemCount": "{count, plural, =0{Sense articles} =1{1 article} other{{count} articles}}",
  "userGreeting": "{gender, select, male{Benvingut} female{Benvinguda} other{Benvingut/da}}",
  "errorNetwork": "Sense connexió. Comprova la teva xarxa i torna-ho a intentar.",
  "errorOutOfStock": "Aquest producte està esgotat. T'avisarem quan torni a estar disponible.",
  "errorSessionExpired": "La teva sessió ha caducat. Si us plau, torna a iniciar sessió."
}
5
main.dart — configuración de MaterialApp
Punto de entrada de la aplicación lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() => runApp(CultivaShopApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // ── Localización ─────────────────────────────────────────
      localizationsDelegates: const [
        AppLocalizations.delegate,              // generado por flutter gen-l10n
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('es'),  // Español — idioma base
        Locale('en'),  // English
        Locale('ca'),  // Català
      ],
      // ─────────────────────────────────────────────────────────
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF2D6A4F),  // verde CultivaShop
        useMaterial3: true,
      ),
      home: const HomeScreen(),
    );
  }
}
6
Uso en widgets — HomeScreen con los 4 tipos de string
Widget completo con todos los tipos de localización lib/screens/home_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

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

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(l10n.appTitle),          // estático
      ),
      body: Column(
        children: [

          // placeholder — nombre de usuario
          Text(l10n.welcomeUser('María')),

          // plural — carrito
          Text(l10n.cartItemCount(cartCount)),

          // select — género
          Text(l10n.userGreeting(user.gender)),

          // estáticos — errores (condicional)
          if (hasNetworkError)
            Text(l10n.errorNetwork),

        ],
      ),
    );
  }
}
7
Flujo de trabajo — Dev a CI/CD
✏️ Editar .arb app_es.arb
(template)
🌍 Traducir app_en.arb
app_ca.arb
⚙️ flutter pub get genera
AppLocalizations
🔍 Revisar errores ARB syntax
placeholders
🚀 CI GitHub flutter build
+ test
 .github/workflows/flutter-ci.yml
name: CultivaShop CI
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.x'
      - name: Instalar deps + generar l10n
        run: flutter pub get   # dispara generación de AppLocalizations
      - name: Análisis estático
        run: flutter analyze
      - name: Tests
        run: flutter test
💡
Pro tip — nullable-getter: false
Con nullable-getter: false en l10n.yaml, AppLocalizations.of(context) devuelve directamente AppLocalizations sin ! ni null-checks. Requiere Flutter 3.18+.
⚠️
Catalan (ca) no incluido por defecto en GlobalMaterialLocalizations
Verifica que usas GlobalCupertinoLocalizations.delegate y que el paquete intl_utils reconoce 'ca'. Si ves warning en build, añade el package intl: any y corre flutter pub get de nuevo.
3 idiomas
7 strings ARB
4 tipos de string
5 archivos generados
Stack: Flutter 3.x · Material 3 · flutter_localizations · intl:any
Generado por CULTIVA IA — skill flutter-configurar-localizacion