Initial commit

This commit is contained in:
Muhammad Mohiuddin
2019-07-04 18:50:11 +02:00
commit 0d5cb7791a
18 changed files with 865 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
# Files and directories created by pub
.dart_tool/
.packages
.idea
*.iml
# Remove the following pattern if you wish to check in your lock file
pubspec.lock
*.i18n.dart
# Conventional directory for build outputs
build/
# Directory created by dartdoc
doc/api/

3
CHANGELOG.md Normal file
View File

@@ -0,0 +1,3 @@
## 0.8.5
- Initial fork from original library

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 fnx.io
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

263
README.md Normal file
View File

@@ -0,0 +1,263 @@
Simple internationalization package for Dart and Flutter.
# Overview
Write your messages into YAML files, and let this package generate
convenient Dart classes from those files.
Turn this **YAML** file:
lib/exampleMessages.i18n.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](https://pub.dartlang.org/packages/build_runner)
(Dart standard for source generation) and it can be used with Flutter, AngularDart
or any other type of Dart project.
# i18n: 51 points simpler than your standard i18n!
## 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.
* I love the name. i18n is hilarious.
## Solution
Write your messages into a YAML file:
exampleMessages.i18n.yaml (default messages):
generic:
ok: OK
done: DONE
invoice:
create: Create invoice
delete: Delete invoice
Write your translations into other YAML files:
exampleMessages_cs.i18n.yaml (_cs = Czech translation)
generic:
done: Hotovo
invoice:
create: Vytvořit fakturu
delete: Smazat fakturu
... run the `webdev` tool, or `build_runner` directly, and use your messages like this:
ExampleMessages m = ExampleMessages();
print(m.generic.ok); // output: OK
print(m.generic.done); // output: DONE
m = ExampleMessages_cs();
print(m.generic.ok); // output: OK
print(m.generic.done); // output: Hotovo
## 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)`? (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 Czech, we need three:
apples:
_apples(int cnt): "${_plural(cnt, one:'jablko', few: 'jablka', many:'jablek')}"
See also:
* http://cldr.unicode.org/index/cldr-spec/plural-rules
* https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
## 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.i18n.dart';
import 'exampleMessages_cs.i18n.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.
## 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](example/main.dart)
and [Czech](lib/src/cs.dart) and [English](lib/src/en.dart)
implementation.
If you implement support for your language, please let me know,
I'll gladly embed it into the package.
# TODO
* More detailed docs.
* Use it in some of our projects for real.
* Current limitation: default language must be english
* TODO: support custom imports
# Example
See [example](example). Clone the package repository ([https://github.com/fnx-io/i18n](https://github.com/fnx-io/i18n)) 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](https://fnx.io).

14
analysis_options.yaml Normal file
View File

@@ -0,0 +1,14 @@
# Defines a default set of lint rules enforced for
# projects at Google. For details and rationale,
# see https://github.com/dart-lang/pedantic#enabled-lints.
include: package:pedantic/analysis_options.yaml
# For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
# Uncomment to specify additional rules.
# linter:
# rules:
# - camel_case_types
analyzer:
# exclude:
# - path/to/excluded/files/**

9
build.yaml Normal file
View File

@@ -0,0 +1,9 @@
# Read about `build.yaml` at https://pub.dartlang.org/packages/build_config
builders:
yamlBasedBuilder:
import: "package:i18n/builder.dart"
builder_factories: ["yamlBasedBuilder"]
build_extensions: {".i69n.yaml": [".i18n.dart"]}
build_to: source
auto_apply: root_package

View File

@@ -0,0 +1,13 @@
generic:
ok: OK
done: DONE
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)}."

View File

@@ -0,0 +1,12 @@
generic:
done: Hotovo
invoice:
create: Vytvořit fakturu
delete: Smazat fakturu
help: "Tuhle funkci použij
na vytváření faktur.
Boží!"
count(int a): "Už jsi vytvořil ${_plural(a, one:'fakturu', few:'faktury', many:'faktur')}."
apples:
_apples(int cnt): "${_plural(cnt, one:'jablko', few: 'jablka', many:'jablek')}"
count(int cnt): "Snědl jsi $cnt ${_apples(cnt)}."

7
example/index.html Normal file
View File

@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
<p>Open the dev tools console ...</p>
<script src="main.dart.js"></script>
</body>
</html>

42
example/main.dart Normal file
View File

@@ -0,0 +1,42 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:i18n/i18n.dart' as i18n;
import 'exampleMessages.i18n.dart';
import 'exampleMessages_cs.i18n.dart' deferred as cs;
void main() async {
print("Hello from i18n!");
print("Some english:");
ExampleMessages m = ExampleMessages();
print(m.generic.ok);
print(m.generic.done);
print(m.invoice.help);
print(m.apples.count(1));
print(m.apples.count(2));
print(m.apples.count(5));
print("Asynchronous load of Czech messages:");
await cs.loadLibrary();
print("Some czech:");
m = cs.ExampleMessages_cs();
print(m.generic.ok); // inherited from default
print(m.generic.done);
print(m.invoice.help);
print(m.apples.count(1));
print(m.apples.count(2));
print(m.apples.count(5));
// Override plurals for Czech or register support for your own language:
i18n.registerResolver("cs", (int count, i18n.QuantityType type) {
if (type == i18n.QuantityType.cardinal && count == 1) {
return i18n.QuantityCategory.one;
}
return i18n.QuantityCategory.other;
});
// See:
// http://cldr.unicode.org/index/cldr-spec/plural-rules
// https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
}

32
lib/builder.dart Normal file
View File

@@ -0,0 +1,32 @@
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:build/build.dart';
import 'package:i18n/src/i18n_impl.dart';
Builder yamlBasedBuilder(BuilderOptions options) => YamlBasedBuilder();
class YamlBasedBuilder implements Builder {
@override
Future build(BuildStep buildStep) async {
// Each [buildStep] has a single input.
var inputId = buildStep.inputId;
// Create a new target [AssetId] based on the old one.
var contents = await buildStep.readAsString(inputId);
var objectName = generateMessageObjectName(inputId.pathSegments.last);
var dartContent = generateDartContentFromYaml(objectName, contents);
var copy = inputId.changeExtension(".dart");
// Write out the new asset.
await buildStep.writeAsString(copy, dartContent);
}
@override
final buildExtensions = const {
".i18n.yaml": [".i18n.dart"]
};
}

99
lib/i18n.dart Normal file
View File

@@ -0,0 +1,99 @@
import 'src/cs.dart' as cs;
import 'src/en.dart' as en;
///
/// Language specific function, which is provided with a number and should return one of possible categories.
/// count is never null.
///
typedef QuantityCategory CategoryResolver(int count, QuantityType type);
enum QuantityCategory { zero, one, two, few, many, other }
enum QuantityType { cardinal, ordinal }
void registerResolver(String languageCode, CategoryResolver resolver) {
_resolverRegistry[languageCode] = resolver;
}
///
/// Same as ordinal.
///
String plural(int count, String languageCode, {String zero, String one, String two, String few, String many, String other}) {
return _resolvePlural(count, languageCode, QuantityType.cardinal, zero: zero, one: one, two: two, few: few, many: many, other: other);
}
///
/// See: http://cldr.unicode.org/index/cldr-spec/plural-rules
///
String cardinal(int count, String languageCode, {String zero, String one, String two, String few, String many, String other}) {
return _resolvePlural(count, languageCode, QuantityType.cardinal, zero: zero, one: one, two: two, few: few, many: many, other: other);
}
///
/// See: http://cldr.unicode.org/index/cldr-spec/plural-rules
///
String ordinal(int count, String languageCode, {String zero, String one, String two, String few, String many, String other}) {
return _resolvePlural(count, languageCode, QuantityType.ordinal, zero: zero, one: one, two: two, few: few, many: many, other: other);
}
Map<String, CategoryResolver> _resolverRegistry = {
"en": en.quantityResolver,
"cs": cs.quantityResolver,
};
String _resolvePlural(int count, String languageCode, QuantityType type, {String zero, String one, String two, String few, String many, String other}) {
QuantityCategory c = _resolveCategory(languageCode, count, type);
if (c == null) c = QuantityCategory.other;
if (many == null) many = other;
switch (c) {
case QuantityCategory.zero:
return _firstNotNull(zero, many);
case QuantityCategory.one:
return _firstNotNull(one, many);
case QuantityCategory.two:
return _firstNotNull(two, many);
case QuantityCategory.few:
return _firstNotNull(few, many);
case QuantityCategory.many:
return _firstNotNull(many, other);
case QuantityCategory.other:
return _firstNotNull(other, many);
}
return "???";
}
QuantityCategory _defaultResolver(int count, QuantityType type) {
switch (count) {
case 0:
return QuantityCategory.zero;
case 1:
return QuantityCategory.one;
case 2:
return QuantityCategory.two;
case 3:
return QuantityCategory.few;
case 4:
return QuantityCategory.few;
}
return QuantityCategory.other;
}
QuantityCategory _resolveCategory(String languageCode, int count, QuantityType type) {
if (count == null) return QuantityCategory.other;
CategoryResolver resolver;
if (languageCode != null) {
resolver = _resolverRegistry[languageCode];
if (resolver == null) {
resolver = _defaultResolver;
}
} else {
resolver = _defaultResolver;
}
return resolver(count, type);
}
String _firstNotNull(String a, String b) {
if (a != null) return a;
if (b != null) return b;
return "???";
}

32
lib/src/cs.dart Normal file
View File

@@ -0,0 +1,32 @@
import 'package:i18n/i18n.dart';
///
/// Quantity category resolver for czech.
///
/// See:
///
/// https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#cs
///
///
QuantityCategory quantityResolver(int count, QuantityType type) {
if (type == QuantityType.ordinal) return _resolveOrdinal(count);
return _resolveCardinal(count);
}
QuantityCategory _resolveCardinal(int count) {
switch (count) {
case 1:
return QuantityCategory.one;
case 2:
return QuantityCategory.few;
case 3:
return QuantityCategory.few;
case 4:
return QuantityCategory.few;
}
return QuantityCategory.other;
}
QuantityCategory _resolveOrdinal(int count) {
return QuantityCategory.other;
}

31
lib/src/en.dart Normal file
View File

@@ -0,0 +1,31 @@
import 'package:i18n/i18n.dart';
///
/// Quantity category resolver for english.
///
/// See:
///
/// https://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#en
///
///
QuantityCategory quantityResolver(int count, QuantityType type) {
if (type == QuantityType.ordinal) return _resolveOrdinal(count);
return _resolveCardinal(count);
}
QuantityCategory _resolveCardinal(int count) {
switch (count) {
case 1:
return QuantityCategory.one;
}
return QuantityCategory.other;
}
QuantityCategory _resolveOrdinal(int count) {
int mod10 = count % 10;
int mod100 = count % 100;
if (mod10 == 1 && mod100 != 11) return QuantityCategory.one;
if (mod10 == 2 && mod100 != 12) return QuantityCategory.two;
if (mod10 == 3 && mod100 != 13) return QuantityCategory.few;
return QuantityCategory.other;
}

143
lib/src/i18n_impl.dart Normal file
View File

@@ -0,0 +1,143 @@
library i18n;
import 'package:yaml/yaml.dart';
part 'model.dart';
Pattern twoCharsLower = RegExp("^[a-z]{2}\$");
Pattern twoCharsUpper = RegExp("^[A-Z]{2}\$");
String generateDartContentFromYaml(ClassMeta meta, String yamlContent) {
YamlMap messages = loadYaml(yamlContent);
List<TodoItem> todoList = [];
prepareTodoList(todoList, messages, meta);
StringBuffer output = StringBuffer();
output.writeln("// GENERATED FILE, do not edit!");
output.writeln("import 'package:i18n/i18n.dart' as i18n;");
if (meta.defaultFileName != null) {
output.writeln("import '${meta.defaultFileName}';");
}
output.writeln("");
output.writeln("String get _languageCode => '${meta.languageCode}';");
output.writeln("String get _localeName => '${meta.localeName}';");
output.writeln("");
output.writeln("String _plural(int count, {String zero, String one, String two, String few, String many, String other}) =>");
output.writeln("\ti18n.plural(count, _languageCode, zero:zero, one:one, two:two, few:few, many:many, other:other);");
output.writeln("String _ordinal(int count, {String zero, String one, String two, String few, String many, String other}) =>");
output.writeln("\ti18n.ordinal(count, _languageCode, zero:zero, one:one, two:two, few:few, many:many, other:other);");
output.writeln("String _cardinal(int count, {String zero, String one, String two, String few, String many, String other}) =>");
output.writeln("\ti18n.cardinal(count, _languageCode, zero:zero, one:one, two:two, few:few, many:many, other:other);");
output.writeln("");
for (var todo in todoList) {
renderTodoItem(todo, output);
output.writeln("");
}
return output.toString();
}
ClassMeta generateMessageObjectName(String fileName) {
String name = fileName.replaceAll(".i18n.yaml", "");
List<String> nameParts = name.split("_");
if (nameParts.isEmpty) {
throw Exception(_renderFileNameError(name));
}
ClassMeta result = ClassMeta();
result.defaultObjectName = _firstCharUpper(nameParts[0]);
if (nameParts.length == 1) {
result.objectName = result.defaultObjectName;
result.isDefault = true;
result.languageCode = "en";
result.localeName = "en";
return result;
} else {
result.defaultFileName = "${nameParts[0]}.i18n.dart";
result.isDefault = false;
if (nameParts.length > 3) {
throw Exception(_renderFileNameError(name));
}
if (nameParts.length >= 2) {
String languageCode = nameParts[1];
if (twoCharsLower.allMatches(languageCode).length != 1) {
throw Exception("Wrong language code '$languageCode' in file name '$fileName'. Language code must match $twoCharsLower");
}
result.languageCode = languageCode;
result.localeName = languageCode;
}
if (nameParts.length == 3) {
String countryCode = nameParts[2];
if (twoCharsUpper.allMatches(countryCode).length != 1) {
throw Exception("Wrong country code '$countryCode' in file name '$fileName'. Country code must match $twoCharsUpper");
}
result.localeName = "${result.languageCode}_${countryCode}";
}
result.objectName = "${result.defaultObjectName}_${result.localeName}";
return result;
}
}
void renderTodoItem(TodoItem todo, StringBuffer output) {
var meta = todo.meta;
YamlMap content = todo.content;
if (meta.isDefault) {
output.writeln("class ${meta.objectName} {");
} else {
output.writeln("class ${meta.objectName} extends ${meta.defaultObjectName} {");
}
if (meta.parent == null) {
output.writeln("\tconst ${meta.objectName}();");
} else {
output.writeln("\tfinal ${meta.parent.objectName} _parent;");
if (meta.isDefault) {
output.writeln("\tconst ${meta.objectName}(this._parent);");
} else {
output.writeln("\tconst ${meta.objectName}(this._parent):super(_parent);");
}
}
content.forEach((k, v) {
if (v is YamlMap) {
String prefix = _firstCharUpper(k);
ClassMeta child = meta.nest(prefix);
output.writeln("\t${child.objectName} get ${k} => ${child.objectName}(this);");
} else {
if (k.contains("(")) {
// function
output.writeln('\tString ${k} => "${v}";');
} else {
output.writeln('\tString get ${k} => "${v}";');
}
}
});
output.writeln("}");
}
void prepareTodoList(List<TodoItem> todoList, YamlMap messages, ClassMeta name) {
TodoItem todo = TodoItem(name, messages);
todoList.add(todo);
messages.forEach((k, v) {
if (v is YamlMap) {
String prefix = _firstCharUpper(k);
prepareTodoList(todoList, v, name.nest(prefix));
}
});
}
String _firstCharUpper(String s) {
return s.replaceRange(0, 1, s[0].toUpperCase());
}
String _renderFileNameError(String name) {
return "Wrong file name: '$name'";
}

29
lib/src/model.dart Normal file
View File

@@ -0,0 +1,29 @@
part of i18n;
class ClassMeta {
ClassMeta parent;
bool isDefault;
String defaultObjectName;
String defaultFileName;
String objectName;
String localeName;
String languageCode;
ClassMeta nest(String namePrefix) {
ClassMeta result = ClassMeta();
result.parent = this;
result.isDefault = isDefault;
result.defaultObjectName = "${namePrefix}${defaultObjectName}";
result.objectName = "${namePrefix}${objectName}";
result.localeName = localeName;
result.languageCode = languageCode;
return result;
}
}
class TodoItem {
ClassMeta meta;
YamlMap content;
TodoItem(this.meta, this.content);
}

21
pubspec.yaml Normal file
View File

@@ -0,0 +1,21 @@
name: i18n
description: Simple internationalization tool for Dart and Flutter, based on YAML files and source generation.
version: 0.8.5
homepage: https://github.com/MohiuddinM/i18n
author: Muhammad Mohiuddin <muhammad.mohiuddin@live.com>
environment:
sdk: '>=2.1.0 <3.0.0'
dependencies:
build: ^1.0.0
# Not imported in code, but used to constrain `build.yaml` requirements
build_config: ^0.3.0
yaml: ^2.1.15
dev_dependencies:
pedantic: ^1.0.0
test: ^1.0.0
build_runner: ^1.0.0
build_web_compilers: ^1.0.0

80
test/i18n_test.dart Normal file
View File

@@ -0,0 +1,80 @@
import 'package:i18n/i18n.dart';
import 'package:i18n/src/i18n_impl.dart';
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';
void main() {
group('Messages meta data', () {
testMeta("messages", isDefault: true, defaultObjectName: "Messages", objectName: "Messages", languageCode: "en", localeName: "en");
testMeta("messages_cs", isDefault: false, defaultObjectName: "Messages", objectName: "Messages_cs", languageCode: "cs", localeName: "cs");
testMeta("domainMessages", isDefault: true, defaultObjectName: "DomainMessages", objectName: "DomainMessages", languageCode: "en", localeName: "en");
testMeta("domainMessages_cs", isDefault: false, defaultObjectName: "DomainMessages", objectName: "DomainMessages_cs", languageCode: "cs", localeName: "cs");
testMeta("domainMessages_cs_CZ",
isDefault: false, defaultObjectName: "DomainMessages", objectName: "DomainMessages_cs_CZ", languageCode: "cs", localeName: "cs_CZ");
});
group('Plurals', () {
test('en', () {
expect(plural(1, "en", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("ONE!"));
expect(plural(2, "en", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("OTHER!"));
expect(plural(3, "en", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("OTHER!"));
expect(plural(10, "en", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("OTHER!"));
});
test('cs', () {
expect(plural(1, "cs", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("ONE!"));
expect(plural(2, "cs", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("FEW!"));
expect(plural(3, "cs", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("FEW!"));
expect(plural(10, "cs", one: "ONE!", few: "FEW!", other: "OTHER!"), equals("OTHER!"));
});
});
group('Message building', () {
test('Todo list', () {
ClassMeta root = new ClassMeta();
root.objectName = "Test";
root.defaultObjectName = "Test";
List<TodoItem> todoList = [];
var yaml = "foo:\n"
" subfoo: subbar\n"
" subfoo2: subbar2\n"
"other: maybe\n"
"or:\n"
" status:\n"
" name: not\n";
prepareTodoList(todoList, loadYaml(yaml), root);
todoList.sort((a, b) {
return a.meta.objectName.compareTo(b.meta.objectName);
});
expect(todoList.length, equals(4));
expect(todoList[0].meta.objectName, equals("FooTest"));
expect(todoList[1].meta.objectName, equals("OrTest"));
expect(todoList[2].meta.objectName, equals("StatusOrTest"));
expect(todoList[2].meta.parent, equals(todoList[1].meta));
expect(todoList[2].meta.parent.parent, equals(todoList[3].meta));
expect(todoList[3].meta.objectName, equals("Test"));
expect(todoList[3].meta.parent, isNull);
});
});
}
void testMeta(String name, {bool isDefault, String defaultObjectName, String objectName, String languageCode, String localeName}) {
ClassMeta meta = generateMessageObjectName(name);
test('$name: isDefault', () {
expect(meta.isDefault, equals(isDefault));
});
test('$name: defaultObjectName', () {
expect(meta.defaultObjectName, equals(defaultObjectName));
});
test('$name: objectName', () {
expect(meta.objectName, equals(objectName));
});
test('$name: localeName', () {
expect(meta.localeName, equals(localeName));
});
test('$name: languageCode', () {
expect(meta.languageCode, equals(languageCode));
});
}