@umituz/react-native-localization 2.4.0 → 2.6.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.4.0",
3
+ "version": "2.6.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 with namespace support
5
- * - Loads package translations
6
- * - Loads app translations (merges with package)
7
- * - Namespace-based organization (common, auth, etc.)
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,13 +14,20 @@ 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 with package + app translations merged
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
- const appTranslations = TranslationLoader.loadAppTranslations();
26
31
 
27
32
  const resources: Record<string, Record<string, any>> = {
28
33
  'en-US': {},
@@ -35,8 +40,8 @@ export class I18nInitializer {
35
40
  resources['en-US'][namespace] = translations;
36
41
  }
37
42
 
38
- // Merge app namespaces (app overrides package)
39
- for (const [namespace, translations] of Object.entries(appTranslations)) {
43
+ // Merge app translations (app overrides package)
44
+ for (const [namespace, translations] of Object.entries(this.appTranslations)) {
40
45
  if (resources['en-US'][namespace]) {
41
46
  resources['en-US'][namespace] = TranslationLoader.mergeTranslations(
42
47
  resources['en-US'][namespace],
@@ -52,12 +57,11 @@ export class I18nInitializer {
52
57
 
53
58
  private static getNamespaces(): string[] {
54
59
  const packageTranslations = TranslationLoader.loadPackageTranslations();
55
- const appTranslations = TranslationLoader.loadAppTranslations();
56
-
57
60
  const enUSPackage = packageTranslations['en-US'] || {};
61
+
58
62
  const namespaces = new Set([
59
63
  ...Object.keys(enUSPackage),
60
- ...Object.keys(appTranslations),
64
+ ...Object.keys(this.appTranslations),
61
65
  ]);
62
66
 
63
67
  if (!namespaces.has(DEFAULT_NAMESPACE)) {
@@ -106,9 +110,9 @@ export class I18nInitializer {
106
110
  }
107
111
 
108
112
  /**
109
- * Add translation resources at runtime
113
+ * Add translations at runtime
110
114
  */
111
- static addTranslationResources(
115
+ static addTranslations(
112
116
  languageCode: string,
113
117
  namespaceResources: Record<string, any>
114
118
  ): void {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Translation Loader
3
3
  *
4
- * Loads translations from package and app
4
+ * Loads package translations
5
5
  */
6
6
 
7
7
  export class TranslationLoader {
@@ -19,30 +19,7 @@ export class TranslationLoader {
19
19
  }
20
20
 
21
21
  /**
22
- * Load app translations from common paths
23
- */
24
- static loadAppTranslations(): Record<string, any> {
25
- const paths = [
26
- '@/domains/localization/infrastructure/locales/en-US',
27
- './src/domains/localization/infrastructure/locales/en-US',
28
- '../../../src/domains/localization/infrastructure/locales/en-US',
29
- ];
30
-
31
- for (const path of paths) {
32
- try {
33
- // eslint-disable-next-line @typescript-eslint/no-require-imports
34
- const translations = require(path);
35
- return translations.default || translations;
36
- } catch {
37
- continue;
38
- }
39
- }
40
-
41
- return {};
42
- }
43
-
44
- /**
45
- * Deep merge translations (app overrides package)
22
+ * Deep merge translations (override wins)
46
23
  */
47
24
  static mergeTranslations(base: any, override: any): any {
48
25
  if (!override || Object.keys(override).length === 0) {
@@ -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;
@@ -5,22 +5,51 @@
5
5
  * - React i18next integration
6
6
  * - Direct i18n fallback
7
7
  * - Type-safe translation function
8
+ * - Auto-namespace detection from dot notation
8
9
  */
9
10
 
10
- import { useTranslation } from 'react-i18next';
11
11
  import i18n from '../config/i18n';
12
12
 
13
13
  /**
14
14
  * Hook for translation functionality
15
+ * Supports both formats:
16
+ * - t('namespace:key.subkey') - explicit namespace
17
+ * - t('namespace.key.subkey') - auto-detected namespace (first segment before dot)
15
18
  */
16
19
  export const useTranslationFunction = (): ((key: string, options?: any) => string) => {
17
- // Use direct i18n.t for reliability (no React context issues)
18
20
  return (key: string, options?: any): string => {
19
- if (i18n.isInitialized && typeof i18n.t === 'function') {
21
+ if (!i18n.isInitialized || typeof i18n.t !== 'function') {
22
+ return key;
23
+ }
24
+
25
+ // If key already has namespace separator (:), use as-is
26
+ if (key.includes(':')) {
20
27
  const result = i18n.t(key, options);
21
28
  return typeof result === 'string' ? result : String(result);
22
29
  }
23
- // Final fallback: return key
24
- return key;
30
+
31
+ // Auto-detect namespace from first dot segment
32
+ // e.g., 'settings.appearance.title' -> namespace: 'settings', key: 'appearance.title'
33
+ const firstDotIndex = key.indexOf('.');
34
+ if (firstDotIndex > 0) {
35
+ const potentialNamespace = key.substring(0, firstDotIndex);
36
+ const restOfKey = key.substring(firstDotIndex + 1);
37
+
38
+ // Check if this namespace exists in i18n resources
39
+ const hasNamespace = i18n.hasResourceBundle(i18n.language, potentialNamespace);
40
+
41
+ if (hasNamespace) {
42
+ const namespacedKey = `${potentialNamespace}:${restOfKey}`;
43
+ const result = i18n.t(namespacedKey, options);
44
+ // If translation found (not same as key), return it
45
+ if (result !== namespacedKey && result !== restOfKey) {
46
+ return typeof result === 'string' ? result : String(result);
47
+ }
48
+ }
49
+ }
50
+
51
+ // Fallback: try original key as-is
52
+ const result = i18n.t(key, options);
53
+ return typeof result === 'string' ? result : String(result);
25
54
  };
26
55
  };