How AppFlowy Implements Localization and Internationalization with JSON Translation Files
AppFlowy uses the EasyLocalization package to provide runtime translation of UI strings through JSON assets, generating type-safe Dart constants that enable compile-time key validation while supporting dynamic locale switching.
The AppFlowy-IO/AppFlowy repository implements a robust internationalization system for its Flutter frontend that decouples translation content from presentation logic. By storing localized strings as JSON assets and leveraging code generation, the codebase ensures type-safe access to translations while maintaining a clean separation between UI components and language-specific text.
JSON Translation Assets Structure
Translation files follow a flat JSON structure stored in frontend/appflowy_flutter/resources/translations/. Each supported locale maintains its own file, such as en-US.json for United States English or zh-CN.json for Simplified Chinese.
Key/value pairs map semantic identifiers to localized strings:
// frontend/appflowy_flutter/resources/translations/en-US.json
{
"welcome_message": "Welcome to AppFlowy"
}
// frontend/appflowy_flutter/resources/translations/zh-CN.json
{
"welcome_message": "欢迎使用 AppFlowy"
}
EasyLocalization Initialization
The application bootstrap configures EasyLocalization in frontend/appflowy_flutter/lib/startup/tasks/app_widget.dart. The InitAppWidgetTask class wraps the root widget with EasyLocalization, specifying the asset directory, supported locales list, and fallback locale.
Configuration parameters include:
- Asset path: Points to
assets/translationsdirectory - Supported locales: Explicit list of all application locales
- Fallback locale: Defaults to
en-USwhen translations are missing
Generated Type-Safe Translation Keys
A build-time code generation step produces frontend/appflowy_flutter/lib/generated/locale_keys.g.dart using the easy_localization:generate command. This generated file contains a LocaleKeys class where each JSON key becomes a static constant.
Generate the keys by running:
flutter pub run easy_localization:generate -f json -o lib/generated/locale_keys.g.dart
The resulting class eliminates magic strings from the codebase, providing IDE autocomplete and compile-time validation for translation keys.
Runtime Translation Usage
UI components import the generated keys and invoke the .tr() extension method provided by EasyLocalization. This pattern appears throughout the codebase, including in frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart at line 117.
Usage example:
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
class WelcomeBanner extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(LocaleKeys.welcome_message.tr());
}
}
The .tr() method queries EasyLocalization’s EasyLocalizationController to resolve the current locale's translation at runtime.
Fallback Translation Handling
For scenarios requiring manual fallback resolution, frontend/appflowy_flutter/lib/shared/easy_localiation_service.dart provides the EasyLocalizationService class. This service exposes helper methods to retrieve either the current locale's translation or the fallback translation when keys are missing.
Example service usage:
final service = getIt<EasyLocalizationService>();
String fallback = service.getFallbackTranslation(LocaleKeys.welcome_message);
Summary
- Asset Organization: JSON translation files reside in
resources/translations/with locale-specific naming (e.g.,en-US.json,zh-CN.json). - Type Safety: The
locale_keys.g.dartfile provides compile-time constants generated from JSON keys via theeasy_localization:generatecommand. - Runtime Resolution: UI code calls
.tr()onLocaleKeysconstants to trigger EasyLocalization's lookup mechanism. - Bootstrap Configuration:
InitAppWidgetTaskinitializes the localization system with supported locales and fallback settings inapp_widget.dart. - Fallback Support:
EasyLocalizationServiceoffers programmatic access to fallback translations for edge cases.
Frequently Asked Questions
How do I add a new translation key to AppFlowy?
Add the key-value pair to all JSON files in frontend/appflowy_flutter/resources/translations/, then run the generator command to update locale_keys.g.dart. Import the generated file and use the new constant with .tr() in your widget code.
Where does AppFlowy store its translation files?
Translation assets are stored in frontend/appflowy_flutter/resources/translations/ as JSON files named with locale codes (e.g., en-US.json, zh-CN.json). The build system treats this directory as a Flutter asset folder referenced during EasyLocalization initialization.
What happens if a translation key is missing in the current locale?
EasyLocalization automatically falls back to the configured fallback locale (en-US) when a key is absent. Additionally, the EasyLocalizationService class in easy_localiation_service.dart provides explicit methods to retrieve fallback translations programmatically when needed.
How does AppFlowy ensure type safety for translation keys?
The project uses the easy_localization:generate package to parse JSON translation files and generate the LocaleKeys class in lib/generated/locale_keys.g.dart. This converts string keys into static constants, enabling IDE autocomplete and compile-time validation rather than relying on error-prone string literals.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →