diff --git a/src/App.test.tsx b/src/App.test.tsx
index c10622f..7e5e4ca 100644
--- a/src/App.test.tsx
+++ b/src/App.test.tsx
@@ -2,7 +2,7 @@ import { HelmetProvider } from "react-helmet-async";
import { MemoryRouter, type MemoryRouterProps } from "react-router-dom";
-import { render, screen } from "@testing-library/react";
+import { render, screen, waitFor } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import ThemeProvider from "@providers/ThemeProvider";
@@ -30,15 +30,21 @@ describe("상단 네비게이션", () => {
const languageButton = await screen.findByTestId("language-button");
expect(languageButton).toBeInTheDocument();
await userEvent.click(languageButton);
- expect(await screen.findByTestId(/language-option-korea/)).toBeInTheDocument();
+ expect(await screen.findByTestId(/language-option-korean/)).toBeInTheDocument();
expect(await screen.findByTestId(/language-option-english/)).toBeInTheDocument();
if (languageButton.getAttribute("aria-label") === "한국어") {
await userEvent.click(screen.getByTestId("language-option-english"));
- expect(languageButton.getAttribute("aria-label")).equal("english");
+ await waitFor(async () => {
+ const languageButton = await screen.findByTestId("language-button");
+ expect(languageButton.getAttribute("aria-label")).equal("English");
+ });
} else {
- await userEvent.click(screen.getByTestId("language-option-korea"));
- expect(languageButton.getAttribute("aria-label")).equal("korea");
+ await userEvent.click(screen.getByTestId("language-option-korean"));
+ await waitFor(async () => {
+ const languageButton = await screen.findByTestId("language-button");
+ expect(languageButton.getAttribute("aria-label")).equal("한국어");
+ });
}
});
diff --git a/src/App.tsx b/src/App.tsx
index ea2db8f..b2ba992 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,7 @@
import { Route, Routes } from "react-router-dom";
import ErrorBoundary from "@components/utils/ErrorBoundary";
+import LangLayout from "@components/utils/LangLayout";
import Error404Page from "@pages/error/404/page";
import Error500Page from "@pages/error/500/page";
import HowToConnectPage from "@pages/faq/how-to-connect/page";
@@ -25,6 +26,13 @@ function App() {
/>
} />
} />
+ }>
+ } />
+ } />
+ } />
+ } />
+ } />
+
} />
diff --git a/src/components/molecules/Footer/Footer.tsx b/src/components/molecules/Footer/Footer.tsx
index 13e157e..b74ebb3 100644
--- a/src/components/molecules/Footer/Footer.tsx
+++ b/src/components/molecules/Footer/Footer.tsx
@@ -1,4 +1,4 @@
-import { Link } from "react-router-dom";
+import { Link, useParams } from "react-router-dom";
import Button from "@components/atoms/Button";
import Container from "@components/atoms/Container";
@@ -7,6 +7,10 @@ import { GoogleFirebase } from "@utils/google-firebase";
import { Copyright, Divider, InfoBox, PolicyButtonGroup, StyledFooter } from "./Footer.styles";
function Footer() {
+ const { lang } = useParams();
+
+ const prefixUrlLang = lang ? `/${lang}` : "";
+
const handleLogEvent = (label: string) => {
GoogleFirebase.logEvent("click_top_nav", {
item_name: label
@@ -20,12 +24,12 @@ function Footer() {
Copyright © {new Date().getFullYear()} Plandy
- handleLogEvent("terms")}>
+ handleLogEvent("terms")}>
- handleLogEvent("privacy")}>
+ handleLogEvent("privacy")}>
diff --git a/src/components/molecules/Header/Header.tsx b/src/components/molecules/Header/Header.tsx
index 96be095..f76e067 100644
--- a/src/components/molecules/Header/Header.tsx
+++ b/src/components/molecules/Header/Header.tsx
@@ -1,6 +1,5 @@
-import { useState } from "react";
-
-import { Link } from "react-router-dom";
+import { useTranslation } from "react-i18next";
+import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import Button from "@components/atoms/Button";
import Container from "@components/atoms/Container";
@@ -9,31 +8,21 @@ import Select, { Option } from "@components/atoms/Select";
import useThemeStore from "@stores/theme";
import { GoogleFirebase } from "@utils/google-firebase";
-import i18n from "@utils/i18n";
+import { matchSupportLanguage, SupportLanguage } from "@utils/i18n";
import { Adornment, HeaderInner, Logo, StyledHeader } from "./Header.styles";
-const Language = {
- en: {
- name: "English",
- value: "english"
- },
- ko: {
- name: "한국어",
- value: "korean"
- },
- ja: {
- name: "日本語",
- value: "japanese"
- }
-};
-
function Header() {
+ const { i18n } = useTranslation();
+ const navigate = useNavigate();
+ const { pathname } = useLocation();
+ const { lang } = useParams();
+
const mode = useThemeStore((state) => state.mode);
const updateTrigger = useThemeStore((state) => state.updateTrigger);
const updateMode = useThemeStore((state) => state.updateMode);
- const [language, setLanguage] = useState(i18n.language);
+ const prefixUrlLang = lang ? `/${lang}` : "";
const handleClick = () => {
updateTrigger("manual");
@@ -42,8 +31,7 @@ function Header() {
};
const handleChangeLang = (newLang?: string) => {
- i18n.changeLanguage(newLang);
- setLanguage(newLang || "en");
+ navigate(`/${newLang}${pathname.replace(prefixUrlLang, "")}`);
};
const handleLogEvent = (label: string) => {
@@ -56,7 +44,7 @@ function Header() {