@umituz/react-native-localization 1.2.2 → 1.3.1

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/lib/index.d.ts CHANGED
@@ -4,6 +4,8 @@
4
4
  */
5
5
  export { useLocalization, useLocalizationStore } from './infrastructure/storage/LocalizationStore';
6
6
  export { LocalizationProvider } from './infrastructure/components/LocalizationProvider';
7
+ export { LanguageSwitcher } from './infrastructure/components/LanguageSwitcher';
8
+ export { useLanguageNavigation } from './infrastructure/components/useLanguageNavigation';
7
9
  export { default as i18n } from './infrastructure/config/i18n';
8
10
  export { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE, getLanguageByCode, isLanguageSupported, getDefaultLanguage, getDeviceLocale, } from './infrastructure/config/languages';
9
11
  export type { Language, ILocalizationRepository } from './domain/repositories/ILocalizationRepository';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAGnG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AAGxF,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,GAChB,MAAM,mCAAmC,CAAC;AAG3C,YAAY,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAGnG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAC;AAG1F,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,GAChB,MAAM,mCAAmC,CAAC;AAG3C,YAAY,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC"}
package/lib/index.js CHANGED
@@ -6,6 +6,8 @@
6
6
  export { useLocalization, useLocalizationStore } from './infrastructure/storage/LocalizationStore';
7
7
  // Components
8
8
  export { LocalizationProvider } from './infrastructure/components/LocalizationProvider';
9
+ export { LanguageSwitcher } from './infrastructure/components/LanguageSwitcher';
10
+ export { useLanguageNavigation } from './infrastructure/components/useLanguageNavigation';
9
11
  // Configuration
10
12
  export { default as i18n } from './infrastructure/config/i18n';
11
13
  export { SUPPORTED_LANGUAGES, DEFAULT_LANGUAGE, getLanguageByCode, isLanguageSupported, getDefaultLanguage, getDeviceLocale, } from './infrastructure/config/languages';
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,QAAQ;AACR,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAEnG,aAAa;AACb,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AAExF,gBAAgB;AAChB,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,GAChB,MAAM,mCAAmC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,QAAQ;AACR,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAEnG,aAAa;AACb,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAC;AAE1F,gBAAgB;AAChB,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,GAChB,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ interface LanguageSwitcherProps {
3
+ showName?: boolean;
4
+ showFlag?: boolean;
5
+ color?: string;
6
+ navigationScreen?: string;
7
+ style?: any;
8
+ textStyle?: any;
9
+ }
10
+ export declare const LanguageSwitcher: React.FC<LanguageSwitcherProps>;
11
+ export default LanguageSwitcher;
12
+ //# sourceMappingURL=LanguageSwitcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LanguageSwitcher.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/components/LanguageSwitcher.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,UAAU,qBAAqB;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,SAAS,CAAC,EAAE,GAAG,CAAC;CACjB;AAQD,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAwC5D,CAAC;AAqBF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, Text, StyleSheet } from 'react-native';
3
+ // @ts-ignore - Optional peer dependency
4
+ import { useNavigation } from '@react-navigation/native';
5
+ import { useLocalization } from '../storage/LocalizationStore';
6
+ import { getLanguageByCode, getDefaultLanguage } from '../config/languages';
7
+ const languageSwitcherConfig = {
8
+ defaultIconSize: 20,
9
+ defaultNavigationScreen: 'LanguageSelection',
10
+ hitSlop: { top: 10, bottom: 10, left: 10, right: 10 },
11
+ };
12
+ export const LanguageSwitcher = ({ showName = false, showFlag = true, color, navigationScreen = languageSwitcherConfig.defaultNavigationScreen, style, textStyle, }) => {
13
+ const navigation = useNavigation();
14
+ const { currentLanguage } = useLocalization();
15
+ const currentLang = getLanguageByCode(currentLanguage) || getDefaultLanguage();
16
+ const navigateToLanguageSelection = () => {
17
+ if (navigation && navigationScreen) {
18
+ navigation.navigate(navigationScreen);
19
+ }
20
+ };
21
+ const iconColor = color || '#000000';
22
+ return (<TouchableOpacity style={[styles.container, style]} onPress={navigateToLanguageSelection} activeOpacity={0.7} hitSlop={languageSwitcherConfig.hitSlop}>
23
+ {showFlag && (<Text style={[styles.flag, textStyle]}>{currentLang.flag}</Text>)}
24
+ {showName && (<Text style={[styles.languageName, { color: iconColor }, textStyle]}>
25
+ {currentLang.nativeName}
26
+ </Text>)}
27
+ {!showName && !showFlag && (<Text style={[styles.icon, { color: iconColor }]}>🌐</Text>)}
28
+ </TouchableOpacity>);
29
+ };
30
+ const styles = StyleSheet.create({
31
+ container: {
32
+ flexDirection: 'row',
33
+ alignItems: 'center',
34
+ gap: 8,
35
+ paddingHorizontal: 4,
36
+ },
37
+ flag: {
38
+ fontSize: 20,
39
+ },
40
+ languageName: {
41
+ fontSize: 14,
42
+ fontWeight: '600',
43
+ },
44
+ icon: {
45
+ fontSize: 20,
46
+ },
47
+ });
48
+ export default LanguageSwitcher;
49
+ //# sourceMappingURL=LanguageSwitcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LanguageSwitcher.js","sourceRoot":"","sources":["../../../src/infrastructure/components/LanguageSwitcher.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Language } from '../../domain/repositories/ILocalizationRepository';
2
+ export declare const useLanguageNavigation: (navigationScreen: string) => {
3
+ currentLang: Language;
4
+ navigateToLanguageSelection: () => void;
5
+ };
6
+ //# sourceMappingURL=useLanguageNavigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLanguageNavigation.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/components/useLanguageNavigation.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,mDAAmD,CAAC;AAE7E,eAAO,MAAM,qBAAqB,GAAI,kBAAkB,MAAM;;;CAY7D,CAAC"}
@@ -0,0 +1,16 @@
1
+ // @ts-ignore - Optional peer dependency
2
+ import { useNavigation } from '@react-navigation/native';
3
+ import { useLocalization } from '../storage/LocalizationStore';
4
+ import { getLanguageByCode, getDefaultLanguage } from '../config/languages';
5
+ export const useLanguageNavigation = (navigationScreen) => {
6
+ const navigation = useNavigation();
7
+ const { currentLanguage } = useLocalization();
8
+ const currentLang = getLanguageByCode(currentLanguage) || getDefaultLanguage();
9
+ const navigateToLanguageSelection = () => {
10
+ if (navigation && navigationScreen) {
11
+ navigation.navigate(navigationScreen);
12
+ }
13
+ };
14
+ return { currentLang, navigateToLanguageSelection };
15
+ };
16
+ //# sourceMappingURL=useLanguageNavigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLanguageNavigation.js","sourceRoot":"","sources":["../../../src/infrastructure/components/useLanguageNavigation.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG5E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,gBAAwB,EAAE,EAAE;IAChE,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,MAAM,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CAAC;IAC9C,MAAM,WAAW,GAAG,iBAAiB,CAAC,eAAe,CAAC,IAAI,kBAAkB,EAAE,CAAC;IAE/E,MAAM,2BAA2B,GAAG,GAAG,EAAE;QACvC,IAAI,UAAU,IAAI,gBAAgB,EAAE,CAAC;YACnC,UAAU,CAAC,QAAQ,CAAC,gBAAyB,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,EAAE,WAAW,EAAE,2BAA2B,EAAE,CAAC;AACtD,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"LocalizationStore.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/storage/LocalizationStore.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mDAAmD,CAAC;AAElF,UAAU,iBAAiB;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,QAAQ,EAAE,CAAC;IAC/B,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,eAAO,MAAM,oBAAoB,gFAqE9B,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;gCA/EE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClC,OAAO,CAAC,IAAI,CAAC;CAoGhC,CAAC"}
1
+ {"version":3,"file":"LocalizationStore.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/storage/LocalizationStore.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mDAAmD,CAAC;AAElF,UAAU,iBAAiB;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,QAAQ,EAAE,CAAC;IAC/B,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,eAAO,MAAM,oBAAoB,gFA0J9B,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;gCApKE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClC,OAAO,CAAC,IAAI,CAAC;CAyLhC,CAAC"}
@@ -19,54 +19,151 @@ export const useLocalizationStore = create((set, get) => ({
19
19
  * - Fallback: English (en-US) if device locale not supported
20
20
  */
21
21
  initialize: async () => {
22
- // CRITICAL FIX: Don't reset isInitialized if already initialized
23
- // This prevents UI flash on re-initialization
24
- const { isInitialized: alreadyInitialized } = get();
25
- if (alreadyInitialized)
26
- return;
27
- // Get saved language preference
28
- const savedLanguage = await StorageWrapper.getString(STORAGE_KEYS.LANGUAGE, DEFAULT_LANGUAGE);
29
- // DEVICE LOCALE DETECTION: Use device locale on first launch
30
- let languageCode;
31
- if (savedLanguage && savedLanguage !== DEFAULT_LANGUAGE) {
32
- // User has previously selected a language → Use their choice
33
- languageCode = savedLanguage;
22
+ /* eslint-disable-next-line no-console */
23
+ if (__DEV__)
24
+ console.log('[LocalizationStore] Starting initialization...');
25
+ try {
26
+ // ✅ CRITICAL FIX: Don't reset isInitialized if already initialized
27
+ // This prevents UI flash on re-initialization
28
+ const { isInitialized: alreadyInitialized } = get();
29
+ if (alreadyInitialized) {
30
+ /* eslint-disable-next-line no-console */
31
+ if (__DEV__)
32
+ console.log('[LocalizationStore] Already initialized, skipping...');
33
+ return;
34
+ }
35
+ /* eslint-disable-next-line no-console */
36
+ if (__DEV__)
37
+ console.log('[LocalizationStore] Getting saved language preference...');
38
+ // Get saved language preference
39
+ const savedLanguage = await StorageWrapper.getString(STORAGE_KEYS.LANGUAGE, DEFAULT_LANGUAGE);
40
+ /* eslint-disable-next-line no-console */
41
+ if (__DEV__)
42
+ console.log('[LocalizationStore] Saved language:', savedLanguage);
43
+ // ✅ DEVICE LOCALE DETECTION: Use device locale on first launch
44
+ let languageCode;
45
+ if (savedLanguage && savedLanguage !== DEFAULT_LANGUAGE) {
46
+ // User has previously selected a language → Use their choice
47
+ /* eslint-disable-next-line no-console */
48
+ if (__DEV__)
49
+ console.log('[LocalizationStore] Using saved language preference:', savedLanguage);
50
+ languageCode = savedLanguage;
51
+ }
52
+ else {
53
+ // First launch → Detect device locale automatically
54
+ /* eslint-disable-next-line no-console */
55
+ if (__DEV__)
56
+ console.log('[LocalizationStore] First launch, detecting device locale...');
57
+ languageCode = getDeviceLocale();
58
+ /* eslint-disable-next-line no-console */
59
+ if (__DEV__)
60
+ console.log('[LocalizationStore] Detected device locale:', languageCode);
61
+ // Save detected locale for future launches
62
+ await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
63
+ /* eslint-disable-next-line no-console */
64
+ if (__DEV__)
65
+ console.log('[LocalizationStore] Saved detected locale to storage');
66
+ }
67
+ // ✅ DEFENSIVE: Validate language exists, fallback to default
68
+ /* eslint-disable-next-line no-console */
69
+ if (__DEV__)
70
+ console.log('[LocalizationStore] Validating language code:', languageCode);
71
+ const language = getLanguageByCode(languageCode);
72
+ const finalLanguage = language ? languageCode : DEFAULT_LANGUAGE;
73
+ const finalLanguageObj = getLanguageByCode(finalLanguage);
74
+ if (!language) {
75
+ /* eslint-disable-next-line no-console */
76
+ console.warn('[LocalizationStore] ⚠️ Language not found:', languageCode, 'falling back to:', DEFAULT_LANGUAGE);
77
+ }
78
+ /* eslint-disable-next-line no-console */
79
+ if (__DEV__)
80
+ console.log('[LocalizationStore] Changing i18n language to:', finalLanguage);
81
+ await i18n.changeLanguage(finalLanguage);
82
+ /* eslint-disable-next-line no-console */
83
+ if (__DEV__)
84
+ console.log('[LocalizationStore] Setting store state...');
85
+ set({
86
+ currentLanguage: finalLanguage,
87
+ isRTL: finalLanguageObj?.rtl || false,
88
+ isInitialized: true, // ✅ Always set true to unblock UI
89
+ });
90
+ /* eslint-disable-next-line no-console */
91
+ if (__DEV__)
92
+ console.log('[LocalizationStore] ✅ Initialization complete. Language:', finalLanguage, 'RTL:', finalLanguageObj?.rtl || false);
34
93
  }
35
- else {
36
- // First launch → Detect device locale automatically
37
- languageCode = getDeviceLocale();
38
- // Save detected locale for future launches
39
- await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
94
+ catch (error) {
95
+ /* eslint-disable-next-line no-console */
96
+ console.error('[LocalizationStore] FATAL: Initialization failed:', error);
97
+ /* eslint-disable-next-line no-console */
98
+ if (error instanceof Error) {
99
+ /* eslint-disable-next-line no-console */
100
+ console.error('[LocalizationStore] Error name:', error.name);
101
+ /* eslint-disable-next-line no-console */
102
+ console.error('[LocalizationStore] Error message:', error.message);
103
+ /* eslint-disable-next-line no-console */
104
+ console.error('[LocalizationStore] Error stack:', error.stack);
105
+ }
106
+ // Set to default language even on error to prevent app from breaking
107
+ try {
108
+ await i18n.changeLanguage(DEFAULT_LANGUAGE);
109
+ set({
110
+ currentLanguage: DEFAULT_LANGUAGE,
111
+ isRTL: false,
112
+ isInitialized: true, // Set true even on error to unblock UI
113
+ });
114
+ /* eslint-disable-next-line no-console */
115
+ console.warn('[LocalizationStore] ⚠️ Fallback to default language due to error');
116
+ }
117
+ catch (fallbackError) {
118
+ /* eslint-disable-next-line no-console */
119
+ console.error('[LocalizationStore] ❌ CRITICAL: Even fallback failed:', fallbackError);
120
+ throw fallbackError;
121
+ }
40
122
  }
41
- // ✅ DEFENSIVE: Validate language exists, fallback to default
42
- const language = getLanguageByCode(languageCode);
43
- const finalLanguage = language ? languageCode : DEFAULT_LANGUAGE;
44
- const finalLanguageObj = getLanguageByCode(finalLanguage);
45
- await i18n.changeLanguage(finalLanguage);
46
- set({
47
- currentLanguage: finalLanguage,
48
- isRTL: finalLanguageObj?.rtl || false,
49
- isInitialized: true, // ✅ Always set true to unblock UI
50
- });
51
123
  },
52
124
  /**
53
125
  * Change language
54
126
  * Updates i18n, state, and persists to AsyncStorage
55
127
  */
56
128
  setLanguage: async (languageCode) => {
57
- const language = getLanguageByCode(languageCode);
58
- // ✅ DEFENSIVE: Early return if unsupported language
59
- if (!language)
60
- return;
61
- // Update i18n
62
- await i18n.changeLanguage(languageCode);
63
- // Update state
64
- set({
65
- currentLanguage: languageCode,
66
- isRTL: language.rtl || false,
67
- });
68
- // Persist language preference
69
- await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
129
+ /* eslint-disable-next-line no-console */
130
+ if (__DEV__)
131
+ console.log('[LocalizationStore] Changing language to:', languageCode);
132
+ try {
133
+ const language = getLanguageByCode(languageCode);
134
+ // ✅ DEFENSIVE: Early return if unsupported language
135
+ if (!language) {
136
+ /* eslint-disable-next-line no-console */
137
+ console.warn('[LocalizationStore] ⚠️ Unsupported language code:', languageCode);
138
+ return;
139
+ }
140
+ /* eslint-disable-next-line no-console */
141
+ if (__DEV__)
142
+ console.log('[LocalizationStore] Updating i18n language...');
143
+ // Update i18n
144
+ await i18n.changeLanguage(languageCode);
145
+ /* eslint-disable-next-line no-console */
146
+ if (__DEV__)
147
+ console.log('[LocalizationStore] Updating store state...');
148
+ // Update state
149
+ set({
150
+ currentLanguage: languageCode,
151
+ isRTL: language.rtl || false,
152
+ });
153
+ /* eslint-disable-next-line no-console */
154
+ if (__DEV__)
155
+ console.log('[LocalizationStore] Persisting language preference...');
156
+ // Persist language preference
157
+ await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
158
+ /* eslint-disable-next-line no-console */
159
+ if (__DEV__)
160
+ console.log('[LocalizationStore] ✅ Language changed successfully to:', languageCode);
161
+ }
162
+ catch (error) {
163
+ /* eslint-disable-next-line no-console */
164
+ console.error('[LocalizationStore] ❌ Error changing language:', error);
165
+ throw error;
166
+ }
70
167
  },
71
168
  }));
72
169
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"LocalizationStore.js","sourceRoot":"","sources":["../../../src/infrastructure/storage/LocalizationStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LocalizationStore.js","sourceRoot":"","sources":["../../../src/infrastructure/storage/LocalizationStore.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-localization",
3
- "version": "1.2.2",
3
+ "version": "1.3.1",
4
4
  "description": "Universal localization system for React Native apps with i18n support",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -46,7 +46,13 @@
46
46
  },
47
47
  "peerDependencies": {
48
48
  "react": ">=18.2.0",
49
- "react-native": ">=0.74.0"
49
+ "react-native": ">=0.74.0",
50
+ "@react-navigation/native": "^6.0.0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "@react-navigation/native": {
54
+ "optional": true
55
+ }
50
56
  },
51
57
  "devDependencies": {
52
58
  "typescript": "^5.3.3",
@@ -54,11 +54,40 @@ async function setupLanguages() {
54
54
 
55
55
  console.log("🚀 Setting up language directories and files...\n");
56
56
 
57
- // Check if en-US directory exists
57
+ // Create en-US directory if it doesn't exist
58
58
  if (!fs.existsSync(enUSDir)) {
59
- console.error("❌ en-US directory not found!");
60
- console.error(" Expected path:", enUSDir);
61
- process.exit(1);
59
+ fs.mkdirSync(enUSDir, { recursive: true });
60
+ console.log(`📁 Created en-US directory: ${enUSDir}`);
61
+ }
62
+
63
+ // Get default files from localization package
64
+ const packageRoot = path.dirname(require.resolve('@umituz/react-native-localization/package.json'));
65
+ const packageEnUSDir = path.join(packageRoot, 'src/infrastructure/locales/en-US');
66
+ const defaultFiles = [
67
+ 'auth.json',
68
+ 'branding.json',
69
+ 'datetime.json',
70
+ 'errors.json',
71
+ 'general.json',
72
+ 'navigation.json',
73
+ 'onboarding.json',
74
+ 'settings.json',
75
+ ];
76
+
77
+ // Copy default files from package if they don't exist in project
78
+ let copiedFiles = 0;
79
+ if (fs.existsSync(packageEnUSDir)) {
80
+ for (const file of defaultFiles) {
81
+ const packageFile = path.join(packageEnUSDir, file);
82
+ const projectFile = path.join(enUSDir, file);
83
+
84
+ if (fs.existsSync(packageFile) && !fs.existsSync(projectFile)) {
85
+ const content = fs.readFileSync(packageFile, "utf8");
86
+ fs.writeFileSync(projectFile, content);
87
+ console.log(`📄 Copied default file: en-US/${file}`);
88
+ copiedFiles++;
89
+ }
90
+ }
62
91
  }
63
92
 
64
93
  // Automatically discover all JSON files in en-US directory
@@ -69,6 +98,7 @@ async function setupLanguages() {
69
98
 
70
99
  if (files.length === 0) {
71
100
  console.error("❌ No JSON files found in en-US directory!");
101
+ console.error(" Please add translation files to:", enUSDir);
72
102
  process.exit(1);
73
103
  }
74
104
 
@@ -14,16 +14,16 @@ const path = require('path');
14
14
  function findLocalesDir() {
15
15
  const projectRoot = process.cwd();
16
16
 
17
- // Common paths to search
17
+ // Common paths to search (prefer simple structure first)
18
18
  const possiblePaths = [
19
+ // Simple structure (preferred)
20
+ path.join(projectRoot, 'src/locales'),
21
+ path.join(projectRoot, 'locales'),
22
+ path.join(projectRoot, 'translations'),
19
23
  // DDD structure
20
24
  path.join(projectRoot, 'src/domains/localization/infrastructure/locales'),
21
25
  path.join(projectRoot, 'src/domains/i18n/infrastructure/locales'),
22
26
  path.join(projectRoot, 'src/infrastructure/localization/locales'),
23
- // Simple structure
24
- path.join(projectRoot, 'src/locales'),
25
- path.join(projectRoot, 'locales'),
26
- path.join(projectRoot, 'translations'),
27
27
  // Alternative DDD
28
28
  path.join(projectRoot, 'src/features/localization/locales'),
29
29
  ];
@@ -49,25 +49,22 @@ function getLocalesDir(createIfNotExists = false) {
49
49
 
50
50
  if (!localesDir) {
51
51
  if (createIfNotExists) {
52
- // Try to create in most common location
52
+ // Try to create in most common location (prefer simple structure)
53
53
  const projectRoot = process.cwd();
54
- localesDir = path.join(projectRoot, 'src/domains/localization/infrastructure/locales');
55
- const enUSDir = path.join(localesDir, 'en-US');
56
-
54
+ // Always prefer simple structure first
55
+ localesDir = path.join(projectRoot, 'src/locales');
57
56
  if (!fs.existsSync(localesDir)) {
58
57
  fs.mkdirSync(localesDir, { recursive: true });
59
- fs.mkdirSync(enUSDir, { recursive: true });
60
58
  console.log(`✅ Created locales directory: ${localesDir}`);
61
59
  }
62
-
63
60
  return localesDir;
64
61
  }
65
62
 
66
63
  console.error('❌ Locales directory not found!');
67
64
  console.error('\nPlease create a locales directory in one of these locations:');
68
- console.error(' - src/domains/localization/infrastructure/locales');
69
65
  console.error(' - src/locales');
70
66
  console.error(' - locales');
67
+ console.error(' - src/domains/localization/infrastructure/locales');
71
68
  console.error('\nOr run: npm run i18n:setup');
72
69
  process.exit(1);
73
70
  }
package/src/index.ts CHANGED
@@ -8,6 +8,8 @@ export { useLocalization, useLocalizationStore } from './infrastructure/storage/
8
8
 
9
9
  // Components
10
10
  export { LocalizationProvider } from './infrastructure/components/LocalizationProvider';
11
+ export { LanguageSwitcher } from './infrastructure/components/LanguageSwitcher';
12
+ export { useLanguageNavigation } from './infrastructure/components/useLanguageNavigation';
11
13
 
12
14
  // Configuration
13
15
  export { default as i18n } from './infrastructure/config/i18n';
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, Text, View, StyleSheet } from 'react-native';
3
+ // @ts-ignore - Optional peer dependency
4
+ import { useNavigation } from '@react-navigation/native';
5
+ import { useLocalization } from '../storage/LocalizationStore';
6
+ import { getLanguageByCode, getDefaultLanguage } from '../config/languages';
7
+ import { Language } from '../../domain/repositories/ILocalizationRepository';
8
+
9
+ interface LanguageSwitcherProps {
10
+ showName?: boolean;
11
+ showFlag?: boolean;
12
+ color?: string;
13
+ navigationScreen?: string;
14
+ style?: any;
15
+ textStyle?: any;
16
+ }
17
+
18
+ const languageSwitcherConfig = {
19
+ defaultIconSize: 20,
20
+ defaultNavigationScreen: 'LanguageSelection',
21
+ hitSlop: { top: 10, bottom: 10, left: 10, right: 10 },
22
+ };
23
+
24
+ export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({
25
+ showName = false,
26
+ showFlag = true,
27
+ color,
28
+ navigationScreen = languageSwitcherConfig.defaultNavigationScreen,
29
+ style,
30
+ textStyle,
31
+ }) => {
32
+ const navigation = useNavigation();
33
+ const { currentLanguage } = useLocalization();
34
+ const currentLang = getLanguageByCode(currentLanguage) || getDefaultLanguage();
35
+
36
+ const navigateToLanguageSelection = () => {
37
+ if (navigation && navigationScreen) {
38
+ navigation.navigate(navigationScreen as never);
39
+ }
40
+ };
41
+
42
+ const iconColor = color || '#000000';
43
+
44
+ return (
45
+ <TouchableOpacity
46
+ style={[styles.container, style]}
47
+ onPress={navigateToLanguageSelection}
48
+ activeOpacity={0.7}
49
+ hitSlop={languageSwitcherConfig.hitSlop}
50
+ >
51
+ {showFlag && (
52
+ <Text style={[styles.flag, textStyle]}>{currentLang.flag}</Text>
53
+ )}
54
+ {showName && (
55
+ <Text style={[styles.languageName, { color: iconColor }, textStyle]}>
56
+ {currentLang.nativeName}
57
+ </Text>
58
+ )}
59
+ {!showName && !showFlag && (
60
+ <Text style={[styles.icon, { color: iconColor }]}>🌐</Text>
61
+ )}
62
+ </TouchableOpacity>
63
+ );
64
+ };
65
+
66
+ const styles = StyleSheet.create({
67
+ container: {
68
+ flexDirection: 'row',
69
+ alignItems: 'center',
70
+ gap: 8,
71
+ paddingHorizontal: 4,
72
+ },
73
+ flag: {
74
+ fontSize: 20,
75
+ },
76
+ languageName: {
77
+ fontSize: 14,
78
+ fontWeight: '600',
79
+ },
80
+ icon: {
81
+ fontSize: 20,
82
+ },
83
+ });
84
+
85
+ export default LanguageSwitcher;
86
+
@@ -0,0 +1,20 @@
1
+ // @ts-ignore - Optional peer dependency
2
+ import { useNavigation } from '@react-navigation/native';
3
+ import { useLocalization } from '../storage/LocalizationStore';
4
+ import { getLanguageByCode, getDefaultLanguage } from '../config/languages';
5
+ import { Language } from '../../domain/repositories/ILocalizationRepository';
6
+
7
+ export const useLanguageNavigation = (navigationScreen: string) => {
8
+ const navigation = useNavigation();
9
+ const { currentLanguage } = useLocalization();
10
+ const currentLang = getLanguageByCode(currentLanguage) || getDefaultLanguage();
11
+
12
+ const navigateToLanguageSelection = () => {
13
+ if (navigation && navigationScreen) {
14
+ navigation.navigate(navigationScreen as never);
15
+ }
16
+ };
17
+
18
+ return { currentLang, navigateToLanguageSelection };
19
+ };
20
+
@@ -32,37 +32,100 @@ export const useLocalizationStore = create<LocalizationState>((set, get) => ({
32
32
  * - Fallback: English (en-US) if device locale not supported
33
33
  */
34
34
  initialize: async () => {
35
- // CRITICAL FIX: Don't reset isInitialized if already initialized
36
- // This prevents UI flash on re-initialization
37
- const { isInitialized: alreadyInitialized } = get();
38
- if (alreadyInitialized) return;
39
-
40
- // Get saved language preference
41
- const savedLanguage = await StorageWrapper.getString(STORAGE_KEYS.LANGUAGE, DEFAULT_LANGUAGE);
42
-
43
- // DEVICE LOCALE DETECTION: Use device locale on first launch
44
- let languageCode: string;
45
- if (savedLanguage && savedLanguage !== DEFAULT_LANGUAGE) {
46
- // User has previously selected a language → Use their choice
47
- languageCode = savedLanguage;
48
- } else {
49
- // First launch Detect device locale automatically
50
- languageCode = getDeviceLocale();
51
- // Save detected locale for future launches
52
- await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
35
+ /* eslint-disable-next-line no-console */
36
+ if (__DEV__) console.log('[LocalizationStore] Starting initialization...');
37
+
38
+ try {
39
+ // ✅ CRITICAL FIX: Don't reset isInitialized if already initialized
40
+ // This prevents UI flash on re-initialization
41
+ const { isInitialized: alreadyInitialized } = get();
42
+ if (alreadyInitialized) {
43
+ /* eslint-disable-next-line no-console */
44
+ if (__DEV__) console.log('[LocalizationStore] Already initialized, skipping...');
45
+ return;
46
+ }
47
+
48
+ /* eslint-disable-next-line no-console */
49
+ if (__DEV__) console.log('[LocalizationStore] Getting saved language preference...');
50
+ // Get saved language preference
51
+ const savedLanguage = await StorageWrapper.getString(STORAGE_KEYS.LANGUAGE, DEFAULT_LANGUAGE);
52
+ /* eslint-disable-next-line no-console */
53
+ if (__DEV__) console.log('[LocalizationStore] Saved language:', savedLanguage);
54
+
55
+ // ✅ DEVICE LOCALE DETECTION: Use device locale on first launch
56
+ let languageCode: string;
57
+ if (savedLanguage && savedLanguage !== DEFAULT_LANGUAGE) {
58
+ // User has previously selected a language → Use their choice
59
+ /* eslint-disable-next-line no-console */
60
+ if (__DEV__) console.log('[LocalizationStore] Using saved language preference:', savedLanguage);
61
+ languageCode = savedLanguage;
62
+ } else {
63
+ // First launch → Detect device locale automatically
64
+ /* eslint-disable-next-line no-console */
65
+ if (__DEV__) console.log('[LocalizationStore] First launch, detecting device locale...');
66
+ languageCode = getDeviceLocale();
67
+ /* eslint-disable-next-line no-console */
68
+ if (__DEV__) console.log('[LocalizationStore] Detected device locale:', languageCode);
69
+ // Save detected locale for future launches
70
+ await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
71
+ /* eslint-disable-next-line no-console */
72
+ if (__DEV__) console.log('[LocalizationStore] Saved detected locale to storage');
73
+ }
74
+
75
+ // ✅ DEFENSIVE: Validate language exists, fallback to default
76
+ /* eslint-disable-next-line no-console */
77
+ if (__DEV__) console.log('[LocalizationStore] Validating language code:', languageCode);
78
+ const language = getLanguageByCode(languageCode);
79
+ const finalLanguage = language ? languageCode : DEFAULT_LANGUAGE;
80
+ const finalLanguageObj = getLanguageByCode(finalLanguage);
81
+
82
+ if (!language) {
83
+ /* eslint-disable-next-line no-console */
84
+ console.warn('[LocalizationStore] ⚠️ Language not found:', languageCode, 'falling back to:', DEFAULT_LANGUAGE);
85
+ }
86
+
87
+ /* eslint-disable-next-line no-console */
88
+ if (__DEV__) console.log('[LocalizationStore] Changing i18n language to:', finalLanguage);
89
+ await i18n.changeLanguage(finalLanguage);
90
+
91
+ /* eslint-disable-next-line no-console */
92
+ if (__DEV__) console.log('[LocalizationStore] Setting store state...');
93
+ set({
94
+ currentLanguage: finalLanguage,
95
+ isRTL: finalLanguageObj?.rtl || false,
96
+ isInitialized: true, // ✅ Always set true to unblock UI
97
+ });
98
+
99
+ /* eslint-disable-next-line no-console */
100
+ if (__DEV__) console.log('[LocalizationStore] ✅ Initialization complete. Language:', finalLanguage, 'RTL:', finalLanguageObj?.rtl || false);
101
+ } catch (error) {
102
+ /* eslint-disable-next-line no-console */
103
+ console.error('[LocalizationStore] ❌ FATAL: Initialization failed:', error);
104
+ /* eslint-disable-next-line no-console */
105
+ if (error instanceof Error) {
106
+ /* eslint-disable-next-line no-console */
107
+ console.error('[LocalizationStore] Error name:', error.name);
108
+ /* eslint-disable-next-line no-console */
109
+ console.error('[LocalizationStore] Error message:', error.message);
110
+ /* eslint-disable-next-line no-console */
111
+ console.error('[LocalizationStore] Error stack:', error.stack);
112
+ }
113
+ // Set to default language even on error to prevent app from breaking
114
+ try {
115
+ await i18n.changeLanguage(DEFAULT_LANGUAGE);
116
+ set({
117
+ currentLanguage: DEFAULT_LANGUAGE,
118
+ isRTL: false,
119
+ isInitialized: true, // Set true even on error to unblock UI
120
+ });
121
+ /* eslint-disable-next-line no-console */
122
+ console.warn('[LocalizationStore] ⚠️ Fallback to default language due to error');
123
+ } catch (fallbackError) {
124
+ /* eslint-disable-next-line no-console */
125
+ console.error('[LocalizationStore] ❌ CRITICAL: Even fallback failed:', fallbackError);
126
+ throw fallbackError;
127
+ }
53
128
  }
54
-
55
- // ✅ DEFENSIVE: Validate language exists, fallback to default
56
- const language = getLanguageByCode(languageCode);
57
- const finalLanguage = language ? languageCode : DEFAULT_LANGUAGE;
58
- const finalLanguageObj = getLanguageByCode(finalLanguage);
59
-
60
- await i18n.changeLanguage(finalLanguage);
61
- set({
62
- currentLanguage: finalLanguage,
63
- isRTL: finalLanguageObj?.rtl || false,
64
- isInitialized: true, // ✅ Always set true to unblock UI
65
- });
66
129
  },
67
130
 
68
131
  /**
@@ -70,22 +133,44 @@ export const useLocalizationStore = create<LocalizationState>((set, get) => ({
70
133
  * Updates i18n, state, and persists to AsyncStorage
71
134
  */
72
135
  setLanguage: async (languageCode: string) => {
73
- const language = getLanguageByCode(languageCode);
74
-
75
- // ✅ DEFENSIVE: Early return if unsupported language
76
- if (!language) return;
77
-
78
- // Update i18n
79
- await i18n.changeLanguage(languageCode);
80
-
81
- // Update state
82
- set({
83
- currentLanguage: languageCode,
84
- isRTL: language.rtl || false,
85
- });
86
-
87
- // Persist language preference
88
- await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
136
+ /* eslint-disable-next-line no-console */
137
+ if (__DEV__) console.log('[LocalizationStore] Changing language to:', languageCode);
138
+
139
+ try {
140
+ const language = getLanguageByCode(languageCode);
141
+
142
+ // ✅ DEFENSIVE: Early return if unsupported language
143
+ if (!language) {
144
+ /* eslint-disable-next-line no-console */
145
+ console.warn('[LocalizationStore] ⚠️ Unsupported language code:', languageCode);
146
+ return;
147
+ }
148
+
149
+ /* eslint-disable-next-line no-console */
150
+ if (__DEV__) console.log('[LocalizationStore] Updating i18n language...');
151
+ // Update i18n
152
+ await i18n.changeLanguage(languageCode);
153
+
154
+ /* eslint-disable-next-line no-console */
155
+ if (__DEV__) console.log('[LocalizationStore] Updating store state...');
156
+ // Update state
157
+ set({
158
+ currentLanguage: languageCode,
159
+ isRTL: language.rtl || false,
160
+ });
161
+
162
+ /* eslint-disable-next-line no-console */
163
+ if (__DEV__) console.log('[LocalizationStore] Persisting language preference...');
164
+ // Persist language preference
165
+ await StorageWrapper.setString(STORAGE_KEYS.LANGUAGE, languageCode);
166
+
167
+ /* eslint-disable-next-line no-console */
168
+ if (__DEV__) console.log('[LocalizationStore] ✅ Language changed successfully to:', languageCode);
169
+ } catch (error) {
170
+ /* eslint-disable-next-line no-console */
171
+ console.error('[LocalizationStore] ❌ Error changing language:', error);
172
+ throw error;
173
+ }
89
174
  },
90
175
  }));
91
176