@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-localization",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "English-only localization system for React Native apps with i18n support",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * React Native Localization
3
- * Universal localization system with i18n support for React Native apps
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, addTranslationResources } from './infrastructure/config/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, // Alias for SUPPORTED_LANGUAGES (backward compatibility)
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
- * Handles i18n configuration and initialization with namespace support
5
- * - Auto-discovers project translations
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
- * Build resources object with namespace support
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
- // Each key in packageTranslations is a namespace
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
- // Ensure default namespace is included
51
- if (!namespaces.includes(DEFAULT_NAMESPACE)) {
52
- namespaces.unshift(DEFAULT_NAMESPACE);
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
- interpolation: {
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] i18n initialization error:', error);
107
+ console.error('[Localization] init error:', error);
105
108
  }
106
109
  }
107
110
  }
108
111
 
109
112
  /**
110
- * Add additional translation resources with namespace support
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 addTranslationResources(
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
- * Handles loading of translations from package only
4
+ * Loads package translations
5
5
  */
6
6
 
7
7
  export class TranslationLoader {
8
8
  /**
9
- * Load package translations (en-US only)
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 (error) {
16
+ } catch {
17
17
  return { 'en-US': {} };
18
18
  }
19
19
  }
20
20
 
21
21
  /**
22
- * Merge package defaults with project-specific translations
22
+ * Deep merge translations (override wins)
23
23
  */
24
- static mergeTranslations(
25
- packageTranslations: any,
26
- projectTranslations: any
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 = { ...packageTranslations };
29
+ const merged = { ...base };
33
30
 
34
- for (const key in projectTranslations) {
35
- if (projectTranslations.hasOwnProperty(key)) {
36
- if (
37
- typeof projectTranslations[key] === 'object' &&
38
- projectTranslations[key] !== null &&
39
- !Array.isArray(projectTranslations[key]) &&
40
- typeof packageTranslations[key] === 'object' &&
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
- // Override with project translation
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 Configuration
2
+ * i18n Instance
3
3
  *
4
- * Auto-initializes i18n with namespace support
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;