@comvi/next 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.
Files changed (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +256 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  4. package/dist/client/I18nProvider.cjs +101 -0
  5. package/dist/client/I18nProvider.d.ts +84 -0
  6. package/dist/client/I18nProvider.d.ts.map +1 -0
  7. package/dist/client/I18nProvider.js +99 -0
  8. package/dist/client/index.d.ts +5 -0
  9. package/dist/client/index.d.ts.map +1 -0
  10. package/dist/client.cjs +31 -0
  11. package/dist/client.d.ts +5 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +5 -0
  14. package/dist/createNextI18n.cjs +153 -0
  15. package/dist/createNextI18n.d.ts +183 -0
  16. package/dist/createNextI18n.d.ts.map +1 -0
  17. package/dist/createNextI18n.js +152 -0
  18. package/dist/index.cjs +17 -0
  19. package/dist/index.d.ts +9 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +3 -0
  22. package/dist/middleware/createMiddleware.cjs +185 -0
  23. package/dist/middleware/createMiddleware.d.ts +38 -0
  24. package/dist/middleware/createMiddleware.d.ts.map +1 -0
  25. package/dist/middleware/createMiddleware.js +184 -0
  26. package/dist/middleware/types.d.ts +87 -0
  27. package/dist/middleware/types.d.ts.map +1 -0
  28. package/dist/middleware.cjs +3 -0
  29. package/dist/middleware.d.ts +3 -0
  30. package/dist/middleware.d.ts.map +1 -0
  31. package/dist/middleware.js +2 -0
  32. package/dist/navigation.cjs +8 -0
  33. package/dist/navigation.d.ts +5 -0
  34. package/dist/navigation.d.ts.map +1 -0
  35. package/dist/navigation.js +5 -0
  36. package/dist/routing/Link.cjs +42 -0
  37. package/dist/routing/Link.d.ts +25 -0
  38. package/dist/routing/Link.d.ts.map +1 -0
  39. package/dist/routing/Link.js +39 -0
  40. package/dist/routing/context.cjs +21 -0
  41. package/dist/routing/context.d.ts +9 -0
  42. package/dist/routing/context.d.ts.map +1 -0
  43. package/dist/routing/context.js +18 -0
  44. package/dist/routing/defineRouting.cjs +141 -0
  45. package/dist/routing/defineRouting.d.ts +123 -0
  46. package/dist/routing/defineRouting.d.ts.map +1 -0
  47. package/dist/routing/defineRouting.js +139 -0
  48. package/dist/routing/hooks.cjs +104 -0
  49. package/dist/routing/hooks.d.ts +66 -0
  50. package/dist/routing/hooks.d.ts.map +1 -0
  51. package/dist/routing/hooks.js +102 -0
  52. package/dist/routing/types.d.ts +35 -0
  53. package/dist/routing/types.d.ts.map +1 -0
  54. package/dist/routing/utils.cjs +94 -0
  55. package/dist/routing/utils.d.ts +8 -0
  56. package/dist/routing/utils.d.ts.map +1 -0
  57. package/dist/routing/utils.js +91 -0
  58. package/dist/routing.cjs +5 -0
  59. package/dist/routing.d.ts +4 -0
  60. package/dist/routing.d.ts.map +1 -0
  61. package/dist/routing.js +2 -0
  62. package/dist/server/cache.cjs +69 -0
  63. package/dist/server/cache.d.ts +42 -0
  64. package/dist/server/cache.d.ts.map +1 -0
  65. package/dist/server/cache.js +66 -0
  66. package/dist/server/ensureInitialized.cjs +19 -0
  67. package/dist/server/ensureInitialized.d.ts +7 -0
  68. package/dist/server/ensureInitialized.d.ts.map +1 -0
  69. package/dist/server/ensureInitialized.js +19 -0
  70. package/dist/server/getI18n.cjs +115 -0
  71. package/dist/server/getI18n.d.ts +61 -0
  72. package/dist/server/getI18n.d.ts.map +1 -0
  73. package/dist/server/getI18n.js +114 -0
  74. package/dist/server/getLocale.cjs +37 -0
  75. package/dist/server/getLocale.d.ts +22 -0
  76. package/dist/server/getLocale.d.ts.map +1 -0
  77. package/dist/server/getLocale.js +36 -0
  78. package/dist/server/loadTranslations.cjs +54 -0
  79. package/dist/server/loadTranslations.d.ts +34 -0
  80. package/dist/server/loadTranslations.d.ts.map +1 -0
  81. package/dist/server/loadTranslations.js +54 -0
  82. package/dist/server/setRequestLocale.cjs +31 -0
  83. package/dist/server/setRequestLocale.d.ts +26 -0
  84. package/dist/server/setRequestLocale.d.ts.map +1 -0
  85. package/dist/server/setRequestLocale.js +31 -0
  86. package/dist/server/types.d.ts +43 -0
  87. package/dist/server/types.d.ts.map +1 -0
  88. package/dist/server.cjs +11 -0
  89. package/dist/server.d.ts +8 -0
  90. package/dist/server.d.ts.map +1 -0
  91. package/dist/server.js +6 -0
  92. package/package.json +111 -0
@@ -0,0 +1,185 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");
2
+ const require_defineRouting = require("../routing/defineRouting.cjs");
3
+ const require_utils = require("../routing/utils.cjs");
4
+ let next_server = require("next/server");
5
+ //#region src/middleware/createMiddleware.ts
6
+ /**
7
+ * Header name for passing locale to Server Components
8
+ */
9
+ var LOCALE_HEADER = "x-comvi-locale";
10
+ /**
11
+ * Default detection order
12
+ */
13
+ var DEFAULT_DETECTION_ORDER = ["cookie", "accept-language"];
14
+ /**
15
+ * Creates i18n middleware for Next.js
16
+ *
17
+ * The middleware handles:
18
+ * - Locale detection from URL, cookie, header, and Accept-Language
19
+ * - Locale persistence via cookie
20
+ * - URL prefix handling based on localePrefix mode
21
+ * - Setting x-comvi-locale header for Server Components
22
+ *
23
+ * @param config - Middleware configuration
24
+ * @returns Next.js middleware function
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * // middleware.ts - Basic usage
29
+ * import { createMiddleware } from '@comvi/next/middleware';
30
+ * import { routing } from './i18n/routing';
31
+ *
32
+ * export default createMiddleware(routing);
33
+ * ```
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // middleware.ts - Custom detection order
38
+ * export default createMiddleware({
39
+ * ...routing,
40
+ * localeDetection: {
41
+ * order: ['header', 'cookie', 'accept-language'],
42
+ * headerName: 'x-user-locale',
43
+ * cookieName: 'MY_LOCALE',
44
+ * },
45
+ * });
46
+ * ```
47
+ */
48
+ function createMiddleware(config) {
49
+ const { locales, defaultLocale, localePrefix = "as-needed", localeCookie = "NEXT_LOCALE", detectLocale: customDetector, localeDetection } = config;
50
+ const detectionOrder = localeDetection?.order ?? DEFAULT_DETECTION_ORDER;
51
+ const cookieName = localeDetection?.cookieName ?? localeCookie;
52
+ const cookieSecureConfig = localeDetection?.cookieSecure ?? true;
53
+ const isDev = process.env.NODE_ENV === "development";
54
+ const headerName = localeDetection?.headerName;
55
+ const resolveAcceptLanguage = localeDetection?.resolveAcceptLanguage ?? defaultResolveAcceptLanguage;
56
+ const routing = {
57
+ locales,
58
+ defaultLocale,
59
+ localePrefix,
60
+ localeCookie,
61
+ pathnames: config.pathnames ?? {}
62
+ };
63
+ const getPathname = require_defineRouting.createGetPathname(routing);
64
+ return function middleware(request) {
65
+ const { pathname } = request.nextUrl;
66
+ if (shouldSkipPath(pathname)) return next_server.NextResponse.next();
67
+ const pathLocale = extractLocaleFromPath(pathname, locales);
68
+ let detectedLocale;
69
+ if (customDetector) detectedLocale = customDetector(request);
70
+ if (!detectedLocale && pathLocale) detectedLocale = pathLocale;
71
+ if (!detectedLocale) for (const source of detectionOrder) {
72
+ const detected = detectFromSource(request, source, locales, defaultLocale, cookieName, headerName, resolveAcceptLanguage);
73
+ if (detected) {
74
+ detectedLocale = detected;
75
+ break;
76
+ }
77
+ }
78
+ const locale = detectedLocale && locales.includes(detectedLocale) ? detectedLocale : defaultLocale;
79
+ const requestHeaders = new Headers(request.headers);
80
+ requestHeaders.set(LOCALE_HEADER, locale);
81
+ const canonicalPathname = require_utils.getCanonicalPathname(require_utils.stripLocalePrefix(pathname, locales), routing, locale);
82
+ const publicPathname = getPathname({
83
+ locale,
84
+ href: canonicalPathname
85
+ });
86
+ let response;
87
+ if (pathname !== publicPathname) {
88
+ const url = request.nextUrl.clone();
89
+ url.pathname = publicPathname;
90
+ response = next_server.NextResponse.redirect(url);
91
+ } else {
92
+ const internalPathname = getInternalPathname(locale, canonicalPathname);
93
+ const middlewareInit = { request: { headers: requestHeaders } };
94
+ if (pathname === internalPathname) response = next_server.NextResponse.next(middlewareInit);
95
+ else {
96
+ const url = request.nextUrl.clone();
97
+ url.pathname = internalPathname;
98
+ response = next_server.NextResponse.rewrite(url, middlewareInit);
99
+ }
100
+ }
101
+ response.headers.set(LOCALE_HEADER, locale);
102
+ if (request.cookies.get(cookieName)?.value !== locale) response.cookies.set(cookieName, locale, {
103
+ path: "/",
104
+ maxAge: 365 * 24 * 60 * 60,
105
+ sameSite: "lax",
106
+ secure: isDev ? false : cookieSecureConfig
107
+ });
108
+ return response;
109
+ };
110
+ }
111
+ /**
112
+ * Detect locale from a specific source
113
+ */
114
+ function detectFromSource(request, source, locales, defaultLocale, cookieName, headerName, resolveAcceptLanguage) {
115
+ switch (source) {
116
+ case "cookie": {
117
+ const cookieValue = request.cookies.get(cookieName)?.value;
118
+ if (cookieValue && locales.includes(cookieValue)) return cookieValue;
119
+ return;
120
+ }
121
+ case "header": {
122
+ if (!headerName) return void 0;
123
+ const headerValue = request.headers.get(headerName);
124
+ if (headerValue && locales.includes(headerValue)) return headerValue;
125
+ return;
126
+ }
127
+ case "accept-language": {
128
+ const acceptLanguage = request.headers.get("accept-language");
129
+ if (!acceptLanguage) return void 0;
130
+ return resolveAcceptLanguage(acceptLanguage, locales, defaultLocale);
131
+ }
132
+ default: return;
133
+ }
134
+ }
135
+ /**
136
+ * Check if path should be skipped by middleware.
137
+ * Static asset filtering is expected to be handled by Next.js matcher config.
138
+ */
139
+ function shouldSkipPath(pathname) {
140
+ const isApiPath = pathname === "/api" || pathname.startsWith("/api/");
141
+ return pathname.startsWith("/_next") || isApiPath;
142
+ }
143
+ /**
144
+ * Extract locale from URL path
145
+ */
146
+ function extractLocaleFromPath(pathname, locales) {
147
+ const firstSegment = pathname.split("/").filter(Boolean)[0];
148
+ return locales.includes(firstSegment) ? firstSegment : void 0;
149
+ }
150
+ /**
151
+ * Default Accept-Language resolver
152
+ *
153
+ * Handles common cases with a simple parser. For production apps with
154
+ * diverse locales (CJK, regional variants), use @formatjs/intl-localematcher.
155
+ *
156
+ * Matching strategy:
157
+ * 1. Try exact match (e.g., "en-US" matches "en-US")
158
+ * 2. Try base language match (e.g., "en-US" matches "en")
159
+ * 3. Try finding a locale that starts with the language (e.g., "en" matches "en-US")
160
+ */
161
+ function defaultResolveAcceptLanguage(acceptLanguage, locales, _defaultLocale) {
162
+ const languages = acceptLanguage.split(",").map((lang) => {
163
+ const [code, q = "q=1"] = lang.trim().split(";");
164
+ const quality = parseFloat(q.split("=")[1] || "1");
165
+ return {
166
+ code: code.trim(),
167
+ quality: isNaN(quality) ? 1 : quality
168
+ };
169
+ }).filter(({ code, quality }) => code.length > 0 && quality > 0).sort((a, b) => b.quality - a.quality);
170
+ for (const { code } of languages) {
171
+ const normalizedCode = code.toLowerCase();
172
+ const exactMatch = locales.find((locale) => locale.toLowerCase() === normalizedCode);
173
+ if (exactMatch) return exactMatch;
174
+ const baseLang = normalizedCode.split("-")[0];
175
+ const baseMatch = locales.find((locale) => locale.toLowerCase() === baseLang);
176
+ if (baseMatch) return baseMatch;
177
+ const prefixMatch = locales.find((locale) => locale.toLowerCase().startsWith(baseLang + "-"));
178
+ if (prefixMatch) return prefixMatch;
179
+ }
180
+ }
181
+ function getInternalPathname(locale, canonicalPathname) {
182
+ return canonicalPathname === "/" ? `/${locale}` : `/${locale}${canonicalPathname}`;
183
+ }
184
+ //#endregion
185
+ exports.createMiddleware = createMiddleware;
@@ -0,0 +1,38 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { MiddlewareConfig } from './types';
3
+ /**
4
+ * Creates i18n middleware for Next.js
5
+ *
6
+ * The middleware handles:
7
+ * - Locale detection from URL, cookie, header, and Accept-Language
8
+ * - Locale persistence via cookie
9
+ * - URL prefix handling based on localePrefix mode
10
+ * - Setting x-comvi-locale header for Server Components
11
+ *
12
+ * @param config - Middleware configuration
13
+ * @returns Next.js middleware function
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // middleware.ts - Basic usage
18
+ * import { createMiddleware } from '@comvi/next/middleware';
19
+ * import { routing } from './i18n/routing';
20
+ *
21
+ * export default createMiddleware(routing);
22
+ * ```
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * // middleware.ts - Custom detection order
27
+ * export default createMiddleware({
28
+ * ...routing,
29
+ * localeDetection: {
30
+ * order: ['header', 'cookie', 'accept-language'],
31
+ * headerName: 'x-user-locale',
32
+ * cookieName: 'MY_LOCALE',
33
+ * },
34
+ * });
35
+ * ```
36
+ */
37
+ export declare function createMiddleware(config: MiddlewareConfig): (request: NextRequest) => NextResponse;
38
+ //# sourceMappingURL=createMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/createMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,SAAS,CAAC;AAevE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,IA2B5B,SAAS,WAAW,KAAG,YAAY,CA6F/D"}
@@ -0,0 +1,184 @@
1
+ import { createGetPathname } from "../routing/defineRouting.js";
2
+ import { getCanonicalPathname, stripLocalePrefix } from "../routing/utils.js";
3
+ import { NextResponse } from "next/server";
4
+ //#region src/middleware/createMiddleware.ts
5
+ /**
6
+ * Header name for passing locale to Server Components
7
+ */
8
+ var LOCALE_HEADER = "x-comvi-locale";
9
+ /**
10
+ * Default detection order
11
+ */
12
+ var DEFAULT_DETECTION_ORDER = ["cookie", "accept-language"];
13
+ /**
14
+ * Creates i18n middleware for Next.js
15
+ *
16
+ * The middleware handles:
17
+ * - Locale detection from URL, cookie, header, and Accept-Language
18
+ * - Locale persistence via cookie
19
+ * - URL prefix handling based on localePrefix mode
20
+ * - Setting x-comvi-locale header for Server Components
21
+ *
22
+ * @param config - Middleware configuration
23
+ * @returns Next.js middleware function
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // middleware.ts - Basic usage
28
+ * import { createMiddleware } from '@comvi/next/middleware';
29
+ * import { routing } from './i18n/routing';
30
+ *
31
+ * export default createMiddleware(routing);
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // middleware.ts - Custom detection order
37
+ * export default createMiddleware({
38
+ * ...routing,
39
+ * localeDetection: {
40
+ * order: ['header', 'cookie', 'accept-language'],
41
+ * headerName: 'x-user-locale',
42
+ * cookieName: 'MY_LOCALE',
43
+ * },
44
+ * });
45
+ * ```
46
+ */
47
+ function createMiddleware(config) {
48
+ const { locales, defaultLocale, localePrefix = "as-needed", localeCookie = "NEXT_LOCALE", detectLocale: customDetector, localeDetection } = config;
49
+ const detectionOrder = localeDetection?.order ?? DEFAULT_DETECTION_ORDER;
50
+ const cookieName = localeDetection?.cookieName ?? localeCookie;
51
+ const cookieSecureConfig = localeDetection?.cookieSecure ?? true;
52
+ const isDev = process.env.NODE_ENV === "development";
53
+ const headerName = localeDetection?.headerName;
54
+ const resolveAcceptLanguage = localeDetection?.resolveAcceptLanguage ?? defaultResolveAcceptLanguage;
55
+ const routing = {
56
+ locales,
57
+ defaultLocale,
58
+ localePrefix,
59
+ localeCookie,
60
+ pathnames: config.pathnames ?? {}
61
+ };
62
+ const getPathname = createGetPathname(routing);
63
+ return function middleware(request) {
64
+ const { pathname } = request.nextUrl;
65
+ if (shouldSkipPath(pathname)) return NextResponse.next();
66
+ const pathLocale = extractLocaleFromPath(pathname, locales);
67
+ let detectedLocale;
68
+ if (customDetector) detectedLocale = customDetector(request);
69
+ if (!detectedLocale && pathLocale) detectedLocale = pathLocale;
70
+ if (!detectedLocale) for (const source of detectionOrder) {
71
+ const detected = detectFromSource(request, source, locales, defaultLocale, cookieName, headerName, resolveAcceptLanguage);
72
+ if (detected) {
73
+ detectedLocale = detected;
74
+ break;
75
+ }
76
+ }
77
+ const locale = detectedLocale && locales.includes(detectedLocale) ? detectedLocale : defaultLocale;
78
+ const requestHeaders = new Headers(request.headers);
79
+ requestHeaders.set(LOCALE_HEADER, locale);
80
+ const canonicalPathname = getCanonicalPathname(stripLocalePrefix(pathname, locales), routing, locale);
81
+ const publicPathname = getPathname({
82
+ locale,
83
+ href: canonicalPathname
84
+ });
85
+ let response;
86
+ if (pathname !== publicPathname) {
87
+ const url = request.nextUrl.clone();
88
+ url.pathname = publicPathname;
89
+ response = NextResponse.redirect(url);
90
+ } else {
91
+ const internalPathname = getInternalPathname(locale, canonicalPathname);
92
+ const middlewareInit = { request: { headers: requestHeaders } };
93
+ if (pathname === internalPathname) response = NextResponse.next(middlewareInit);
94
+ else {
95
+ const url = request.nextUrl.clone();
96
+ url.pathname = internalPathname;
97
+ response = NextResponse.rewrite(url, middlewareInit);
98
+ }
99
+ }
100
+ response.headers.set(LOCALE_HEADER, locale);
101
+ if (request.cookies.get(cookieName)?.value !== locale) response.cookies.set(cookieName, locale, {
102
+ path: "/",
103
+ maxAge: 365 * 24 * 60 * 60,
104
+ sameSite: "lax",
105
+ secure: isDev ? false : cookieSecureConfig
106
+ });
107
+ return response;
108
+ };
109
+ }
110
+ /**
111
+ * Detect locale from a specific source
112
+ */
113
+ function detectFromSource(request, source, locales, defaultLocale, cookieName, headerName, resolveAcceptLanguage) {
114
+ switch (source) {
115
+ case "cookie": {
116
+ const cookieValue = request.cookies.get(cookieName)?.value;
117
+ if (cookieValue && locales.includes(cookieValue)) return cookieValue;
118
+ return;
119
+ }
120
+ case "header": {
121
+ if (!headerName) return void 0;
122
+ const headerValue = request.headers.get(headerName);
123
+ if (headerValue && locales.includes(headerValue)) return headerValue;
124
+ return;
125
+ }
126
+ case "accept-language": {
127
+ const acceptLanguage = request.headers.get("accept-language");
128
+ if (!acceptLanguage) return void 0;
129
+ return resolveAcceptLanguage(acceptLanguage, locales, defaultLocale);
130
+ }
131
+ default: return;
132
+ }
133
+ }
134
+ /**
135
+ * Check if path should be skipped by middleware.
136
+ * Static asset filtering is expected to be handled by Next.js matcher config.
137
+ */
138
+ function shouldSkipPath(pathname) {
139
+ const isApiPath = pathname === "/api" || pathname.startsWith("/api/");
140
+ return pathname.startsWith("/_next") || isApiPath;
141
+ }
142
+ /**
143
+ * Extract locale from URL path
144
+ */
145
+ function extractLocaleFromPath(pathname, locales) {
146
+ const firstSegment = pathname.split("/").filter(Boolean)[0];
147
+ return locales.includes(firstSegment) ? firstSegment : void 0;
148
+ }
149
+ /**
150
+ * Default Accept-Language resolver
151
+ *
152
+ * Handles common cases with a simple parser. For production apps with
153
+ * diverse locales (CJK, regional variants), use @formatjs/intl-localematcher.
154
+ *
155
+ * Matching strategy:
156
+ * 1. Try exact match (e.g., "en-US" matches "en-US")
157
+ * 2. Try base language match (e.g., "en-US" matches "en")
158
+ * 3. Try finding a locale that starts with the language (e.g., "en" matches "en-US")
159
+ */
160
+ function defaultResolveAcceptLanguage(acceptLanguage, locales, _defaultLocale) {
161
+ const languages = acceptLanguage.split(",").map((lang) => {
162
+ const [code, q = "q=1"] = lang.trim().split(";");
163
+ const quality = parseFloat(q.split("=")[1] || "1");
164
+ return {
165
+ code: code.trim(),
166
+ quality: isNaN(quality) ? 1 : quality
167
+ };
168
+ }).filter(({ code, quality }) => code.length > 0 && quality > 0).sort((a, b) => b.quality - a.quality);
169
+ for (const { code } of languages) {
170
+ const normalizedCode = code.toLowerCase();
171
+ const exactMatch = locales.find((locale) => locale.toLowerCase() === normalizedCode);
172
+ if (exactMatch) return exactMatch;
173
+ const baseLang = normalizedCode.split("-")[0];
174
+ const baseMatch = locales.find((locale) => locale.toLowerCase() === baseLang);
175
+ if (baseMatch) return baseMatch;
176
+ const prefixMatch = locales.find((locale) => locale.toLowerCase().startsWith(baseLang + "-"));
177
+ if (prefixMatch) return prefixMatch;
178
+ }
179
+ }
180
+ function getInternalPathname(locale, canonicalPathname) {
181
+ return canonicalPathname === "/" ? `/${locale}` : `/${locale}${canonicalPathname}`;
182
+ }
183
+ //#endregion
184
+ export { createMiddleware };
@@ -0,0 +1,87 @@
1
+ import { NextRequest } from 'next/server';
2
+ import { RoutingConfig } from '../routing/types';
3
+ /**
4
+ * Locale detection source types
5
+ */
6
+ export type LocaleDetectionSource = "cookie" | "header" | "accept-language";
7
+ /**
8
+ * Function type for custom Accept-Language resolution
9
+ *
10
+ * @param acceptLanguage - The Accept-Language header value
11
+ * @param locales - Array of supported locales
12
+ * @param defaultLocale - The default locale to fall back to
13
+ * @returns The matched locale or undefined
14
+ *
15
+ * @example Using @formatjs/intl-localematcher (recommended for production)
16
+ * ```typescript
17
+ * import Negotiator from 'negotiator';
18
+ * import { match } from '@formatjs/intl-localematcher';
19
+ *
20
+ * const resolveAcceptLanguage = (header, locales, defaultLocale) => {
21
+ * try {
22
+ * const languages = new Negotiator({
23
+ * headers: { 'accept-language': header }
24
+ * }).languages();
25
+ * return match(languages, [...locales], defaultLocale);
26
+ * } catch {
27
+ * return undefined;
28
+ * }
29
+ * };
30
+ * ```
31
+ */
32
+ export type ResolveAcceptLanguage = (acceptLanguage: string, locales: readonly string[], defaultLocale: string) => string | undefined;
33
+ /**
34
+ * Configuration for locale detection
35
+ */
36
+ export interface LocaleDetectionConfig {
37
+ /**
38
+ * Detection order (URL path is always checked first)
39
+ * @default ['cookie', 'accept-language']
40
+ */
41
+ order?: LocaleDetectionSource[];
42
+ /**
43
+ * Cookie name for storing locale preference
44
+ * @default 'NEXT_LOCALE'
45
+ */
46
+ cookieName?: string;
47
+ /**
48
+ * Set the Secure flag on the locale cookie.
49
+ * When true, the cookie is only sent over HTTPS.
50
+ * Automatically disabled in development (NODE_ENV=development) regardless of this setting.
51
+ * @default true
52
+ */
53
+ cookieSecure?: boolean;
54
+ /**
55
+ * Custom header name to read locale from
56
+ * Useful when you have a proxy that sets locale header
57
+ * @default undefined (not checked unless in order)
58
+ */
59
+ headerName?: string;
60
+ /**
61
+ * Custom Accept-Language resolution function (pluggable)
62
+ *
63
+ * By default, uses a simple parser that handles common cases.
64
+ * For production apps with diverse locales (especially CJK languages),
65
+ * consider using @formatjs/intl-localematcher for RFC-compliant matching.
66
+ *
67
+ * @default Built-in simple parser
68
+ */
69
+ resolveAcceptLanguage?: ResolveAcceptLanguage;
70
+ }
71
+ /**
72
+ * Configuration for i18n middleware
73
+ */
74
+ export interface MiddlewareConfig extends RoutingConfig {
75
+ /**
76
+ * Custom locale detection logic (optional)
77
+ * If provided, this function is called first to detect locale
78
+ * Return undefined to fall back to default detection
79
+ */
80
+ detectLocale?: (request: NextRequest) => string | undefined;
81
+ /**
82
+ * Locale detection configuration
83
+ * Allows customizing detection sources and their order
84
+ */
85
+ localeDetection?: LocaleDetectionConfig;
86
+ }
87
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/middleware/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,QAAQ,GAAG,iBAAiB,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAClC,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,aAAa,EAAE,MAAM,KAClB,MAAM,GAAG,SAAS,CAAC;AAExB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,KAAK,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAEhC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,GAAG,SAAS,CAAC;IAE5D;;;OAGG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAC;CACzC"}
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_createMiddleware = require("./middleware/createMiddleware.cjs");
3
+ exports.createMiddleware = require_createMiddleware.createMiddleware;
@@ -0,0 +1,3 @@
1
+ export { createMiddleware } from './middleware/createMiddleware';
2
+ export type { MiddlewareConfig, LocaleDetectionConfig, LocaleDetectionSource, ResolveAcceptLanguage, } from './middleware/types';
3
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { createMiddleware } from "./middleware/createMiddleware.js";
2
+ export { createMiddleware };
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ "use client";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const require_Link = require("./routing/Link.cjs");
5
+ const require_hooks = require("./routing/hooks.cjs");
6
+ exports.Link = require_Link.Link;
7
+ exports.useLocalizedRouter = require_hooks.useLocalizedRouter;
8
+ exports.usePathname = require_hooks.usePathname;
@@ -0,0 +1,5 @@
1
+ export { Link } from './routing/Link';
2
+ export { usePathname, useLocalizedRouter } from './routing/hooks';
3
+ export type { LocalizedLinkProps } from './routing/Link';
4
+ export type { LocalizedRouter } from './routing/hooks';
5
+ //# sourceMappingURL=navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAClE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,5 @@
1
+ "use client";
2
+ "use client";
3
+ import { Link } from "./routing/Link.js";
4
+ import { useLocalizedRouter, usePathname } from "./routing/hooks.js";
5
+ export { Link, useLocalizedRouter, usePathname };
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ "use client";
3
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
4
+ const require_context = require("./context.cjs");
5
+ const require_utils = require("./utils.cjs");
6
+ let react = require("react");
7
+ react = require_runtime.__toESM(react);
8
+ let _comvi_react = require("@comvi/react");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ let next_link = require("next/link");
11
+ next_link = require_runtime.__toESM(next_link);
12
+ //#region src/routing/Link.tsx
13
+ /**
14
+ * Localized Link component that adds locale prefix based on routing config
15
+ *
16
+ * This component wraps Next.js Link and prepends the current locale
17
+ * using localePrefix/pathnames rules from the routing configuration.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * import { Link } from '@comvi/next/navigation';
22
+ *
23
+ * // Uses current locale
24
+ * <Link href="/about">About</Link>
25
+ *
26
+ * // Specify different locale
27
+ * <Link href="/about" locale="de">German About</Link>
28
+ * ```
29
+ */
30
+ var Link = (0, react.forwardRef)(function Link({ href, locale: targetLocale, ...props }, ref) {
31
+ const { locale: currentLocale } = (0, _comvi_react.useI18n)();
32
+ const locale = targetLocale ?? currentLocale;
33
+ const routing = require_context.useRoutingConfig();
34
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(next_link.default, {
35
+ ref,
36
+ href: typeof href === "string" ? require_utils.localizeHref(href, locale, routing ?? void 0) : require_utils.localizeUrlObject(href, locale, routing ?? void 0),
37
+ ...props
38
+ });
39
+ });
40
+ Link.displayName = "LocalizedLink";
41
+ //#endregion
42
+ exports.Link = Link;
@@ -0,0 +1,25 @@
1
+ import { default as NextLink } from 'next/link';
2
+ import { default as React, ComponentProps } from 'react';
3
+ export interface LocalizedLinkProps extends Omit<ComponentProps<typeof NextLink>, "locale"> {
4
+ /** Target locale (defaults to current locale) */
5
+ locale?: string;
6
+ }
7
+ /**
8
+ * Localized Link component that adds locale prefix based on routing config
9
+ *
10
+ * This component wraps Next.js Link and prepends the current locale
11
+ * using localePrefix/pathnames rules from the routing configuration.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { Link } from '@comvi/next/navigation';
16
+ *
17
+ * // Uses current locale
18
+ * <Link href="/about">About</Link>
19
+ *
20
+ * // Specify different locale
21
+ * <Link href="/about" locale="de">German About</Link>
22
+ * ```
23
+ */
24
+ export declare const Link: React.ForwardRefExoticComponent<Omit<LocalizedLinkProps, "ref"> & React.RefAttributes<HTMLAnchorElement>>;
25
+ //# sourceMappingURL=Link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Link.d.ts","sourceRoot":"","sources":["../../src/routing/Link.tsx"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,WAAW,CAAC;AAEjC,OAAO,KAAqB,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAI5C,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,cAAc,CAAC,OAAO,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACzF,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,IAAI,2GAef,CAAC"}
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ "use client";
3
+ import { useRoutingConfig } from "./context.js";
4
+ import { localizeHref, localizeUrlObject } from "./utils.js";
5
+ import { forwardRef } from "react";
6
+ import { useI18n } from "@comvi/react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ import NextLink from "next/link";
9
+ //#region src/routing/Link.tsx
10
+ /**
11
+ * Localized Link component that adds locale prefix based on routing config
12
+ *
13
+ * This component wraps Next.js Link and prepends the current locale
14
+ * using localePrefix/pathnames rules from the routing configuration.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * import { Link } from '@comvi/next/navigation';
19
+ *
20
+ * // Uses current locale
21
+ * <Link href="/about">About</Link>
22
+ *
23
+ * // Specify different locale
24
+ * <Link href="/about" locale="de">German About</Link>
25
+ * ```
26
+ */
27
+ var Link = forwardRef(function Link({ href, locale: targetLocale, ...props }, ref) {
28
+ const { locale: currentLocale } = useI18n();
29
+ const locale = targetLocale ?? currentLocale;
30
+ const routing = useRoutingConfig();
31
+ return /* @__PURE__ */ jsx(NextLink, {
32
+ ref,
33
+ href: typeof href === "string" ? localizeHref(href, locale, routing ?? void 0) : localizeUrlObject(href, locale, routing ?? void 0),
34
+ ...props
35
+ });
36
+ });
37
+ Link.displayName = "LocalizedLink";
38
+ //#endregion
39
+ export { Link };