简体   繁体   中英

Javascript recursive function to modify an object is not working

All the texts for my app are inside a .json file for translation purposes. I need a function to get the corresponding text based on the selected language.

I think it should be an easy task, but I don't see the solution.

I have the following object:

{
  section1: {
    btn: { en: "English", es: "Español" },
    title: {
      en: "Web Frontend Developer",
      es: "Desarrollador Web de Frontend"
    },
    card: {
      title: { en: "Hello!", es: "Hola!" },
      btn: { en: "Get started", es: "Empecemos" },
    }
  }
}

This object should be passed to a function as the first parameter and the second parameter should be the language (in this case "en" or "es"). I want something like this: filterObjByLanguage(obj, "es")

And it should return:

{
  section1: {
    btn: "Español",
    title: "Desarrollador Web de Frontend",
    card: {
      title: "Hola!",
      btn: "Empecemos"
    }
  }
}

Basically, it goes through each part of the object and wherever there is a { en:"text", es: "texto" } , it chooses one of them based on the second parameter.

This is my attempt, but only the first layer of the object is returned correctly, the rest is undefined.

const filterObjByLanguage= (obj: any, lang: string): any => {
  const output = Object.assign(obj, {});

  const loop = (obj: any, isRoot: boolean = true): any => {
    for (var k in obj) {
      const value = output[k];
      const valueAtSelected = value?.[lang];

      if (typeof value === "string") {
        continue;
      } else if (valueAtSelected) {
        if (isRoot) output[k] = valueAtSelected;
        else return valueAtSelected;
      } else {
        if (isRoot) output[k] = loop(value, false);
        else return loop(value, false);
      }
    }
  };

  loop(output);

  return output;
};

There's one difficulty here: knowing what objects are language values versus which are collections of them. In this solution, I use the existence of a 'en' property as a signal. Once we have that, it's mostly a matter of tearing apart and rebuilding objects using Object.entries and Object.fromEntries . In plain JS (I'll leave Typescript for those who appreciate it!), it might look like this:

 const filterObjByLanguage = (lang) => (o) => Object (o) === o ? 'en' in o ? o [lang] || '' : Object .fromEntries ( Object .entries (o) .map (([k, v]) => [k, filterObjByLanguage (lang) (v)]) ) : o const i18n = {section1: {btn: {en: "English", es: "Español"}, title: {en: "Web Frontend Developer", es: "Desarrollador Web de Frontend"}, card: {title: {en: "Hello!", es: "Hola!"}, btn: {en: "Get started", es: "Empecemos"}}}} console .log ('English', filterObjByLanguage ('en') (i18n)) console .log ('Español', filterObjByLanguage ('es') (i18n)) console .log ('Français', filterObjByLanguage ('fr') (i18n))
 .as-console-wrapper {max-height: 100% !important; top: 0}

This also makes the decision that missing language values are simply empty strings. If you prefer undefined , then you could just remove || '' || '' . We can extend this to arrays if that's required with a simple modification:

const filterObjByLanguage = (lang) => (o) =>
  Array .isArray (o) 
    ? o .map (filterObjByLanguage (lang)) 
  : Object (o) === o
    ? 'en' in o
      ? o [lang] || ''
      : Object .fromEntries (
          Object .entries (o) .map (([k, v]) => [k, filterObjByLanguage (lang) (v)])
        )
  : o

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM