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>
This commit is contained in:
yuanzonghao
2026-06-18 23:16:17 +08:00
parent 941b54c3f8
commit 0a7076d5b9
301 changed files with 2447 additions and 4358 deletions
+228
View File
@@ -0,0 +1,228 @@
import type { Metadata } from "next";
import Link from "next/link";
import { localePath } from "@/lib/i18n/navigation";
import { isValidLocale } from "@/lib/i18n/utils";
import { DEFAULT_LOCALE, type Locale } from "@/lib/i18n/config";
export const metadata: Metadata = {
title: "隐私政策 — InfiPlot",
description: "InfiPlot 隐私政策:了解我们如何收集、使用和保护您的个人信息。",
};
export default async function PrivacyPage({
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 (
<main className="mx-auto w-full max-w-3xl px-6 md:px-16 py-16 md:py-24">
<Link
href={lp("/")}
className="inline-flex items-center gap-2 text-clay-500 hover:text-ember-500 transition-colors text-sm mb-12"
>
<i className="fa-solid fa-arrow-left text-xs" />
<span></span>
</Link>
<h1 className="font-serif text-3xl md:text-4xl text-clay-900 mb-4">
</h1>
<p className="text-sm text-clay-500 mb-12">
2026 6 14 &nbsp;|&nbsp; 2026 6 14
</p>
<div className="hairline-full w-full mb-12" />
<div className="space-y-10 text-clay-800 text-[15px] leading-[1.85]">
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3"></h2>
<p>
InfiPlot"我们" AI
使
InfiPlot 使
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
</h2>
<p className="mb-3">
Google GitHub
</p>
<ul className="list-disc pl-6 space-y-1">
<li></li>
<li></li>
<li> URL</li>
</ul>
<p className="mt-3">
</p>
<p className="mt-3">
AI
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
使
</h2>
<p></p>
<ul className="list-disc pl-6 space-y-1 mt-3">
<li>
<strong></strong>
</li>
<li>
<strong></strong>
</li>
<li>
<strong></strong>
使
</li>
</ul>
<p className="mt-3">
广
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
</h2>
<p>
{" "}
<a
href="https://supabase.com/"
target="_blank"
rel="noopener noreferrer"
className="text-ember-500 hover:text-ember-400 transition-colors underline decoration-clay-900/20 underline-offset-2"
>
Supabase
</a>{" "}
Supabase
TLS
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
</h2>
<p>
广
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
</h2>
<p>
{" "}
<a
href="mailto:hi@infiplot.com"
className="text-ember-500 hover:text-ember-400 transition-colors"
>
hi@infiplot.com
</a>{" "}
30
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
</h2>
<p>
使{" "}
<a
href="https://umami.is/"
target="_blank"
rel="noopener noreferrer"
className="text-ember-500 hover:text-ember-400 transition-colors underline decoration-clay-900/20 underline-offset-2"
>
Umami
</a>{" "}
访使
Cookie
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
Google API
</h2>
<p>
InfiPlot Google API
使{" "}
<a
href="https://developers.google.com/terms/api-services-user-data-policy"
target="_blank"
rel="noopener noreferrer"
className="text-ember-500 hover:text-ember-400 transition-colors underline decoration-clay-900/20 underline-offset-2"
>
Google API Services User Data Policy
</a>
使Limited Use
</p>
<ul className="list-disc pl-6 space-y-1 mt-3">
<li></li>
<li> Google 广</li>
<li> Google </li>
<li> Google </li>
<li> Google AI/ML </li>
</ul>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
</h2>
<p>
InfiPlot
13 13
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3">
</h2>
<p>
"最后更新"使
</p>
</section>
<section>
<h2 className="font-serif text-xl text-clay-900 mb-3"></h2>
<p>
使
</p>
<p className="mt-3">
{" "}
<a
href="mailto:hi@infiplot.com"
className="text-ember-500 hover:text-ember-400 transition-colors"
>
hi@infiplot.com
</a>
</p>
</section>
</div>
<div className="hairline-full w-full mt-16 mb-8" />
<footer className="text-center text-[10px] smallcaps text-clay-500 pb-10">
<span>© 2026 InfiPlot. All rights reserved.</span>
</footer>
</main>
);
}