i18next / i18next-http-backend

i18next-http-backend is a backend layer for i18next using in Node.js, in the browser and for Deno.
MIT License
452 stars 70 forks source link

Translate the REST API content using axios #126

Closed Namnika closed 1 year ago

Namnika commented 1 year ago

🐛 Bug Report

I am trying to translate the REST API content (ex: fetching data from jsonplaceholder) using Axios. I also asked stack overflow question but that answer didn't work for me. But, I'm getting a console logger of backendConnector:

console screenshot:

Screenshot (411)

Screenshot (412)

and it doesn't translate the contents.

To Reproduce

I created a codesandbox to get familiar with this issue.

i18n.js:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";
import axios from "axios";

const loadResources = async (locale) => {
  return await axios
    .get("https://jsonplaceholder.typicode.com/posts", {
      params: { lang: locale },
    })
    .then((res) => {
      return res.data.slice(0, 3);
    })
    .catch((err) => {
      console.log(err);
    });
};

const detectorOptions = {
  // order and from where user language should be detected
  order: ["cookie", "localStorage", "navigator"],

  // keys or params to lookup language from
  lookupCookie: "locales",
  lookupLocalStorage: "locales",

  // cache user language on
  caches: ["localStorage", "cookie"],
  excludeCacheFor: ["cimode"],

  //   only detect languages that are in the whitelist
  checkWhiteList: true,
};

const backendOptions = {
  loadPath: "/locales/{{lng}}/{{ns}}.json", //used to pass language and namespace to custom XHR function
  request: (options, url, payload, callback) => {
    try {
      const [lng] = url.split("|");

      //this mocks the HTTP fetch plugin behaviour so it works with the backend AJAX pattern in this XHR library
      //https://github.com/i18next/i18next-http-backend/blob/master/lib/request.js#56
      loadResources(lng).then((data) => {
        callback(null, {
          data: JSON.stringify(data),
          status: 200, //status code is required by XHR plugin to determine success or failure
        });
      });
    } catch (e) {
      console.error(e);
      callback(null, {
        status: 500,
      });
    }
  },
};

i18n
  // load translation using http -> see /public/locales
  // learn more: https://github.com/i18next/i18next-http-backend
  .use(HttpApi)
  // detect user language
  // learn more: https://github.com/i18next/i18next-browser-languageDetector
  .use(LanguageDetector)
  // pass the i18n instance to react-i18next.
  .use(initReactI18next)
  // init i18next
  // for all options read: https://www.i18next.com/overview/configuration-options
  .init({
    debug: true,
    fallbackLng: "en",
    backend: backendOptions,
    detection: detectorOptions,
    whitelist: ["en", "zh", "jp", "fr"],
    interpolation: {
      escapeValue: false,
    },
  });

export default i18n;

Demo.js:

import Dropdown from "./Dropdown";
import Buttons from "./Buttons";
import { useTranslation } from "react-i18next";
import axios from "axios";
import { useEffect, useState } from "react";

export default function Demo() {
  const { t } = useTranslation();
  const [data, setData] = useState([]);

  useEffect(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/posts")
      .then((res) => {
        setData(res.data.slice(0, 3));
      })
      .catch((err) => {
        console.log(err);
      });
  }, [data]);

  return (
    <>
      <section>
        {data.map((i, index) => {
          return (
            <>
              <div key={index}>
                <h4>{i.title}</h4>
                <p>{i.body}</p>
              </div>
            </>
          );
        })}
      </section>
     <Buttons />
    </>
  );
}

Expected behavior

I am expecting that API content that is fetched from Axios should get translated by i18next into multiple languages.

Your Environment

adrai commented 1 year ago

i18next is an i18n framework. It does not actively translate text... use google translate or similar for that

Namnika commented 1 year ago

but how can i get the translations content without using any application? Im fetching the data using axios.

adrai commented 1 year ago

The data returned by loadResources needs to be a json object (not an array), that looks something like this: https://github.com/i18next/react-i18next/blob/master/example/react/public/locales/en/translation.json

Namnika commented 1 year ago

but still, it doesn't work for me. Can you pls do some editing and tell to me?

adrai commented 1 year ago

Have you checked this example and studied it? https://github.com/i18next/react-i18next/blob/master/example/react

Namnika commented 1 year ago

Yes, I studied and it's about backend api not the api url, like I want to use here https://jsonplaceholder.typicode.com/posts. I know there is localhost is also used. but from jsonplaceholder API url, when I got the data so that I could translate it afterward. I mean like any dynamic values should I use?

adrai commented 1 year ago

You can use whatever url you like, you can also use axios with a custom request function.... but you always need to return json data that is structured to be used by i18next. So the returned data always needs keys and values. Imagine how your t function would look like? For example what would you pass here in the t function instead of 'title'? https://github.com/i18next/react-i18next/blob/master/example/react/src/App.js#L10 Title is the key the resolves to "Welcome to react using react-i18next" in english and "Willkommen zu react und react-i18next" in german. So all your "dynamic" values needs to have at least a key so you can instrument your code. If you don't have this, then i18next is not the right tool for you.

jamuhl commented 1 year ago

@adrai already told you the structure of the loaded content has to be like:

{
  "title": "Welcome to react using react-i18next",
  "description": {
    "part1": "To get started, edit <1>src/App.js</1> and save to reload.",
    "part2": "Switch language between english and german using buttons above."
  }
}

and that you get an array of data items -----> not an object containing translations in one language

So, your approach is already totally wrong.

You like to do: load data and pipe it through i18next -> get magically translated texts in that data (there is no magic) What you need to do: load your data somewhere not i18next -> based on data call t to get translations for content (where it makes sense -> eg. call i18next.t('product.${item.id}.description') assuming your JSON contains a product.123.description)

but for me it looks more like you like to get some machine translations on the fly for some user profile userdescriptions

Namnika commented 1 year ago

@jamuhl , @adrai Thank you for solving my query! Actually I'm new with i18next and couldn't get the thing what i actually wanted to do. But now I got the point like I have to store the api data somewhere and load it from there and then it should be translate having in the format of JSON (with keys and values). Sorry for the inconvenience, I was bit struggling to get to this work.