diff --git a/i18n.ts b/i18n.ts new file mode 100644 index 0000000..2d55008 --- /dev/null +++ b/i18n.ts @@ -0,0 +1,9 @@ +export const locales = ["en", "zh"] as const; +export const defaultLocale = "en"; + +export const i18n = { + locales, + defaultLocale, +} as const; + +export type Locale = (typeof locales)[number]; diff --git a/next.config.mjs b/next.config.mjs index 8c87a00..b5ea6f6 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,7 +1,9 @@ // next.config.mjs import { createMDX } from "fumadocs-mdx/next"; +import createNextIntlPlugin from "next-intl/plugin"; const withMDX = createMDX(); +const withNextIntl = createNextIntlPlugin("./i18n.ts"); /** @type {import('next').NextConfig} */ const config = { @@ -10,4 +12,4 @@ const config = { images: { unoptimized: true }, // 避免使用 Next Image 优化服务 }; -export default withMDX(config); +export default withNextIntl(withMDX(config)); diff --git a/package.json b/package.json index bc43176..d7fc6a9 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@orama/orama": "^3.1.13", "@orama/tokenizers": "^3.1.13", + "@radix-ui/react-slot": "^1.2.3", "@types/mdx": "^2.0.13", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -22,13 +23,13 @@ "fumadocs-ui": "^15.7.11", "lucide-react": "^0.544.0", "next": "^15.5.3", + "next-intl": "^4.3.8", "react": "^19.1.1", "react-dom": "^19.1.1", "rehype-autolink-headings": "^7.1.0", "rehype-slug": "^6.0.0", "remark-gfm": "^4.0.1", - "tailwind-merge": "^3.3.1", - "@radix-ui/react-slot": "^1.2.3" + "tailwind-merge": "^3.3.1" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96af47f..4baaaaa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,9 @@ importers: next: specifier: ^15.5.3 version: 15.5.3(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next-intl: + specifier: ^4.3.8 + version: 4.3.8(next@15.5.3(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2) react: specifier: ^19.1.1 version: 19.1.1 @@ -454,6 +457,36 @@ packages: integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==, } + "@formatjs/ecma402-abstract@2.3.4": + resolution: + { + integrity: sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==, + } + + "@formatjs/fast-memoize@2.2.7": + resolution: + { + integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==, + } + + "@formatjs/icu-messageformat-parser@2.11.2": + resolution: + { + integrity: sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==, + } + + "@formatjs/icu-skeleton-parser@1.8.14": + resolution: + { + integrity: sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==, + } + + "@formatjs/intl-localematcher@0.5.10": + resolution: + { + integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==, + } + "@formatjs/intl-localematcher@0.6.1": resolution: { @@ -1329,6 +1362,12 @@ packages: integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==, } + "@schummar/icu-type-parser@1.21.5": + resolution: + { + integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==, + } + "@shikijs/core@3.12.2": resolution: { @@ -2342,6 +2381,12 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: + { + integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, + } + decode-named-character-reference@1.2.0: resolution: { @@ -3206,6 +3251,12 @@ packages: } engines: { node: ">= 0.4" } + intl-messageformat@10.7.16: + resolution: + { + integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==, + } + is-alphabetical@2.0.1: resolution: { @@ -4138,6 +4189,19 @@ packages: } engines: { node: ">= 0.6" } + next-intl@4.3.8: + resolution: + { + integrity: sha512-epUuRSL1KRQtDdFVRb5j7ZaaF7Sx/aivVA7VY0fc5g1pmpT90ylK6AaBdNsOnc5n+AERVn+zO5HoBsXO0lhTZA==, + } + peerDependencies: + next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + next-themes@0.4.6: resolution: { @@ -5196,6 +5260,14 @@ packages: "@types/react": optional: true + use-intl@4.3.8: + resolution: + { + integrity: sha512-L4mcWCriZCw+qySIk5Octy9ioRNTuFu4y2kTw3NIKfKRk0xAvfobX7/5VHk+eCDJFJeBEqJov/qRe83LDfbqyg==, + } + peerDependencies: + react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + use-sidecar@1.1.3: resolution: { @@ -5469,6 +5541,32 @@ snapshots: "@floating-ui/utils@0.2.10": {} + "@formatjs/ecma402-abstract@2.3.4": + dependencies: + "@formatjs/fast-memoize": 2.2.7 + "@formatjs/intl-localematcher": 0.6.1 + decimal.js: 10.6.0 + tslib: 2.8.1 + + "@formatjs/fast-memoize@2.2.7": + dependencies: + tslib: 2.8.1 + + "@formatjs/icu-messageformat-parser@2.11.2": + dependencies: + "@formatjs/ecma402-abstract": 2.3.4 + "@formatjs/icu-skeleton-parser": 1.8.14 + tslib: 2.8.1 + + "@formatjs/icu-skeleton-parser@1.8.14": + dependencies: + "@formatjs/ecma402-abstract": 2.3.4 + tslib: 2.8.1 + + "@formatjs/intl-localematcher@0.5.10": + dependencies: + tslib: 2.8.1 + "@formatjs/intl-localematcher@0.6.1": dependencies: tslib: 2.8.1 @@ -6036,6 +6134,8 @@ snapshots: "@rushstack/eslint-patch@1.12.0": {} + "@schummar/icu-type-parser@1.21.5": {} + "@shikijs/core@3.12.2": dependencies: "@shikijs/types": 3.12.2 @@ -6639,6 +6739,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -7407,6 +7509,13 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + intl-messageformat@10.7.16: + dependencies: + "@formatjs/ecma402-abstract": 2.3.4 + "@formatjs/fast-memoize": 2.2.7 + "@formatjs/icu-messageformat-parser": 2.11.2 + tslib: 2.8.1 + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -8166,6 +8275,16 @@ snapshots: negotiator@1.0.0: {} + next-intl@4.3.8(next@15.5.3(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2): + dependencies: + "@formatjs/intl-localematcher": 0.5.10 + negotiator: 1.0.0 + next: 15.5.3(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + use-intl: 4.3.8(react@19.1.1) + optionalDependencies: + typescript: 5.9.2 + next-themes@0.4.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: react: 19.1.1 @@ -8966,6 +9085,13 @@ snapshots: optionalDependencies: "@types/react": 19.1.12 + use-intl@4.3.8(react@19.1.1): + dependencies: + "@formatjs/fast-memoize": 2.2.7 + "@schummar/icu-type-parser": 1.21.5 + intl-messageformat: 10.7.16 + react: 19.1.1 + use-sidecar@1.1.3(@types/react@19.1.12)(react@19.1.1): dependencies: detect-node-es: 1.1.0