Knockback.js does not provide a Locale Manager implementation since it depends on how you want to store and where you want to manage your localized strings.
If you choose to use kb.LocalizedObservable, it assumes that you assign your implementation to 'kb.locale_manager'.
Because Knockback actually only requires a Backbone.Model-like signature, a custom locale manager can be implemented quite easily. Here is an example:
class LocaleManager constructor: (locale_identifier, @translations_by_locale) -> @current_locale = ko.observable(locale_identifier) get: (string_id) -> return '(no translation)' unless @translations_by_locale[@current_locale()] return '(no translation)' unless @translations_by_locale[@current_locale()].hasOwnProperty(string_id) return @translations_by_locale[@current_locale()][string_id] getLocale: -> return @current_locale() setLocale: (locale_identifier) -> @current_locale(locale_identifier) @trigger('change', @) _.extend(LocaleManager.prototype, Backbone.Events)
var ko = kb.ko; var LocaleManager = function(locale_identifier, translations_by_locale) { this.translations_by_locale = translations_by_locale; this.current_locale = ko.observable(locale_identifier); this.get = function(string_id) { if (!this.translations_by_locale[this.current_locale()]) { return '(no translation)'; } if (!this.translations_by_locale[this.current_locale()].hasOwnProperty(string_id)) { return '(no translation)'; } return this.translations_by_locale[this.current_locale()][string_id]; }; this.getLocale = function() { return this.current_locale(); }; this.setLocale = function(locale_identifier) { this.current_locale(locale_identifier); return this.trigger('change', this); }; }; _.extend(LocaleManager.prototype, Backbone.Events);
In more detail...we create an observable with the current locale so the locale identifier can be displayed:
@current_locale = ko.observable(locale_identifier)
this.current_locale = ko.observable(locale_identifier);
We create a get() method that is used by ko.observable to listen for changes using Backbone.Events:
get: (string_id) -> ...
get: function(string_id) {...}
We trigger a Backbone.Events event to notify a subscribed ko.observable
setLocale: (locale_identifier) -> @current_locale(locale_identifier) @trigger('change', @)
setLocale: function(locale_identifier) { this.current_locale(locale_identifier); this.trigger('change', this); }
We mixin Backbone.Events into our LocaleManager to provide 'bind', 'trigger', and 'unbind':
_.extend(LocaleManager.prototype, Backbone.Events)
_.extend(LocaleManager.prototype, Backbone.Events);
<div id='lm_simple'> <p> <span data-bind="text: label_name"></span> <span data-bind="text: name"></span> </p> <p> <span>Current Locale: <span> <span data-bind="text: simple_locale_manager.getLocale()"><span> </p> <button data-bind="click: toggleLocale">Toggle Locale</button> </div>
# create and initialize the locale manager with two languages simple_locale_manager = new LocaleManager('en', { 'en': name: 'Name: ' 'fr': name: 'Nom: ' }) bob = new Backbone.Model({name: 'Bob'}) view_model = kb.viewModel(bob) # add a localized label for 'name' and a function for toggling the current locale view_model.label_name = kb.observable(simple_locale_manager, 'name') view_model.toggleLocale = -> simple_locale_manager.setLocale(if (simple_locale_manager.getLocale() is 'en') then 'fr' else 'en') ko.applyBindings(view_model, $('#lm_simple')[0])
// create and initialize the locale manager with two languages var simple_locale_manager = new LocaleManager('en', { 'en': { name: 'Name: ' }, 'fr': { name: 'Nom: ' } }); var bob = new Backbone.Model({name: 'Bob'}); var view_model = kb.viewModel(bob); // add a localized label for 'name' and a function for toggling the current locale view_model.label_name = kb.observable(simple_locale_manager, 'name'); view_model.toggleLocale = function() { return simple_locale_manager.setLocale(simple_locale_manager.getLocale() === 'en' ? 'fr' : 'en'); }; ko.applyBindings(view_model, $('#lm_simple')[0]);
Current Locale:
To make a more advanced version of the locale manager, you will want to add error checking and might choose to add parameters to the get() function, trigger on each string, and allow to query for all available locales (among other possible enhancements!):
class LocaleManager constructor: (locale_identifier, @translations_by_locale) -> @current_locale = ko.observable() @setLocale(locale_identifier) get: (string_id) -> return '(no translation)' unless @translations_by_locale[@current_locale()] return '(no translation)' unless @translations_by_locale[@current_locale()].hasOwnProperty(string_id) string = @translations_by_locale[@current_locale()][string_id] return string if arguments == 1 # no arguments # add arguments string = string.replace("{#{index}}", arg) for arg, index in Array.prototype.slice.call(arguments, 1) return string getLocale: -> return @current_locale() setLocale: (locale_identifier) -> @current_locale(locale_identifier) @trigger('change', @) @trigger("change:#{key}", value) for key, value of @translations_by_locale[@current_locale()] getLocales: -> locales = [] locales.push(string_id) for string_id, value of @translations_by_locale return locales _.extend(LocaleManager.prototype, Backbone.Events)
var ko = kb.ko; var LocaleManager = function() { this.translations_by_locale = translations_by_locale; this.current_locale = ko.observable(locale_identifier); this.get = function(string_id) { if (!this.translations_by_locale[this.current_locale()]) { return '(no translation)'; } if (!this.translations_by_locale[this.current_locale()].hasOwnProperty(string_id)) { return '(no translation)'; } var string = this.translations_by_locale[this.current_locale()][string_id]; if (arguments === 1) { return string; } // no arguments // add arguments arguments = Array.prototype.slice.call(arguments, 1); for (var index = _i = 0; _i < arguments.length; index = ++_i) { string = string.replace("{" + index + "}", arguments[index]); } return string; }; this.getLocale = function() { return this.current_locale(); }; this.setLocale = function(locale_identifier) { this.current_locale(locale_identifier); this.trigger('change', this); var map = this.translations_by_locale[this.current_locale()]; for (key in map) { this.trigger("change:" + key, map[key]); } }; this.getLocales = function() { var locales = []; for (var string_id in this.translations_by_locale) { value = this.translations_by_locale[string_id]; locales.push(string_id); } return locales; }; }; _.extend(LocaleManager.prototype, Backbone.Events);
If you need to create a large number of localized labels, you could use kb.ViewModel like:
<div id='lm_bulk_labels'> <!-- ko foreach:['name', 'start_date', 'end_date'] --> <p> <span data-bind="text: $parent.labels[$data]"></span> <span data-bind="text: $parent[$data]"></span> </p> <!-- /ko --> <p> <span>Current Locale: <span> <span data-bind="text: bulk_locale_manager.getLocale()"><span> </p> <button data-bind="click: toggleLocale">Toggle Locale</button> </div>
bulk_locale_manager = new LocaleManager('en', { 'en': name: 'Name: ' start_date: 'Start Date: ' end_date: 'End Date: ' 'fr': name: 'Nom: ' start_date: 'Commencement: ' end_date: 'Clôture: ' }) view_model = kb.viewModel(new Backbone.Model({name: 'Bob', start_date: '1/1/2012', end_date: '1/2/2012'})) # use ko.observable to create multiple labels quickly view_model.labels = kb.viewModel(bulk_locale_manager, ['name', 'start_date', 'end_date']) view_model.toggleLocale = -> bulk_locale_manager.setLocale(if (bulk_locale_manager.getLocale() is 'en') then 'fr' else 'en') ko.applyBindings(view_model, $('#lm_bulk_labels')[0])
var ko = kb.ko; var bulk_locale_manager = new LocaleManager('en', { 'en': { name: 'Name: ', start_date: 'Start Date: ', end_date: 'End Date: ' }, 'fr': { name: 'Nom: ', start_date: 'Commencement: ', end_date: 'Clôture: ' } }); var view_model = kb.viewModel(new Backbone.Model({name: 'Bob', start_date: '1/1/2012', end_date: '1/2/2012'})); // use ko.observable to create multiple labels quickly view_model.labels = kb.viewModel(bulk_locale_manager, ['name', 'start_date', 'end_date']); view_model.toggleLocale = function() { return bulk_locale_manager.setLocale(bulk_locale_manager.getLocale() === 'en' ? 'fr' : 'en'); }; ko.applyBindings(view_model, $('#lm_bulk_labels')[0]);
Current Locale: