← Back to posts Cover image for Mastering Flutter Localization: From .arb Files to AI-Powered Translations

Mastering Flutter Localization: From .arb Files to AI-Powered Translations

· 5 min read
Weekly Digest

The Flutter news you actually need

No spam, ever. Unsubscribe in one click.

Chris
By Chris

Localizing a Flutter app is a critical step for reaching a global audience, but it often becomes a sprawling, tedious task. As your app grows, you find yourself wrestling with massive .arb files, struggling to invent clear key names, and manually coordinating translations across multiple languages. The process can feel more like bureaucratic management than development. The good news? With a solid foundation and modern tooling, you can transform localization from a chore into a streamlined, even automated, part of your workflow.

The Foundation: flutter_localizations and .arb Files

First, let’s ensure our base is correct. Flutter’s official localization system uses the intl package and Application Resource Bundle (.arb) files. A common mistake is not setting this up for scalability.

1. Add the dependencies:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.19.0

d_dependencies:
  flutter_gen_runner: ^5.4.15
  build_runner: any

2. Configure l10n.yaml in your project root: This file controls the code generation.

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
nullable-getter: false
use-deferred-loading: false

3. Create your .arb files in lib/l10n/. app_en.arb (your template locale):

{
  "@@locale": "en",
  "welcomeTitle": "Hello, {name}!",
  "@welcomeTitle": {
    "description": "A welcome message",
    "placeholders": {
      "name": {}
    }
  },
  "userProfile": "Profile"
}

app_es.arb:

{
  "@@locale": "es",
  "welcomeTitle": "¡Hola, {name}!",
  "userProfile": "Perfil"
}

4. Generate the code: Run flutter gen-l10n or dart run build_runner build. This creates the AppLocalizations class.

5. Use it in your app:

MaterialApp(
  localizationsDelegates: AppLocalizations.localizationsDelegates,
  supportedLocales: AppLocalizations.supportedLocales,
  home: Scaffold(
    appBar: AppBar(title: Text(AppLocalizations.of(context)!.userProfile)),
    body: Center(
      child: Text(
        AppLocalizations.of(context)!.welcomeTitle('Maria'),
      ),
    ),
  ),
);

The Real Pain Points & Solutions

With the basics running, the real challenges emerge.

1. Key Naming Chaos A key named title is meaningless. Does it belong to a button, a screen, a dialog? As your app grows, this becomes unmanageable.

  • Solution: Adopt a hierarchical, descriptive naming convention. Group keys by feature and purpose.
    • Bad: message, title, error
    • Good: homeScreen_welcomeHeader, loginButton_submitLabel, validation_error_emailEmpty

Your .arb files become self-documenting:

{
  "homeScreen_welcomeHeader": "Welcome Back",
  "loginButton_submitLabel": "Sign In"
}

2. Managing Growth and Adding New Strings Manually adding a new key to every .arb file (e.g., en, es, fr, de) is error-prone and slow.

3. Translation Itself Hiring professional translators for every iteration can be costly and slow for agile development.

Leveraging AI for a Supercharged Workflow

This is where modern AI tools can revolutionize the process. The goal isn’t to replace human translators for final copy, but to handle the bulk of the grunt work and initial translation.

Imagine a script that:

  1. Parses your template .arb file (e.g., app_en.arb).
  2. Identifies any new or modified keys since the last run.
  3. Sends only those new strings to an AI translation API (like OpenAI GPT, Gemini, or a dedicated model) for each target language.
  4. Automatically appends the translations to the respective .arb files.

Here’s a conceptual Dart script (bin/translate_arb.dart) to illustrate the logic:

import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;

Future<void> main() async {
  final templateArb = File('lib/l10n/app_en.arb');
  final targetLocales = ['es', 'fr', 'de'];

  final Map<String, dynamic> sourceData = json.decode(await templateArb.readAsString());

  // Filter for actual translatable strings (ignore metadata keys starting with '@')
  final newStrings = <String, String>{};
  sourceData.forEach((key, value) {
    if (!key.startsWith('@') && value is String) {
      newStrings[key] = value;
    }
  });

  for (final locale in targetLocales) {
    final targetFile = File('lib/l10n/app_$locale.arb');
    Map<String, dynamic> existingData = {};
    if (await targetFile.exists()) {
      existingData = json.decode(await targetFile.readAsString());
    }

    final stringsToTranslate = <String, String>{};
    for (final entry in newStrings.entries) {
      if (!existingData.containsKey(entry.key)) {
        stringsToTranslate[entry.key] = entry.value;
      }
    }

    if (stringsToTranslate.isNotEmpty) {
      print('Translating ${stringsToTranslate.length} strings to $locale...');
      final translated = await _batchTranslate(stringsToTranslate, 'en', locale);
      existingData.addAll(translated);
      await targetFile.writeAsString(json.encode(existingData));
    }
  }
  print('Translation update complete.');
}

// Placeholder for your AI/Translation API call.
Future<Map<String, dynamic>> _batchTranslate(
    Map<String, String> strings, String fromLang, String toLang) async {
  // This is a mock implementation.
  // In reality, you would call an API like OpenAI, Google Cloud Translation, etc.
  final translatedMap = <String, dynamic>{};
  for (final entry in strings.entries) {
    // Simulate an API call that returns a translation.
    translatedMap[entry.key] = _mockAITranslate(entry.value, toLang);
  }
  translatedMap['@@locale'] = toLang;
  return translatedMap;
}

String _mockAITranslate(String text, String toLang) {
  // In a real scenario, this is where you'd integrate with an LLM.
  // Example: "Hello!" -> "¡Hola!" for 'es'
  return '[AI_Translated($toLang): $text]';
}

Important: This script is a blueprint. You must replace _mockAITranslate with actual API calls, implement error handling, and respect rate limits. The core idea is powerful: automate the propagation of new strings.

The Final Workflow

  1. Develop: Add all new UI text directly to your template app_en.arb file with clear, hierarchical keys.
  2. Run AI Script: Execute your translation script to populate all other language files with AI-drafted translations.
  3. Review & Refine: Have a native speaker or professional translator review the AI-generated .arb files. They can edit the files directly—the structure is simple JSON.
  4. Generate: Run flutter gen-l10n to update your Dart bindings.

This approach tackles the major pain points head-on. It provides scalable key management, eliminates manual copy-pasting between files, and uses AI as a powerful first draft engine, saving immense time and cost.

This blog is produced with the assistance of AI by a human editor. Learn more

Related Posts

Cover image for Flutter's Hidden Gem: Building Powerful Linux Desktop Apps with Ease

Flutter's Hidden Gem: Building Powerful Linux Desktop Apps with Ease

Flutter is gaining traction as a robust framework for Linux desktop development, offering a smoother experience compared to traditional toolkits like GTK and Qt. This post will explore why Flutter excels for Linux apps, highlight its advantages, and provide practical insights for developers looking to leverage Flutter for their desktop projects.

Cover image for Simplifying Flutter Desktop Deployment: Signing and Distribution for Windows

Simplifying Flutter Desktop Deployment: Signing and Distribution for Windows

Deploying Flutter desktop apps on Windows can be challenging, especially regarding code signing and preventing Windows from blocking installations. This post will guide developers through the process of signing their Flutter desktop applications, exploring alternatives to the Microsoft Store like creating executable installers, and covering the necessary steps to ensure a smooth user experience.

Cover image for Mastering Flutter Tooling: Streamlining SDK Management and Installation on Windows

Mastering Flutter Tooling: Streamlining SDK Management and Installation on Windows

Developers frequently voice frustrations about Flutter SDK version management and installation processes, particularly on Windows. This post will explore best practices for managing multiple Flutter versions using tools like FVM, discuss integrating Flutter with package managers like Winget for a smoother Windows setup, and offer strategies to ensure a consistent development environment across projects and teams.