Skip to content

Commit 0f858c5

Browse files
committed
다국어 i18n 처리
1 parent 911948c commit 0f858c5

File tree

39 files changed

+692
-107
lines changed

39 files changed

+692
-107
lines changed

src/app/avatar/upload/page.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import { type PutBlobResult } from "@vercel/blob";
44
import { upload } from "@vercel/blob/client";
55
import { useState, useRef } from "react";
6+
import { useTranslation } from "@/hooks/useTranslation";
67

78
export default function AvatarUploadPage() {
9+
const { t } = useTranslation();
810
const inputFileRef = useRef<HTMLInputElement>(null);
911
const [blob, setBlob] = useState<PutBlobResult | null>(null);
1012
return (
1113
<>
12-
<h1>Upload Your Avatar</h1>
14+
<h1>{t("login.uploadAvatar")}</h1>
1315

1416
<form
1517
onSubmit={async (event) => {
@@ -30,7 +32,7 @@ export default function AvatarUploadPage() {
3032
}}
3133
>
3234
<input name="file" ref={inputFileRef} type="file" required />
33-
<button type="submit">Upload</button>
35+
<button type="submit">{t("common.upload")}</button>
3436
</form>
3537
{blob && (
3638
<div>

src/app/not-found.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import styles from "./not-found.module.scss";
44
import { DefaultTerminal } from "@/components/common/terminal";
55
import { NOT_FOUND_SEQUENCE } from "@/constants/sequence/sequence";
6+
import { useTranslation } from "@/hooks/useTranslation";
67

78
export default function NotFound() {
9+
const { t } = useTranslation();
810
return (
911
<main className={styles.main}>
1012
<DefaultTerminal
1113
redirect="/"
12-
title="404 Not Found"
14+
title={t("notFound.title")}
1315
command={{ key: "Escape", name: "ESC" }}
1416
sequence={NOT_FOUND_SEQUENCE}
1517
/>

src/app/posts/[index]/edit/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import { setMarkdownValue, setTitle } from "@/store/modules/post";
1212
import AuthorizationComponents from "@/components/common/authorizationComponents";
1313
import { getPost } from "@/app/api/posts";
1414
import { useImageUpload } from "@/hooks/useImageUpload";
15+
import { useTranslation } from "@/hooks/useTranslation";
1516

1617
const MarkdownEditor = dynamic(
1718
() => import("@uiw/react-markdown-editor").then((mod) => mod.default),
1819
{ ssr: false }
1920
);
2021

2122
export default function PostEditPage() {
23+
const { t } = useTranslation();
2224
const dispatch = useAppDispatch();
2325
const device = useDevice();
2426
const pathname = usePathname();
@@ -51,7 +53,7 @@ export default function PostEditPage() {
5153
className={styles.title_input}
5254
type="text"
5355
id="title"
54-
placeholder="제목을 입력하세요."
56+
placeholder={t("posts.titlePlaceholder")}
5557
onChange={(e) => dispatch(setTitle(e.target.value))}
5658
value={title}
5759
/>

src/app/posts/[index]/loading.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1+
"use client";
2+
13
import styles from "./page.module.scss";
4+
import { useTranslation } from "@/hooks/useTranslation";
25

36
const SkeletonPostDetail = () => {
7+
const { t } = useTranslation();
48
return (
59
<>
6-
<h1 className={styles.title}>Now Loading...</h1>
7-
<p>Please wait a moment</p>
10+
<h1 className={styles.title}>{t("posts.nowLoading")}</h1>
11+
<p>{t("posts.pleaseWait")}</p>
812
</>
913
);
1014
};
1115

12-
export default async function PostDetailPage() {
16+
export default function PostDetailPageLoading() {
1317
return (
1418
<main id="main-page" role="main" className={styles.main}>
1519
<SkeletonPostDetail />

src/app/posts/create/page.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import { useAppDispatch, useAppSelector } from "@/hooks/reduxHook";
88
import { setMarkdownValue, setTitle } from "@/store/modules/post";
99
import AuthorizationComponents from "@/components/common/authorizationComponents";
1010
import { useImageUpload } from "@/hooks/useImageUpload";
11+
import { useTranslation } from "@/hooks/useTranslation";
1112

1213
const MarkdownEditor = dynamic(
1314
() => import("@uiw/react-markdown-editor").then((mod) => mod.default),
1415
{ ssr: false }
1516
);
1617

1718
export default function CreatePage() {
19+
const { t } = useTranslation();
1820
const device = useDevice();
1921
const dispatch = useAppDispatch();
2022
const theme = useAppSelector((state) => state.theme.theme);
@@ -29,7 +31,7 @@ export default function CreatePage() {
2931
className={styles.title_input}
3032
type="text"
3133
id="title"
32-
placeholder="제목을 입력하세요."
34+
placeholder={t("posts.titlePlaceholder")}
3335
onChange={(e) => dispatch(setTitle(e.target.value))}
3436
value={title}
3537
/>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"use client";
2+
3+
import { useEffect } from "react";
4+
import { useAppSelector } from "@/hooks/reduxHook";
5+
import { selectLocale } from "@/store/modules/language";
6+
7+
/**
8+
* Redux에 저장된 locale을 document.documentElement.lang에 동기화합니다.
9+
*/
10+
export function SyncHtmlLang() {
11+
const locale = useAppSelector(selectLocale);
12+
13+
useEffect(() => {
14+
if (typeof document !== "undefined") {
15+
document.documentElement.lang = locale;
16+
}
17+
}, [locale]);
18+
19+
return null;
20+
}

src/components/common/header/CreateHeader/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
"use client";
2+
13
import Link from "next/link";
24
import styles from "./styles.module.scss";
35
import { DefaultLogo } from "@/components/common/logo";
6+
import { useTranslation } from "@/hooks/useTranslation";
47

58
const CreateHeader = () => {
9+
const { t } = useTranslation();
610
return (
711
<header className={styles.header}>
812
<section className={styles.content}>
913
<DefaultLogo size="small" />
10-
<Link href="/posts">POST</Link>
14+
<Link href="/posts">{t("posts.navPost").toUpperCase()}</Link>
1115
</section>
1216
</header>
1317
);

src/components/common/header/PostsHeader/index.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import { DefaultLogo } from "@/components/common/logo";
1010
import { useAppDispatch, useAppSelector } from "@/hooks/reduxHook";
1111
import { setResetPost } from "@/store/modules/post";
1212
import { deletePost, postPost, putPost } from "@/app/api/posts";
13+
import { useTranslation } from "@/hooks/useTranslation";
1314

1415
const PostsHeader = () => {
16+
const { t } = useTranslation();
1517
const dispatch = useAppDispatch();
1618
const user = useAppSelector((state) => state.user);
1719
const { title, markdownValue } = useAppSelector((state) => state.post);
@@ -53,8 +55,8 @@ const PostsHeader = () => {
5355
<header className={styles.header}>
5456
<nav className={styles.nav}>
5557
<DefaultLogo size="small" withText />
56-
{isPosts && isAdmin && <Link href="/posts/create">CREATE</Link>}
57-
{!isPosts && <Link href="/posts">POST</Link>}
58+
{isPosts && isAdmin && <Link href="/posts/create">{t("posts.create").toUpperCase()}</Link>}
59+
{!isPosts && <Link href="/posts">{t("posts.navPost").toUpperCase()}</Link>}
5860
</nav>
5961

6062
{isAdmin && !isPosts && (
@@ -71,7 +73,7 @@ const PostsHeader = () => {
7173
}
7274
disabled={loading}
7375
>
74-
{isEdit ? "save" : "create"}
76+
{isEdit ? t("common.save") : t("common.create")}
7577
</button>
7678
<Link
7779
href={isEdit ? `/posts/${index}` : "/posts"}
@@ -80,7 +82,7 @@ const PostsHeader = () => {
8082
}`}
8183
onClick={() => handleButton("cancel")}
8284
>
83-
cancel
85+
{t("common.cancel")}
8486
</Link>
8587
</>
8688
) : (
@@ -91,7 +93,7 @@ const PostsHeader = () => {
9193
loading && styles.loading
9294
}`}
9395
>
94-
edit
96+
{t("common.edit")}
9597
</Link>
9698
<button
9799
type="button"
@@ -101,7 +103,7 @@ const PostsHeader = () => {
101103
onClick={() => handleButton("delete")}
102104
disabled={loading}
103105
>
104-
delete
106+
{t("common.delete")}
105107
</button>
106108
</>
107109
)}

src/components/common/logo/DefaultLogo/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
"use client";
2+
13
import Image from "next/image";
24
import Link from "next/link";
35
import styles from "./styles.module.scss";
6+
import { useTranslation } from "@/hooks/useTranslation";
47

58
interface IProps {
69
size?: "small" | "medium" | "large";
710
withText?: boolean;
811
}
912

1013
const DefaultLogo = ({ size = "medium", withText }: IProps) => {
14+
const { t } = useTranslation();
1115
const logoSize = {
1216
small: { width: 24, height: 24 },
1317
medium: { width: 48, height: 48 },
@@ -18,11 +22,11 @@ const DefaultLogo = ({ size = "medium", withText }: IProps) => {
1822
<Link href="/" passHref className={styles.link}>
1923
<Image
2024
src="/favicon.ico"
21-
alt="logo"
25+
alt={t("common.logoAlt")}
2226
width={logoSize[size].width}
2327
height={logoSize[size].height}
2428
/>
25-
{withText && <p>HOME</p>}
29+
{withText && <p>{t("common.home").toUpperCase()}</p>}
2630
</Link>
2731
);
2832
};

src/components/main/card/ArrowCard/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
"use client";
2+
13
import styles from "./styles.module.scss";
24

35
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
46
import { faAngleLeft, faAngleRight } from "@fortawesome/free-solid-svg-icons";
57
import { useAppDispatch } from "@/hooks/reduxHook";
68
import { setOffset } from "@/store/modules/post";
9+
import { useTranslation } from "@/hooks/useTranslation";
710

811
export const ArrowCard = () => {
12+
const { t } = useTranslation();
913
const dispatch = useAppDispatch();
1014

1115
const handleArrow = (type: "prev" | "next") => {
@@ -20,7 +24,7 @@ export const ArrowCard = () => {
2024
return (
2125
<article id="arrow-card" className={`button-card-shadow ${styles.card}`}>
2226
<button
23-
aria-label="left-arrow"
27+
aria-label={t("common.leftArrow")}
2428
id="arrow-left"
2529
type="button"
2630
className={styles.arrow}
@@ -29,7 +33,7 @@ export const ArrowCard = () => {
2933
<FontAwesomeIcon icon={faAngleLeft} size="xl" />
3034
</button>
3135
<button
32-
aria-label="right-arrow"
36+
aria-label={t("common.rightArrow")}
3337
id="arrow-right"
3438
type="button"
3539
className={styles.arrow}

0 commit comments

Comments
 (0)