Skip to content

Commit

Permalink
add pluralization
Browse files Browse the repository at this point in the history
  • Loading branch information
Nekith committed Feb 15, 2017
1 parent c1f8a74 commit 3aa7e9f
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 28 deletions.
35 changes: 23 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,20 @@ It's JSON, objects and strings:

```json
{
"welcome": {
"hello": "Hoy!",
"subtitle": "Welcome, :name!",
"content": {
"main": "Main content should be longer but you get the idea.",
"side": "Some useful side notes to shine in society."
"welcome": {
"hello": "Hoy!",
"subtitle": "Welcome, :name!",
"content": {
"main": "Main content should be longer but you get the idea.",
"side": "Some useful side notes to shine in society."
}
},
"news": {
"list": { "0": "Nothing to display.", "1": "Only one new item.", "_": ":_ new items." }
},
"secret": {
"intro": "It's a secret page! Do you have authorization?"
}
},
"secret": {
"intro": "It's a secret page! Do you have authorization?"
}
}
```

Expand Down Expand Up @@ -99,11 +102,19 @@ You can pass variables to strings returned by tr() like this:
I18n.tr("welcome/subtitle", [ "name" => "Nekith" ]); // Welcome, Nekith!
```

### Pluralization

```haxe
I18n.tr("news/list", [ "_" => 0 ]); // Nothing to display.
I18n.tr("news/list", [ "_" => 12 ]); // 12 new items.
```

### Configuration

```haxe
I18n.depthDelimiter = "_"; // default: "/"
I18n.varPrefix = "@"; // default: ":"
I18n.depthDelimiter = "."; // default: "/"
I18n.varPrefix = "@"; // default: ":"
I18n.pluralizationVar = "n"; // default: "_"
```

## License
Expand Down
4 changes: 2 additions & 2 deletions haxelib.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"license": "BSD",
"tags": ["cross", "i18n", "translation", "internationalization", "localization"],
"description": "A flexible internationalization library working with JSON files in Haxe.",
"version": "0.2.3",
"releasenote": "Fix the files architecture in release.",
"version": "0.3.0",
"releasenote": "Add pluralization and tests.",
"contributors": ["Nekith"],
"dependencies": {}
}
32 changes: 22 additions & 10 deletions jsoni18n/I18n.hx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import haxe.Json;

class I18n
{
static public var depthDelimiter : String = "/";
static public var varPrefix : String = ":";
public static var depthDelimiter : String = "/";
public static var varPrefix : String = ":";
public static var pluralizationVar : String = "_";

private static var trads : DynamicObject<Dynamic>;

public static function loadFromString(content : String, ?prefix : String) : Void
{
Expand All @@ -22,16 +25,29 @@ class I18n
}
}

public static function tr(id : String, ?vars : Map<String, String>) : String
public static function tr(id : String, ?vars : Map<String, Dynamic>) : String
{
if (trads == null) {
return id;
}
var str : String = id;
if (id.indexOf(depthDelimiter) != -1) {
var o = fetch(trads, new String(id));
var o : DynamicObject<Dynamic> = fetch(trads, new String(id));
if (o != null) {
str = o;
if (Std.is(o, String)) {
str = Std.string(o);
} else if (vars != null && vars.exists(pluralizationVar) && o.exists(pluralizationVar)) {
var n : Null<Int> = Std.parseInt(vars[pluralizationVar]);
if (n != null) {
if (n == 0 && o.exists("0")) {
str = o.get("0");
} else if (n == 1 && o.exists("1")) {
str = o.get("1");
} else if (o.exists(pluralizationVar)) {
str = o.get(pluralizationVar);
}
}
}
}
} else if (trads.exists(id) == true) {
str = trads.get(id);
Expand Down Expand Up @@ -75,7 +91,7 @@ class I18n
}
}

private static function fetch(el : DynamicObject<Dynamic>, rest : String) : String
private static function fetch(el : DynamicObject<Dynamic>, rest : String) : DynamicObject<Dynamic>
{
var pos : Int = rest.indexOf(depthDelimiter);
if (pos == -1) {
Expand All @@ -88,8 +104,6 @@ class I18n
}
return fetch(el.get(part), rest);
}

private static var trads : DynamicObject<Dynamic>;
}

abstract DynamicObject<T>(Dynamic<T>) from Dynamic<T>
Expand All @@ -99,13 +113,11 @@ abstract DynamicObject<T>(Dynamic<T>) from Dynamic<T>
this = {};
}

@:arrayAccess
public inline function set(key : String, value : T) : Void
{
Reflect.setField(this, key, value);
}

@:arrayAccess
public inline function get(key : String) : Null<T>
{
return Reflect.field(this, key);
Expand Down
13 changes: 11 additions & 2 deletions tests/ConfigTestCase.hx
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,32 @@ class ConfigTestCase extends TestCase
"side": "Some useful side notes to shine in society."
}
},
"tweets": {
"list": { "0": "Nothing to display.", "1": "Only one new item.", "n": "@n new items." }
},
"secret": {
"intro": "It\'s a secret page! Do you have authorization?"
},
"title": "jsoni18n tests"
}';
I18n.depthDelimiter = "|";
I18n.varPrefix = "@";
I18n.pluralizationVar = "n";
I18n.loadFromString(json);
}

public function testDepthDelimiter() : Void
{
assertEquals(I18n.tr("welcome|hello"), "Hoy!");
assertEquals("Hoy!", I18n.tr("welcome|hello"));
}

public function testVarPrefix() : Void
{
assertEquals(I18n.tr("welcome|subtitle", [ "name" => "Nekith" ]), "Welcome, Nekith!");
assertEquals("Welcome, Nekith!", I18n.tr("welcome|subtitle", [ "name" => "Nekith" ]));
}

public function testPluralizationVar() : Void
{
assertEquals("7 new items.", I18n.tr("tweets|list", [ "n" => 7 ]));
}
}
25 changes: 23 additions & 2 deletions tests/TrTestCase.hx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ class TrTestCase extends TestCase
"side": "Some useful side notes to shine in society."
}
},
"news": {
"list": { "0": "Nothing to display.", "1": "Only one new item.", "_": ":_ new items." }
},
"title": "jsoni18n tests"
}';
I18n.depthDelimiter = "/";
I18n.varPrefix = ":";
I18n.pluralizationVar = "_";
I18n.loadFromString(json);
}

Expand All @@ -35,21 +39,38 @@ class TrTestCase extends TestCase
assertEquals("brouzoufs", I18n.tr("brouzoufs"));
}

public function testDepth() : Void
{
assertEquals("Hoy!", I18n.tr("welcome/hello"));
}

public function testWrongDepth() : Void
{
assertEquals("title/lol", I18n.tr("title/lol"));
}

public function testDepth() : Void
public function testWrongType() : Void
{
assertEquals("Hoy!", I18n.tr("welcome/hello"));
assertEquals("welcome/content", I18n.tr("welcome/content"));
}

public function testVar() : Void
{
assertEquals("Welcome, Nekith!", I18n.tr("welcome/subtitle", [ "name" => "Nekith" ]));
}

public function testPlur() : Void
{
assertEquals("27 new items.", I18n.tr("news/list", [ "_" => 27 ]));
assertEquals("Nothing to display.", I18n.tr("news/list", [ "_" => 0 ]));
assertEquals("Only one new item.", I18n.tr("news/list", [ "_" => 1 ]));
}

public function testWrongTypePlur() : Void
{
assertEquals("news/list", I18n.tr("news/list"));
}

public function testUtf() : Void
{
assertEquals("Le contenu principal devrait être plus long, mais vous saisissez l\'idée.", I18n.tr("welcome/content/main"));
Expand Down

0 comments on commit 3aa7e9f

Please sign in to comment.