Skip to content

Commit

Permalink
feat: localization 설정
Browse files Browse the repository at this point in the history
  • Loading branch information
danah-kim committed Nov 20, 2024
1 parent d3a98c2 commit cb8b19d
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 20 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
"dependencies": {
"basic-styled": "^0.0.8",
"firebase": "^10.14.0",
"i18next": "^23.16.6",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-i18next": "^15.1.1",
"react-router-dom": "^6.26.2",
"zustand": "^5.0.0-rc.2"
},
Expand Down
4 changes: 4 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Plandy | Reminders & Calendar",
"intro_text": "Simple and easy task Manager"
}
4 changes: 4 additions & 0 deletions public/locales/ja/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Plandy: \u30EA\u30DE\u30A4\u30F3\u30C0\u30FC\u0020\uFF06\u0020\u30AB\u30EC\u30F3\u30C0\u30FC",
"intro_text": "\u30B7\u30F3\u30D7\u30EB\u3067\u7C21\u5358\u306A\u30BF\u30B9\u30AF\u7BA1\u7406"
}
4 changes: 4 additions & 0 deletions public/locales/ko/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Plandy | \uB9AC\uB9C8\uC778\uB354\u0020\u0026\u0020\uB2EC\uB825",
"intro_text": "\uBE60\uB974\uACE0\u0020\uAC04\uD3B8\uD55C\u0020\uC77C\uC815\uACFC\u0020\uD560\uC77C\u0020\uAD00\uB9AC"
}
20 changes: 14 additions & 6 deletions src/components/molecules/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,27 @@ import Select, { Option } from "@components/atoms/Select";
import useThemeStore from "@stores/theme";
import { GoogleAnalytics } from "@utils/google-analytics";

import i18n from "@utils/i18n";

import { Adornment, HeaderInner, Logo, StyledHeader } from "./Header.styles";

function Header() {
const mode = useThemeStore((state) => state.mode);
const updateTrigger = useThemeStore((state) => state.updateTrigger);
const updateMode = useThemeStore((state) => state.updateMode);

const [language, setLanguage] = useState("english");
const [language, setLanguage] = useState(i18n.language);

const handleClick = () => {
updateTrigger("manual");
updateMode(mode === "dark" ? "light" : "dark");
handleLogEvent("theme");
};

const handleChange = (newValue?: string) => setLanguage(newValue || "english");
const handleChangeLang = (newLang?: string) => {
i18n.changeLanguage(newLang);
setLanguage(newLang || "en");
};

const handleLogEvent = (label: string) => {
GoogleAnalytics.logEvent("click_top_nav", {
Expand Down Expand Up @@ -58,15 +63,18 @@ function Header() {
data-testid="language-button"
aria-label={language}
size="small"
onChange={handleChange}
onChange={handleChangeLang}
value={language}
endIcon={<Icon name="ArrowDownBold" width={14} height={14} />}
>
<Option data-testid="language-option-korea" value="korea">
<Option data-testid="language-option-english" value="en">
English
</Option>
<Option data-testid="language-option-korea" value="ko">
한국어
</Option>
<Option data-testid="language-option-english" value="english">
English
<Option data-testid="language-option-japanese" value="ja">
日本語
</Option>
</Select>
<Button
Expand Down
24 changes: 24 additions & 0 deletions src/components/utils/GlobalStyle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,30 @@ const StyledGlobal = styled.style`
flex-direction: column;
min-height: 100%;
}
@-webkit-keyframes bounceDelay {
0%,
80%,
100% {
-webkit-transform: scale(0);
}
40% {
-webkit-transform: scale(1);
}
}
@keyframes bounceDelay {
0%,
80%,
100% {
-webkit-transform: scale(0);
transform: scale(0);
}
40% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
`;

export default GlobalStyle;
47 changes: 47 additions & 0 deletions src/components/utils/Loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import styled from "basic-styled";

function Loading() {
return (
<StyledSpinner>
<Bounce1 />
<Bounce2 />
<Bounce3 />
</StyledSpinner>
);
}

const StyledSpinner = styled.div`
display: flex;
justify-content: center;
gap: 20px;
width: 100%;
flex: 1 1 0;
transform: translateY(50%);
& > div {
width: 18px;
height: 18px;
background-color: ${({ theme: { palette } }) => palette.tertiary};
border-radius: 100%;
display: inline-block;
-webkit-animation: bounceDelay 1.4s infinite ease-in-out both;
animation: bounceDelay 1.4s infinite ease-in-out both;
}
`;

const Bounce1 = styled.div`
-webkit-animation-delay: -0.32s !important;
animation-delay: -0.32s !important;
`;

const Bounce2 = styled.div`
-webkit-animation-delay: -0.16s !important;
animation-delay: -0.16s !important;
`;

const Bounce3 = styled.div`
-webkit-animation-delay: -1.4s !important;
animation-delay: -1.4s !important;
`;

export default Loading;
24 changes: 14 additions & 10 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { StrictMode } from "react";
import { StrictMode, Suspense } from "react";

import { HelmetProvider } from "react-helmet-async";
import { BrowserRouter } from "react-router-dom";
import "@utils/i18n";

import ReactDOM from "react-dom/client";
import { createRoot } from "react-dom/client";

import Loading from "@components/utils/Loading";
import ThemeProvider from "@providers/ThemeProvider";
import { GoogleAnalytics } from "@utils/google-analytics";

import App from "./App";

GoogleAnalytics.initialize();

ReactDOM.createRoot(document.getElementById("root")!).render(
createRoot(document.getElementById("root")!).render(
<StrictMode>
<HelmetProvider>
<ThemeProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</ThemeProvider>
</HelmetProvider>
<Suspense fallback={<Loading />}>
<HelmetProvider>
<ThemeProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</ThemeProvider>
</HelmetProvider>
</Suspense>
</StrictMode>
);
10 changes: 6 additions & 4 deletions src/pages/_components/Intro/Intro.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useTranslation } from "react-i18next";

import Box from "@components/atoms/Box";
import Button from "@components/atoms/Button";
import Icon from "@components/atoms/Icon";
Expand All @@ -6,18 +8,18 @@ import Typography from "@components/atoms/Typography";
import { ImageBox, StyledIntro, SubTitle, Title, TitleBox } from "./Intro.styles";

function Intro() {
const { t } = useTranslation();

return (
<StyledIntro>
<TitleBox>
<Title>Plandy</Title>
<SubTitle>Simple schedule planner with Apple CloudKit</SubTitle>
<SubTitle>{t("intro_text")}</SubTitle>
<Box mt={2}>
<Button
variant="text"
startIcon={<Icon name="ImportBold" />}
onClick={() =>
window.open("https://apps.apple.com/us/app/plandy-reminders-calendar/id6736831438")
}
onClick={() => window.open("https://apps.apple.com/us/app/id6736831438")}
>
<Typography>Download On the</Typography>
<Typography variant="title" fontWeight={700}>
Expand Down
35 changes: 35 additions & 0 deletions src/utils/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { initReactI18next } from "react-i18next";

import i18n from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from "i18next-http-backend";

import translationEN from "../../public/locales/en/translation.json";
import translationJA from "../../public/locales/ja/translation.json";
import translationKO from "../../public/locales/ko/translation.json";

export const resources = {
en: {
translation: translationEN
},
ko: {
translation: translationKO
},
ja: {
translation: translationJA
}
};

i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources,
fallbackLng: "en",
interpolation: {
escapeValue: false
}
});

export default i18n;

0 comments on commit cb8b19d

Please sign in to comment.