tradutor 0.9.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 72

A Flutter package that simplify the internationalizing process using JSON and YAML files. Extracts messages to generate Dart files with friendly way to access messages you need.

String get translator => "tradutor";

Pub

Features #

  • Simple messages.
  • Messages with multiple parameters.
  • List of simple messages.
  • List of messages with multiple parameters.
  • Supported message types:
    • Plural.
    • Date.
    • Numeric.
    • Map.
  • Optional parameters support.
  • JSON and YAML files.
  • Nested messages support.
  • Supports hot reload.

Installation #

In pubspec.yaml add the following dependencies:

dependencies: 
    flutter_localizations: 
        sdk: flutter 

dev_dependencies:
    tradutor: ^0.9.0

Usage #

Open a terminal inside your project directory and run the following command:

flutter pub run tradutor:generate

All supported arguments:

  • --source "DIRECTORY PATH" or -s "DIRECTORY PATH": A source folder contains all JSON/YAML files (defaults to "./i18n");
  • --output "FILE PATH" or -o "FILE PATH": An output file contains all strings (defaults to "./lib/i18n.dart");
  • --fallback "LANGUAGE" or -f "LANGUAGE": Provides a default language, used when the translation for the current running system is not provided (defaults to "en_US");
  • --watch: Watches the JSON/YAML files for edits and does rebuilds as necessary.
  • --class-name "NAME" or -c "NAME": Allows change the generated Dart class name (defaults to "I18n").

Full example: flutter packages pub run tradutor:build -s "/i18n" -o "/lib/i18n.dart" -f "en_US" -c "I18n" --watch

JSON Files #

Crete JSON files naming them with language code (lowercase) and country code (uppercase). Ex.: pt_BR.json, en_US.json, etc.

en_US.json

{
    "simpleMessage": "This is a simple Message",
    "messageWithParameters": "Hi {name}, Welcome you!",
    "brazilFlagColors": ["Green", "Yellow", "Blue", "White"],
    "simpleWhiteCakeIngredients": [
        "{whiteSugar} cup white sugar",
        "{butter} cup butter",
        "{eggs} eggs",
        "{vanilla} teaspoons vanilla extract",
        "{flour} cups all-purpose flour",
        "{bakingPowder} teaspoons baking powder",
        "{milk} cup milk"
    ],
    "homePage": {
        "title": "Home Page"
    },
    "counter.one": "Button clicked 1 time",
    "counter.other": "Button cliked {quantity} times"
}

YAML Files #

Crete YAML files naming them with language code (lowercase) and country code (uppercase). Ex.: pt_BR.yaml, en_US.yaml, etc.

simpleMessage: これは簡単なメッセージです
messageWithParameters: "{name}様、ようこそ!"
brazilFlagColors:
- 緑
- 黄色
- 青い
- 白い
simpleWhiteCakeIngredients:
- "白砂糖{whiteSugar}カップ"
- "バター{butter}カップ"
- "卵{eggs}個"
- "バニラエッセンス小さじ{vanilla}"
- "薄力粉{flour}カップ"
- "小さじ{bakingPowder}杯のベーキングパウダー"
- "牛乳{milk}カップ"
homePage:
  title: ホームページ
counter.other: ボタンが{quantity}回クリックされた

Nested messages #

The messages can be nested. The final message name will be transformed to camelCase format (starting with a lower case character and capital for each later key).

{
  "this" : {
    "is" : {
      "a": {
        "nested": "Message"
      }
    }
  }
}

Generated Dart getter:

String get thisIsANested => 'Message';

Optional parameters #

Use {?name=value} for set the parameter as optional. The =value is optional and will use an empty string when omitted.

Supported Message Type #

The all parameters in a message must be surrounded by curly braces. The parameters are sorted alphabetically.

Simple message #

{ 
    "simpleMessage": "This is a simple Message"
}

Generated Dart getter:

String get simpleMessage => 'This is a simple Message';

Message with parameters #

{
    "messageWithParameters": "Hi {name}, Welcome you!"
}

Generated Dart method:

String messageWithParameters(dynamic name) => 'Hi ${name}, Welcome you!';

List of simple messages #

Define an array of strings.

{
    "brazilFlagColors": ["Green", "Yellow", "Blue", "White"]
}

Generated Dart getter:

List<String> get brazilFlagColors => ['Green', 'Yellow', 'Blue', 'White'];

List of messages with multiple parameters #

{
    "simpleWhiteCakeIngredients": [
        "{whiteSugar} cup white sugar",
        "{butter} cup butter",
        "{eggs} eggs",
        "{vanilla} teaspoons vanilla extract",
        "{flour} cups all-purpose flour",
        "{bakingPowder} teaspoons baking powder",
        "{milk} cup milk"
    ],
}

Generated Dart method:

List<String> simpleWhiteCakeIngredients(
          dynamic bakingPowder, 
          dynamic butter, 
          dynamic eggs, 
          dynamic flour, 
          dynamic milk, 
          dynamic vanilla, 
          dynamic whiteSugar) =>
      [
        '${whiteSugar} cup white sugar',
        '${butter} cup butter',
        '${eggs} eggs',
        '${vanilla} teaspoons vanilla extract',
        '${flour} cups all-purpose flour',
        '${bakingPowder} teaspoons baking powder',
        '${milk} cup milk'
      ];

Plural messages #

The last message name must be zero, one, two, few, many or other. See Language Plural Rules.

{
  "counter.one": "Button clicked 1 time",
  "counter.other": "Button cliked {quantity} times"
}

Or

{
  "counter": {
    "one": "Button clicked 1 time",
    "other": "Button cliked {quantity} times"
  }
}

The 'other' plural form must be provided.

quantity is the parameter used to format a message depending on its value.

Generated Dart method:

String counter(int quantity) => Intl.plural(
        quantity,
        locale: 'en_US',
        one: 'Button clicked 1 time',
        other: 'Button cliked ${quantity} times',
      );

Map messages #

The message name starts with %.

{
  "%eyeColor": {
    "FAMALE": "She has {color} eyes",
    "MALE": "He has {color} eyes"
  }
}

Generated Dart method:

String eyeColor(String key, dynamic color) {
    switch (key) {
      case 'FAMALE':
        return 'She has ${color} eyes';
      case 'MALE':
        return 'He has ${color} eyes';
      default:
        return null;
    }
  }

key is the parameter used to format a message depending on its value.

Date messages #

The message name starts with #.

{
    "#fullDate": "MM dd, yyyy h:mm:ss a"
}

Generated Dart method:

static final _fullDateFormatter = DateFormat('MM dd, yyyy h:mm:ss a', 'en_US');
String fullDate(DateTime date) => _fullDateFormatter.format(date);

See the DateFormat documentation here.

Number messages #

The message name starts with $.

{
    "$currencyMessage": "\\$#,##0.00"
}

Generated Dart method:

static final _currencyMessageFormatter = NumberFormat('\$#,##0.00', 'en_US');
String currencyMessage(num number) => _currencyMessageFormatter.format(number);

See the NumberFormat documentation here.

Usage of Generated Dart file (i18n.dart) #

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

import 'i18n.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final i18n = I18n.delegate;

    return MaterialApp(
      title: 'Tradutor',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
      localizationsDelegates: [
        i18n,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: i18n.supportedLocales,
      localeResolutionCallback: i18n.resolution(
        fallback: const Locale('en', 'US'),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({
    Key key,
  }) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    final i18n = I18n.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(i18n.homePage),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(i18n.counter(_counter)),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Escaping Curly Braces and Dollar Sign. #

JsonDartMessage (a = 'Hi')Note
"{a}""${a}""Hi"Just a parameter
"\\{a}""\{a}""{a}"Text surrounded by curly braces
"\\\\{a}""\\{a}""\{a}"Backslash + text surrounded by curly braces
"\\{!a}""\${a}""\Hi"Backslash + parameter
"\\{!?a}""\${a}""\Hi"Backslash + optional parameter
"\\$""\$""$"Just a dollar sign

Language Options #

@parent #

Use @parent to indicate that language is a parent language.

pt_PT.json

{
    "@parent": true,
}

pt_PT is parent language of pt_BR, pt_AO, pt_TL, etc. If some child language does not found, pt_PT will be used instead.

@textDirection #

Use @textDirection to indicate the direction in which text flows. The valid values are ltr (default) and rtl.

he_IL.json

{
    "@textDirection": "rtl",
}

[0.9.0] #

  • Add Optional parameter support.

[0.8.0] #

  • Add Number Format support.
  • Add Map support.
  • Various bug fixes.

[0.7.0] #

  • Code refactoring.
  • Otimizations and improved performance.
  • Fix escape curly braces.
  • [Breaking]: Remove @extendsOf property in favor of the @parent property.
  • [Breaking]: Remove web support (temporarily).

[0.6.3] #

  • Ignore messages with null value.

[0.6.2] #

  • Bug fixes.

[0.6.1] #

  • Bug fixes.

[0.6.0] #

  • Add Date message type.
  • Bug fixes.

[0.5.1] #

  • Bug fixed.

[0.5.0] #

  • Add support to Flutter Web.
  • Add widget tests.

[0.4.0] #

  • Add support to YAML files.

[0.3.0] #

  • Add support to change the generated Dart class name.

[0.2.0] #

  • Add support to nested messages.
  • Bug fixes.

[0.1.0] #

  • Initial release.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

import 'i18n.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    const i18n = I18n.delegate;

    return MaterialApp(
      title: 'Tradutor',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
      localizationsDelegates: [
        i18n,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: i18n.supportedLocales,
      localeResolutionCallback: i18n.resolution(
        fallback: const Locale('en', 'US'),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({
    Key key,
  }) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  var _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    final i18n = I18n.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(i18n.simpleMessage),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(i18n.pluralMesssage(_counter)),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  tradutor: ^0.9.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:tradutor/tradutor.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
44
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
72
Learn more about scoring.

We analyzed this package on Jul 9, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • tradutor that is a package requiring null.

Package not compatible with runtime flutter-web on web

Because:

  • package:tradutor/tradutor.dart that imports:
  • package:tradutor/src/tradutor.dart that imports:
  • package:dart_style/dart_style.dart that imports:
  • package:dart_style/src/exceptions.dart that imports:
  • package:analyzer/error/error.dart that imports:
  • package:analyzer/src/generated/source.dart that imports:
  • package:analyzer/src/generated/source_io.dart that imports:
  • package:analyzer/src/generated/java_io.dart that imports:
  • dart:io

Health suggestions

Format lib/src/message.dart.

Run flutter format to format lib/src/message.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.8.0 <3.0.0
args ^1.6.0 1.6.0
code_builder ^3.3.0 3.4.0
dart_style ^1.3.6 1.3.6
equatable ^1.2.0 1.2.0
flutter 0.0.0
flutter_localizations 0.0.0
path ^1.6.4 1.6.4 1.7.0
stream_transform ^1.2.0 1.2.0
yaml ^2.2.1 2.2.1
Transitive dependencies
_fe_analyzer_shared 5.0.0
analyzer 0.39.12
async 2.4.2
built_collection 4.3.2
built_value 7.1.0
charcode 1.1.3
collection 1.14.12 1.14.13
convert 2.1.1
crypto 2.1.5
csslib 0.16.1
fixnum 0.10.11
glob 1.2.0
html 0.14.0+3
intl 0.16.1
js 0.6.2
matcher 0.12.8
meta 1.1.8 1.2.2
node_interop 1.1.1
node_io 1.1.1
package_config 1.9.3
pedantic 1.9.0 1.9.2
pub_semver 1.4.4
quiver 2.1.3
sky_engine 0.0.99
source_span 1.7.0
stack_trace 1.9.5
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
watcher 0.9.7+15
Dev dependencies
effective_dart ^1.2.3
flutter_test