Alternative i18n tool for Dart and Flutter.

Related tags

Templates i69n
Overview

Simple internationalization (i18n) package for Dart and Flutter.

Build Status pub package open source

Supports:

  • AngularDart
  • Flutter hot reload
  • deferred loading of translations
  • social distancing
  • null safety

Overview

Turn this YAML file:

lib/exampleMessages.i69n.yaml

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

Into these generated Dart classes:

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

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

ExampleMessages m = ExampleMessages();
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.

i69n: 51 points simpler than your standard i18n!

  • 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, it should be a method which take those parameters!

How to use with Flutter

Create YAML file with your messages, for example:

lib/messages/foo.i69n.yaml

generic:    
  done: Done
  ok: OK
invoice:
  create: Create an invoice
  delete: Delete this invoice    

Add translations for different languages:

lib/messages/foo_cs.i69n.yaml (_cs = Czech translation)

generic:    
  done: Hotovo
  # ok is the same and Foo_cs extends Foo.
invoice:
  create: Vytvořit fakturu
  delete: Smazat fakturu

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

dependencies:
  flutter:
    sdk: flutter
  i69n: 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 --delete-conflicting-outputs

... 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 --delete-conflicting-outputs

Import generated messages and use them:

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

...

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

... or ...

import 'packages:my_app/messages/foo_cs.i69n.dart'

Foo m = Foo_cs(); // Notice: Foo_cs extends Foo
return Text(m.bar);

How to use with AngularDart

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

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 ExampleMessages {
    const ExampleMessages();
    InvoiceExampleMessages get invoice => InvoiceExampleMessages(this);        
    ApplesExampleMessages get apples => ApplesExampleMessages(this);
}
    
class InvoiceExampleMessages {
    final ExampleMessages _parent;
    const InvoiceExampleMessages(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 ApplesExampleMessages {
    final ExampleMessages _parent;
    const ApplesExampleMessages(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)? (!!!)

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 sounds a little bit less scary and in most cases that's the one you need.

We need only two forms of the word "apple" in English. "Apple" (one) and "apples" (many). But in 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_cs?, ExampleMessages_hu?) is up to you. The package simply generates message classes, that's all.

import 'exampleMessages.i69n.dart';
import 'exampleMessages_cs.i69n.dart' deferred as cs;

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

  await cs.loadLibrary();
  m = cs.ExampleMessages_cs(); // 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.

Customization and speacial features

Dynamic access using String keys

It's still useful to access your messages using the String keys in some cases. For example when the key of the message is composed dynamically at runtime, maybe like this:

var vehicleTypeMessageKey = "VehicleType.${data['type']'}";

You can access your messages like this:

print('Static:  ${m.generic.ok}');
print('Dynamic: ${m.generic['ok']}');
print('Or even: ${m['generic.ok']}');

In case the key doesn't exist, an exception is thrown:

...
throw Exception('Message $key doesn\'t exist in $this');

You can disable this behaviour by adding flag 'nothrow':

_i69n: nothrow
generic:
  done: Done
  ok: OK

This flag is inherited to the lower levels of messages structure.

In some rare cases you might want to 'disable' this map generation (maybe to enable better tree-shaking of unused messages?). In such case use a simple flag 'nomap':

generic:
  _i69n: nomap
  done: Done
  ok: OK             

No message in 'generic' message group will be accessible through the [] operator. Scope of the flag is very narrow - one message object in one file. Flag is not inherited into lower levels of messages and in most cases you want to repeat it in all translations files to make an impact.

Escaping special characters

i69n uses double quotes in generated source files. Unless you need to use double quotes in your string, you should be fine.

message: Let's go!
...
String get message => "Let's go!";

Only "\t", "\r" and "\n" characters are automatically escaped by i69n.

message: "Let's\ngo!" // quotes needed by YAML, \n is converted to new line by YAML
...
String get message => "Let's\ngo!"; // new line escaped by i69n -> result is the same string    

If you need to disable escaping, use "noescape" flag.

_i69n: noescape
message: Let's go!

Escaping the details of escaping

These are a few examples of more complicated strings in which you might need to add a backslash here and there:

problematic: "Hello \\\"world\\\"!"  
//                  ^^^ yes, tripple backslash

lessProblematic: 'Hello \"world\"!'
//                      ^ use single quotes in YAML, add only one backslash for Dart    

alsoProblematic(int count): "${_plural(count, zero:'didn\\'t find any tasks', other: 'found some tasks')}"
//                                                      ^ here

Please prefer single quotes inside pluralization functions.

But in most cases you will be fine. Just observe generated classes and maybe experiment a little bit.

More configuration flags

So far only 'noescape' and 'nomap'. If you need both, separate them with comma.

_i69n: noescape,nomap            

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: [email protected]

TODO

  • Current limitation: default language must be english
  • TODO: support custom imports

Example

See example. Clone the package repository (https://github.com/fnx-io/i69n) and run:

webdev serve example:8080

or

pub run build_runner serve example:8080

Now open the browser http://localhost:8080/ and watch the dev tools console.

Credits

Created by https://fnx.io.

See also:

https://pub.dev/packages/preconditions

Comments
  • Escaping issues

    Escaping issues

    First off, thank you for creating this awesome library. It's so much better than any other I have found!

    However, I noticed that this library has some serious escaping issues. To demonstrate them, I created an i69n.yaml file with the following contents:

    didntWork: "That didn't work."
    didntWorkEscaped: "That didn\'t work."
    twoLineMessage: "This is line 1\nThis is line 2"
    

    The first line fails with the following message:

    line 18, column 45: Unterminated string literal.
       ╷
    18 │     String get didntWork => 'That didn't work.';
       │                                                ^
       ╵
    

    Ideally, the library would escape the characters for me, but manually escaping them would be ok. So, in the second line I escaped the ', which gave me the following error:

    Error on line 2, column 29: Unknown escape character.
      ╷
    2 │ didntWorkEscaped: "That didn\'t work."
      │                             ^
      ╵
    

    So, regular escaping does not work. The only workaround is double escaping everything (' becomes \\'). This is far from ideal.

    When you don't double-escape a newline character, the library even processes it as if it is a real newline character. The third line gave me the following error messages:

    line 19, column 45: Unterminated string literal.
       ╷
    19 │     String get twoLineMessage => 'This is line 1
       │                                                ^
       ╵
    
    line 20, column 16: Unterminated string literal.
       ╷
    20 │ This is line 2';
       │                ^
       ╵
    

    The library is usable with the double-escaping workaround, however, when you try to manage a large project with a lot of translations and translators, you would have to either manually check all translations for escaping issues, or write some script to do it for you. So, it would be great if the library could handle this itself.

    opened by renzo800000 11
  • Null safety migration

    Null safety migration

    Thanks for your package! I totally love it!

    I have migrated the package to null safety. Mostly here you might find changes from the Dart migration utility but I have changed code here and there to make linter happy. All tests passed and samples from the example folder were generated fine as well. I have tried this version with a simple app and seems like everything is working as expected.

    Please take a look at the proposed changes. Thanks in advance!

    opened by olexale 7
  • get rid of analyzer warnings

    get rid of analyzer warnings

    Dart analyzer complains about several things in generated code, which makes i69n usage unpleasant.

    typical i69n analyzer complaints:

    info: The declaration '_localeName' isn't referenced. (unused_element at lib/i18n/messages.i69n.dart:5)
    info: The declaration '_plural' isn't referenced. (unused_element at lib/i18n/messages.i69n.dart:7)
    info: The declaration '_ordinal' isn't referenced. (unused_element at lib/i18n/messages.i69n.dart:16)
    info: The declaration '_cardinal' isn't referenced. (unused_element at lib/i18n/messages.i69n.dart:25)
    info: Unnecessary new keyword. (unnecessary_new at lib/i18n/messages.i69n.dart:605)
    info: The declaration '_localeName' isn't referenced. (unused_element at lib/i18n/messages_cs.i69n.dart:7)
    info: The declaration '_plural' isn't referenced. (unused_element at lib/i18n/messages_cs.i69n.dart:9)
    info: The declaration '_ordinal' isn't referenced. (unused_element at lib/i18n/messages_cs.i69n.dart:18)
    info: The declaration '_cardinal' isn't referenced. (unused_element at lib/i18n/messages_cs.i69n.dart:27)
    info: Name types using UpperCamelCase. (camel_case_types at lib/i18n/messages_cs.i69n.dart:37)
    
    bug 
    opened by dafesimonek 4
  • Update dependencies

    Update dependencies

    Hi Tomáš,

    First of all, thanks for this great package! I am using it literally in every Flutter project I can.

    Recently, a new 1.0.0 version of build_config was released with the null safety support, and a build_runner v2.0.1 is also out there. But because i69n depends on build_config: ^0.4.7, it makes it impossible to use with the latest build_runner and other code generating plugins.

    Screenshot 2021-05-01 at 13 33 31

    This PR contains updates for build_config, as well as build_runner and build dependencies.

    opened by foxanna 3
  • Steel this package updated ?

    Steel this package updated ?

    Because build_runner >=1.12.0 depends on build >=2.0.0 <2.1.0 and i69n 1.1.0 depends on build ^1.2.0, build_runner >=1.12.0 is incompatible with i69n 1.1.0.
    So, because muqel_front depends on both i69n 1.1.0 and build_runner 1.12.2, version solving failed.
    pub get failed (1; So, because muqel_front depends on both i69n 1.1.0 and build_runner 1.12.2, version solving failed.)
    
    opened by BeliliFahem 2
  • Support packages

    Support packages

    This package is, of course, superior :fire: to the default localization packages. Althrough it doesn't provide a simple solution for using it in Flutter or Angular project.

    It would be super nice to create support packages – with inspiration in BLoC packages structure – which would contain i69n, flutter_i69n and angular_i69n in packages/ directory.

    This would increase the ability to adapt this package. For example, this is some other package about easy localization which has support for Flutter which gives it right on the spot better prestigue to use it. People are lazy and don't wanna repeat yourselves (or dumb and they cannot create proper support packages).

    For Flutter it would mean to create, probably, only Localizations and LocalizationsDelegate classes and maybe (?) modify a bit the current package to support loading proper file using locale codes, if it isn't already supported now. Dunno, further investigation would be needed.

    Some other resources:

    • https://flutter.dev/docs/development/accessibility-and-localization/internationalization
    • https://medium.com/saugo360/managing-locale-in-flutter-7693a9d4d6ac
    • https://proandroiddev.com/flutter-localization-step-by-step-30f95d06018d
    • https://pub.dev/packages/flutter_i18n

    Personal interest: I would like to use this backage as part of my Bachelor's Thesis, for it's simplicity, so I can try to create the Flutter package or help with it.

    opened by tenhobi 7
Owner
fnx.io
Backend studio.
fnx.io
Small sample app to work on simplifying the i18n process

l10n_s12n A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if thi

Shi-Hao Hong 11 Jul 19, 2020
This is tool to create 3D Models which can be used in Flutter Applications. Tool is developed completely using Flutter.

three_d_model_tool A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you start

Shubham Yeole 2 Nov 8, 2022
Trying out Flutter for desktop Web app development as an alternative to SPA frameworks (such as React and Angular) by recreating one of the pages of an existing CV Management web app

HTML Renderer Demo CanvasKit Renderer Demo Reddit discussion This repo contains a PoC of using Flutter as a traditional SPA framework for creating a d

Maxim Saplin 20 Oct 11, 2022
An alternative UI for the Human Resources Management System.

Human Resources Management System Frontend with Flutter An alternative UI for the Human Resources Management System that uses Flutter Framework and pr

Bulent Baris Kilic 18 Sep 7, 2022
Alternative Skyward gradebook app

SkyMobile SkyMobile is a remake of the original SkyMobile_iOS. SkyMobile is cross platform and is now supported on android. SkyMobile has all the feat

ClearHall Development 9 Oct 28, 2022
Tool made in Dart that allows you to dynamically generate JSON files from data models written in Dart.

Dart JSON Generator Versión v1.1.1 Dart JSON Generator es una herramienta que permite generar archivos JSON a partir de mapas como modelos de datos en

Joinner Medina 7 Nov 23, 2022
A CLI tool and Dart package that can scrape file and directory URLs from h5ai instances.

h5ai scraper A CLI tool and Dart package that can scrape file and directory URLs from h5ai instances. Usage This tool requires the Dart SDK. It can be

null 1 Jan 4, 2023
Dart JS interop for Mermaid - The Javascript tool that makes use of a markdown based syntax to render customizable diagrams, charts and visualizations.

Mermaid (Dart) Dart JS interop for Mermaid - Javascript library that makes use of a markdown based syntax to render customizable diagrams, charts and

Tim Maffett 3 Dec 12, 2022
Protofu is a Dart Command Line tool for generating your protobufs and included dependencies in one command.

ProtoFu - let me compile that for you ProtoFu exists to make working with protobufs easier. Gone are the days of downloading protoc and the dart proto

John McDole 5 Oct 27, 2022
This is an opinionated code-generation tool from GraphQL to Dart/Flutter.

GraphQL Codegen This is an opinionated code-generation tool from GraphQL to Dart/Flutter. It'll allow you to generate Dart serializers and client help

United Traders 1 Dec 29, 2021
A package script for allowing coverage test tool to see all Dart files

full_coverage Coverage tools like codecov only see the files that were actually triggered by tests. This means that a coverage of 100% can easily be a

Flutterando 2 Mar 18, 2022
A tool for maintaining Dart comments.

A tool for maintaining Dart comments (daco). This package is in an early stage of development. Please file an issue if you find a bug or start a discu

Gabriel Terwesten 3 Nov 15, 2022
This is a simple Gantt chart generator written as Dart command line tool.

Gantt Chart Generator This is a simple Gantt chart generator written as Dart command line tool. Currently the tool generates an HTML/CSS Gantt chart.

John Lyon-Smith 1 Apr 26, 2022
Custom flutter testing CLI tool for individual test runs and group testing

fluttertest Custom flutter testing CLI tool for inidividual test runs or group testing Overview Flutter is a great framework which has helps developer

vi_mi 15 Nov 6, 2022
A tool which automatically generates Flutter localization resources from CSV and Excel files.

flappy_translator A tool which automatically generates Flutter localization resources from CSV and Excel files. This is especially useful as any team

Smart&Soft 55 Sep 15, 2022
Quickly is build as a tool to enhance your Flutter UI development experience and make code easier

Quickly is build as a tool to enhance your Flutter UI development experience and make code easier. It is highly inspired by Bootstrap and Tailwind CSS. It also provide lots of extension methods on String, List and Map.

Aniket Khote 11 Oct 24, 2022
The one and only Docker Tool you will ever need again

# container_tool A new Flutter project. ## Getting Started This project is a starting point for a Flutter application. A few resources to get you

null 1 Nov 17, 2021
A tool for digitizing medical history, prescriptions, and reports.

Shealth A tool for digitising medical history, prescriptions and report Landing page Login page Home page Registeration page Installation To render re

Servatom 15 Dec 26, 2022
Software analytics tool that helps developers analyse and improve software quality.

Dart Code Metrics Note: you can find the full documentation on the website Configuration | Rules | Metrics | Anti-patterns Dart Code Metrics is a stat

Dart Code Checker 745 Dec 26, 2022