i18next / react-i18next

Internationalization for react done right. Using the i18next i18n ecosystem.
https://react.i18next.com
MIT License
9.3k stars 1.03k forks source link

Namespaces not being loaded from Locize #1223

Closed AbsentmindedInsanity closed 3 years ago

AbsentmindedInsanity commented 3 years ago

🐛 Bug Report

I have followed the setup in the Locize react example and that works when hooked up with my api keys for locize, however, it is failing on my main project, a rather extensive site, even when having literally copy-pasted the i18n.js file from one to the other, any ideas of what might be causing this to happen would be incredible.

image

When I navigate to my page, I get this error and the text defaults to title. It seems like the call to get the namespaces is never made, when I load the example it is made right away but in this project, it does not happen and no errors are thrown until I navigate to the page where I have implemented the t function

my i18n file:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-locize-backend';
import LastUsed from 'locize-lastused';
import { locizePlugin } from 'locize';
import Editor from 'locize-editor'; 

const locizeOptions = {
    projectId: '',
    apiKey: '',
    referenceLng: 'en'
};

i18n
  .use(Backend)
  .use(LastUsed)
  .use(locizePlugin)
  .use(Editor)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    fallbackLng: 'en',
    debug: true,
    saveMissing: true,
    ns: ['transation', 'countries'],
    initImmediate: true,

    interpolation: {
      escapeValue: false, // not needed for react as it escapes by default
    },
    backend: locizeOptions,
    locizeLastUsed: locizeOptions,
    editor: {
      // old locize editor (will be removed in future)
      ...locizeOptions,
      onEditorSaved: async (lng, ns) => {
        // reload that namespace in given language
        await i18n.reloadResources(lng, ns);
        // trigger an event on i18n which triggers a rerender
        // based on bindI18n below in react options
        i18n.emit('editorSaved');
      },
    },
    react: {
      bindI18n: 'languageChanged editorSaved',
      useSuspense: true,
      wait: true,
    },
  });

export default i18n;

The Index.js

import 'bootstrap/dist/css/bootstrap.css';
import './fonts/';
import './fonts/';
import './index.scss';

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './App';
import PasswordProtected, { getVerifiedPassword } from './components/password';
import store from './redux/store';
import * as serviceWorker from './serviceWorker';

import './i18n';

if (process.env.REACT_APP_PASSWORD_PROTECTED !== '' && !getVerifiedPassword()) {
  ReactDOM.render(<PasswordProtected />, document.getElementById('root'));
} else {
  ReactDOM.render(
    <Router>
      <Provider store={store}>
          <App />
      </Provider>
    </Router>,
    document.getElementById('root')
  );
}

serviceWorker.unregister();

To Reproduce

This is part of the problem, I cannot seem to reproduce the issue using the example

Expected behavior

The namespaces would initialize when the rest of i18n initialized allowing me to access them and use the keys, no warnings or error of an incorrect setup

Your Environment

Chrome, latest with local environment
"i18next": "^19.8.4",
"i18next-browser-languagedetector": "^6.0.1",
"i18next-locize-backend": "^4.1.8",
"locize": "^2.2.4",
"locize-editor": "^3.1.1",
"locize-lastused": "^3.0.10",
"react-i18next": "^11.8.2",
 Windows 10, 64 bit
adrai commented 3 years ago

How do you access that title key? hook? hoc? Can you show that part of your code? Are you sure you are wrapping with a Suspense component? https://react.i18next.com/latest/using-with-hooks#translate-your-content If not, are you waiting for the ready flag? https://react.i18next.com/latest/usetranslation-hook#not-using-suspense

AbsentmindedInsanity commented 3 years ago
import React, {Suspense} from "react";
import InfoTabs from "./InfoTabs";
import { useMediaQuery } from "react-responsive";

import { BladeStateHoc } from "../../../HOCs/UIStateHOC";

import { useTranslation, withTranslation, Trans } from 'react-i18next';

function GetCountries(props) {
  const isMaxWidth991 = useMediaQuery({ maxWidth: 991 });

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

  return (
    <div
      className="screen-container"
      style={{
        transition: ".2s",
        ...(props.bladeStateOpen && !isMaxWidth991 ? { width: "75%" } : { width: "100%" }),
      }}
    >
      <InfoTabs />
      <div className="noScroll screen-scroll" style={styles.container}>
        <h1 style={styles.title}>{ready ? t('countries:title') : ''}</h1>
      </div>
    </div>
  );
}

const SuspendIt = () => {
  return (
    <>
    <Suspense fallback={null}>
      <GetCountries/>
    </Suspense>
    </>
  )
}

export default BladeStateHoc(SuspendIt);

Also modifying the index.js to include this:

  ReactDOM.render(
    <Suspense fallback={null}>
    <Router>
      <Provider store={store}>
          <App />
      </Provider>
    </Router>
    </Suspense>,
    document.getElementById('root')
  );

fails to fix it

adrai commented 3 years ago

Seems you are trying both Suspense + ready

Try to remove the suspense wrappers and set useSuspense to false

adrai commented 3 years ago

btw: and there is no fetch request in the network tab?

adrai commented 3 years ago

Maybe move also the import './i18n'; statement before you import any component

AbsentmindedInsanity commented 3 years ago

Same issue, or close to it.

image

When I boot up the example I see this, which is why I think its not even attempting to make the call, as I cannot catch any call from either the network tab or fiddler on my main project but see some when loading the example image

Maybe move also the import './i18n'; statement before you import any component Ah, good thought, ill try that

adrai commented 3 years ago

and to be sure pass your namespace to the useTranslation call useTranslation('countries')

adrai commented 3 years ago

seems like there is no backend injected... Can you paste i18next instance? maybe hide you apikey before

AbsentmindedInsanity commented 3 years ago

Moving it to the top of the imports does not resolve the issue, and also causes this?? image

Adding the 'countries' just results in a blank section for the text

{
  "debug": true,
  "initImmediate": true,
  "ns": [
    "transation",
    "countries"
  ],
  "defaultNS": "translation",
  "fallbackLng": [
    "en"
  ],
  "fallbackNS": [
    "local",
    "emoji",
    "settings",
    "translation",
    "search"
  ],
  "whitelist": false,
  "nonExplicitWhitelist": false,
  "supportedLngs": false,
  "nonExplicitSupportedLngs": false,
  "load": "all",
  "preload": false,
  "simplifyPluralSuffix": true,
  "keySeparator": ".",
  "nsSeparator": ":",
  "pluralSeparator": "_",
  "contextSeparator": "_",
  "partialBundledLanguages": false,
  "saveMissing": true,
  "updateMissing": false,
  "saveMissingTo": "fallback",
  "saveMissingPlurals": true,
  "missingKeyHandler": false,
  "missingInterpolationHandler": false,
  "postProcess": false,
  "postProcessPassResolved": false,
  "returnNull": true,
  "returnEmptyString": true,
  "returnObjects": false,
  "joinArrays": false,
  "returnedObjectHandler": false,
  "parseMissingKeyHandler": false,
  "appendNamespaceToMissingKey": false,
  "appendNamespaceToCIMode": false,
  "interpolation": {
    "escapeValue": false
  },
  "resources": {
    "en": {
      "search": {
        "ChatsAndContacts": "Chats and contacts",
        "SearchMessagesIn": "Search messages in"
      },
      "settings": {
        "ContactJoinedEnabled": "Enabled",
        "ContactJoinedDisabled": "Disabled",
        "NotificationsEnabled": "Enabled",
        "NotificationsDisabled": "Disabled",
        "PreviewEnabled": "Enabled",
        "PreviewDisabled": "Disabled",
        "BioAbout": "Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco.",
        "Archived": "Archived",
        "Saved": "Saved",
        "EditProfile": "Edit Profile",
        "GeneralSettings": "General Settings"
      },
      "local": {
        "PollQuizOneRightAnswer": "Quiz has only one right answer.",
        "LeftChannel": "Left channel",
        "LeftGroup": "Left group",
        "EnterPassword": "Enter a Password",
        "YourAccountProtectedWithPassword": "Your account is protected with an additional password.",
        "DeletedMessage": "Deleted message",
        "YourPhone": "Your Phone",
        "SignInToTelegram": "Sign in to Telegram",
        "PhoneNumber": "Phone Number",
        "Country": "Country",
        "KeepMeSignedIn": "Keep me signed in",
        "StartText": "Please confirm your country code and enter your phone number.",
        "Next": "Next",
        "InvalidPhoneNumber": "Invalid phone number. Please check the number and try again.",
        "More": "More",
        "SendFileConfirmation": "Are you sure you want to send file?",
        "SendFilesConfirmation": "Are you sure you want to send files?",
        "SendMessage": "Send Message",
      }
    },
    "ru": {
      "search": {
        "ChatsAndContacts": "Чаты и контакты",
        "SearchMessagesIn": "Искать сообщения в"
      },
      "settings": {
        "ContactJoinedEnabled": "Включено",
        "ContactJoinedDisabled": "Выключено",
        "NotificationsEnabled": "Включены",
        "NotificationsDisabled": "Выключены",
        "PreviewEnabled": "Включено",
        "PreviewDisabled": "Выключено",
        "BioAbout": "Любые подробности, например: возраст, род занятий или город.\nПример: 23 года, дизайнер из Санкт-Петербурга.",
        "Archived": "Архив",
        "Saved": "Избранное",
        "EditProfile": "Редактровать профиль",
        "GeneralSettings": "Основные настройки"
      },
      "local": {
        "PollQuizOneRightAnswer": "Quiz has only one right answer.",
        "LeftChannel": "Канал покинут",
        "LeftGroup": "Группа покинута",
        "EnterPassword": "Введите пароль",
        "YourAccountProtectedWithPassword": "Ваш аккаунт защищен дополнительным паролем.",
        "DeletedMessage": "Удаленное сообщение",
        "YourPhone": "Ваш телефон",
        "SignInToTelegram": "Вход в Telegram",
        "PhoneNumber": "Телефонный номер",
        "Country": "Страна",
        "KeepMeSignedIn": "Сохранить авторизацию",
        "StartText": "Пожалуйста, укажите код страны и свой номер телефона.",
        "Next": "Далее",
        "InvalidPhoneNumber": "Некорректный номер телефона. Пожалуйста, проверьте номер и попробуйте ещё раз.",
        "More": "Ещё",
        "SendFileConfirmation": "Вы действительно хотите отправить файл?",
        "SendFilesConfirmation": "Вы действительно хотите отправить файлы?",
        "SendMessage": "Отправить сообщение",
        "ChatInfo": "Информация о чате",
        "ChannelInfo": "Информация о канале",
        "Stickers": "СТИКЕРЫ",
        "Emoji": "ЕМОДЗИ",
        "SelectChatToStartMessaging": "Пожалуйста, выберите, кому хотели бы написать",
        "Text": "Текст",
        "ViewChannelInfo": "Информация о канале",
        "ViewGroupInfo": "Информация о группе",
        "ViewProfile": "Показать профиль",
        "GoToMessage": "Перейти к сообщению",
        "PhotosTitle": "Фотографии",
        "VideosTitle": "Видеозаписи",
        "VoiceTitle": "Голосовые сообщения",
        "UpdateDraftConfirmation": "Вы действительно хотите обновить черновик сообщения?"
      },
      "emoji": {
        "Search": "Поиск",
        "NotEmojiFound": "Емодзи не найдены",
        "ChooseDefaultSkinTone": "Выберите тон кожи по умолчанию",
        "SearchResults": "Результаты поиска",
        "Recent": "Часто используемые",
        "SmileysPeople": "Смайлики и люди",
        "AnimalsNature": "Животные и природа",
        "FoodDrink": "Еда и напитки",
        "Activity": "Активность",
        "TravelPlaces": "Путешествия и местности",
        "Objects": "Предметы",
        "Symbols": "Символы",
        "Flags": "Флаги",
        "Custom": "Пользовательские"
      },
      "translation": {
        "AppName": "Telegram",
        "Connecting": "Соединение...",
        "ConnectingToProxy": "Подключение к прокси...",
        "Loading": "Загрузка...",
        "Updating": "Обновление...",
        "WaitingForNetwork": "Ожидание сети...",
        "ContinueOnThisLanguage": "Продолжить на русском"
      }
    }
  },
  "lng": "en",
  "react": {
    "bindI18n": "languageChanged editorSaved",
    "useSuspense": false,
    "wait": true
  },
  "backend": {
    "projectId": "",
    "apiKey": "",
    "referenceLng": "en",
    "loadPath": "https://api.locize.app/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "privatePath": "https://api.locize.app/private/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "getLanguagesPath": "https://api.locize.app/languages/{{projectId}}",
    "addPath": "https://api.locize.app/missing/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "updatePath": "https://api.locize.app/update/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "crossDomain": true,
    "setContentTypeJSON": false,
    "version": "latest",
    "private": false,
    "translatedPercentageThreshold": 0.9,
    "whitelistThreshold": 0.9,
    "failLoadingOnEmptyJSON": false,
    "allowedAddOrUpdateHosts": [
      "localhost"
    ],
    "onSaved": false,
    "reloadInterval": false,
    "checkForProjectTimeout": 3000,
    "storageExpiration": 3600000,
    "lastUsedPath": "https://api.locize.app/used/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "debounceSubmit": 90000,
    "allowedHosts": [
      "localhost"
    ]
  },
  "locizeLastUsed": {
    "projectId": "",
    "apiKey": "",
    "referenceLng": "en",
    "loadPath": "https://api.locize.app/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "privatePath": "https://api.locize.app/private/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "getLanguagesPath": "https://api.locize.app/languages/{{projectId}}",
    "addPath": "https://api.locize.app/missing/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "updatePath": "https://api.locize.app/update/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "crossDomain": true,
    "setContentTypeJSON": false,
    "version": "latest",
    "private": false,
    "translatedPercentageThreshold": 0.9,
    "whitelistThreshold": 0.9,
    "failLoadingOnEmptyJSON": false,
    "allowedAddOrUpdateHosts": [
      "localhost"
    ],
    "onSaved": false,
    "reloadInterval": false,
    "checkForProjectTimeout": 3000,
    "storageExpiration": 3600000,
    "lastUsedPath": "https://api.locize.app/used/{{projectId}}/{{version}}/{{lng}}/{{ns}}",
    "debounceSubmit": 90000,
    "allowedHosts": [
      "localhost"
    ]
  },
  "editor": {
    "projectId": "",
    "apiKey": "",
    "referenceLng": "en"
  }
}

sooo it seems like apparently one of the other intigrations we set up (telegram) also has i18next? Could that possibly cause the issues?

I deleted literally 100s of lines of text from the "local" section all from the telegram section. It doesnt use locize it seems

AbsentmindedInsanity commented 3 years ago

So I disabled the telegram client, and it still has the same issue unfortunately

adrai commented 3 years ago

What do you mean by telegram client? is i18next used twice? i18next is a singleton. You may try with createInstance: https://www.i18next.com/overview/api#createinstance

AbsentmindedInsanity commented 3 years ago

I found the problem. A third party integration we had set up apparently used i18next, which I did not know and was conflicting with the implementation I was just attempting to do.

Is there any way to use two instances of it in an app completely separately? Will createinstance effectively do this?

Sorry to take up all your time with this, though I would love any more comments you have.

adrai commented 3 years ago

Yes, createInstance helps in this case.