230 likes | 402 Views
Internationalization and the Java Stack Part 2. Matt Wheeler. Notes. This is a training NOT a presentation Please ask questions Prerequisites Introduction to Java Stack Introduction to Spring Basic Java and XML skills Internationalization and the Java Stack Part 1
E N D
Internationalization and the Java Stack Part 2 Matt Wheeler
Notes • This is a training NOT a presentation • Please ask questions • Prerequisites • Introduction to Java Stack • Introduction to Spring • Basic Java and XML skills • Internationalization and the Java Stack Part 1 • Installed LdsTech IDE (or other equivalent)
Overview • Stack provided internationalization tools • Message source expression language resolver • JS Message Source • RequireJS Message Source • Internationalization best practices • Internationalization testing • Dynamic pseudo translation
Expression Language Resolver • Provided message resolver can be utilized in EL • http://code.lds.org/maven-sites/stack/module.html?module=web-spring/#El_Message_Source <%@ taglib prefix="web-spring" uri="http://code.lds.org/web/spring" %> <!-- This makes the internationalized properties of the default MessageSource available as a map in the specified scope--> <web-spring:message-source var="messages" scope="request"/> <!- Reference the messages in EL by key --> ${messages['abc.def.ghi']}
JavaScript MessageSource • Configuration <bean id="messageSource"class="org.lds.stack.web.spring.i18n.message.EnumeratableMessageSourceImpl"> <property name="basenames"> <list> <value>classpath:messages</value> </list> </property> </bean> <bean id="messageSourceController" class="org.lds.stack.web.spring.i18n.MessageSourceController"> <property name="messageSource" ref="messageSource" /> </bean> <!-- Create adapter to handle all non-annotation controllers --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!-- Map the properties url to the controller --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/messageSource.js">messageSourceController</prop> </props> </property> <property name="order" value="#{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE}"/> </bean>
MessageSource with JS • Usage in a JSP page <script charset="utf-8" src="${pageContext['request'].contextPath}/messageSource.js" type="text/javascript"></script> <!– This effectively writes out the following variable in JavaScript containing key value pairs for everything in the MessageSource, for use as follows --> varmsgs= { "index.welcome":"Welcome", "accessDenied.accessdenied":"Access Denied", "def":"{0} asdfaasdfasd {1} asdfasdasdf ", "index.mainpage":"Main page - {0}“ }; <script type="text/javascript"> alert(msgs['index.welcome']); </script>
RequireJSMessageSource • Configuration <bean id="messageSource"class="org.lds.stack.web.spring.i18n.EnumeratableMessageSourceImpl"> <property name="basenames"> <list> <value>classpath:messages</value> </list> </property> </bean> <bean id="messageSourceController" class="org.lds.stack.web.spring.i18n.RequireJsMessageSourceController"> <property name="messageSource" ref="messageSource"/> <property name="replaceDotsInKeys" value="true"/> </bean> <!-- Create adapter to handle all non-annotation controllers --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> <!-- Map the properties url to the controller --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="urlMap"> <map> <entry key="/js/nls/messages.js" value-ref="messageSourceController"/> <entry key="/js/nls/*/messages.js" value-ref="messageSourceController"/> </map> </property> <property name="order" value="1"/> </bean>
MessageSource with RequireJS • Usage define([ 'tmpl!example/templates/list', 'i18n!nls/messages' ], function (listTemplate, messages) { alert(messages['createExample_loading']); }; <!– This will result in loading the following which can then be used in the page. --> <script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data- requiremodule="nls/messages" src="js/nls/messages.js"> define({"root": { "createExample_loading":"Carga…", "createExample_form_label_data":"Datos:", "createExample_form_label_name":"Nombre:", "createExample_form_message_success":"Ejemplo \"<%= name %>\" se ha …" "createExample_form_button_save":"Cree Ejemplo“ } }); </script>
Some I18n Best Practices • Externalize ALL translatable text • Do not concatenate translations • Create duplicate / separate resources • Avoid text in images • Introduce pseudo-translation tool
Externalize ALL Text • Example – Name: • Notice that we have concatenated the : with the translated name label • "Name:" including the colon should be externalized • Concatenation itself is a best practice violation • : and associated formatting can be different • It might also be in a different order #messages.properties name.label=Name #JSP page ${messages['name.label']}: <input type="text" /> #messages.properties name.label=Name: #JSP page ${messages['name.label']} <input type="text" />
Do NOT Concatenate Translations • For example: • Attempted usage: #messages.properties key1=Someone named key2=likes hiking. #JSP Page ${messages['key1']} Billy ${messages['key2']}
More Correct Way • Create a single string with replaceable parameters • Sentence maintains context • Parameters can be moved around to accommodate language grammar • For example: • And then it will be used as follows: key=Someone named {0} likes hiking. <spring:message code="key" arguments="${hikersName}" text="Default text."/>
Create Separate Resources • Counter-intuitive to code reuse concept • Same English strings should not be shared • Within the same application, or even the same page • Create a separate key value pair for each occurrence • Words change based on context • Concept of masculine and/or feminine, age classes, … • Or usage • Similar English words may not be similar in another language
Avoid Placing Text in Images • An image that contains text will require a new image for each language • It will also require a custom way to load the image • Impose the text over the image, using .css or other ingenious alternative • Then the text can be stored in the resource bundles with all other strings • And it won’t require a new image for each language
I18n Testing (Dynamic Pseudo Translation) • Configured to translate for specified locales • Simplifies testing • Expansion • Special characters • Completeness • zz locale • http://code.lds.org/maven-sites/stack/module.html?module=web-spring/#Pseudo_Translation_Message_Source_Facade
Dynamic Pseudo Translation • The trick (delegate) <!-- Application Message Bundle --> <bean id="messageSource" class="org.lds.stack.web.spring.i18n.message.PseudoMessageSourceFacade"> <constructor-arg ref="delegatingMessageSource" /> </bean> <bean id="delegatingMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename"> <list> <value>classpath:messages</value> </list> </property> <property name="defaultEncoding" value="UTF-8"/> </bean>
Credit where credit is due • http://wiki.answers.com/Q/5_words_that_have_multiple_meanings • http://en.wikipedia.org/wiki/Homonym
Lab 1: Internationalize a page https://tech.lds.org/wiki/Internationalization_and_the_Java_Stack_-_Part_2#Lab_1_Pseudo_Translator