Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #26 #28

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions app-localize-behavior.html
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,14 @@
if (!key || !resources || !language)
return;

var rawTranslation = proto._extractTranslation(resources, language, key);
// Cache the key/value pairs for the same language, so that we don't
// do extra work if we're just reusing strings across an application.
var messageKey = key + resources[language][key];
var messageKey = key + rawTranslation;
var msg = proto.__localizationCache.messages[messageKey];

if (!msg) {
msg = new IntlMessageFormat(resources[language][key], language, formats);
msg = new IntlMessageFormat(proto._extractTranslation(resources, language, key), language, formats);
proto.__localizationCache.messages[messageKey] = msg;
}

Expand All @@ -227,6 +228,16 @@
};
},

_extractTranslation: function (resources, language, key){
var resource = resources[language], keyChain = key.split('.');

keyChain.forEach(function (currentKey) {
resource = resource[currentKey];
});

return resource;
},

__onRequestResponse: function(event) {
this.resources = event.response;
this.fire('app-resources-loaded');
Expand Down
320 changes: 320 additions & 0 deletions test/basic-resource-tree.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
<!doctype html>
<!--
@license
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>

<title>app-localize-behavior tests</title>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes">

<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../web-component-tester/browser.js"></script>

<!-- Intl polyfill -->
<script src="../../intl/dist/Intl.min.js"></script>
<script src="../../intl/locale-data/jsonp/en.js"></script>

<link rel="import" href="./resource-tree/x-translate.html">
<link rel="import" href="./resource-tree/x-translate2.html">
<link rel="import" href="./resource-tree/x-translate-only-imperative.html">

</head>
<body>

<test-fixture id="basic">
<template>
<x-translate></x-translate>
</template>
</test-fixture>

<test-fixture id="basic2">
<template>
<x-translate></x-translate>
</template>
</test-fixture>

<test-fixture id="interpolated">
<template>
<x-translate2></x-translate2>
</template>
</test-fixture>

<test-fixture id="only-imperative">
<template>
<x-translate-only-imperative></x-translate-only-imperative>
</template>
</test-fixture>

<script>
function getRequestsCache(elem) {
return elem.constructor.prototype.__localizationCache.requests;
}

function getStringsCache(elem) {
return elem.constructor.prototype.__localizationCache.messages;
}

function resetRequestsCache(elem) {
elem.constructor.prototype.__localizationCache.requests = {};
}

function resetStringsCache(elem) {
elem.constructor.prototype.__localizationCache.messages = {};
}

suite('basic', function() {
test('can translate a basic string', function() {
var app = fixture('basic');

assert.equal(app.language, 'en');
assert.equal(app.$.output.innerHTML, 'hello');

app.language = 'fr';
assert.equal(app.language, 'fr');
assert.equal(app.$.output.innerHTML, 'bonjour');
});

test('can translate a string with a parameter', function() {
var app = fixture('interpolated');

assert.equal(app.language, 'en');
assert.equal(app.$.output.innerHTML, 'my name is batman. i have 3 cats.');

app.language = 'fr';
assert.equal(app.language, 'fr');
assert.equal(app.$.output.innerHTML, 'je m\'apelle batman. j\'ai 3 chats.');
});

test('can translate strings imperatively', function() {
var app1 = fixture('basic');
var app2 = fixture('interpolated');

assert.equal(app1.language, 'en');
assert.equal(app2.language, 'en');
assert.equal(app1.localize('my.greeting'), 'hello');
assert.equal(app2.localize('my.intro', 'name', 'robin', 'numCats', 17), 'my name is robin. i have 17 cats.');

app1.language = 'fr';
app2.language = 'fr';
assert.equal(app1.language, 'fr');
assert.equal(app2.language, 'fr');

assert.equal(app1.localize('my.greeting'), 'bonjour');
assert.equal(app2.localize('my.intro', 'name', 'robin', 'numCats', 17), 'je m\'apelle robin. j\'ai 17 chats.');
});
});

suite('external resources', function() {
var path;

test('can translate a basic string', function(done) {
var app = fixture('basic');

// Keep the tests independent by resetting the internal cache.
resetRequestsCache(app);

app.addEventListener('app-resources-loaded', function() {
assert.equal(app.language, 'en');
assert.equal(app.$.output.innerHTML, 'hello');

app.language = 'fr';
assert.equal(app.language, 'fr');
assert.equal(app.$.output.innerHTML, 'bonjour');

done();
});

app.loadResources(app.resolveUrl('./locales.json'));
});

test('can translate a string with a parameter', function(done) {
var app = fixture('interpolated');

// Keep the tests independent by resetting the internal cache.
resetRequestsCache(app);

app.addEventListener('app-resources-loaded', function() {
assert.equal(app.language, 'en');
assert.equal(app.$.output.innerHTML, 'my name is batman. i have 3 cats.');

app.language = 'fr';
assert.equal(app.language, 'fr');
assert.equal(app.$.output.innerHTML, 'je m\'apelle batman. j\'ai 3 chats.');

done();
});

app.loadResources(app.resolveUrl('./locales.json'));
});

test('can translate strings imperatively', function(done) {
var app1 = fixture('basic');
var app2 = fixture('interpolated');

// Keep the tests independent by resetting the internal cache.
resetRequestsCache(app1);
resetRequestsCache(app2);

app1.addEventListener('app-resources-loaded', function() {
assert.equal(app1.language, 'en');
assert.equal(app2.language, 'en');
assert.equal(app1.localize('my.greeting'), 'hello');
assert.equal(app2.localize('my.intro', 'name', 'robin', 'numCats', 17), 'my name is robin. i have 17 cats.');

app1.language = app2.language = 'fr';
assert.equal(app1.language, 'fr');
assert.equal(app2.language, 'fr');
assert.equal(app1.localize('my.greeting'), 'bonjour');
assert.equal(app2.localize('my.intro', 'name', 'robin', 'numCats', 17), 'je m\'apelle robin. j\'ai 17 chats.');

done();
});

app1.loadResources(app1.resolveUrl('./locales.json'));
app2.loadResources(app2.resolveUrl('./locales.json'));
});
});

suite('json loading and caching', function() {
test('loads the same json only file once', function(done) {
// TL;DR: this test makes sure that objects of the same type, and of
// different types play together in the same, nice, way.
var app1 = fixture('basic');
var app2 = fixture('basic2'); // different type.
var app3 = fixture('interpolated');
var path = app1.resolveUrl('./locales.json');

// Keep the tests independent by resetting the internal cache.
resetRequestsCache(app1);
resetRequestsCache(app2);
resetRequestsCache(app3);

// Nothing in the cache.
assert.equal(0, Object.keys(getRequestsCache(app1)).length);
assert.equal(0, Object.keys(getRequestsCache(app2)).length);
assert.equal(0, Object.keys(getRequestsCache(app3)).length);

// Once the first file has been loaded, it should be the only thing in the cache.
app1.addEventListener('app-resources-loaded', function() {
assert.equal(1, Object.keys(getRequestsCache(app1)).length, 'there is 1 request cached in app1');
assert.equal(1, Object.keys(getRequestsCache(app2)).length, 'there is 1 request cached in app2');
assert.equal(1, Object.keys(getRequestsCache(app3)).length, 'there is 1 request cached in app3');

var request1 = getRequestsCache(app1)[path];
assert.notEqual(undefined, request1, 'the cached request is ok');
assert.equal(app1.resources['fr']['my']['greeting'], 'bonjour');

// Loading the second file should not make an iron-ajax request, and re-use the one in the cache.
app2.addEventListener('app-resources-loaded', function() {
assert.equal(1, Object.keys(getRequestsCache(app1)).length, 'there is 1 request cached in app1');
assert.equal(1, Object.keys(getRequestsCache(app2)).length, 'there is 1 request cached in app2');
assert.equal(1, Object.keys(getRequestsCache(app3)).length, 'there is 1 request cached in app3');

assert.equal(request1, getRequestsCache(app2)[path], 'the same request is cached');
expect(app1.resources).eql(app2.resources);
assert.equal(app2.resources['fr']['my']['greeting'], 'bonjour');

// Loading the third file should not make an iron-ajax request, and re-use the one in the cache.
app3.addEventListener('app-resources-loaded', function() {
assert.equal(1, Object.keys(getRequestsCache(app1)).length, 'there is 1 request cached in app1');
assert.equal(1, Object.keys(getRequestsCache(app2)).length, 'there is 1 request cached in app2');
assert.equal(1, Object.keys(getRequestsCache(app3)).length, 'there is 1 request cached in app3');

assert.equal(request1, getRequestsCache(app3)[path], 'the same request is cached');
expect(app1.resources).eql(app2.resources);
expect(app1.resources).eql(app3.resources);
assert.equal(app2.resources['fr']['my']['greeting'], 'bonjour');

done();
});
app3.loadResources(path);
});
app2.loadResources(path);
});
app1.loadResources(path);
});

test('can load different json files', function(done) {
var app1 = fixture('basic');
var app2 = fixture('interpolated');

// Keep the tests independent by resetting the internal cache.
resetRequestsCache(app1);
resetRequestsCache(app2);

// Nothing in the cache.
assert.equal(0, Object.keys(getRequestsCache(app1)).length);
assert.equal(0, Object.keys(getRequestsCache(app2)).length);

var path1 = app1.resolveUrl('./locales.json');
var path2 = app1.resolveUrl('./locales2.json');

// Once the first file has been loaded, it should be the only thing in the cache.
app1.addEventListener('app-resources-loaded', function() {
assert.equal(1, Object.keys(getRequestsCache(app1)).length, 'there is 1 request cached in app1');
assert.equal(1, Object.keys(getRequestsCache(app2)).length, 'there is 1 request cached in app2');

var request1 = getRequestsCache(app1)[path1];
assert.notEqual(undefined, request1, 'the cached request is ok');
assert.equal(app1.resources['fr']['my']['greeting'], 'bonjour');

// Loading a different file should make a different ajax request.
app2.addEventListener('app-resources-loaded', function() {
assert.equal(2, Object.keys(getRequestsCache(app1)).length, 'there are 2 requests cached in app1');
assert.equal(2, Object.keys(getRequestsCache(app2)).length, 'there are 2 requests cached in app2');

var request2 = getRequestsCache(app2)[path2];
assert.notEqual(request1, request2, 'the cached requests are different');
expect(app1.resources).to.not.eql(app2.resources, 'the apps don\'t have the same resources');
assert.equal(app2.resources['fr']['my']['greeting'], 'bonjour!');

done();
});
app2.loadResources(app2.resolveUrl(path2));
});
app1.loadResources(app1.resolveUrl(path1));
});
});

suite('localized string caching', function() {
test('constructs the same string only once', function() {
var app = fixture('only-imperative');

// Keep the tests independent by resetting the internal cache.
resetStringsCache(app);

// Translating one string should add it to the cache.
assert.equal(app.localize('my.greeting'), 'hello');
assert.equal(1, Object.keys(getStringsCache(app)).length, 'there is 1 string cached');
var cachedString = getStringsCache(app)['my.greetinghello'];
assert.isNotNull(cachedString, 'cached string has an object');

// Translating the same string again should re-use it from the cache.
assert.equal(app.localize('my.greeting'), 'hello');
assert.equal(1, Object.keys(getStringsCache(app)).length, 'there is still 1 string cached');
assert.equal(cachedString, getStringsCache(app)['my.greetinghello'], 'cached string is the same');

// Changing the language should reset the cache.
app.language = 'fr';
assert.equal(0, Object.keys(getStringsCache(app)).length, 'the cache is empty');

// But translating a new string will re-add it to the cache.
assert.equal(app.localize('my.greeting'), 'bonjour');
assert.equal(1, Object.keys(getStringsCache(app)).length, 'there is 1 string cached');
var newCachedString = getStringsCache(app)['my.greetinghello'];
assert.notEqual(cachedString, newCachedString, 'cached string is different than before');
});
});
</script>
</body>
</html>
4 changes: 3 additions & 1 deletion test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
<script>
WCT.loadSuites([
'basic.html',
'basic.html?dom=shadow'
'basic.html?dom=shadow',
'basic-resource-tree.html',
'basic-resource-tree.html?dom=shadow'
]);
</script>
</body>
Expand Down
14 changes: 14 additions & 0 deletions test/resource-tree/locales.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"en": {
"my": {
"greeting": "hello",
"intro": "my name is {name}. i have {numCats, number} cats."
}
},
"fr": {
"my": {
"greeting": "bonjour",
"intro": "je m'apelle {name}. j'ai {numCats, number} chats."
}
}
}
14 changes: 14 additions & 0 deletions test/resource-tree/locales2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"en": {
"my": {
"greeting": "hello!",
"intro": "my name is {name}! i have {numCats, number} cats!"
}
},
"fr": {
"my": {
"greeting": "bonjour!",
"intro": "je m'apelle {name}! j'ai {numCats, number} chats!"
}
}
}
Loading