1

I have 2 JSON files with my translation data -> EN file and FR file.

I am using react-i18next to handle my translations this way, which works fine, but since I am having repeatable components I need to map/loop through the translations to get the right output

Example

en:

export const EN = {
    page1: {
      section1 {
         titles: {
            title1: "title1_name",
            title2: "title2_name",
            title3: "title3_name"
          },
          buttons: {
             button1 : "button1_name",
             button2 : "button2_name",
             button3 : "button3_name",
          }
       }
       section2 { THE SAME AS SECTION 1 }
       section3 { THE SAME AS SECTION 1 }

    page2 { SAME AS PAGE 1 } 

The same thing applies for FR file (with french translations instead)

How can achieve to mapping all e.g titles from section1 and page1. Would that be even correct to use map() ?

Right now my solution is just to use {t page1.section1.0.titles.title1} which of course print the same title everywhere - in this case title1_name

Current Output with using {t page1.section1.0.titles.title1} :

slider1: title1_name

slider2: title1_name

slider3: title1_name

slider4: title1_name and so on so on...

Expected output:

slider1: title1_name, button1_name

slider2: title2_name, button2_name

slider3: title4_name, button3_name

slider4: title4_name, button4_name

  • Hey Eliot, do you need `EN.page2` to be the same as `EN.page1`? – jburtondev Mar 16 '21 at 16:27
  • @jburtondev no, thats is just example to make it shorter - explanation of my json file structure. What I need is to print out all titles from page1 and section1 - you can see I am using { t } from useTranslation so I right now I am printing it `{t page1.section1.0.titles.title1}`but this is printing only title1_name everywhere –  Mar 16 '21 at 16:29
  • Sure, please add the expected output object in your question and then i'll be able to add an answer. – jburtondev Mar 16 '21 at 16:32
  • 1
    I updated my post, let me know if doesnt make sense –  Mar 16 '21 at 16:37
  • Sure, taking a look now. – jburtondev Mar 16 '21 at 16:38
  • Added my answer. – jburtondev Mar 16 '21 at 16:59

3 Answers3

0

This works, you'll need to do the translation, but this gives you access to the title object in each page and section:

    Object.entries(EN).map(([key,object]) => {

      Object.entries(object).map(([token, value]) => {
          console.log(`${token} : ${value}`);
          Object.keys(value).map((key, index) => {
            console.log(value[key]); // titles access here
        });
      });
    });
jburtondev
  • 1,011
  • 10
  • 16
0

When you are iterating over an object you'll want to use a function that gets data from your object in an array. Traditionally that is Object.keys(), but newer versions of Javascript introduced Object.values() and Object.entries() which can be very helpful depending on the situation. You can access a value from its key like myObject[myKey] so Object.keys() can work in every situation.

The current structure of your JSON file is not ideal because you have totally separate objects for titles and buttons so you can't ensure that you have the same amount of title texts as button texts. I'll address this in a moment, but first here is one way that you can use the current structure.

const MySlider = () => {
  const currentlang = ... // get the EN or FR object based on your current language

  // an array of all the title texts
  const titles = Object.values(currentlang.page1.section1.titles);

  // an array of all the button texts
  const buttons = Object.values(currentlang.page1.section1.buttons);

  return (
    <Slider>
      {titles.map((title, i) => ( // map through the titles
        <Slide
          title={title}
          buttonText={buttons[i]} // get the button text with the same index -- how do we know this is valid?
          key={i} // i as a key is not great
        />
      ))}
    </Slider>
  );
};

With some dummy components so that you can render it:

const Slider: React.FC = ({ children }) => <div>{children}</div>;

interface SliderProps {
  title: string;
  buttonText: string;
}

const Slide: React.FC<SliderProps> = ({ title, buttonText }) => {
  return (
    <div>
      <h2>{title}</h2>
      <button>{buttonText}</button>
    </div>
  );
};

I would recommend grouping the labels by slide rather than by titles and buttons. This ensures that titles and buttons match up, allows easy access by the slide key if you want to customize the order, and gives us a unique key property for our components.

export const EN = {
  page1: {
    section1: {
      slides: {
        sale: {
          title: "title1_name",
          button: "button1_name"
        },
        featured: {
          title: "title2_name",
          button: "button2_name"
        },
        promo: {
          title: "title3_name",
          button: "button3_name"
        },
      }
    }
  }
};
const MySlider = () => {
  const currentlang = ... // get the EN or FR object based on your current language

  // keyed object of slides
  const slides = currentlang.page1.section1.slides;

  return (
    <Slider>
      {Object.entries(slides).map(
        // Object.entries gives us an array with elements key and value
        ([key, value]) => (
          <Slide
            title={value.title}
            buttonText={value.button}
            key={key}
          />
        )
      )}
    </Slider>
  );
};

Key-based approach to allow custom ordering:

const MySlider = () => {
  const currentlang = ... // get the EN or FR object based on your current language

  // keyed object of slides
  const slides = currentlang.page1.section1.slides;

  // helper function renders the slide for a given key
  const renderSlide = (key: keyof typeof slides) => {
    const {title, button} = slides[key];
    return (
      <Slide 
        title={title}
        buttonText={button}
      />
    )
  }

  return (
    <Slider>
      {renderSlide("promo")}
      {renderSlide("sale")}
      {renderSlide("featured")}
    </Slider>
  );
};
Linda Paiste
  • 20,442
  • 2
  • 16
  • 40
  • Great explanation, I will try to follow along. Altough, my translations are coming from i18next useTranslation = so I am using `{t ` something ` }`, should I just create function currentLang instead of that ? –  Mar 16 '21 at 19:56
  • `currentLang` is just a variable with the language object. I was setting `const currentLang = EN` to play with it. – Linda Paiste Mar 16 '21 at 19:58
-1

There is no option for a .map functions on objects but you can use the Object.keys Option.:

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

Object.keys(myObject).map(function(key, index) {
  myObject[key] *= 2;
});

console.log(myObject);
// => { 'a': 2, 'b': 4, 'c': 6 }

map function for objects (instead of arrays)

Medium MultiLanguage Website

Ethanolle
  • 461
  • 1
  • 1
  • 12
  • But what would be myObject ? Since I have 2 of them (translations EN & FR) I need to use. Could you specify it based on my example ? –  Mar 16 '21 at 16:24
  • Put some effort into lectures and documentation, added a medium post on how to do a multiLanguage Website in general with i18next. – Ethanolle Mar 16 '21 at 16:29
  • It has nothing to do with that though, my translations are working. –  Mar 16 '21 at 16:32
  • {Object.values(currentLang.page1.section1.titles).map(title => ())} ? Higher up in the code (outside render) you would get the currentLang object based on your current language for translations. – Linda Paiste Mar 16 '21 at 16:46
  • Thank @LindaPaiste for comment, once again closest to the logic I need. I just don't understand where is the title={title} coming from inside the Slider ? –  Mar 16 '21 at 17:17
  • `Object.values(currentLang.page1.section1.titles)` gives you an array of all the titles in page 1 section 1. When we call `map` on the array we map from an element of that array to a React component. The local variable in the `map` callback is the element of the array. You can call it anything but I am calling it `title` (you set the name in this part: `title =>`). I am assuming that your `Slide` takes a `title` prop so I set the prop `title` on the `Slide` to this `title` variable. – Linda Paiste Mar 16 '21 at 17:32
  • 1
    Useful docs: [Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), [Object.values()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values) – Linda Paiste Mar 16 '21 at 17:35