@djangocfg/i18n 2.1.190 → 2.1.192
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 +72 -2
- package/dist/index.d.mts +23 -1
- package/package.json +3 -3
- package/src/index.ts +5 -0
- package/src/utils/index.ts +1 -1
- package/src/utils/path-keys.ts +39 -0
package/README.md
CHANGED
|
@@ -181,16 +181,86 @@ function MyComponent() {
|
|
|
181
181
|
}
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
## Type-safe next-intl Integration
|
|
185
|
+
|
|
186
|
+
Override `useTranslations` in your app's `global.d.ts` to get compile-time key validation.
|
|
187
|
+
|
|
188
|
+
### 1. Merge translations (flat, no namespace)
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
// i18n/request.ts
|
|
192
|
+
import { en as baseEn } from '@djangocfg/i18n/locales';
|
|
193
|
+
import { en as appEn } from './locales';
|
|
194
|
+
|
|
195
|
+
const locales = {
|
|
196
|
+
en: { ...baseEn, ...appEn },
|
|
197
|
+
};
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 2. Add `global.d.ts` to your app root
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
// global.d.ts
|
|
204
|
+
type _Messages = import('@djangocfg/i18n').I18nTranslations &
|
|
205
|
+
import('./i18n/locales/types').AppTranslations;
|
|
206
|
+
|
|
207
|
+
type _NSKeys = import('@djangocfg/i18n').NamespaceKeys<
|
|
208
|
+
_Messages,
|
|
209
|
+
import('@djangocfg/i18n').NestedKeyOf<_Messages>
|
|
210
|
+
>;
|
|
211
|
+
|
|
212
|
+
declare module 'next-intl' {
|
|
213
|
+
export function useTranslations<NS extends _NSKeys>(
|
|
214
|
+
namespace: NS,
|
|
215
|
+
): import('@djangocfg/i18n').IntlTranslator<_Messages, NS>;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 3. Include in tsconfig.json
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{ "include": ["global.d.ts", "app/**/*.ts", ...] }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Now invalid keys and namespaces produce compile errors:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
const t = useTranslations('machines');
|
|
229
|
+
t('title'); // OK
|
|
230
|
+
t('dialogs.delete.title'); // OK
|
|
231
|
+
t('NONEXISTENT'); // Error!
|
|
232
|
+
|
|
233
|
+
useTranslations('BOGUS'); // Error!
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
> **Why not `use-intl` AppConfig?** The standard `declare module 'use-intl' { interface AppConfig }` augmentation doesn't propagate through pnpm's nested `node_modules`. Overriding `useTranslations` in `next-intl` directly works reliably.
|
|
237
|
+
|
|
238
|
+
### Exported utility types
|
|
239
|
+
|
|
240
|
+
| Type | Description |
|
|
241
|
+
|------|-------------|
|
|
242
|
+
| `NestedKeyOf<T>` | All dot-separated paths (leaves + namespaces) |
|
|
243
|
+
| `NestedValueOf<T, P>` | Resolve value type by dot path |
|
|
244
|
+
| `NamespaceKeys<T, A>` | Paths resolving to objects (valid namespaces) |
|
|
245
|
+
| `MessageKeys<T, A>` | Paths resolving to strings (valid keys) |
|
|
246
|
+
| `IntlTranslator<M, NS>` | Type-safe translator with `t()`, `rich()`, `has()`, `raw()` |
|
|
247
|
+
|
|
184
248
|
## Extending Translations
|
|
185
249
|
|
|
186
|
-
###
|
|
250
|
+
### Spread merge (recommended for next-intl apps)
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import { en as baseEn } from '@djangocfg/i18n/locales';
|
|
254
|
+
const messages = { ...baseEn, ...appEn };
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### mergeTranslations() (deep merge with overrides)
|
|
187
258
|
|
|
188
259
|
```tsx
|
|
189
260
|
import { mergeTranslations, ru } from '@djangocfg/i18n'
|
|
190
261
|
|
|
191
262
|
const customRu = mergeTranslations(ru, {
|
|
192
263
|
ui: { select: { placeholder: 'Выберите...' } },
|
|
193
|
-
app: { title: 'Мое приложение' }
|
|
194
264
|
})
|
|
195
265
|
```
|
|
196
266
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
1
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -72,6 +73,27 @@ type TypedTranslationFn<T extends object> = TranslationFn<T>;
|
|
|
72
73
|
* ```
|
|
73
74
|
*/
|
|
74
75
|
type TranslationKeys<T extends object> = PathKeys<T>;
|
|
76
|
+
/** All dot-separated paths (both leaves and namespaces) */
|
|
77
|
+
type NestedKeyOf<T> = T extends object ? {
|
|
78
|
+
[K in keyof T & string]: K | `${K}.${NestedKeyOf<T[K]>}`;
|
|
79
|
+
}[keyof T & string] : never;
|
|
80
|
+
/** Resolve nested value by dot-separated path */
|
|
81
|
+
type NestedValueOf<T, P extends string> = P extends `${infer K}.${infer R}` ? K extends keyof T ? NestedValueOf<T[K], R> : never : P extends keyof T ? T[P] : never;
|
|
82
|
+
/** Paths that resolve to objects (valid useTranslations namespaces) */
|
|
83
|
+
type NamespaceKeys<T, A extends string> = {
|
|
84
|
+
[K in A]: NestedValueOf<T, K> extends string ? never : K;
|
|
85
|
+
}[A];
|
|
86
|
+
/** Paths that resolve to strings (valid t() keys) */
|
|
87
|
+
type MessageKeys<T, A extends string> = {
|
|
88
|
+
[K in A]: NestedValueOf<T, K> extends string ? K : never;
|
|
89
|
+
}[A];
|
|
90
|
+
/** Type-safe translator returned by useTranslations(namespace) */
|
|
91
|
+
type IntlTranslator<Messages, NS extends NamespaceKeys<Messages, NestedKeyOf<Messages>>> = {
|
|
92
|
+
<K extends MessageKeys<NestedValueOf<Messages, NS>, NestedKeyOf<NestedValueOf<Messages, NS>>>>(key: K, values?: Record<string, unknown>): string;
|
|
93
|
+
rich<K extends MessageKeys<NestedValueOf<Messages, NS>, NestedKeyOf<NestedValueOf<Messages, NS>>>>(key: K, values?: Record<string, unknown>): react.ReactNode;
|
|
94
|
+
has(key: NestedKeyOf<NestedValueOf<Messages, NS>>): boolean;
|
|
95
|
+
raw(key: NestedKeyOf<NestedValueOf<Messages, NS>>): unknown;
|
|
96
|
+
};
|
|
75
97
|
|
|
76
98
|
/**
|
|
77
99
|
* All valid translation keys for base I18nTranslations
|
|
@@ -1019,4 +1041,4 @@ declare function mergeTranslations<T extends Record<string, unknown> = Record<st
|
|
|
1019
1041
|
*/
|
|
1020
1042
|
declare function createTranslations(base: I18nTranslations, ...overrides: PartialTranslations[]): I18nTranslations;
|
|
1021
1043
|
|
|
1022
|
-
export { type DeepPartial, type I18nContextValue, type I18nKeys, I18nProvider, type I18nProviderProps, type I18nTranslationFn, type I18nTranslations, type LocaleCode, type PartialTranslations, type PathKeys, type TranslationFn, type TranslationKeys, type TypedTranslationFn, createTranslations, en, getT, interpolate, ko, mergeTranslations, ru, useI18n, useLocale, useT, useTranslation, useTypedT };
|
|
1044
|
+
export { type DeepPartial, type I18nContextValue, type I18nKeys, I18nProvider, type I18nProviderProps, type I18nTranslationFn, type I18nTranslations, type IntlTranslator, type LocaleCode, type MessageKeys, type NamespaceKeys, type NestedKeyOf, type NestedValueOf, type PartialTranslations, type PathKeys, type TranslationFn, type TranslationKeys, type TypedTranslationFn, createTranslations, en, getT, interpolate, ko, mergeTranslations, ru, useI18n, useLocale, useT, useTranslation, useTypedT };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/i18n",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.192",
|
|
4
4
|
"description": "Lightweight i18n library for @djangocfg packages with built-in translations for English, Russian, and Korean",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
@@ -72,13 +72,13 @@
|
|
|
72
72
|
"react": "^18.0.0 || ^19.0.0"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@djangocfg/llm": "^2.1.
|
|
75
|
+
"@djangocfg/llm": "^2.1.192",
|
|
76
76
|
"citty": "^0.1.6",
|
|
77
77
|
"consola": "^3.4.0",
|
|
78
78
|
"jiti": "^2.4.2"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
81
|
+
"@djangocfg/typescript-config": "^2.1.192",
|
|
82
82
|
"@types/node": "^25.2.3",
|
|
83
83
|
"@types/react": "^19.1.0",
|
|
84
84
|
"eslint": "^9.37.0",
|
package/src/index.ts
CHANGED
package/src/utils/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { interpolate } from './interpolate'
|
|
2
2
|
export { mergeTranslations, createTranslations } from './merge'
|
|
3
3
|
export { getNestedValue } from './get-value'
|
|
4
|
-
export type { PathKeys, TranslationFn, TypedTranslationFn, TranslationKeys } from './path-keys'
|
|
4
|
+
export type { PathKeys, TranslationFn, TypedTranslationFn, TranslationKeys, NestedKeyOf, NestedValueOf, NamespaceKeys, MessageKeys, IntlTranslator } from './path-keys'
|
package/src/utils/path-keys.ts
CHANGED
|
@@ -83,3 +83,42 @@ export type TypedTranslationFn<T extends object> = TranslationFn<T>
|
|
|
83
83
|
* ```
|
|
84
84
|
*/
|
|
85
85
|
export type TranslationKeys<T extends object> = PathKeys<T>
|
|
86
|
+
|
|
87
|
+
// --- next-intl augmentation utilities ---
|
|
88
|
+
// Used in app global.d.ts to override useTranslations with type-safe keys.
|
|
89
|
+
// The standard use-intl AppConfig augmentation doesn't propagate through
|
|
90
|
+
// pnpm's nested node_modules, so apps override useTranslations directly.
|
|
91
|
+
|
|
92
|
+
/** All dot-separated paths (both leaves and namespaces) */
|
|
93
|
+
export type NestedKeyOf<T> = T extends object
|
|
94
|
+
? { [K in keyof T & string]: K | `${K}.${NestedKeyOf<T[K]>}` }[keyof T & string]
|
|
95
|
+
: never
|
|
96
|
+
|
|
97
|
+
/** Resolve nested value by dot-separated path */
|
|
98
|
+
export type NestedValueOf<T, P extends string> = P extends `${infer K}.${infer R}`
|
|
99
|
+
? K extends keyof T ? NestedValueOf<T[K], R> : never
|
|
100
|
+
: P extends keyof T ? T[P] : never
|
|
101
|
+
|
|
102
|
+
/** Paths that resolve to objects (valid useTranslations namespaces) */
|
|
103
|
+
export type NamespaceKeys<T, A extends string> = {
|
|
104
|
+
[K in A]: NestedValueOf<T, K> extends string ? never : K
|
|
105
|
+
}[A]
|
|
106
|
+
|
|
107
|
+
/** Paths that resolve to strings (valid t() keys) */
|
|
108
|
+
export type MessageKeys<T, A extends string> = {
|
|
109
|
+
[K in A]: NestedValueOf<T, K> extends string ? K : never
|
|
110
|
+
}[A]
|
|
111
|
+
|
|
112
|
+
/** Type-safe translator returned by useTranslations(namespace) */
|
|
113
|
+
export type IntlTranslator<Messages, NS extends NamespaceKeys<Messages, NestedKeyOf<Messages>>> = {
|
|
114
|
+
<K extends MessageKeys<NestedValueOf<Messages, NS>, NestedKeyOf<NestedValueOf<Messages, NS>>>>(
|
|
115
|
+
key: K,
|
|
116
|
+
values?: Record<string, unknown>,
|
|
117
|
+
): string
|
|
118
|
+
rich<K extends MessageKeys<NestedValueOf<Messages, NS>, NestedKeyOf<NestedValueOf<Messages, NS>>>>(
|
|
119
|
+
key: K,
|
|
120
|
+
values?: Record<string, unknown>,
|
|
121
|
+
): import('react').ReactNode
|
|
122
|
+
has(key: NestedKeyOf<NestedValueOf<Messages, NS>>): boolean
|
|
123
|
+
raw(key: NestedKeyOf<NestedValueOf<Messages, NS>>): unknown
|
|
124
|
+
}
|