Skip to content

Completely remove custom inlined sprintf implementation #47

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

Closed
wants to merge 2 commits into from
Closed
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
182 changes: 1 addition & 181 deletions jed.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ in order to offer easy upgrades -- jsgettext.berlios.de
if ( {}.toString.call( sArr ) != '[object Array]' ) {
sArr = [].slice.call(arguments, 0);
}
return ( sArr && sArr.length ? Jed.sprintf : function(x){ return x; } )(
return ( function(x){ return x; } )(
this._i18n.dcnpgettext(this._domain, this._context, this._key, this._pkey, this._val),
sArr
);
Expand Down Expand Up @@ -321,191 +321,11 @@ in order to offer easy upgrades -- jsgettext.berlios.de
}
});


// We add in sprintf capabilities for post translation value interolation
// This is not internally used, so you can remove it if you have this
// available somewhere else, or want to use a different system.

// We _slightly_ modify the normal sprintf behavior to more gracefully handle
// undefined values.

/**
sprintf() for JavaScript 0.7-beta1
http://www.diveintojavascript.com/projects/javascript-sprintf

Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of sprintf() for JavaScript nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var sprintf = (function() {
function get_type(variable) {
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
}
function str_repeat(input, multiplier) {
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
return output.join('');
}

var str_format = function() {
if (!str_format.cache.hasOwnProperty(arguments[0])) {
str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
}
return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
};

str_format.format = function(parse_tree, argv) {
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
for (i = 0; i < tree_length; i++) {
node_type = get_type(parse_tree[i]);
if (node_type === 'string') {
output.push(parse_tree[i]);
}
else if (node_type === 'array') {
match = parse_tree[i]; // convenience purposes only
if (match[2]) { // keyword argument
arg = argv[cursor];
for (k = 0; k < match[2].length; k++) {
if (!arg.hasOwnProperty(match[2][k])) {
throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
}
arg = arg[match[2][k]];
}
}
else if (match[1]) { // positional argument (explicit)
arg = argv[match[1]];
}
else { // positional argument (implicit)
arg = argv[cursor++];
}

if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
}

// Jed EDIT
if ( typeof arg == 'undefined' || arg === null ) {
arg = '';
}
// Jed EDIT

switch (match[8]) {
case 'b': arg = arg.toString(2); break;
case 'c': arg = String.fromCharCode(arg); break;
case 'd': arg = parseInt(arg, 10); break;
case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
case 'o': arg = arg.toString(8); break;
case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
case 'u': arg = Math.abs(arg); break;
case 'x': arg = arg.toString(16); break;
case 'X': arg = arg.toString(16).toUpperCase(); break;
}
arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
pad_length = match[6] - String(arg).length;
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
output.push(match[5] ? arg + pad : pad + arg);
}
}
return output.join('');
};

str_format.cache = {};

str_format.parse = function(fmt) {
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
while (_fmt) {
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
parse_tree.push(match[0]);
}
else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
parse_tree.push('%');
}
else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
if (match[2]) {
arg_names |= 1;
var field_list = [], replacement_field = match[2], field_match = [];
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else {
throw('[sprintf] huh?');
}
}
}
else {
throw('[sprintf] huh?');
}
match[2] = field_list;
}
else {
arg_names |= 2;
}
if (arg_names === 3) {
throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
}
parse_tree.push(match);
}
else {
throw('[sprintf] huh?');
}
_fmt = _fmt.substring(match[0].length);
}
return parse_tree;
};

return str_format;
})();

var vsprintf = function(fmt, argv) {
argv.unshift(fmt);
return sprintf.apply(null, argv);
};

Jed.parse_plural = function ( plural_forms, n ) {
plural_forms = plural_forms.replace(/n/g, n);
return Jed.parse_expression(plural_forms);
};

Jed.sprintf = function ( fmt, args ) {
if ( {}.toString.call( args ) == '[object Array]' ) {
return vsprintf( fmt, [].slice.call(args) );
}
return sprintf.apply(this, [].slice.call(arguments) );
};

Jed.prototype.sprintf = function () {
return Jed.sprintf.apply(this, arguments);
};
// END sprintf Implementation

// Start the Plural forms section
// This is a full plural form expression parser. It is used to avoid
// running 'eval' or 'new Function' directly against the plural
Expand Down
85 changes: 0 additions & 85 deletions test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -652,94 +652,9 @@
expect( i18n.translate("test plural %1$d").ifPlural(5, "dont matta").fetch() ).to.be( "test_1_plural %1$d" );
});

it("should take in a sprintf set of args (as array) on the plural lookup", function(){
expect( i18n.translate("test plural %1$d").ifPlural(5, "dont matta").fetch([5]) ).to.be( "test_1_plural 5" );
expect( i18n.translate("test plural %1$d %2$d").ifPlural(5, "dont matta %1$d %2$d").fetch([5, 6]) ).to.be( "dont matta 5 6" );
expect( i18n.translate("test plural %1$d %2$d").ifPlural(1, "dont matta %1$d %2$d").fetch([1, 6]) ).to.be( "test plural 1 6" );
});

it("should take in a sprintf set of args (as args) on the plural lookup", function(){
expect( i18n.translate("test plural %1$d %2$d").ifPlural(5, "dont matta %1$d %2$d").fetch(5, 6) ).to.be( "dont matta 5 6" );
expect( i18n.translate("test plural %1$d %2$d").ifPlural(1, "dont matta %1$d %2$d").fetch(1, 6) ).to.be( "test plural 1 6" );
});

it("should handle context information.", function () {
expect(i18n.translate('test context').withContext('context').fetch() ).to.be('test_1context');
});

it("should be able to do all at the same time.", function () {
expect( i18n.translate("context other plural %1$d").withContext('context').onDomain('other_domain').ifPlural(5, "ignored %1$d").fetch(5) ).to.be( "context_plural_1 plural 5" );
expect( i18n.translate("context other plural %1$d").withContext('context').onDomain('other_domain').ifPlural(1, "ignored %1$d").fetch(1) ).to.be( "context_plural_1 singular 1" );
});

});

describe("Sprintf", function () {
var locale_data_w_context = {
"context_sprintf_test": {
"": {
"domain": "context_sprintf_test",
"lang": "en",
"plural-forms": "nplurals=2; plural=(n != 1);"
},
"test singular": ["test_1"],
"test plural %1$d": ["test_1_singular %1$d", "test_1_plural %1$d"],
"context\u0004test context": ["test_1context"],
"test2": ["test_2"],
"zero length translation": [""],
"context\u0004test2": ["test_2context"],
"context\u0004context plural %1$d": ["context_plural_1 singular %1$d", "context_plural_1 plural %1$d"]
}
};

var i18n = new Jed({
"locale_data" : locale_data_w_context,
"domain": "context_sprintf_test"
});


it("should take multiple types of arrays as input", function () {
var strings = {
"blah" : "blah",
"thing%1$sbob" : "thing[one]bob",
"thing%1$s%2$sbob" : "thing[one][two]bob",
"thing%1$sasdf%2$sasdf" : "thing[one]asdf[two]asdf",
"%1$s%2$s%3$s" : "[one][two]",
"tom%1$saDick" : "tom[one]aDick"
};
var args = ["[one]", "[two]"];

for (var i in strings) {
// test using new Array
expect(Jed.sprintf(i, ["[one]","[two]"])).to.be(strings[i]);
expect(i18n.sprintf(i, ["[one]","[two]"])).to.be(strings[i]);
// test using predefined array
expect(Jed.sprintf(i, args)).to.be(strings[i]);
expect(i18n.sprintf(i, args)).to.be(strings[i]);
}
});



it("should accept a single string instead of an array", function () {
// test using scalar rather than array
var strings = {
"blah" : "blah",
"" : "",
"%%" : "%",
"tom%%dick" : "tom%dick",
"thing%1$sbob" : "thing[one]bob",
"thing%1$s%2$sbob" : "thing[one]bob",
"thing%1$sasdf%2$sasdf" : "thing[one]asdfasdf",
"%1$s%2$s%3$s" : "[one]"
};
var arg = "[one]";

for (var i in strings) {
expect(Jed.sprintf(i, arg)).to.be(strings[i]);
expect(i18n.sprintf(i, arg)).to.be(strings[i]);
}
});
});
})();

Expand Down