From e0794822b1e50864b78e3d9d4e6774dee63d267c Mon Sep 17 00:00:00 2001 From: gunshiz Date: Mon, 14 Sep 2026 16:44:44 +0700 Subject: [PATCH] feat(i18n) : add personal languages and compact weapon exports --- app/admin/[character]/[page]/page.tsx | 5 +- app/admin/[character]/page.tsx | 7 +- app/admin/create/page.tsx | 5 +- app/admin/generate/image/page.tsx | 9 +- app/admin/glossary/page.tsx | 5 +- app/admin/page.tsx | 7 +- app/admin/register/page.tsx | 8 +- app/layout.tsx | 3 + app/not-found.tsx | 8 +- app/page.tsx | 7 +- app/thanks/page.tsx | 39 +- components/admin/admin-header.tsx | 22 +- components/admin/autosave-status.tsx | 21 +- components/admin/create-guide-flow.tsx | 28 +- components/admin/delete-condition-dialog.tsx | 10 +- .../admin/delete-confirmation-dialog.tsx | 12 +- components/admin/editor-presence.tsx | 4 +- components/admin/extra-sheet-editor.tsx | 61 +- components/admin/glossary-manager.tsx | 35 +- components/admin/guide-extra-controls.tsx | 41 +- components/admin/guide-overview-form.tsx | 72 +- components/admin/guide-section-editor.tsx | 568 ++-- components/admin/guide-section-nav.tsx | 11 +- components/admin/guide-visibility-control.tsx | 18 +- components/admin/login-card.tsx | 30 +- components/admin/media-uploader.tsx | 18 +- components/admin/overview-image-generator.tsx | 209 +- components/admin/register-card.tsx | 45 +- components/admin/structured-dashboard.tsx | 83 +- components/deployment-update-notifier.tsx | 22 +- components/i18n/language-dropdown.tsx | 35 + components/i18n/locale-provider.tsx | 33 + components/i18n/text.tsx | 48 + components/public/artifact-image-export.tsx | 23 +- components/public/condition-switcher.tsx | 8 +- components/public/constellation-image.tsx | 18 +- components/public/guide-export-branding.tsx | 16 +- components/public/guide-image-export.tsx | 36 +- components/public/material-poster.tsx | 11 +- components/public/scrollable-nav.tsx | 8 +- components/public/section-drawer.tsx | 13 +- components/public/site-footer.tsx | 4 +- components/public/site-header.tsx | 8 +- components/public/structured-directory.tsx | 15 +- components/public/structured-guide.tsx | 153 +- components/public/team-search.tsx | 73 +- components/public/term-detail.tsx | 6 +- components/public/visitor-presence.tsx | 10 +- components/public/weapon-chart.tsx | 14 +- components/public/weapon-display-mode.tsx | 8 +- components/public/weapon-image-export.tsx | 50 +- components/rich-text-editor.tsx | 50 +- components/tooltip.tsx | 4 +- components/ui/breadcrumb.tsx | 9 +- components/ui/carousel.tsx | 5 +- components/ui/command.tsx | 6 +- components/ui/dialog.tsx | 3 +- components/ui/pagination.tsx | 15 +- components/ui/sheet.tsx | 3 +- components/ui/sidebar.tsx | 13 +- components/ui/spinner.tsx | 6 +- hooks/use-autosave.ts | 19 +- lib/guides/export-text.test.tsx | 9 +- lib/guides/export-text.ts | 15 +- lib/guides/validation-messages.ts | 19 +- lib/i18n/i18n.test.tsx | 106 + lib/i18n/locale-store.ts | 31 + lib/i18n/locales.ts | 12 + lib/i18n/messages.ts | 2878 +++++++++++++++++ public/flags/gb.svg | 1 + public/flags/th.svg | 1 + tests/deployment-contract.test.ts | 2 +- 72 files changed, 4199 insertions(+), 1011 deletions(-) create mode 100644 components/i18n/language-dropdown.tsx create mode 100644 components/i18n/locale-provider.tsx create mode 100644 components/i18n/text.tsx create mode 100644 lib/i18n/i18n.test.tsx create mode 100644 lib/i18n/locale-store.ts create mode 100644 lib/i18n/locales.ts create mode 100644 lib/i18n/messages.ts create mode 100644 public/flags/gb.svg create mode 100644 public/flags/th.svg diff --git a/app/admin/[character]/[page]/page.tsx b/app/admin/[character]/[page]/page.tsx index 3653b0b..f7d90f2 100644 --- a/app/admin/[character]/[page]/page.tsx +++ b/app/admin/[character]/[page]/page.tsx @@ -1,3 +1,4 @@ +import { Localized } from "@/components/i18n/text"; import Link from "next/link"; import Image from "next/image"; import { ArrowLeftIcon } from "lucide-react"; @@ -65,7 +66,7 @@ export default async function StructuredEditorPage({
- +
{guide.name}
diff --git a/app/admin/[character]/page.tsx b/app/admin/[character]/page.tsx index f42b8aa..7777ff4 100644 --- a/app/admin/[character]/page.tsx +++ b/app/admin/[character]/page.tsx @@ -1,3 +1,4 @@ +import { Localized, Text } from "@/components/i18n/text"; import Image from "next/image"; import Link from "next/link"; import { ArrowLeftIcon, ArrowUpRightIcon } from "lucide-react"; @@ -40,14 +41,14 @@ export default async function GuideOverviewPage({ params }: { params: Promise<{
- +
{guide.name}
-

เลือกส่วนที่ต้องการแก้ไข

+

{guide.name}

diff --git a/app/admin/create/page.tsx b/app/admin/create/page.tsx index 82f834b..f5b2447 100644 --- a/app/admin/create/page.tsx +++ b/app/admin/create/page.tsx @@ -1,3 +1,4 @@ +import { Localized, Text } from "@/components/i18n/text"; import Link from "next/link"; import { ArrowLeftIcon } from "lucide-react"; import { redirect } from "next/navigation"; @@ -19,8 +20,8 @@ export default async function CreateGuidePage() {
- -

Catalog ล่าสุดจาก Lunaris

สร้าง Guide ใหม่

+ +

guide.characterKey)} />
diff --git a/app/admin/generate/image/page.tsx b/app/admin/generate/image/page.tsx index cfabbcd..1350084 100644 --- a/app/admin/generate/image/page.tsx +++ b/app/admin/generate/image/page.tsx @@ -1,3 +1,4 @@ +import { Text } from "@/components/i18n/text"; import { redirect } from "next/navigation"; import { connection } from "next/server"; @@ -29,13 +30,11 @@ export default async function OverviewImagePage() {
-

Image generator

+

- สร้างภาพ Overview -

+

- สร้างและดาวน์โหลดภาพ PNG ขนาด 3000×3000 โดยไม่เปลี่ยนภาพใน Guide -

+

({ diff --git a/app/admin/glossary/page.tsx b/app/admin/glossary/page.tsx index 0c75055..d73ab05 100644 --- a/app/admin/glossary/page.tsx +++ b/app/admin/glossary/page.tsx @@ -1,3 +1,4 @@ +import { Text } from "@/components/i18n/text"; import { redirect } from "next/navigation"; import { connection } from "next/server"; @@ -20,8 +21,8 @@ export default async function GlossaryPage() {
-

Search shortcuts

-

Glossary Manager

+

+

-

Content workspace

-

Guide Dashboard

-

จัดการ Guide แบบภาพ-first และบันทึกขึ้นเว็บไซต์ทันที

+

+

+

diff --git a/app/admin/register/page.tsx b/app/admin/register/page.tsx index 6966998..5dd9766 100644 --- a/app/admin/register/page.tsx +++ b/app/admin/register/page.tsx @@ -1,3 +1,4 @@ +import { Text } from "@/components/i18n/text"; import { asc } from "drizzle-orm"; import { redirect } from "next/navigation"; import { connection } from "next/server"; @@ -28,11 +29,10 @@ export default async function RegisterPage() {
-

Admin access

-

จัดการบัญชี

+

+

- สร้างบัญชีให้ผู้ดูแลคนอื่น หรือลบบัญชีที่ไม่ต้องใช้งานแล้ว -

+

diff --git a/app/layout.tsx b/app/layout.tsx index 5f9006f..ba60593 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Anuphan, Geist_Mono } from "next/font/google"; +import { LocaleProvider } from "@/components/i18n/locale-provider"; import { DeploymentUpdateNotifier } from "@/components/deployment-update-notifier"; import { PageProgress } from "@/components/page-progress"; import { Toaster } from "@/components/ui/sonner"; @@ -38,6 +39,7 @@ export default function RootLayout({ children }: LayoutProps<"/">) { className={`dark ${anuphan.variable} ${geistMono.variable} h-full antialiased`} > + {children} @@ -47,6 +49,7 @@ export default function RootLayout({ children }: LayoutProps<"/">) { position="bottom-right" toastOptions={{ className: anuphan.className }} /> + ); diff --git a/app/not-found.tsx b/app/not-found.tsx index 92b1ab5..26eb2e6 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,3 +1,4 @@ +import { Text } from "@/components/i18n/text"; import Link from "next/link"; import { MapPinOffIcon } from "lucide-react"; @@ -20,13 +21,12 @@ export default function NotFound() { - ไม่พบคู่มือนี้ + - หน้านี้อาจถูกซ่อน เปลี่ยนชื่อ หรือยังไม่มีเนื้อหาที่เผยแพร่ - + - +
diff --git a/app/page.tsx b/app/page.tsx index d34fa5c..819b148 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,3 +1,4 @@ +import { Text } from "@/components/i18n/text"; import { connection } from "next/server"; import { Suspense } from "react"; @@ -21,11 +22,9 @@ export default function HomePage() {

- เลือกตัวละครแล้วส่องดูได้เลย -

+

- ไกด์ตัวละคร อาวุธ อาร์ติแฟกต์ กลุ่มดาว และคำนวณทีม -

+

@@ -87,11 +88,9 @@ export default function ThanksPage() {

- Special Thanks -

+

- ขอบคุณทุกคนที่มอบแรงสนับสนุนและเวลาให้เว็บไซต์นี้เกิดขึ้นได้ -

+

@@ -105,16 +104,13 @@ export default function ThanksPage() { className="row-span-2 size-16 object-contain" /> - Astral Development - + - ผู้สนับสนุนหลักด้าน Development - +

- ขอบคุณที่สนับสนุนระบบ Hosting ที่สามารถให้เว็บออนไลน์และพร้อมใช้งาน -

+

{specialThanks.length > 0 ? ( -
@@ -138,21 +133,20 @@ export default function ThanksPage() { - + /> {getInitials(person.username)} {person.username} {person.role ? ( - {person.role} + ) : null}

- ขอบคุณที่เป็นส่วนสำคัญของเว็บไซด์นี้ -

+

))} -
+
) : ( -
I am sigma number one
+
)}
diff --git a/components/admin/admin-header.tsx b/components/admin/admin-header.tsx index 6da1140..b36c28d 100644 --- a/components/admin/admin-header.tsx +++ b/components/admin/admin-header.tsx @@ -1,5 +1,7 @@ "use client"; +import { useTranslation } from "@/components/i18n/locale-provider"; +import { LanguageDropdown } from "@/components/i18n/language-dropdown"; import Image from "next/image"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; @@ -17,6 +19,7 @@ import { Button } from "@/components/ui/button"; import { authClient } from "@/lib/auth/client"; export function AdminHeader() { + const { t } = useTranslation(); const pathname = usePathname(); const router = useRouter(); @@ -28,9 +31,10 @@ export function AdminHeader() { className="mr-auto flex min-h-11 items-center gap-3 rounded-lg font-heading font-semibold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" > - ไกด์เกนชินไม่ใช่เกมมือถือ + {t("ไกด์เกนชินไม่ใช่เกมมือถือ")} -