diff --git a/web/src/context/l10n.jsx b/web/src/context/l10n.jsx index a04bb57018..436c04cd77 100644 --- a/web/src/context/l10n.jsx +++ b/web/src/context/l10n.jsx @@ -165,17 +165,21 @@ function findSupportedLanguage(languages) { /** * Reloads the page * - * It uses the window.location.replace instead of the reload function dropping - * the "lang" argument from the URL. + * It uses the window.location.replace instead of the reload function + * synchronizing the "lang" argument from the URL if present. + * + * @param {string} newLanguage */ -function reload() { +function reload(newLanguage) { const query = new URLSearchParams(window.location.search); - query.delete("lang"); - let url = window.location.pathname; - if (query.size > 0) { - url = `${url}?${query.toString()}`; + if (query.has("lang") && query.get("lang") !== newLanguage) { + query.set("lang", newLanguage); + // Calling search() with a different value makes the browser to navigate + // to the new URL. + window.location.search = query.toString(); + } else { + window.location.reload(); } - window.location.replace(url); } /** @@ -198,10 +202,14 @@ function reload() { function L10nProvider({ children }) { const client = useInstallerClient(); const [language, setLanguage] = useState(undefined); + const [backendPending, setBackendPending] = useState(false); const { cancellablePromise } = useCancellablePromise(); const storeBackendLanguage = useCallback(async languageString => { - if (!client) return false; + if (!client) { + setBackendPending(true); + return false; + } const currentLang = await cancellablePromise(client.language.getUILanguage()); const normalizedLang = languageFromBackend(currentLang); @@ -233,7 +241,7 @@ function L10nProvider({ children }) { let mustReload = storeUILanguage(newLanguage); mustReload = await storeBackendLanguage(newLanguage) || mustReload; if (mustReload) { - reload(); + reload(newLanguage); } else { setLanguage(newLanguage); } @@ -243,6 +251,13 @@ function L10nProvider({ children }) { if (!language) changeLanguage(); }, [changeLanguage, language]); + useEffect(() => { + if (!client || !backendPending) return; + + storeBackendLanguage(language); + setBackendPending(false); + }, [client, language, backendPending, storeBackendLanguage]); + return ( {children} ); diff --git a/web/src/context/l10n.test.jsx b/web/src/context/l10n.test.jsx index 47f29ad84c..47e975fbbe 100644 --- a/web/src/context/l10n.test.jsx +++ b/web/src/context/l10n.test.jsx @@ -76,10 +76,10 @@ describe("L10nProvider", () => { const origLocation = window.location; const origNavigator = window.navigator; - // mock window.location.replace + // mock window.location.reload and search beforeAll(() => { delete window.location; - window.location = { replace: jest.fn(), pathname: "/" }; + window.location = { reload: jest.fn(), search: "" }; delete window.navigator; window.navigator = { languages: ["es-es", "cs-cz"] }; @@ -110,14 +110,15 @@ describe("L10nProvider", () => { it("displays the children content and does not reload", async () => { render( - Testing content + ); // children are displayed - await screen.findByText("Testing content"); + await screen.findByText("hello"); - expect(window.location.replace).not.toHaveBeenCalled(); + expect(window.location.search).toEqual(""); + expect(window.location.reload).not.toHaveBeenCalled(); }); }); @@ -135,7 +136,7 @@ describe("L10nProvider", () => { ); - await waitFor(() => expect(window.location.replace).toHaveBeenCalledWith("/")); + await waitFor(() => expect(window.location.reload).toHaveBeenCalled()); // reload the component render( @@ -163,7 +164,7 @@ describe("L10nProvider", () => { ); - await waitFor(() => expect(window.location.replace).toHaveBeenCalledWith("/")); + await waitFor(() => expect(window.location.reload).toHaveBeenCalled()); render( @@ -184,7 +185,7 @@ describe("L10nProvider", () => { ); - await waitFor(() => expect(window.location.replace).toHaveBeenCalledWith("/")); + await waitFor(() => expect(window.location.reload).toHaveBeenCalled()); render( @@ -220,7 +221,8 @@ describe("L10nProvider", () => { expect(setUILanguageFn).not.toHaveBeenCalled(); expect(document.cookie).toEqual("CockpitLang=cs-cz"); - expect(window.location.replace).not.toHaveBeenCalled(); + expect(window.location.reload).not.toHaveBeenCalled(); + expect(window.location.search).toEqual("?lang=cs-CZ"); }); }); @@ -238,7 +240,7 @@ describe("L10nProvider", () => { ); - await waitFor(() => expect(window.location.replace).toHaveBeenCalledWith("/")); + await waitFor(() => expect(window.location.search).toEqual("lang=cs-cz")); // reload the component render( @@ -265,7 +267,7 @@ describe("L10nProvider", () => { ); - await waitFor(() => expect(window.location.replace).toHaveBeenCalledWith("/")); + await waitFor(() => expect(window.location.search).toEqual("lang=cs-cz")); // reload the component render(