tanstack-start-intl 0.0.0-alpha.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 (40) hide show
  1. package/README.md +122 -0
  2. package/config.d.ts +15 -0
  3. package/dist/esm/development/config.js +5 -0
  4. package/dist/esm/development/index.js +113 -0
  5. package/dist/esm/development/routing.js +25 -0
  6. package/dist/esm/development/server.js +180 -0
  7. package/dist/esm/development/utils-UUs5JKte.js +136 -0
  8. package/dist/esm/production/config.js +1 -0
  9. package/dist/esm/production/index.js +1 -0
  10. package/dist/esm/production/routing.js +1 -0
  11. package/dist/esm/production/server.js +1 -0
  12. package/dist/esm/production/utils-Y3ZAPIbE.js +1 -0
  13. package/dist/types/src/NextIntlClientProvider.d.ts +9 -0
  14. package/dist/types/src/config.d.ts +1 -0
  15. package/dist/types/src/index.d.ts +4 -0
  16. package/dist/types/src/navigation/createNavigation.d.ts +20 -0
  17. package/dist/types/src/routing/config.d.ts +50 -0
  18. package/dist/types/src/routing/defineRouting.d.ts +3 -0
  19. package/dist/types/src/routing/index.d.ts +3 -0
  20. package/dist/types/src/routing/types.d.ts +22 -0
  21. package/dist/types/src/routing.d.ts +3 -0
  22. package/dist/types/src/server/RequestLocale.d.ts +7 -0
  23. package/dist/types/src/server/RequestLocaleCache.d.ts +3 -0
  24. package/dist/types/src/server/createRequestConfig.d.ts +3 -0
  25. package/dist/types/src/server/getConfig.d.ts +13 -0
  26. package/dist/types/src/server/getConfigNow.d.ts +4 -0
  27. package/dist/types/src/server/getDefaultNow.d.ts +3 -0
  28. package/dist/types/src/server/getFormatter.d.ts +4 -0
  29. package/dist/types/src/server/getLocale.d.ts +4 -0
  30. package/dist/types/src/server/getMessages.d.ts +6 -0
  31. package/dist/types/src/server/getNow.d.ts +4 -0
  32. package/dist/types/src/server/getRequestConfig.d.ts +18 -0
  33. package/dist/types/src/server/getServerFormatter.d.ts +25 -0
  34. package/dist/types/src/server/getServerTranslator.d.ts +4 -0
  35. package/dist/types/src/server/getTimeZone.d.ts +4 -0
  36. package/dist/types/src/server/getTranslations.d.ts +8 -0
  37. package/dist/types/src/server/validateLocale.d.ts +1 -0
  38. package/dist/types/src/server.d.ts +9 -0
  39. package/dist/types/src/shared/utils.d.ts +21 -0
  40. package/package.json +67 -0
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # tanstack-start-intl
2
+
3
+ Internationalization (i18n) for [TanStack Start](https://tanstack.com/start), built on [use-intl](https://github.com/amannn/next-intl/tree/main/packages/use-intl) and aligned with [next-intl](https://next-intl.dev) semantics.
4
+
5
+ ## Setup
6
+
7
+ ### 1. Install
8
+
9
+ ```bash
10
+ pnpm add tanstack-start-intl use-intl
11
+ # peer: @tanstack/react-router, react
12
+ ```
13
+
14
+ ### 2. Config alias
15
+
16
+ In your Vite or TanStack Start config, add an alias so the package can load your request config:
17
+
18
+ ```ts
19
+ // e.g. app.config.ts or vite.config.ts
20
+ export default defineConfig({
21
+ resolve: {
22
+ alias: {
23
+ 'tanstack-start-intl/config': resolve(__dirname, 'src/i18n/request.ts')
24
+ }
25
+ }
26
+ });
27
+ ```
28
+
29
+ ### 3. Request config
30
+
31
+ Create `src/i18n/request.ts` (or your chosen path):
32
+
33
+ ```ts
34
+ import { getRequestConfig } from 'tanstack-start-intl/server';
35
+ import { hasLocale } from 'tanstack-start-intl';
36
+ import { routing } from './routing';
37
+
38
+ export default getRequestConfig(async ({ requestLocale }) => {
39
+ const requested = await requestLocale;
40
+ const locale = hasLocale(routing.locales, requested)
41
+ ? requested
42
+ : routing.defaultLocale;
43
+
44
+ return {
45
+ locale,
46
+ messages: (await import(`../../messages/${locale}.json`)).default
47
+ };
48
+ });
49
+ ```
50
+
51
+ ### 4. Routing config
52
+
53
+ Create `src/i18n/routing.ts`:
54
+
55
+ ```ts
56
+ import { defineRouting } from 'tanstack-start-intl/routing';
57
+
58
+ export const routing = defineRouting({
59
+ locales: ['en', 'fr'],
60
+ defaultLocale: 'en',
61
+ pathnames: {
62
+ '/': '/',
63
+ '/about': { en: '/about', fr: '/a-propos' }
64
+ }
65
+ });
66
+ ```
67
+
68
+ ### 5. Locale layout
69
+
70
+ In your locale route (e.g. `src/routes/$locale.tsx`), set the request locale and wrap with the provider:
71
+
72
+ ```tsx
73
+ import { createFileRoute } from '@tanstack/react-router';
74
+ import { setRequestLocale, getRequestConfig } from 'tanstack-start-intl/server';
75
+ import { NextIntlClientProvider, hasLocale } from 'tanstack-start-intl';
76
+ import { routing } from '@/i18n/routing';
77
+
78
+ export const Route = createFileRoute('/$locale')({
79
+ component: LocaleLayout
80
+ });
81
+
82
+ async function LocaleLayout() {
83
+ const { locale } = Route.useParams();
84
+ if (!hasLocale(routing.locales, locale)) {
85
+ throw new Error('Invalid locale');
86
+ }
87
+ setRequestLocale(locale);
88
+ const config = await getRequestConfig({
89
+ requestLocale: Promise.resolve(locale)
90
+ });
91
+
92
+ return (
93
+ <NextIntlClientProvider locale={config.locale} messages={config.messages}>
94
+ <Outlet />
95
+ </NextIntlClientProvider>
96
+ );
97
+ }
98
+ ```
99
+
100
+ ### 6. Navigation (optional)
101
+
102
+ If you use pathnames and locale prefix, create navigation helpers:
103
+
104
+ ```tsx
105
+ // src/i18n/navigation.ts
106
+ import { createNavigation } from 'tanstack-start-intl';
107
+ import { routing } from './routing';
108
+
109
+ export const { Link, redirect, usePathname, useRouter, getPathname } =
110
+ createNavigation(routing);
111
+ ```
112
+
113
+ ## API
114
+
115
+ - **From `tanstack-start-intl`**: `NextIntlClientProvider`, `createNavigation`, `hasLocale`, `useTranslations`, `useFormatter`, `useLocale`, `useMessages`, `useNow`, `useTimeZone`
116
+ - **From `tanstack-start-intl/server`**: `getRequestConfig`, `setRequestLocale`, `getTranslations`, `getLocale`, `getMessages`, `getFormatter`, `getTimeZone`, `getNow`
117
+ - **From `tanstack-start-intl/routing`**: `defineRouting`, routing types
118
+
119
+ ## Notes
120
+
121
+ - Locale is **only** taken from `setRequestLocale(locale)` in your locale layout. There is no header-based detection; call `setRequestLocale` before any server API use.
122
+ - Configure the alias `tanstack-start-intl/config` to your request file so server APIs can load messages and locale.
package/config.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * This module is resolved at build time via your Vite/Start alias to your
3
+ * i18n request file (e.g. src/i18n/request.ts). The default export should
4
+ * be the result of getRequestConfig().
5
+ */
6
+ declare module 'tanstack-start-intl/config' {
7
+ import type {
8
+ GetRequestConfigParams,
9
+ RequestConfig
10
+ } from 'tanstack-start-intl/server';
11
+ const getRequestConfig: (
12
+ params: GetRequestConfigParams
13
+ ) => RequestConfig | Promise<RequestConfig>;
14
+ export default getRequestConfig;
15
+ }
@@ -0,0 +1,5 @@
1
+ function getConfig() {
2
+ throw new Error("Could not find tanstack-start-intl config. Add a Vite/Start alias: 'tanstack-start-intl/config' -> './src/i18n/request' (or your request file).");
3
+ }
4
+
5
+ export { getConfig as default };
@@ -0,0 +1,113 @@
1
+ import { IntlProvider } from 'use-intl/react';
2
+ export { useFormatter, useLocale, useMessages, useNow, useTimeZone, useTranslations } from 'use-intl/react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { useRouter, useLocation, Link, redirect } from '@tanstack/react-router';
5
+ import { useMemo } from 'react';
6
+ import { useLocale } from 'use-intl';
7
+ import { u as unprefixPathname, g as getRoute, a as applyPathnamePrefix, b as getLocalePrefix } from './utils-UUs5JKte.js';
8
+ export { hasLocale } from 'use-intl/core';
9
+
10
+ function NextIntlClientProvider({
11
+ locale,
12
+ ...rest
13
+ }) {
14
+ if (!locale) {
15
+ throw new Error('Missing `locale` prop on NextIntlClientProvider. Pass the locale from your layout (e.g. from getRequestConfig or route params).' );
16
+ }
17
+ return /*#__PURE__*/jsx(IntlProvider, {
18
+ locale: locale,
19
+ ...rest
20
+ });
21
+ }
22
+
23
+ /** Cookie config for locale persistence (framework-agnostic). */
24
+
25
+ function receiveRoutingConfig(input) {
26
+ return {
27
+ ...input,
28
+ localePrefix: receiveLocalePrefixConfig(input.localePrefix),
29
+ localeCookie: receiveLocaleCookie(input.localeCookie),
30
+ localeDetection: input.localeDetection ?? true,
31
+ alternateLinks: input.alternateLinks ?? true
32
+ };
33
+ }
34
+ function receiveLocaleCookie(localeCookie) {
35
+ return localeCookie ?? true ? {
36
+ name: 'TANSTACK_LOCALE',
37
+ sameSite: 'lax',
38
+ ...(typeof localeCookie === 'object' && localeCookie)
39
+ } : false;
40
+ }
41
+ function receiveLocalePrefixConfig(localePrefix) {
42
+ return typeof localePrefix === 'object' ? localePrefix : {
43
+ mode: localePrefix || 'always'
44
+ };
45
+ }
46
+
47
+ function createNavigation(routing) {
48
+ const config = receiveRoutingConfig(routing || {});
49
+ function usePathname() {
50
+ const location = useLocation();
51
+ const pathname = location.pathname || '/';
52
+ const locale = useLocale();
53
+ const prefix = getLocalePrefix(locale, config.localePrefix);
54
+ const unprefixed = prefix ? unprefixPathname(pathname, prefix) : pathname;
55
+ if (!config.pathnames) return unprefixed;
56
+ return getRoute(locale, unprefixed, config.pathnames);
57
+ }
58
+ function getPathname(pathnameOrPathnames, locale, opts) {
59
+ const pathname = typeof pathnameOrPathnames === 'string' ? pathnameOrPathnames : pathnameOrPathnames[locale] || pathnameOrPathnames[config.defaultLocale] || '/';
60
+ return applyPathnamePrefix(pathname, locale, config, opts?.force);
61
+ }
62
+ function Link$1({
63
+ href,
64
+ locale: localeProp,
65
+ ...rest
66
+ }) {
67
+ const currentLocale = useLocale();
68
+ const locale = localeProp ?? currentLocale;
69
+ const to = useMemo(() => {
70
+ const pathname = typeof href === 'object' ? href.pathname : href;
71
+ const params = typeof href === 'object' ? href.params : undefined;
72
+ const pathnames = config.pathnames;
73
+ const pathnameConfig = pathnames ? pathnames[pathname] : pathname;
74
+ let resolvedPathname;
75
+ if (pathnameConfig === undefined || pathnameConfig === null) {
76
+ resolvedPathname = pathname;
77
+ } else if (typeof pathnameConfig === 'string') {
78
+ resolvedPathname = pathnameConfig;
79
+ } else {
80
+ const localizedPathnames = pathnameConfig;
81
+ resolvedPathname = localizedPathnames[locale] ?? localizedPathnames[config.defaultLocale] ?? pathname;
82
+ }
83
+ const localized = getPathname(resolvedPathname, locale);
84
+ if (params && Object.keys(params).length > 0) {
85
+ return {
86
+ pathname: localized,
87
+ search: params
88
+ };
89
+ }
90
+ return localized;
91
+ }, [href, locale]);
92
+ return /*#__PURE__*/jsx(Link, {
93
+ to: to,
94
+ ...rest
95
+ });
96
+ }
97
+ function redirect$1(pathname, locale) {
98
+ const loc = locale ?? config.defaultLocale;
99
+ const target = getPathname(pathname, loc);
100
+ throw redirect({
101
+ to: target
102
+ });
103
+ }
104
+ return {
105
+ Link: Link$1,
106
+ redirect: redirect$1,
107
+ usePathname,
108
+ useRouter,
109
+ getPathname
110
+ };
111
+ }
112
+
113
+ export { NextIntlClientProvider, createNavigation };
@@ -0,0 +1,25 @@
1
+ function defineRouting(config) {
2
+ if (config.domains) {
3
+ validateUniqueLocalesPerDomain(config.domains);
4
+ }
5
+ return config;
6
+ }
7
+ function validateUniqueLocalesPerDomain(domains) {
8
+ const domainsByLocale = new Map();
9
+ for (const {
10
+ domain,
11
+ locales
12
+ } of domains) {
13
+ for (const locale of locales) {
14
+ const localeDomains = domainsByLocale.get(locale) || new Set();
15
+ localeDomains.add(domain);
16
+ domainsByLocale.set(locale, localeDomains);
17
+ }
18
+ }
19
+ const duplicateLocaleMessages = Array.from(domainsByLocale.entries()).filter(([, localeDomains]) => localeDomains.size > 1).map(([locale, localeDomains]) => `- "${locale}" is used by: ${Array.from(localeDomains).join(', ')}`);
20
+ if (duplicateLocaleMessages.length > 0) {
21
+ console.warn('Locales are expected to be unique per domain, but found overlap:\n' + duplicateLocaleMessages.join('\n'));
22
+ }
23
+ }
24
+
25
+ export { defineRouting };
@@ -0,0 +1,180 @@
1
+ import { cache } from 'react';
2
+ import { initializeConfig, _createIntlFormatters, _createCache, createTranslator, createFormatter } from 'use-intl/core';
3
+ import { i as isPromise } from './utils-UUs5JKte.js';
4
+ import getRuntimeConfig from 'tanstack-start-intl/config';
5
+
6
+ /**
7
+ * Use in your i18n request file (e.g. `src/i18n/request.ts`) to create
8
+ * the configuration for the current request. Configure your Vite/Start
9
+ * alias: `'tanstack-start-intl/config'` -> `'./src/i18n/request'`.
10
+ */
11
+ function getRequestConfig(createRequestConfig) {
12
+ return createRequestConfig;
13
+ }
14
+
15
+ function getCacheImpl() {
16
+ const value = {
17
+ locale: undefined
18
+ };
19
+ return value;
20
+ }
21
+ const getCache$1 = cache(getCacheImpl);
22
+ function getCachedRequestLocale() {
23
+ return getCache$1().locale;
24
+ }
25
+ function setCachedRequestLocale(locale) {
26
+ getCache$1().locale = locale;
27
+ }
28
+
29
+ /**
30
+ * Returns the request locale. In TanStack Start, locale must be set via
31
+ * `setRequestLocale(locale)` in your locale layout (e.g. in `$locale.tsx`)
32
+ * before any server APIs are used.
33
+ */
34
+ async function getRequestLocale() {
35
+ return getCachedRequestLocale();
36
+ }
37
+
38
+ function validateLocale(locale) {
39
+ try {
40
+ const constructed = new Intl.Locale(locale);
41
+ if (!constructed.language) {
42
+ throw new Error('Language is required');
43
+ }
44
+ } catch {
45
+ console.error(`An invalid locale was provided: "${locale}". Please use a valid Unicode locale identifier (e.g. "en-US").`);
46
+ }
47
+ }
48
+
49
+ function getDefaultTimeZoneImpl() {
50
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
51
+ }
52
+ const getDefaultTimeZone = cache(getDefaultTimeZoneImpl);
53
+ async function receiveRuntimeConfigImpl(getConfig, localeOverride) {
54
+ if (typeof getConfig !== 'function') {
55
+ throw new Error(`Invalid i18n request configuration.
56
+
57
+ Please:
58
+ 1. Add a Vite/Start alias: 'tanstack-start-intl/config' -> './src/i18n/request' (or your request file path).
59
+ 2. Ensure your i18n request file has a default export that calls getRequestConfig().
60
+
61
+ See: https://github.com/amannn/next-intl#tanstack-start`);
62
+ }
63
+ const params = {
64
+ locale: localeOverride,
65
+ get requestLocale() {
66
+ return localeOverride ? Promise.resolve(localeOverride) : getRequestLocale();
67
+ }
68
+ };
69
+ let result = getConfig(params);
70
+ if (isPromise(result)) {
71
+ result = await result;
72
+ }
73
+ if (!result.locale) {
74
+ throw new Error('No locale was returned from `getRequestConfig`. Ensure your i18n request file returns { locale, messages, ... }.');
75
+ }
76
+ {
77
+ validateLocale(result.locale);
78
+ }
79
+ return result;
80
+ }
81
+ const receiveRuntimeConfig = cache(receiveRuntimeConfigImpl);
82
+ const getFormatters = cache(_createIntlFormatters);
83
+ const getCache = cache(_createCache);
84
+ async function getConfigImpl(localeOverride) {
85
+ const runtimeConfig = await receiveRuntimeConfig(getRuntimeConfig, localeOverride);
86
+ return {
87
+ ...initializeConfig(runtimeConfig),
88
+ _formatters: getFormatters(getCache()),
89
+ timeZone: runtimeConfig.timeZone || getDefaultTimeZone()
90
+ };
91
+ }
92
+ const getConfig = cache(getConfigImpl);
93
+
94
+ function getServerTranslatorImpl(config, namespace) {
95
+ return createTranslator({
96
+ ...config,
97
+ namespace
98
+ });
99
+ }
100
+ var getServerTranslator = cache(getServerTranslatorImpl);
101
+
102
+ async function getTranslations(namespaceOrOpts) {
103
+ let namespace;
104
+ let locale;
105
+ if (typeof namespaceOrOpts === 'string') {
106
+ namespace = namespaceOrOpts;
107
+ } else if (namespaceOrOpts) {
108
+ locale = namespaceOrOpts.locale;
109
+ namespace = namespaceOrOpts.namespace;
110
+ }
111
+ const config = await getConfig(locale);
112
+ return getServerTranslator(config, namespace);
113
+ }
114
+ var getTranslations_default = cache(getTranslations);
115
+
116
+ async function getLocaleCachedImpl() {
117
+ const config = await getConfig();
118
+ return config.locale;
119
+ }
120
+ const getLocaleCached = cache(getLocaleCachedImpl);
121
+
122
+ function getMessagesFromConfig(config) {
123
+ if (!config.messages) {
124
+ throw new Error('No messages found. Have you configured them in getRequestConfig?');
125
+ }
126
+ return config.messages;
127
+ }
128
+ async function getMessagesCachedImpl(locale) {
129
+ const config = await getConfig(locale);
130
+ return getMessagesFromConfig(config);
131
+ }
132
+ const getMessagesCached = cache(getMessagesCachedImpl);
133
+ async function getMessages(opts) {
134
+ return getMessagesCached(opts?.locale);
135
+ }
136
+
137
+ function defaultNow() {
138
+ return new Date();
139
+ }
140
+ const getDefaultNow = cache(defaultNow);
141
+
142
+ function getFormatterCachedImpl$1(config) {
143
+ return createFormatter({
144
+ ...config,
145
+ get now() {
146
+ return config.now ?? getDefaultNow();
147
+ }
148
+ });
149
+ }
150
+ const getFormatterCached$1 = cache(getFormatterCachedImpl$1);
151
+
152
+ async function getFormatterCachedImpl(locale) {
153
+ const config = await getConfig(locale);
154
+ return getFormatterCached$1(config);
155
+ }
156
+ const getFormatterCached = cache(getFormatterCachedImpl);
157
+ async function getFormatter(opts) {
158
+ return getFormatterCached(opts?.locale);
159
+ }
160
+
161
+ async function getTimeZoneCachedImpl(locale) {
162
+ const config = await getConfig(locale);
163
+ return config.timeZone;
164
+ }
165
+ const getTimeZoneCached = cache(getTimeZoneCachedImpl);
166
+ async function getTimeZone(opts) {
167
+ return getTimeZoneCached(opts?.locale);
168
+ }
169
+
170
+ async function getConfigNowImpl(locale) {
171
+ const config = await getConfig(locale);
172
+ return config.now;
173
+ }
174
+ const getConfigNow = cache(getConfigNowImpl);
175
+
176
+ async function getNow(opts) {
177
+ return (await getConfigNow(opts?.locale)) ?? getDefaultNow();
178
+ }
179
+
180
+ export { getFormatter, getLocaleCached as getLocale, getMessages, getMessagesFromConfig, getNow, getRequestConfig, getTimeZone, getTranslations_default as getTranslations, setCachedRequestLocale as setRequestLocale };
@@ -0,0 +1,136 @@
1
+ function isRelativeHref(href) {
2
+ const pathname = typeof href === 'object' ? href.pathname : href;
3
+ return typeof pathname === 'string' && !pathname.startsWith('/');
4
+ }
5
+ function isLocalHref(href) {
6
+ if (typeof href === 'object') {
7
+ return true;
8
+ }
9
+ const hasProtocol = /^[a-z]+:/i.test(href);
10
+ return !hasProtocol;
11
+ }
12
+ function isLocalizableHref(href) {
13
+ return isLocalHref(href) && !isRelativeHref(href);
14
+ }
15
+ function unprefixPathname(pathname, prefix) {
16
+ return pathname.replace(new RegExp(`^${prefix}`), '') || '/';
17
+ }
18
+ function prefixPathname(prefix, pathname) {
19
+ let localizedHref = prefix;
20
+ if (/^\/(\?.*)?$/.test(pathname)) {
21
+ pathname = pathname.slice(1);
22
+ }
23
+ localizedHref += pathname;
24
+ return localizedHref;
25
+ }
26
+ function getLocalizedTemplate(pathnameConfig, locale, internalTemplate) {
27
+ return typeof pathnameConfig === 'string' ? pathnameConfig : pathnameConfig[locale] || internalTemplate;
28
+ }
29
+ function normalizeTrailingSlash(pathname, trailingSlash = false) {
30
+ const [path, ...hashParts] = pathname.split('#');
31
+ const hash = hashParts.join('#');
32
+ let normalizedPath = path;
33
+ if (normalizedPath !== '/') {
34
+ const pathnameEndsWithSlash = normalizedPath.endsWith('/');
35
+ if (trailingSlash && !pathnameEndsWithSlash) {
36
+ normalizedPath += '/';
37
+ } else if (!trailingSlash && pathnameEndsWithSlash) {
38
+ normalizedPath = normalizedPath.slice(0, -1);
39
+ }
40
+ }
41
+ if (hash) {
42
+ normalizedPath += '#' + hash;
43
+ }
44
+ return normalizedPath;
45
+ }
46
+ function matchesPathname(template, pathname) {
47
+ const normalizedTemplate = normalizeTrailingSlash(template);
48
+ const normalizedPathname = normalizeTrailingSlash(pathname);
49
+ const regex = templateToRegex(normalizedTemplate);
50
+ return regex.test(normalizedPathname);
51
+ }
52
+ function getLocalePrefix(locale, localePrefix) {
53
+ return localePrefix.mode !== 'never' && localePrefix.prefixes?.[locale] || getLocaleAsPrefix(locale);
54
+ }
55
+ function getLocaleAsPrefix(locale) {
56
+ return '/' + locale;
57
+ }
58
+ function templateToRegex(template) {
59
+ const regexPattern = template.replace(/\/\[\[(\.\.\.[^\]]+)\]\]/g, '(?:/(.*))?').replace(/\[\[(\.\.\.[^\]]+)\]\]/g, '(?:/(.*))?').replace(/\[(\.\.\.[^\]]+)\]/g, '(.+)').replace(/\[([^\]]+)\]/g, '([^/]+)');
60
+ return new RegExp(`^${regexPattern}$`);
61
+ }
62
+ function isOptionalCatchAllSegment(pathname) {
63
+ return pathname.includes('[[...');
64
+ }
65
+ function isCatchAllSegment(pathname) {
66
+ return pathname.includes('[...');
67
+ }
68
+ function isDynamicSegment(pathname) {
69
+ return pathname.includes('[');
70
+ }
71
+ function comparePathnamePairs(a, b) {
72
+ const pathA = a.split('/');
73
+ const pathB = b.split('/');
74
+ const maxLength = Math.max(pathA.length, pathB.length);
75
+ for (let i = 0; i < maxLength; i++) {
76
+ const segmentA = pathA[i];
77
+ const segmentB = pathB[i];
78
+ if (!segmentA && segmentB) return -1;
79
+ if (segmentA && !segmentB) return 1;
80
+ if (!segmentA && !segmentB) continue;
81
+ if (!isDynamicSegment(segmentA) && isDynamicSegment(segmentB)) return -1;
82
+ if (isDynamicSegment(segmentA) && !isDynamicSegment(segmentB)) return 1;
83
+ if (!isCatchAllSegment(segmentA) && isCatchAllSegment(segmentB)) return -1;
84
+ if (isCatchAllSegment(segmentA) && !isCatchAllSegment(segmentB)) return 1;
85
+ if (!isOptionalCatchAllSegment(segmentA) && isOptionalCatchAllSegment(segmentB)) {
86
+ return -1;
87
+ }
88
+ if (isOptionalCatchAllSegment(segmentA) && !isOptionalCatchAllSegment(segmentB)) {
89
+ return 1;
90
+ }
91
+ if (segmentA === segmentB) continue;
92
+ }
93
+ return 0;
94
+ }
95
+ function getSortedPathnames(pathnames) {
96
+ return pathnames.sort(comparePathnamePairs);
97
+ }
98
+ function isPromise(value) {
99
+ return typeof value.then === 'function';
100
+ }
101
+ function getRoute(locale, pathname, pathnames) {
102
+ const sortedPathnames = getSortedPathnames(Object.keys(pathnames));
103
+ const decoded = decodeURI(pathname);
104
+ for (const internalPathname of sortedPathnames) {
105
+ const localizedPathnamesOrPathname = pathnames[internalPathname];
106
+ if (typeof localizedPathnamesOrPathname === 'string') {
107
+ const localizedPathname = localizedPathnamesOrPathname;
108
+ if (matchesPathname(localizedPathname, decoded)) {
109
+ return internalPathname;
110
+ }
111
+ } else {
112
+ if (matchesPathname(getLocalizedTemplate(localizedPathnamesOrPathname, locale, internalPathname), decoded)) {
113
+ return internalPathname;
114
+ }
115
+ }
116
+ }
117
+ return pathname;
118
+ }
119
+ function applyPathnamePrefix(pathname, locale, routing, force) {
120
+ const {
121
+ mode
122
+ } = routing.localePrefix;
123
+ let shouldPrefix;
124
+ if (force !== undefined) {
125
+ shouldPrefix = force;
126
+ } else if (isLocalizableHref(pathname)) {
127
+ if (mode === 'always') {
128
+ shouldPrefix = true;
129
+ } else if (mode === 'as-needed') {
130
+ shouldPrefix = routing.domains ? !routing.domains.some(cur => cur.defaultLocale === locale) : locale !== routing.defaultLocale;
131
+ }
132
+ }
133
+ return shouldPrefix ? prefixPathname(getLocalePrefix(locale, routing.localePrefix), pathname) : pathname;
134
+ }
135
+
136
+ export { applyPathnamePrefix as a, getLocalePrefix as b, getRoute as g, isPromise as i, unprefixPathname as u };
@@ -0,0 +1 @@
1
+ function t(){throw new Error("Could not find tanstack-start-intl config. Add a Vite/Start alias: 'tanstack-start-intl/config' -> './src/i18n/request' (or your request file).")}export{t as default};
@@ -0,0 +1 @@
1
+ import{IntlProvider as e}from"use-intl/react";export{useFormatter,useLocale,useMessages,useNow,useTimeZone,useTranslations}from"use-intl/react";import{jsx as t}from"react/jsx-runtime";import{useRouter as o,useLocation as a,Link as r,redirect as n}from"@tanstack/react-router";import{useMemo as s}from"react";import{useLocale as c}from"use-intl";import{u as l,g as i,a as u,b as f}from"./utils-Y3ZAPIbE.js";export{hasLocale}from"use-intl/core";function m({locale:o,...a}){if(!o)throw new Error(void 0);return t(e,{locale:o,...a})}function p(e){const m=(p=e||{},{...p,localePrefix:(L=p.localePrefix,"object"==typeof L?L:{mode:L||"always"}),localeCookie:(h=p.localeCookie,!!(h??1)&&{name:"TANSTACK_LOCALE",sameSite:"lax",..."object"==typeof h&&h}),localeDetection:p.localeDetection??!0,alternateLinks:p.alternateLinks??!0});var p,h,L;function x(e,t,o){const a="string"==typeof e?e:e[t]||e[m.defaultLocale]||"/";return u(a,t,m,o?.force)}return{Link:function({href:e,locale:o,...a}){const n=c(),l=o??n,i=s((()=>{const t="object"==typeof e?e.pathname:e,o="object"==typeof e?e.params:void 0,a=m.pathnames,r=a?a[t]:t;let n;if(null==r)n=t;else if("string"==typeof r)n=r;else{const e=r;n=e[l]??e[m.defaultLocale]??t}const s=x(n,l);return o&&Object.keys(o).length>0?{pathname:s,search:o}:s}),[e,l]);return t(r,{to:i,...a})},redirect:function(e,t){const o=x(e,t??m.defaultLocale);throw n({to:o})},usePathname:function(){const e=a().pathname||"/",t=c(),o=f(t,m.localePrefix),r=o?l(e,o):e;return m.pathnames?i(t,r,m.pathnames):r},useRouter:o,getPathname:x}}export{m as NextIntlClientProvider,p as createNavigation};
@@ -0,0 +1 @@
1
+ function n(n){return n}export{n as defineRouting};
@@ -0,0 +1 @@
1
+ import{cache as n}from"react";import{initializeConfig as t,_createIntlFormatters as e,_createCache as o,createTranslator as r,createFormatter as a}from"use-intl/core";import{i as c}from"./utils-Y3ZAPIbE.js";import s from"tanstack-start-intl/config";function i(n){return n}const u=n((function(){return{locale:void 0}}));function l(n){u().locale=n}async function f(){return u().locale}const m=n((function(){return Intl.DateTimeFormat().resolvedOptions().timeZone}));const w=n((async function(n,t){let e=n({locale:t,get requestLocale(){return t?Promise.resolve(t):f()}});if(c(e)&&(e=await e),!e.locale)throw new Error("No locale was returned from `getRequestConfig`. Ensure your i18n request file returns { locale, messages, ... }.");return e})),y=n(e),g=n(o);const p=n((async function(n){const e=await w(s,n);return{...t(e),_formatters:y(g()),timeZone:e.timeZone||m()}}));var v=n((function(n,t){return r({...n,namespace:t})}));var d=n((async function(n){let t,e;"string"==typeof n?t=n:n&&(e=n.locale,t=n.namespace);const o=await p(e);return v(o,t)}));const q=n((async function(){return(await p()).locale}));function Z(n){if(!n.messages)throw new Error("No messages found. Have you configured them in getRequestConfig?");return n.messages}const h=n((async function(n){return Z(await p(n))}));async function E(n){return h(n?.locale)}const L=n((function(){return new Date}));const R=n((function(n){return a({...n,get now(){return n.now??L()}})}));const C=n((async function(n){const t=await p(n);return R(t)}));async function D(n){return C(n?.locale)}const N=n((async function(n){return(await p(n)).timeZone}));async function T(n){return N(n?.locale)}const j=n((async function(n){return(await p(n)).now}));async function k(n){return await j(n?.locale)??L()}export{D as getFormatter,q as getLocale,E as getMessages,Z as getMessagesFromConfig,k as getNow,i as getRequestConfig,T as getTimeZone,d as getTranslations,l as setRequestLocale};
@@ -0,0 +1 @@
1
+ function t(t,n){return t.replace(new RegExp(`^${n}`),"")||"/"}function n(t,n,e){return"string"==typeof t?t:t[n]||e}function e(t,n=!1){const[e,...r]=t.split("#"),o=r.join("#");let i=e;if("/"!==i){const t=i.endsWith("/");n&&!t?i+="/":!n&&t&&(i=i.slice(0,-1))}return o&&(i+="#"+o),i}function r(t,n){const r=e(t),o=e(n),i=function(t){const n=t.replace(/\/\[\[(\.\.\.[^\]]+)\]\]/g,"(?:/(.*))?").replace(/\[\[(\.\.\.[^\]]+)\]\]/g,"(?:/(.*))?").replace(/\[(\.\.\.[^\]]+)\]/g,"(.+)").replace(/\[([^\]]+)\]/g,"([^/]+)");return new RegExp(`^${n}$`)}(r);return i.test(o)}function o(t,n){return"never"!==n.mode&&n.prefixes?.[t]||function(t){return"/"+t}(t)}function i(t){return t.includes("[[...")}function u(t){return t.includes("[...")}function c(t){return t.includes("[")}function f(t,n){const e=t.split("/"),r=n.split("/"),o=Math.max(e.length,r.length);for(let t=0;t<o;t++){const n=e[t],o=r[t];if(!n&&o)return-1;if(n&&!o)return 1;if(n||o){if(!c(n)&&c(o))return-1;if(c(n)&&!c(o))return 1;if(!u(n)&&u(o))return-1;if(u(n)&&!u(o))return 1;if(!i(n)&&i(o))return-1;if(i(n)&&!i(o))return 1}}return 0}function s(t){return"function"==typeof t.then}function a(t,e,o){const i=function(t){return t.sort(f)}(Object.keys(o)),u=decodeURI(e);for(const e of i){const i=o[e];if("string"==typeof i){if(r(i,u))return e}else if(r(n(i,t,e),u))return e}return e}function l(t,n,e,r){const{mode:i}=e.localePrefix;let u;var c;return void 0!==r?u=r:function(t){return"object"==typeof t||!/^[a-z]+:/i.test(t)}(c=t)&&!function(t){const n="object"==typeof t?t.pathname:t;return"string"==typeof n&&!n.startsWith("/")}(c)&&("always"===i?u=!0:"as-needed"===i&&(u=e.domains?!e.domains.some((t=>t.defaultLocale===n)):n!==e.defaultLocale)),u?function(t,n){let e=t;return/^\/(\?.*)?$/.test(n)&&(n=n.slice(1)),e+=n,e}(o(n,e.localePrefix),t):t}export{l as a,o as b,a as g,s as i,t as u};
@@ -0,0 +1,9 @@
1
+ import type { ComponentProps } from 'react';
2
+ import type { Locale } from 'use-intl';
3
+ import { IntlProvider } from 'use-intl/react';
4
+ type Props = Omit<ComponentProps<typeof IntlProvider>, 'locale'> & {
5
+ /** Pass explicitly when rendering in layout; required when not from a Server Component that set request locale. */
6
+ locale?: Locale;
7
+ };
8
+ export default function NextIntlClientProvider({ locale, ...rest }: Props): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1 @@
1
+ export default function getConfig(): void;
@@ -0,0 +1,4 @@
1
+ export { default as NextIntlClientProvider } from './NextIntlClientProvider.js';
2
+ export { default as createNavigation } from './navigation/createNavigation.js';
3
+ export { hasLocale } from 'use-intl/core';
4
+ export { useTranslations, useFormatter, useLocale, useMessages, useNow, useTimeZone } from 'use-intl/react';
@@ -0,0 +1,20 @@
1
+ import { Link as RouterLink, useRouter } from '@tanstack/react-router';
2
+ import type { ComponentProps } from 'react';
3
+ import type { Locale } from 'use-intl';
4
+ import type { RoutingConfigLocalizedNavigation, RoutingConfigSharedNavigation } from '../routing/config.js';
5
+ import type { DomainsConfig, LocalePrefixMode, Locales, Pathnames } from '../routing/types.js';
6
+ export default function createNavigation<const AppLocales extends Locales, const AppLocalePrefixMode extends LocalePrefixMode = 'always', const AppPathnames extends Pathnames<AppLocales> = never, const AppDomains extends DomainsConfig<AppLocales> = never>(routing?: [AppPathnames] extends [never] ? RoutingConfigSharedNavigation<AppLocales, AppLocalePrefixMode, AppDomains> | undefined : RoutingConfigLocalizedNavigation<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>): {
7
+ Link: <Pathname extends keyof AppPathnames = never>({ href, locale: localeProp, ...rest }: Omit<ComponentProps<typeof RouterLink>, "to"> & {
8
+ href: [AppPathnames] extends [never] ? string : Pathname | {
9
+ pathname: Pathname;
10
+ params?: Record<string, string>;
11
+ };
12
+ locale?: Locale;
13
+ }) => import("react/jsx-runtime").JSX.Element;
14
+ redirect: (pathname: string, locale?: Locale) => void;
15
+ usePathname: () => [AppPathnames] extends [never] ? string : keyof AppPathnames;
16
+ useRouter: typeof useRouter;
17
+ getPathname: (pathnameOrPathnames: string | Record<string, string>, locale: Locale, opts?: {
18
+ force?: boolean;
19
+ }) => string;
20
+ };
@@ -0,0 +1,50 @@
1
+ import type { DomainsConfig, LocalePrefix, LocalePrefixConfigVerbose, LocalePrefixMode, Locales, Pathnames } from './types.js';
2
+ /** Cookie config for locale persistence (framework-agnostic). */
3
+ export type LocaleCookieAttributes = {
4
+ maxAge?: number;
5
+ domain?: string;
6
+ path?: string;
7
+ sameSite?: 'lax' | 'strict' | 'none';
8
+ secure?: boolean;
9
+ name?: string;
10
+ };
11
+ export type RoutingConfig<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppPathnames extends Pathnames<AppLocales> | undefined, AppDomains extends DomainsConfig<AppLocales> | undefined> = {
12
+ locales: AppLocales;
13
+ defaultLocale: AppLocales[number];
14
+ localePrefix?: LocalePrefix<AppLocales, AppLocalePrefixMode>;
15
+ domains?: AppDomains;
16
+ localeCookie?: boolean | LocaleCookieAttributes;
17
+ alternateLinks?: boolean;
18
+ localeDetection?: boolean;
19
+ } & ([AppPathnames] extends [never] ? {} : {
20
+ pathnames: AppPathnames;
21
+ });
22
+ export type RoutingConfigSharedNavigation<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppDomains extends DomainsConfig<AppLocales> = never> = Omit<RoutingConfig<AppLocales, AppLocalePrefixMode, never, AppDomains>, 'defaultLocale' | 'locales' | 'pathnames'> & Partial<Pick<RoutingConfig<AppLocales, never, never, AppDomains>, 'defaultLocale' | 'locales'>>;
23
+ export type RoutingConfigLocalizedNavigation<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppPathnames extends Pathnames<AppLocales>, AppDomains extends DomainsConfig<AppLocales> = never> = Omit<RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>, 'defaultLocale' | 'pathnames'> & Partial<Pick<RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>, 'defaultLocale'>> & {
24
+ pathnames: AppPathnames;
25
+ };
26
+ export type InitializedLocaleCookieConfig = false | {
27
+ name: string;
28
+ sameSite: 'lax' | 'strict' | 'none';
29
+ [key: string]: unknown;
30
+ };
31
+ export type ResolvedRoutingConfig<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppPathnames extends Pathnames<AppLocales> | undefined, AppDomains extends DomainsConfig<AppLocales> | undefined> = Omit<RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>, 'localePrefix' | 'localeCookie' | 'alternateLinks' | 'localeDetection'> & {
32
+ localePrefix: LocalePrefixConfigVerbose<AppLocales, AppLocalePrefixMode>;
33
+ localeCookie: InitializedLocaleCookieConfig;
34
+ alternateLinks: boolean;
35
+ localeDetection: boolean;
36
+ };
37
+ export declare function receiveRoutingConfig<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppPathnames extends Pathnames<AppLocales> | undefined, AppDomains extends DomainsConfig<AppLocales> | undefined, Config extends Partial<RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>>>(input: Config): Omit<Config, "localePrefix" | "localeCookie" | "alternateLinks" | "localeDetection"> & {
38
+ localePrefix: {
39
+ mode: "never";
40
+ } | {
41
+ mode: "always";
42
+ prefixes?: Partial<Record<AppLocales[number], string>> | undefined;
43
+ } | {
44
+ mode: "as-needed";
45
+ prefixes?: Partial<Record<AppLocales[number], string>> | undefined;
46
+ };
47
+ localeCookie: InitializedLocaleCookieConfig;
48
+ localeDetection: boolean | NonNullable<RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>["localeDetection"]>;
49
+ alternateLinks: boolean | NonNullable<RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>["alternateLinks"]>;
50
+ };
@@ -0,0 +1,3 @@
1
+ import type { RoutingConfig } from './config.js';
2
+ import type { DomainsConfig, LocalePrefixMode, Locales, Pathnames } from './types.js';
3
+ export default function defineRouting<const AppLocales extends Locales, const AppLocalePrefixMode extends LocalePrefixMode = 'always', const AppPathnames extends Pathnames<AppLocales> = never, const AppDomains extends DomainsConfig<AppLocales> = never>(config: RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>): RoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>;
@@ -0,0 +1,3 @@
1
+ export type { Pathnames, LocalePrefix, DomainsConfig, LocalePrefixMode } from './types.js';
2
+ export { default as defineRouting } from './defineRouting.js';
3
+ export type { RoutingConfig } from './config.js';
@@ -0,0 +1,22 @@
1
+ export type Locales = ReadonlyArray<string>;
2
+ export type LocalePrefixMode = 'always' | 'as-needed' | 'never';
3
+ type Pathname = string;
4
+ export type LocalePrefixes<AppLocales extends Locales> = Partial<Record<AppLocales[number], Pathname>>;
5
+ export type LocalePrefixConfigVerbose<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode> = AppLocalePrefixMode extends 'always' ? {
6
+ mode: 'always';
7
+ prefixes?: LocalePrefixes<AppLocales>;
8
+ } : AppLocalePrefixMode extends 'as-needed' ? {
9
+ mode: 'as-needed';
10
+ prefixes?: LocalePrefixes<AppLocales>;
11
+ } : {
12
+ mode: 'never';
13
+ };
14
+ export type LocalePrefix<AppLocales extends Locales = [], AppLocalePrefixMode extends LocalePrefixMode = 'always'> = AppLocalePrefixMode | LocalePrefixConfigVerbose<AppLocales, AppLocalePrefixMode>;
15
+ export type Pathnames<AppLocales extends Locales> = Record<Pathname, Partial<Record<AppLocales[number], Pathname>> | Pathname>;
16
+ export type DomainConfig<AppLocales extends Locales> = {
17
+ defaultLocale: AppLocales[number];
18
+ domain: string;
19
+ locales: Array<AppLocales[number]>;
20
+ };
21
+ export type DomainsConfig<AppLocales extends Locales> = Array<DomainConfig<AppLocales>>;
22
+ export {};
@@ -0,0 +1,3 @@
1
+ export type { Pathnames, LocalePrefix, DomainsConfig, LocalePrefixMode } from './routing/types.js';
2
+ export { default as defineRouting } from './routing/defineRouting.js';
3
+ export type { RoutingConfig } from './routing/config.js';
@@ -0,0 +1,7 @@
1
+ import type { Locale } from 'use-intl';
2
+ /**
3
+ * Returns the request locale. In TanStack Start, locale must be set via
4
+ * `setRequestLocale(locale)` in your locale layout (e.g. in `$locale.tsx`)
5
+ * before any server APIs are used.
6
+ */
7
+ export declare function getRequestLocale(): Promise<Locale | undefined>;
@@ -0,0 +1,3 @@
1
+ import type { Locale } from 'use-intl';
2
+ export declare function getCachedRequestLocale(): string | undefined;
3
+ export declare function setCachedRequestLocale(locale: Locale): void;
@@ -0,0 +1,3 @@
1
+ import type { GetRequestConfigParams, RequestConfig } from './getRequestConfig.js';
2
+ declare const _default: (params: GetRequestConfigParams) => RequestConfig | Promise<RequestConfig>;
3
+ export default _default;
@@ -0,0 +1,13 @@
1
+ import { type IntlConfig, type Locale, _createIntlFormatters } from 'use-intl/core';
2
+ declare function getConfigImpl(localeOverride?: Locale): Promise<{
3
+ locale: IntlConfig['locale'];
4
+ formats?: NonNullable<IntlConfig['formats']>;
5
+ timeZone: NonNullable<IntlConfig['timeZone']>;
6
+ onError: NonNullable<IntlConfig['onError']>;
7
+ getMessageFallback: NonNullable<IntlConfig['getMessageFallback']>;
8
+ messages?: NonNullable<IntlConfig['messages']>;
9
+ now?: NonNullable<IntlConfig['now']>;
10
+ _formatters: ReturnType<typeof _createIntlFormatters>;
11
+ }>;
12
+ declare const getConfig: typeof getConfigImpl;
13
+ export default getConfig;
@@ -0,0 +1,4 @@
1
+ import type { Locale } from 'use-intl';
2
+ declare function getConfigNowImpl(locale?: Locale): Promise<Date | undefined>;
3
+ declare const getConfigNow: typeof getConfigNowImpl;
4
+ export default getConfigNow;
@@ -0,0 +1,3 @@
1
+ declare function defaultNow(): Date;
2
+ declare const getDefaultNow: typeof defaultNow;
3
+ export default getDefaultNow;
@@ -0,0 +1,4 @@
1
+ import type { Locale, createFormatter } from 'use-intl/core';
2
+ export default function getFormatter(opts?: {
3
+ locale?: Locale;
4
+ }): Promise<ReturnType<typeof createFormatter>>;
@@ -0,0 +1,4 @@
1
+ import type { Locale } from 'use-intl';
2
+ declare function getLocaleCachedImpl(): Promise<Locale>;
3
+ declare const getLocaleCached: typeof getLocaleCachedImpl;
4
+ export default getLocaleCached;
@@ -0,0 +1,6 @@
1
+ import type { Locale, useMessages as useMessagesType } from 'use-intl';
2
+ import getConfig from './getConfig.js';
3
+ export declare function getMessagesFromConfig(config: Awaited<ReturnType<typeof getConfig>>): ReturnType<typeof useMessagesType>;
4
+ export default function getMessages(opts?: {
5
+ locale?: Locale;
6
+ }): Promise<ReturnType<typeof useMessagesType>>;
@@ -0,0 +1,4 @@
1
+ import type { Locale } from 'use-intl';
2
+ export default function getNow(opts?: {
3
+ locale?: Locale;
4
+ }): Promise<Date>;
@@ -0,0 +1,18 @@
1
+ import type { IntlConfig, Locale } from 'use-intl/core';
2
+ export type RequestConfig = Omit<IntlConfig, 'locale'> & {
3
+ locale: IntlConfig['locale'];
4
+ };
5
+ export type GetRequestConfigParams = {
6
+ locale?: Locale;
7
+ /**
8
+ * The locale from the current route (e.g. `$locale` param).
9
+ * Set via `setRequestLocale(locale)` in your locale layout.
10
+ */
11
+ requestLocale: Promise<string | undefined>;
12
+ };
13
+ /**
14
+ * Use in your i18n request file (e.g. `src/i18n/request.ts`) to create
15
+ * the configuration for the current request. Configure your Vite/Start
16
+ * alias: `'tanstack-start-intl/config'` -> `'./src/i18n/request'`.
17
+ */
18
+ export default function getRequestConfig(createRequestConfig: (params: GetRequestConfigParams) => RequestConfig | Promise<RequestConfig>): (params: GetRequestConfigParams) => RequestConfig | Promise<RequestConfig>;
@@ -0,0 +1,25 @@
1
+ import { createFormatter } from 'use-intl/core';
2
+ declare function getFormatterCachedImpl(config: Parameters<typeof createFormatter>[0]): {
3
+ dateTime: {
4
+ (value: Date | number, options?: import("use-intl/core").DateTimeFormatOptions): string;
5
+ (value: Date | number, format?: import("node_modules/use-intl/dist/types/core/AppConfig.js").FormatNames["dateTime"], options?: import("use-intl/core").DateTimeFormatOptions): string;
6
+ };
7
+ number: {
8
+ (value: number | bigint, options?: import("use-intl/core").NumberFormatOptions): string;
9
+ (value: number | bigint, format?: import("node_modules/use-intl/dist/types/core/AppConfig.js").FormatNames["number"], options?: import("use-intl/core").NumberFormatOptions): string;
10
+ };
11
+ relativeTime: {
12
+ (date: number | Date, now?: import("use-intl/core").RelativeTimeFormatOptions["now"]): string;
13
+ (date: number | Date, options?: import("use-intl/core").RelativeTimeFormatOptions): string;
14
+ };
15
+ list: {
16
+ <Value extends string | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>>(value: Iterable<Value>, options?: Intl.ListFormatOptions): Value extends string ? string : Iterable<import("react").ReactElement>;
17
+ <Value extends string | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>>(value: Iterable<Value>, format?: import("node_modules/use-intl/dist/types/core/AppConfig.js").FormatNames["list"], options?: Intl.ListFormatOptions): Value extends string ? string : Iterable<import("react").ReactElement>;
18
+ };
19
+ dateTimeRange: {
20
+ (start: Date | number, end: Date | number, options?: import("use-intl/core").DateTimeFormatOptions): string;
21
+ (start: Date | number, end: Date | number, format?: import("node_modules/use-intl/dist/types/core/AppConfig.js").FormatNames["dateTime"], options?: import("use-intl/core").DateTimeFormatOptions): string;
22
+ };
23
+ };
24
+ declare const getFormatterCached: typeof getFormatterCachedImpl;
25
+ export default getFormatterCached;
@@ -0,0 +1,4 @@
1
+ import { type Messages, type NamespaceKeys, type NestedKeyOf, createTranslator } from 'use-intl/core';
2
+ declare function getServerTranslatorImpl<NestedKey extends NamespaceKeys<Messages, NestedKeyOf<Messages>> = never>(config: Parameters<typeof createTranslator>[0], namespace?: NestedKey): ReturnType<typeof createTranslator<Messages, NestedKey>>;
3
+ declare const _default: typeof getServerTranslatorImpl;
4
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import type { Locale, Timezone } from 'use-intl';
2
+ export default function getTimeZone(opts?: {
3
+ locale?: Locale;
4
+ }): Promise<Timezone>;
@@ -0,0 +1,8 @@
1
+ import type { Locale, Messages, NamespaceKeys, NestedKeyOf, createTranslator } from 'use-intl/core';
2
+ declare function getTranslations<NestedKey extends NamespaceKeys<Messages, NestedKeyOf<Messages>> = never>(namespace?: NestedKey): Promise<ReturnType<typeof createTranslator<Messages, NestedKey>>>;
3
+ declare function getTranslations<NestedKey extends NamespaceKeys<Messages, NestedKeyOf<Messages>> = never>(opts?: {
4
+ locale: Locale;
5
+ namespace?: NestedKey;
6
+ }): Promise<ReturnType<typeof createTranslator<Messages, NestedKey>>>;
7
+ declare const _default: typeof getTranslations;
8
+ export default _default;
@@ -0,0 +1 @@
1
+ export default function validateLocale(locale: string): void;
@@ -0,0 +1,9 @@
1
+ export { default as getRequestConfig, type GetRequestConfigParams, type RequestConfig } from './server/getRequestConfig.js';
2
+ export { setCachedRequestLocale as setRequestLocale } from './server/RequestLocaleCache.js';
3
+ export { default as getTranslations } from './server/getTranslations.js';
4
+ export { default as getLocale } from './server/getLocale.js';
5
+ export { default as getMessages } from './server/getMessages.js';
6
+ export { getMessagesFromConfig } from './server/getMessages.js';
7
+ export { default as getFormatter } from './server/getFormatter.js';
8
+ export { default as getTimeZone } from './server/getTimeZone.js';
9
+ export { default as getNow } from './server/getNow.js';
@@ -0,0 +1,21 @@
1
+ import type { ResolvedRoutingConfig } from '../routing/config.js';
2
+ import type { DomainsConfig, LocalePrefixConfigVerbose, LocalePrefixMode, Locales, Pathnames } from '../routing/types.js';
3
+ export type Href = string | {
4
+ pathname: string;
5
+ query?: Record<string, string>;
6
+ };
7
+ export declare function isLocalizableHref(href: Href): boolean;
8
+ export declare function unprefixPathname(pathname: string, prefix: string): string;
9
+ export declare function prefixPathname(prefix: string, pathname: string): string;
10
+ export declare function hasPathnamePrefixed(prefix: string | undefined, pathname: string): boolean;
11
+ export declare function getLocalizedTemplate<AppLocales extends Locales>(pathnameConfig: Pathnames<AppLocales>[keyof Pathnames<AppLocales>], locale: AppLocales[number], internalTemplate: string): string;
12
+ export declare function normalizeTrailingSlash(pathname: string, trailingSlash?: boolean): string;
13
+ export declare function matchesPathname(template: string, pathname: string): boolean;
14
+ export declare function getLocalePrefix<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode>(locale: AppLocales[number], localePrefix: LocalePrefixConfigVerbose<AppLocales, AppLocalePrefixMode>): string;
15
+ export declare function getLocaleAsPrefix(locale: string): string;
16
+ export declare function templateToRegex(template: string): RegExp;
17
+ export declare function getSortedPathnames(pathnames: Array<string>): string[];
18
+ export declare function isPromise<Value>(value: Value | Promise<Value>): value is Promise<Value>;
19
+ export declare function encodePathname(pathname: string): string;
20
+ export declare function getRoute<AppLocales extends Locales>(locale: AppLocales[number], pathname: string, pathnames: Pathnames<AppLocales>): keyof Pathnames<AppLocales>;
21
+ export declare function applyPathnamePrefix<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppPathnames extends Pathnames<AppLocales> | undefined, AppDomains extends DomainsConfig<AppLocales> | undefined>(pathname: string, locale: Locales[number], routing: Pick<ResolvedRoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>, 'localePrefix' | 'domains'> & Partial<Pick<ResolvedRoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>, 'defaultLocale'>>, force?: boolean): string;
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "tanstack-start-intl",
3
+ "version": "0.0.0-alpha.0",
4
+ "sideEffects": false,
5
+ "description": "Internationalization (i18n) for TanStack Start",
6
+ "license": "MIT",
7
+ "scripts": {
8
+ "build": "rm -rf dist && rollup -c",
9
+ "lint": "eslint src && tsc --noEmit",
10
+ "lint:prettier": "prettier src --check"
11
+ },
12
+ "type": "module",
13
+ "main": "./dist/esm/production/index.js",
14
+ "typings": "./dist/types/src/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/types/src/index.d.ts",
18
+ "development": "./dist/esm/development/index.js",
19
+ "default": "./dist/esm/production/index.js"
20
+ },
21
+ "./server": {
22
+ "types": "./dist/types/src/server.d.ts",
23
+ "development": "./dist/esm/development/server.js",
24
+ "default": "./dist/esm/production/server.js"
25
+ },
26
+ "./routing": {
27
+ "types": "./dist/types/src/routing.d.ts",
28
+ "development": "./dist/esm/development/routing.js",
29
+ "default": "./dist/esm/production/routing.js"
30
+ },
31
+ "./config": {
32
+ "types": "./dist/types/src/config.d.ts",
33
+ "development": "./dist/esm/development/config.js",
34
+ "default": "./dist/esm/production/config.js"
35
+ }
36
+ },
37
+ "files": ["dist", "config.d.ts", "routing.d.ts", "server.d.ts"],
38
+ "keywords": [
39
+ "react",
40
+ "intl",
41
+ "i18n",
42
+ "internationalization",
43
+ "localization",
44
+ "tanstack",
45
+ "tanstack-start"
46
+ ],
47
+ "dependencies": {
48
+ "use-intl": "workspace:^"
49
+ },
50
+ "peerDependencies": {
51
+ "@tanstack/react-router": ">=1.0.0",
52
+ "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^20.14.5",
56
+ "@types/react": "^19.2.3",
57
+ "@types/react-dom": "^19.2.3",
58
+ "eslint": "9.11.1",
59
+ "eslint-config-molindo": "^8.0.0",
60
+ "prettier": "^3.3.3",
61
+ "react": "^19.2.3",
62
+ "react-dom": "^19.2.3",
63
+ "rollup": "^4.18.0",
64
+ "tools": "workspace:^",
65
+ "typescript": "^5.5.3"
66
+ }
67
+ }