@djangocfg/nextjs 2.1.110 → 2.1.112
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 +208 -7
- package/dist/config/index.d.mts +16 -1
- package/dist/config/index.mjs +83 -14
- package/dist/config/index.mjs.map +1 -1
- package/dist/i18n/client.d.mts +123 -0
- package/dist/i18n/client.mjs +104 -0
- package/dist/i18n/client.mjs.map +1 -0
- package/dist/i18n/components.d.mts +11 -0
- package/dist/i18n/components.mjs +90 -0
- package/dist/i18n/components.mjs.map +1 -0
- package/dist/i18n/index.d.mts +18 -0
- package/dist/i18n/index.mjs +226 -0
- package/dist/i18n/index.mjs.map +1 -0
- package/dist/i18n/navigation.d.mts +1095 -0
- package/dist/i18n/navigation.mjs +45 -0
- package/dist/i18n/navigation.mjs.map +1 -0
- package/dist/i18n/plugin.d.mts +41 -0
- package/dist/i18n/plugin.mjs +17 -0
- package/dist/i18n/plugin.mjs.map +1 -0
- package/dist/i18n/provider.d.mts +18 -0
- package/dist/i18n/provider.mjs +54 -0
- package/dist/i18n/provider.mjs.map +1 -0
- package/dist/i18n/proxy.d.mts +40 -0
- package/dist/i18n/proxy.mjs +42 -0
- package/dist/i18n/proxy.mjs.map +1 -0
- package/dist/i18n/request.d.mts +42 -0
- package/dist/i18n/request.mjs +63 -0
- package/dist/i18n/request.mjs.map +1 -0
- package/dist/i18n/routing.d.mts +79 -0
- package/dist/i18n/routing.mjs +33 -0
- package/dist/i18n/routing.mjs.map +1 -0
- package/dist/i18n/server.d.mts +90 -0
- package/dist/i18n/server.mjs +79 -0
- package/dist/i18n/server.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +176 -30
- package/dist/index.mjs.map +1 -1
- package/dist/sitemap/index.d.mts +22 -3
- package/dist/sitemap/index.mjs +92 -15
- package/dist/sitemap/index.mjs.map +1 -1
- package/dist/types-Cy349X20.d.mts +60 -0
- package/package.json +54 -4
- package/src/config/constants.ts +1 -0
- package/src/config/createNextConfig.ts +39 -17
- package/src/i18n/client.ts +221 -0
- package/src/i18n/components/LocaleSwitcher.tsx +60 -0
- package/src/i18n/components/index.ts +7 -0
- package/src/i18n/index.ts +149 -0
- package/src/i18n/navigation.ts +90 -0
- package/src/i18n/plugin.ts +66 -0
- package/src/i18n/provider.tsx +91 -0
- package/src/i18n/proxy.ts +81 -0
- package/src/i18n/request.ts +141 -0
- package/src/i18n/routing.ts +84 -0
- package/src/i18n/server.ts +175 -0
- package/src/i18n/types.ts +88 -0
- package/src/sitemap/generator.ts +84 -9
- package/src/sitemap/index.ts +1 -1
- package/src/sitemap/route.ts +71 -8
- package/src/sitemap/types.ts +9 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side i18n Hooks
|
|
3
|
+
*
|
|
4
|
+
* For use in Client Components ('use client')
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* 'use client';
|
|
9
|
+
*
|
|
10
|
+
* import { useTranslations, useLocale } from '@djangocfg/nextjs/i18n/client';
|
|
11
|
+
*
|
|
12
|
+
* export function MyComponent() {
|
|
13
|
+
* const t = useTranslations('HomePage');
|
|
14
|
+
* const locale = useLocale();
|
|
15
|
+
*
|
|
16
|
+
* return <h1>{t('title')}</h1>;
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
'use client';
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
useTranslations as useNextIntlTranslations,
|
|
25
|
+
useLocale as useNextIntlLocale,
|
|
26
|
+
useMessages,
|
|
27
|
+
useNow,
|
|
28
|
+
useTimeZone,
|
|
29
|
+
useFormatter,
|
|
30
|
+
} from 'next-intl';
|
|
31
|
+
import { useRouter, usePathname } from './navigation';
|
|
32
|
+
import { routing } from './routing';
|
|
33
|
+
import type { LocaleCode } from './types';
|
|
34
|
+
|
|
35
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
+
// Core Client Hooks
|
|
37
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get translations in Client Components
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```tsx
|
|
44
|
+
* const t = useTranslations('HomePage');
|
|
45
|
+
* return <h1>{t('title')}</h1>;
|
|
46
|
+
*
|
|
47
|
+
* // With interpolation
|
|
48
|
+
* return <p>{t('greeting', { name: 'John' })}</p>;
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function useTranslations<Namespace extends string = never>(
|
|
52
|
+
namespace?: Namespace
|
|
53
|
+
) {
|
|
54
|
+
return useNextIntlTranslations(namespace);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get current locale in Client Components
|
|
59
|
+
*/
|
|
60
|
+
export function useLocale(): LocaleCode {
|
|
61
|
+
return useNextIntlLocale() as LocaleCode;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get all messages
|
|
66
|
+
* Useful for passing to child providers
|
|
67
|
+
*/
|
|
68
|
+
export { useMessages };
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get current time
|
|
72
|
+
*/
|
|
73
|
+
export { useNow };
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get timezone
|
|
77
|
+
*/
|
|
78
|
+
export { useTimeZone };
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get formatter for dates, numbers, etc.
|
|
82
|
+
*/
|
|
83
|
+
export { useFormatter };
|
|
84
|
+
|
|
85
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
86
|
+
// Convenience Hooks
|
|
87
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Shorthand for useTranslations
|
|
91
|
+
* Alias for compatibility with @djangocfg/i18n
|
|
92
|
+
*/
|
|
93
|
+
export const useT = useTranslations;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get namespaced translations
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* const pt = useNamespacedTranslations('payments');
|
|
101
|
+
* return <span>{pt('balance.available')}</span>;
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function useNamespacedTranslations(namespace: string) {
|
|
105
|
+
return useNextIntlTranslations(namespace);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
109
|
+
// Formatting Hooks
|
|
110
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format a date according to locale
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```tsx
|
|
117
|
+
* const formatDate = useDateFormatter();
|
|
118
|
+
* return <span>{formatDate(new Date())}</span>;
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function useDateFormatter() {
|
|
122
|
+
const formatter = useFormatter();
|
|
123
|
+
return (date: Date | number, options?: Parameters<typeof formatter.dateTime>[1]) =>
|
|
124
|
+
formatter.dateTime(date, options);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Format a number according to locale
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```tsx
|
|
132
|
+
* const formatNumber = useNumberFormatter();
|
|
133
|
+
* return <span>{formatNumber(1234.56)}</span>;
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export function useNumberFormatter() {
|
|
137
|
+
const formatter = useFormatter();
|
|
138
|
+
return (number: number, options?: Parameters<typeof formatter.number>[1]) =>
|
|
139
|
+
formatter.number(number, options);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Format relative time
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```tsx
|
|
147
|
+
* const formatRelative = useRelativeTimeFormatter();
|
|
148
|
+
* return <span>{formatRelative(new Date())}</span>;
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
export function useRelativeTimeFormatter() {
|
|
152
|
+
const formatter = useFormatter();
|
|
153
|
+
return (date: Date | number, options?: Parameters<typeof formatter.relativeTime>[1]) =>
|
|
154
|
+
formatter.relativeTime(date, options);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
158
|
+
// Locale Switching Hooks
|
|
159
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get list of available locales from routing config
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```tsx
|
|
166
|
+
* const locales = useLocales();
|
|
167
|
+
* // ['en', 'ru', 'ko']
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export function useLocales(): LocaleCode[] {
|
|
171
|
+
return routing.locales as LocaleCode[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get default locale from routing config
|
|
176
|
+
*/
|
|
177
|
+
export function useDefaultLocale(): LocaleCode {
|
|
178
|
+
return routing.defaultLocale as LocaleCode;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Hook to change current locale
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```tsx
|
|
186
|
+
* const changeLocale = useChangeLocale();
|
|
187
|
+
*
|
|
188
|
+
* <button onClick={() => changeLocale('ru')}>
|
|
189
|
+
* Switch to Russian
|
|
190
|
+
* </button>
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export function useChangeLocale() {
|
|
194
|
+
const router = useRouter();
|
|
195
|
+
const pathname = usePathname();
|
|
196
|
+
|
|
197
|
+
return (locale: LocaleCode) => {
|
|
198
|
+
router.replace(pathname, { locale });
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Combined hook for locale switching
|
|
204
|
+
* Returns current locale, available locales, and change function
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```tsx
|
|
208
|
+
* const { locale, locales, changeLocale } = useLocaleSwitcher();
|
|
209
|
+
*
|
|
210
|
+
* <select value={locale} onChange={(e) => changeLocale(e.target.value)}>
|
|
211
|
+
* {locales.map(l => <option key={l} value={l}>{l}</option>)}
|
|
212
|
+
* </select>
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export function useLocaleSwitcher() {
|
|
216
|
+
const locale = useLocale();
|
|
217
|
+
const locales = useLocales();
|
|
218
|
+
const changeLocale = useChangeLocale();
|
|
219
|
+
|
|
220
|
+
return { locale, locales, changeLocale };
|
|
221
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocaleSwitcher Component (Smart)
|
|
3
|
+
*
|
|
4
|
+
* Wrapper around @djangocfg/layouts LocaleSwitcher with next-intl hooks.
|
|
5
|
+
* Automatically gets locale data from routing config.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { LocaleSwitcher } from '@djangocfg/nextjs/i18n/components';
|
|
10
|
+
*
|
|
11
|
+
* // Basic usage (uses all locales from routing config)
|
|
12
|
+
* <LocaleSwitcher />
|
|
13
|
+
*
|
|
14
|
+
* // With custom locales
|
|
15
|
+
* <LocaleSwitcher locales={['en', 'ru']} />
|
|
16
|
+
*
|
|
17
|
+
* // With custom labels
|
|
18
|
+
* <LocaleSwitcher
|
|
19
|
+
* labels={{
|
|
20
|
+
* en: 'English',
|
|
21
|
+
* ru: 'Русский',
|
|
22
|
+
* ko: '한국어',
|
|
23
|
+
* }}
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
'use client';
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
LocaleSwitcher as BaseLocaleSwitcher,
|
|
32
|
+
type LocaleSwitcherProps as BaseLocaleSwitcherProps,
|
|
33
|
+
} from '@djangocfg/layouts';
|
|
34
|
+
|
|
35
|
+
import { useLocaleSwitcher } from '../client';
|
|
36
|
+
import type { LocaleCode } from '../types';
|
|
37
|
+
|
|
38
|
+
export interface LocaleSwitcherProps
|
|
39
|
+
extends Omit<BaseLocaleSwitcherProps, 'locale' | 'locales' | 'onChange'> {
|
|
40
|
+
/** Available locales (defaults to all from routing config) */
|
|
41
|
+
locales?: LocaleCode[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function LocaleSwitcher({
|
|
45
|
+
locales: customLocales,
|
|
46
|
+
...props
|
|
47
|
+
}: LocaleSwitcherProps) {
|
|
48
|
+
const { locale, locales: routingLocales, changeLocale } = useLocaleSwitcher();
|
|
49
|
+
|
|
50
|
+
const availableLocales = customLocales || routingLocales;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<BaseLocaleSwitcher
|
|
54
|
+
locale={locale}
|
|
55
|
+
locales={availableLocales}
|
|
56
|
+
onChange={changeLocale}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Module for Next.js App Router
|
|
3
|
+
*
|
|
4
|
+
* Complete internationalization solution integrating next-intl with @djangocfg/i18n
|
|
5
|
+
*
|
|
6
|
+
* @example Setup
|
|
7
|
+
* ```ts
|
|
8
|
+
* // 1. Create i18n/routing.ts
|
|
9
|
+
* import { createRouting } from '@djangocfg/nextjs/i18n';
|
|
10
|
+
*
|
|
11
|
+
* export const routing = createRouting({
|
|
12
|
+
* locales: ['en', 'ru', 'ko'],
|
|
13
|
+
* defaultLocale: 'en',
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // 2. Create i18n/request.ts
|
|
17
|
+
* import { createRequestConfig } from '@djangocfg/nextjs/i18n';
|
|
18
|
+
* import { leadsI18n } from '@djangocfg/ext-leads';
|
|
19
|
+
*
|
|
20
|
+
* export default createRequestConfig({
|
|
21
|
+
* extensions: [leadsI18n],
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // 3. Create i18n/navigation.ts
|
|
25
|
+
* import { createNavigation } from '@djangocfg/nextjs/i18n';
|
|
26
|
+
* import { routing } from './routing';
|
|
27
|
+
*
|
|
28
|
+
* export const { Link, redirect, usePathname, useRouter } = createNavigation(routing);
|
|
29
|
+
*
|
|
30
|
+
* // 4. Create proxy.ts
|
|
31
|
+
* import { createProxy } from '@djangocfg/nextjs/i18n';
|
|
32
|
+
* import { routing } from './i18n/routing';
|
|
33
|
+
*
|
|
34
|
+
* export default createProxy(routing);
|
|
35
|
+
* export const config = { matcher: ['/((?!api|_next|.*\\..*).*)'] };
|
|
36
|
+
*
|
|
37
|
+
* // 5. Update next.config.ts
|
|
38
|
+
* import { createBaseNextConfig } from '@djangocfg/nextjs/config';
|
|
39
|
+
*
|
|
40
|
+
* export default createBaseNextConfig({
|
|
41
|
+
* i18n: {
|
|
42
|
+
* locales: ['en', 'ru', 'ko'],
|
|
43
|
+
* defaultLocale: 'en',
|
|
44
|
+
* },
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* // 6. Create app/[locale]/layout.tsx
|
|
48
|
+
* import { I18nProvider } from '@djangocfg/nextjs/i18n';
|
|
49
|
+
* import { getMessages, getLocaleFromParams } from '@djangocfg/nextjs/i18n/server';
|
|
50
|
+
*
|
|
51
|
+
* export default async function LocaleLayout({ children, params }) {
|
|
52
|
+
* const locale = await getLocaleFromParams(params);
|
|
53
|
+
* const messages = await getMessages();
|
|
54
|
+
*
|
|
55
|
+
* return (
|
|
56
|
+
* <html lang={locale}>
|
|
57
|
+
* <body>
|
|
58
|
+
* <I18nProvider locale={locale} messages={messages}>
|
|
59
|
+
* {children}
|
|
60
|
+
* </I18nProvider>
|
|
61
|
+
* </body>
|
|
62
|
+
* </html>
|
|
63
|
+
* );
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
// Types
|
|
70
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
export type {
|
|
73
|
+
I18nConfig,
|
|
74
|
+
I18nPluginOptions,
|
|
75
|
+
I18nProviderProps,
|
|
76
|
+
Messages,
|
|
77
|
+
MessagesLoader,
|
|
78
|
+
ExtensionMessages,
|
|
79
|
+
LocaleParams,
|
|
80
|
+
LocaleLayoutProps,
|
|
81
|
+
LocalePageProps,
|
|
82
|
+
LocaleCode,
|
|
83
|
+
I18nTranslations,
|
|
84
|
+
} from './types';
|
|
85
|
+
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
// Routing
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
createRouting,
|
|
92
|
+
routing,
|
|
93
|
+
isValidLocale,
|
|
94
|
+
getLocaleFromParams,
|
|
95
|
+
generateLocaleParams,
|
|
96
|
+
} from './routing';
|
|
97
|
+
|
|
98
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
99
|
+
// Navigation
|
|
100
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
createNavigation,
|
|
104
|
+
createNavigationFromConfig,
|
|
105
|
+
Link,
|
|
106
|
+
redirect,
|
|
107
|
+
usePathname,
|
|
108
|
+
useRouter,
|
|
109
|
+
getPathname,
|
|
110
|
+
} from './navigation';
|
|
111
|
+
|
|
112
|
+
export type { LinkProps } from './navigation';
|
|
113
|
+
|
|
114
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
+
// Provider
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
export { I18nProvider, NextIntlProvider } from './provider';
|
|
119
|
+
|
|
120
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
121
|
+
// Request Config (for i18n/request.ts)
|
|
122
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
export {
|
|
125
|
+
createRequestConfig,
|
|
126
|
+
type RequestConfigOptions,
|
|
127
|
+
} from './request';
|
|
128
|
+
|
|
129
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
// Proxy (Next.js 16+)
|
|
131
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
export {
|
|
134
|
+
createProxy,
|
|
135
|
+
createProxyFromConfig,
|
|
136
|
+
proxy,
|
|
137
|
+
config as proxyConfig,
|
|
138
|
+
} from './proxy';
|
|
139
|
+
|
|
140
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
141
|
+
// Plugin (for next.config.ts) - import from '@djangocfg/nextjs/i18n/plugin'
|
|
142
|
+
// Not exported here to avoid SWC plugin issues with Turbopack
|
|
143
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
146
|
+
// Components
|
|
147
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
export { LocaleSwitcher, type LocaleSwitcherProps } from './components';
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Navigation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides locale-aware navigation components and functions
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // In your app's i18n/navigation.ts
|
|
9
|
+
* import { createNavigation } from '@djangocfg/nextjs/i18n';
|
|
10
|
+
* import { routing } from './routing';
|
|
11
|
+
*
|
|
12
|
+
* export const { Link, redirect, usePathname, useRouter } = createNavigation(routing);
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* // Using in components
|
|
18
|
+
* import { Link, usePathname } from '@/i18n/navigation';
|
|
19
|
+
*
|
|
20
|
+
* function Nav() {
|
|
21
|
+
* const pathname = usePathname();
|
|
22
|
+
* return <Link href="/about">About</Link>;
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { createNavigation as createNextIntlNavigation } from 'next-intl/navigation';
|
|
28
|
+
import { routing, createRouting } from './routing';
|
|
29
|
+
import type { I18nConfig } from './types';
|
|
30
|
+
|
|
31
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
// Navigation Factory
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create navigation utilities with custom routing
|
|
37
|
+
*
|
|
38
|
+
* Returns locale-aware versions of:
|
|
39
|
+
* - Link: Locale-prefixed links
|
|
40
|
+
* - redirect: Server-side redirect with locale
|
|
41
|
+
* - usePathname: Current pathname without locale prefix
|
|
42
|
+
* - useRouter: Router with locale-aware navigation
|
|
43
|
+
* - getPathname: Get pathname for a route
|
|
44
|
+
*/
|
|
45
|
+
export function createNavigation(routingConfig?: ReturnType<typeof createRouting>) {
|
|
46
|
+
const config = routingConfig ?? routing;
|
|
47
|
+
return createNextIntlNavigation(config);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create navigation from config options
|
|
52
|
+
*/
|
|
53
|
+
export function createNavigationFromConfig(config: Partial<I18nConfig>) {
|
|
54
|
+
const routingConfig = createRouting(config);
|
|
55
|
+
return createNextIntlNavigation(routingConfig);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
59
|
+
// Default Navigation (using default routing)
|
|
60
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Default navigation utilities
|
|
64
|
+
* Use these directly or create custom ones with createNavigation()
|
|
65
|
+
*/
|
|
66
|
+
export const {
|
|
67
|
+
Link,
|
|
68
|
+
redirect,
|
|
69
|
+
usePathname,
|
|
70
|
+
useRouter,
|
|
71
|
+
getPathname,
|
|
72
|
+
} = createNavigation();
|
|
73
|
+
|
|
74
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
75
|
+
// Types
|
|
76
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Props for locale-aware Link component
|
|
80
|
+
*/
|
|
81
|
+
export interface LinkProps {
|
|
82
|
+
/** Target href */
|
|
83
|
+
href: string;
|
|
84
|
+
/** Target locale (optional, defaults to current) */
|
|
85
|
+
locale?: string;
|
|
86
|
+
/** Children */
|
|
87
|
+
children?: React.ReactNode;
|
|
88
|
+
/** Additional props passed to Next.js Link */
|
|
89
|
+
[key: string]: unknown;
|
|
90
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Plugin for Next.js Configuration
|
|
3
|
+
*
|
|
4
|
+
* Wraps next-intl plugin for use with createBaseNextConfig
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // next.config.ts
|
|
9
|
+
* import { createBaseNextConfig } from '@djangocfg/nextjs/config';
|
|
10
|
+
*
|
|
11
|
+
* export default createBaseNextConfig({
|
|
12
|
+
* i18n: {
|
|
13
|
+
* locales: ['en', 'ru', 'ko'],
|
|
14
|
+
* defaultLocale: 'en',
|
|
15
|
+
* },
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import createNextIntlPlugin from 'next-intl/plugin';
|
|
21
|
+
import type { NextConfig } from 'next';
|
|
22
|
+
import type { I18nPluginOptions } from './types';
|
|
23
|
+
|
|
24
|
+
export type { I18nPluginOptions } from './types';
|
|
25
|
+
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
// Plugin Factory
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create next-intl plugin wrapper
|
|
32
|
+
*
|
|
33
|
+
* @param options - i18n configuration options
|
|
34
|
+
* @returns Next.js config wrapper function
|
|
35
|
+
*/
|
|
36
|
+
export function createI18nPlugin(options?: I18nPluginOptions) {
|
|
37
|
+
// Determine request config path
|
|
38
|
+
const requestConfigPath = options?.requestConfig
|
|
39
|
+
?? './i18n/request.ts';
|
|
40
|
+
|
|
41
|
+
// Create next-intl plugin
|
|
42
|
+
const withNextIntl = createNextIntlPlugin(requestConfigPath);
|
|
43
|
+
|
|
44
|
+
return withNextIntl;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Apply i18n plugin to Next.js config
|
|
49
|
+
*
|
|
50
|
+
* @param config - Base Next.js config
|
|
51
|
+
* @param options - i18n options
|
|
52
|
+
* @returns Modified Next.js config
|
|
53
|
+
*/
|
|
54
|
+
export function withI18n(
|
|
55
|
+
config: NextConfig,
|
|
56
|
+
options?: I18nPluginOptions
|
|
57
|
+
): NextConfig {
|
|
58
|
+
const plugin = createI18nPlugin(options);
|
|
59
|
+
return plugin(config);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
63
|
+
// Export for direct use
|
|
64
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
export { createNextIntlPlugin };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Provider for Next.js App Router
|
|
3
|
+
*
|
|
4
|
+
* Bridges next-intl with @djangocfg/i18n
|
|
5
|
+
* Provides translations to both Server and Client components
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* // app/[locale]/layout.tsx
|
|
10
|
+
* import { I18nProvider } from '@djangocfg/nextjs/i18n';
|
|
11
|
+
* import { getMessages, getLocale } from '@djangocfg/nextjs/i18n/server';
|
|
12
|
+
*
|
|
13
|
+
* export default async function LocaleLayout({ children, params }) {
|
|
14
|
+
* const locale = await getLocale(params);
|
|
15
|
+
* const messages = await getMessages();
|
|
16
|
+
*
|
|
17
|
+
* return (
|
|
18
|
+
* <I18nProvider locale={locale} messages={messages}>
|
|
19
|
+
* {children}
|
|
20
|
+
* </I18nProvider>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
'use client';
|
|
27
|
+
|
|
28
|
+
import { NextIntlClientProvider } from 'next-intl';
|
|
29
|
+
import { I18nProvider as DjangoCfgI18nProvider } from '@djangocfg/i18n';
|
|
30
|
+
import type { I18nProviderProps, LocaleCode } from './types';
|
|
31
|
+
|
|
32
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
33
|
+
// Provider Component
|
|
34
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Unified i18n Provider
|
|
38
|
+
*
|
|
39
|
+
* Wraps both NextIntlClientProvider and @djangocfg/i18n's I18nProvider
|
|
40
|
+
* This ensures both next-intl hooks and @djangocfg/i18n hooks work correctly
|
|
41
|
+
*/
|
|
42
|
+
export function I18nProvider({
|
|
43
|
+
locale,
|
|
44
|
+
messages,
|
|
45
|
+
timeZone = 'UTC',
|
|
46
|
+
now,
|
|
47
|
+
children,
|
|
48
|
+
}: I18nProviderProps) {
|
|
49
|
+
return (
|
|
50
|
+
<NextIntlClientProvider
|
|
51
|
+
locale={locale}
|
|
52
|
+
messages={messages}
|
|
53
|
+
timeZone={timeZone}
|
|
54
|
+
now={now}
|
|
55
|
+
>
|
|
56
|
+
<DjangoCfgI18nProvider
|
|
57
|
+
locale={locale as LocaleCode}
|
|
58
|
+
translations={messages}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
</DjangoCfgI18nProvider>
|
|
62
|
+
</NextIntlClientProvider>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
// Lightweight Provider (next-intl only)
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Next-intl only provider (without @djangocfg/i18n context)
|
|
72
|
+
* Use if you only need next-intl hooks
|
|
73
|
+
*/
|
|
74
|
+
export function NextIntlProvider({
|
|
75
|
+
locale,
|
|
76
|
+
messages,
|
|
77
|
+
timeZone = 'UTC',
|
|
78
|
+
now,
|
|
79
|
+
children,
|
|
80
|
+
}: I18nProviderProps) {
|
|
81
|
+
return (
|
|
82
|
+
<NextIntlClientProvider
|
|
83
|
+
locale={locale}
|
|
84
|
+
messages={messages}
|
|
85
|
+
timeZone={timeZone}
|
|
86
|
+
now={now}
|
|
87
|
+
>
|
|
88
|
+
{children}
|
|
89
|
+
</NextIntlClientProvider>
|
|
90
|
+
);
|
|
91
|
+
}
|