A Flutter package designed to simplify localization by providing a solution for managing custom translations with variable support. This package allows you to use translation keys to fetch localized values with dynamic variable replacements.
- Key-Based Translations: Use translation keys to fetch localized values.
- Simplified Access: Access translations directly using an extension on
BuildContext. - Custom Localization: Supports dynamic translations with simple variable replacements.
- Regional Locale Resolution: Locales with a country code (e.g.
de_CH) load their country-specific file first and fall back to the language-only file. - Fallback Support: Prevents crashes by providing fallback translations.
- Missing-Key Hook: Optional
CustomLocalization.onMissingKeycallback to report missing translations to logging, analytics or crash reporting.
Note: Currently, the custom localization only supports simple variables. Plurals, dates, and other advanced formatting are not yet supported.
A runnable app with the full setup (delegate, supported locales, .arb
assets and context.translate) is in the example/ folder.
Add the package to your pubspec.yaml:
dependencies:
flutter_translation_mapper: ^0.2.0Run flutter pub get to fetch the package.
In your main.dart, configure the supported locales:
import 'package:flutter/material.dart';
import 'package:flutter_translation_mapper/app_localization_provider.dart';
void main() {
// Set the supported locales
TranslationMapper.setSupportedLocales([
Locale('en'),
Locale('es'),
Locale('fr'),
]);
runApp(MyApp());
}To use the custom localization feature, add the CustomLocalization.delegate to your app's localizationsDelegates and supportedLocales.
In your MaterialApp:
import 'package:flutter_translation_mapper/custom_localization.dart';
MaterialApp(
localizationsDelegates: [
CustomLocalization.delegate,
// Add other delegates like AppLocalizations.delegate
],
supportedLocales: TranslationMapper.supportedLocales,
home: MyHomePage(),
);Place your .arb files in the lib/l10n directory. For example:
lib/l10n/app_en.arblib/l10n/app_es.arb
Each file should contain key-value pairs for translations:
{
"welcomeMessage": "Welcome to our app!",
"greeting": "Hello, {name}!"
}Add assets in the pubspec.yaml
assets:
- lib/l10n/If a supported locale includes a country code, the package looks for a country-specific file first and falls back to the language-only file. For Locale('de', 'CH') it tries, in order:
lib/l10n/app_de_CH.arblib/l10n/app_de.arb
A locale without a country code (e.g. Locale('de')) only looks for lib/l10n/app_de.arb.
This lets you ship a single app_de_CH.arb without needing an app_de.arb next to it, or serve several regional locales from one shared app_de.arb:
TranslationMapper.setSupportedLocales([
Locale('en'),
Locale('de', 'CH'),
Locale('fr', 'CH'),
]);The first candidate that exists is used. Files are not merged: if app_de_CH.arb is found, app_de.arb is never read for that locale, so a country-specific file must contain every key. If that file is malformed, the delegate does not fall back to the next candidate either: it logs the parse error and returns an empty localization, so every lookup renders as ??:key and the broken file is visible in QA instead of being masked by the base file. If none of the candidates exist, the delegate logs the files it tried (under the CustomLocalization logger name) and likewise returns an empty localization rather than crashing the app.
By default, the package looks for translation files with the app_ prefix (e.g., app_en.arb, app_es.arb). You can customize this prefix to match your project's naming convention:
void main() {
TranslationMapper.setSupportedLocales([
Locale('en'),
Locale('es'),
]);
// Customize the file prefix (default is 'app_')
CustomLocalization.delegate.filePrefix = 'translations_';
runApp(MyApp());
}With the custom prefix translations_, the package will load:
lib/l10n/translations_en.arblib/l10n/translations_es.arb
Regional files follow the same pattern, e.g. lib/l10n/translations_de_CH.arb.
Use the translate method for translations with or without variables:
// Simple translation
Text(context.translate('welcomeMessage'));
// Translation with variables
Text(context.translate('greeting', params: {'name': 'John'}));If the key is not found, the package will return ??:key as a fallback.
A lookup that misses renders as ??:key so it is visible in QA, but it is otherwise silent. To be told about every miss, set CustomLocalization.onMissingKey once at startup (for example in main.dart). It receives the missing key and is called exactly once per miss, before the fallback is returned:
CustomLocalization.onMissingKey = (key) {
myLogger.warning('No translation for "$key"');
assert(false, 'No translation for l10n key "$key"');
};Typical uses are reporting to a crash reporter (Sentry, Crashlytics), analytics, or asserting in debug builds so a missing key fails fast during development. The callback is not guarded: if it throws, the exception propagates out of context.translate, which is what makes the assert above work. Leave the hook unset (the default) to keep the previous behaviour.
If you're using Flutter's built-in AppLocalizations and want quick access to variable-based translations, you can add this extension to your project:
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
extension AppLocalizationExtension on BuildContext {
AppLocalizations get loc => AppLocalizations.of(this)!;
}This allows you to access your standard localizations easily:
Text(context.loc.welcomeMessage);When working with localization in Flutter, it can be cumbersome to manage dynamic translations with variable replacements. This package solves that problem by:
- Providing a simple way to manage custom translations with variable support.
- Adding an extension to make accessing translations easier with
context.translate(). - Supporting custom localization for dynamic translations.
- Resolving regional locales (
de_CH, thende) so country-specific translation files are picked up when present. - Providing fallback support to prevent crashes when keys or translation files are missing.
- No Advanced Formatting: Currently, the custom localization only supports simple variable replacements. Plurals, dates, and other advanced formatting are not yet supported.
- Manual Setup: You need to manually set the supported locales and add the delegate in your app.