@meursyphus/i18n-llm 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,7 @@ LLM-friendly Next.js i18n library with type-safe translations.
10
10
  - **Zero dependencies**: Only requires Next.js and React
11
11
  - **App Router only**: Built specifically for Next.js 14+ App Router
12
12
  - **Server & Client components**: Works seamlessly in both
13
+ - **No URL routing**: Language managed via cookies (no `/en`, `/ko` prefixes needed)
13
14
 
14
15
  ## Installation
15
16
 
@@ -30,15 +31,15 @@ Or follow the manual setup in [llm.txt](./llm.txt).
30
31
  ### 2. Wrap your app
31
32
 
32
33
  ```tsx
33
- // app/[lang]/layout.tsx
34
- import { TranslationsProvider } from '@meursyphus/i18n-llm';
34
+ // app/layout.tsx
35
+ import { TranslationsProvider, getLocale } from '@meursyphus/i18n-llm';
35
36
 
36
- export default async function RootLayout({ children, params }) {
37
- const { lang } = await params;
37
+ export default async function RootLayout({ children }) {
38
+ const locale = await getLocale();
38
39
  return (
39
- <html lang={lang}>
40
+ <html lang={locale}>
40
41
  <body>
41
- <TranslationsProvider locale={lang}>
42
+ <TranslationsProvider>
42
43
  {children}
43
44
  </TranslationsProvider>
44
45
  </body>
@@ -53,9 +54,8 @@ export default async function RootLayout({ children, params }) {
53
54
  ```tsx
54
55
  import { getTranslations } from '@meursyphus/i18n-llm';
55
56
 
56
- export default async function Page({ params }) {
57
- const { lang } = await params;
58
- const t = await getTranslations('common', lang);
57
+ export default async function Page() {
58
+ const t = await getTranslations('common');
59
59
 
60
60
  return <h1>{t('greeting', { name: 'World' })}</h1>;
61
61
  }
@@ -74,34 +74,35 @@ export function Greeting({ name }) {
74
74
 
75
75
  ## API Reference
76
76
 
77
- ### Server Exports (`i18n-llm`)
77
+ ### Server Exports (`@meursyphus/i18n-llm`)
78
78
 
79
79
  | Export | Description |
80
80
  |--------|-------------|
81
- | `getTranslations(namespace, locale)` | Get translation function for server components |
81
+ | `getTranslations(namespace)` | Get translation function for server components |
82
82
  | `TranslationsProvider` | Provider component for client components |
83
+ | `getLocale()` | Get current locale from cookie/header |
83
84
  | `defineI18nConfig(config)` | Define i18n configuration |
84
- | `getPreferredLanguage(request)` | Get user's preferred language from request |
85
85
 
86
- ### Client Exports (`i18n-llm/client`)
86
+ ### Client Exports (`@meursyphus/i18n-llm/client`)
87
87
 
88
88
  | Export | Description |
89
89
  |--------|-------------|
90
90
  | `useTranslations(namespace)` | Hook to get translation function |
91
91
  | `useCurrentLanguage()` | Hook to get current locale |
92
+ | `useSetLanguage()` | Hook to change current language |
92
93
 
93
- ### Middleware (`i18n-llm/middleware`)
94
+ ### Middleware (`@meursyphus/i18n-llm/middleware`)
94
95
 
95
96
  | Export | Description |
96
97
  |--------|-------------|
97
98
  | `defineMiddleware(config)` | Configure i18n middleware |
98
99
  | `middleware` | The middleware function to export |
99
100
 
100
- ### Actions (`i18n-llm/actions`)
101
+ ### Actions (`@meursyphus/i18n-llm/actions`)
101
102
 
102
103
  | Export | Description |
103
104
  |--------|-------------|
104
- | `setLanguagePreference(locale, path)` | Server action to change language |
105
+ | `setLanguagePreference(locale)` | Server action to change language |
105
106
  | `getLanguagePreference()` | Get stored language preference |
106
107
 
107
108
  ## Configuration
@@ -131,7 +132,7 @@ defineMiddleware({
131
132
  export { middleware };
132
133
 
133
134
  export const config = {
134
- matcher: ['/((?!api|_next|.*\\..*).*)'],
135
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)'],
135
136
  };
136
137
  ```
137
138
 
@@ -203,20 +204,14 @@ t('greeting', { name: 'John', count: 5 });
203
204
 
204
205
  ```tsx
205
206
  'use client';
206
- import { useCurrentLanguage } from '@meursyphus/i18n-llm/client';
207
- import { setLanguagePreference } from '@meursyphus/i18n-llm/actions';
208
- import { usePathname } from 'next/navigation';
207
+ import { useCurrentLanguage, useSetLanguage } from '@meursyphus/i18n-llm/client';
209
208
 
210
209
  export function LanguageSwitcher() {
211
210
  const currentLang = useCurrentLanguage();
212
- const pathname = usePathname();
213
-
214
- const handleChange = async (locale: string) => {
215
- await setLanguagePreference(locale, pathname);
216
- };
211
+ const setLanguage = useSetLanguage();
217
212
 
218
213
  return (
219
- <select value={currentLang} onChange={(e) => handleChange(e.target.value)}>
214
+ <select value={currentLang} onChange={(e) => setLanguage(e.target.value)}>
220
215
  <option value="en">English</option>
221
216
  <option value="ko">한국어</option>
222
217
  <option value="ja">日本語</option>
package/dist/actions.d.ts CHANGED
@@ -1,14 +1,18 @@
1
1
  /**
2
2
  * Server action to set the user's language preference.
3
- * Updates the cookie and redirects to the new locale path.
3
+ * Updates the cookie only - no redirect. Use with router.refresh() on client.
4
4
  *
5
5
  * @example
6
6
  * 'use client';
7
- * import { setLanguagePreference } from 'i18n-llm/actions';
7
+ * import { setLanguagePreference } from '@meursyphus/i18n-llm/actions';
8
+ * import { useRouter } from 'next/navigation';
8
9
  *
9
10
  * function LanguageSwitcher() {
11
+ * const router = useRouter();
12
+ *
10
13
  * const handleChange = async (locale: string) => {
11
- * await setLanguagePreference(locale, window.location.pathname);
14
+ * await setLanguagePreference(locale);
15
+ * router.refresh();
12
16
  * };
13
17
  *
14
18
  * return (
@@ -19,7 +23,7 @@
19
23
  * );
20
24
  * }
21
25
  */
22
- declare function setLanguagePreference(locale: string, currentPath: string): Promise<void>;
26
+ declare function setLanguagePreference(locale: string): Promise<void>;
23
27
  /**
24
28
  * Gets the user's stored language preference from cookies.
25
29
  * Returns null if no preference is set.
package/dist/actions.js CHANGED
@@ -5,10 +5,9 @@ import {
5
5
 
6
6
  // src/actions.ts
7
7
  import { cookies } from "next/headers";
8
- import { redirect } from "next/navigation";
9
8
  var LANGUAGE_COOKIE_NAME = "preferred-language";
10
9
  var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
11
- async function setLanguagePreference(locale, currentPath) {
10
+ async function setLanguagePreference(locale) {
12
11
  const config = getI18nConfig();
13
12
  if (config && !config.locales.includes(locale)) {
14
13
  throw new Error(`i18n-llm: Unsupported locale: ${locale}`);
@@ -21,10 +20,6 @@ async function setLanguagePreference(locale, currentPath) {
21
20
  maxAge: COOKIE_MAX_AGE,
22
21
  path: "/"
23
22
  });
24
- const pathSegments = currentPath.split("/");
25
- pathSegments[1] = locale;
26
- const newPath = pathSegments.join("/");
27
- redirect(newPath);
28
23
  }
29
24
  async function getLanguagePreference() {
30
25
  const cookieStore = await cookies();
package/dist/cli/index.js CHANGED
@@ -122,17 +122,16 @@ export default messages;
122
122
  \u2705 i18n-llm initialized successfully!
123
123
 
124
124
  Next steps:
125
- 1. Move your app/ contents to app/[lang]/
126
- 2. Update your root layout to use TranslationsProvider:
125
+ 1. Update your root layout to use TranslationsProvider:
127
126
 
128
- import { TranslationsProvider } from '@meursyphus/i18n-llm';
127
+ import { TranslationsProvider, getLocale } from '@meursyphus/i18n-llm';
129
128
 
130
- export default async function RootLayout({ children, params }) {
131
- const { lang } = await params;
129
+ export default async function RootLayout({ children }) {
130
+ const locale = await getLocale();
132
131
  return (
133
- <html lang={lang}>
132
+ <html lang={locale}>
134
133
  <body>
135
- <TranslationsProvider locale={lang}>
134
+ <TranslationsProvider>
136
135
  {children}
137
136
  </TranslationsProvider>
138
137
  </body>
@@ -140,15 +139,30 @@ Next steps:
140
139
  );
141
140
  }
142
141
 
143
- 3. Use translations in your components:
142
+ 2. Use translations in your components:
144
143
 
145
144
  // Server Component
146
145
  import { getTranslations } from '@meursyphus/i18n-llm';
147
- const t = await getTranslations('common', lang);
146
+ const t = await getTranslations('common');
148
147
 
149
148
  // Client Component
150
149
  import { useTranslations } from '@meursyphus/i18n-llm/client';
151
150
  const t = useTranslations('common');
151
+
152
+ 3. Add a language switcher:
153
+
154
+ import { useSetLanguage, useCurrentLanguage } from '@meursyphus/i18n-llm/client';
155
+
156
+ function LanguageSwitcher() {
157
+ const currentLang = useCurrentLanguage();
158
+ const setLanguage = useSetLanguage();
159
+ return (
160
+ <select value={currentLang} onChange={(e) => setLanguage(e.target.value)}>
161
+ <option value="en">English</option>
162
+ <option value="ko">\uD55C\uAD6D\uC5B4</option>
163
+ </select>
164
+ );
165
+ }
152
166
  `);
153
167
  }
154
168
 
package/dist/client.js CHANGED
@@ -78,9 +78,54 @@ function useCurrentLanguage() {
78
78
  const { locale } = useContext2(TranslationsContext);
79
79
  return locale;
80
80
  }
81
+
82
+ // src/client/use-set-language.ts
83
+ import { useRouter } from "next/navigation";
84
+ import { useCallback } from "react";
85
+
86
+ // src/actions.ts
87
+ import { cookies } from "next/headers";
88
+
89
+ // src/shared/load-translations.ts
90
+ var cachedConfig = null;
91
+ function getI18nConfig() {
92
+ return cachedConfig;
93
+ }
94
+
95
+ // src/actions.ts
96
+ var LANGUAGE_COOKIE_NAME = "preferred-language";
97
+ var COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
98
+ async function setLanguagePreference(locale) {
99
+ const config = getI18nConfig();
100
+ if (config && !config.locales.includes(locale)) {
101
+ throw new Error(`i18n-llm: Unsupported locale: ${locale}`);
102
+ }
103
+ const cookieStore = await cookies();
104
+ cookieStore.set(LANGUAGE_COOKIE_NAME, locale, {
105
+ httpOnly: true,
106
+ secure: process.env.NODE_ENV === "production",
107
+ sameSite: "lax",
108
+ maxAge: COOKIE_MAX_AGE,
109
+ path: "/"
110
+ });
111
+ }
112
+
113
+ // src/client/use-set-language.ts
114
+ function useSetLanguage() {
115
+ const router = useRouter();
116
+ const setLanguage = useCallback(
117
+ async (locale) => {
118
+ await setLanguagePreference(locale);
119
+ router.refresh();
120
+ },
121
+ [router]
122
+ );
123
+ return setLanguage;
124
+ }
81
125
  export {
82
126
  ClientTranslationsProvider,
83
127
  TranslationsContext,
84
128
  useCurrentLanguage,
129
+ useSetLanguage,
85
130
  useTranslations
86
131
  };
@@ -19,11 +19,6 @@ interface TranslationsContextType<TMessages = Messages> {
19
19
  interface MiddlewareConfig {
20
20
  locales: string[];
21
21
  defaultLocale: string;
22
- localePrefix?: "always" | "as-needed" | "never";
23
- redirects?: Array<{
24
- from: string;
25
- to: string;
26
- }>;
27
22
  }
28
23
 
29
24
  export type { InterpolationVariables as I, Messages as M, NestedKeyOf as N, TranslationValue as T, NestedValueOf as a, I18nConfig as b, MiddlewareConfig as c, TranslationsContextType as d };
package/dist/index.d.ts CHANGED
@@ -1,38 +1,46 @@
1
- import { M as Messages, N as NestedKeyOf, I as InterpolationVariables, a as NestedValueOf, b as I18nConfig } from './index-KVK-m3p1.js';
2
- export { c as MiddlewareConfig, T as TranslationValue, d as TranslationsContextType } from './index-KVK-m3p1.js';
1
+ import { M as Messages, N as NestedKeyOf, I as InterpolationVariables, a as NestedValueOf, b as I18nConfig } from './index-D9guefzL.js';
2
+ export { c as MiddlewareConfig, T as TranslationValue, d as TranslationsContextType } from './index-D9guefzL.js';
3
3
  import { ReactNode } from 'react';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { NextRequest } from 'next/server';
6
6
 
7
7
  /**
8
8
  * Gets translations for a specific namespace in server components.
9
+ * Automatically detects locale from cookie if not explicitly provided.
9
10
  *
10
11
  * @example
11
- * // In a Server Component
12
- * const t = await getTranslations('common', lang);
12
+ * // In a Server Component (auto-detect locale)
13
+ * const t = await getTranslations('common');
13
14
  * return <h1>{t('title')}</h1>;
14
15
  *
16
+ * // With explicit locale
17
+ * const t = await getTranslations('common', 'ko');
18
+ *
15
19
  * // With variables
16
20
  * return <p>{t('greeting', { name: 'World' })}</p>;
17
21
  */
18
- declare function getTranslations<TMessages extends Messages, Namespace extends keyof TMessages>(namespace: Namespace, locale: string): Promise<(<Key extends NestedKeyOf<TMessages[Namespace] & object>>(key: Key, variables?: InterpolationVariables) => NestedValueOf<TMessages[Namespace] & object, Key> extends string ? string : NestedValueOf<TMessages[Namespace] & object, Key> extends ReactNode ? ReactNode : string)>;
22
+ declare function getTranslations<TMessages extends Messages, Namespace extends keyof TMessages>(namespace: Namespace, locale?: string): Promise<(<Key extends NestedKeyOf<TMessages[Namespace] & object>>(key: Key, variables?: InterpolationVariables) => NestedValueOf<TMessages[Namespace] & object, Key> extends string ? string : NestedValueOf<TMessages[Namespace] & object, Key> extends ReactNode ? ReactNode : string)>;
19
23
 
20
24
  interface TranslationsProviderProps {
21
25
  children: ReactNode;
22
- locale: string;
26
+ /** Optional locale override. If not provided, auto-detects from cookie/header. */
27
+ locale?: string;
23
28
  }
24
29
  /**
25
30
  * Server-side translations provider.
26
31
  * Loads translations and passes them to the client context.
32
+ * Automatically detects locale from cookie if not explicitly provided.
27
33
  *
28
34
  * @example
29
- * // In your root layout
30
- * export default async function RootLayout({ children, params }) {
31
- * const { lang } = await params;
35
+ * // In your root layout (auto-detect locale)
36
+ * import { TranslationsProvider, getLocale } from '@meursyphus/i18n-llm';
37
+ *
38
+ * export default async function RootLayout({ children }) {
39
+ * const locale = await getLocale();
32
40
  * return (
33
- * <html lang={lang}>
41
+ * <html lang={locale}>
34
42
  * <body>
35
- * <TranslationsProvider locale={lang}>
43
+ * <TranslationsProvider>
36
44
  * {children}
37
45
  * </TranslationsProvider>
38
46
  * </body>
@@ -40,7 +48,7 @@ interface TranslationsProviderProps {
40
48
  * );
41
49
  * }
42
50
  */
43
- declare function TranslationsProvider<TMessages extends Messages = Messages>({ children, locale }: TranslationsProviderProps): Promise<react_jsx_runtime.JSX.Element>;
51
+ declare function TranslationsProvider<TMessages extends Messages = Messages>({ children, locale: localeProp }: TranslationsProviderProps): Promise<react_jsx_runtime.JSX.Element>;
44
52
 
45
53
  /**
46
54
  * Gets the user's preferred language from the request.
@@ -48,6 +56,21 @@ declare function TranslationsProvider<TMessages extends Messages = Messages>({ c
48
56
  */
49
57
  declare function getPreferredLanguage(request: NextRequest): string;
50
58
 
59
+ /**
60
+ * Gets the current locale in server components.
61
+ * Reads from cookie first, then falls back to middleware header, then default locale.
62
+ *
63
+ * @example
64
+ * // In a Server Component
65
+ * import { getLocale } from '@meursyphus/i18n-llm';
66
+ *
67
+ * export default async function Page() {
68
+ * const locale = await getLocale();
69
+ * return <html lang={locale}>...</html>;
70
+ * }
71
+ */
72
+ declare function getLocale(): Promise<string>;
73
+
51
74
  /**
52
75
  * Sets the i18n configuration for message loading.
53
76
  * This should be called once at application startup.
@@ -96,4 +119,4 @@ declare function defineI18nConfig<T extends readonly string[]>(config: {
96
119
  messagesPath: string;
97
120
  }): I18nConfig;
98
121
 
99
- export { I18nConfig, InterpolationVariables, Messages, NestedKeyOf, NestedValueOf, TranslationsProvider, createMessageLoader, defineI18nConfig, getI18nConfig, getPreferredLanguage, getTranslations, loadTranslations, setI18nConfig };
122
+ export { I18nConfig, InterpolationVariables, Messages, NestedKeyOf, NestedValueOf, TranslationsProvider, createMessageLoader, defineI18nConfig, getI18nConfig, getLocale, getPreferredLanguage, getTranslations, loadTranslations, setI18nConfig };
package/dist/index.js CHANGED
@@ -26,9 +26,31 @@ function getNestedValue(obj, path) {
26
26
  return value;
27
27
  }
28
28
 
29
+ // src/server/get-locale.ts
30
+ import { cookies, headers } from "next/headers";
31
+ var LANGUAGE_COOKIE_NAME = "preferred-language";
32
+ var LOCALE_HEADER_NAME = "x-i18n-locale";
33
+ async function getLocale() {
34
+ const config = getI18nConfig();
35
+ const defaultLocale = config?.defaultLocale || "en";
36
+ const locales = config?.locales || [defaultLocale];
37
+ const cookieStore = await cookies();
38
+ const cookieValue = cookieStore.get(LANGUAGE_COOKIE_NAME)?.value;
39
+ if (cookieValue && locales.includes(cookieValue)) {
40
+ return cookieValue;
41
+ }
42
+ const headerStore = await headers();
43
+ const headerValue = headerStore.get(LOCALE_HEADER_NAME);
44
+ if (headerValue && locales.includes(headerValue)) {
45
+ return headerValue;
46
+ }
47
+ return defaultLocale;
48
+ }
49
+
29
50
  // src/server/get-translations.ts
30
51
  async function getTranslations(namespace, locale) {
31
- const messages = await loadTranslations(locale);
52
+ const resolvedLocale = locale ?? await getLocale();
53
+ const messages = await loadTranslations(resolvedLocale);
32
54
  function t(key, variables) {
33
55
  const namespaceMessages = messages[namespace];
34
56
  const value = getNestedValue(namespaceMessages, key);
@@ -63,13 +85,14 @@ function ClientTranslationsProvider({
63
85
 
64
86
  // src/server/translations-provider.tsx
65
87
  import { jsx as jsx2 } from "react/jsx-runtime";
66
- async function TranslationsProvider({ children, locale }) {
88
+ async function TranslationsProvider({ children, locale: localeProp }) {
89
+ const locale = localeProp ?? await getLocale();
67
90
  const messages = await loadTranslations(locale);
68
91
  return /* @__PURE__ */ jsx2(ClientTranslationsProvider, { messages, locale, children });
69
92
  }
70
93
 
71
94
  // src/server/get-preferred-language.ts
72
- var LANGUAGE_COOKIE_NAME = "preferred-language";
95
+ var LANGUAGE_COOKIE_NAME2 = "preferred-language";
73
96
  function getPreferredLanguage(request) {
74
97
  const config = getI18nConfig();
75
98
  if (!config) {
@@ -79,7 +102,7 @@ function getPreferredLanguage(request) {
79
102
  return "en";
80
103
  }
81
104
  const { defaultLocale, locales } = config;
82
- const cookieValue = request.cookies.get(LANGUAGE_COOKIE_NAME)?.value;
105
+ const cookieValue = request.cookies.get(LANGUAGE_COOKIE_NAME2)?.value;
83
106
  if (cookieValue && locales.includes(cookieValue)) {
84
107
  return cookieValue;
85
108
  }
@@ -115,6 +138,7 @@ export {
115
138
  createMessageLoader,
116
139
  defineI18nConfig,
117
140
  getI18nConfig,
141
+ getLocale,
118
142
  getPreferredLanguage,
119
143
  getTranslations,
120
144
  loadTranslations,
@@ -1,14 +1,15 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
- import { c as MiddlewareConfig } from './index-KVK-m3p1.js';
2
+ import { c as MiddlewareConfig } from './index-D9guefzL.js';
3
3
  import 'react';
4
4
 
5
+ declare const LOCALE_HEADER_NAME = "x-i18n-locale";
5
6
  /**
6
7
  * Defines the i18n middleware configuration.
7
8
  * Call this once in your middleware.ts file.
8
9
  *
9
10
  * @example
10
11
  * // middleware.ts
11
- * import { defineMiddleware, middleware } from 'i18n-llm/middleware';
12
+ * import { defineMiddleware, middleware } from '@meursyphus/i18n-llm/middleware';
12
13
  *
13
14
  * defineMiddleware({
14
15
  * locales: ['en', 'ko', 'ja'],
@@ -22,10 +23,16 @@ import 'react';
22
23
  * };
23
24
  */
24
25
  declare function defineMiddleware(config: MiddlewareConfig): void;
26
+ /**
27
+ * Gets the middleware configuration.
28
+ * Returns null if not configured.
29
+ */
30
+ declare function getMiddlewareConfig(): MiddlewareConfig | null;
25
31
  /**
26
32
  * The i18n middleware function.
27
- * Make sure to call defineMiddleware() before exporting this.
33
+ * Sets the locale header based on cookie or browser preference.
34
+ * No URL redirects - locale is managed via global state.
28
35
  */
29
36
  declare function middleware(request: NextRequest): NextResponse;
30
37
 
31
- export { defineMiddleware, middleware };
38
+ export { LOCALE_HEADER_NAME, defineMiddleware, getMiddlewareConfig, middleware };
@@ -1,10 +1,14 @@
1
1
  // src/middleware.ts
2
2
  import { NextResponse } from "next/server";
3
3
  var LANGUAGE_COOKIE_NAME = "preferred-language";
4
+ var LOCALE_HEADER_NAME = "x-i18n-locale";
4
5
  var middlewareConfig = null;
5
6
  function defineMiddleware(config) {
6
7
  middlewareConfig = config;
7
8
  }
9
+ function getMiddlewareConfig() {
10
+ return middlewareConfig;
11
+ }
8
12
  function middleware(request) {
9
13
  if (!middlewareConfig) {
10
14
  console.error(
@@ -12,45 +16,17 @@ function middleware(request) {
12
16
  );
13
17
  return NextResponse.next();
14
18
  }
15
- const {
16
- locales,
17
- defaultLocale,
18
- localePrefix = "always",
19
- redirects = []
20
- } = middlewareConfig;
21
- const { pathname } = request.nextUrl;
22
- if (pathname.startsWith("/_next") || pathname.startsWith("/api") || pathname.includes(".")) {
23
- return NextResponse.next();
24
- }
25
- const pathnameLocale = locales.find(
26
- (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
27
- );
28
- const preferredLocale = getPreferredLocale(request, locales, defaultLocale);
29
- if (pathname === "/") {
30
- if (localePrefix === "always") {
31
- return NextResponse.redirect(
32
- new URL(`/${preferredLocale}`, request.url)
33
- );
34
- }
35
- return NextResponse.next();
36
- }
37
- if (!pathnameLocale) {
38
- if (localePrefix === "always") {
39
- return NextResponse.redirect(
40
- new URL(`/${preferredLocale}${pathname}`, request.url)
41
- );
19
+ const { locales, defaultLocale } = middlewareConfig;
20
+ const locale = getPreferredLocale(request, locales, defaultLocale);
21
+ const response = NextResponse.next();
22
+ response.headers.set(LOCALE_HEADER_NAME, locale);
23
+ const requestHeaders = new Headers(request.headers);
24
+ requestHeaders.set(LOCALE_HEADER_NAME, locale);
25
+ return NextResponse.next({
26
+ request: {
27
+ headers: requestHeaders
42
28
  }
43
- return NextResponse.next();
44
- }
45
- for (const redirect of redirects) {
46
- const pathWithoutLocale = pathname.replace(`/${pathnameLocale}`, "");
47
- if (pathWithoutLocale === redirect.from) {
48
- return NextResponse.redirect(
49
- new URL(`/${pathnameLocale}${redirect.to}`, request.url)
50
- );
51
- }
52
- }
53
- return NextResponse.next();
29
+ });
54
30
  }
55
31
  function getPreferredLocale(request, locales, defaultLocale) {
56
32
  const cookieValue = request.cookies.get(LANGUAGE_COOKIE_NAME)?.value;
@@ -70,6 +46,8 @@ function getPreferredLocale(request, locales, defaultLocale) {
70
46
  return defaultLocale;
71
47
  }
72
48
  export {
49
+ LOCALE_HEADER_NAME,
73
50
  defineMiddleware,
51
+ getMiddlewareConfig,
74
52
  middleware
75
53
  };
package/llm.txt CHANGED
@@ -39,7 +39,7 @@ defineMiddleware({
39
39
  export { middleware };
40
40
 
41
41
  export const config = {
42
- matcher: ['/((?!api|_next|.*\\..*).*)'],
42
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\..*).*)'],
43
43
  };
44
44
  ```
45
45
 
@@ -85,45 +85,23 @@ const messages: Messages = {
85
85
  export default messages;
86
86
  ```
87
87
 
88
- ## Step 5: Update app directory structure
88
+ ## Step 5: Update layout.tsx
89
89
 
90
- Rename `app/` contents to `app/[lang]/`:
91
-
92
- Before:
93
- ```
94
- app/
95
- ├── layout.tsx
96
- ├── page.tsx
97
- └── about/page.tsx
98
- ```
99
-
100
- After:
101
- ```
102
- app/
103
- └── [lang]/
104
- ├── layout.tsx
105
- ├── page.tsx
106
- └── about/page.tsx
107
- ```
108
-
109
- ## Step 6: Update layout.tsx
110
-
111
- Replace `app/[lang]/layout.tsx`:
90
+ Update `app/layout.tsx`:
112
91
  ```typescript
113
- import { TranslationsProvider } from '@meursyphus/i18n-llm';
92
+ import { TranslationsProvider, getLocale } from '@meursyphus/i18n-llm';
114
93
 
115
94
  interface LayoutProps {
116
95
  children: React.ReactNode;
117
- params: Promise<{ lang: string }>;
118
96
  }
119
97
 
120
- export default async function RootLayout({ children, params }: LayoutProps) {
121
- const { lang } = await params;
98
+ export default async function RootLayout({ children }: LayoutProps) {
99
+ const locale = await getLocale();
122
100
 
123
101
  return (
124
- <html lang={lang}>
102
+ <html lang={locale}>
125
103
  <body>
126
- <TranslationsProvider locale={lang}>
104
+ <TranslationsProvider>
127
105
  {children}
128
106
  </TranslationsProvider>
129
107
  </body>
@@ -132,15 +110,14 @@ export default async function RootLayout({ children, params }: LayoutProps) {
132
110
  }
133
111
  ```
134
112
 
135
- ## Step 7: Use translations in components
113
+ ## Step 6: Use translations in components
136
114
 
137
115
  Server Component:
138
116
  ```typescript
139
117
  import { getTranslations } from '@meursyphus/i18n-llm';
140
118
 
141
- export default async function Page({ params }: { params: Promise<{ lang: string }> }) {
142
- const { lang } = await params;
143
- const t = await getTranslations('common', lang);
119
+ export default async function Page() {
120
+ const t = await getTranslations('common');
144
121
 
145
122
  return (
146
123
  <div>
@@ -164,26 +141,20 @@ export function Greeting({ name }: { name: string }) {
164
141
  }
165
142
  ```
166
143
 
167
- ## Step 8: Language Switcher (Optional)
144
+ ## Step 7: Language Switcher (Optional)
168
145
 
169
146
  Create a language switcher component:
170
147
  ```typescript
171
148
  'use client';
172
149
 
173
- import { useCurrentLanguage } from '@meursyphus/i18n-llm/client';
174
- import { setLanguagePreference } from '@meursyphus/i18n-llm/actions';
175
- import { usePathname } from 'next/navigation';
150
+ import { useCurrentLanguage, useSetLanguage } from '@meursyphus/i18n-llm/client';
176
151
 
177
152
  export function LanguageSwitcher() {
178
153
  const currentLang = useCurrentLanguage();
179
- const pathname = usePathname();
180
-
181
- const handleChange = async (locale: string) => {
182
- await setLanguagePreference(locale, pathname);
183
- };
154
+ const setLanguage = useSetLanguage();
184
155
 
185
156
  return (
186
- <select value={currentLang} onChange={(e) => handleChange(e.target.value)}>
157
+ <select value={currentLang} onChange={(e) => setLanguage(e.target.value)}>
187
158
  <option value="en">English</option>
188
159
  <option value="ko">한국어</option>
189
160
  </select>
@@ -191,20 +162,15 @@ export function LanguageSwitcher() {
191
162
  }
192
163
  ```
193
164
 
194
- ## Step 9: Generate Metadata (Optional)
165
+ ## Step 8: Generate Metadata (Optional)
195
166
 
196
167
  Use translations in generateMetadata:
197
168
  ```typescript
198
169
  import { getTranslations } from '@meursyphus/i18n-llm';
199
170
  import type { Metadata } from 'next';
200
171
 
201
- interface PageProps {
202
- params: Promise<{ lang: string }>;
203
- }
204
-
205
- export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
206
- const { lang } = await params;
207
- const t = await getTranslations('common', lang);
172
+ export async function generateMetadata(): Promise<Metadata> {
173
+ const t = await getTranslations('common');
208
174
 
209
175
  return {
210
176
  title: t('title'),
@@ -217,6 +183,7 @@ export async function generateMetadata({ params }: PageProps): Promise<Metadata>
217
183
 
218
184
  i18n-llm has been configured successfully.
219
185
  The app now supports English (en) and Korean (ko).
186
+ Language is managed via cookies - no URL routing needed.
220
187
 
221
188
  To add more locales:
222
189
  1. Add locale to `locales` array in `i18n.config.ts`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meursyphus/i18n-llm",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "LLM-friendly Next.js i18n library with type-safe translations",
5
5
  "keywords": [
6
6
  "i18n",