How to Get AEM i18n Dictionary in JSON Format

AEM i18n dictionary out of the box tools does not offer an elegant way to get the JSON representation of targeted the i18n dictionary. Without out of the box AEM i18n dictionary JSON formatting tools, we first solutionize to what we know the most.

As of AEM developers, we all know that we can trigger the JSON default rendering by appending a .json extension to a request, which triggers the default Sling GET servlet returning application/json. And of course, we all know about using the infinity selector in combination with the .json extension, which recursively returns the entire JCR structure in JSON format. Our first attempt is likely to attempt to get the formatted JSON from .infinity.json.

Illustrated below, on the left hand side you can see a structured AEM i18n dictionary from the out of the box commerce components library (these nodes should exist in a clean installation of AEM 6.4+). On the right hand side, you can see the attempt to get a JSON format of this structure, using the selector, “infinity”, and the extension, “json”, http://localhost:4502/libs/commerce/components/search/i18n.infinity.json.

Out of the box i18n JCR structure for commerce/search infinity JSON structure of the i18n dictionary
As you can see, the default Sling GET servlet’s JSON response returns a lot of extra unwanted properties. These unwanted properties will increase file size of the outputted JSON file, which will impact load time, which will ultimately impact the overall performance of your website. Also all the unwanted JSON properties will add complexity to consumers, wether it’s a front-end or to a third-party.

In this article, I will share with you a way that I go forward with getting a JSON representation of an AEM i18n dictionary, without the unwanted properties. The solution will make the JSON response from the above AEM i18n dictionary structure, return JSON in this format:

1
2
3
4
5
6
{
  sort: "Sort by:",
  firstPage: "First",
  timelessStatisticsText: "Results {0} - {1} of {2} for <b>{3}</b>.",
  lastPage: "Last",
}

Custom servlet to format all AEM i18N structured dictionaries

That’s right. In most of my AEM project implementations, I would introduce a servlet that can be used for all AEM i18N structured dictionaries. It’s very simple.

Requesting on http://localhost:4502/libs/commerce/components/search/i18n.jsonform.en or http://localhost:4502/libs/commerce/components/search/i18n.jsonform.de will return me a JSON formatted AEM i18n dictionary by language, and the JSON response will only include the dictionary keys.

Servlet Request: http://localhost:4502/libs/commerce/components/search/i18n.jsonform.en
Selector: jsonform
Extension: en|fr|de|kr
The extension is the target language that you want to receive. For example, if the extension was set for “de”, then the JCR searched would be /libs/commerce/components/search/i18n/de.

Here is the implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.sourcedcode.core.servlets;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.servlet.Servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import static org.apache.jackrabbit.JcrConstants.NT_FOLDER;
import static org.apache.sling.api.servlets.HttpConstants.METHOD_GET;
import static org.apache.sling.jcr.resource.api.JcrResourceConstants.NT_SLING_FOLDER;

@Component(service = Servlet.class, configurationPolicy = ConfigurationPolicy.OPTIONAL)
@SlingServletResourceTypes(
        resourceTypes = { NT_FOLDER, NT_SLING_FOLDER},
        selectors = "jsonform",
        methods = METHOD_GET)
public class JsonDictionaryFormatServlet extends SlingSafeMethodsServlet {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonDictionaryFormatServlet.class);
   
    @Activate
    protected void activate(Map<String, Object> properties) {
        LOGGER.info("Servlet activated");
    }

    @Deactivate
    protected void deactivate() {
        LOGGER.info("Servlet deactivated");
    }

    @Override
    protected void doGet(SlingHttpServletRequest req, SlingHttpServletResponse res) throws IOException {
        try {
            jsonFormatDictionary(req, res);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    private void jsonFormatDictionary(SlingHttpServletRequest req, SlingHttpServletResponse res) throws RepositoryException, IOException, JSONException {  
        String language = req.getRequestPathInfo().getExtension();
        PrintWriter writer = res.getWriter();
        if (language != null) {
            Node rootLanguageNode = req.getResource().adaptTo(Node.class);
            if (rootLanguageNode.hasNode("i18n")) {
                rootLanguageNode = rootLanguageNode.getNode("i18n");
            }
            Node languageNode = rootLanguageNode.getNode(language);
            if (languageNode != null) {
                res.setContentType("application/json; charset=UTF-8");
                res.setCharacterEncoding("UTF-8");
                JSONObject rootObject = new JSONObject();
                NodeIterator iterator = languageNode.getNodes();
                while (iterator.hasNext()) {
                    Node languageItem = (Node) iterator.next();
                    String key = languageItem.getName();
                    String value = languageItem.getProperty("sling:message").getString();
                    rootObject.put(key, value);
                }
                writer.write(rootObject.toString());
            } else {
                LOGGER.error("{} language is not exist", language);
            }
        } else {
            LOGGER.error("no language have been found in the extension");
        }
    }
}

HTTP Get Request:
http://localhost:4502/libs/commerce/components/search/i18n.jsonform.en

JSON output:

1
2
3
4
5
6
{
  sort: "Sort by:",
  firstPage: "First",
  timelessStatisticsText: "Results {0} - {1} of {2} for <b>{3}</b>.",
  lastPage: "Last",
}
An example of an i18n served to production-live would be a path that looks like this,
/etc/commerce/components/search/i18n.jsonform.en

Only because, /apps and /libs are reserved areas, which means for security purposes, no one should have access to these areas. Using the /etc path to access dictionaries are fine.


Hello, I am an enthusiastic Adobe Community Advisor and a seasoned Lead AEM Developer. I am currently serving as an AEM Technical Lead at MNPDigital.ca, bringing over a decade of extensive web engineering experience and more than eight years of practical AEM experience to the table. My goal is to give back to the AEM Full Stack Development community by sharing my wealth of knowledge with others. You can connect with me on LinkedIn.

6 thoughts on “How to Get AEM i18n Dictionary in JSON Format

Leave a Reply

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


Back To Top