i18n 4.2.1 copy "i18n: ^4.2.1" to clipboard
i18n: ^4.2.1 copied to clipboard

Simple i18n solution for dart and flutter. Uses code generation to generate translations as dart classes. Efficient and works with autocomplete!

i18n #

tests pub package

Simple internationalization package for Dart and Flutter. This package now supports hot reload and is tested on latest versions of Flutter.

Overview #

Write your messages into YAML files, and let this package generate convenient Dart classes from those files.

Turn this YAML file:

lib/messages.i18n.yaml

button:
  save: Save
  load: Load
users:
  welcome(String name): "Hello $name!"
  logout: Logout

Into these generated Dart classes:

class Messages {
    const Messages();
    ButtonMessages get button => ButtonExampleMessages(this);
    UsersMessages get users => UsersExampleMessages(this);
}
class ButtonMessages {
    final Messages _parent;
    const ButtonMessages(this._parent);
    String get save => "Save";
    String get load => "Load";
}
class UsersMessages {
    final Messages _parent;
    const UsersMessages(this._parent);
    String get logout => "Logout";
    String welcome(String name) => "Hello $name!";
}

... and use them in your code - plain and simple.

Messages m = Messages();
print(m.users.welcome('World'));
// outputs: Hello World!

Package is an extension (custom builder) for build_runner (Dart standard for source generation) and it can be used with Flutter, AngularDart or any other type of Dart project.

Motivation and goals #

  • The official Dart/Flutter approach to i18n seems to be ... complicated and kind of ... heavyweight.
  • I would like my messages to be checked during compile time. Is that message really there?
  • Key to the localized message shouldn't be just some arbitrary String, it should be a getter method!
  • And if the message takes some parameters, the method should take those parameters!
  • I like to bundle messages into thematic groups, the i18n tool should support that and help me with it.
  • Dart has awesome string interpolation, I want to leverage that!
  • I like build_runner and code generation.

Solution #

Write your messages into a YAML file:

messages.i18n.yaml (default messages):

generic:
  ok: OK
  done: DONE
invoice:
  create: Create invoice
  delete: Delete invoice

Write your translations into other YAML files:

messages_de.i18n.yaml (_de = German translation)

generic:
  ok: OK
  done: ERLEDIGT
invoice:
  create: Rechnung erstellen
  delete: Rechnung löschen

... run the webdev tool, or build_runner directly, and use your messages like this:

Messages m = Messages();
print(m.generic.ok); // output: OK
print(m.generic.done); // output: DONE

m = Messages_de();
print(m.generic.ok); // output: OK
print(m.generic.done); // output: ERLEDIGT

Parameters and pluralization #

The implementation is VERY straightforward, which allows you to do all sorts of crazy stuff:

invoice:
  create: Create invoice
  delete: Delete invoice
  help: "Use this function
  to generate new invoices and stuff.
  Awesome!"
  count(int cnt): "You have created $cnt ${_plural(cnt, one:'invoice', many:'invoices')}."
apples:
  _apples(int cnt): "${_plural(cnt, one:'apple', many:'apples')}"
  count(int cnt): "You have eaten $cnt ${_apples(cnt)}."

Now see the generated classes:

class Messages {
    const Messages();
    InvoiceMessages get invoice => InvoiceExampleMessages(this);        
    ApplesMessages get apples => ApplesExampleMessages(this);
}
    
class InvoiceMessages {
    final Messages _parent;
    const InvoiceMessages(this._parent);
    String get create => "Create invoice";
    String get help => "Use this function to generate new invoices and stuff. Awesome!";
    String get delete => "Delete invoice";
    String count(int cnt) => "You have created $cnt ${_plural(cnt, one:'invoice', many:'invoices')}.";
}

class ApplesMessages {
    final Messages _parent;
    const ApplesMessages(this._parent);
    String _apples(int cnt) => "${_plural(cnt, one:'apple', many:'apples')}";
    String count(int cnt) => "You have eaten $cnt ${_apples(cnt)}.";
}         

See how you can reuse the pluralization of _apples(int cnt)? (nice!)

There are three functions you can use in your message:

String _plural(int count, {String zero, String one, String two, String few, String many, String other})

String _cardinal(int count, {String zero, String one, String two, String few, String many, String other})

String _ordinal(int count, {String zero, String one, String two, String few, String many, String other})

_plural and _cardinal do the same. I just felt that _plural is a little bit less scary name :-)

We need only two forms of the word "apple" in English. "Apple" (one) and "apples" (many). But in some languages like Czech, we need three:

apples:
  _apples(int cnt): "${_plural(cnt, one:'jablko', few: 'jablka', many:'jablek')}"

See also:

How to use generated classes #

How to decide what translation to use (ExampleMessages_de?, ExampleMessages_hu?) is up to you. The package simply generates message classes, that's all.

import 'messages.i18n.dart';
import 'messages_de.i18n.dart' as de;

void main() async {
  Messages m = Messages();
  print(m.apples.count(1));
  print(m.apples.count(2));
  print(m.apples.count(5));

  m = de.Messages_de(); // see? ExampleMessages_cs extends ExampleMessages
  print(m.apples.count(1));
  print(m.apples.count(2));
  print(m.apples.count(5));    
}    

Where and how to store instances of these message classes - again, up to you. I would consider ScopedModel for Flutter and registering messages instance into dependency injection in AngularDart.

But in this case a singleton would be acceptable also.

How to use with Flutter #

Create YAML file with your messages, for example:

lib/messages/foo.i18n.yaml

Add build_runner as a dev_dependency and i18n as a dependency to pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  i18n: any
  ...

dev_dependencies:
  build_runner: any
  flutter_test:
    sdk: flutter

Open a terminal and in the root of your Flutter project run:

flutter packages pub run build_runner watch

... and keep it running. Your message classes will appear next to YAML files and will be rebuilt automatically each time you change the source YAML.

For one-time (re)build of your messages run:

flutter packages pub run build_runner build

Import generated messages and use them:

import 'packages:my_app/messages/foo.i18n.dart'

...

Foo m = Foo();
return Text(m.bar);
...

How to use with AngularDart #

You are probably using webdev tool already, so you just need to add i18n as a dependency and that's all.

Custom pluralization #

The package can correctly decide between 'one', 'few', 'many', etc. only for English and Czech (for now). But you can easily plug your own language, see example/main.dart and Czech and English implementation.

If you implement support for your language, please let me know, I'll gladly embed it into the package.

78
likes
160
pub points
89%
popularity

Publisher

verified publishermuha.dev

Simple i18n solution for dart and flutter. Uses code generation to generate translations as dart classes. Efficient and works with autocomplete!

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

build, build_config, dart_style, glob, quick_log, yaml

More

Packages that depend on i18n