Files
infiplot-web/lib/i18n/navigation.ts
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

23 lines
820 B
TypeScript

import { DEFAULT_LOCALE, type Locale } from "./config";
/**
* Build a locale-prefixed path. For the default locale (zh-CN), returns the
* bare path so the URL stays clean (middleware rewrites internally).
* For en/ja, prepends the locale segment.
*/
export function localePath(path: string, locale: Locale): string {
if (locale === DEFAULT_LOCALE) return path;
return `/${locale}${path.startsWith("/") ? "" : "/"}${path}`;
}
/**
* Strip any locale prefix from a pathname, returning the bare path.
* "/en/play" → "/play", "/ja" → "/", "/play" → "/play"
*/
export function stripLocalePrefix(pathname: string): string {
const match = pathname.match(/^\/(en|ja)(\/|$)/);
if (!match) return pathname;
const rest = pathname.slice(match[0].length - (match[2] === "/" ? 1 : 0));
return rest || "/";
}