Reputation: 310
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
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
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