Merge pull request #5120 from jhass/i18n_js_fallback
Add fallback system to frontend I18n system
This commit is contained in:
commit
98c3a0a871
12 changed files with 77 additions and 43 deletions
|
|
@ -1,47 +1,70 @@
|
||||||
/* Copyright (c) 2010-2011, Diaspora Inc. This file is
|
|
||||||
* licensed under the Affero General Public License version 3 or later. See
|
|
||||||
* the COPYRIGHT file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Diaspora.I18n = {
|
Diaspora.I18n = {
|
||||||
language: "en",
|
language: "en",
|
||||||
locale: {},
|
locale: {
|
||||||
|
pluralizationKey: function(n) { return this.fallback.pluralizationKey(n); },
|
||||||
|
data: {},
|
||||||
|
fallback: {
|
||||||
|
pluralizationKey: function(n) { return n == 1 ? "one" : "other"; },
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
loadLocale: function(locale, language) {
|
load: function(locale, language, fallbackLocale) {
|
||||||
this.locale = $.extend(this.locale, locale);
|
this.updateLocale(this.locale, locale);
|
||||||
|
this.updateLocale(this.locale.fallback, fallbackLocale);
|
||||||
this.language = language;
|
this.language = language;
|
||||||
rule = this.t('pluralization_rule');
|
},
|
||||||
if (rule === "")
|
|
||||||
rule = 'function (n) { return n == 1 ? "one" : "other" }';
|
updateLocale: function(locale, data) {
|
||||||
eval("this.pluralizationKey = "+rule);
|
locale.data = $.extend(locale.data, data);
|
||||||
|
|
||||||
|
rule = this.resolve(locale, ['pluralization_rule']);
|
||||||
|
if (rule !== "") {
|
||||||
|
eval("locale.pluralizationKey = "+rule);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
t: function(item, views) {
|
t: function(item, views) {
|
||||||
var items = item.split("."),
|
var items = item.split(".");
|
||||||
translatedMessage,
|
return this.resolve(this.locale, items, views);
|
||||||
nextNamespace;
|
},
|
||||||
|
|
||||||
|
resolve: function(locale, items, views) {
|
||||||
|
var translatedMessage, nextNamespace, originalItems = items.slice();
|
||||||
|
|
||||||
if(views && typeof views.count !== "undefined") {
|
if(views && typeof views.count !== "undefined") {
|
||||||
items.push(this.pluralizationKey(views.count));
|
items.push(locale.pluralizationKey(views.count));
|
||||||
}
|
}
|
||||||
|
|
||||||
while(nextNamespace = items.shift()) {
|
while(nextNamespace = items.shift()) {
|
||||||
translatedMessage = (translatedMessage)
|
translatedMessage = (translatedMessage)
|
||||||
? translatedMessage[nextNamespace]
|
? translatedMessage[nextNamespace]
|
||||||
: this.locale[nextNamespace];
|
: locale.data[nextNamespace];
|
||||||
|
|
||||||
if(typeof translatedMessage === "undefined") {
|
if(typeof translatedMessage === "undefined") {
|
||||||
return "";
|
if (typeof locale.fallback === "undefined") {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return this.resolve(locale.fallback, originalItems, views);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.template(translatedMessage, views || {});
|
try {
|
||||||
|
return _.template(translatedMessage, views || {});
|
||||||
|
} catch (e) {
|
||||||
|
if (typeof locale.fallback === "undefined") {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return this.resolve(locale.fallback, originalItems, views);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
this.locale = {};
|
this.locale.data = {};
|
||||||
|
|
||||||
if( arguments.length > 0 && !(_.isEmpty(arguments[0])) )
|
if( arguments.length > 0 && !(_.isEmpty(arguments[0])) )
|
||||||
this.locale = arguments[0];
|
this.locale.data = arguments[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,9 @@ module LayoutHelper
|
||||||
def load_javascript_locales(section = 'javascripts')
|
def load_javascript_locales(section = 'javascripts')
|
||||||
content_tag(:script) do
|
content_tag(:script) do
|
||||||
<<-JS.html_safe
|
<<-JS.html_safe
|
||||||
Diaspora.I18n.loadLocale(#{get_javascript_strings_for(I18n.locale, section).to_json}, "#{I18n.locale}");
|
Diaspora.I18n.load(#{get_javascript_strings_for(I18n.locale, section).to_json},
|
||||||
|
"#{I18n.locale}",
|
||||||
|
#{get_javascript_strings_for(DEFAULT_LANGUAGE, section).to_json});
|
||||||
Diaspora.Page = "#{params[:controller].camelcase}#{params[:action].camelcase}";
|
Diaspora.Page = "#{params[:controller].camelcase}#{params[:action].camelcase}";
|
||||||
JS
|
JS
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
describe("app.collections.Aspects", function(){
|
describe("app.collections.Aspects", function(){
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
Diaspora.I18n.loadLocale({
|
Diaspora.I18n.load({
|
||||||
'and' : "and",
|
'and' : "and",
|
||||||
'comma' : ",",
|
'comma' : ",",
|
||||||
'my_aspects' : "My Aspects"
|
'my_aspects' : "My Aspects"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
describe("app.views.AspectsList", function(){
|
describe("app.views.AspectsList", function(){
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
setFixtures('<ul id="aspects_list"></ul>');
|
setFixtures('<ul id="aspects_list"></ul>');
|
||||||
Diaspora.I18n.loadLocale({ aspect_navigation : {
|
Diaspora.I18n.load({ aspect_navigation : {
|
||||||
'select_all' : 'Select all',
|
'select_all' : 'Select all',
|
||||||
'deselect_all' : 'Deselect all'
|
'deselect_all' : 'Deselect all'
|
||||||
}});
|
}});
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ describe("app.views.CommentStream", function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't add the comment to the view, when the request fails", function(){
|
it("doesn't add the comment to the view, when the request fails", function(){
|
||||||
Diaspora.I18n.loadLocale({failed_to_post_message: "posting failed!"});
|
Diaspora.I18n.load({failed_to_post_message: "posting failed!"});
|
||||||
this.request.response({status: 500});
|
this.request.response({status: 500});
|
||||||
|
|
||||||
expect(this.view.$(".comment-content p").text()).not.toEqual("a new comment");
|
expect(this.view.$(".comment-content p").text()).not.toEqual("a new comment");
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ describe("app.views.Feedback", function(){
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
loginAs({id : -1, name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
loginAs({id : -1, name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
||||||
|
|
||||||
Diaspora.I18n.loadLocale({stream : {
|
Diaspora.I18n.load({stream : {
|
||||||
'like' : "Like",
|
'like' : "Like",
|
||||||
'unlike' : "Unlike",
|
'unlike' : "Unlike",
|
||||||
'public' : "Public",
|
'public' : "Public",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@ describe("app.views.LikesInfo", function(){
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
loginAs({id : -1, name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
loginAs({id : -1, name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
||||||
|
|
||||||
Diaspora.I18n.loadLocale({stream : {
|
Diaspora.I18n.load({stream : {
|
||||||
pins : {
|
pins : {
|
||||||
zero : "<%= count %> Pins",
|
zero : "<%= count %> Pins",
|
||||||
one : "<%= count %> Pin"}
|
one : "<%= count %> Pin"}
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,7 @@ describe("app.views.Publisher", function() {
|
||||||
|
|
||||||
context('successful completion', function() {
|
context('successful completion', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
Diaspora.I18n.loadLocale({ photo_uploader: { completed: '<%= file %> completed' }});
|
Diaspora.I18n.load({ photo_uploader: { completed: '<%= file %> completed' }});
|
||||||
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');
|
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');
|
||||||
|
|
||||||
this.uploader.onComplete(null, 'test.jpg', {
|
this.uploader.onComplete(null, 'test.jpg', {
|
||||||
|
|
@ -469,7 +469,7 @@ describe("app.views.Publisher", function() {
|
||||||
|
|
||||||
context('unsuccessful completion', function() {
|
context('unsuccessful completion', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
Diaspora.I18n.loadLocale({ photo_uploader: { completed: '<%= file %> completed' }});
|
Diaspora.I18n.load({ photo_uploader: { completed: '<%= file %> completed' }});
|
||||||
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');
|
$('#photodropzone').html('<li class="publisher_photo loading"><img src="" /></li>');
|
||||||
|
|
||||||
this.uploader.onComplete(null, 'test.jpg', {
|
this.uploader.onComplete(null, 'test.jpg', {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ describe("app.views.StreamPost", function(){
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}});
|
||||||
|
|
||||||
Diaspora.I18n.loadLocale({stream : {
|
Diaspora.I18n.load({stream : {
|
||||||
reshares : {
|
reshares : {
|
||||||
one : "<%= count %> reshare",
|
one : "<%= count %> reshare",
|
||||||
other : "<%= count %> reshares"
|
other : "<%= count %> reshares"
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ beforeEach(function() {
|
||||||
var Page = Diaspora.Pages["TestPage"];
|
var Page = Diaspora.Pages["TestPage"];
|
||||||
$.extend(Page.prototype, Diaspora.EventBroker.extend(Diaspora.BaseWidget));
|
$.extend(Page.prototype, Diaspora.EventBroker.extend(Diaspora.BaseWidget));
|
||||||
|
|
||||||
Diaspora.I18n.loadLocale({}, 'en');
|
Diaspora.I18n.load({}, 'en', {});
|
||||||
|
|
||||||
Diaspora.page = new Page();
|
Diaspora.page = new Page();
|
||||||
Diaspora.page.publish("page/ready", [$(document.body)]);
|
Diaspora.page.publish("page/ready", [$(document.body)]);
|
||||||
|
|
|
||||||
|
|
@ -24,27 +24,28 @@ describe("Diaspora.I18n", function() {
|
||||||
Diaspora.I18n.reset(); // run tests with clean locale
|
Diaspora.I18n.reset(); // run tests with clean locale
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("::loadLocale", function() {
|
describe("::load", function() {
|
||||||
it("sets the class's locale variable", function() {
|
it("sets the class's locale variable", function() {
|
||||||
Diaspora.I18n.loadLocale(locale);
|
Diaspora.I18n.load(locale, "en", locale);
|
||||||
|
|
||||||
expect(Diaspora.I18n.locale).toEqual(locale);
|
expect(Diaspora.I18n.locale.data).toEqual(locale);
|
||||||
|
expect(Diaspora.I18n.locale.fallback.data).toEqual(locale);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("extends the class's locale variable on multiple calls", function() {
|
it("extends the class's locale variable on multiple calls", function() {
|
||||||
var data = {another: 'section'},
|
var data = {another: 'section'},
|
||||||
extended = $.extend(locale, data);
|
extended = $.extend(locale, data);
|
||||||
|
|
||||||
Diaspora.I18n.loadLocale(locale);
|
Diaspora.I18n.load(locale, "en", locale);
|
||||||
Diaspora.I18n.loadLocale(data);
|
Diaspora.I18n.load(data, "en", data);
|
||||||
|
|
||||||
expect(Diaspora.I18n.locale).toEqual(extended);
|
expect(Diaspora.I18n.locale.data).toEqual(extended);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("::t", function() {
|
describe("::t", function() {
|
||||||
var translation;
|
var translation;
|
||||||
beforeEach(function() { Diaspora.I18n.loadLocale(locale); });
|
beforeEach(function() { Diaspora.I18n.load(locale, "en", {fallback: "fallback", namespace: {template: "no template"}}); });
|
||||||
|
|
||||||
it("returns the specified translation", function() {
|
it("returns the specified translation", function() {
|
||||||
translation = Diaspora.I18n.t("namespace.message");
|
translation = Diaspora.I18n.t("namespace.message");
|
||||||
|
|
@ -67,20 +68,28 @@ describe("Diaspora.I18n", function() {
|
||||||
it("returns an empty string if the translation is not found", function() {
|
it("returns an empty string if the translation is not found", function() {
|
||||||
expect(Diaspora.I18n.t("missing.locale")).toEqual("");
|
expect(Diaspora.I18n.t("missing.locale")).toEqual("");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("falls back on missing key", function() {
|
||||||
|
expect(Diaspora.I18n.t("fallback")).toEqual("fallback");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back on interpolation errors", function() {
|
||||||
|
expect(Diaspora.I18n.t("namespace.template")).toEqual("no template");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("::reset", function(){
|
describe("::reset", function(){
|
||||||
it("clears the current locale", function() {
|
it("clears the current locale", function() {
|
||||||
Diaspora.I18n.loadLocale(locale);
|
Diaspora.I18n.load(locale, "en", locale);
|
||||||
Diaspora.I18n.reset()
|
Diaspora.I18n.reset()
|
||||||
expect(Diaspora.I18n.locale).toEqual({});
|
expect(Diaspora.I18n.locale.data).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets the locale to only a specific value", function() {
|
it("sets the locale to only a specific value", function() {
|
||||||
var data = { some: 'value' };
|
var data = { some: 'value' };
|
||||||
Diaspora.I18n.loadLocale(locale);
|
Diaspora.I18n.load(locale, "en", locale);
|
||||||
Diaspora.I18n.reset(data);
|
Diaspora.I18n.reset(data);
|
||||||
expect(Diaspora.I18n.locale).toEqual(data);
|
expect(Diaspora.I18n.locale.data).toEqual(data);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue