@umituz/react-native-localization 2.3.0 → 2.5.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.
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* React Native Localization
|
|
3
|
-
*
|
|
3
|
+
* i18n with namespace support for React Native apps
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Hooks
|
|
@@ -13,10 +13,11 @@ export { LanguageSwitcher } from './infrastructure/components/LanguageSwitcher';
|
|
|
13
13
|
export { useLanguageNavigation } from './infrastructure/components/useLanguageNavigation';
|
|
14
14
|
|
|
15
15
|
// Configuration
|
|
16
|
-
export { default as i18n
|
|
16
|
+
export { default as i18n } from './infrastructure/config/i18n';
|
|
17
|
+
export { I18nInitializer } from './infrastructure/config/I18nInitializer';
|
|
17
18
|
export {
|
|
18
19
|
SUPPORTED_LANGUAGES,
|
|
19
|
-
LANGUAGES,
|
|
20
|
+
LANGUAGES,
|
|
20
21
|
DEFAULT_LANGUAGE,
|
|
21
22
|
getLanguageByCode,
|
|
22
23
|
isLanguageSupported,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* i18n Initializer
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Namespace-based organization (common, auth, etc.)
|
|
7
|
-
* - React i18next integration
|
|
4
|
+
* Namespace-based i18n configuration
|
|
5
|
+
* Usage: t('namespace:key') e.g., t('common:cancel')
|
|
8
6
|
*/
|
|
9
7
|
|
|
10
8
|
import i18n from 'i18next';
|
|
@@ -16,48 +14,63 @@ const DEFAULT_NAMESPACE = 'common';
|
|
|
16
14
|
|
|
17
15
|
export class I18nInitializer {
|
|
18
16
|
private static reactI18nextInitialized = false;
|
|
17
|
+
private static appTranslations: Record<string, any> = {};
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
|
-
*
|
|
20
|
+
* Register app translations (call before initialize)
|
|
21
|
+
*/
|
|
22
|
+
static registerAppTranslations(translations: Record<string, any>): void {
|
|
23
|
+
this.appTranslations = translations;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Build resources with package + registered app translations
|
|
22
28
|
*/
|
|
23
29
|
private static buildResources(): Record<string, Record<string, any>> {
|
|
24
30
|
const packageTranslations = TranslationLoader.loadPackageTranslations();
|
|
25
31
|
|
|
26
|
-
// Create namespace-based resources structure
|
|
27
32
|
const resources: Record<string, Record<string, any>> = {
|
|
28
33
|
'en-US': {},
|
|
29
34
|
};
|
|
30
35
|
|
|
31
|
-
// Package translations are already in namespace format (alerts, auth, etc.)
|
|
32
36
|
const enUSPackage = packageTranslations['en-US'] || {};
|
|
33
37
|
|
|
34
|
-
//
|
|
38
|
+
// Add package namespaces
|
|
35
39
|
for (const [namespace, translations] of Object.entries(enUSPackage)) {
|
|
36
40
|
resources['en-US'][namespace] = translations;
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
// Merge app translations (app overrides package)
|
|
44
|
+
for (const [namespace, translations] of Object.entries(this.appTranslations)) {
|
|
45
|
+
if (resources['en-US'][namespace]) {
|
|
46
|
+
resources['en-US'][namespace] = TranslationLoader.mergeTranslations(
|
|
47
|
+
resources['en-US'][namespace],
|
|
48
|
+
translations
|
|
49
|
+
);
|
|
50
|
+
} else {
|
|
51
|
+
resources['en-US'][namespace] = translations;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
return resources;
|
|
40
56
|
}
|
|
41
57
|
|
|
42
|
-
/**
|
|
43
|
-
* Get all available namespaces from package translations
|
|
44
|
-
*/
|
|
45
58
|
private static getNamespaces(): string[] {
|
|
46
59
|
const packageTranslations = TranslationLoader.loadPackageTranslations();
|
|
47
60
|
const enUSPackage = packageTranslations['en-US'] || {};
|
|
48
|
-
const namespaces = Object.keys(enUSPackage);
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
const namespaces = new Set([
|
|
63
|
+
...Object.keys(enUSPackage),
|
|
64
|
+
...Object.keys(this.appTranslations),
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
if (!namespaces.has(DEFAULT_NAMESPACE)) {
|
|
68
|
+
namespaces.add(DEFAULT_NAMESPACE);
|
|
53
69
|
}
|
|
54
70
|
|
|
55
|
-
return namespaces;
|
|
71
|
+
return Array.from(namespaces);
|
|
56
72
|
}
|
|
57
73
|
|
|
58
|
-
/**
|
|
59
|
-
* Initialize i18next with namespace support
|
|
60
|
-
*/
|
|
61
74
|
static initialize(): void {
|
|
62
75
|
if (i18n.isInitialized) {
|
|
63
76
|
return;
|
|
@@ -79,39 +92,27 @@ export class I18nInitializer {
|
|
|
79
92
|
ns: namespaces,
|
|
80
93
|
defaultNS: DEFAULT_NAMESPACE,
|
|
81
94
|
fallbackNS: DEFAULT_NAMESPACE,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
escapeValue: false,
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
react: {
|
|
88
|
-
useSuspense: false,
|
|
89
|
-
},
|
|
90
|
-
|
|
95
|
+
interpolation: { escapeValue: false },
|
|
96
|
+
react: { useSuspense: false },
|
|
91
97
|
compatibilityJSON: 'v3',
|
|
92
98
|
pluralSeparator: '_',
|
|
93
99
|
keySeparator: '.',
|
|
94
100
|
nsSeparator: ':',
|
|
95
|
-
|
|
96
101
|
saveMissing: false,
|
|
97
102
|
missingKeyHandler: false,
|
|
98
|
-
|
|
99
103
|
debug: false,
|
|
100
104
|
});
|
|
101
|
-
|
|
102
105
|
} catch (error) {
|
|
103
106
|
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
104
|
-
console.error('[Localization]
|
|
107
|
+
console.error('[Localization] init error:', error);
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
/**
|
|
110
|
-
* Add
|
|
111
|
-
* @param languageCode - Language code (e.g., 'en-US')
|
|
112
|
-
* @param namespaceResources - Object with namespace keys and translation objects
|
|
113
|
+
* Add translations at runtime
|
|
113
114
|
*/
|
|
114
|
-
static
|
|
115
|
+
static addTranslations(
|
|
115
116
|
languageCode: string,
|
|
116
117
|
namespaceResources: Record<string, any>
|
|
117
118
|
): void {
|
|
@@ -1,58 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Translation Loader
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Loads package translations
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export class TranslationLoader {
|
|
8
8
|
/**
|
|
9
|
-
* Load package translations (en-US
|
|
9
|
+
* Load package translations (en-US)
|
|
10
10
|
*/
|
|
11
11
|
static loadPackageTranslations(): Record<string, any> {
|
|
12
12
|
try {
|
|
13
13
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
14
14
|
const translations = require('../locales/en-US');
|
|
15
15
|
return { 'en-US': translations.default || translations };
|
|
16
|
-
} catch
|
|
16
|
+
} catch {
|
|
17
17
|
return { 'en-US': {} };
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Deep merge translations (override wins)
|
|
23
23
|
*/
|
|
24
|
-
static mergeTranslations(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
): any {
|
|
28
|
-
if (!projectTranslations || Object.keys(projectTranslations).length === 0) {
|
|
29
|
-
return packageTranslations;
|
|
24
|
+
static mergeTranslations(base: any, override: any): any {
|
|
25
|
+
if (!override || Object.keys(override).length === 0) {
|
|
26
|
+
return base;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
const merged = { ...
|
|
29
|
+
const merged = { ...base };
|
|
33
30
|
|
|
34
|
-
for (const key in
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
packageTranslations[key] !== null &&
|
|
42
|
-
!Array.isArray(packageTranslations[key])
|
|
43
|
-
) {
|
|
44
|
-
// Deep merge nested objects
|
|
45
|
-
merged[key] = this.mergeTranslations(
|
|
46
|
-
packageTranslations[key],
|
|
47
|
-
projectTranslations[key]
|
|
48
|
-
);
|
|
31
|
+
for (const key in override) {
|
|
32
|
+
if (Object.prototype.hasOwnProperty.call(override, key)) {
|
|
33
|
+
const baseVal = base[key];
|
|
34
|
+
const overrideVal = override[key];
|
|
35
|
+
|
|
36
|
+
if (this.isObject(baseVal) && this.isObject(overrideVal)) {
|
|
37
|
+
merged[key] = this.mergeTranslations(baseVal, overrideVal);
|
|
49
38
|
} else {
|
|
50
|
-
|
|
51
|
-
merged[key] = projectTranslations[key];
|
|
39
|
+
merged[key] = overrideVal;
|
|
52
40
|
}
|
|
53
41
|
}
|
|
54
42
|
}
|
|
55
43
|
|
|
56
44
|
return merged;
|
|
57
45
|
}
|
|
46
|
+
|
|
47
|
+
private static isObject(val: any): boolean {
|
|
48
|
+
return val !== null && typeof val === 'object' && !Array.isArray(val);
|
|
49
|
+
}
|
|
58
50
|
}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* i18n
|
|
2
|
+
* i18n Instance
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Usage: t('namespace:key') e.g., t('common:cancel')
|
|
4
|
+
* Raw i18n instance - use I18nInitializer for configuration
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
import { I18nInitializer } from './I18nInitializer';
|
|
9
7
|
import i18n from 'i18next';
|
|
10
8
|
|
|
11
|
-
I18nInitializer.initialize();
|
|
12
|
-
|
|
13
|
-
export const addTranslationResources = I18nInitializer.addTranslationResources;
|
|
14
9
|
export default i18n;
|