using variables in i18n resource files - javascript

Hello everyone,
I'm using the i18n (for require.js) library to load my translated strings from resource files based on the user's language.
I have used this approach, since I'm using both backbone and require.js in my project. But I'd like also to put an argument/variable to the string stored in the i18n resource file.
Let's say this is the i18n resource file
define({
'root': {
'options': {
'test': 'Yellow {variable.x}'
}
},
"en-us": true
});
now I'm not really sure whether it's possible possible to pass an argument to evaluate the variable inside the resource file.
define(['underscore', 'backbone', 'models/model', 'templates/template' , 'i18n!nls/resource'], function ( _, Backbone, tModel, template, resource) {
var TooltipView = Backbone.View.extend({
el : $('#test'),
initialize: function(options){
this.model = new tModel();
},
render: function(){
var $el = this.$el;
if(template.length != 0){
var compiledTemplate = template['test']( resource, { variable: "14"} ) /// loads pre-compiled template ///
$el.html(compiledTemplate);
}else{
console.log(" [e] No template found. ");
}
});
}
});
return TooltipView;
});
I'd like to achieve this output:
<h1> Yellow 14 </h1>

As #Evgeniy suggested , I should have used the sprintf library for this case.
Solution:
template:
<span> <%= sprintf( data.text, data.id ) %> </span>
resource file:
define({
'root': {
'options': {
'test': 'Yellow %i'
}
},
"en-us": true
});
then in view I needed to change this:
define(['underscore', 'backbone', 'models/model', 'templates/template' , 'i18n!nls/resource', 'sprintf'], function ( _, Backbone, tModel, template, resource, s_utils) {
...
..
var data = _.extend( resource, { id: 11 } , s_utils ); // this enabled me to use the sprintf function in my template file.
var compiledTemplate = template['test']( data ) /// loads pre-compiled template ///
$el.html(compiledTemplate);
..
return TooltipView;
});

Related

Backbone.js Collection.add method being overwritten by 3rd party script

I am having an issue with a method being overwritten on a dependency that is being injected with Require.
Currently I am have a utility that adds and controls some notifacations across our site that you can see below.
define([
'jQuery',
'Underscore',
'Backbone',
'Data',
'Window',
'text!utilities/notify/templates/utility.html'
], function($, _, Backbone, Data, Window, Template) {
var Notify = Backbone.View.extend({
events: {
'click': 'close'
},
initialize: function() {
var self = this;
_.bindAll(this);
// add notify if not in the dom
var element = $('#notify')[0];
if(_.isEmpty(element)) {
var template = _.template(Template, {});
$('body').prepend(template);
}
Data.add([
// notify object
{
id: 'notify',
addToUrl: false,
addToHistory: false
}
]);
}
return new Notify;
});
(This is only a small portion of this file with the relevant data)
The Data dependency is a wrapper to add a few helper methods to deal with Collections. But we do not overload or modify the add method on the Collection in anyway. The problem I am facing is that in every modern evergreen browser (chrome, firefox, etc) Data is injected correctly and Data.add() works as expected. But in IE8 (sadly I have to support this) the Data.add method sudenly executes a function in Googles Adsense async-ads.js file that we use on our page. When this happens its causes a crazy recursion and IE8 gives a stack overflow message.
I am totally perplexed as to how the Data.add() method is being overwritten by a 3rd party JS function! Any ideas would be greatly appriciated!
Included JS Version Info
Backbone 1.0.0
Require 2.1.2
Underscore 1.3.3
EDIT: I've included the code from the Data utility as requested
/**
#appular {utility} data - designed to store variables for apps that multiple modules may need access too. works closely with the router to keep the url updated as well.
#extends backbone.collection
#define Data
*/
define([
'jQuery',
'Underscore',
'Backbone',
'utilities/data/models/data',
'Cookies'
], function($, _, Backbone, DataModel, Cookies){
var Collection = Backbone.Collection.extend({
model: DataModel,
lastChanged: '',
initialize: function() {
_.bindAll(this);
this.on('add', function(model) {
model.on('change', function() {
this.lastChanged = model.get('id');
this.trigger('dataChanged', model.get('id'));
}, this);
}, this);
},
// Sets data based on url data on initial load (ignores any parameters that are not defined in initialize above)
load: function(data) {
var dataInitialized = _.after(data.length, this.finalizeLoad);
_.each(data, function(dataArray) {
var model = this.get(dataArray[0]);
if(!model) {
model = _.find(this.models, function(model) { return model.get('alias').toLowerCase() === dataArray[0].toLowerCase(); });
}
if(model) {
model.set({value: decodeURIComponent(dataArray[1])}, {silent: true});
}
dataInitialized();
}, this);
},
finalizeLoad: function() {
var triggerInitialized = _.after(this.length, this.triggerInitialized);
_.each(this.models, function(model) {
if(model.get('getFromCookie')) {
var cookieName = null;
if(model.get('alias') !== '') {
cookieName = model.get('alias');
} else {
cookieName = model.get('id');
}
model.set({value: Cookies.get(cookieName)});
}
if(model.get('isArray') && _.isString(model.get('value'))) {
var value = model.get('value');
model.set('value', value.split(','));
}
triggerInitialized();
}, this);
},
/**
#doc {event} initialized - fires when all data has been loaded
*/
triggerInitialized: function() {
this.trigger('initialized');
},
/**
#doc {function} getValueOf - shortcut to get model's value
*/
getValueOf: function(name) {
return this.get(name).get('value');
},
/**
#doc {function} setValueOf - shortcut to set model's value
*/
setValueOf: function(name, value) {
return this.get(name).set('value', value);
}
});
return new Collection;
});

Reloading the backbone view and i18n translation file in Backbone Application?

I am using i18n.js in BackboneJS application for Text localization. but stuck at a point where i need reload the text translation on language change in the application. i am calling setting view render() function on language change but doesn't work for me, but on reloading index.html it works. So How can i reload the translation file and view to reflect the changes. below is what i tried--
application-bootstrap.js
var locale = {};
locale.language = "en-us";//default
locale.setLanguage = function (language) {
localStorage.setItem('localLanguage', '' + language);
};
locale.getLanguage = function () {
return localStorage.getItem('localLanguage');
};
require.config({
config: {
i18n: {
locale: locale.getLanguage()
}
}
});
settingView.js
define(['compiled/settingTmpl','i18n!nls/setting'
], function (SettingTmpl,setting) {
'use strict';
var SettingView = Backbone.View.extend({
events: {
"change #languageSelect": "changeLocale"
},
initialize: function () {
WellNation.helper.log("Setting view initialize");
this.render();
},
changeLocale: function (e) {
var locale = e.currentTarget.value;
WellNation.locale.setLanguage(locale);
this.render();
},
render: function () {
this.$el.html(SettingTmpl({speak:setting}));
return this;
}
});
return SettingView;
});
settingTmpl.handlebars
<div class="row">
<label>{{speak.language}}</label>
<select id="languageSelect">
<option value="en-us">English (United States)</option>
<option value="fr-fr">Francais (France)</option>
</select>
</div>
nls/fr-fr/setting.js
define({
"language" : "langue"
});
nls/setting.js
define({
"root" : {
"language" : "Language"
},
"fr-fr" : true // The system will accept French
});
According to this SO question and this github issue it's not possible to change locale at runtime with i18n.js.
From official docs it's not clear could we use it at runtime or not: "RequireJS will use the browser's navigator.language or navigator.userLanguage property to determine what locale values to use for my/nls/colors, so your app does not have to change. If you prefer to set the locale, you can use the module config to pass the locale to the plugin"
So after some researches and walk throw the i18n.js source code I found that the best solution for you will be to keep the same structure and use location.reload().

Angular external templating vs Handlebars external templating

this is how I load and compile my templates in Handlebarsjs,
define([
'jquery',
'handlebars'
],
function ($, Handlebars) {
// #reference: http://berzniz.com/post/24743062344/handling-handlebars-js-like-a-pro
Handlebars.getTemplate = function(name) {
if (Handlebars.templates === undefined || Handlebars.templates[name] === undefined) {
$.ajax({
url : 'js/template/' + name + '.handlebars',
success : function(data) {
if (Handlebars.templates === undefined) {
Handlebars.templates = {};
}
Handlebars.templates[name] = Handlebars.compile(data);
},
dataType: "text",
async : false
});
}
return Handlebars.templates[name];
};
// Precompile your templates and add/paste them below here.
});
and I can just retrieve the template with this line below,
this.$el.html(Handlebars.getTemplate('list')({contacts: response}));
So this I don't have to use ajax to call the same template over and over again. I can just get it from the Handlebars' storage.
Is this possible with Angularjs? I have been looking up for tutorials for doing this in Angualrjs but can't find any. Any ideas where is the good start?
Angular.js caches templates when they are requested (so the request only happens once), but you can prepopulate that cache using $templateCache.

backbone.js Search Filtering System [Structure]

I'm using Controller to Fetch URL. I need a way to put Parameter in this POST. These Parameters are selected by users on View & Not Stored yet(I do not know how to store)
Currently I managed to
Display & Route The View with search result coming from API
Display and refresh the page when someone selects a Filter Option
Problem
I got no idea how to record what the users clicked
How do i "re-post" so i can get the new set of results
I read and say people saying POST Fetch should be done in Model ,
Collection is for Store Multiple Models which i don't know in this
scenario?
Collections
Jobs.js
define([
'jquery',
'underscore',
'backbone',
'models/filter'
], function($, _, Backbone,JobListFilterModel){
var Jobs = Backbone.Collection.extend({
url: function () {
return 'http://punchgag.com/api/jobs?page='+this.page+''
},
page: 1,
model: JobListFilterModel
});
return Jobs;
});
Collections Filter.JS
define([
'jquery',
'underscore',
'backbone',
'models/filter'
], function($, _, Backbone,JobListFilterModel){
console.log("Loaded");
var Jobs = Backbone.Collection.extend({
url: function () {
return 'http://punchgag.com/api/jobs?page='+this.page+''
},
page: 1,
model: JobListFilterModel
});
// var donuts = new JobListFilterModel;
// console.log(donuts.get("E"));
return Jobs;
});
Models
Filter.js
define([
'underscore',
'backbone'
], function(_, Backbone){
var JobFilterModel = Backbone.Model.extend({
defaults: {
T: '1', //Task / Event-based
PT: '1', //Part-time
C: '1', //Contract
I: '1' //Internship
}
});
// Return the model for the module
return JobFilterModel;
});
Models
Job.js
define([
'underscore',
'backbone'
], function(_, Backbone){
var JobModel = Backbone.Model.extend({
defaults: {
name: "Harry Potter"
}
});
// Return the model for the module
return JobModel;
});
Router.js
define([
'jquery',
'underscore',
'backbone',
'views/jobs/list',
'views/jobs/filter'
], function($, _, Backbone, JobListView, JobListFilterView){
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
'seeker/jobs': 'showJobs',
'*actions': 'defaultAction'
},
initialize: function(attr)
{
Backbone.history.start({pushState: true, root: "/"})
},
showJobs: function()
{
var view = new JobListView();
view.$el.appendTo('#bbJobList');
view.render();
console.log(view);
var jobListFilterView = new JobListFilterView();
jobListFilterView.render()
},
defaultAction: function(actions)
{
console.info('defaultAction Route');
console.log('No route:', actions);
}
});
var initialize = function(){
console.log('Router Initialized');// <- To e sure yout initialize method is called
var app_router = new AppRouter();
};
return {
initialize: initialize
};
});
Some Examples would be awesome. Thank you
Fetching means to retrieve (as you probably know), to GET from the server some information.
POST is usually for creating new resources. For instance, saving a new Job would be a POST on the /jobs URL in a REST like API.
In your case, what you probably want is a:
JobCollection which would extend from Backbone Collection and use a JobModel as the model
JobModel which would represents a Job.
You currently already have the JobModel but it has no Collection... And instead you have a Collection of JobFilters, which means that you are handling multiple set of filters. That's probably not what you had in mind?
Assuming you now have a JobCollection that represents the list of all the jobs your views will display, when you do a collection.fetch() on it, it'll GET all the jobs, without any filters.
The question now becomes: how do I pass extra parameters to fetch() in a collection?
There are many ways to do that. As you already have a JobFilterModel, what you can do in your JobFilterModel is implement a method such as:
//jobCollection being the instance of Job collection you want to refresh
refreshJobs: function(jobCollection) {
jobCollection.fetch({reset: true, data: this.toJSON()});
}
A model's toJSON will transform the Model into a nice Javascript object. So for your JobFilterModel, toJSON() will give back something like:
{
T: '1', //Task / Event-based
PT: '1', //Part-time
C: '1', //Contract
I: '1' //Internship
}
Putting it in the data property of the Collection's fetch() option hash will add those to the query to the server. Then, whatever jobs your server answer with, they will be used to reset (that's why reset: true in the options, otherwise it just updates) the collection of jobs. You can then bind in your views on jobCollection "reset" event to know when to re-render.
So, now, your JobFilterModel only 'job' is to store (in memory) the filters the user has chosen, and the JobCollection and JobModel don't know anything about the filters (and they shouldn't). As for storing the JobFilterModel's current status, you can look at Backbone localstorage plugin or save it on your server / get it from your server (using the model's fetch() and save() method).
I hope this helps!

Ensure requireJS conditional module loaded

I'm trying to use requireJS in an existing project. I have an App module that has information about current locale, url ... and want load another module if current locale is something special:
// App.js
define([], function() {
return {
setEnv: function(obj) { //obj: {locale:'en'}
this.env = obj;
that = this;
if( this.env.locale == 'fa' )
require(['locale/fa'], function(fa) {
that.number = fa.number
};
else
this.number = function( n ) { return n+'' }
}
}
});
Locale files are like:
// locale/fa.js
define([], function() {
var enToFaDigit(n) { // a function converts a number to persian representation };
// more stuff
return {
number: enToFaDigit
};
});
Now the problem is i don't know when App module loaded number method. If i was sure locale/fa module should load at some point, i could wrap it using require(['locale/fa'] or use shim. Currently I'm using old blocking way to load appropriate locale/*.js and PHP to choose right file, without problem. But i want know how requireJS programmers write similar code. Probably there is some way better than this code:
require(['app'], function(App) {
if(App.env.locale == 'fa') {
require(['locale/fa'], function(fa) {
// 8-S
}
}
});
This sounds like a case for the i18n plugin! I believe you are allowed to define functions as well as strings. I would define a bundle like so:
//nls/formatters.js
define({
root: {
currency: function(num) {
// implementation for default currency format (en-US often)
}
},
"fa": true
})
And here's your Persian override:
//nls/fa/formatters.js
define({
currency: function(num) {
// implementation for farsi currency format
}
})
In your app.js file:
require(['app','i18n!nls/formatters'], function(App, Formatters) {
Formatters.currency(5.00); // works!
});

Categories