DEV Community

Aastha Talwaria
Aastha Talwaria

Posted on

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.

Top comments (0)