3

我在一个网站上工作,遇到了一个我似乎无法解决的 i18next 奇怪问题。

我希望 i18next 将当前语言保存在 cookie 中,但前提是用户已接受存储 cookie(自定义 cookie 横幅组件)

目前,我有以下代码:

i18n.js

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    whitelist: ['en', 'fr', 'nl'],
    fallbackLng: 'en',
    interpolation: {
      escapeValue: false,
    },
    ns: [
      'common',
    ],
    react: {
      wait: true,
    },
    detection: {
      caches: ['cookie'],
      lookupFromPathIndex: 0,
    },
  });

index.js

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';

import './i18n';

...

我的问题是caches: ['cookie']物业。我希望它包含“cookie”,但只有在用户单击网站某处的“我同意”按钮之后。有没有办法先用空的缓存数组加载 i18next 并在用户单击“我同意”时重新加载对象。

在下一页访问(当用户接受 cookie 时)i18next 应该知道它也可以从语言 cookie 中读取。但我认为当我知道如何解决第一个问题时,我可以轻松解决这个问题。

4

2 回答 2

0

所以我用以下解决方案解决了这个问题。不确定它是否是最干净的,但它似乎正在工作。

索引.tsx

function getCookieValue(a: string) {
  const b = document.cookie.match(`(^|;)\\s*${a}\\s*=\\s*([^;]+)`);
  return b ? b.pop() : '';
}

if (getCookieValue('accepts-cookies') === 'true') {
  initI18n(['cookie']);
} else {
  initI18n([]);
}

ReactDOM.render(
  <YOUR APP>
)

CookieBanner 组件

import { useCookies } from 'react-cookie';
import { initI18n, getLanguage } from '../../../i18n';
...

export const CookieBanner = () => {
...
const [cookies, setCookie] = useCookies();

const updateCookie = (name: string, value: boolean | string) => {
  const expires = new Date();
  expires.setFullYear(expires.getFullYear() + 1);
  setCookie(name, value, { path: '/', expires });
};

const onAccept = () => {
  updateCookie('accepts-cookies', true);
  const lng = getLanguage();
  updateCookie('i18next', lng);
};

return (
...
)

}

i18n.tsx

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

i18n
  .use(HttpApi)
  .use(LanguageDetector)
  .use(initReactI18next);

export const initI18n = (caches: string[]): void => {
  i18n.init({
    detection: {
      order: ['querystring', 'cookie', 'path', 'navigator'],
      caches
    },
    <YOUR CONFIG>
  })
};

export const getLanguage = (): string => i18n.language || (typeof window !== 'undefined' && window.localStorage.i18nextLng) || 'nl';

解决方案

  1. 当用户接受 cookie 横幅时,我自己创建 i18next cookie。在 CookieBanner 组件的 OnAccept 函数中,我创建了 2 个 cookie(i18next 和 accept-cookies)
  2. 用户当前正在查看网站,因此无需在 i18n 中刷新页面或将缓存设置为“cookie”
  3. 当用户刷新或重新访问网站时,index.tsx 中的 if 语句将检查 cookie 是否被接受,如果接受,将使用 cookie 缓存初始化 i18n。

评论

我现在唯一的问题是,当用户在接受 cookie 后和刷新/重新打开网站之前更改他的语言时,i18n cookie 已设置但 i18n 未通过 cookie 缓存启动,因此它不会将 cookie 更新为新语言。

解决方案是像这样在 onAccept 函数中调用 i18nInit(['cookie'])

const onAccept = () => {
  updateCookie(acceptCookieName, true);
  updateCookieState(true);
  initI18n(['cookie']);   <-- ADD THIS LINE
  const lng = getLanguage();
  updateCookie(i18CookieName, lng);
};

第二次启动 i18n 感觉很奇怪,但它似乎对我有用。如果有人为此问题找到更好的解决方案,请随时发表评论或添加其他解决方案。

于 2021-08-26T18:03:57.810 回答
0

最后,我使用下面的代码片段解决了它。基本上我们用 caches:[] 初始化 i18n,所以没有设置 cookie。设置 cookie 后(我为此使用 Redux,这就是我使用 useSelector 的原因),然后我创建另一个传递给 I18nextProvider 的实例。这样就不需要刷新,我的 LanguageManager 成为应用程序的包装器。嵌套在其中的所有内容都可以访问传递的实例。

const initialLanguageConfiguration = {
  fallbackLng: 'en',
  whitelist: ['en'],
  preload: ['en'],
  load: 'languageOnly',
  ns: namespaces,
  debug: false,
  interpolation: {
    escapeValue: false // not needed for react as it escapes by default
  },
  detection: {
    order: ['querystring'],
    caches: []
  },
  react: {
    // useSuspense: false,
  }
};

const languageConfigurationWithCookies = {
  fallbackLng: 'en',
  whitelist: ['en'],
  preload: ['en'],
  load: 'languageOnly',
  ns: namespaces,
  debug: false,
  interpolation: {
    escapeValue: false // not needed for react as it escapes by default
  },
  detection: {
    order: ['querystring', 'cookie', 'localStorage', 'navigator'],
    caches: ['cookie', 'localStorage'],
    lookupQuerystring: 'lng',
    lookupCookie: 'lng',
    lookupLocalStorage: 'lng',
    cookieOptions: { path: '/', sameSite: 'strict', secure: false }
  },
  react: {
    // useSuspense: false,
  }
};

const getInitialLanguageManager = (): typeof i18n => {
  const initialInstance = i18n.createInstance();
  initialInstance
    .use(HttpApi)
    .use(LanguageDetector)
    .use(initReactI18next)
    .init(initialLanguageConfiguration as any);
  return initialInstance;
};

const getLanguageManagerWithCookies = (): typeof i18n => {
  const instanceWithCookies = i18n.createInstance();
  instanceWithCookies
    .use(HttpApi)
    .use(LanguageDetector)
    .use(initReactI18next)
    .init(languageConfigurationWithCookies as any);
  return instanceWithCookies;
};

const LanguageManager: React.FC = ({ children }): JSX.Element => {
  const { areCookiesSaved } = useSelector(selectCookiesConsent);
  const [languageManagerCurrentInstance, setLanguageManagerCurrentInstance] = useState(
    getInitialLanguageManager()
  );

  useEffect(() => {
    if (areCookiesSaved) {
      setLanguageManagerCurrentInstance(getLanguageManagerWithCookies());
    }
  }, [areCookiesSaved]);

  return <I18nextProvider i18n={languageManagerCurrentInstance}>{children}</I18nextProvider>;
};

export { LanguageManager };

于 2021-09-02T04:56:24.947 回答