@better-translate/nextjs 1.0.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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/chunk-VW2LUTE7.js +291 -0
- package/dist/index.cjs +267 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +20 -0
- package/dist/navigation.cjs +374 -0
- package/dist/navigation.d.cts +55 -0
- package/dist/navigation.d.ts +55 -0
- package/dist/navigation.js +163 -0
- package/dist/proxy.cjs +318 -0
- package/dist/proxy.d.cts +11 -0
- package/dist/proxy.d.ts +11 -0
- package/dist/proxy.js +86 -0
- package/dist/server.cjs +121 -0
- package/dist/server.d.cts +30 -0
- package/dist/server.d.ts +30 -0
- package/dist/server.js +98 -0
- package/package.json +67 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/navigation.tsx
|
|
21
|
+
var navigation_exports = {};
|
|
22
|
+
__export(navigation_exports, {
|
|
23
|
+
createNavigationFunctions: () => createNavigationFunctions
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(navigation_exports);
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
|
|
28
|
+
// src/shared.ts
|
|
29
|
+
var DEFAULT_ROUTE_TEMPLATE = "/[lang]";
|
|
30
|
+
function hasLocale(locales, value) {
|
|
31
|
+
return locales.includes(value);
|
|
32
|
+
}
|
|
33
|
+
function normalizePathname(pathname) {
|
|
34
|
+
if (!pathname) {
|
|
35
|
+
return "/";
|
|
36
|
+
}
|
|
37
|
+
return pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
38
|
+
}
|
|
39
|
+
function getPathnameLocale(pathname, routing) {
|
|
40
|
+
const parsedTemplate = parseRouteTemplate(routing.routeTemplate);
|
|
41
|
+
const pathnameSegments = splitPathname(pathname);
|
|
42
|
+
if (pathnameSegments.length < parsedTemplate.localizedSegments.length) {
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
for (let index = 0; index < parsedTemplate.localizedSegments.length; index += 1) {
|
|
46
|
+
const templateSegment = parsedTemplate.localizedSegments[index];
|
|
47
|
+
const pathnameSegment = pathnameSegments[index];
|
|
48
|
+
if (index === parsedTemplate.localeSegmentIndex) {
|
|
49
|
+
if (!pathnameSegment || !hasLocale(routing.locales, pathnameSegment)) {
|
|
50
|
+
return void 0;
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (pathnameSegment !== templateSegment) {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return pathnameSegments[parsedTemplate.localeSegmentIndex];
|
|
59
|
+
}
|
|
60
|
+
function isPathnameInScope(pathname, routing) {
|
|
61
|
+
if (getPathnameLocale(pathname, routing)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
const parsedTemplate = parseRouteTemplate(routing.routeTemplate);
|
|
65
|
+
const pathnameSegments = splitPathname(pathname);
|
|
66
|
+
if (pathnameSegments.length < parsedTemplate.deLocalizedSegments.length) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
for (let index = 0; index < parsedTemplate.deLocalizedSegments.length; index += 1) {
|
|
70
|
+
if (pathnameSegments[index] !== parsedTemplate.deLocalizedSegments[index]) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
function stripLocaleFromPathname(pathname, routing) {
|
|
77
|
+
const locale = getPathnameLocale(pathname, routing);
|
|
78
|
+
if (!locale) {
|
|
79
|
+
return normalizePathname(pathname);
|
|
80
|
+
}
|
|
81
|
+
const parsedTemplate = parseRouteTemplate(routing.routeTemplate);
|
|
82
|
+
const pathnameSegments = splitPathname(pathname);
|
|
83
|
+
pathnameSegments.splice(parsedTemplate.localeSegmentIndex, 1);
|
|
84
|
+
return joinPathname(pathnameSegments);
|
|
85
|
+
}
|
|
86
|
+
function localizePathname(pathname, locale, routing) {
|
|
87
|
+
const normalizedPathname = normalizePathname(pathname);
|
|
88
|
+
const deLocalizedPathname = stripLocaleFromPathname(
|
|
89
|
+
normalizedPathname,
|
|
90
|
+
routing
|
|
91
|
+
);
|
|
92
|
+
if (!isPathnameInScope(deLocalizedPathname, routing)) {
|
|
93
|
+
return normalizedPathname;
|
|
94
|
+
}
|
|
95
|
+
const parsedTemplate = parseRouteTemplate(routing.routeTemplate);
|
|
96
|
+
const pathnameSegments = splitPathname(deLocalizedPathname);
|
|
97
|
+
const localizedSegments = [...pathnameSegments];
|
|
98
|
+
localizedSegments.splice(parsedTemplate.localeSegmentIndex, 0, locale);
|
|
99
|
+
return joinPathname(localizedSegments);
|
|
100
|
+
}
|
|
101
|
+
function splitHrefString(href) {
|
|
102
|
+
const hashIndex = href.indexOf("#");
|
|
103
|
+
const searchIndex = href.indexOf("?");
|
|
104
|
+
const pathnameEnd = hashIndex === -1 ? searchIndex === -1 ? href.length : searchIndex : searchIndex === -1 ? hashIndex : Math.min(searchIndex, hashIndex);
|
|
105
|
+
const pathname = href.slice(0, pathnameEnd) || "/";
|
|
106
|
+
const search = searchIndex === -1 ? "" : href.slice(searchIndex, hashIndex === -1 ? href.length : hashIndex);
|
|
107
|
+
const hash = hashIndex === -1 ? "" : href.slice(hashIndex);
|
|
108
|
+
return {
|
|
109
|
+
pathname: normalizePathname(pathname),
|
|
110
|
+
search,
|
|
111
|
+
hash
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function isAbsoluteHref(href) {
|
|
115
|
+
return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(href) || href.startsWith("//");
|
|
116
|
+
}
|
|
117
|
+
function normalizeHost(host) {
|
|
118
|
+
return host.toLowerCase().replace(/:\d+$/, "");
|
|
119
|
+
}
|
|
120
|
+
function getDomainForLocale(routing, locale) {
|
|
121
|
+
return routing.domains?.find(
|
|
122
|
+
(domain) => getDomainLocales(domain).includes(locale)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
function buildDomainAwareHref(routing, href, locale, options) {
|
|
126
|
+
if (isAbsoluteHref(href)) {
|
|
127
|
+
return {
|
|
128
|
+
href,
|
|
129
|
+
external: true
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const { hash, pathname, search } = splitHrefString(href);
|
|
133
|
+
const isScopedPath = isPathnameInScope(pathname, routing);
|
|
134
|
+
if (!isScopedPath) {
|
|
135
|
+
return {
|
|
136
|
+
href: `${pathname}${search}${hash}`,
|
|
137
|
+
external: false
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const localizedPathname = localizePathname(pathname, locale, routing);
|
|
141
|
+
const localizedHref = `${localizedPathname}${search}${hash}`;
|
|
142
|
+
const targetDomain = getDomainForLocale(routing, locale);
|
|
143
|
+
if (!targetDomain) {
|
|
144
|
+
return {
|
|
145
|
+
href: localizedHref,
|
|
146
|
+
external: false
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const targetHost = normalizeHost(targetDomain.domain);
|
|
150
|
+
const currentHost = options?.currentHost ? normalizeHost(options.currentHost) : void 0;
|
|
151
|
+
const shouldUseAbsolute = options?.forceAbsolute === true || currentHost === void 0 || currentHost !== targetHost;
|
|
152
|
+
if (!shouldUseAbsolute) {
|
|
153
|
+
return {
|
|
154
|
+
href: localizedHref,
|
|
155
|
+
external: false
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const protocol = targetDomain.protocol ?? options?.protocol ?? "https";
|
|
159
|
+
return {
|
|
160
|
+
href: `${protocol}://${targetHost}${localizedHref}`,
|
|
161
|
+
external: currentHost === void 0 ? true : currentHost !== targetHost
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function parseRouteTemplate(routeTemplate) {
|
|
165
|
+
const normalizedRouteTemplate = normalizePathname(
|
|
166
|
+
routeTemplate ?? DEFAULT_ROUTE_TEMPLATE
|
|
167
|
+
);
|
|
168
|
+
const localizedSegments = splitPathname(normalizedRouteTemplate);
|
|
169
|
+
let localeSegmentIndex = -1;
|
|
170
|
+
let localeParamName = "";
|
|
171
|
+
for (let index = 0; index < localizedSegments.length; index += 1) {
|
|
172
|
+
const segment = localizedSegments[index];
|
|
173
|
+
const dynamicMatch = /^\[([^\]/]+)\]$/.exec(segment);
|
|
174
|
+
if (!dynamicMatch) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (localeSegmentIndex !== -1) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Route template "${normalizedRouteTemplate}" must contain exactly one dynamic locale segment.`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
localeSegmentIndex = index;
|
|
183
|
+
localeParamName = dynamicMatch[1];
|
|
184
|
+
}
|
|
185
|
+
if (localeSegmentIndex === -1) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Route template "${normalizedRouteTemplate}" must contain one dynamic locale segment like "[lang]".`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const deLocalizedSegments = localizedSegments.filter(
|
|
191
|
+
(_, index) => index !== localeSegmentIndex
|
|
192
|
+
);
|
|
193
|
+
const scopePrefixSegments = localizedSegments.slice(0, localeSegmentIndex);
|
|
194
|
+
return {
|
|
195
|
+
deLocalizedSegments,
|
|
196
|
+
localeParamName,
|
|
197
|
+
localeSegmentIndex,
|
|
198
|
+
localizedSegments,
|
|
199
|
+
routeTemplate: normalizedRouteTemplate,
|
|
200
|
+
scopePrefix: joinPathname(scopePrefixSegments)
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function splitPathname(pathname) {
|
|
204
|
+
return normalizePathname(pathname).split("/").filter(Boolean);
|
|
205
|
+
}
|
|
206
|
+
function joinPathname(segments) {
|
|
207
|
+
if (segments.length === 0) {
|
|
208
|
+
return "/";
|
|
209
|
+
}
|
|
210
|
+
return `/${segments.join("/")}`;
|
|
211
|
+
}
|
|
212
|
+
function getDomainLocales(domain) {
|
|
213
|
+
if (!domain.locales || domain.locales.length === 0) {
|
|
214
|
+
return [domain.defaultLocale];
|
|
215
|
+
}
|
|
216
|
+
if (!domain.locales.includes(domain.defaultLocale)) {
|
|
217
|
+
return [...domain.locales, domain.defaultLocale];
|
|
218
|
+
}
|
|
219
|
+
return domain.locales;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/navigation.tsx
|
|
223
|
+
function createNavigationFunctions({
|
|
224
|
+
Link: LinkComponent,
|
|
225
|
+
routing,
|
|
226
|
+
useParams: useInjectedParams,
|
|
227
|
+
usePathname: useInjectedPathname,
|
|
228
|
+
useRouter: useInjectedRouter
|
|
229
|
+
}) {
|
|
230
|
+
const parsedTemplate = parseRouteTemplate(routing.routeTemplate);
|
|
231
|
+
function getPathname({
|
|
232
|
+
href,
|
|
233
|
+
locale = routing.defaultLocale
|
|
234
|
+
}) {
|
|
235
|
+
return localizePathname(href, locale, routing);
|
|
236
|
+
}
|
|
237
|
+
function Link(rawProps) {
|
|
238
|
+
const { href, locale, ...props } = rawProps;
|
|
239
|
+
const params = useInjectedParams();
|
|
240
|
+
const activeLocale = resolveActiveLocale(
|
|
241
|
+
params,
|
|
242
|
+
routing,
|
|
243
|
+
parsedTemplate.localeParamName
|
|
244
|
+
);
|
|
245
|
+
const localizedHref = localizeLinkHref(
|
|
246
|
+
href,
|
|
247
|
+
locale ?? activeLocale,
|
|
248
|
+
routing
|
|
249
|
+
);
|
|
250
|
+
return (0, import_react.createElement)(
|
|
251
|
+
LinkComponent,
|
|
252
|
+
{
|
|
253
|
+
...props,
|
|
254
|
+
href: localizedHref
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
function usePathname() {
|
|
259
|
+
return stripLocaleFromPathname(useInjectedPathname(), routing);
|
|
260
|
+
}
|
|
261
|
+
function useRouter() {
|
|
262
|
+
const router = useInjectedRouter();
|
|
263
|
+
const params = useInjectedParams();
|
|
264
|
+
const activeLocale = resolveActiveLocale(
|
|
265
|
+
params,
|
|
266
|
+
routing,
|
|
267
|
+
parsedTemplate.localeParamName
|
|
268
|
+
);
|
|
269
|
+
const localizedRouter = {
|
|
270
|
+
...router,
|
|
271
|
+
push(href, options) {
|
|
272
|
+
return navigate(
|
|
273
|
+
router.push.bind(router),
|
|
274
|
+
window.location.assign.bind(window.location),
|
|
275
|
+
routing,
|
|
276
|
+
href,
|
|
277
|
+
activeLocale,
|
|
278
|
+
options
|
|
279
|
+
);
|
|
280
|
+
},
|
|
281
|
+
replace(href, options) {
|
|
282
|
+
return navigate(
|
|
283
|
+
router.replace.bind(router),
|
|
284
|
+
window.location.replace.bind(window.location),
|
|
285
|
+
routing,
|
|
286
|
+
href,
|
|
287
|
+
activeLocale,
|
|
288
|
+
options
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
if (typeof router.prefetch === "function") {
|
|
293
|
+
localizedRouter.prefetch = (href, options) => {
|
|
294
|
+
const target = buildNavigationTarget(
|
|
295
|
+
routing,
|
|
296
|
+
href,
|
|
297
|
+
activeLocale,
|
|
298
|
+
options
|
|
299
|
+
);
|
|
300
|
+
if (target.external) {
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
return router.prefetch?.(target.href, target.forwardedOptions);
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return localizedRouter;
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
Link,
|
|
310
|
+
getPathname,
|
|
311
|
+
usePathname,
|
|
312
|
+
useRouter
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function buildNavigationTarget(routing, href, activeLocale, options) {
|
|
316
|
+
const nextLocale = options?.locale ?? activeLocale;
|
|
317
|
+
const forwardedOptions = {
|
|
318
|
+
...options ?? {}
|
|
319
|
+
};
|
|
320
|
+
delete forwardedOptions.locale;
|
|
321
|
+
const target = buildDomainAwareHref(routing, href, nextLocale, {
|
|
322
|
+
currentHost: window.location.host,
|
|
323
|
+
protocol: window.location.protocol.replace(":", "")
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
external: target.external,
|
|
327
|
+
forwardedOptions,
|
|
328
|
+
href: target.href
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function navigate(navigateWithRouter, navigateWithDocument, routing, href, activeLocale, options) {
|
|
332
|
+
const target = buildNavigationTarget(routing, href, activeLocale, options);
|
|
333
|
+
if (target.external) {
|
|
334
|
+
navigateWithDocument(target.href);
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
return navigateWithRouter(target.href, target.forwardedOptions);
|
|
338
|
+
}
|
|
339
|
+
function localizeLinkHref(href, locale, routing) {
|
|
340
|
+
if (typeof href === "string") {
|
|
341
|
+
return buildDomainAwareHref(routing, href, locale, {
|
|
342
|
+
forceAbsolute: Boolean(getDomainForLocale(routing, locale))
|
|
343
|
+
}).href;
|
|
344
|
+
}
|
|
345
|
+
const pathname = typeof href.pathname === "string" ? href.pathname : "/";
|
|
346
|
+
const search = typeof href.search === "string" ? href.search : "";
|
|
347
|
+
const hash = typeof href.hash === "string" ? href.hash : "";
|
|
348
|
+
const localizedHref = buildDomainAwareHref(
|
|
349
|
+
routing,
|
|
350
|
+
`${pathname}${search}${hash}`,
|
|
351
|
+
locale,
|
|
352
|
+
{
|
|
353
|
+
forceAbsolute: Boolean(getDomainForLocale(routing, locale))
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
if (localizedHref.external) {
|
|
357
|
+
return localizedHref.href;
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
...href,
|
|
361
|
+
pathname: localizedHref.href
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function resolveActiveLocale(params, routing, localeParamName) {
|
|
365
|
+
const localeParam = params[localeParamName];
|
|
366
|
+
if (typeof localeParam === "string" && routing.locales.includes(localeParam)) {
|
|
367
|
+
return localeParam;
|
|
368
|
+
}
|
|
369
|
+
return routing.defaultLocale;
|
|
370
|
+
}
|
|
371
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
372
|
+
0 && (module.exports = {
|
|
373
|
+
createNavigationFunctions
|
|
374
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ElementType, ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
import { RoutingConfig } from './index.cjs';
|
|
4
|
+
|
|
5
|
+
type NavigationHref = string | {
|
|
6
|
+
hash?: string;
|
|
7
|
+
pathname?: string;
|
|
8
|
+
search?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
11
|
+
type ParamsLike = Record<string, string | string[] | undefined>;
|
|
12
|
+
type RouterLike = {
|
|
13
|
+
prefetch?: (href: string, options?: any) => unknown;
|
|
14
|
+
push: (href: string, options?: any) => unknown;
|
|
15
|
+
replace: (href: string, options?: any) => unknown;
|
|
16
|
+
};
|
|
17
|
+
type RouterMethodReturn<TRouter extends RouterLike, TMethod extends "push" | "replace" | "prefetch"> = TMethod extends keyof TRouter ? Extract<TRouter[TMethod], (...args: any[]) => unknown> extends (...args: any[]) => infer TReturn ? TReturn : void : void;
|
|
18
|
+
type WithLocale<TLocale extends string, TOptions> = [TOptions] extends [
|
|
19
|
+
undefined
|
|
20
|
+
] ? {
|
|
21
|
+
locale?: TLocale;
|
|
22
|
+
} : TOptions & {
|
|
23
|
+
locale?: TLocale;
|
|
24
|
+
};
|
|
25
|
+
interface GetPathnameOptions<TLocale extends string> {
|
|
26
|
+
href: string;
|
|
27
|
+
locale?: TLocale;
|
|
28
|
+
}
|
|
29
|
+
type LocalizedLinkProps<TLocale extends string, TLink extends ElementType> = Omit<ComponentPropsWithoutRef<TLink>, "href"> & {
|
|
30
|
+
href: ComponentPropsWithoutRef<TLink> extends {
|
|
31
|
+
href: infer THref;
|
|
32
|
+
} ? THref : NavigationHref;
|
|
33
|
+
locale?: TLocale;
|
|
34
|
+
};
|
|
35
|
+
type LocalizedRouter<TLocale extends string, TRouter extends RouterLike> = Omit<TRouter, "prefetch" | "push" | "replace"> & {
|
|
36
|
+
push(href: string, options?: WithLocale<TLocale, Record<string, unknown>>): RouterMethodReturn<TRouter, "push">;
|
|
37
|
+
replace(href: string, options?: WithLocale<TLocale, Record<string, unknown>>): RouterMethodReturn<TRouter, "replace">;
|
|
38
|
+
} & ("prefetch" extends keyof TRouter ? {
|
|
39
|
+
prefetch(href: string, options?: WithLocale<TLocale, Record<string, unknown>>): RouterMethodReturn<TRouter, "prefetch">;
|
|
40
|
+
} : {});
|
|
41
|
+
interface NavigationFunctionsConfig<TLocale extends string, TRouter extends RouterLike, TParams extends ParamsLike, TLink extends ElementType> {
|
|
42
|
+
Link: TLink;
|
|
43
|
+
routing: RoutingConfig<TLocale>;
|
|
44
|
+
useParams: () => TParams;
|
|
45
|
+
usePathname: () => string;
|
|
46
|
+
useRouter: () => TRouter;
|
|
47
|
+
}
|
|
48
|
+
declare function createNavigationFunctions<TLocale extends string, TRouter extends RouterLike, TParams extends ParamsLike, TLink extends ElementType>({ Link: LinkComponent, routing, useParams: useInjectedParams, usePathname: useInjectedPathname, useRouter: useInjectedRouter, }: NavigationFunctionsConfig<TLocale, TRouter, TParams, TLink>): {
|
|
49
|
+
Link: (rawProps: LocalizedLinkProps<TLocale, TLink>) => react.ReactElement<any, string | react.JSXElementConstructor<any>>;
|
|
50
|
+
getPathname: ({ href, locale, }: GetPathnameOptions<TLocale>) => string;
|
|
51
|
+
usePathname: () => string;
|
|
52
|
+
useRouter: () => LocalizedRouter<TLocale, TRouter>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export { type GetPathnameOptions, type LocalizedLinkProps, type LocalizedRouter, type NavigationFunctionsConfig, createNavigationFunctions };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ElementType, ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
import { RoutingConfig } from './index.js';
|
|
4
|
+
|
|
5
|
+
type NavigationHref = string | {
|
|
6
|
+
hash?: string;
|
|
7
|
+
pathname?: string;
|
|
8
|
+
search?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
11
|
+
type ParamsLike = Record<string, string | string[] | undefined>;
|
|
12
|
+
type RouterLike = {
|
|
13
|
+
prefetch?: (href: string, options?: any) => unknown;
|
|
14
|
+
push: (href: string, options?: any) => unknown;
|
|
15
|
+
replace: (href: string, options?: any) => unknown;
|
|
16
|
+
};
|
|
17
|
+
type RouterMethodReturn<TRouter extends RouterLike, TMethod extends "push" | "replace" | "prefetch"> = TMethod extends keyof TRouter ? Extract<TRouter[TMethod], (...args: any[]) => unknown> extends (...args: any[]) => infer TReturn ? TReturn : void : void;
|
|
18
|
+
type WithLocale<TLocale extends string, TOptions> = [TOptions] extends [
|
|
19
|
+
undefined
|
|
20
|
+
] ? {
|
|
21
|
+
locale?: TLocale;
|
|
22
|
+
} : TOptions & {
|
|
23
|
+
locale?: TLocale;
|
|
24
|
+
};
|
|
25
|
+
interface GetPathnameOptions<TLocale extends string> {
|
|
26
|
+
href: string;
|
|
27
|
+
locale?: TLocale;
|
|
28
|
+
}
|
|
29
|
+
type LocalizedLinkProps<TLocale extends string, TLink extends ElementType> = Omit<ComponentPropsWithoutRef<TLink>, "href"> & {
|
|
30
|
+
href: ComponentPropsWithoutRef<TLink> extends {
|
|
31
|
+
href: infer THref;
|
|
32
|
+
} ? THref : NavigationHref;
|
|
33
|
+
locale?: TLocale;
|
|
34
|
+
};
|
|
35
|
+
type LocalizedRouter<TLocale extends string, TRouter extends RouterLike> = Omit<TRouter, "prefetch" | "push" | "replace"> & {
|
|
36
|
+
push(href: string, options?: WithLocale<TLocale, Record<string, unknown>>): RouterMethodReturn<TRouter, "push">;
|
|
37
|
+
replace(href: string, options?: WithLocale<TLocale, Record<string, unknown>>): RouterMethodReturn<TRouter, "replace">;
|
|
38
|
+
} & ("prefetch" extends keyof TRouter ? {
|
|
39
|
+
prefetch(href: string, options?: WithLocale<TLocale, Record<string, unknown>>): RouterMethodReturn<TRouter, "prefetch">;
|
|
40
|
+
} : {});
|
|
41
|
+
interface NavigationFunctionsConfig<TLocale extends string, TRouter extends RouterLike, TParams extends ParamsLike, TLink extends ElementType> {
|
|
42
|
+
Link: TLink;
|
|
43
|
+
routing: RoutingConfig<TLocale>;
|
|
44
|
+
useParams: () => TParams;
|
|
45
|
+
usePathname: () => string;
|
|
46
|
+
useRouter: () => TRouter;
|
|
47
|
+
}
|
|
48
|
+
declare function createNavigationFunctions<TLocale extends string, TRouter extends RouterLike, TParams extends ParamsLike, TLink extends ElementType>({ Link: LinkComponent, routing, useParams: useInjectedParams, usePathname: useInjectedPathname, useRouter: useInjectedRouter, }: NavigationFunctionsConfig<TLocale, TRouter, TParams, TLink>): {
|
|
49
|
+
Link: (rawProps: LocalizedLinkProps<TLocale, TLink>) => react.ReactElement<any, string | react.JSXElementConstructor<any>>;
|
|
50
|
+
getPathname: ({ href, locale, }: GetPathnameOptions<TLocale>) => string;
|
|
51
|
+
usePathname: () => string;
|
|
52
|
+
useRouter: () => LocalizedRouter<TLocale, TRouter>;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export { type GetPathnameOptions, type LocalizedLinkProps, type LocalizedRouter, type NavigationFunctionsConfig, createNavigationFunctions };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildDomainAwareHref,
|
|
3
|
+
getDomainForLocale,
|
|
4
|
+
localizePathname,
|
|
5
|
+
parseRouteTemplate,
|
|
6
|
+
stripLocaleFromPathname
|
|
7
|
+
} from "./chunk-VW2LUTE7.js";
|
|
8
|
+
|
|
9
|
+
// src/navigation.tsx
|
|
10
|
+
import {
|
|
11
|
+
createElement
|
|
12
|
+
} from "react";
|
|
13
|
+
function createNavigationFunctions({
|
|
14
|
+
Link: LinkComponent,
|
|
15
|
+
routing,
|
|
16
|
+
useParams: useInjectedParams,
|
|
17
|
+
usePathname: useInjectedPathname,
|
|
18
|
+
useRouter: useInjectedRouter
|
|
19
|
+
}) {
|
|
20
|
+
const parsedTemplate = parseRouteTemplate(routing.routeTemplate);
|
|
21
|
+
function getPathname({
|
|
22
|
+
href,
|
|
23
|
+
locale = routing.defaultLocale
|
|
24
|
+
}) {
|
|
25
|
+
return localizePathname(href, locale, routing);
|
|
26
|
+
}
|
|
27
|
+
function Link(rawProps) {
|
|
28
|
+
const { href, locale, ...props } = rawProps;
|
|
29
|
+
const params = useInjectedParams();
|
|
30
|
+
const activeLocale = resolveActiveLocale(
|
|
31
|
+
params,
|
|
32
|
+
routing,
|
|
33
|
+
parsedTemplate.localeParamName
|
|
34
|
+
);
|
|
35
|
+
const localizedHref = localizeLinkHref(
|
|
36
|
+
href,
|
|
37
|
+
locale ?? activeLocale,
|
|
38
|
+
routing
|
|
39
|
+
);
|
|
40
|
+
return createElement(
|
|
41
|
+
LinkComponent,
|
|
42
|
+
{
|
|
43
|
+
...props,
|
|
44
|
+
href: localizedHref
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function usePathname() {
|
|
49
|
+
return stripLocaleFromPathname(useInjectedPathname(), routing);
|
|
50
|
+
}
|
|
51
|
+
function useRouter() {
|
|
52
|
+
const router = useInjectedRouter();
|
|
53
|
+
const params = useInjectedParams();
|
|
54
|
+
const activeLocale = resolveActiveLocale(
|
|
55
|
+
params,
|
|
56
|
+
routing,
|
|
57
|
+
parsedTemplate.localeParamName
|
|
58
|
+
);
|
|
59
|
+
const localizedRouter = {
|
|
60
|
+
...router,
|
|
61
|
+
push(href, options) {
|
|
62
|
+
return navigate(
|
|
63
|
+
router.push.bind(router),
|
|
64
|
+
window.location.assign.bind(window.location),
|
|
65
|
+
routing,
|
|
66
|
+
href,
|
|
67
|
+
activeLocale,
|
|
68
|
+
options
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
replace(href, options) {
|
|
72
|
+
return navigate(
|
|
73
|
+
router.replace.bind(router),
|
|
74
|
+
window.location.replace.bind(window.location),
|
|
75
|
+
routing,
|
|
76
|
+
href,
|
|
77
|
+
activeLocale,
|
|
78
|
+
options
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
if (typeof router.prefetch === "function") {
|
|
83
|
+
localizedRouter.prefetch = (href, options) => {
|
|
84
|
+
const target = buildNavigationTarget(
|
|
85
|
+
routing,
|
|
86
|
+
href,
|
|
87
|
+
activeLocale,
|
|
88
|
+
options
|
|
89
|
+
);
|
|
90
|
+
if (target.external) {
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
return router.prefetch?.(target.href, target.forwardedOptions);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return localizedRouter;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
Link,
|
|
100
|
+
getPathname,
|
|
101
|
+
usePathname,
|
|
102
|
+
useRouter
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function buildNavigationTarget(routing, href, activeLocale, options) {
|
|
106
|
+
const nextLocale = options?.locale ?? activeLocale;
|
|
107
|
+
const forwardedOptions = {
|
|
108
|
+
...options ?? {}
|
|
109
|
+
};
|
|
110
|
+
delete forwardedOptions.locale;
|
|
111
|
+
const target = buildDomainAwareHref(routing, href, nextLocale, {
|
|
112
|
+
currentHost: window.location.host,
|
|
113
|
+
protocol: window.location.protocol.replace(":", "")
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
external: target.external,
|
|
117
|
+
forwardedOptions,
|
|
118
|
+
href: target.href
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function navigate(navigateWithRouter, navigateWithDocument, routing, href, activeLocale, options) {
|
|
122
|
+
const target = buildNavigationTarget(routing, href, activeLocale, options);
|
|
123
|
+
if (target.external) {
|
|
124
|
+
navigateWithDocument(target.href);
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
return navigateWithRouter(target.href, target.forwardedOptions);
|
|
128
|
+
}
|
|
129
|
+
function localizeLinkHref(href, locale, routing) {
|
|
130
|
+
if (typeof href === "string") {
|
|
131
|
+
return buildDomainAwareHref(routing, href, locale, {
|
|
132
|
+
forceAbsolute: Boolean(getDomainForLocale(routing, locale))
|
|
133
|
+
}).href;
|
|
134
|
+
}
|
|
135
|
+
const pathname = typeof href.pathname === "string" ? href.pathname : "/";
|
|
136
|
+
const search = typeof href.search === "string" ? href.search : "";
|
|
137
|
+
const hash = typeof href.hash === "string" ? href.hash : "";
|
|
138
|
+
const localizedHref = buildDomainAwareHref(
|
|
139
|
+
routing,
|
|
140
|
+
`${pathname}${search}${hash}`,
|
|
141
|
+
locale,
|
|
142
|
+
{
|
|
143
|
+
forceAbsolute: Boolean(getDomainForLocale(routing, locale))
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
if (localizedHref.external) {
|
|
147
|
+
return localizedHref.href;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
...href,
|
|
151
|
+
pathname: localizedHref.href
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function resolveActiveLocale(params, routing, localeParamName) {
|
|
155
|
+
const localeParam = params[localeParamName];
|
|
156
|
+
if (typeof localeParam === "string" && routing.locales.includes(localeParam)) {
|
|
157
|
+
return localeParam;
|
|
158
|
+
}
|
|
159
|
+
return routing.defaultLocale;
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
createNavigationFunctions
|
|
163
|
+
};
|