i18n in javascript code

92 views
Skip to first unread message

Joe Barnes

unread,
Jan 16, 2014, 7:11:42 PM1/16/14
to lif...@googlegroups.com
I'm looking for some good ideas on how to provide i18n translations of my application's strings that are needed by the client-side javascript code.  I'd like to only have to maintain the java-based resource bundle files that Lift already utilizes.  Ideally, I could just inject a JS representation of the appropriate resource bundle into my page via a snippet.  I can certainly write one, but I thought I'd check to see if anyone was already doing this.  BTW, my client code utilizes angular.

Thanks,
Joe

Antonio Salazar Cardozo

unread,
Jan 16, 2014, 8:11:56 PM1/16/14
to lif...@googlegroups.com
I think there are .resources file readers for JS out there, so you could just serve up the appropriate file to the client.

Personally, we avoid doing i18n on the client side and instead serve up already-i18n-ed content via JSON or CSS selector transforms, depending.
Thanks,
Antonio

Andreas Joseph Krogh

unread,
Jan 16, 2014, 11:03:34 PM1/16/14
to lif...@googlegroups.com
På fredag 17. januar 2014 kl. 02:11:56, skrev Antonio Salazar Cardozo <savedf...@gmail.com>:
I think there are .resources file readers for JS out there, so you could just serve up the appropriate file to the client.
 
Personally, we avoid doing i18n on the client side and instead serve up already-i18n-ed content via JSON or CSS selector transforms, depending.
Thanks,
Antonio
 
+1
 
We use Enums (custom Scala-enum) instead of strings too so we only have to change one place if the key changes. This enum knows how to translate itself according to a Local set in LocaleContextHolder (provided by Spring), so we can call MyTexts.stuff_someKey.localize (or MyTexts.localize(args*)) and have "stuff_someKey=Translated text here" in a property-file which the MyTexts knows about. This way we can have multiple i18n files and use them in a type-safe way.
 
--
Andreas Joseph Krogh <and...@officenet.no>      mob: +47 909 56 963
Senior Software Developer / CTO - OfficeNet AS - http://www.officenet.no
Public key: http://home.officenet.no/~andreak/public_key.asc
 

Antonio Salazar Cardozo

unread,
Jan 16, 2014, 11:15:31 PM1/16/14
to lif...@googlegroups.com
For what it's worth, I also consider properties-file-based i18n pretty broken. It's not designed with any of the complexities of translation (e.g., pluralization) in mind. But I haven't gotten time to look into the best way of integrating gettext with Lift yet.
Thanks,
Antonio

Andreas Joseph Krogh

unread,
Jan 17, 2014, 5:30:40 AM1/17/14
to lif...@googlegroups.com
På fredag 17. januar 2014 kl. 05:15:31, skrev Antonio Salazar Cardozo <savedf...@gmail.com>:
For what it's worth, I also consider properties-file-based i18n pretty broken. It's not designed with any of the complexities of translation (e.g., pluralization) in mind. But I haven't gotten time to look into the best way of integrating gettext with Lift yet.
Thanks,
Antonio
 
Do yoy mean Lift's i18 or JAVA's? JAVA's most certainly handles pluralization. That's why our i18n-enums use JAVA's 18n mechanism and not Lifts. Can you give an example of how JAVA's i18n isn't complete enough?

Joe Barnes

unread,
Jan 17, 2014, 9:52:18 AM1/17/14
to lif...@googlegroups.com
Thus far I had only found this jQuery plugin.  I might use it but man... I really hate jQuery.  I know it doesn't mean I'll have to use it, but just means that it's hanging around. :)

Joe

Andreas Joseph Krogh

unread,
Jan 17, 2014, 10:25:01 AM1/17/14
to lif...@googlegroups.com
På fredag 17. januar 2014 kl. 15:52:18, skrev Joe Barnes <barn...@gmail.com>:
Thus far I had only found this jQuery plugin.  I might use it but man... I really hate jQuery.  I know it doesn't mean I'll have to use it, but just means that it's hanging around. :)
 
I whould strongly recommend to do i18n serverside and keep it type-safe. Spreading i18n strings around quickly becomes a mess and maintenance-nightmare, especially if you include i18n in JS.

Antonio Salazar Cardozo

unread,
Jan 17, 2014, 3:16:03 PM1/17/14
to lif...@googlegroups.com
On Friday, January 17, 2014 5:30:40 AM UTC-5, andreak wrote:
På fredag 17. januar 2014 kl. 05:15:31, skrev Antonio Salazar Cardozo <savedf...@gmail.com>:
For what it's worth, I also consider properties-file-based i18n pretty broken. It's not designed with any of the complexities of translation (e.g., pluralization) in mind. But I haven't gotten time to look into the best way of integrating gettext with Lift yet.
Thanks,
Antonio
 
Do yoy mean Lift's i18 or JAVA's? JAVA's most certainly handles pluralization. That's why our i18n-enums use JAVA's 18n mechanism and not Lifts. Can you give an example of how JAVA's i18n isn't complete enough?

Lift's; when I say gettext in this case, I'm referring to gettext style, which Java does seem to support. I haven't investigated the issue in depth yet. It'd be cool if you could post a brief description with some sample code of how you guys do i18n, it sounds interesting/useful (or if you've already done it, a link to it ;) ).
Thanks,
Antonio

Joe Barnes

unread,
Jan 17, 2014, 9:33:22 PM1/17/14
to lif...@googlegroups.com
I see the points you both are making regarding doing the localization on the server.  I'm still pondering over how I can do it exactly.  Just to give troublesome case... I have an alert box that shows when communication to the server appears to be failing.  In that case I obviously can't fetch the localized string from the server on demand.  The alert is in JS, so I have to have somewhere that string has been given to me for that locale.  What I had in mind would involve sending any of the strings up to the client as a JSON object... perhaps only containing the simple strings which require no parameters.

Joe



--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
 
---
You received this message because you are subscribed to a topic in the Google Groups "Lift" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/liftweb/8w6lGzifOpo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Antonio Salazar Cardozo

unread,
Jan 18, 2014, 11:33:06 AM1/18/14
to lif...@googlegroups.com
The easiest way to deal with that is to attach common errors of that sort to the DOM at render time. For example, you can attach a data-server-error attribute to the body tag that contains the i18ned server error string.

But even if you're serving it up via JSON, you're fine, because you can do the i18n server-side—wasn't clear on whether that's what you were already saying.
Thanks,
Antonio

Andreas Joseph Krogh

unread,
Jan 18, 2014, 7:32:58 PM1/18/14
to lif...@googlegroups.com
I have a sample-project here: https://github.com/andreak/on-example-rpm
I try to keep it up to date with the stuff we use in Lift/Scala
 
Note: We use Spring, and hence LocaleContextHolder to track the locale, but one coud use whatever appropriate...
 
Key parts of the i18n-support is:
 
UrlLocalizer (to grab the locale from the URI):
 
Which is installed in Boot.scala:
        LiftRules.localeCalculator = UrlLocalizer.calcLocale
 
 
ResourceBundleHelper
 
Localizer
 
ResourceBundleEnum
 
This makes us able to defined i18n-enums such as:
 
ProjectTexts:
 
Thee key issue with these enums is that the enum-field must match the i18n-key in the property-file. It's also very easi to write a test to ensure the keys in the enum matches what's in the properties-file, see f.ex. ProjectTextsTest
 
Which lets us use it like this:
".name *" #> ProjectTexts.D.description.localize
 
And voila, such enums are used all over so I can track them easily in my IDE and safely rename them etc.
 
Hope this helps and please comment if you have any thoughts regardiing our solution.

Antonio Salazar Cardozo

unread,
Jan 18, 2014, 7:36:40 PM1/18/14
to lif...@googlegroups.com
Very interesting, I'll have a look. Thanks for sharing!
Antonio

Joe Barnes

unread,
Jan 18, 2014, 9:11:51 PM1/18/14
to lif...@googlegroups.com
Thanks for sharing, Andreas.  If I have understood correctly, you are manually creating the enums to match the properties file, correct?  Have you considered generating the enums from the properties file?  This is basically what I had in mind in my original post, except that it would be JS which is generated.

Do you guys think this idea I have will be useful to others, or is a good idea at all? :)  I just want to be able to take the properties defined in the current locale's resource bundle and toss them up as JS to the client.  Everything will be done server side, and the client will be given a dictionary to utilize when it makes sense.  I even have in mind to do some of the easier cases of substitution.  

Joe


--

Andreas Joseph Krogh

unread,
Jan 19, 2014, 5:09:01 AM1/19/14
to lif...@googlegroups.com
På søndag 19. januar 2014 kl. 03:11:51, skrev Joe Barnes <barn...@gmail.com>:
Thanks for sharing, Andreas.  If I have understood correctly, you are manually creating the enums to match the properties file, correct?  Have you considered generating the enums from the properties file?  This is basically what I had in mind in my original post, except that it would be JS which is generated.
 
Do you guys think this idea I have will be useful to others, or is a good idea at all? :)  I just want to be able to take the properties defined in the current locale's resource bundle and toss them up as JS to the client.  Everything will be done server side, and the client will be given a dictionary to utilize when it makes sense.  I even have in mind to do some of the easier cases of substitution. 
 
I don't know how that would be possible. We use "containers" for similar enums, like f.ex. ProjectTexts. It's a singleton holding two other singletons, like this:
 
 


object ProjectTexts {
        object D extends ResourceBundleEnum {
                val
                name,
                isActive
                = BundleEnum(Bundle.PROJECT_D)
        }
        object V extends ResourceBundleEnum {
                val projectDialog_details_label_budget
                , projectDialog_title_newProject
                = BundleEnum(Bundle.PROJECT_V)
        }
}
 
And we have a convention for using "D" for "domain-object texts" (like project's name, description etc) and "V" for "view texts", that is i18n-strings used in the view on f.ex. ProjectViewSnippet. Bundle.PROJECT_D and Bundle.PROJECT_V holds references to "projectDomainResources_<locale>.properties" and "projectViewResources_<locale>.properties" respectively.
Reply all
Reply to author
Forward
0 new messages