@djangocfg/i18n 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.
@@ -0,0 +1,88 @@
1
+ import type { I18nTranslations, PartialTranslations } from '../types'
2
+
3
+ type AnyObject = { [key: string]: unknown }
4
+
5
+ /**
6
+ * Deep merge two objects
7
+ */
8
+ function deepMerge(target: AnyObject, source: AnyObject): AnyObject {
9
+ const result = { ...target }
10
+
11
+ for (const key in source) {
12
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
13
+ const sourceValue = source[key]
14
+ const targetValue = result[key]
15
+
16
+ if (
17
+ sourceValue !== null &&
18
+ typeof sourceValue === 'object' &&
19
+ !Array.isArray(sourceValue) &&
20
+ targetValue !== null &&
21
+ typeof targetValue === 'object' &&
22
+ !Array.isArray(targetValue)
23
+ ) {
24
+ // Recursively merge objects
25
+ result[key] = deepMerge(
26
+ targetValue as AnyObject,
27
+ sourceValue as AnyObject
28
+ )
29
+ } else if (sourceValue !== undefined) {
30
+ // Override with source value
31
+ result[key] = sourceValue
32
+ }
33
+ }
34
+ }
35
+
36
+ return result
37
+ }
38
+
39
+ /**
40
+ * Merge base translations with partial overrides or additional namespaces
41
+ *
42
+ * @example
43
+ * // Override existing keys
44
+ * const customRu = mergeTranslations(ru, {
45
+ * ui: {
46
+ * select: {
47
+ * placeholder: 'Выберите марку авто...'
48
+ * }
49
+ * }
50
+ * })
51
+ *
52
+ * @example
53
+ * // Add app-specific namespace
54
+ * const appTranslations = mergeTranslations(en, {
55
+ * hub: {
56
+ * nav: { home: 'Home' }
57
+ * }
58
+ * })
59
+ */
60
+ export function mergeTranslations<T extends Record<string, unknown> = Record<string, unknown>>(
61
+ base: I18nTranslations,
62
+ overrides: PartialTranslations | T
63
+ ): I18nTranslations & T {
64
+ return deepMerge(
65
+ base as unknown as AnyObject,
66
+ overrides as unknown as AnyObject
67
+ ) as unknown as I18nTranslations & T
68
+ }
69
+
70
+ /**
71
+ * Create translations from multiple partial sources
72
+ *
73
+ * @example
74
+ * const translations = createTranslations(
75
+ * en, // base
76
+ * uiOverrides, // UI customizations
77
+ * appStrings // App-specific strings
78
+ * )
79
+ */
80
+ export function createTranslations(
81
+ base: I18nTranslations,
82
+ ...overrides: PartialTranslations[]
83
+ ): I18nTranslations {
84
+ return overrides.reduce<I18nTranslations>(
85
+ (acc, override) => mergeTranslations(acc, override),
86
+ base
87
+ )
88
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Type-safe translation keys
3
+ *
4
+ * Generates a union of all possible dot-notation paths from a nested object type.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * type Translations = {
9
+ * ui: {
10
+ * form: { save: string; cancel: string }
11
+ * select: { placeholder: string }
12
+ * }
13
+ * }
14
+ *
15
+ * type Keys = PathKeys<Translations>
16
+ * // = "ui.form.save" | "ui.form.cancel" | "ui.select.placeholder"
17
+ * ```
18
+ */
19
+
20
+ /**
21
+ * Recursively generates dot-notation paths for nested object types
22
+ *
23
+ * - For leaf values (string), returns the accumulated path
24
+ * - For nested objects, recursively builds paths
25
+ * - Limits depth to prevent infinite recursion with circular types
26
+ */
27
+ export type PathKeys<T, Prefix extends string = '', Depth extends number[] = []> =
28
+ // Limit recursion depth to 10 levels (prevents infinite types)
29
+ Depth['length'] extends 10
30
+ ? never
31
+ : T extends string
32
+ ? Prefix
33
+ : T extends object
34
+ ? {
35
+ [K in keyof T & string]: PathKeys<
36
+ T[K],
37
+ Prefix extends '' ? K : `${Prefix}.${K}`,
38
+ [...Depth, 0]
39
+ >
40
+ }[keyof T & string]
41
+ : never
42
+
43
+ /**
44
+ * Type-safe translation function signature
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * import type { TranslationFn, I18nTranslations } from '@djangocfg/i18n'
49
+ *
50
+ * // Type-safe t function
51
+ * const t: TranslationFn<I18nTranslations> = useT()
52
+ * t('ui.form.save') // OK
53
+ * t('ui.form.typo') // Error: Argument of type '"ui.form.typo"' is not assignable
54
+ * ```
55
+ */
56
+ export type TranslationFn<T extends object> = {
57
+ (key: PathKeys<T>, params?: Record<string, string | number>): string
58
+ }
59
+
60
+ /**
61
+ * Creates a type-safe translation function type for a specific translations object
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * // In extension
66
+ * import type { TypedTranslationFn } from '@djangocfg/i18n'
67
+ * import type { LeadsTranslations } from './types'
68
+ *
69
+ * type LeadsT = TypedTranslationFn<{ leads: LeadsTranslations }>
70
+ * ```
71
+ */
72
+ export type TypedTranslationFn<T extends object> = TranslationFn<T>
73
+
74
+ /**
75
+ * Extracts the keys type from a translations object
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * import type { TranslationKeys, I18nTranslations } from '@djangocfg/i18n'
80
+ *
81
+ * type Keys = TranslationKeys<I18nTranslations>
82
+ * // = "ui.form.save" | "ui.select.placeholder" | ...
83
+ * ```
84
+ */
85
+ export type TranslationKeys<T extends object> = PathKeys<T>