DEV Community

Aastha Talwaria
Aastha Talwaria

Posted on

3 2

i18n in STRAPI

As per wikipedia

Internationalisation and Localisation, often abbreviated i18n and L10n, is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes.

Let's take an example use-case; so that we both are on the same page.

  • We have one collection named home-page having the following fields. ( My use-case was to provide support for 4-5 languages, may require more in future).

collection

where banner(single component) and brand(repeatable component) are components.
Fields description:

  • title     - home-page having three fields title, title_ar, title_ru, values of title English(en), Arabic(ar) and Russian(ru) language repectively.
  • Banner and Brand     - title in banner and name in the brand are language-dependent and the rest of the fields are the same for all languages.     - And, when selected languageCode is ar, my frontend will need title = titile_ar in response for banner and name = name_ar in response for brand.

My Approach ( didn't use any plugin ):

  • Made a file specifying the language dependent fields.

    This is generated automatically (based on the collection) when server starts.
    projec_dir/constants/i18n.config.json

    {
      "home-page": [
        "title"
      ],
      "component.banner-component": [
        "title"
      ],
      "component.brand-component": [
        "name"
      ]
    }
    
  • get data from the model using find, findOne or any other customised service.

  • fetch schema of the model (required for the name of the attribute to get language dependent fields from the JSON imported from project-dir/constants/i18n.config.json)

  • replace ${field} with ${field}${languageCode}.

  • delete ${field}${languageCode}.

Code:

  • project_dir/constants/i18n.constants.js

    const i18nFields = require('./i18n.config.json');
    const DEFAULT_LANGUAGE = 'en';
    const LANGUAGE_LIST = ['en', 'ar', 'ka', 'ru', 'uzb'];
    const I_18_FIELDS = i18nFields;
    const I18N_LIST = ['home-page'];
    
    module.exports = {
      DEFAULT_LANGUAGE,
      LANGUAGE_LIST,
      I_18_FIELDS,
      I18N_LIST,
    };
    
    
  • project_dir/utils/i18n.utils.js

     const _ = require('lodash');
    const {
      LANGUAGE_LIST,
      DEFAULT_LANGUAGE,
    } = require('../constants/i18n.constants');
    
    function internationalizeFields(components, langCode, fields) {
      if (!components) {
        return undefined;
      }
      const i18Component = { ...components };
      if (fields) {
        fields.forEach((field) => {
          const currentLanguageFieldValue = _.get(
            i18Component,
            `${field}_${langCode}`
          );
          if (langCode !== DEFAULT_LANGUAGE) {
            _.set(i18Component, field, currentLanguageFieldValue);
          }
          LANGUAGE_LIST.forEach((lang) =>
            _.unset(i18Component, `${field}_${lang}`)
          );
        });
      }
      return i18Component;
    }
    module.exports = {
      internationalizeFields,
    };
    
    • project_dir/utils/strapi.util.js
    /* global strapi */
    
    function getAttributes({ modelName }) {
      try {
        return strapi.api[modelName].models[modelName].__schema__.attributes;
      } catch (err) {
        //throw error
      }
    }
    function addComponentSchema({ parentSchema }) {
      const result = Object.keys(parentSchema).reduce((acc, curr) => {
        acc[curr] = parentSchema[curr];
        if (parentSchema[curr].type !== 'component') {
          return acc;
        }
        const component = strapi.components[acc[curr].component];
        if (!component) {
          throw error
        }
        acc[curr].schema = component.attributes;
        acc[curr].schema = addComponentSchema({ parentSchema: acc[curr].schema });
        return acc;
      }, {});
      if (!result || Object.keys(result).length === 0) {
        //through error
      }
      return result;
    }
    
    module.exports = {
      getAttributes,
      addComponentSchema,
    };
    
    
    • project_dir/utils/controller.util.js
    
    const { DEFAULT_LANGUAGE } = require('../common/constants/i18n.constants');
    const { I_18_FIELDS } = require('../common/constants/i18n.constants');
    const { internationalizeFields } = require('../common/utils/i18n.utils');
    const { getAttributes, addComponentSchema } = require('./strapi.utils');
    
    /**
     * @param {*} modelName modelName for keys in I_18_FIELDS
     * @param {*} data data to be internationlised
     * @param {*} schema schema corresponding to modelName
     * @param {*} lang
     * @returns internationalised data
     */
    async function internationalizeData({
      modelName,
      data,
      schema,
      lang = DEFAULT_LANGUAGE,
    }) {
      if (!data) {
        return data;
      }
      if (!schema) {
        const modelSchema = getAttributes({ modelName });
        schema = addComponentSchema({ parentSchema: modelSchema });
      }
      const schemaKeys = Object.keys(schema);
      if (!schemaKeys.length) {
        return data;
      }
      if (Array.isArray(data)) {
        data.forEach(async (entry, index) => {
          data[index] = await internationalizeData({
            modelName,
            data: entry,
            schema,
            lang,
          });
        });
        return data;
      } else {
        data = internationalizeFields(data, lang, I_18_FIELDS[modelName], null);
      }
      schemaKeys.forEach(async (curr) => {
        if (
          schema[curr] &&
          (schema[curr].type === 'component' || schema[curr].collection)
        ) {
          if ((schema[curr].repeatable || schema[curr].collection) && data[curr]) {
            data[curr].forEach(async (entry, index) => {
              data[curr][index] = await internationalizeData({
                modelName: schema[curr].component || schema[curr].collection,
                data: entry,
                schema: schema[curr].schema,
                lang,
              });
            });
          } else if (modelName.indexOf('.') === -1) {
            data[curr] = await internationalizeData({
              modelName: schema[curr].component,
              data: data[curr],
              schema: schema[curr].schema,
              lang,
            });
          }
        }
      });
      return data;
    }
    module.exports = {
      internationalizeData,
    };
    
    • project_dir/api/homepage/controller.js
    /* global strapi */
    
    const { sanitizeEntity } = require('strapi-utils');
    const { internationalizeData } = require('../../../utils/controller.util');
    
    async function findOne(ctx) {
      const { id } = ctx.params;
      const { lang } = ctx.request.header;
      const entity = await strapi.services['home-page'].find({ id });
      const sanitizedEntity = sanitizeEntity(entity, {
        model: strapi.models['home-page'],
      });
      const data = internationalizeData({
        modelName: 'home-page',
        data: sanitizedEntity,
        schema: null,
        lang,
      });
      return data;
    }
    module.exports = {
      findOne,
    };
    
    

    Similarly, you can do for other apis.

Let's discuss your approach in the discussion box or you can hit me up at aastha.talwaria29@gmail.com.

Thanks for reading.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay