Translations code generator
This is a simple tool to generate the translations code for the Dart/Flutter projects.
Install
1. Add the dependency
dependencies:
translations_code_gen: ^1.3.4
2. Run this commend
flutter pub get
Usage
1. Create a translations files in assets folder
Create a folder called assets in the root of your project and create a file called en.json and ar.json and add the following content:
example: assets/translations/en.json
{
"GENERAL": {
"HELLO": "Hello",
"WELCOME": "Welcome",
"WELCOME_USER": "Welcome {name}!",
"GREETING_WITH_TIME": "Good {timeOfDay}, {name}!"
},
"HOME": {
"TITLE": "Home"
},
"MESSAGES": {
"SIMPLE_MESSAGE": "This is a simple message",
"USER_PROFILE": "User {username} has {count} notifications",
"POSITIONAL_EXAMPLE": "First: {}, Second: {}, Third: {}",
"MIXED_PLACEHOLDERS": "Hello {name}, you have {} new messages and {} pending tasks"
}
}
example: assets/translations/ar.json
{
"GENERAL": {
"HELLO": "مرحبا",
"WELCOME": "أهلا بك",
"WELCOME_USER": "أهلا بك {name}!",
"GREETING_WITH_TIME": "{timeOfDay} طيب، {name}!"
},
"HOME": {
"TITLE": "الرئيسية"
},
"MESSAGES": {
"SIMPLE_MESSAGE": "هذه رسالة بسيطة",
"USER_PROFILE": "المستخدم {username} لديه {count} إشعارات",
"POSITIONAL_EXAMPLE": "الأول: {}، الثاني: {}، الثالث: {}",
"MIXED_PLACEHOLDERS": "مرحبا {name}، لديك {} رسائل جديدة و {} مهام معلقة"
}
}
2. Add the translations file paths to the pubspec.yaml or translations_code_gen.yaml file
translations_code_gen:
keys:
input: 'assets/translations/en.json'
output: 'lib/translations/keys.dart'
values:
input: 'assets/translations/'
output: 'lib/translations/values/'
Run this command to generate the translations keys and values to the output:
flutter pub run translations_code_gen
The -g or --generate flag is optional, if you don't use it, the tool will normally generate dart keys and values code for you.
This will generate the following keys to the lib/translations/keys.dart file:
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: constant_identifier_names, camel_case_types
import 'package:easy_localization/easy_localization.dart';
class GENERAL {
static const String HELLO = "GENERAL.HELLO";
static String hello() => HELLO.tr();
static const String WELCOME = "GENERAL.WELCOME";
static String welcome() => WELCOME.tr();
static const String WELCOME_USER = "GENERAL.WELCOME_USER";
static String welcomeUser({String? name, }) => WELCOME_USER.tr(namedArgs: {if (name != null) 'name': name, });
static const String GREETING_WITH_TIME = "GENERAL.GREETING_WITH_TIME";
static String greetingWithTime({String? timeOfDay, String? name, }) => GREETING_WITH_TIME.tr(namedArgs: {if (timeOfDay != null) 'timeOfDay': timeOfDay, if (name != null) 'name': name, });
}
class HOME {
static const String TITLE = "HOME.TITLE";
static String title() => TITLE.tr();
}
class MESSAGES {
static const String SIMPLE_MESSAGE = "MESSAGES.SIMPLE_MESSAGE";
static String simpleMessage() => SIMPLE_MESSAGE.tr();
static const String USER_PROFILE = "MESSAGES.USER_PROFILE";
static String userProfile({String? username, String? count, }) => USER_PROFILE.tr(namedArgs: {if (username != null) 'username': username, if (count != null) 'count': count, });
static const String POSITIONAL_EXAMPLE = "MESSAGES.POSITIONAL_EXAMPLE";
static String positionalExample({List<String?>? args, }) => POSITIONAL_EXAMPLE.tr(args: args?.whereType<String>().toList(), );
static const String MIXED_PLACEHOLDERS = "MESSAGES.MIXED_PLACEHOLDERS";
static String mixedPlaceholders({String? name, List<String?>? args, }) => MIXED_PLACEHOLDERS.tr(args: args?.whereType<String>().toList(), namedArgs: {if (name != null) 'name': name, });
}
and the following values to the en.dart and ar.dart file to lib/translations/values/
example: lib/translations/values/en.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: constant_identifier_names
import '../keys.dart'; // sometimes you need to change this path to match your project structure
const Map<String, String> _general = {
GENERAL.HELLO: "Hello",
GENERAL.WELCOME: "Welcome",
GENERAL.WELCOME_USER: "Welcome {name}!",
GENERAL.GREETING_WITH_TIME: "Good {timeOfDay}, {name}!",
};
const Map<String, String> _home = {
HOME.TITLE: "Home",
};
const Map<String, String> _messages = {
MESSAGES.SIMPLE_MESSAGE: "This is a simple message",
MESSAGES.USER_PROFILE: "User {username} has {count} notifications",
MESSAGES.POSITIONAL_EXAMPLE: "First: {}, Second: {}, Third: {}",
MESSAGES.MIXED_PLACEHOLDERS: "Hello {name}, you have {} new messages and {} pending tasks",
};
final Map<String, String> enValues = {
..._general,
..._home,
..._messages,
};
example: lib/translations/values/ar.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: constant_identifier_names
import '../keys.dart'; // sometimes you need to change this path to match your project structure
const Map<String, String> _general = {
GENERAL.HELLO: "مرحبا",
GENERAL.WELCOME: "أهلا بك",
GENERAL.WELCOME_USER: "أهلا بك {name}!",
GENERAL.GREETING_WITH_TIME: "{timeOfDay} طيب، {name}!",
};
const Map<String, String> _home = {
HOME.TITLE: "الرئيسية",
};
const Map<String, String> _messages = {
MESSAGES.SIMPLE_MESSAGE: "هذه رسالة بسيطة",
MESSAGES.USER_PROFILE: "المستخدم {username} لديه {count} إشعارات",
MESSAGES.POSITIONAL_EXAMPLE: "الأول: {}، الثاني: {}، الثالث: {}",
MESSAGES.MIXED_PLACEHOLDERS: "مرحبا {name}، لديك {} رسائل جديدة و {} مهام معلقة",
};
final Map<String, String> arValues = {
..._general,
..._home,
..._messages,
};
You might have to change the keys import path to match your project structure.
Placeholder Types and Usage
This package supports three types of placeholders in your translation strings:
1. Named Placeholders {name}
Named placeholders use curly braces with a parameter name inside. They generate methods with named parameters.
Translation JSON:
{
"WELCOME_USER": "Welcome {name}!",
"USER_STATS": "User {username} has {count} points"
}
Generated Dart Code:
static String welcomeUser({String? name, }) => _WELCOME_USER.tr(namedArgs: {if (name != null) 'name': name, });
static String userStats({String? username, String? count, }) => _USER_STATS.tr(namedArgs: {if (username != null) 'username': username, if (count != null) 'count': count, });
Usage:
Text(GENERAL.welcomeUser(name: 'John'))
Text(GENERAL.userStats(username: 'Alice', count: '150'))
2. Positional Placeholders {}
Positional placeholders use empty curly braces {}. They generate methods that accept a list of arguments.
Translation JSON:
{
"ORDERED_LIST": "First: {}, Second: {}, Third: {}"
}
Generated Dart Code:
static String orderedList({List<String?>? args, }) => _ORDERED_LIST.tr(args: args?.whereType<String>().toList(), );
Usage:
Text(MESSAGES.orderedList(args: ['Apple', 'Banana', 'Cherry']))
3. Mixed Placeholders {name} + {}
You can combine both named and positional placeholders in the same translation string.
Translation JSON:
{
"MIXED_MESSAGE": "Hello {name}, you have {} new messages and {} pending tasks"
}
Generated Dart Code:
static String mixedMessage({String? name, List<String?>? args, }) => _MIXED_MESSAGE.tr(args: args?.whereType<String>().toList(), namedArgs: {if (name != null) 'name': name, });
Usage:
Text(MESSAGES.mixedMessage(
name: 'Alice',
args: ['5', '3']
))
4. Simple Strings (No Placeholders)
Strings without placeholders generate simple methods that call .tr() directly.
Translation JSON:
{
"SIMPLE_MESSAGE": "This is a simple message"
}
Generated Dart Code:
static String simpleMessage() => _SIMPLE_MESSAGE.tr();
Usage:
Text(MESSAGES.simpleMessage())
Key Features
- Type Safety: All generated methods provide compile-time type checking
- Null Safety: All parameters are nullable with proper null checks
- easy_localization Integration: Generated code works seamlessly with the
easy_localizationpackage - Automatic Formatting: Translation keys are converted to camelCase method names
- Public Constants: Translation key constants are publicly accessible for direct usage and testing
- Flexible Access: Use either the generated methods or access the constants directly
4. Use the generated code
import 'package:flutter/material.dart';
import './translations/keys.dart';
// any translation package
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(HOME.title()),
),
body: Column(
children: [
// Simple translation without placeholders
Text(GENERAL.hello()),
// Named placeholder example
Text(GENERAL.welcomeUser(name: 'John')),
// Multiple named placeholders
Text(GENERAL.greetingWithTime(
timeOfDay: 'morning',
name: 'Sarah'
)),
// Positional placeholders
Text(MESSAGES.positionalExample(
args: ['Apple', 'Banana', 'Cherry']
)),
// Mixed placeholders (named + positional)
Text(MESSAGES.mixedPlaceholders(
name: 'Alice',
args: ['5', '3']
)),
// User profile with named placeholders
Text(MESSAGES.userProfile(
username: 'developer',
count: '12'
)),
],
),
);
}
}
5. Public Constant Accessibility
Starting from version 1.3.6, all translation key constants are publicly accessible, allowing for more flexible usage patterns:
Direct Constant Access
You can now access the translation key constants directly:
// Access constants directly for custom usage
String keyValue = GENERAL.HELLO; // "GENERAL.HELLO"
String translatedText = keyValue.tr(); // "Hello"
// Use in custom translation logic
Map<String, String> customTranslations = {
GENERAL.HELLO: "Custom Hello",
HOME.TITLE: "Custom Title",
};
// Useful for testing and debugging
print('Translation key: ${MESSAGES.USER_PROFILE}');
Benefits of Public Constants
- Testing: Easily test translation keys in unit tests
- Custom Logic: Build custom translation logic around the constants
- Debugging: Access raw translation keys for debugging purposes
- Integration: Better integration with other localization tools
- Flexibility: Choose between method calls or direct constant access
Usage Patterns
// Method approach (recommended for most cases)
Text(GENERAL.hello())
// Direct constant approach (for custom logic)
Text(GENERAL.HELLO.tr())
// Both approaches are equivalent and produce the same result
6. Same as above but with the --generate flag
flutter pub run translations_code_gen --generate=json-values
There are 4 types of code generation mode:
dart(default): generate dart keys and values codedart-keys: generate dart keys only.dart-values: generate dart values only.json-values: generate json values only.
Output with the --generate=json-values flag
example: lib/translations/values/en.json
{
"GENERAL.HELLO": "Hello",
"GENERAL.WELCOME": "Welcome",
"GENERAL.WELCOME_USER": "Welcome {name}!",
"GENERAL.GREETING_WITH_TIME": "Good {timeOfDay}, {name}!",
"HOME.TITLE": "Home",
"MESSAGES.SIMPLE_MESSAGE": "This is a simple message",
"MESSAGES.USER_PROFILE": "User {username} has {count} notifications",
"MESSAGES.POSITIONAL_EXAMPLE": "First: {}, Second: {}, Third: {}",
"MESSAGES.MIXED_PLACEHOLDERS": "Hello {name}, you have {} new messages and {} pending tasks"
}
example: lib/translations/values/ar.json
{
"GENERAL.HELLO": "مرحبا",
"GENERAL.WELCOME": "أهلا بك",
"GENERAL.WELCOME_USER": "أهلا بك {name}!",
"GENERAL.GREETING_WITH_TIME": "{timeOfDay} طيب، {name}!",
"HOME.TITLE": "الرئيسية",
"MESSAGES.SIMPLE_MESSAGE": "هذه رسالة بسيطة",
"MESSAGES.USER_PROFILE": "المستخدم {username} لديه {count} إشعارات",
"MESSAGES.POSITIONAL_EXAMPLE": "الأول: {}، الثاني: {}، الثالث: {}",
"MESSAGES.MIXED_PLACEHOLDERS": "مرحبا {name}، لديك {} رسائل جديدة و {} مهام معلقة"
}
Development
Requirements
- Dart SDK:
>=3.1.0 <4.0.0(recommended:3.9.2or later) - Flutter SDK:
3.35.4or later (when using FVM)
Setting up with FVM (Flutter Version Manager)
This project uses FVM for Flutter and Dart SDK version management to ensure consistent development environments.
1. Install FVM
# Using Homebrew (macOS)
brew tap leoafarias/fvm
brew install fvm
# Using pub global
dart pub global activate fvm
# Using Chocolatey (Windows)
choco install fvm
2. Install and use the project's Flutter version
# Install the required Flutter version
fvm install 3.35.4
# Use it for this project
fvm use 3.35.4
3. Run commands with FVM
# Get dependencies
fvm dart pub get
# Run the application
fvm dart run bin/translations_code_gen.dart
# Analyze code
fvm dart analyze
# Run tests
fvm dart test
Without FVM
If you prefer not to use FVM, ensure you have Dart SDK 3.9.2 or later installed:
# Check your Dart version
dart --version
# Run the application
dart run bin/translations_code_gen.dart
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Install dependencies with
fvm dart pub get(ordart pub get) - Make your changes
- Run tests and ensure code analysis passes
- Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Libraries
- constants/constants
- constants/errors
- constants/help
- generators/generate_dart_keys
- generators/generate_dart_values
- generators/generate_json_values
- generators/generators
- translations_code_gen
- A comprehensive library for generating Dart code from translation files.