@comvi/next 0.1.1 → 0.3.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 +58 -2
- package/dist/client/I18nProvider.d.ts.map +1 -1
- package/dist/client/I18nProvider.js +25 -9
- package/dist/client.d.ts +2 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -2
- package/dist/routing/Link.js +2 -2
- package/dist/routing/hooks.js +3 -3
- package/package.json +13 -17
- package/dist/_virtual/_rolldown/runtime.cjs +0 -23
- package/dist/client/I18nProvider.cjs +0 -101
- package/dist/client/index.d.ts +0 -5
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client.cjs +0 -31
- package/dist/createNextI18n.cjs +0 -153
- package/dist/index.cjs +0 -17
- package/dist/middleware/createMiddleware.cjs +0 -185
- package/dist/middleware.cjs +0 -3
- package/dist/navigation.cjs +0 -8
- package/dist/routing/Link.cjs +0 -42
- package/dist/routing/context.cjs +0 -21
- package/dist/routing/defineRouting.cjs +0 -141
- package/dist/routing/hooks.cjs +0 -104
- package/dist/routing/utils.cjs +0 -94
- package/dist/routing.cjs +0 -5
- package/dist/server/cache.cjs +0 -69
- package/dist/server/ensureInitialized.cjs +0 -19
- package/dist/server/getI18n.cjs +0 -115
- package/dist/server/getLocale.cjs +0 -37
- package/dist/server/loadTranslations.cjs +0 -54
- package/dist/server/setRequestLocale.cjs +0 -31
- package/dist/server.cjs +0 -11
|
@@ -1,185 +0,0 @@
|
|
|
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;
|
package/dist/middleware.cjs
DELETED
package/dist/navigation.cjs
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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;
|
package/dist/routing/Link.cjs
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
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;
|
package/dist/routing/context.cjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
-
const require_defineRouting = require("./defineRouting.cjs");
|
|
4
|
-
let react = require("react");
|
|
5
|
-
react = require_runtime.__toESM(react);
|
|
6
|
-
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
-
//#region src/routing/context.tsx
|
|
8
|
-
var RoutingContext = (0, react.createContext)(null);
|
|
9
|
-
function RoutingProvider({ routing, children }) {
|
|
10
|
-
const value = (0, react.useMemo)(() => require_defineRouting.defineRouting(routing), [routing]);
|
|
11
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RoutingContext.Provider, {
|
|
12
|
-
value,
|
|
13
|
-
children
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
function useRoutingConfig() {
|
|
17
|
-
return (0, react.useContext)(RoutingContext);
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
exports.RoutingProvider = RoutingProvider;
|
|
21
|
-
exports.useRoutingConfig = useRoutingConfig;
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
//#region src/routing/defineRouting.ts
|
|
2
|
-
/**
|
|
3
|
-
* Define routing configuration for your app
|
|
4
|
-
*
|
|
5
|
-
* This configuration is used by both the middleware and navigation components
|
|
6
|
-
* to handle localized routing.
|
|
7
|
-
*
|
|
8
|
-
* @param config - Routing configuration
|
|
9
|
-
* @returns The same configuration with defaults applied
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```typescript
|
|
13
|
-
* // i18n/routing.ts
|
|
14
|
-
* import { defineRouting } from '@comvi/next/routing';
|
|
15
|
-
*
|
|
16
|
-
* export const routing = defineRouting({
|
|
17
|
-
* locales: ['en', 'uk', 'de'],
|
|
18
|
-
* defaultLocale: 'en',
|
|
19
|
-
* localePrefix: 'as-needed',
|
|
20
|
-
* });
|
|
21
|
-
* ```
|
|
22
|
-
*/
|
|
23
|
-
function defineRouting(config) {
|
|
24
|
-
return {
|
|
25
|
-
...config,
|
|
26
|
-
localePrefix: config.localePrefix ?? "as-needed",
|
|
27
|
-
localeCookie: config.localeCookie ?? "NEXT_LOCALE",
|
|
28
|
-
pathnames: config.pathnames ?? {}
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Type guard to check if a string is a valid locale
|
|
33
|
-
*
|
|
34
|
-
* Use this in layouts/pages to validate the locale parameter
|
|
35
|
-
* and get proper TypeScript narrowing.
|
|
36
|
-
*
|
|
37
|
-
* @param locales - Array of supported locales
|
|
38
|
-
* @param locale - String to check
|
|
39
|
-
* @returns True if locale is valid
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```typescript
|
|
43
|
-
* import { hasLocale } from '@comvi/next/routing';
|
|
44
|
-
* import { routing } from '@/i18n/routing';
|
|
45
|
-
* import { notFound } from 'next/navigation';
|
|
46
|
-
*
|
|
47
|
-
* export default async function Page({ params }: { params: Promise<{ locale: string }> }) {
|
|
48
|
-
* const { locale } = await params;
|
|
49
|
-
*
|
|
50
|
-
* if (!hasLocale(routing.locales, locale)) {
|
|
51
|
-
* notFound();
|
|
52
|
-
* }
|
|
53
|
-
*
|
|
54
|
-
* // locale is now typed as 'en' | 'uk' | 'de'
|
|
55
|
-
* return <div>Locale: {locale}</div>;
|
|
56
|
-
* }
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
function hasLocale(locales, locale) {
|
|
60
|
-
return locales.includes(locale);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Creates a getPathname function bound to your routing configuration
|
|
64
|
-
*
|
|
65
|
-
* Use this to construct localized pathnames for sitemaps, hreflang tags,
|
|
66
|
-
* and canonical URLs.
|
|
67
|
-
*
|
|
68
|
-
* @param config - Your routing configuration
|
|
69
|
-
* @returns A getPathname function
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```typescript
|
|
73
|
-
* // i18n/navigation.ts
|
|
74
|
-
* import { createGetPathname } from '@comvi/next/routing';
|
|
75
|
-
* import { routing } from './routing';
|
|
76
|
-
*
|
|
77
|
-
* export const getPathname = createGetPathname(routing);
|
|
78
|
-
* ```
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```typescript
|
|
82
|
-
* // app/sitemap.ts
|
|
83
|
-
* import { getPathname } from '@/i18n/navigation';
|
|
84
|
-
* import { routing } from '@/i18n/routing';
|
|
85
|
-
*
|
|
86
|
-
* export default function sitemap() {
|
|
87
|
-
* const baseUrl = 'https://example.com';
|
|
88
|
-
* const pages = ['/', '/about', '/contact'];
|
|
89
|
-
*
|
|
90
|
-
* return pages.flatMap((page) =>
|
|
91
|
-
* routing.locales.map((locale) => ({
|
|
92
|
-
* url: `${baseUrl}${getPathname({ locale, href: page })}`,
|
|
93
|
-
* lastModified: new Date(),
|
|
94
|
-
* alternates: {
|
|
95
|
-
* languages: Object.fromEntries(
|
|
96
|
-
* routing.locales.map((l) => [
|
|
97
|
-
* l,
|
|
98
|
-
* `${baseUrl}${getPathname({ locale: l, href: page })}`,
|
|
99
|
-
* ])
|
|
100
|
-
* ),
|
|
101
|
-
* },
|
|
102
|
-
* }))
|
|
103
|
-
* );
|
|
104
|
-
* }
|
|
105
|
-
* ```
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* ```typescript
|
|
109
|
-
* // Generate hreflang tags
|
|
110
|
-
* import { getPathname } from '@/i18n/navigation';
|
|
111
|
-
* import { routing } from '@/i18n/routing';
|
|
112
|
-
*
|
|
113
|
-
* function generateHreflangTags(currentPath: string) {
|
|
114
|
-
* return routing.locales.map((locale) => ({
|
|
115
|
-
* rel: 'alternate',
|
|
116
|
-
* hrefLang: locale,
|
|
117
|
-
* href: `https://example.com${getPathname({ locale, href: currentPath })}`,
|
|
118
|
-
* }));
|
|
119
|
-
* }
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
function createGetPathname(config) {
|
|
123
|
-
const { locales, defaultLocale, localePrefix, pathnames } = config;
|
|
124
|
-
return function getPathname({ locale, href }) {
|
|
125
|
-
if (!locales.includes(locale)) console.warn(`[getPathname] Unknown locale "${locale}". Expected one of: ${locales.join(", ")}`);
|
|
126
|
-
const localizedPath = pathnames[href]?.[locale] ?? href;
|
|
127
|
-
const normalizedPath = localizedPath.startsWith("/") ? localizedPath : `/${localizedPath}`;
|
|
128
|
-
switch (localePrefix) {
|
|
129
|
-
case "always": return `/${locale}${normalizedPath === "/" ? "" : normalizedPath}`;
|
|
130
|
-
case "as-needed":
|
|
131
|
-
if (locale === defaultLocale) return normalizedPath;
|
|
132
|
-
return `/${locale}${normalizedPath === "/" ? "" : normalizedPath}`;
|
|
133
|
-
case "never": return normalizedPath;
|
|
134
|
-
default: return normalizedPath;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
//#endregion
|
|
139
|
-
exports.createGetPathname = createGetPathname;
|
|
140
|
-
exports.defineRouting = defineRouting;
|
|
141
|
-
exports.hasLocale = hasLocale;
|
package/dist/routing/hooks.cjs
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
"use client";
|
|
3
|
-
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
|
-
let _comvi_react = require("@comvi/react");
|
|
8
|
-
let next_navigation = require("next/navigation");
|
|
9
|
-
//#region src/routing/hooks.ts
|
|
10
|
-
/**
|
|
11
|
-
* Get pathname without locale prefix
|
|
12
|
-
*
|
|
13
|
-
* This hook returns the current pathname with the locale prefix removed,
|
|
14
|
-
* making it easier to work with routes in a locale-agnostic way.
|
|
15
|
-
*
|
|
16
|
-
* @returns Pathname without locale prefix
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```tsx
|
|
20
|
-
* import { usePathname } from '@comvi/next/navigation';
|
|
21
|
-
*
|
|
22
|
-
* function MyComponent() {
|
|
23
|
-
* const pathname = usePathname();
|
|
24
|
-
* // If URL is /en/about, pathname is /about
|
|
25
|
-
* return <p>Current page: {pathname}</p>;
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
function usePathname() {
|
|
30
|
-
const pathname = (0, next_navigation.usePathname)() ?? "/";
|
|
31
|
-
const routing = require_context.useRoutingConfig();
|
|
32
|
-
const { locale } = (0, _comvi_react.useI18n)();
|
|
33
|
-
if (routing) return require_utils.getCanonicalPathname(require_utils.stripLocalePrefix(pathname, routing.locales), routing, locale);
|
|
34
|
-
if (pathname.startsWith(`/${locale}/`)) return pathname.slice(locale.length + 1);
|
|
35
|
-
if (pathname === `/${locale}`) return "/";
|
|
36
|
-
return pathname;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Localized router with automatic locale prefixing
|
|
40
|
-
*
|
|
41
|
-
* This hook wraps Next.js useRouter and automatically adds locale
|
|
42
|
-
* prefixes to navigation methods.
|
|
43
|
-
*
|
|
44
|
-
* @returns Localized router object
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```tsx
|
|
48
|
-
* import { useLocalizedRouter } from '@comvi/next/navigation';
|
|
49
|
-
*
|
|
50
|
-
* function MyComponent() {
|
|
51
|
-
* const router = useLocalizedRouter();
|
|
52
|
-
*
|
|
53
|
-
* const handleClick = () => {
|
|
54
|
-
* router.push('/about'); // Navigates to /en/about (or current locale)
|
|
55
|
-
* };
|
|
56
|
-
*
|
|
57
|
-
* const handleGerman = () => {
|
|
58
|
-
* router.push('/about', 'de'); // Navigates to /de/about
|
|
59
|
-
* };
|
|
60
|
-
*
|
|
61
|
-
* return <button onClick={handleClick}>Go to About</button>;
|
|
62
|
-
* }
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
function useLocalizedRouter() {
|
|
66
|
-
const router = (0, next_navigation.useRouter)();
|
|
67
|
-
const { locale } = (0, _comvi_react.useI18n)();
|
|
68
|
-
const routing = require_context.useRoutingConfig();
|
|
69
|
-
const push = (0, react.useCallback)((href, targetLocale) => {
|
|
70
|
-
const localizedHref = require_utils.localizeHref(href, targetLocale ?? locale, routing ?? void 0);
|
|
71
|
-
router.push(localizedHref);
|
|
72
|
-
}, [
|
|
73
|
-
router,
|
|
74
|
-
locale,
|
|
75
|
-
routing
|
|
76
|
-
]);
|
|
77
|
-
const replace = (0, react.useCallback)((href, targetLocale) => {
|
|
78
|
-
const localizedHref = require_utils.localizeHref(href, targetLocale ?? locale, routing ?? void 0);
|
|
79
|
-
router.replace(localizedHref);
|
|
80
|
-
}, [
|
|
81
|
-
router,
|
|
82
|
-
locale,
|
|
83
|
-
routing
|
|
84
|
-
]);
|
|
85
|
-
const prefetch = (0, react.useCallback)((href, targetLocale) => {
|
|
86
|
-
const localizedHref = require_utils.localizeHref(href, targetLocale ?? locale, routing ?? void 0);
|
|
87
|
-
router.prefetch(localizedHref);
|
|
88
|
-
}, [
|
|
89
|
-
router,
|
|
90
|
-
locale,
|
|
91
|
-
routing
|
|
92
|
-
]);
|
|
93
|
-
return {
|
|
94
|
-
push,
|
|
95
|
-
replace,
|
|
96
|
-
back: router.back,
|
|
97
|
-
forward: router.forward,
|
|
98
|
-
refresh: router.refresh,
|
|
99
|
-
prefetch
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
//#endregion
|
|
103
|
-
exports.useLocalizedRouter = useLocalizedRouter;
|
|
104
|
-
exports.usePathname = usePathname;
|
package/dist/routing/utils.cjs
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
const require_defineRouting = require("./defineRouting.cjs");
|
|
2
|
-
//#region src/routing/utils.ts
|
|
3
|
-
var getPathnameCache = /* @__PURE__ */ new WeakMap();
|
|
4
|
-
var isExternalHref = (href) => {
|
|
5
|
-
return /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith("//");
|
|
6
|
-
};
|
|
7
|
-
var isRelativeFragmentOrQuery = (href) => {
|
|
8
|
-
return href.startsWith("#") || href.startsWith("?");
|
|
9
|
-
};
|
|
10
|
-
var normalizePath = (path) => {
|
|
11
|
-
if (!path) return "/";
|
|
12
|
-
return path.startsWith("/") ? path : `/${path}`;
|
|
13
|
-
};
|
|
14
|
-
var getCanonicalPathMatch = (pathname, routing, preferredLocale) => {
|
|
15
|
-
const normalizedPath = normalizePath(pathname);
|
|
16
|
-
const localesToTry = preferredLocale ? [preferredLocale, ...routing.locales.filter((locale) => locale !== preferredLocale)] : [...routing.locales];
|
|
17
|
-
for (const [canonicalPath, localizedByLocale] of Object.entries(routing.pathnames)) {
|
|
18
|
-
const normalizedCanonicalPath = normalizePath(canonicalPath);
|
|
19
|
-
if (normalizedPath === normalizedCanonicalPath) return normalizedCanonicalPath;
|
|
20
|
-
for (const locale of localesToTry) {
|
|
21
|
-
const localizedPath = localizedByLocale[locale];
|
|
22
|
-
if (localizedPath && normalizePath(localizedPath) === normalizedPath) return normalizedCanonicalPath;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var stripLocalePrefix = (pathname, locales) => {
|
|
27
|
-
const normalized = normalizePath(pathname);
|
|
28
|
-
const segments = normalized.split("/").filter(Boolean);
|
|
29
|
-
if (segments.length === 0) return "/";
|
|
30
|
-
const first = segments[0];
|
|
31
|
-
if (locales.includes(first)) {
|
|
32
|
-
const rest = segments.slice(1).join("/");
|
|
33
|
-
return rest ? `/${rest}` : "/";
|
|
34
|
-
}
|
|
35
|
-
return normalized;
|
|
36
|
-
};
|
|
37
|
-
var getCanonicalPathname = (pathname, routing, preferredLocale) => {
|
|
38
|
-
const normalizedPath = normalizePath(pathname);
|
|
39
|
-
return getCanonicalPathMatch(normalizedPath, routing, preferredLocale) ?? normalizedPath;
|
|
40
|
-
};
|
|
41
|
-
var splitHref = (href) => {
|
|
42
|
-
const match = href.match(/[?#]/);
|
|
43
|
-
if (!match || match.index === void 0) return {
|
|
44
|
-
path: href,
|
|
45
|
-
suffix: ""
|
|
46
|
-
};
|
|
47
|
-
const index = match.index;
|
|
48
|
-
return {
|
|
49
|
-
path: href.slice(0, index),
|
|
50
|
-
suffix: href.slice(index)
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
var localizePathname = (pathname, locale, routing) => {
|
|
54
|
-
const { path, suffix } = splitHref(pathname);
|
|
55
|
-
const canonicalPath = getCanonicalPathname(stripLocalePrefix(path, routing.locales), routing, locale);
|
|
56
|
-
let getPathname = getPathnameCache.get(routing);
|
|
57
|
-
if (!getPathname) {
|
|
58
|
-
getPathname = require_defineRouting.createGetPathname(routing);
|
|
59
|
-
getPathnameCache.set(routing, getPathname);
|
|
60
|
-
}
|
|
61
|
-
return `${getPathname({
|
|
62
|
-
locale,
|
|
63
|
-
href: canonicalPath
|
|
64
|
-
})}${suffix}`;
|
|
65
|
-
};
|
|
66
|
-
var localizeHref = (href, locale, routing) => {
|
|
67
|
-
if (isExternalHref(href)) return href;
|
|
68
|
-
if (isRelativeFragmentOrQuery(href)) return href;
|
|
69
|
-
if (!routing) {
|
|
70
|
-
const normalized = normalizePath(href);
|
|
71
|
-
return `/${locale}${normalized === "/" ? "" : normalized}`;
|
|
72
|
-
}
|
|
73
|
-
return localizePathname(href, locale, routing);
|
|
74
|
-
};
|
|
75
|
-
var localizeUrlObject = (href, locale, routing) => {
|
|
76
|
-
if (href.protocol) return href;
|
|
77
|
-
const pathname = typeof href.pathname === "string" ? href.pathname : "/";
|
|
78
|
-
if (!routing) {
|
|
79
|
-
const normalized = normalizePath(pathname);
|
|
80
|
-
return {
|
|
81
|
-
...href,
|
|
82
|
-
pathname: `/${locale}${normalized === "/" ? "" : normalized}`
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
...href,
|
|
87
|
-
pathname: localizePathname(pathname, locale, routing)
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
//#endregion
|
|
91
|
-
exports.getCanonicalPathname = getCanonicalPathname;
|
|
92
|
-
exports.localizeHref = localizeHref;
|
|
93
|
-
exports.localizeUrlObject = localizeUrlObject;
|
|
94
|
-
exports.stripLocalePrefix = stripLocalePrefix;
|
package/dist/routing.cjs
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_defineRouting = require("./routing/defineRouting.cjs");
|
|
3
|
-
exports.createGetPathname = require_defineRouting.createGetPathname;
|
|
4
|
-
exports.defineRouting = require_defineRouting.defineRouting;
|
|
5
|
-
exports.hasLocale = require_defineRouting.hasLocale;
|
package/dist/server/cache.cjs
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
-
//#region src/server/cache.ts
|
|
3
|
-
/**
|
|
4
|
-
* Request-scoped locale storage using React cache()
|
|
5
|
-
* This allows setRequestLocale() to store locale that getTranslations() can read
|
|
6
|
-
*
|
|
7
|
-
* React's cache() creates a per-request memoized value in Server Components,
|
|
8
|
-
* allowing us to share state across the component tree without prop drilling.
|
|
9
|
-
*/
|
|
10
|
-
var getRequestStore = (0, require("react").cache)(() => ({ locale: void 0 }));
|
|
11
|
-
/**
|
|
12
|
-
* Global i18n reference (set once via setI18n)
|
|
13
|
-
* This is safe because Next.js creates separate module instances for server/client
|
|
14
|
-
*/
|
|
15
|
-
var globalI18n;
|
|
16
|
-
/**
|
|
17
|
-
* Configure the global i18n instance for server-side usage
|
|
18
|
-
*
|
|
19
|
-
* Call this once in your i18n configuration file to make getTranslations() work.
|
|
20
|
-
*
|
|
21
|
-
* @param i18n - The i18n instance created with createI18n
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```typescript
|
|
25
|
-
* // i18n/index.ts
|
|
26
|
-
* import { createI18n } from '@comvi/next';
|
|
27
|
-
* import { setI18n } from '@comvi/next/server';
|
|
28
|
-
* import { translations } from './translations';
|
|
29
|
-
*
|
|
30
|
-
* export const i18n = createI18n({
|
|
31
|
-
* locale: 'en',
|
|
32
|
-
* defaultNs: 'default',
|
|
33
|
-
* translation: translations,
|
|
34
|
-
* });
|
|
35
|
-
*
|
|
36
|
-
* // Configure for server-side usage
|
|
37
|
-
* setI18n(i18n);
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
function setI18n(i18n) {
|
|
41
|
-
globalI18n = i18n;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Get the global i18n instance
|
|
45
|
-
* @internal
|
|
46
|
-
*/
|
|
47
|
-
function getI18nInstance() {
|
|
48
|
-
if (!globalI18n) throw new Error("[comvi/next] i18n not configured. Call setI18n(i18n) in your i18n configuration file.");
|
|
49
|
-
return globalI18n;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Set the request locale in the cache
|
|
53
|
-
* @internal
|
|
54
|
-
*/
|
|
55
|
-
function setRequestLocaleInternal(locale) {
|
|
56
|
-
getRequestStore().locale = locale;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Get the request locale from cache
|
|
60
|
-
* @internal
|
|
61
|
-
*/
|
|
62
|
-
function getRequestLocaleFromCache() {
|
|
63
|
-
return getRequestStore().locale;
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
|
-
exports.getI18nInstance = getI18nInstance;
|
|
67
|
-
exports.getRequestLocaleFromCache = getRequestLocaleFromCache;
|
|
68
|
-
exports.setI18n = setI18n;
|
|
69
|
-
exports.setRequestLocaleInternal = setRequestLocaleInternal;
|