@umituz/react-native-localization 2.5.0 → 2.7.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.
Files changed (42) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +0 -0
  3. package/package.json +1 -1
  4. package/src/domain/repositories/ILocalizationRepository.ts +0 -0
  5. package/src/index.ts +0 -0
  6. package/src/infrastructure/components/LanguageSwitcher.tsx +0 -0
  7. package/src/infrastructure/components/LocalizationProvider.tsx +11 -3
  8. package/src/infrastructure/components/useLanguageNavigation.ts +0 -0
  9. package/src/infrastructure/config/I18nInitializer.ts +13 -66
  10. package/src/infrastructure/config/NamespaceResolver.ts +36 -0
  11. package/src/infrastructure/config/ResourceBuilder.ts +43 -0
  12. package/src/infrastructure/config/TranslationLoader.ts +0 -0
  13. package/src/infrastructure/config/i18n.ts +0 -0
  14. package/src/infrastructure/config/languages.ts +0 -0
  15. package/src/infrastructure/config/languagesData.ts +0 -0
  16. package/src/infrastructure/hooks/TranslationHook.ts +0 -0
  17. package/src/infrastructure/hooks/useTranslation.ts +34 -5
  18. package/src/infrastructure/locales/en-US/alerts.json +0 -0
  19. package/src/infrastructure/locales/en-US/auth.json +0 -0
  20. package/src/infrastructure/locales/en-US/branding.json +0 -0
  21. package/src/infrastructure/locales/en-US/clipboard.json +0 -0
  22. package/src/infrastructure/locales/en-US/common.json +0 -0
  23. package/src/infrastructure/locales/en-US/datetime.json +0 -0
  24. package/src/infrastructure/locales/en-US/device.json +0 -0
  25. package/src/infrastructure/locales/en-US/editor.json +0 -0
  26. package/src/infrastructure/locales/en-US/errors.json +0 -0
  27. package/src/infrastructure/locales/en-US/general.json +0 -0
  28. package/src/infrastructure/locales/en-US/goals.json +0 -0
  29. package/src/infrastructure/locales/en-US/haptics.json +0 -0
  30. package/src/infrastructure/locales/en-US/home.json +0 -0
  31. package/src/infrastructure/locales/en-US/index.ts +0 -0
  32. package/src/infrastructure/locales/en-US/navigation.json +0 -0
  33. package/src/infrastructure/locales/en-US/onboarding.json +0 -0
  34. package/src/infrastructure/locales/en-US/projects.json +0 -0
  35. package/src/infrastructure/locales/en-US/settings.json +0 -0
  36. package/src/infrastructure/locales/en-US/sharing.json +0 -0
  37. package/src/infrastructure/locales/en-US/templates.json +0 -0
  38. package/src/infrastructure/scripts/createLocaleLoaders.js +0 -0
  39. package/src/infrastructure/storage/AsyncStorageWrapper.ts +0 -0
  40. package/src/infrastructure/storage/LanguageInitializer.ts +0 -0
  41. package/src/infrastructure/storage/LanguageSwitcher.ts +0 -0
  42. package/src/infrastructure/storage/LocalizationStore.ts +0 -0
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-localization",
3
- "version": "2.5.0",
3
+ "version": "2.7.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
File without changes
@@ -1,21 +1,29 @@
1
1
  /**
2
2
  * LocalizationProvider Component
3
- * Initializes localization system on mount
3
+ * Initializes localization system with app translations
4
4
  */
5
5
 
6
6
  import React, { useEffect, ReactNode } from 'react';
7
7
  import { useLocalizationStore } from '../storage/LocalizationStore';
8
+ import { I18nInitializer } from '../config/I18nInitializer';
8
9
 
9
10
  interface LocalizationProviderProps {
10
11
  children: ReactNode;
12
+ translations: Record<string, any>;
13
+ defaultLanguage?: string;
11
14
  }
12
15
 
13
- export const LocalizationProvider: React.FC<LocalizationProviderProps> = ({ children }) => {
16
+ export const LocalizationProvider: React.FC<LocalizationProviderProps> = ({
17
+ children,
18
+ translations,
19
+ defaultLanguage = 'en-US',
20
+ }) => {
14
21
  const initialize = useLocalizationStore((state) => state.initialize);
15
22
 
16
23
  useEffect(() => {
24
+ I18nInitializer.initialize(translations, defaultLanguage);
17
25
  initialize();
18
- }, [initialize]);
26
+ }, [translations, defaultLanguage, initialize]);
19
27
 
20
28
  return <>{children}</>;
21
29
  };
@@ -8,70 +8,16 @@
8
8
  import i18n from 'i18next';
9
9
  import { initReactI18next } from 'react-i18next';
10
10
  import { DEFAULT_LANGUAGE } from './languages';
11
- import { TranslationLoader } from './TranslationLoader';
12
-
13
- const DEFAULT_NAMESPACE = 'common';
11
+ import { ResourceBuilder } from './ResourceBuilder';
12
+ import { NamespaceResolver } from './NamespaceResolver';
14
13
 
15
14
  export class I18nInitializer {
16
15
  private static reactI18nextInitialized = false;
17
- private static appTranslations: Record<string, any> = {};
18
-
19
- /**
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
28
- */
29
- private static buildResources(): Record<string, Record<string, any>> {
30
- const packageTranslations = TranslationLoader.loadPackageTranslations();
31
-
32
- const resources: Record<string, Record<string, any>> = {
33
- 'en-US': {},
34
- };
35
-
36
- const enUSPackage = packageTranslations['en-US'] || {};
37
-
38
- // Add package namespaces
39
- for (const [namespace, translations] of Object.entries(enUSPackage)) {
40
- resources['en-US'][namespace] = translations;
41
- }
42
16
 
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
-
55
- return resources;
56
- }
57
-
58
- private static getNamespaces(): string[] {
59
- const packageTranslations = TranslationLoader.loadPackageTranslations();
60
- const enUSPackage = packageTranslations['en-US'] || {};
61
-
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);
69
- }
70
-
71
- return Array.from(namespaces);
72
- }
73
-
74
- static initialize(): void {
17
+ static initialize(
18
+ appTranslations: Record<string, any>,
19
+ languageCode: string = DEFAULT_LANGUAGE
20
+ ): void {
75
21
  if (i18n.isInitialized) {
76
22
  return;
77
23
  }
@@ -82,16 +28,17 @@ export class I18nInitializer {
82
28
  this.reactI18nextInitialized = true;
83
29
  }
84
30
 
85
- const resources = this.buildResources();
86
- const namespaces = this.getNamespaces();
31
+ const resources = ResourceBuilder.buildResources(appTranslations, languageCode);
32
+ const namespaces = NamespaceResolver.getNamespaces(appTranslations, languageCode);
33
+ const defaultNamespace = NamespaceResolver.getDefaultNamespace();
87
34
 
88
35
  i18n.init({
89
36
  resources,
90
- lng: DEFAULT_LANGUAGE,
91
- fallbackLng: DEFAULT_LANGUAGE,
37
+ lng: languageCode,
38
+ fallbackLng: languageCode,
92
39
  ns: namespaces,
93
- defaultNS: DEFAULT_NAMESPACE,
94
- fallbackNS: DEFAULT_NAMESPACE,
40
+ defaultNS: defaultNamespace,
41
+ fallbackNS: defaultNamespace,
95
42
  interpolation: { escapeValue: false },
96
43
  react: { useSuspense: false },
97
44
  compatibilityJSON: 'v3',
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Namespace Resolver
3
+ * Resolves available namespaces from translations
4
+ */
5
+
6
+ import { TranslationLoader } from './TranslationLoader';
7
+
8
+ const DEFAULT_NAMESPACE = 'common';
9
+
10
+ export class NamespaceResolver {
11
+ /**
12
+ * Get all available namespaces from package and app translations
13
+ */
14
+ static getNamespaces(
15
+ appTranslations: Record<string, any>,
16
+ languageCode: string
17
+ ): string[] {
18
+ const packageTranslations = TranslationLoader.loadPackageTranslations();
19
+ const packageLang = packageTranslations[languageCode] || {};
20
+
21
+ const namespaces = new Set([
22
+ ...Object.keys(packageLang),
23
+ ...Object.keys(appTranslations),
24
+ ]);
25
+
26
+ if (!namespaces.has(DEFAULT_NAMESPACE)) {
27
+ namespaces.add(DEFAULT_NAMESPACE);
28
+ }
29
+
30
+ return Array.from(namespaces);
31
+ }
32
+
33
+ static getDefaultNamespace(): string {
34
+ return DEFAULT_NAMESPACE;
35
+ }
36
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Resource Builder
3
+ * Builds i18n resources from package and app translations
4
+ */
5
+
6
+ import { TranslationLoader } from './TranslationLoader';
7
+
8
+ export class ResourceBuilder {
9
+ /**
10
+ * Build resources with package + app translations
11
+ */
12
+ static buildResources(
13
+ appTranslations: Record<string, any>,
14
+ languageCode: string
15
+ ): Record<string, Record<string, any>> {
16
+ const packageTranslations = TranslationLoader.loadPackageTranslations();
17
+
18
+ const resources: Record<string, Record<string, any>> = {
19
+ [languageCode]: {},
20
+ };
21
+
22
+ const packageLang = packageTranslations[languageCode] || {};
23
+
24
+ // Add package namespaces
25
+ for (const [namespace, translations] of Object.entries(packageLang)) {
26
+ resources[languageCode][namespace] = translations;
27
+ }
28
+
29
+ // Merge app translations (app overrides package)
30
+ for (const [namespace, translations] of Object.entries(appTranslations)) {
31
+ if (resources[languageCode][namespace]) {
32
+ resources[languageCode][namespace] = TranslationLoader.mergeTranslations(
33
+ resources[languageCode][namespace],
34
+ translations
35
+ );
36
+ } else {
37
+ resources[languageCode][namespace] = translations;
38
+ }
39
+ }
40
+
41
+ return resources;
42
+ }
43
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -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
  };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes