From 648c705d839ba578a2add41dc497cc9c20f04a33 Mon Sep 17 00:00:00 2001 From: Adriano Raiano Date: Wed, 17 Jul 2024 14:20:01 +0200 Subject: [PATCH] use modern hasLoadedNamespace code (now requires at least i18next > v19.4.5 (introduced in june 2020)) --- CHANGELOG.md | 7 +++++ src/utils.js | 49 ------------------------------ test/I18nextProvider.spec.jsx | 2 ++ test/hasLoadedNamespaceMock.js | 40 ++++++++++++++++++++++++ test/useTranslation.ready.spec.jsx | 2 ++ test/withSSR.spec.jsx | 2 ++ 6 files changed, 53 insertions(+), 49 deletions(-) create mode 100644 test/hasLoadedNamespaceMock.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e623cabe..51eb5b2b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +### 15.0.0 + +- use optional chaining, nullish coalescing and nullish coalescing assignment [1774](https://github.com/i18next/react-i18next/pull/1774) +- Build config and optimizations [1769](https://github.com/i18next/react-i18next/pull/1769) +- some dependency updates [1768](https://github.com/i18next/react-i18next/pull/1768) +- use modern hasLoadedNamespace code (now requires at least i18next > v19.4.5 (introduced in june 2020)) + ### 14.1.3 - create a isObject helper function [1766](https://github.com/i18next/react-i18next/pull/1766) diff --git a/src/utils.js b/src/utils.js index 26b3155bc..e6924fa17 100644 --- a/src/utils.js +++ b/src/utils.js @@ -51,61 +51,12 @@ export const loadLanguages = (i18n, lng, ns, cb) => { i18n.loadLanguages(lng, loadedClb(i18n, cb)); }; -// WAIT A LITTLE FOR I18NEXT BEING UPDATED IN THE WILD, before removing this old i18next version support -const oldI18nextHasLoadedNamespace = (ns, i18n, options = {}) => { - const lng = i18n.languages[0]; - const fallbackLng = i18n.options?.fallbackLng ?? false; - const lastLng = i18n.languages[i18n.languages.length - 1]; - - // we're in cimode so this shall pass - if (lng.toLowerCase() === 'cimode') return true; - - const loadNotPending = (l, n) => { - const loadState = i18n.services.backendConnector.state[`${l}|${n}`]; - return loadState === -1 || loadState === 2; - }; - - // bound to trigger on event languageChanging - // so set ready to false while we are changing the language - // and namespace pending (depends on having a backend) - if ( - options.bindI18n?.indexOf('languageChanging') > -1 && - i18n.services.backendConnector.backend && - i18n.isLanguageChangingTo && - !loadNotPending(i18n.isLanguageChangingTo, ns) - ) - return false; - - // loaded -> SUCCESS - if (i18n.hasResourceBundle(lng, ns)) return true; - - // were not loading at all -> SEMI SUCCESS - if ( - !i18n.services.backendConnector.backend || - (i18n.options.resources && !i18n.options.partialBundledLanguages) - ) - return true; - - // failed loading ns - but at least fallback is not pending -> SEMI SUCCESS - if (loadNotPending(lng, ns) && (!fallbackLng || loadNotPending(lastLng, ns))) return true; - - return false; -}; - export const hasLoadedNamespace = (ns, i18n, options = {}) => { if (!i18n.languages || !i18n.languages.length) { warnOnce('i18n.languages were undefined or empty', i18n.languages); return true; } - // ignoreJSONStructure was introduced in v20.0.0 (MARCH 2021) - const isNewerI18next = i18n.options.ignoreJSONStructure !== undefined; - if (!isNewerI18next) { - // WAIT A LITTLE FOR I18NEXT BEING UPDATED IN THE WILD, before removing this old i18next version support - return oldI18nextHasLoadedNamespace(ns, i18n, options); - } - - // IN I18NEXT > v19.4.5 WE CAN (INTRODUCED JUNE 2020) return i18n.hasLoadedNamespace(ns, { lng: options.lng, precheck: (i18nInstance, loadNotPending) => { diff --git a/test/I18nextProvider.spec.jsx b/test/I18nextProvider.spec.jsx index 910716d4d..0b8333d9f 100644 --- a/test/I18nextProvider.spec.jsx +++ b/test/I18nextProvider.spec.jsx @@ -3,6 +3,7 @@ import React from 'react'; import { render, screen, cleanup } from '@testing-library/react'; import { I18nextProvider } from '../src/I18nextProvider'; import { useTranslation } from '../src/useTranslation'; +import hasLoadedNamespace from './hasLoadedNamespaceMock.js'; vitest.unmock('../src/useTranslation'); vitest.unmock('../src/I18nextProvider'); @@ -21,6 +22,7 @@ const instance = { getFixedT: () => (message) => message, hasResourceBundle: (lng, ns) => ns === 'translation', loadNamespaces: () => {}, + hasLoadedNamespace: (ns) => hasLoadedNamespace(ns, instance), on: () => {}, off: () => {}, options: {}, diff --git a/test/hasLoadedNamespaceMock.js b/test/hasLoadedNamespaceMock.js new file mode 100644 index 000000000..c1a535474 --- /dev/null +++ b/test/hasLoadedNamespaceMock.js @@ -0,0 +1,40 @@ +export default (ns, i18n, options = {}) => { + const lng = i18n.languages[0]; + const fallbackLng = i18n.options ? i18n.options.fallbackLng : false; + const lastLng = i18n.languages[i18n.languages.length - 1]; + + // we're in cimode so this shall pass + if (lng.toLowerCase() === 'cimode') return true; + + const loadNotPending = (l, n) => { + const loadState = i18n.services.backendConnector.state[`${l}|${n}`]; + return loadState === -1 || loadState === 2; + }; + + // bound to trigger on event languageChanging + // so set ready to false while we are changing the language + // and namespace pending (depends on having a backend) + if ( + options.bindI18n && + options.bindI18n.indexOf('languageChanging') > -1 && + i18n.services.backendConnector.backend && + i18n.isLanguageChangingTo && + !loadNotPending(i18n.isLanguageChangingTo, ns) + ) + return false; + + // loaded -> SUCCESS + if (i18n.hasResourceBundle(lng, ns)) return true; + + // were not loading at all -> SEMI SUCCESS + if ( + !i18n.services.backendConnector.backend || + (i18n.options.resources && !i18n.options.partialBundledLanguages) + ) + return true; + + // failed loading ns - but at least fallback is not pending -> SEMI SUCCESS + if (loadNotPending(lng, ns) && (!fallbackLng || loadNotPending(lastLng, ns))) return true; + + return false; +}; diff --git a/test/useTranslation.ready.spec.jsx b/test/useTranslation.ready.spec.jsx index 64323c194..87f921193 100644 --- a/test/useTranslation.ready.spec.jsx +++ b/test/useTranslation.ready.spec.jsx @@ -3,6 +3,7 @@ import React from 'react'; import { renderHook, cleanup as cleanupHook } from '@testing-library/react-hooks'; import { render, cleanup } from '@testing-library/react'; import { useTranslation } from '../src/useTranslation'; +import hasLoadedNamespace from './hasLoadedNamespaceMock.js'; vitest.unmock('../src/useTranslation'); @@ -28,6 +29,7 @@ afterEach(() => { getFixedT: () => (message) => message, hasResourceBundle: (lng, ns) => ns === 'alreadyLoadedNS', loadNamespaces: () => {}, + hasLoadedNamespace: (ns) => hasLoadedNamespace(ns, instance), on: () => {}, off: () => {}, options: {}, diff --git a/test/withSSR.spec.jsx b/test/withSSR.spec.jsx index 08749c2a0..c8b1d23da 100644 --- a/test/withSSR.spec.jsx +++ b/test/withSSR.spec.jsx @@ -5,6 +5,7 @@ import i18n from './i18n'; import { setI18n } from '../src/context'; import { withSSR } from '../src/withSSR'; import { useTranslation } from '../src/useTranslation'; +import hasLoadedNamespace from './hasLoadedNamespaceMock.js'; vitest.unmock('../src/withSSR'); @@ -36,6 +37,7 @@ describe('withSSR', () => { hasResourceBundle: (lng, ns) => ns === 'alreadyLoadedNS', getResourceBundle: (lng, ns) => ({ lng, ns }), loadNamespaces: () => { }, + hasLoadedNamespace: (ns) => hasLoadedNamespace(ns, mockI18n), on: () => { }, off: () => { }, };