Skip to content

Localize from object #94

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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,49 @@ $("[data-localize]").localize("application", {
</script>
```

See the test/samples for working examples.
See the _examples_ folder for working examples.

### Using raw objects as data source
Given you can't have your texts stored inside of a json file that follows the naming convention defined earlier, you may also pass a raw javascript object to the `localize` plugin, just like this:

```javascript
// Define a javascript object with your texts
// Its structure must match that of a json object
var myTexts = {
"app": {
"name": "MyApp",
"version": "1.2",
"description": "App using 'jquery-localize'"
}
};

// Localize your content by using the object values
$("[data-localize]").localize(myTexts);
```

The way of referencing the different object properties from the html file is exactly the same as before. In the following example, the `p` tag content will be replaced with the value of the `app.name` property inside our `myTexts` object:

```html
<p data-localize="app.name">(text to replace)</p>
```

This feature might be useful if you'll be retrieving your texts from an external service whose url doesn't match the `"{pathPrefix}/{filename}-{lang}.{extension}"` syntax.

In that case, you'll need to perform an ajax request to said service from inside your own code and call `localize` by passing in the results returned by the server:

```javascript
$.ajax({
method: "GET",
url: "api/app/details",
data: { lang: "en" },
dataType: "json",
success: function (data) {
$("[data-localize]").localize(data);
}
});
```

Remember to keep a json friendly structure for every object you send as an argument to `localize`, such as in the previous examples.

# Contributing

Expand Down
31 changes: 26 additions & 5 deletions dist/jquery.localize.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ http://keith-wood.name/localisation.html
};
$.defaultLanguage = normaliseLang(navigator.languages && navigator.languages.length > 0 ? navigator.languages[0] : navigator.language || navigator.userLanguage);
$.localize = function(pkg, options) {
var defaultCallback, deferred, fileExtension, intermediateLangData, jsonCall, lang, loadLanguage, localizeElement, localizeForSpecialKeys, localizeImageElement, localizeInputElement, localizeOptgroupElement, notifyDelegateLanguageLoaded, regexify, setAttrFromValueForKey, setTextFromValueForKey, valueForKey, wrappedSet;
var defaultCallback, deferred, fileExtension, intermediateLangData, jsonCall, loadLanguage, localizeElement, localizeForSpecialKeys, localizeImageElement, localizeInputElement, localizeOptgroupElement, notifyDelegateLanguageLoaded, regexify, sanitizedCallback, setAttrFromValueForKey, setTextFromValueForKey, useFileAsDataSource, useObjectAsDataSource, valueForKey, wrappedSet;
if (options == null) {
options = {};
}
Expand Down Expand Up @@ -180,11 +180,32 @@ http://keith-wood.name/localisation.html
return string_or_regex_or_array;
}
};
lang = normaliseLang(options.language ? options.language : $.defaultLanguage);
if (options.skipLanguage && lang.match(regexify(options.skipLanguage))) {
deferred.resolve();
useFileAsDataSource = function(filename) {
var lang;
lang = normaliseLang(options.language ? options.language : $.defaultLanguage);
if (options.skipLanguage && lang.match(regexify(options.skipLanguage))) {
return deferred.resolve();
} else {
return loadLanguage(filename, lang, 1);
}
};
sanitizedCallback = function(object) {
var data;
data = JSON.parse(JSON.stringify(object));
return defaultCallback(data);
};
useObjectAsDataSource = function(object) {
if (options.callback != null) {
options.callback(object, sanitizedCallback);
} else {
sanitizedCallback(object);
}
return deferred.resolve();
};
if (typeof pkg === "object") {
useObjectAsDataSource(pkg);
} else {
loadLanguage(pkg, lang, 1);
useFileAsDataSource(pkg);
}
wrappedSet.localizePromise = deferred;
return wrappedSet;
Expand Down
6 changes: 3 additions & 3 deletions dist/jquery.localize.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions examples/objectDS/localize_from_external_ws.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Localize Test</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../../dist/jquery.localize.min.js" type="text/javascript" charset="utf-8"></script>
</head>

<body>
<h1>Test localization...</h1>
<h2 data-localize="app.name">Waiting for server response...</h2>
<h3 data-localize="app.version">Waiting for server response...</h3>
<p data-localize="app.description">
Waiting for server response...
</p>

<script type="text/javascript" charset="utf-8">
$(function(){
$.ajax({
method: "GET",
url: "res/ws-response",
data: { lang: "en" },
dataType: "json",
success: function (data) {
$("[data-localize]").localize(data);
}
});
})
</script>
</body>

</html>
69 changes: 69 additions & 0 deletions examples/objectDS/localize_from_object.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Localize Test</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../../dist/jquery.localize.min.js" type="text/javascript" charset="utf-8"></script>
</head>

<body>
<h1>Test localization...</h1>
<p data-localize="test.nested">puts 2 + 2</p>
<input data-localize="test.input" type="text" value="fail" />
<select>
<optgroup data-localize="test.optgroup" label="Fail">
<option data-localize="test.option" value="1">Fail</option>
</optgroup>
</select>
<p>
<img src="../ruby_square.gif" alt="a square ruby" data-localize="test.ruby_image"/>
Ruby image should be round.
</p>
<p data-localize="basic">It failed :(</p>
<p data-localize="message">Optional callback never happened.</p>

<script type="text/javascript" charset="utf-8">
$(function(){
var object = {
"test": {
"nested": "nested success",
"input": "input success",
"input_as_obj": {
"value": "input_as_obj value success",
"title": "input_as_obj title success"
},
"optgroup": "optgroup success",
"option": "option success",
"ruby_image": {
"src": "../ruby_round.gif",
"alt": "a round ruby",
"title": "A Round Ruby"
},
"link": {
"text": "success",
"href": "http://success"
}
},
"basic": "basic success",
"with_title": {
"text": "with_title text success",
"title": "with_title title success"
}
};

var customCallback = function(data, defaultCallback) {
data.message = "Optional call back works.";
defaultCallback(data);
};

var opts = { callback: customCallback };
$("[data-localize]").localize(object, opts);
})
</script>
</body>

</html>
7 changes: 7 additions & 0 deletions examples/objectDS/res/ws-response
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"app": {
"name": "MyApp",
"version": "1.2",
"description": "App using 'jquery-localize'"
}
}
28 changes: 25 additions & 3 deletions src/jquery.localize.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,33 @@ do ($ = jQuery) ->
else
string_or_regex_or_array

lang = normaliseLang(if options.language then options.language else $.defaultLanguage)
if (options.skipLanguage && lang.match(regexify(options.skipLanguage)))
# Retrieve translations from an external file depending on required language
useFileAsDataSource = (filename) ->
lang = normaliseLang(if options.language then options.language else $.defaultLanguage)
if (options.skipLanguage && lang.match(regexify(options.skipLanguage)))
deferred.resolve()
else
loadLanguage(filename, lang, 1)

# We stringify and parse the received object to ensure the object is a valid json
# Any functions defined within the object will be removed during this process
sanitizedCallback = (object) ->
data = JSON.parse(JSON.stringify(object))
defaultCallback(data)

# Retrieve translations from an object
useObjectAsDataSource = (object) ->
if options.callback?
options.callback(object, sanitizedCallback)
else
sanitizedCallback(object)
deferred.resolve()

# If 'pkg' is an object, use it as the source for translations
if typeof(pkg) == "object"
useObjectAsDataSource(pkg)
else
loadLanguage(pkg, lang, 1)
useFileAsDataSource(pkg)

wrappedSet.localizePromise = deferred

Expand Down
39 changes: 39 additions & 0 deletions test/localize_test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,42 @@ do ($ = jQuery) ->
t = localizableTagWithRel("p", "en_us_message", text: "en-US not loaded")
t.localize("test", opts).localizePromise.then ->
assert.equal t.text(), "en-US not loaded"

# Ref: https://github.com/coderifous/jquery-localize/issues/62
module "Using object as data source"

asyncTest "basic tag text substitution using object as data source", (assert) ->
obj = basic: "basic success"
t = localizableTagWithRel("p", "basic", text: "basic fail")
t.localize(obj).localizePromise.then ->
assert.equal t.text(), "basic success"

asyncTest "custom callback is fired when object is used as data source", (assert) ->
opts = {}
opts.callback = (data, defaultCallback) ->
data.custom_callback = "custom callback success"
defaultCallback(data)
t = localizableTagWithRel("p", "custom_callback", text: "custom callback fail")
t.localize({}, opts).localizePromise.then ->
assert.equal t.text(), "custom callback success"

asyncTest "tag text must not be replaced if matching object property contains a function", (assert) ->
obj = "function": (->)
t = localizableTagWithRel("p", "function", text: "this text should remain unchanged")
t.localize(obj).localizePromise.then ->
assert.equal t.text(), "this text should remain unchanged"

asyncTest "input value must not be replaced if matching object property contains a function", (assert) ->
obj = "function": (->)
t = localizableTagWithRel("input", "function", text: "remain after default callback")
t.localize(obj).localizePromise.then ->
assert.equal t.text(), "remain after default callback"

asyncTest "input value must not be replaced if custom callback introduced a matching property that contains a function", (assert) ->
opts = {}
opts.callback = (data, defaultCallback) ->
data.added_function = (->)
defaultCallback(data)
t = localizableTagWithRel("input", "added_function", text: "remain after custom callback")
t.localize({}, opts).localizePromise.then ->
assert.equal t.text(), "remain after custom callback"
67 changes: 66 additions & 1 deletion test/localize_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@
return assert.equal(t.text(), "en not loaded");
});
});
return asyncTest("skipping region language using array match", function(assert) {
asyncTest("skipping region language using array match", function(assert) {
var opts, t;
opts = {
language: "en-US",
Expand All @@ -362,4 +362,69 @@
return assert.equal(t.text(), "en-US not loaded");
});
});
module("Using object as data source");
asyncTest("basic tag text substitution using object as data source", function(assert) {
var obj, t;
obj = {
basic: "basic success"
};
t = localizableTagWithRel("p", "basic", {
text: "basic fail"
});
return t.localize(obj).localizePromise.then(function() {
return assert.equal(t.text(), "basic success");
});
});
asyncTest("custom callback is fired when object is used as data source", function(assert) {
var opts, t;
opts = {};
opts.callback = function(data, defaultCallback) {
data.custom_callback = "custom callback success";
return defaultCallback(data);
};
t = localizableTagWithRel("p", "custom_callback", {
text: "custom callback fail"
});
return t.localize({}, opts).localizePromise.then(function() {
return assert.equal(t.text(), "custom callback success");
});
});
asyncTest("tag text must not be replaced if matching object property contains a function", function(assert) {
var obj, t;
obj = {
"function": (function() {})
};
t = localizableTagWithRel("p", "function", {
text: "this text should remain unchanged"
});
return t.localize(obj).localizePromise.then(function() {
return assert.equal(t.text(), "this text should remain unchanged");
});
});
asyncTest("input value must not be replaced if matching object property contains a function", function(assert) {
var obj, t;
obj = {
"function": (function() {})
};
t = localizableTagWithRel("input", "function", {
text: "remain after default callback"
});
return t.localize(obj).localizePromise.then(function() {
return assert.equal(t.text(), "remain after default callback");
});
});
return asyncTest("input value must not be replaced if custom callback introduced a matching property that contains a function", function(assert) {
var opts, t;
opts = {};
opts.callback = function(data, defaultCallback) {
data.added_function = (function() {});
return defaultCallback(data);
};
t = localizableTagWithRel("input", "added_function", {
text: "remain after custom callback"
});
return t.localize({}, opts).localizePromise.then(function() {
return assert.equal(t.text(), "remain after custom callback");
});
});
})(jQuery);