Files
infiplot-web/app/[locale]/new/page.tsx
T
yuanzonghao 0a7076d5b9 fix(i18n): overhaul i18n with [locale] routing, SSR translations, and hreflang SEO
Rewrites the i18n system introduced in PR #94 to use Next.js App Router
[locale] dynamic segments with SSR-rendered translations and proper
middleware locale routing.

- Add middleware locale detection: / rewrites to /zh-CN/ internally,
  /en and /ja pass through, /zh-CN/... redirects to bare path
- Move all 7 pages under app/[locale]/ with SSR translation injection
- Fix server→client serialization: pre-evaluate function-valued
  translations (makeSerializable) to eliminate hydration flash
- Fix language switch key flash: use hard navigation with localStorage-
  only persistence, avoiding React state update before page reload
- Add <link rel="alternate" hreflang> tags for multilingual SEO
- Fix Supabase setAll overwriting locale rewrite response
- Trim locales from 22 to 3 (zh-CN/en/ja), delete 19 incomplete files
- LLM-translate 240 firstact game preset JSONs (en + ja, landscape +
  portrait) and story titles via gemini-3.5-flash
- Delete 11 one-off migration scripts and outdated i18n docs
- Add useLocalePath hook and navigation utilities

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-18 23:16:17 +08:00

69 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from "next/link";
import { CustomForm } from "@/components/CustomForm";
import { localePath } from "@/lib/i18n/navigation";
import { isValidLocale } from "@/lib/i18n/utils";
import { DEFAULT_LOCALE, type Locale } from "@/lib/i18n/config";
export default async function NewPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale: rawLocale } = await params;
const locale: Locale = isValidLocale(rawLocale) ? rawLocale : DEFAULT_LOCALE;
const lp = (path: string) => localePath(path, locale);
return (
<div className="min-h-screen flex flex-col">
<header className="px-6 md:px-16 pt-7 md:pt-10 flex items-center justify-between">
<Link
href={lp("/")}
className="text-[10px] smallcaps text-clay-700 hover:text-clay-900 transition-colors flex items-center gap-2"
>
<i className="fa-solid fa-arrow-left text-[9px]" />
InfiPlot
</Link>
<span className="text-[10px] smallcaps text-clay-500">
</span>
</header>
<section className="px-6 md:px-16 pt-20 md:pt-32 pb-20 md:pb-24 flex-1">
<div className="grid grid-cols-12 gap-8 md:gap-16 max-w-6xl">
<div className="col-span-12 md:col-span-4 animate-fade-in">
<p className="text-[10px] smallcaps text-clay-500 mb-6">
·
</p>
<h1 className="font-serif text-[44px] md:text-[64px] text-clay-900 leading-[0.96] mb-8">
写下
<br />
<em className="italic text-clay-600">两段</em>
<br />
文字。
</h1>
<div className="hairline w-12 mb-6" />
<p className="font-serif text-base text-clay-700 leading-[1.7]">
第一段,勾勒故事所在的世界。第二段,描述世界应是什么模样
它的介质、氛围、颗粒感。
</p>
<p className="font-serif italic text-sm text-clay-500 mt-5 leading-relaxed">
两栏皆可任意语言。越具体,回报越具体。
</p>
</div>
<div className="col-span-12 md:col-span-7 md:col-start-6">
<CustomForm />
</div>
</div>
</section>
<footer className="px-6 md:px-16 pb-8">
<div className="hairline-full w-full mb-4" />
<div className="flex items-center justify-between text-[10px] smallcaps text-clay-500">
<span>MMXXVI</span>
<span className="num"> · </span>
</div>
</footer>
</div>
);
}