Encontrar las claves faltantes en los archivos ARB de una app Flutter

Actualizado el · LocalePilot

Una clave añadida en inglés y olvidada en alemán, un placeholder renombrado en un solo idioma: estos errores compilan sin problema y terminan delante de tus usuarios. Así puedes detectarlos antes de publicar.

¿Tienes prisa? Revisa tus archivos ARB directamente en el navegador: gratis, sin registro, no se sube nada.

Repaso rápido: cómo se organizan los archivos ARB

Con flutter gen-l10n, cada idioma tiene su propio archivo (app_en.arb, app_es.arb…). El archivo de plantilla, definido por template-arb-file en l10n.yaml, es la referencia: contiene todas las claves y sus metadatos @key (descripción, placeholders).

{
  "cartCount": "{count, plural, =0{Empty cart} =1{1 item} other{{count} items}}",
  "@cartCount": {
    "placeholders": { "count": { "type": "int" } }
  }
}

1. Deja que Flutter liste los mensajes sin traducir

Añade la opción untranslated-messages-file a l10n.yaml:

arb-dir: lib/l10n
template-arb-file: app_en.arb
untranslated-messages-file: untranslated.json

En cada generación, Flutter escribe en ese archivo las claves de la plantilla que faltan en cada idioma. Es la verificación más sencilla de configurar; asegúrate de que el archivo esté vacío (o solo contenga {}) antes de publicar.

2. Compara las claves con un pequeño script en Dart

Para una verificación en CI que falle de forma clara, basta con un script:

import 'dart:convert';
import 'dart:io';

void main() {
  final dir = Directory('lib/l10n');
  final template = _keys(File('lib/l10n/app_en.arb'));
  var failed = false;
  for (final file in dir.listSync().whereType<File>()) {
    if (!file.path.endsWith('.arb')) continue;
    final missing = template.difference(_keys(file));
    if (missing.isNotEmpty) {
      failed = true;
      print('${file.path}: ${missing.join(', ')}');
    }
  }
  if (failed) exit(1);
}

Set<String> _keys(File f) => (jsonDecode(f.readAsStringSync()) as Map<String, dynamic>)
    .keys
    .where((k) => !k.startsWith('@'))
    .toSet();

Las claves que empiezan por @ son metadatos, así que se excluyen de la comparación.

3. Verifica los placeholders

Una clave presente en todos los idiomas todavía puede romperse: si la traducción al francés usa {nombre} mientras la plantilla declara {count}, gen-l10n añade nombre como parámetro adicional en todas las configuraciones regionales, y las llamadas existentes dejan de compilar. Al revés, una traducción que simplemente omite un placeholder pasa sin ningún aviso (los detalles están en la guía de plurales y placeholders ICU). Para cada clave, compara los nombres entre llaves con los declarados en @key.placeholders. Cuidado con los mensajes ICU (plural, select): palabras clave como other no son placeholders.

4. Vigila también los valores vacíos

Algunos flujos de traducción crean la clave con un valor vacío hasta que se traduce. No aparece como «faltante», así que añade una verificación que señale las cadenas vacías en todos los idiomas excepto la plantilla.

Lista de verificación antes de publicar

  • El archivo de mensajes sin traducir está vacío.
  • Todos los idiomas tienen las mismas claves que la plantilla.
  • Los placeholders coinciden, clave por clave.
  • No hay valores vacíos fuera de la plantilla.
  • Los mensajes largos se han revisado en pantalla en los idiomas más extensos (el alemán, por ejemplo).

Preguntas frecuentes

¿Flutter avisa de las traducciones faltantes?

Sí, si añades untranslated-messages-file a l10n.yaml: flutter gen-l10n anota ahí las claves de la plantilla que faltan en cada idioma. Sin esa opción, solo obtienes un aviso resumido.

¿Tengo que copiar los metadatos @key en todos los idiomas?

No. Los metadatos (descripción, placeholders) se leen del archivo de plantilla; los demás archivos solo necesitan las traducciones.

¿Cómo evito placeholders mal nombrados?

Para cada clave, compara los nombres entre llaves de la traducción con los declarados en la plantilla, y haz fallar la CI cuando no coincidan.