Skip to content

Commit

Permalink
Merge pull request #2 from chris-rudmin/master
Browse files Browse the repository at this point in the history
merging chris-rudmin's support for custom plural method (options.getSuffixMethod)
  • Loading branch information
jpjoyal committed May 24, 2012
2 parents 3fdd949 + 8b6aa16 commit d5f7083
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 111 deletions.
108 changes: 63 additions & 45 deletions README.markdown
Expand Up @@ -15,33 +15,33 @@ Depends on jQuery 1.3.2+ (uses $.ajax, $.each, $.extend)
Usage example
=============

$.jsperanto.init(function(t){
t('project.name'); //-> "jsperanto"
$.t('project.store'); //-> "JSON"
$.t('can_speak',{count:1}); //-> "I can only speak one language"
$.t('can_speak',{count:3}); //-> "I can speak 3 languages"
$.t('can_speak_plural',{count:'any'}); //-> "I can speak any languages"
$.t('project.size.source',{value:4,unit:"kb"}); //-> "jsperanto is 4 kb"
$.t('project.size.min',{value:1727,unit:"bytes"}) //-> "jsperanto is 1727 bytes when minified"
$.t('project.size.gzip',{value:833,unit:"bytes"}) //-> "jsperanto is 833 bytes when minified and gzipped"
});

//given this dictionary
{
"project" : {
"name" : "jsperanto",
"store" : "JSON",
"size" : {
"source" : "$t(project.name) is __value__ __unit__",
"min" : "$t(project.size.source) when minified",
"gzip" : "$t(project.size.min) and gzipped"
}
},
"can_speak" : "I can only speak one language",
"can_speak_plural" : "I can speak __count__ languages"
}
$.jsperanto.init(function(t){
t('project.name'); //-> "jsperanto"
$.t('project.store'); //-> "JSON"
$.t('can_speak',{count:1}); //-> "I can only speak one language"
$.t('can_speak',{count:3}); //-> "I can speak 3 languages"
$.t('can_speak_plural',{count:'any'}); //-> "I can speak any languages"
$.t('project.size.source',{value:4,unit:"kb"}); //-> "jsperanto is 4 kb"
$.t('project.size.min',{value:1727,unit:"bytes"}) //-> "jsperanto is 1727 bytes when minified"
$.t('project.size.gzip',{value:833,unit:"bytes"}) //-> "jsperanto is 833 bytes when minified and gzipped"
});

//given this dictionary
{
"project" : {
"name" : "jsperanto",
"store" : "JSON",
"size" : {
"source" : "$t(project.name) is __value__ __unit__",
"min" : "$t(project.size.source) when minified",
"gzip" : "$t(project.size.min) and gzipped"
}
},
"can_speak" : "I can only speak one language",
"can_speak_plural" : "I can speak __count__ languages"
}

API
===
Expand All @@ -54,22 +54,23 @@ initialize jsperanto by loading the dictionary, calling back when ready

**options** extends these defaults

o.interpolationPrefix = '__';
o.interpolationSuffix = '__';
o.pluralSuffix = "_plural";
o.maxRecursion = 50; //used while applying reuse of strings to avoid infinite loop
o.reusePrefix = "$t("; //nested lookup prefix
o.reuseSuffix = ")"; //nested lookup suffix
o.fallbackLang = 'en-US'; // see Language fallback section
o.dicoPath = 'locales'; // see Dictionary section
o.keyseparator = "."; // keys passed to $.jsperanto.translate use this separator
o.setDollarT = true; // $.t aliases $.jsperanto.translate, nice shortcut
o.dictionary = false; // to supply the dictionary instead of loading it using $.ajax. A (big) javascript object containing your namespaced translations
o.lang = false; //specify a language to use i.e en-US
o.interpolationPrefix = '__';
o.interpolationSuffix = '__';
o.pluralSuffix = "_plural";
o.getSuffixMethod = function(count){ return ( count > 1 || typeof(count) == "string" ) ? o.pluralSuffix : ""; };
o.maxRecursion = 50; //used while applying reuse of strings to avoid infinite loop
o.reusePrefix = "$t("; //nested lookup prefix
o.reuseSuffix = ")"; //nested lookup suffix
o.fallbackLang = 'en-US'; // see Language fallback section
o.dicoPath = 'locales'; // see Dictionary section
o.keyseparator = "."; // keys passed to $.jsperanto.translate use this separator
o.setDollarT = true; // $.t aliases $.jsperanto.translate, nice shortcut
o.dictionary = false; // to supply the dictionary instead of loading it using $.ajax. A (big) javascript object containing your namespaced translations
o.lang = false; //specify a language to use i.e en-US

Use init to switch language too :

$.jsperanto.init(someMethod,{lang:"fr"})
$.jsperanto.init(someMethod,{lang:"fr"})

**$.jsperanto.translate(key,options)**

Expand All @@ -90,7 +91,7 @@ Dictionary loading

Using defaults, jsperanto uses a basic browser language detection

(navigator.language) ? navigator.language : navigator.userLanguage
(navigator.language) ? navigator.language : navigator.userLanguage

to determine what dictionary file to load. You can also instruct jsperanto to load a specific language (via init option _lang_).

Expand All @@ -103,7 +104,25 @@ Switching language

Simply use init again and specify a language (or dictionary) to use.

$.jsperanto.init(someMethod,{lang:"fr"})
$.jsperanto.init(someMethod,{lang:"fr"})

Custom suffixes
==================

At init time you can specify a method which will calculate the suffix to use if count is present. the argument passed to this method is the count and can be a string or number.
Anything other than a string returned will be disregarded.

$.jsperanto.init(someMethod, {
lang:"en-us",
getSuffixMethod : function(count){
if ( count == 0 ) {
return "_zero";
}
if ( count != 1 ) {
return "_plural";
}
}
)

Licence
=======
Expand Down Expand Up @@ -140,11 +159,10 @@ Inspiration & similar projects

[l10n.js](http://github.com/eligrey/l10n.js)

[javascript_i18n](http://github.com/qoobaa/javascript_i18n)
[javascript_i18n](http://github.com/qoobaa/javascript_i18n)


Thanks
======

Many thanks to Radialpoint for letting me open source this work

Many thanks to Radialpoint for letting me open source this work
38 changes: 23 additions & 15 deletions jquery.jsperanto.js
@@ -1,11 +1,14 @@
//jquery 1.3.2 dependencies : $.each, $.extend, $.ajax

(function($) {

//defaults

var o = {};
o.interpolationPrefix = '__';
o.interpolationSuffix = '__';
o.pluralSuffix = "_plural";
o.pluralSuffix = '_plural';
o.getSuffixMethod = function(count){ return ( count > 1 || typeof(count) == "string" ) ? o.pluralSuffix : ""; };
o.maxRecursion = 50; //used while applying reuse of strings to avoid infinite loop
o.reusePrefix = "$t(";
o.reuseSuffix = ")";
Expand All @@ -15,11 +18,12 @@
o.setDollarT = true; // $.t aliases $.jsperanto.translate, nice shortcut
o.dictionary = false; // to supply the dictionary instead of loading it using $.ajax. A (big) javascript object containing your namespaced translations
o.lang = false; //specify a language to use
o.pluralNotFound = ["plural_not_found_", Math.random()].join(''); // used internally by translate
o.suffixNotFound = ["suffix_not_found_", Math.random()].join(''); // used internally by translate

var dictionary = false; //not yet loaded
var currentLang = false;
var count_of_replacement = 0;


function init(callback,options){
$.extend(o,options);
Expand All @@ -30,7 +34,7 @@
callback(translate);
});
}

function applyReplacement(string,replacementHash){
$.each(replacementHash,function(key,value){
string = string.replace([o.interpolationPrefix,key,o.interpolationSuffix].join(''),value);
Expand Down Expand Up @@ -59,17 +63,21 @@
return o.fallbackLang;
}
}
function needsPlural(options){
return (options.count && typeof options.count != 'string' && options.count > 1);

function containsCount(options){
return (typeof options.count == 'number' || typeof options.count == 'string');
}


function getCountSuffix(options) {
var suffix = o.getSuffixMethod(options.count);
return ( typeof(suffix) == "string" ) ? suffix : '';
}

function translate(dottedkey,options){
count_of_replacement = 0;
return _translate(dottedkey,options);
}

/*
options.defaultValue
options.count
Expand All @@ -78,14 +86,14 @@
options = options || {};
var notfound = options.defaultValue || dottedkey;
if(!dictionary){return notfound;} // No dictionary to translate from
if(needsPlural(options)){

if(containsCount(options)){
var optionsSansCount = $.extend({},options);
delete optionsSansCount.count;
optionsSansCount.defaultValue = o.pluralNotFound;
var pluralKey = dottedkey + o.pluralSuffix;
var translated = translate(pluralKey,optionsSansCount);
if(translated != o.pluralNotFound){
optionsSansCount.defaultValue = o.suffixNotFound;
var suffixKey = dottedkey + getCountSuffix(options);
var translated = translate(suffixKey,optionsSansCount);
if(translated != o.suffixNotFound){
return applyReplacement(translated,{count:options.count});//apply replacement for count only
}// else continue translation with original/singular key
}
Expand Down Expand Up @@ -132,7 +140,7 @@
function lang(){
return currentLang;
}

$.jsperanto = $.jsperanto || {
init:init,
t:translate,
Expand Down
76 changes: 40 additions & 36 deletions test/locales/testlang.json
@@ -1,38 +1,42 @@
{
"sections" : {
"home" : "Home",
"files" : "My Files"
},
"product" : {
"name" : "jsperanto"
},
"withHTML" : "<b>this would be bold</b>",
"withreuse" : "$t(product.name) and $t(sections.home)",
"withreplacement" : "since __year__",
"4" : {
"level" : {
"of" : {
"nesting": "4 level of nesting"
}
}
},
"pluralversionexist" : "singular version of pluralversionexist",
"pluralversionexist_plural" : "plural version of pluralversionexist",
"pluralversiondoesnotexist" : "plural version does not exist",
"count and replacement" : "you have __count__ friend",
"count and replacement_plural" : "you have __count__ friends",

"infinite" : "infinite $t(recursion)",
"recursion" : "recursion $t(infinite)",
"project" : {
"name" : "jsperanto",
"store" : "JSON",
"size" : {
"source" : "$t(project.name) is __value__ __unit__",
"min" : "$t(project.size.source) when minified",
"gzip" : "$t(project.size.min) and gzipped"
}
},
"can_speak" : "I can only speak one language",
"can_speak_plural" : "I can speak __count__ languages"
"sections" : {
"home" : "Home",
"files" : "My Files"
},
"product" : {
"name" : "jsperanto"
},
"withHTML" : "<b>this would be bold</b>",
"withreuse" : "$t(product.name) and $t(sections.home)",
"withreplacement" : "since __year__",
"4" : {
"level" : {
"of" : {
"nesting": "4 level of nesting"
}
}
},
"pluralversionexist" : "singular version of pluralversionexist",
"pluralversionexist_plural" : "plural version of pluralversionexist",
"pluralversiondoesnotexist" : "plural version does not exist",
"count and replacement" : "you have __count__ friend",
"count and replacement_plural" : "you have __count__ friends",

"infinite" : "infinite $t(recursion)",
"recursion" : "recursion $t(infinite)",
"project" : {
"name" : "jsperanto",
"store" : "JSON",
"size" : {
"source" : "$t(project.name) is __value__ __unit__",
"min" : "$t(project.size.source) when minified",
"gzip" : "$t(project.size.min) and gzipped"
}
},
"can_speak" : "I can only speak one language",
"can_speak_plural" : "I can speak __count__ languages",

"countCustomSuffix" : "I have exactly one. The count is __count__",
"countCustomSuffix_zero" : "I have exactly zero. The count is __count__",
"countCustomSuffix_plural" : "I have many. The count is __count__"
}
50 changes: 35 additions & 15 deletions test/test.js
@@ -1,8 +1,8 @@
asyncTest("translation", function() {
var o = {
lang : 'testlang'
};
$.jsperanto.init(function(t){
lang : 'testlang'
};
$.jsperanto.init(function(t){
equals(t('product.name'),"jsperanto");
equals(t('withreuse'),"jsperanto and Home");
equals(t('withreplacement',{year:2005}),"since 2005");
Expand All @@ -19,19 +19,39 @@ asyncTest("translation", function() {
equals(t('count and replacement',{count:3}),"you have 3 friends");


equals($.t('can_speak_plural',{count:'any'}),"I can speak any languages","count can be a string");
equals($.t('project.size.source',{value:4,unit:"kb"}),"jsperanto is 4 kb","Interpolation variables can be a number");
equals($.t('project.size.min',{value:1010,unit:"bytes"}),"jsperanto is 1010 bytes when minified","options are also used for nested lookup ");
equals($.t('project.size.gzip',{value:505,unit:"bytes"}),"jsperanto is 505 bytes when minified and gzipped","options are also used for nested lookup");

equals($.t('can_speak_plural',{count:'any'}),"I can speak any languages","count can be a string");

equals($.t('project.size.source',{value:4,unit:"kb"}),"jsperanto is 4 kb","Interpolation variables can be a number");
equals($.t('project.size.min',{value:1010,unit:"bytes"}),"jsperanto is 1010 bytes when minified","options are also used for nested lookup ");
equals($.t('project.size.gzip',{value:505,unit:"bytes"}),"jsperanto is 505 bytes when minified and gzipped","options are also used for nested lookup");

equals(t('not.existing.key'),"not.existing.key");

t('infinite');
equals(true,true,"recursive nested lookup should not crash");

start();

t('infinite');
equals(true,true,"recursive nested lookup should not crash");

},o);

o.getSuffixMethod = function(count){
if ( count == 0 ) {
return "_zero";
}
if ( count != 1 ) {
return "_plural";
}
};
$.jsperanto.init(function(t){
equals(t('countCustomSuffix',{count:0}),"I have exactly zero. The count is 0");
equals(t('countCustomSuffix',{count:0.5}),"I have many. The count is 0.5");
equals(t('countCustomSuffix',{count:1}),"I have exactly one. The count is 1");
equals(t('countCustomSuffix',{count:3}),"I have many. The count is 3");
equals(t('countCustomSuffix',{count:-1}),"I have many. The count is -1");
equals(t('countCustomSuffix',{count:"0"}),"I have exactly zero. The count is 0");
equals(t('countCustomSuffix',{count:"string"}),"I have many. The count is string");
equals(t('countCustomSuffix',{count:"1"}),"I have exactly one. The count is 1");
equals(t('countCustomSuffix',{count:"01"}),"I have exactly one. The count is 01");
start();
},o);
});

Expand Down

0 comments on commit d5f7083

Please sign in to comment.