@djangocfg/nextjs 2.1.109 → 2.1.111
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 +176 -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 +22 -0
- package/dist/i18n/components.mjs +133 -0
- package/dist/i18n/components.mjs.map +1 -0
- package/dist/i18n/index.d.mts +17 -0
- package/dist/i18n/index.mjs +269 -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 +124 -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,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
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Proxy for Next.js App Router (Next.js 16+)
|
|
3
|
+
*
|
|
4
|
+
* Handles locale detection and routing
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // proxy.ts
|
|
9
|
+
* export { proxy as default, config } from '@djangocfg/nextjs/i18n';
|
|
10
|
+
*
|
|
11
|
+
* // Or with custom routing:
|
|
12
|
+
* import { createProxy } from '@djangocfg/nextjs/i18n';
|
|
13
|
+
* import { routing } from './i18n/routing';
|
|
14
|
+
*
|
|
15
|
+
* export default createProxy(routing);
|
|
16
|
+
* export const config = { matcher: [...] };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import createIntlMiddleware from 'next-intl/middleware';
|
|
21
|
+
import type { NextRequest } from 'next/server';
|
|
22
|
+
|
|
23
|
+
import { routing, createRouting } from './routing';
|
|
24
|
+
import type { I18nConfig } from './types';
|
|
25
|
+
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
// Proxy Factory
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create i18n proxy handler with custom routing configuration
|
|
32
|
+
*/
|
|
33
|
+
export function createProxy(routingConfig?: ReturnType<typeof createRouting>) {
|
|
34
|
+
const config = routingConfig ?? routing;
|
|
35
|
+
return createIntlMiddleware(config);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create i18n proxy from config options
|
|
40
|
+
*/
|
|
41
|
+
export function createProxyFromConfig(config: Partial<I18nConfig>) {
|
|
42
|
+
const routingConfig = createRouting(config);
|
|
43
|
+
return createIntlMiddleware(routingConfig);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
|
+
// Default Proxy
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
const handleI18nRouting = createProxy();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Default proxy function using default routing
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* // proxy.ts
|
|
58
|
+
* export { proxy as default, config } from '@djangocfg/nextjs/i18n';
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function proxy(request: NextRequest) {
|
|
62
|
+
return handleI18nRouting(request);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Config
|
|
67
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Default proxy config
|
|
71
|
+
* Matches all paths except static files, API routes, and Next.js internals
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* // proxy.ts
|
|
76
|
+
* export { proxy as default, config } from '@djangocfg/nextjs/i18n';
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export const config = {
|
|
80
|
+
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)',],
|
|
81
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Request Configuration for Server Components
|
|
3
|
+
*
|
|
4
|
+
* Provides translations to server components via next-intl's request scope
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // i18n/request.ts in your app
|
|
9
|
+
* import { createRequestConfig } from '@djangocfg/nextjs/i18n';
|
|
10
|
+
* import { en, ru, ko } from '@djangocfg/i18n';
|
|
11
|
+
* import { leadsI18n } from '@djangocfg/ext-leads';
|
|
12
|
+
*
|
|
13
|
+
* export default createRequestConfig({
|
|
14
|
+
* locales: { en, ru, ko },
|
|
15
|
+
* extensions: [leadsI18n],
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { getRequestConfig } from 'next-intl/server';
|
|
21
|
+
import { mergeTranslations, en, ru, ko } from '@djangocfg/i18n';
|
|
22
|
+
import type { I18nTranslations, LocaleCode } from '@djangocfg/i18n';
|
|
23
|
+
import type { Messages } from './types';
|
|
24
|
+
import { routing } from './routing';
|
|
25
|
+
|
|
26
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
// Types
|
|
28
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export interface RequestConfigOptions {
|
|
31
|
+
/** Base locale translations from @djangocfg/i18n */
|
|
32
|
+
locales?: Record<LocaleCode, I18nTranslations>;
|
|
33
|
+
/** Extension i18n instances to merge */
|
|
34
|
+
extensions?: Array<{
|
|
35
|
+
namespace: string;
|
|
36
|
+
locales: Record<string, Record<string, unknown>>;
|
|
37
|
+
}>;
|
|
38
|
+
/** Custom message loader (overrides locales) */
|
|
39
|
+
loadMessages?: (locale: LocaleCode) => Promise<Messages> | Messages;
|
|
40
|
+
/** Time zone for date/time formatting */
|
|
41
|
+
timeZone?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
45
|
+
// Default Locales
|
|
46
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
const DEFAULT_LOCALES: Record<LocaleCode, I18nTranslations> = {
|
|
49
|
+
en,
|
|
50
|
+
ru,
|
|
51
|
+
ko,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
55
|
+
// Message Loading
|
|
56
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Load and merge messages for a locale
|
|
60
|
+
*/
|
|
61
|
+
function loadMessages(
|
|
62
|
+
locale: LocaleCode,
|
|
63
|
+
options: RequestConfigOptions
|
|
64
|
+
): Messages {
|
|
65
|
+
// Get base translations
|
|
66
|
+
const locales = options.locales ?? DEFAULT_LOCALES;
|
|
67
|
+
const baseMessages = locales[locale] ?? locales.en ?? en;
|
|
68
|
+
|
|
69
|
+
// If no extensions, return base
|
|
70
|
+
if (!options.extensions?.length) {
|
|
71
|
+
return baseMessages as Messages;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Merge extension translations
|
|
75
|
+
let mergedMessages = { ...baseMessages } as Messages;
|
|
76
|
+
|
|
77
|
+
for (const extension of options.extensions) {
|
|
78
|
+
const extMessages = extension.locales[locale] ?? extension.locales.en;
|
|
79
|
+
if (extMessages) {
|
|
80
|
+
mergedMessages = mergeTranslations(mergedMessages, {
|
|
81
|
+
[extension.namespace]: extMessages,
|
|
82
|
+
}) as Messages;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return mergedMessages;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
90
|
+
// Request Config Factory
|
|
91
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create request configuration for next-intl
|
|
95
|
+
*
|
|
96
|
+
* This is used in your app's `i18n/request.ts` file
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* // i18n/request.ts
|
|
101
|
+
* import { createRequestConfig } from '@djangocfg/nextjs/i18n';
|
|
102
|
+
* import { leadsI18n } from '@djangocfg/ext-leads';
|
|
103
|
+
* import { paymentsI18n } from '@djangocfg/ext-payments';
|
|
104
|
+
*
|
|
105
|
+
* export default createRequestConfig({
|
|
106
|
+
* extensions: [leadsI18n, paymentsI18n],
|
|
107
|
+
* });
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function createRequestConfig(options: RequestConfigOptions = {}) {
|
|
111
|
+
return getRequestConfig(async ({ requestLocale }) => {
|
|
112
|
+
// Get locale from request or default
|
|
113
|
+
let locale = await requestLocale;
|
|
114
|
+
|
|
115
|
+
// Validate and fallback to default
|
|
116
|
+
if (!locale || !routing.locales.includes(locale as LocaleCode)) {
|
|
117
|
+
locale = routing.defaultLocale;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Load messages
|
|
121
|
+
const messages = options.loadMessages
|
|
122
|
+
? await options.loadMessages(locale as LocaleCode)
|
|
123
|
+
: loadMessages(locale as LocaleCode, options);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
locale,
|
|
127
|
+
messages,
|
|
128
|
+
timeZone: options.timeZone ?? 'UTC',
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
134
|
+
// Default Export
|
|
135
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Default request config with base @djangocfg/i18n translations
|
|
139
|
+
* Can be used directly if no extensions are needed
|
|
140
|
+
*/
|
|
141
|
+
export default createRequestConfig();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Routing Configuration
|
|
3
|
+
*
|
|
4
|
+
* Creates routing configuration for next-intl
|
|
5
|
+
* Used by proxy and navigation components
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { defineRouting } from 'next-intl/routing';
|
|
9
|
+
import type { I18nConfig, LocaleCode } from './types';
|
|
10
|
+
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
// Default Configuration
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
const DEFAULT_LOCALES: LocaleCode[] = ['en', 'ru', 'ko'];
|
|
16
|
+
const DEFAULT_LOCALE: LocaleCode = 'en';
|
|
17
|
+
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
// Routing Factory
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create routing configuration for next-intl
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* // i18n/routing.ts
|
|
28
|
+
* import { createRouting } from '@djangocfg/nextjs/i18n';
|
|
29
|
+
*
|
|
30
|
+
* export const routing = createRouting({
|
|
31
|
+
* locales: ['en', 'ru', 'ko'],
|
|
32
|
+
* defaultLocale: 'en',
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function createRouting(config?: Partial<I18nConfig>) {
|
|
37
|
+
const locales = config?.locales ?? DEFAULT_LOCALES;
|
|
38
|
+
const defaultLocale = config?.defaultLocale ?? DEFAULT_LOCALE;
|
|
39
|
+
const localePrefix = config?.localePrefix ?? 'always';
|
|
40
|
+
|
|
41
|
+
return defineRouting({
|
|
42
|
+
locales,
|
|
43
|
+
defaultLocale,
|
|
44
|
+
localePrefix,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Default routing configuration
|
|
50
|
+
* Can be overridden by app-specific configuration
|
|
51
|
+
*/
|
|
52
|
+
export const routing = createRouting();
|
|
53
|
+
|
|
54
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
55
|
+
// Locale Utilities
|
|
56
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if a locale is supported
|
|
60
|
+
*/
|
|
61
|
+
export function isValidLocale(
|
|
62
|
+
locale: string,
|
|
63
|
+
supportedLocales: readonly string[] = DEFAULT_LOCALES
|
|
64
|
+
): locale is LocaleCode {
|
|
65
|
+
return supportedLocales.includes(locale as LocaleCode);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get locale from params (handles async params in Next.js 15+)
|
|
70
|
+
*/
|
|
71
|
+
export async function getLocaleFromParams(
|
|
72
|
+
params: Promise<{ locale: string }> | { locale: string }
|
|
73
|
+
): Promise<LocaleCode> {
|
|
74
|
+
const resolved = await params;
|
|
75
|
+
return resolved.locale as LocaleCode;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Generate static params for all locales
|
|
80
|
+
* Use in generateStaticParams for locale pages
|
|
81
|
+
*/
|
|
82
|
+
export function generateLocaleParams(locales: readonly string[] = DEFAULT_LOCALES) {
|
|
83
|
+
return locales.map((locale) => ({ locale }));
|
|
84
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side i18n Utilities
|
|
3
|
+
*
|
|
4
|
+
* For use in Server Components, Server Actions, and Route Handlers
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* // In a Server Component
|
|
9
|
+
* import { getTranslations, getLocale } from '@djangocfg/nextjs/i18n/server';
|
|
10
|
+
*
|
|
11
|
+
* export default async function Page() {
|
|
12
|
+
* const t = await getTranslations('HomePage');
|
|
13
|
+
* const locale = await getLocale();
|
|
14
|
+
*
|
|
15
|
+
* return <h1>{t('title')}</h1>;
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
getTranslations as getNextIntlTranslations,
|
|
22
|
+
getLocale as getNextIntlLocale,
|
|
23
|
+
getMessages as getNextIntlMessages,
|
|
24
|
+
getNow,
|
|
25
|
+
getTimeZone,
|
|
26
|
+
getFormatter,
|
|
27
|
+
} from 'next-intl/server';
|
|
28
|
+
import type { LocaleCode, Messages, LocaleParams } from './types';
|
|
29
|
+
import { isValidLocale, generateLocaleParams } from './routing';
|
|
30
|
+
|
|
31
|
+
// Re-export routing utilities for convenience
|
|
32
|
+
export { isValidLocale, generateLocaleParams };
|
|
33
|
+
|
|
34
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
// Core Server Functions
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get translations for Server Components
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const t = await getTranslations('HomePage');
|
|
44
|
+
* return <h1>{t('title')}</h1>;
|
|
45
|
+
*
|
|
46
|
+
* // Or without namespace
|
|
47
|
+
* const t = await getTranslations();
|
|
48
|
+
* return <h1>{t('HomePage.title')}</h1>;
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export async function getTranslations<Namespace extends string = never>(
|
|
52
|
+
namespace?: Namespace
|
|
53
|
+
) {
|
|
54
|
+
return getNextIntlTranslations(namespace);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get current locale in Server Components
|
|
59
|
+
*/
|
|
60
|
+
export async function getLocale(): Promise<LocaleCode> {
|
|
61
|
+
return (await getNextIntlLocale()) as LocaleCode;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get all messages for the current request
|
|
66
|
+
* Useful for passing to I18nProvider in layouts
|
|
67
|
+
*/
|
|
68
|
+
export async function getMessages(): Promise<Messages> {
|
|
69
|
+
return (await getNextIntlMessages()) as Messages;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get current time for the request
|
|
74
|
+
* Useful for consistent time-based formatting
|
|
75
|
+
*/
|
|
76
|
+
export { getNow };
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get timezone for the request
|
|
80
|
+
*/
|
|
81
|
+
export { getTimeZone };
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get formatter for dates, numbers, etc.
|
|
85
|
+
*/
|
|
86
|
+
export { getFormatter };
|
|
87
|
+
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
89
|
+
// Locale Extraction Helpers
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extract locale from page/layout params
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```tsx
|
|
97
|
+
* // app/[locale]/page.tsx
|
|
98
|
+
* export default async function Page({ params }) {
|
|
99
|
+
* const locale = await getLocaleFromParams(params);
|
|
100
|
+
* // ...
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export async function getLocaleFromParams(
|
|
105
|
+
params: Promise<LocaleParams> | LocaleParams
|
|
106
|
+
): Promise<LocaleCode> {
|
|
107
|
+
const resolved = await params;
|
|
108
|
+
return resolved.locale as LocaleCode;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Shorthand for getting locale from params
|
|
113
|
+
* Alias for getLocaleFromParams
|
|
114
|
+
*/
|
|
115
|
+
export const extractLocale = getLocaleFromParams;
|
|
116
|
+
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
// Typed Translation Helpers
|
|
119
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get typed translations for a specific namespace
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```tsx
|
|
126
|
+
* const t = await getNamespacedTranslations('payments');
|
|
127
|
+
* return <span>{t('balance.available')}</span>;
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export async function getNamespacedTranslations(namespace: string) {
|
|
131
|
+
return getNextIntlTranslations(namespace);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
135
|
+
// Metadata Helpers
|
|
136
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Generate localized metadata for pages
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```tsx
|
|
143
|
+
* // app/[locale]/about/page.tsx
|
|
144
|
+
* import { generateLocalizedMetadata } from '@djangocfg/nextjs/i18n/server';
|
|
145
|
+
*
|
|
146
|
+
* export async function generateMetadata({ params }) {
|
|
147
|
+
* return generateLocalizedMetadata(params, {
|
|
148
|
+
* titleKey: 'AboutPage.meta.title',
|
|
149
|
+
* descriptionKey: 'AboutPage.meta.description',
|
|
150
|
+
* });
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export async function generateLocalizedMetadata(
|
|
155
|
+
params: Promise<LocaleParams> | LocaleParams,
|
|
156
|
+
options: {
|
|
157
|
+
titleKey?: string;
|
|
158
|
+
descriptionKey?: string;
|
|
159
|
+
namespace?: string;
|
|
160
|
+
} = {}
|
|
161
|
+
) {
|
|
162
|
+
const locale = await getLocaleFromParams(params);
|
|
163
|
+
const t = await getTranslations(options.namespace);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
title: options.titleKey ? t(options.titleKey as never) : undefined,
|
|
167
|
+
description: options.descriptionKey ? t(options.descriptionKey as never) : undefined,
|
|
168
|
+
// Add locale to alternate languages
|
|
169
|
+
alternates: {
|
|
170
|
+
languages: {
|
|
171
|
+
[locale]: `/${locale}`,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Types for Next.js App Router
|
|
3
|
+
*
|
|
4
|
+
* Integrates next-intl with @djangocfg/i18n
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { LocaleCode, I18nTranslations } from '@djangocfg/i18n';
|
|
8
|
+
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
// Configuration Types
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export interface I18nConfig {
|
|
14
|
+
/** Supported locales */
|
|
15
|
+
locales: LocaleCode[];
|
|
16
|
+
/** Default locale (fallback) */
|
|
17
|
+
defaultLocale: LocaleCode;
|
|
18
|
+
/** Locale prefix strategy */
|
|
19
|
+
localePrefix?: 'always' | 'as-needed' | 'never';
|
|
20
|
+
/** Cookie name for locale preference */
|
|
21
|
+
cookieName?: string;
|
|
22
|
+
/** Paths to exclude from locale routing (e.g., '/api', '/_next') */
|
|
23
|
+
excludedPaths?: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface I18nPluginOptions extends I18nConfig {
|
|
27
|
+
/** Path to i18n request config file (default: './src/i18n/request.ts' or './i18n/request.ts') */
|
|
28
|
+
requestConfig?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
|
+
// Translation Types
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/** Messages structure - compatible with next-intl */
|
|
36
|
+
export type Messages = I18nTranslations & {
|
|
37
|
+
[namespace: string]: Record<string, unknown>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Locale-specific messages loader */
|
|
41
|
+
export type MessagesLoader = (locale: LocaleCode) => Promise<Messages> | Messages;
|
|
42
|
+
|
|
43
|
+
/** Extension translations that can be merged */
|
|
44
|
+
export interface ExtensionMessages {
|
|
45
|
+
namespace: string;
|
|
46
|
+
messages: Record<LocaleCode, Record<string, unknown>>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
// Provider Types
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
export interface I18nProviderProps {
|
|
54
|
+
/** Current locale */
|
|
55
|
+
locale: LocaleCode;
|
|
56
|
+
/** Translation messages */
|
|
57
|
+
messages: Messages;
|
|
58
|
+
/** Time zone for date/time formatting */
|
|
59
|
+
timeZone?: string;
|
|
60
|
+
/** Now value for relative time */
|
|
61
|
+
now?: Date;
|
|
62
|
+
/** Children */
|
|
63
|
+
children: React.ReactNode;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
+
// Routing Types
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
export interface LocaleParams {
|
|
71
|
+
locale: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface LocaleLayoutProps {
|
|
75
|
+
children: React.ReactNode;
|
|
76
|
+
params: Promise<LocaleParams>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface LocalePageProps {
|
|
80
|
+
params: Promise<LocaleParams>;
|
|
81
|
+
searchParams?: Promise<Record<string, string | string[] | undefined>>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
// Re-exports from @djangocfg/i18n
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
export type { LocaleCode, I18nTranslations } from '@djangocfg/i18n';
|