In this tutorial, you'll localize the user interface of a Zendesk Support app. The app displays "Hello, World!" in different languages based on the user's locale and available translations. You can use the app's code as a starting point for localizing your own apps.

Tip: The app in this tutorial uses basic app starter files. If your app uses React, we recommend using the i18n module in the React app scaffold instead. For details, see Using the i18n module on GitHub.

Disclaimer: Zendesk provides this article for instructional purposes only. Zendesk doesn't provide support for the app or the example code in this tutorial.

What you'll need

To complete this tutorial, you'll need the following:

Creating the app files

First, create basic starter files for an app named i18n.

  1. In your shell, navigate to the folder where you want to store the app. Example:

    cd projects
  2. In the folder, run:

    zcli apps:new
  3. At the prompts, enter the following values:

    • Directory name: i18n
    • Author's name: Your name
    • Author's email: Your email address
    • Author's website: Leave blank and press Enter.
    • App name: i18n

    ZCLI creates app starter files in the i18n folder.

Adding translation strings

The starter files include an en.json file in the app's translations folder. You can add translation strings to JSON files in this folder.

  1. In the app's translations folder, open en.json in your text editor.

  2. Add the following default property:

    {  "app": {    ...  },  "default": {    "hello_world": "Hello, World!"  }}

    Note: Don't add translation strings to the app object. The app object contains content for the app listing on the Zendesk Marketplace.

  3. In the translations folder, create a fr.json file. Paste the following into the file:

    {  "default": {    "hello_world": "Bonjour le Monde!"  }}
  4. Save en.json and fr.json.

Loading translation strings

A Zendesk app's iframe can only access local files in the assets folder. Create a script to output the translation strings to a JavaScript file in this folder.

  1. In the app's root folder, create a load-translations.js file. Paste the following into the file:

    const fs = require("fs");const path = require("path");
    const translations = getTranslationsFromDir("./translations");writeTranslationsToFile(translations);
    function getTranslationsFromDir(translationsDir) {  const jsonFiles = fs    .readdirSync(translationsDir)    .filter((file) => path.extname(file) === ".json");
      let translations = {};
      jsonFiles.forEach((file) => {    const fileData = fs.readFileSync(path.join(translationsDir, file));    const filename = file.replace(".json", "").toLowerCase();    const json = JSON.parse(fileData.toString());    translations[filename] = json;  });
      return translations;}
    function writeTranslationsToFile(translations) {  const translationsString = JSON.stringify(translations);  const translationsObj = `const translations = ${translationsString};`;  const translationsFile = "./assets/translation-strings.js";
      fs.writeFileSync(translationsFile, translationsObj);  console.log(`🚀 Translations written to ${translationsFile}`);}

    The code fetches translation strings from JSON files in the translations folder. The code adds the strings to a translations object. It then writes the object to a translation-strings.js file in the assets folder.

  2. In your shell, navigate the app's root folder. Example:

    cd project/i18n
  3. Use the following command to run the script:

    node load-translations.js

    The script creates a translation-strings.js file in the app's assets folder. To update the file, rerun the command.

    Important: If you add or edit any translation strings in the translations folder, you need to rerun the script before using the strings in your app.

Localizing the app interface

Next, add code to insert a translation string into the app's user interface based on the current user's locale in Zendesk.

  1. In the app's assets folder, open iframe.html. Replace the file's contents with the following:

    <!DOCTYPE html><html>  <head>    <meta charset="utf-8" />    <link      rel="stylesheet"      href="https://cdn.jsdelivr.net/combine/npm/@zendeskgarden/[email protected],npm/@zendeskgarden/[email protected]"    />  </head>  <body>    <h2 data-i18n-key="hello_world"></h2>    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>    <script src="translation-strings.js"></script>    <script src="i18n.js"></script>  </body></html>

    The HTML contains an empty H2 heading. The heading has a data-i18n-key attribute of "hello_world". Later, you'll uses this attribute to insert a translation string.

    The HTML also loads the translation-strings.js and i18n.js scripts. You'll create i18n.js in the next step.

  2. In the assets folder, create an i18n.js file. Paste the following into the file:

    (function () {  const client = ZAFClient.init();
      client.get("currentUser.locale").then((data) => {    let userLocale = data["currentUser.locale"].toLowerCase();    const locale = getSupportedLocale(userLocale);
        document.querySelectorAll("[data-i18n-key]").forEach((element) => {      translateElement(element, locale);    });  });})();
    function getSupportedLocale(userLocale) {  // Check if translations exist for locale  // If not, return a locale of "en"  let supportedLocales = Object.keys(translations).map((item) => {    return item.toLowerCase();  });
      if (supportedLocales.includes(userLocale)) {    return userLocale;  } else {    return "en";  }}
    function translateElement(element, locale) {  const key = element.getAttribute("data-i18n-key");  const translation = translations[locale]["default"][key];  element.innerText = translation;}

    The code uses the ZAF client's get() method to get the current user's locale in Zendesk. It then checks whether the locale exists in the translation-strings.js file's translations object. If not, the code uses the en locale instead.

    The code replaces the text of HTML elements with a data-i18n-key attribute with a corresponding string from the translations object. For example, for a user with an "fr" (French) locale, an element with a data-i18n-key of "hello_world" uses the translations.fr.default.hello_world string.

Testing the app

Change your locale in Zendesk to verify the app works as intended.

  1. Use the following Show Self request to get your user id and locale.

    curl https://{subdomain}.zendesk.com/api/v2/users/me.json \  -u {email_address}/token:{api_token}

    Save the user.id and user.locale from the response. You'll use these later in the tutorial.

    {  "user": {    "id": 1905826600027,    ...    "locale": "en-US",    ...  }}
  2. Use the following Update User request to change your locale to en. Replace {user_id} with your user id from step 1.

    curl -X PUT https://{subdomain}.zendesk.com/api/v2/users/{user_id}.json \  -u {email_address}/token:{api_token} \  -H "Content-Type: application/json" \  -d '{  "user": {    "locale": "en"    }  }'
  3. In your shell, navigate to the i18n folder. Example:

    cd projects/i18n
  4. Start a local ZCLI web server by running the following command:

    zcli apps:server

    After a moment, a status message appears informing you that the server has started.

    Note: To stop the server, press Ctrl+C.

  5. In your browser's private or incognito window, sign in to Zendesk Support and go to the Agent Workspace. From the workspace, open a new or existing ticket.

    The URL should look like this:

    https://{subdomain}.zendesk.com/agent/tickets/{ticket_id}

  6. Append ?zcli_apps=true to the URL and press Enter. The URL should now look like this:

    https://{subdomain}.zendesk.com/agent/tickets/{ticket_id}?zcli_apps=true

  7. Click the Apps icon.

    The app displays a "Hello, World!" heading.

  8. In your shell, use the following Update User request to update your locale to fr. Replace {user_id} with your user id from step 1.

    curl -X PUT https://{subdomain}.zendesk.com/api/v2/users/{user_id}.json \  -u {email_address}/token:{api_token} \  -H "Content-Type: application/json" \  -d '{  "user": {    "locale": "fr"    }  }'
  9. In your browser, reload the Agent Workspace page. The app now displays a localized French heading.

  10. Use the following Update User request to reset your locale to en. Replace {user_id} and "LOCALE" with your user id and locale from step 1.

    curl -X PUT https://{subdomain}.zendesk.com/api/v2/users/{user_id}.json \  -u {email_address}/token:{api_token} \  -H "Content-Type: application/json" \  -d '{  "user": {    "locale": "LOCALE"    }  }'

Adding a new locale

To add a new locale to the app, add a corresponding JSON file to the translations folder. The file name must be a BCP-47 compliant tag for a language. For a list of supported language tags, see the supported language for Zendesk Support in Zendesk help.

Use the following steps to add the "it" (Italian) locale.

  1. In the translations folder, create an it.json file. Paste the following into the file:

    {  "default": {    "hello_world": "Ciao mondo!"  }}
  2. Save it.json.

  3. In your shell, navigate the app's root folder. Example:

    cd project/i18n
  4. To update the translation strings in the app, rerun the load-translations.js script:

    node load-translations.js
  5. Repeat the steps in Testing the app using the "it" locale instead of the "fr" locale.

    The app displays a localized Italian heading for the "it" locale.

Adding a new translation string

To add a new translation string, add a new element with the data-i18n-key attribute to iframe.html. Then add a corresponding translation string to the default object of each JSON file in the translations folder. The property's key must be the data-i18n-key attribute's value.

  1. In iframe.html, add the following button:

    ...  <body>    <h2 data-i18n-key="hello_world"></h2>    <button data-i18n-key="submit"></button>    <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>    ...  </body></html>

    The button has a data-i18n-key value of "submit".

  2. In the translations folder, add a submit property to the default object of each JSON file.

    en.json:

    {  "default": {    "hello_world": "Hello, World!",    "submit": "Submit"  }}

    fr.json:

    {  "default": {    "hello_world": "Bonjour le Monde!",    "submit": "Envoyer"  }}

    it.json:

    {  "default": {    "hello_world": "Ciao mondo!",    "submit": "Invia"  }}
  3. Save iframe.html, en.json, fr.json, and it.json.

  4. In your shell, navigate the app's root folder. Example:

    cd project/i18n
  5. To update the translation strings in the app, rerun the load-translations.js script:

    node load-translations.js
  6. To test the translation string, repeat the steps in Testing the app with the "en", "fr", and "it" locales.

    "en" locale:

    "fr" locale:

    "it" locale:

Congratulations! You've localized a Zendesk app. If wanted, you can extend the app by adding more locales and translation strings.