Implement Internationalization in Adobe CQ

Internationalization can be achieved by using i18n bundle approach in adobe CQ or AEM(Adobe Experience Manager). Based on our use-case/requirement their are different approaches to implement internationalization in adobe cq.

Lets see both the approaches to implement internationalization in Adobe CQ –

Approach 1

If we have to print and value in js(JavaScript) file we can directly use granite api. For Ex:-

Granite.I18n.get("Letter created successfully.")

Approach 2

If we want to display certain text in different language based on URL,page language or region detected by browser. Then we should use internationalization feature of CQ.

Let’s understand how to implement it with an example:-

  • Create a i18n node under /apps/<myapp>/ of type sling:Folder.
  • Create a node with name as language code (fr) of type sling:Folder under i18n.
    • Add property jcr:language and assign value of lang code or lang_COUNTRY.
    • Add mixin type as mix:language.
      Note:- By adding mixin type we enforced that along with sling:folder this node has addition behavior for mix languages.

add mixin language type

  • Select Language Node (fr) –> Right click and create new node of type sling:messageEntry and add below properties on this node.
    • Name – sling:key              Type – String      Value – <our message in english>
    • Name – sling:message    Type – String Value – <our message in french>

add i18n sling key aem

Our directory structure would look similar to below structure where an example of key/value pairs for both English and French is shown:-

/apps/myapp/i18n
             +-- en (nt:folder, mix:language)
             |    +-- jcr:language = "en"
             |    +-- hello_world (sling:MessageEntry)
             |    |    +-- sling:key = "hello_world"
             |    |    +-- sling:message = "Hello world!"
             |    +-- goodbye (sling:MessageEntry)
             |         +-- sling:key - "goodbye"
             |         +-- sling:message = "Goodbye!"
             +-- fr (nt:folder, mix:language)
                  +-- jcr:language = "fr"
                  +-- hello_world (nt:unstructured,sling:Message)
                  |    +-- sling:key = "hello_world"
                  |    +-- sling:message = "Bonjour tout le monde!"
                  +-- goodbye (nt:unstructured,sling:Message)
                       +-- sling:key - "goodbye"
                       +-- sling:message = "Au revoir!"

Note:- If we create i18n at project level then it will be applicable for our entire project but if we require internationalization for specific component then we can create i18n node under our component

Now let’s write jsp code :

Detailed explanation of each method used in Below program.

When the <sling:defineObjects /> tag is used within a JSP page, the slingRequest value is created. The slingRequest implements the SlingHttpServletRequest interface and has couple of useful methods for internationalization: getLocale(), getLocales(), getResourceBundle(Locale), and getResourceBundle(String, Locale).

The getLocale() method gets the default Locale of the request. The getLocales() method gets all of the methods for a request. Typically the value for these come from the browser.

The method, getResourceBundle(Locale), gets a ResourceBundle instance that has all of the found key/value pairs for the locale. The method, getResourceBundle(String, Locale), gets a ResourceBundle with all of the key/value pairs for a base name and a locale.

The ResourceBundle interface has a method, getString(String), in which the argument is the key and the value of the key is returned. If the ResourceBundle does not have that key, the value returned is the key itself.

One thing to keep in mind is that a ResourceBundle is not the same thing as an OSGi bundle. They are two different concepts using the same name.

<%@page session="false"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@page import="java.util.Locale,java.util.ResourceBundle,com.day.cq.i18n.I18n"%>
<%@include file="/libs/foundation/global.jsp"%>
<cq:setContentBundle/>
<%
final Locale pageLocale = currentPage.getLanguage(false);
final ResourceBundle resourceBundle = slingRequest.getResourceBundle(pageLocale);
I18n i18n = new I18n(resourceBundle);
%>
<%= i18n.get("goodbye") %> 

That’s it. Now this jsp page will be rendered according to page language or region detected by browser.

You can also see the list of all available dictionary available in aem here:- http://localhost:4502/libs/cq/i18n/translator.html

Note:- You can see that your training dictionary is also added to it. First priority goes to project dictionary if no match found then it looks in global dictionary available in aem.

list of available dictionary aem

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  1. Hello Ankur

    We have one scenario like to display the metadata fields label and values in different language. Can we implement the i18n on Assets metadata fields and values as well

    Thanks
    Samer

    • You can display i18n values using a local template in sightly

      <template data-sly-template.i18="${@ key}">
       ${key @ i18n}
      </template>
      
      <div data-sly-call="${i18 @ key='key-name'}"></div>

      Note:- Here i18n is out object name you can use any name that you want

      • Hi Ankur,

        I have multiple dictionaries under “/etc/project_name/i18n”(e.g “/etc/project_name/i18n/default” and “/etc/project_name/i18n/overlay”).
        My requirement is picking up the values first from overlay path and if the overlay dictionary is empty, select the default path.
        With the usage of internationalization in sightly, I am always getting the key values from “default” dictionary first. Is there a way to change this order of preference?

        Thankyou

  2. When using Approach 1 and apply what described there to internationalize error messages in a form it works fine, but not when there’s a dispatcher between you and the aem publish.
    What’s the problem with dispatcher? Is there some config to add for the locale in the js?

  3. Hello Ankur,
    I’ve tried to use the approach 1 Granite.I18n.setLocale(“fr”) inside a Javascript file, but I get an error
    500 ReferenceError: “Granite” is not defined.

    Maybe I have to import something? There is a file called I18n.js inside etc/clientlibs/granite/utils, but I couldn’t import those files (maybe I just did it the wrong way).

    Thanks for your work

  4. Hi, I am confused what should we use internationalization or translation service provider for multilingual website?

  5. Hi Ankur , I am using AEM6.2 and I followed all the steps mentioned above by you correctly but I was not able to implement the internationalization feature in AEM. Later I understood the folder type for i18n and its child folders i.e language should be of the type nt:folder & not sling:folder. Now am able test the internationalization feature as per the above steps mentioned . I hope this is the right way to implement and it will help other new beginners , please suggest.

  6. Thanks alot!!! ,it worked for me, a quick question is there any feature in aem in which entire components in aem page are translated to a particular language automatically without manual eforts.