javascript - How to handle multiple languages in AngularJS for form validation messages / alerts? -
i'm trying develop app using angularjs should work in multiple, user selected languages. put views in views/en
, views/fr
, etc, if user viewing in english, template url controllers views/en/somefile.html
, , french /views/fr/somefile.html
, , on.
the question is, how can handle displaying random form validation messages / alerts displayed javascript, displayed in correct language?
e.g in controllers have $scope.title
variable sets <title>
in browser. if user selects different language, want <title>
updated shown in selected language well.
what's best way of achieving this?
textservice
i have updated answer reflect current solution problem. removing textual data dependence in templates important problem feel , have gone ahead , wrote own little module this. if want use text insertion options (state text , relative text), you'll need use ui-router, configure routes.
here's usage. i'll put module @ end of answer.
angular.module("app", ["textservice"]
var textdata = { home: { // 'home' state // text data home state here child { // 'home.child' state // text data child state } } }
text objects should object literals language names keys:
var textdata = { home: { title: { en: "home", fr: "maison" } } } }
set language , bind text object textservice:
app.run(function(textservice) { var textdata = ... textservice .setlanguage("en") .bindtext(textdata) });
having text data inside run function may not desirable - user if want use factory or number of factories inject this:
app.factory("textdata", function() { var textdata = ... return textdata } app.run(function(textservice, textdata) { textservice .setlanguage("en") .bindtext(textdata) });
-
atext
- "absolute text" -
stext
- "state text" -
rtext
- "relative text" -
text
- "text"
to use template, add, say, atext
attribute , set value string locating text data interested in:
<h1 atext="home.title.en"></h1>
all directives replace innerhtml of whatever dom element add text data, make sure has no children dom elements care about.
the differences in directives locating text strings. suppose had following text data object:
var textdata = { title: { en: "index", fr: "index" }, // home state home: { title: {en: "home",fr: "maison"}, header: {en: "heading", fr: ""} child: { // home.child title: {en: "child",fr: "enfant"}, intro: { en: "welcome child", fr: "bienvenue à l'enfant" } } lonelychild: { // no text data } } };
atext
refers absolute location of string. atext="title.en"
fetches "index" atext="title"
throw error (for now).
stext
refers string relative current state. example, if in state 'home', stext="title"
fetches "home". if weren't in state @ all, fetch "index". , if navigate 'home.child' state, fetch "child". (note: these provided have set language "en")
rtext
refers relative text location. behaves stext
except search state heirarchy looking textual data match well. handy dynamically changing title depending on state:
<title rtext="title"></title>
this replace innerhtml of title dom element nearest 'title' text data. if in state 'home.lonelychild' title still bind "home" (because in parent state's text data).
text
directive works atext
, except don't need specify language.
the directives change dynamically on language change stext, rtext , text. can change language using, e.g., textservice.setlanguage("fr")
. $broadcast "languagechange" event text directives indicating should update. if want suppress update, pass second parameter indicating whether update should done: textservice.setlanguage("fr", false)
/
the directives change dynamically on state change stext , rtext.
you can force update using textservice.update()
i've gone lengths make plugin fast possible. general rule of thumb, each text directive incur initial processing time of 1ms. think of bulk comes angular initialising directive each piece of text data. following that, dynamic updates quick, if have 100 or text directives on 1 page.
here's module , link github repo
/* * text-service.js * author: ian haggerty - iahag001@yahoo.co.uk * last edit: 17/08/2013 */ angular.module("textservice", []) .factory("textservice", function ($rootscope, $log) { /* internal implementation */ var textservice; textservice = { language: "", state: "", textdata: {}, /* text(request) - text request * @request absolute path text without language appended - e.g. 'home.title' */ text: function (request) { return (new function( "return arguments[0].textdata." + request + ((textservice.language) ? ("." + textservice.language) : "") ))(textservice); }, /* abstext(request)- absolute text request * @request absolute path text language appended - e.g. 'home.title.en' */ abstext: function (request) { return (new function( "return arguments[0].textdata." + request ))(textservice); }, /* reltext(request, cut) - scoped text request, search state heirarchy * @request relative path text without language appended - e.g. 'title' * @state state test textual data - defaults current state, used recursively */ reltext: function (request, state) { if(state === undefined) { // initial call function state = textservice.state } try { return textservice.text((state?state+".":"") + request)} catch(e) { if(!state) return "" // terminate avoid infinite recursion return textservice.reltext(request, state.split(".")).slice(0,-1).join("."); } }, /* statetext - request string in current state(e.g. statetext('title') * @request - relative path string in current state */ statetext: function (request) { return (textservice.state) ? textservice.text(textservice.state + "." + request) : ""; } } // register handler state changes $rootscope.$on("$statechangesuccess", function (event, tostate) { textservice.state = tostate.name; }); /* public api */ var textserviceapi = { /* bindtext - bind entire textual data new object * @textdata - text data object bound */ bindtext: function (textdata) { textservice.textdata = textdata; $rootscope.$broadcast("textdatachange") return textserviceapi; }, /* settext() - function set textual data , update text directives * @request request string, e.g. 'home.title', 'home.title.en' * @textdata textual data. literal string or object textual data * @doupdate boolean indicating whether update text directives. defaults false. * example usage 1: settext('home.title.en', "title") - set text string without update * example usage 2: settext('home.title', {en:"title", fr:"maison"}, true) * - set text object update page */ settext: function(request, textdata, doupdate) { (new function( "arguments[0].textdata." + request + "=arguments[1]" ))(textservice, textdata) if(!doupdate) $rootscope.$broadcast("textdatachange") return textserviceapi }, /* gettext() - function returning textual data * @request absolute reference text * example usage: gettext('home.title.en'), gettext('home.title') // returns text object */ gettext: function(request) { if(!request) return textservice.textdata else { return (new function( "return arguments[0].textdata." + request ))(textservice) } }, /* setlanguage() - set current language * @langauge - new language. e.g. "fr", "en" * @doupdate - boolean indicating whether update text directives, defaults true * example usage: setlanguage("fr") // change french , update page */ setlanguage: function (language, doupdate) { if(doupdate === undefined) doupdate = true; textservice.language = language $rootscope.$broadcast("languagechange") return textserviceapi; }, getlanguage: function () { return textservice.language; }, /* update() - requests text directives update */ update: function() { $rootscope.$broadcast("textdatachange") return textserviceapi }, /* used text directives */ text: textservice.text, abstext: textservice.abstext, reltext: textservice.reltext, statetext: textservice.statetext } return textserviceapi }) /* text directive */ .directive("text", function (textservice) { return { restrict: "a", link: function (scope, element, attrs) { function update() { element.html(textservice.text(attrs.text)) } scope.$on("languagechange", update) scope.$on("textdatachange", update) update() } } }) /* absolute text directive */ .directive("atext", function (textservice) { return { restrict: "a", link: function (scope, element, attrs) { function update() { element.html(textservice.abstext(attrs.atext)) } scope.$on("textdatachange", update) update() } } }) /* state text directive */ .directive("stext", function (textservice) { return { restrict: "a", link: function (scope, element, attrs) { function update() { element.html(textservice.statetext(attrs.stext)) } scope.$on("languagechange", update) scope.$on("textdatachange", update) scope.$on("$statechangesuccess", update) update() } } }) /* relative text directive */ .directive("rtext", function (textservice, $log) { return { restrict: "a", link: function (scope, element, attrs) { function update(event, request) { element.html(textservice.reltext(attrs.rtext)) } scope.$on("languagechange", update) scope.$on("textdatachange", update) scope.$on("$statechangesuccess", update) update() } } })
Comments
Post a Comment