Use a dedicated i18next instance instead of the global singleton

Element Call configured the global i18next singleton. When Element Call
runs embedded in a host application rather than as its own page, that
singleton belongs to the host, so configuring it would clobber the
host's translations.

Create Element Call's own instance in utils/i18n.ts, configure it in the
initializer, and pass it to components via <I18nextProvider>. Drop
.use(initReactI18next) from the initializer: it registers the instance
as react-i18next's global default, which is the global we are trying to
avoid. Tests and stories keep using it, so that they do not need to wrap
every render in a provider.

Two modules imported `t` directly from "i18next" and so were bound to
the global instance: utils/errors.ts now calls i18n.t() on the instance
(reached at call time, since i18next only assigns `t` during init), and
QrCode uses useTranslation() like every other component.

No functional change.
This commit is contained in:
Valere
2026-09-01 18:30:05 +02:00
parent efdee7dcdb
commit 7485b3d71f
7 changed files with 77 additions and 46 deletions
+19 -15
View File
@@ -17,6 +17,7 @@ import { BrowserRouter, Route, useLocation, Routes } from "react-router-dom";
import * as Sentry from "@sentry/react";
import { TooltipProvider } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/lib/logger";
import { I18nextProvider } from "react-i18next";
import { HomePage } from "./home/HomePage";
import { LoginPage } from "./auth/LoginPage";
@@ -32,6 +33,7 @@ import { type AppViewModel } from "./state/AppViewModel";
import { MediaDevicesContext } from "./MediaDevicesContext";
import { getUrlParams, HeaderStyle, useUrlParams } from "./UrlParams";
import { AppBar } from "./AppBar";
import { i18n } from "./utils/i18n";
const SentryRoute = Sentry.withSentryReactRouterV7Routing(Route);
@@ -98,20 +100,22 @@ export const App: FC<Props> = ({ vm }) => {
);
return (
<BrowserRouter>
<BackgroundProvider>
<ThemeProvider>
<TooltipProvider>
<Suspense fallback={null}>
{header === HeaderStyle.AppBar ? (
<AppBar>{content}</AppBar>
) : (
content
)}
</Suspense>
</TooltipProvider>
</ThemeProvider>
</BackgroundProvider>
</BrowserRouter>
<I18nextProvider i18n={i18n}>
<BrowserRouter>
<BackgroundProvider>
<ThemeProvider>
<TooltipProvider>
<Suspense fallback={null}>
{header === HeaderStyle.AppBar ? (
<AppBar>{content}</AppBar>
) : (
content
)}
</Suspense>
</TooltipProvider>
</ThemeProvider>
</BackgroundProvider>
</BrowserRouter>
</I18nextProvider>
);
};