Mehdi Khoshnevis
Mehdi Khoshnevis

Reputation: 310

How to iterate i18n array of objects in React app?

I use i18n package in my React application to translate some texts. But I can't iterate through an array of objects which is my translation data.

I want to do iterating through socials array and show it in my template. but when I do that, it says: socials.map is not a function

this is my translation json file:

{
  "socials": [
  {
    "name": "Github",
    "url": "#"
  },
  {
    "name": "Twitter",
    "url": "#"
  },
  {
    "name": "Linkedin",
    "url": "#"
  },
  {
    "name": "Instagram",
    "url": "#"
  }
 ]
}

this is my jsx code:

import { useTranslation } from 'react-i18next';

const Content = () => {
  const { t, i18n } = useTranslation();

  const socials = t('socials', { returnObjects: true });

  rerurn (
    <div className="flex">
      {socials.map((social) => (
        <a href={social.url}>{social.name}</a>
      ))}
    </div>
  );
}

export default Content;

How can I solve this problem?

Upvotes: 6

Views: 21118

Answers (2)

adrai
adrai

Reputation: 3168

Your translations are probably lazy loaded, and you're probably also not using Suspense. This means the translations are not ready for the first render, and you need to check the ready flag: https://react.i18next.com/latest/usetranslation-hook#not-using-suspense

import { useTranslation } from "react-i18next";

const Content = () => {
    const { t, i18n, ready } = useTranslation();

    if (!ready) return "loading translations...";

    const socials = t("socials", { returnObjects: true });

    return (
        <div className="flex">
            {socials.map((social) => (
                <a href={social.url}>{social.name}</a>
            ))}
        </div>
    );
};

export default Content;

Upvotes: 21

Venca
Venca

Reputation: 71

In my case iterating through translation array worked well until changeLanguage method was triggered. Thing was that for some reason i18n converted array items into properties of an object. Following Mehdi's data from above:

This:

    {
  "socials": [
  {
    "name": "Github",
    "url": "#"
  },
  {
    "name": "Twitter",
    "url": "#"
  },
  {
    "name": "Linkedin",
    "url": "#"
  },
  {
    "name": "Instagram",
    "url": "#"
  }
 ]
}

was turned into this:

    {
  "socials": {
  social1: {
    "name": "Github",
    "url": "#"
  },
  social2: {
    "name": "Twitter",
    "url": "#"
  },
  social3: {
    "name": "Linkedin",
    "url": "#"
  },
  social4: {
    "name": "Instagram",
    "url": "#"
  }
 }
}

To make it work I've written a helper function that checks if passed object is an array or an object (because when 18n is init()ialized, it's an array) and if it's an object it converts its props back into array items. After this conversion mapping / looping works, no more ".map() is not a function" errors.

const objToArr = (obj) => {
    if (obj.length > 0) {
      return obj;
    }
    let arr = [];
    for (let i = 0; i < Object.keys(obj).length; i++) {
      const n = `social${i + 1}`;
      arr.push(obj[n]);
    }
    return arr;
  };

with return object being:

 return (
    <div className="flex">
      {objToArr(socials).map((social) => (
        <a href={social.url}>{social.name}</a>
      ))}
    </div>
  );
}

Upvotes: 0

Related Questions