Internacionalizar aplicaciones de Flutter
Cómo internacionalizar tu aplicación de Flutter.
Si tu aplicación podría desplegarse para usuarios que hablan otro idioma, entonces necesitarás internacionalizarla. Eso significa que necesitas escribir la aplicación de una manera que haga posible localizar valores como texto y layouts para cada idioma o locale que admita la aplicación. Flutter proporciona widgets y clases que ayudan con la internacionalización y las propias librerías de Flutter están internacionalizadas.
Esta página cubre conceptos y flujos de trabajo necesarios para
localizar una aplicación de Flutter usando las
clases MaterialApp y CupertinoApp,
ya que la mayoría de las aplicaciones se escriben de esa manera.
Sin embargo, las aplicaciones escritas usando la clase de nivel más bajo
WidgetsApp también se pueden internacionalizar
usando las mismas clases y lógica.
Introducción a las localizaciones en Flutter
#Esta sección proporciona un tutorial sobre cómo crear e internacionalizar una nueva aplicación de Flutter, junto con cualquier configuración adicional que una plataforma de destino pueda requerir.
Puedes encontrar el código fuente de este ejemplo en
gen_l10n_example.
Configurar una aplicación internacionalizada: el paquete Flutter_localizations
#
Por defecto, Flutter solo proporciona localizaciones en inglés de EE. UU.
Para añadir soporte para otros idiomas,
una aplicación debe especificar propiedades adicionales de
MaterialApp (o CupertinoApp),
e incluir un paquete llamado flutter_localizations.
Para empezar, comienza creando una nueva aplicación de Flutter
en el directorio que elijas con el comando flutter create.
flutter create <name_of_flutter_app>
Para usar flutter_localizations,
añade el paquete como una dependencia en tu archivo pubspec.yaml,
así como el paquete intl:
flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any
Esto crea un archivo pubspec.yml con las siguientes entradas:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: any
Luego importa la librería flutter_localizations y especifica
localizationsDelegates y supportedLocales para
tu MaterialApp o CupertinoApp:
import 'package:flutter_localizations/flutter_localizations.dart';
return const MaterialApp(
title: 'Localizations Sample App',
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
Locale('en'), // English
Locale('es'), // Spanish
],
home: MyHomePage(),
);
Después de introducir el paquete flutter_localizations
y añadir el código anterior,
los paquetes Material y Cupertino
deberían estar correctamente localizados en
uno de los locales admitidos.
Los widgets deberían adaptarse a los mensajes localizados,
junto con un layout correcto de izquierda a derecha o de derecha a izquierda.
Prueba cambiar el locale de la plataforma de destino a
español (es) y los mensajes deberían estar localizados.
Las aplicaciones basadas en WidgetsApp son similares excepto que el
GlobalMaterialLocalizations.delegate no es necesario.
Se prefiere el constructor completo Locale.fromSubtags
ya que admite scriptCode, aunque el
constructor por defecto de Locale
sigue siendo completamente válido.
Los elementos de la lista localizationsDelegates son
factorys que producen colecciones de valores localizados.
GlobalMaterialLocalizations.delegate proporciona strings
localizados y otros valores para la librería de Material Components.
GlobalWidgetsLocalizations.delegate
define la dirección del texto por defecto,
ya sea de izquierda a derecha o de derecha a izquierda, para la librería de widgets.
Más información sobre estas propiedades de la aplicación, los tipos de los que dependen, y cómo se estructuran típicamente las aplicaciones internacionalizadas de Flutter, se cubre en esta página.
Anular el locale
#Localizations.override es un constructor factory
para el widget Localizations que permite manejar la situación
(típicamente poco común) en la que una sección de tu aplicación
necesita estar localizada en un locale diferente al locale
configurado para tu dispositivo.
Para observar este comportamiento, añade una llamada a Localizations.override
y un simple CalendarDatePicker:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Add the following code
Localizations.override(
context: context,
locale: const Locale('es'),
// Using a Builder to get the correct BuildContext.
// Alternatively, you can create a new widget and Localizations.override
// will pass the updated BuildContext to the new widget.
child: Builder(
builder: (context) {
// A toy example for an internationalized Material widget.
return CalendarDatePicker(
initialDate: DateTime.now(),
firstDate: DateTime(1900),
lastDate: DateTime(2100),
onDateChanged: (value) {},
);
},
),
),
],
),
),
);
}
Haz Hot Reload en la aplicación y el widget CalendarDatePicker
debería volver a renderizarse en español.
Añadir tus propios mensajes localizados
#Después de añadir el paquete flutter_localizations,
puedes configurar la localización.
Para añadir texto localizado a tu aplicación,
completa las siguientes instrucciones:
-
Añade el paquete
intlcomo dependencia, trayendo la versión fijada porflutter_localizations:flutter pub add intl:any -
Abre el archivo
pubspec.yamly habilita el flaggenerate. Este flag se encuentra en la secciónflutterdel archivo pubspec.yaml# The following section is specific to Flutter. flutter: generate: true # Add this line -
Añade un nuevo archivo yaml al directorio raíz del proyecto de Flutter. Nombra este archivo
l10n.yamle incluye el siguiente contenido:yamlarb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dartEste archivo configura la herramienta de localización. En este ejemplo, has hecho lo siguiente:
- Pusiste los archivos de entrada App Resource Bundle (
.arb) en${FLUTTER_PROJECT}/lib/l10n. Los.arbproporcionan recursos de localización para tu aplicación. - Estableciste la plantilla en inglés como
app_en.arb. - Le dijiste a Flutter que genere las localizaciones en el
archivo
app_localizations.dart.
- Pusiste los archivos de entrada App Resource Bundle (
-
En
${FLUTTER_PROJECT}/lib/l10n, añade el archivo de plantillaapp_en.arb. Por ejemplo:json{ "helloWorld": "Hello World!", "@helloWorld": { "description": "The conventional newborn programmer greeting" } } -
Añade otro archivo de bundle llamado
app_es.arben el mismo directorio. En este archivo, añade la traducción al español del mismo mensaje.json{ "helloWorld": "¡Hola Mundo!" } -
Ahora, ejecuta
flutter pub getoflutter runy la generación de código (codegen) se realiza automáticamente. Deberías encontrar los archivos generados en el directorio en la ruta que especificaste con las opcionesarb-dirooutput-dir. Alternativamente, también puedes ejecutarflutter gen-l10npara generar los mismos archivos sin ejecutar la aplicación. -
Añade la sentencia de importación en
app_localizations.dartyAppLocalizations.delegateen tu llamada al constructor paraMaterialApp:dartimport 'l10n/app_localizations.dart';dartreturn const MaterialApp( title: 'Localizations Sample App', localizationsDelegates: [ AppLocalizations.delegate, // Add this line GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ Locale('en'), // English Locale('es'), // Spanish ], home: MyHomePage(), );La clase
AppLocalizationstambién proporciona listas auto-generadas delocalizationsDelegatesysupportedLocales. Puedes usar estas en lugar de proporcionarlas manualmente.dartconst MaterialApp( title: 'Localizations Sample App', localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, ); -
Una vez que la aplicación Material ha iniciado, puedes usar
AppLocalizationsen cualquier lugar de tu aplicación:dartappBar: AppBar( // The [AppBar] title text should update its message // according to the system locale of the target platform. // Switching between English and Spanish locales should // cause this text to update. title: Text(AppLocalizations.of(context)!.helloWorld), ),
Este código genera un widget Text que muestra "Hello World!"
si el locale del dispositivo de destino está configurado en inglés,
y "¡Hola Mundo!" si el locale del dispositivo de destino está configurado
en español. En los archivos arb,
la clave de cada entrada se usa como el nombre del método del getter,
mientras que el valor de esa entrada contiene el mensaje localizado.
El gen_l10n_example
usa esta herramienta.
Para localizar la descripción de tu aplicación de dispositivo,
pasa el string localizado a
MaterialApp.onGenerateTitle:
return MaterialApp(
onGenerateTitle: (context) => DemoLocalizations.of(context).title,
Marcadores de posición, plurales y selecciones
#También puedes incluir valores de la aplicación en un mensaje con
sintaxis especial que usa un marcador de posición (placeholder) para generar un método
en lugar de un getter.
Un marcador de posición, que debe ser un nombre identificador de Dart válido,
se convierte en un parámetro posicional en el método generado en el
código de AppLocalizations. Define un nombre de marcador de posición envolviéndolo
en llaves como se muestra a continuación:
"{placeholderName}"
Define cada marcador de posición en el objeto placeholders
en el archivo .arb de la aplicación. Por ejemplo,
para definir un mensaje de saludo con un parámetro userName,
añade lo siguiente a lib/l10n/app_en.arb:
"hello": "Hello {userName}",
"@hello": {
"description": "A message with a single parameter",
"placeholders": {
"userName": {
"type": "String",
"example": "Bob"
}
}
}
Este fragmento de código añade una llamada al método hello al
objeto AppLocalizations.of(context),
y el método acepta un parámetro de tipo String;
el método hello devuelve un string.
Regenera el archivo AppLocalizations.
Reemplaza el código pasado a Builder con lo siguiente:
// Examples of internationalized strings.
return Column(
children: <Widget>[
// Returns 'Hello John'
Text(AppLocalizations.of(context)!.hello('John')),
],
);
También puedes usar marcadores de posición numéricos para especificar múltiples valores.
Diferentes idiomas tienen diferentes formas de pluralizar palabras.
La sintaxis también admite especificar cómo se debe pluralizar una palabra.
Un mensaje pluralizado debe incluir un parámetro num que indique
cómo pluralizar la palabra en diferentes situaciones.
El inglés, por ejemplo, pluraliza "person" a "people",
pero eso no es suficiente.
El plural de message0 podría ser "no people" o "zero people".
El plural de messageFew podría ser
"several people", "some people", o "a few people".
El plural de messageMany podría
ser "most people" o "many people", o "a crowd".
Solo el campo más general messageOther es obligatorio.
El siguiente ejemplo muestra qué opciones están disponibles:
"{countPlaceholder, plural, =0{message0} =1{message1} =2{message2} few{messageFew} many{messageMany} other{messageOther}}"
La expresión anterior se reemplaza por la variación del mensaje
(message0, message1, ...) correspondiente al valor
del countPlaceholder.
Solo el campo messageOther es obligatorio.
El siguiente ejemplo define un mensaje que pluraliza la palabra "wombat":
"nWombats": "{count, plural, =0{no wombats} =1{1 wombat} other{{count} wombats}}",
"@nWombats": {
"description": "A plural message",
"placeholders": {
"count": {
"type": "num",
"format": "compact"
}
}
}
Usa un método plural pasando el parámetro count:
// Examples of internationalized strings.
return Column(
children: <Widget>[
...
// Returns 'no wombats'
Text(AppLocalizations.of(context)!.nWombats(0)),
// Returns '1 wombat'
Text(AppLocalizations.of(context)!.nWombats(1)),
// Returns '5 wombats'
Text(AppLocalizations.of(context)!.nWombats(5)),
],
);
De forma similar a los plurales,
también puedes elegir un valor basado en un marcador de posición de tipo String.
Esto se utiliza más a menudo para admitir idiomas con género.
La sintaxis es la siguiente:
"{selectPlaceholder, select, case{message} ... other{messageOther}}"
El siguiente ejemplo define un mensaje que selecciona un pronombre basado en el género:
"pronoun": "{gender, select, male{he} female{she} other{they}}",
"@pronoun": {
"description": "A gendered message",
"placeholders": {
"gender": {
"type": "String"
}
}
}
Usa esta característica pasando la cadena de género como parámetro:
// Examples of internationalized strings.
return Column(
children: <Widget>[
...
// Returns 'he'
Text(AppLocalizations.of(context)!.pronoun('male')),
// Returns 'she'
Text(AppLocalizations.of(context)!.pronoun('female')),
// Returns 'they'
Text(AppLocalizations.of(context)!.pronoun('other')),
],
);
Ten en cuenta que al usar sentencias select,
la comparación entre el parámetro y el valor
real distingue entre mayúsculas y minúsculas.
Es decir, AppLocalizations.of(context)!.pronoun("Male")
por defecto recurre al caso "other", y devuelve "they".
Sintaxis de escape
#A veces, tienes que usar tokens,
tales como { y }, como caracteres normales.
Para ignorar dichos tokens sin que sean analizados,
habilita el flag use-escaping añadiendo lo
siguiente a l10n.yaml:
use-escaping: true
El analizador ignora cualquier cadena de caracteres
envuelta con un par de comillas simples.
Para usar un carácter de comilla simple normal,
usa un par de comillas simples consecutivas.
Por ejemplo, el siguiente texto se convierte
a un String de Dart:
{
"helloWorld": "Hello! '{Isn''t}' this a wonderful day?"
}
El string resultante es el siguiente:
"Hello! {Isn't} this a wonderful day?"
Mensajes con números y monedas
#Los números, incluidos los que representan valores de moneda,
se muestran de manera muy diferente en distintas localizaciones.
La herramienta de generación de localizaciones en
flutter_localizations usa la clase
NumberFormat
en el paquete intl para formatear
números según el locale y el formato deseado.
Los tipos int, double y num pueden usar cualquiera de los
siguientes constructores de NumberFormat:
| Valor de "formato" del mensaje | Salida para 1200000 |
|---|---|
compact | "1.2M" |
compactCurrency* | "$1.2M" |
compactSimpleCurrency* | "$1.2M" |
compactLong | "1.2 millones" |
currency* | "USD1,200,000.00" |
decimalPattern | "1,200,000" |
decimalPatternDigits* | "1,200,000" |
decimalPercentPattern* | "120,000,000%" |
percentPattern | "120,000,000%" |
scientificPattern | "1E6" |
simpleCurrency* | "$1,200,000" |
Los constructores con asterisco de NumberFormat en la tabla
ofrecen parámetros nombrados opcionales.
Esos parámetros se pueden especificar como el valor
del objeto optionalParameters del marcador de posición.
Por ejemplo, para especificar el parámetro opcional decimalDigits
para compactCurrency,
realiza los siguientes cambios en el archivo lib/l10n/app_en.arb:
"numberOfDataPoints": "Number of data points: {value}",
"@numberOfDataPoints": {
"description": "A message with a formatted int parameter",
"placeholders": {
"value": {
"type": "int",
"format": "compactCurrency",
"optionalParameters": {
"decimalDigits": 2
}
}
}
}
Mensajes con fechas
#Las cadenas de fechas se formatean de muchas maneras diferentes dependiendo tanto del locale como de las necesidades de la aplicación.
Los valores del marcador de posición con tipo DateTime se formatean con
DateFormat
en el paquete intl.
Hay 41 variaciones de formato,
identificadas por los nombres de sus constructores factory de DateFormat.
En el siguiente ejemplo, el valor de DateTime
que aparece en el mensaje helloWorldOn se
formatea con DateFormat.yMd:
"helloWorldOn": "Hello World on {date}",
"@helloWorldOn": {
"description": "A message with a date parameter",
"placeholders": {
"date": {
"type": "DateTime",
"format": "yMd"
}
}
}
En una aplicación donde el locale es inglés de EE. UU., la siguiente expresión produciría "7/9/1959". En un locale ruso, produciría "9.07.1959".
AppLocalizations.of(context).helloWorldOn(DateTime.utc(1959, 7, 9))
Localizar para iOS: Actualizar el bundle de la aplicación iOS
#Aunque las localizaciones son gestionadas por Flutter, necesitas añadir los idiomas admitidos en el proyecto de Xcode. Esto garantiza que tu entrada en la App Store muestre correctamente los idiomas admitidos.
Para configurar los locales admitidos por tu aplicación, usa las siguientes instrucciones:
Abre el archivo Xcode
ios/Runner.xcodeprojde tu proyecto.-
En el Project Navigator, selecciona el archivo de proyecto
Runnerdebajo de Projects. Selecciona la pestaña
Infoen el editor del proyecto.-
En la sección Localizations, haz clic en el botón
Add(+) para añadir los idiomas y regiones admitidos a tu proyecto. Cuando se te pida elegir archivos e idioma de referencia, simplemente seleccionaFinish. -
Xcode crea automáticamente archivos
.stringsvacíos y actualiza el archivoios/Runner.xcodeproj/project.pbxproj. Estos archivos son utilizados por la App Store para determinar qué idiomas y regiones admite tu aplicación.
Temas avanzados para una mayor personalización
#Esta sección cubre formas adicionales de personalizar una aplicación de Flutter localizada.
Definición avanzada de locales
#Algunos idiomas con múltiples variantes requieren más que solo un código de idioma para diferenciarse correctamente.
Por ejemplo, diferenciar completamente todas las variantes de chino requiere especificar el código de idioma, el código de escritura (script code), y el código de país. Esto se debe a la existencia de escritura simplificada y tradicional, así como a diferencias regionales en la forma en que se escriben los caracteres dentro del mismo tipo de escritura.
Para expresar completamente cada variante de chino para los
códigos de país CN, TW y HK, la lista de locales
admitidos debería incluir:
supportedLocales: [
Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hans',
), // generic simplified Chinese 'zh_Hans'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant',
), // generic traditional Chinese 'zh_Hant'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hans',
countryCode: 'CN',
), // 'zh_Hans_CN'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant',
countryCode: 'TW',
), // 'zh_Hant_TW'
Locale.fromSubtags(
languageCode: 'zh',
scriptCode: 'Hant',
countryCode: 'HK',
), // 'zh_Hant_HK'
],
Esta definición completa y explícita garantiza que tu aplicación pueda
distinguir y proporcionar contenido localizado con todos los matices a todas
las combinaciones de estos códigos de país.
Si el locale preferido del usuario no está especificado,
Flutter selecciona la coincidencia más cercana,
que probablemente contiene diferencias con respecto a lo que el usuario espera.
Flutter solo se resuelve a locales definidos en supportedLocales
y proporciona contenido localizado diferenciado por scriptCode
para idiomas comúnmente utilizados.
Consulta Localizations
para obtener información sobre cómo se resuelven los locales
admitidos y los locales preferidos.
Aunque el chino es un ejemplo principal,
otros idiomas como el francés (fr_FR, fr_CA)
también deberían estar completamente diferenciados para una localización con más matices.
Rastrear el locale: La clase Locale y el widget Localizations
#La clase Locale identifica el idioma del usuario.
Los dispositivos móviles admiten establecer el locale para todas las aplicaciones,
usualmente usando un menú de configuración del sistema.
Las aplicaciones internacionalizadas responden mostrando valores que son
específicos del locale. Por ejemplo, si el usuario cambia el locale del dispositivo
de inglés a francés, entonces un widget Text que originalmente
mostraba "Hello World" se reconstruiría con "Bonjour le monde".
El widget Localizations
define el locale
para su hijo y los recursos localizados de los que depende el hijo.
El widget WidgetsApp
crea un widget Localizations
y lo reconstruye si el locale del sistema cambia.
Siempre puedes consultar el locale actual de una aplicación con
Localizations.localeOf():
Locale myLocale = Localizations.localeOf(context);
Especificar el parámetro supportedLocales de la aplicación
#Aunque la librería flutter_localizations
admite muchos idiomas y variantes de idioma,
por defecto solo están disponibles las traducciones en idioma inglés.
Depende del desarrollador decidir exactamente qué idiomas admitir.
El parámetro supportedLocales
de MaterialApp limita los cambios de locale. Cuando el usuario cambia la configuración
de locale en su dispositivo, el widget Localizations de la aplicación solo
sigue el cambio si el nuevo locale es miembro de esta lista.
Si no se encuentra una coincidencia exacta para el locale del dispositivo,
entonces se utiliza el primer locale admitido con un languageCode
coincidente. Si eso falla, entonces se utiliza el primer elemento de la
lista supportedLocales.
Una aplicación que quiera usar un método diferente de "resolución de locale"
puede proporcionar un localeResolutionCallback.
Por ejemplo, para hacer que tu aplicación acepte incondicionalmente
cualquier locale que seleccione el usuario:
MaterialApp(
localeResolutionCallback: (locale, supportedLocales) {
return locale;
},
);
Configurar el archivo l10n.yaml
#El archivo l10n.yaml te permite configurar la herramienta gen-l10n
para especificar lo siguiente:
- dónde se ubican todos los archivos de entrada
- dónde deberían crearse todos los archivos de salida
- qué nombre de clase Dart darle a tu delegado de localizaciones
Para ver una lista completa de opciones, ejecuta flutter gen-l10n --help
en la línea de comandos o consulta la siguiente tabla:
| Opción | Descripción |
|---|---|
arb-dir |
El directorio donde se ubican la plantilla y los archivos arb traducidos. El valor por defecto es
lib/l10n
. |
output-dir |
El directorio donde se escriben las clases de localización generadas. Esta opción solo es relevante si quieres generar el código de localizaciones en otro lugar del proyecto de Flutter. También necesitas establecer el flag
synthetic-package
en false.
La aplicación debe importar el archivo especificado en la opción output-localization-file
desde este directorio. Si no se especifica, el valor por defecto es el mismo directorio que el directorio de entrada especificado en
arb-dir
. |
template-arb-file |
El archivo arb de plantilla que se utiliza como base para generar los archivos de mensajes y localización de Dart. El valor por defecto es
app_en.arb
. |
output-localization-file |
El nombre de archivo para las clases de salida de localización y delegado de localizaciones. El valor por defecto es
app_localizations.dart
. |
untranslated-messages-file |
La ubicación de un archivo que describe los mensajes de localización que aún no se han traducido. El uso de esta opción crea un archivo JSON en la ubicación de destino, con el siguiente formato:
"locale": ["message_1", "message_2" ... "message_n"]
Si no se especifica esta opción, se imprime un resumen de los mensajes que no se han traducido en la línea de comandos. |
output-class |
El nombre de clase Dart a utilizar para las clases de salida de localización y delegado de localizaciones. El valor por defecto es
AppLocalizations
. |
preferred-supported-locales |
La lista de locales admitidos preferidos para la aplicación. Por defecto, la herramienta genera la lista de locales admitidos en orden alfabético. Usa este flag para establecer por defecto un locale diferente.
Por ejemplo, pasa [ en_US ]
para establecer inglés americano por defecto si el dispositivo lo admite. |
header |
El encabezado a anteponer a los archivos de localizaciones de Dart generados. Esta opción toma un string.
Por ejemplo, pasa "/// All localized files."
para anteponer este string al archivo Dart generado.
Alternativamente, consulta la opción header-file
para pasar un archivo de texto para encabezados más largos. |
header-file |
El encabezado a anteponer a los archivos de localizaciones de Dart generados. El valor de esta opción es el nombre del archivo que contiene el texto de encabezado que se inserta en la parte superior de cada archivo Dart generado.
Alternativamente, consulta la opción header
para pasar un string para un encabezado más simple.
Este archivo debe colocarse en el directorio especificado en arb-dir
. |
[no-]use-deferred-loading |
Especifica si se debe generar el archivo de localización de Dart con los locales importados como diferidos (deferred), lo que permite la carga diferida (lazy loading) de cada locale en Flutter Web.
Esto puede reducir el tiempo de inicio inicial de una aplicación web al disminuir el tamaño del bundle de JavaScript. Cuando este flag se establece en true, los mensajes para un locale en particular solo son descargados y cargados por la aplicación de Flutter según se necesiten. Para proyectos con muchos locales diferentes y muchas cadenas de localización, puede mejorar el rendimiento diferir la carga. Para proyectos con un número pequeño de locales, la diferencia es insignificante y podría ralentizar el inicio en comparación con incluir las localizaciones con el resto de la aplicación. Ten en cuenta que este flag no afecta a otras plataformas como mobile o desktop. |
gen-inputs-and-outputs-list |
Cuando se especifica, la herramienta genera un archivo JSON que contiene las entradas y salidas de la herramienta, llamado
gen_l10n_inputs_and_outputs.json
.
Esto puede ser útil para realizar un seguimiento de qué archivos del proyecto de Flutter se utilizaron al generar el último conjunto de localizaciones. Por ejemplo, el sistema de build de la herramienta de Flutter usa este archivo para realizar un seguimiento de cuándo llamar a gen_l10n durante Hot Reload. El valor de esta opción es el directorio donde se genera el archivo JSON. Cuando es null, el archivo JSON no se generará. |
synthetic-package |
Determina si los archivos de salida generados se generan como un paquete sintético o en un directorio especificado en el proyecto de Flutter. Este flag es
true
por defecto. Cuando
synthetic-package
se establece en
false
, genera los archivos de localizaciones en el directorio especificado por
arb-dir
por defecto. Si se especifica
output-dir
, los archivos se generan allí. |
project-dir |
Cuando se especifica, la herramienta usa la ruta pasada a esta opción como el directorio raíz del proyecto de Flutter.
Cuando es null, se utiliza la ruta relativa al directorio de trabajo actual. |
[no-]required-resource-attributes |
Requiere que todos los id de recurso contengan un atributo de recurso correspondiente.
Por defecto, los mensajes simples no requerirán metadatos, pero se recomienda encarecidamente ya que esto proporciona contexto sobre el significado de un mensaje a los lectores. Los atributos de recurso siguen siendo necesarios para los mensajes plurales. |
[no-]nullable-getter |
Especifica si el getter de la clase de localizaciones admite nulos (is nullable).
Por defecto, este valor es true para que Localizations.of(context)
devuelva un valor anulable para compatibilidad hacia atrás. Si este valor es false, entonces se realiza una verificación de null en el valor devuelto de
Localizations.of(context)
, eliminando la necesidad de verificación de null en el código del usuario. |
[no-]format |
Cuando se especifica, el comando dart format se ejecuta después de generar los archivos de localización. |
use-escaping |
Especifica si se habilita el uso de comillas simples como sintaxis de escape. |
[no-]suppress-warnings | Cuando se especifica, se suprimen todas las advertencias. |
[no-]relax-syntax |
Cuando se especifica, la sintaxis se relaja de modo que el carácter especial "{" se trata como un string si no va seguido de un marcador de posición válido y "}" se trata como un string si no cierra ningún "{" anterior que se trate como un carácter especial. |
[no-]use-named-parameters |
Si se deben usar parámetros nombrados para los métodos de localización generados. |
Cómo funciona la internacionalización en Flutter
#Esta sección cubre los detalles técnicos de cómo funcionan las localizaciones en Flutter. Si estás planeando admitir tu propio conjunto de mensajes localizados, el siguiente contenido será de ayuda. De lo contrario, puedes omitir esta sección.
Cargar y recuperar valores localizados
#El widget Localizations se utiliza para cargar y
buscar objetos que contienen colecciones de valores localizados.
Las aplicaciones se refieren a estos objetos con Localizations.of(context,type).
Si el locale del dispositivo cambia,
el widget Localizations carga automáticamente los valores para
el nuevo locale y luego reconstruye los widgets que lo usaron.
Esto sucede porque Localizations funciona como un
InheritedWidget.
Cuando una función de build se refiere a un inherited widget,
se crea una dependencia implícita en el inherited widget.
Cuando un inherited widget cambia
(cuando cambia el locale del widget Localizations),
sus contextos dependientes son reconstruidos.
Los valores localizados son cargados por la lista de
LocalizationsDelegates del widget Localizations.
Cada delegado debe definir un método asíncrono load()
que produce un objeto que encapsula una
colección de valores localizados.
Típicamente estos objetos definen un método por valor localizado.
En una aplicación grande, diferentes módulos o paquetes pueden empaquetarse con
sus propias localizaciones. Es por eso que el widget Localizations
gestiona una tabla de objetos, uno por cada LocalizationsDelegate.
Para recuperar el objeto producido por uno de los métodos load de un LocalizationsDelegate,
especifica un BuildContext y el tipo de objeto.
Por ejemplo,
los strings localizados para los widgets de Material Components
están definidos por la clase MaterialLocalizations.
Las instancias de esta clase son creadas por un LocalizationDelegate
proporcionado por la clase MaterialApp.
Se pueden recuperar con Localizations.of():
Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
Esta expresión particular Localizations.of() se usa con frecuencia,
por lo que la clase MaterialLocalizations proporciona un atajo conveniente:
static MaterialLocalizations of(BuildContext context) {
return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
}
/// References to the localized values defined by MaterialLocalizations
/// are typically written like this:
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
Definir una clase para los recursos localizados de la aplicación
#Armar una aplicación de Flutter internacionalizada usualmente comienza con la clase que encapsula los valores localizados de la aplicación. El ejemplo que sigue es típico de tales clases.
Código fuente completo de intl_example
para esta aplicación.
Este ejemplo se basa en las API y herramientas proporcionadas por el
paquete intl. La sección Una clase alternativa para los recursos localizados de la aplicación
describe un ejemplo
que no depende del paquete intl.
La clase DemoLocalizations
(definida en el siguiente fragmento de código)
contiene los strings de la aplicación (solo uno para el ejemplo)
traducidos a los locales que admite la aplicación.
Usa la función initializeMessages()
generada por el paquete intl de Dart,
Intl.message(), para buscarlos.
class DemoLocalizations {
DemoLocalizations(this.localeName);
static Future<DemoLocalizations> load(Locale locale) {
final String name =
locale.countryCode == null || locale.countryCode!.isEmpty
? locale.languageCode
: locale.toString();
final String localeName = Intl.canonicalizedLocale(name);
return initializeMessages(localeName).then((_) {
return DemoLocalizations(localeName);
});
}
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
final String localeName;
String get title {
return Intl.message(
'Hello World',
name: 'title',
desc: 'Title for the Demo application',
locale: localeName,
);
}
}
Una clase basada en el paquete intl importa un catálogo de mensajes
generado que proporciona la función initializeMessages()
y el almacenamiento de respaldo por locale para Intl.message().
El catálogo de mensajes se produce mediante una herramienta de intl
que analiza el código fuente en busca de clases que contengan
llamadas a Intl.message().
En este caso, esa sería simplemente la clase DemoLocalizations.
Añadir soporte para un nuevo idioma
#Una aplicación que necesita admitir un idioma que no está incluido en
GlobalMaterialLocalizations
tiene que hacer algún trabajo adicional:
debe proporcionar alrededor de 70 traducciones ("localizaciones")
para palabras o frases y los patrones de fecha y símbolos para el
locale.
Consulta lo siguiente para ver un ejemplo de cómo añadir soporte para el idioma noruego Nynorsk.
Una nueva subclase de GlobalMaterialLocalizations define las
localizaciones de las que depende la librería Material.
También debe definirse una nueva subclase de LocalizationsDelegate, que sirve
como factory para la subclase de GlobalMaterialLocalizations.
Aquí está el código fuente para el ejemplo completo add_language,
menos las traducciones reales a Nynorsk.
La subclase de GlobalMaterialLocalizations específica del locale
se llama NnMaterialLocalizations,
y la subclase de LocalizationsDelegate es
_NnMaterialLocalizationsDelegate.
El valor de NnMaterialLocalizations.delegate
es una instancia del delegado, y es todo
lo que necesita una aplicación que usa estas localizaciones.
La clase delegada incluye localizaciones básicas de formato de fecha y número.
Todas las demás localizaciones se definen mediante getters de propiedades
con valor String en NnMaterialLocalizations, como este:
@override
String get moreButtonTooltip => r'More';
@override
String get aboutListTileTitleRaw => r'About $applicationName';
@override
String get alertDialogLabel => r'Alert';
Estas son las traducciones al inglés, por supuesto. Para completar el trabajo, necesitas cambiar el valor de retorno de cada getter a una cadena apropiada en Nynorsk.
Los getters devuelven cadenas "raw" de Dart que tienen un prefijo r,
tales como r'About $applicationName',
porque a veces las cadenas contienen variables con un prefijo $.
Las variables se expanden mediante métodos de localización parametrizados:
@override
String get pageRowsInfoTitleRaw => r'$firstRow–$lastRow of $rowCount';
@override
String get pageRowsInfoTitleApproximateRaw =>
r'$firstRow–$lastRow of about $rowCount';
Los patrones de fecha y símbolos del locale también deben especificarse, los cuales se definen en el código fuente de la siguiente manera:
const nnLocaleDatePatterns = {
'd': 'd.',
'E': 'ccc',
'EEEE': 'cccc',
'LLL': 'LLL',
// ...
}
const Map<String, Object?> nnDateSymbols = {
'NAME': 'nn',
'ERAS': <dynamic>['f.Kr.', 'e.Kr.'],
Estos valores deben ser modificados para que el locale use el formato de fecha
correcto. Desafortunadamente, dado que la librería intl no
comparte la misma flexibilidad para el formateo de números,
el formateo para un locale existente debe ser usado
como sustituto en _NnMaterialLocalizationsDelegate:
class _NnMaterialLocalizationsDelegate
extends LocalizationsDelegate<MaterialLocalizations> {
const _NnMaterialLocalizationsDelegate();
@override
bool isSupported(Locale locale) => locale.languageCode == 'nn';
@override
Future<MaterialLocalizations> load(Locale locale) async {
final String localeName = intl.Intl.canonicalizedLocale(locale.toString());
// The locale (in this case `nn`) needs to be initialized into the custom
// date symbols and patterns setup that Flutter uses.
date_symbol_data_custom.initializeDateFormattingCustom(
locale: localeName,
patterns: nnLocaleDatePatterns,
symbols: intl.DateSymbols.deserializeFromMap(nnDateSymbols),
);
return SynchronousFuture<MaterialLocalizations>(
NnMaterialLocalizations(
localeName: localeName,
// The `intl` library's NumberFormat class is generated from CLDR data
// (see https://github.com/dart-lang/i18n/blob/main/pkgs/intl/lib/number_symbols_data.dart).
// Unfortunately, there is no way to use a locale that isn't defined in
// this map and the only way to work around this is to use a listed
// locale's NumberFormat symbols. So, here we use the number formats
// for 'en_US' instead.
decimalFormat: intl.NumberFormat('#,##0.###', 'en_US'),
twoDigitZeroPaddedFormat: intl.NumberFormat('00', 'en_US'),
// DateFormat here will use the symbols and patterns provided in the
// `date_symbol_data_custom.initializeDateFormattingCustom` call above.
// However, an alternative is to simply use a supported locale's
// DateFormat symbols, similar to NumberFormat above.
fullYearFormat: intl.DateFormat('y', localeName),
compactDateFormat: intl.DateFormat('yMd', localeName),
shortDateFormat: intl.DateFormat('yMMMd', localeName),
mediumDateFormat: intl.DateFormat('EEE, MMM d', localeName),
longDateFormat: intl.DateFormat('EEEE, MMMM d, y', localeName),
yearMonthFormat: intl.DateFormat('MMMM y', localeName),
shortMonthDayFormat: intl.DateFormat('MMM d'),
),
);
}
@override
bool shouldReload(_NnMaterialLocalizationsDelegate old) => false;
}
Para obtener más información sobre las cadenas de localización, consulta el README de flutter_localizations.
Una vez que hayas implementado tus subclases específicas de idioma de
GlobalMaterialLocalizations y LocalizationsDelegate,
necesitas añadir el idioma y una instancia del delegado a tu aplicación.
El siguiente código establece el idioma de la aplicación en Nynorsk y
añade la instancia del delegado NnMaterialLocalizations a la lista
localizationsDelegates de la aplicación:
const MaterialApp(
localizationsDelegates: [
GlobalWidgetsLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
NnMaterialLocalizations.delegate, // Add the newly created delegate
],
supportedLocales: [Locale('en', 'US'), Locale('nn')],
home: Home(),
),
Flujos de trabajo de internacionalización alternativos
#Esta sección describe diferentes enfoques para internacionalizar tu aplicación de Flutter.
Una clase alternativa para los recursos localizados de la aplicación
#El ejemplo anterior se definió en términos del paquete intl de Dart.
Puedes elegir tu propio enfoque para gestionar
valores localizados en aras de la simplicidad o tal vez para integrarte
con un framework de i18n diferente.
Código fuente completo para la aplicación minimal.
En el siguiente ejemplo, la clase DemoLocalizations
incluye todas sus traducciones directamente en Maps por idioma:
class DemoLocalizations {
DemoLocalizations(this.locale);
final Locale locale;
static DemoLocalizations of(BuildContext context) {
return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;
}
static const _localizedValues = <String, Map<String, String>>{
'en': {'title': 'Hello World'},
'es': {'title': 'Hola Mundo'},
};
static List<String> languages() => _localizedValues.keys.toList();
String get title {
return _localizedValues[locale.languageCode]!['title']!;
}
}
En la aplicación mínima, el DemoLocalizationsDelegate es ligeramente
diferente. Su método load devuelve un SynchronousFuture
porque no es necesario que se realice ninguna carga asíncrona.
class DemoLocalizationsDelegate
extends LocalizationsDelegate<DemoLocalizations> {
const DemoLocalizationsDelegate();
@override
bool isSupported(Locale locale) =>
DemoLocalizations.languages().contains(locale.languageCode);
@override
Future<DemoLocalizations> load(Locale locale) {
// Returning a SynchronousFuture here because an async "load" operation
// isn't needed to produce an instance of DemoLocalizations.
return SynchronousFuture<DemoLocalizations>(DemoLocalizations(locale));
}
@override
bool shouldReload(DemoLocalizationsDelegate old) => false;
}
Usar las herramientas intl de Dart
#Antes de construir una API usando el paquete intl de Dart,
revisa la documentación del paquete intl.
La siguiente lista resume el proceso para
localizar una aplicación que depende del paquete intl:
La aplicación de demostración depende de un archivo fuente generado llamado
l10n/messages_all.dart, que define todas las
cadenas localizables utilizadas por la aplicación.
Reconstruir l10n/messages_all.dart requiere dos pasos.
-
Con el directorio raíz de la aplicación como directorio actual, genera
l10n/intl_messages.arba partir delib/main.dart:dart run intl_translation:extract_to_arb --output-dir=lib/l10n lib/main.dartEl archivo
intl_messages.arbes un mapa en formato JSON con una entrada por cada funciónIntl.message()definida enmain.dart. Este archivo sirve como plantilla para las traducciones en inglés y español,intl_en.arbeintl_es.arb. Estas traducciones son creadas por ti, el desarrollador. -
Con el directorio raíz de la aplicación como directorio actual, genera
intl_messages_<locale>.dartpara cada archivointl_<locale>.arbeintl_messages_all.dart, el cual importa todos los archivos de mensajes:dart run intl_translation:generate_from_arb \ --output-dir=lib/l10n --no-use-deferred-loading \ lib/main.dart lib/l10n/intl_*.arbWindows no admite comodines en nombres de archivo. En su lugar, enumera los archivos .arb que fueron generados por el comando
intl_translation:extract_to_arb.dart run intl_translation:generate_from_arb \ --output-dir=lib/l10n --no-use-deferred-loading \ lib/main.dart \ lib/l10n/intl_en.arb lib/l10n/intl_fr.arb lib/l10n/intl_messages.arbLa clase
DemoLocalizationsusa la función generadainitializeMessages()(definida enintl_messages_all.dart) para cargar los mensajes localizados eIntl.message()para buscarlos.
Más información
#Si aprendes mejor leyendo código, consulta los siguientes ejemplos.
minimal
El ejemplominimalestá diseñado para ser lo más simple posible.intl_example
usa las API y herramientas proporcionadas por el paqueteintl.
Si el paquete intl de Dart es nuevo para ti,
consulta Usar las herramientas intl de Dart.
A menos que se indique lo contrario, la documentación de este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-05-11. Ver código fuente oreportar un problema.