I’m developing a Chrome extension using React, TypeScript, and i18next for localization. The extension stores the user’s preferred language in Chrome storage. On initialization, I retrieve the language from storage and attempt to update i18next with i18n.changeLanguage(). While the language change seems successful, components still render with the default language (vi-VN).
Relevant Code:
- i18n/index.ts: This file initializes i18next and checks for the
stored language. - i18n/settings.ts: Defines languages, namespaces, and options for
i18next. - index.tsx: The main component with buttons to switch languages and
display translated text. - helper.tsx: Utility function to render React components.
Steps to Reproduce:
- Install the extension.
- Choose a language different from the fallback (e.g., en-US).
- Observe that the UI still shows text in the fallback language.
- Click the button to switch to the chosen language (e.g., “Change to English”).
- The UI now updates and displays the correct language.
Expected Behavior:
The UI should display the language retrieved from Chrome storage on initial load without needing to manually switch languages.
Actual Behavior:
The UI displays the default language until a language change is triggered manually.
Questions:
- Is my approach to initialize i18next with the stored language correct? If not, what’s a better way?
- Why does the language not update in components despite calling i18n.changeLanguage()?
- How can I ensure the UI reflects the correct language from Chrome storage on initial load?
Additional Information:
- I’ve included relevant code snippets from the project for reference.
- The console log in i18n/index.ts confirms the correct language is retrieved from storage.
Technologies Used:
- Webpack
- ReactJs
- Typescript
- I18next
- React-i18next
Code
project/src/helper.tsx:
import ReactDOM from 'react-dom/client';
import React from 'react';
export function renderReact(
component: React.ReactNode,
container?: HTMLElement,
) {
if (!container) {
container = document.createElement('div');
document.body.appendChild(container);
}
const root = ReactDOM.createRoot(container);
root.render(<React.StrictMode>{component}</React.StrictMode>);
}
project/src/index.tsx:
import { useTranslation } from './i18n';
const Home = () => {
const { t, i18n } = useTranslation();
const [currentLanguage, setCurrentLanguage] = useState(i18n.language);
/// It did change the language instantly, working wel
const handleChangeLanguage = async (newLanguage: string) => {
setCurrentLanguage(newLanguage);
await i18n.changeLanguage(newLanguage);
await chrome.storage.sync.set({ language: newLanguage });
};
return (
<>
Test: {t('s')}
<div>
<button
className={cn('dl-bg-blue-900 dl-text-white dl-p-2 dl-rounded')}
onClick={() => handleChangeLanguage('en-US')}
>
Change To English
</button>
</div>
<div>
<button
className={cn('dl-bg-blue-900 dl-text-white dl-p-2 dl-rounded')}
onClick={() => handleChangeLanguage('vi-VN')}
>
Change To Vietnamese
</button>
</div>
</>
);
}
renderReact(<Home />);
project/src/i18n/index.ts:
import i18n from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
import { defaultNS, getOptions, languages } from './settings';
import {
initReactI18next,
useTranslation as useTranslationOriginal,
} from 'react-i18next';
i18n
.use(initReactI18next)
.use(
resourcesToBackend((language: string, namespace: string) =>
require(`./locales/${language}/${namespace}.json`),
),
)
.init({
...getOptions(),
})
.then(() => {
chrome.storage.sync.get(['language'], (result: any) => {
if (result.hasOwnProperty('language') && result.language) {
console.log('i18n', result.language); // Output: en-US
i18n.changeLanguage(result.language); // Changed language but index.tsx file is still using fallbackLng
}
});
});
export function useTranslation(
lng?: string,
ns: string = defaultNS,
options: any = {},
) {
const ret = useTranslationOriginal(ns, options);
const { i18n } = ret;
if (
lng &&
i18n.resolvedLanguage !== lng &&
Object.keys(languages).includes(lng)
) {
i18n.changeLanguage(lng);
}
return ret;
}
project/src/i18n/settings.ts:
xport const fallbackLng = 'vi-VN';
export const languages = {
'vi-VN': 'Vietnamese',
'en-US': 'English(US)',
};
export const defaultNS = 'translation';
export function getOptions(lng = fallbackLng, ns = defaultNS) {
return {
// debug: true,
supportedLngs: Object.keys(languages),
fallbackLng,
lng,
fallbackNS: defaultNS,
defaultNS,
ns,
};
}
project/src/i18n/locales/vi-VN/translation.json:
{
"s": "Test Vietnamese"
}
project/src/i18n/locales/en-US/translation.json:
{
"s": "Test English"
}