@umituz/react-native-settings 4.23.68 β†’ 4.23.70

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 (46) hide show
  1. package/package.json +2 -1
  2. package/src/domains/about/presentation/hooks/useAboutInfo.ts +0 -1
  3. package/src/domains/appearance/data/colorPalettes.ts +1 -6
  4. package/src/domains/appearance/hooks/useAppearance.ts +5 -3
  5. package/src/domains/appearance/hooks/useAppearanceActions.ts +2 -1
  6. package/src/domains/appearance/index.ts +2 -1
  7. package/src/domains/appearance/presentation/components/ColorPicker.tsx +1 -1
  8. package/src/domains/appearance/presentation/screens/AppearanceScreen.tsx +2 -1
  9. package/src/domains/appearance/types/index.ts +5 -2
  10. package/src/domains/disclaimer/presentation/components/DisclaimerModal.tsx +1 -1
  11. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +48 -12
  12. package/src/domains/feedback/presentation/components/FeedbackModal.tsx +1 -1
  13. package/src/domains/gamification/components/GamificationScreen/index.tsx +83 -4
  14. package/src/domains/gamification/components/GamificationScreen/types.ts +5 -0
  15. package/src/domains/gamification/components/index.ts +0 -1
  16. package/src/domains/gamification/index.ts +0 -1
  17. package/src/domains/gamification/utils/calculations.ts +1 -1
  18. package/src/domains/legal/presentation/components/LegalItem.tsx +1 -1
  19. package/src/domains/localization/index.ts +0 -2
  20. package/src/domains/localization/infrastructure/components/useLanguageSwitcher.ts +18 -1
  21. package/src/domains/localization/infrastructure/config/languages.ts +0 -9
  22. package/src/domains/localization/infrastructure/hooks/TranslationHook.ts +22 -24
  23. package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +163 -111
  24. package/src/domains/notifications/infrastructure/services/NotificationManager.ts +1 -3
  25. package/src/domains/notifications/infrastructure/services/NotificationScheduler.ts +2 -2
  26. package/src/domains/notifications/quietHours/presentation/components/QuietHoursCard.tsx +0 -1
  27. package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +60 -25
  28. package/src/domains/notifications/reminders/presentation/screens/ReminderListScreen.tsx +2 -2
  29. package/src/domains/rating/presentation/components/RatingPromptModal.tsx +1 -1
  30. package/src/domains/rating/presentation/components/StarRating.tsx +1 -1
  31. package/src/presentation/components/SettingsErrorBoundary.tsx +14 -18
  32. package/src/presentation/components/SettingsFooter.tsx +1 -3
  33. package/src/presentation/components/SettingsItemCard.tsx +27 -6
  34. package/src/presentation/hooks/queries/useSettingsQuery.ts +0 -1
  35. package/src/presentation/hooks/useSettings.ts +8 -0
  36. package/src/presentation/hooks/useSettingsScreenConfig.ts +3 -3
  37. package/src/presentation/navigation/SettingsStackNavigator.tsx +6 -8
  38. package/src/presentation/screens/SettingsScreen.tsx +6 -3
  39. package/src/presentation/screens/components/SettingsContent.tsx +16 -1
  40. package/src/domains/appearance/hooks/index.ts +0 -6
  41. package/src/domains/appearance/presentation/screens/index.ts +0 -2
  42. package/src/domains/gamification/components/GamificationScreenWrapper.tsx +0 -65
  43. package/src/presentation/navigation/components/wrappers/AboutScreenWrapper.tsx +0 -14
  44. package/src/presentation/navigation/components/wrappers/LegalScreenWrapper.tsx +0 -50
  45. package/src/presentation/navigation/components/wrappers/SettingsScreenWrapper.tsx +0 -44
  46. package/src/presentation/navigation/components/wrappers/index.ts +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "4.23.68",
3
+ "version": "4.23.70",
4
4
  "description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -81,6 +81,7 @@
81
81
  "@umituz/react-native-sentry": "*",
82
82
  "eslint": "^8.57.0",
83
83
  "eslint-plugin-react": "^7.37.5",
84
+ "eslint-plugin-react-hooks": "^7.0.1",
84
85
  "eslint-plugin-react-native": "^5.0.0",
85
86
  "expo-apple-authentication": "^8.0.8",
86
87
  "expo-auth-session": "^5.0.0",
@@ -9,7 +9,6 @@ import { AboutRepository } from '../../infrastructure/repositories/AboutReposito
9
9
  import type { UseAboutInfoOptions, UseAboutInfoReturn } from './useAboutInfo.types';
10
10
  import {
11
11
  setErrorIfMounted,
12
- setLoadingIfMounted,
13
12
  initializeAppInfo,
14
13
  updateAppInfoConfig,
15
14
  updateAppInfoPartial,
@@ -86,9 +86,4 @@ export const generateColorPalette = (
86
86
  name: "custom",
87
87
  colors,
88
88
  };
89
- };
90
-
91
- // Legacy exports for backward compatibility
92
- export const PRIMARY_COLORS = DEFAULT_PRIMARY_COLORS.colors;
93
- export const SECONDARY_COLORS = DEFAULT_SECONDARY_COLORS.colors;
94
- export const ACCENT_COLORS = DEFAULT_ACCENT_COLORS.colors;
89
+ };
@@ -1,15 +1,17 @@
1
- import { useTheme, type ThemeMode, type CustomThemeColors } from "@umituz/react-native-design-system";
1
+ import { useTheme, type ThemeMode as BaseThemeMode, type CustomThemeColors } from "@umituz/react-native-design-system";
2
+ import type { ThemeMode } from "../types";
2
3
 
3
4
  export const useAppearance = () => {
4
5
  const { themeMode, customColors, defaultColors, isInitialized, setThemeMode, setCustomColors, resetToDefaults } = useTheme();
5
6
 
6
7
  return {
7
- themeMode,
8
+ themeMode: themeMode as ThemeMode,
8
9
  customColors,
9
10
  defaultColors,
10
11
  isLoading: !isInitialized,
11
12
  setThemeMode: (mode: ThemeMode) => {
12
- void setThemeMode(mode);
13
+ // Cast to base ThemeMode since design system doesn't support 'auto'
14
+ void setThemeMode(mode as BaseThemeMode);
13
15
  },
14
16
  setCustomColors: (colors: CustomThemeColors) => {
15
17
  void setCustomColors(colors);
@@ -1,6 +1,7 @@
1
1
  import { useState, useCallback, useEffect, useRef } from "react";
2
2
  import { useAppearance } from "./useAppearance";
3
- import type { CustomThemeColors, ThemeMode } from "@umituz/react-native-design-system";
3
+ import type { CustomThemeColors } from "@umituz/react-native-design-system";
4
+ import type { ThemeMode } from "../types";
4
5
 
5
6
  export const useAppearanceActions = () => {
6
7
  const { themeMode, customColors, setThemeMode, setCustomColors, reset } = useAppearance();
@@ -5,5 +5,6 @@
5
5
 
6
6
  export * from './presentation/screens/AppearanceScreen';
7
7
  export * from './presentation/components';
8
- export * from './hooks';
8
+ export { useAppearance } from './hooks/useAppearance';
9
+ export { useAppearanceActions } from './hooks/useAppearanceActions';
9
10
  export * from './types';
@@ -49,7 +49,7 @@ export const ColorPicker: React.FC<ColorPickerProps> = ({
49
49
 
50
50
  // Memoize color options to prevent unnecessary re-renders
51
51
  const colorOptions = useMemo(() => {
52
- return colorsMemo.map((color, index) => {
52
+ return colorsMemo.map((color) => {
53
53
  const isSelected = value === color;
54
54
 
55
55
  return (
@@ -13,7 +13,8 @@ import {
13
13
  useAppNavigation
14
14
  } from "@umituz/react-native-design-system";
15
15
  import { useLocalization } from "../../../localization";
16
- import { useAppearance, useAppearanceActions } from "../../hooks";
16
+ import { useAppearance } from "../../hooks/useAppearance";
17
+ import { useAppearanceActions } from "../../hooks/useAppearanceActions";
17
18
  import {
18
19
  AppearanceHeader,
19
20
  ThemeModeSection,
@@ -5,11 +5,14 @@
5
5
  */
6
6
 
7
7
  import type {
8
- ThemeMode,
8
+ ThemeMode as BaseThemeMode,
9
9
  CustomThemeColors,
10
10
  } from "@umituz/react-native-design-system";
11
11
 
12
- export type { ThemeMode, CustomThemeColors };
12
+ // Extended theme mode to support 'auto' option
13
+ export type ThemeMode = BaseThemeMode | 'auto';
14
+
15
+ export type { CustomThemeColors };
13
16
 
14
17
  export interface AppearanceSettings {
15
18
  themeMode: ThemeMode;
@@ -69,7 +69,7 @@ export const DisclaimerModal: React.FC<DisclaimerModalProps> = ({
69
69
  };
70
70
 
71
71
 
72
- const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
72
+ const getStyles = (_tokens: ReturnType<typeof useAppDesignTokens>) =>
73
73
  StyleSheet.create({
74
74
  modalContainer: {
75
75
  flex: 1,
@@ -7,7 +7,6 @@ import React, { useState } from "react";
7
7
  import { View, StyleSheet, TouchableOpacity, ScrollView, TextInput } from "react-native";
8
8
  import { useAppDesignTokens, AtomicText, AtomicButton, AtomicIcon } from "@umituz/react-native-design-system";
9
9
  import type { FeedbackType, FeedbackRating } from "../../domain/entities/FeedbackEntity";
10
- import { useFeedbackForm } from "../hooks/useFeedbackForm";
11
10
 
12
11
  export interface FeedbackFormProps {
13
12
  onSubmit: (data: { type: FeedbackType; rating: FeedbackRating; description: string; title: string }) => Promise<void>;
@@ -35,16 +34,38 @@ export const FeedbackForm: React.FC<FeedbackFormProps> = ({
35
34
  const [rating, setRating] = useState<FeedbackRating>(5);
36
35
  const [description, setDescription] = useState("");
37
36
  const [title, setTitle] = useState("");
37
+ const [error, setError] = useState<string | null>(null);
38
+ const [isSubmittingLocal, setIsSubmittingLocal] = useState(false);
38
39
 
39
40
  const handleSubmit = async () => {
40
- if (!description.trim()) return;
41
+ // Validate input
42
+ if (!description.trim()) {
43
+ setError("Please provide a description");
44
+ return;
45
+ }
41
46
 
42
- await onSubmit({
43
- type: selectedType,
44
- rating,
45
- description,
46
- title: title || texts.defaultTitle(selectedType),
47
- });
47
+ setIsSubmittingLocal(true);
48
+ setError(null);
49
+
50
+ try {
51
+ await onSubmit({
52
+ type: selectedType,
53
+ rating,
54
+ description,
55
+ title: title || texts.defaultTitle(selectedType),
56
+ });
57
+
58
+ // Clear form on success
59
+ setDescription("");
60
+ setTitle("");
61
+ setRating(5);
62
+ } catch (err) {
63
+ const errorMessage = err instanceof Error ? err.message : "Failed to submit feedback";
64
+ setError(errorMessage);
65
+ console.error("[FeedbackForm] Submission error:", err);
66
+ } finally {
67
+ setIsSubmittingLocal(false);
68
+ }
48
69
  };
49
70
 
50
71
  const renderRating = () => (
@@ -116,7 +137,10 @@ export const FeedbackForm: React.FC<FeedbackFormProps> = ({
116
137
  <View style={styles.inputContainer}>
117
138
  <TextInput
118
139
  value={description}
119
- onChangeText={setDescription}
140
+ onChangeText={(text) => {
141
+ setDescription(text);
142
+ setError(null); // Clear error on input
143
+ }}
120
144
  placeholder={texts.descriptionPlaceholder}
121
145
  placeholderTextColor={tokens.colors.textTertiary}
122
146
  multiline
@@ -126,18 +150,27 @@ export const FeedbackForm: React.FC<FeedbackFormProps> = ({
126
150
  {
127
151
  color: tokens.colors.textPrimary,
128
152
  backgroundColor: tokens.colors.surface,
129
- borderColor: tokens.colors.border,
153
+ borderColor: error ? tokens.colors.error : tokens.colors.border,
130
154
  }
131
155
  ]}
132
156
  />
157
+ {error && (
158
+ <AtomicText
159
+ type="bodySmall"
160
+ color="error"
161
+ style={styles.errorText}
162
+ >
163
+ {error}
164
+ </AtomicText>
165
+ )}
133
166
  </View>
134
167
 
135
168
  <AtomicButton
136
169
  onPress={handleSubmit}
137
- disabled={isSubmitting || !description.trim()}
170
+ disabled={isSubmitting || isSubmittingLocal || !description.trim()}
138
171
  style={styles.submitButton}
139
172
  >
140
- {isSubmitting ? texts.submittingButton : texts.submitButton}
173
+ {(isSubmitting || isSubmittingLocal) ? texts.submittingButton : texts.submitButton}
141
174
  </AtomicButton>
142
175
  </View>
143
176
  );
@@ -185,6 +218,9 @@ const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
185
218
  padding: 12,
186
219
  fontSize: tokens.typography.bodyMedium.fontSize,
187
220
  },
221
+ errorText: {
222
+ marginTop: 8,
223
+ },
188
224
  submitButton: {
189
225
  width: "100%",
190
226
  },
@@ -79,7 +79,7 @@ export const FeedbackModal: React.FC<FeedbackModalProps> = ({
79
79
  };
80
80
 
81
81
 
82
- const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
82
+ const getStyles = (_tokens: ReturnType<typeof useAppDesignTokens>) =>
83
83
  StyleSheet.create({
84
84
  safeArea: {
85
85
  flex: 1,
@@ -1,11 +1,11 @@
1
1
  import React from "react";
2
2
  /**
3
3
  * GamificationScreen Component
4
- * Full gamification screen - all text via props
4
+ * Full gamification screen with integrated hook
5
5
  * Generic for 100+ apps - NO hardcoded strings
6
6
  */
7
7
 
8
- import { View, ScrollView, StyleSheet } from "react-native";
8
+ import { View, ScrollView } from "react-native";
9
9
  import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
10
10
  import { LevelProgress } from "../LevelProgress";
11
11
  import { StreakDisplay } from "../StreakDisplay";
@@ -13,11 +13,90 @@ import { Header } from "./Header";
13
13
  import { StatsGrid } from "./StatsGrid";
14
14
  import { AchievementsList } from "./AchievementsList";
15
15
  import { styles } from "./styles";
16
- import type { GamificationScreenProps } from "./types";
16
+ import type { GamificationScreenProps, GamificationConfigProps } from "./types";
17
+ import type { Achievement } from "../../types";
17
18
 
18
19
  export type { GamificationScreenProps };
19
20
 
20
- export const GamificationScreen: React.FC<GamificationScreenProps> = ({
21
+ /**
22
+ * GamificationScreen that accepts either detailed props OR a config object
23
+ * When config is provided, a hook consumer component is used
24
+ */
25
+ export const GamificationScreen: React.FC<GamificationScreenProps | GamificationConfigProps> = (props) => {
26
+ // If config is provided, use the hook-based component
27
+ if ('config' in props && props.config) {
28
+ return <GamificationScreenWithConfig config={props.config} />;
29
+ }
30
+
31
+ // Otherwise, render directly with provided props
32
+ return <GamificationScreenInner {...(props as GamificationScreenProps)} />;
33
+ };
34
+
35
+ /**
36
+ * Component that uses the hook - separated to satisfy React Hooks rules
37
+ */
38
+ const GamificationScreenWithConfig: React.FC<GamificationConfigProps> = ({ config }) => {
39
+ // Import hook here to avoid conditional hook usage
40
+ const { useGamification } = require("../../hooks/useGamification");
41
+
42
+ const {
43
+ points,
44
+ level,
45
+ streak,
46
+ achievements,
47
+ } = useGamification(config);
48
+
49
+ // Transform store achievements to UI props
50
+ const achievementItems = achievements.map((a: Achievement) => ({
51
+ ...a,
52
+ title: a.title,
53
+ description: a.description,
54
+ icon: a.icon,
55
+ isUnlocked: a.isUnlocked,
56
+ progress: a.progress,
57
+ threshold: a.threshold,
58
+ id: a.id,
59
+ type: a.type
60
+ }));
61
+
62
+ const screenProps: GamificationScreenProps = {
63
+ title: config.translations.title,
64
+ statsTitle: config.translations.statsTitle,
65
+ achievementsTitle: config.translations.achievementsTitle,
66
+ streakTitle: config.translations.streakTitle,
67
+ levelProps: {
68
+ level: level.currentLevel,
69
+ points: level.currentPoints,
70
+ pointsToNext: level.pointsToNext,
71
+ progress: level.progress,
72
+ levelTitle: config.translations.levelTitle,
73
+ showPoints: true,
74
+ },
75
+ streakProps: {
76
+ current: streak.current,
77
+ longest: streak.longest,
78
+ currentLabel: config.translations.currentStreak,
79
+ bestLabel: config.translations.bestStreak,
80
+ daysLabel: config.translations.days,
81
+ },
82
+ stats: [
83
+ {
84
+ label: config.translations.statsTitle,
85
+ value: points,
86
+ icon: "star",
87
+ },
88
+ ],
89
+ achievements: achievementItems,
90
+ emptyAchievementsText: config.translations.emptyAchievements,
91
+ };
92
+
93
+ return <GamificationScreenInner {...screenProps} />;
94
+ };
95
+
96
+ /**
97
+ * Internal component that renders the screen with all props
98
+ */
99
+ const GamificationScreenInner: React.FC<GamificationScreenProps> = ({
21
100
  title,
22
101
  statsTitle,
23
102
  achievementsTitle,
@@ -9,6 +9,11 @@ import type { StatsCardProps } from "../StatsCard";
9
9
  import type { AchievementItemProps } from "../AchievementItem";
10
10
  import type { StreakDisplayProps } from "../StreakDisplay";
11
11
  import type { ViewStyle, TextStyle } from "react-native";
12
+ import type { GamificationConfig } from "../../types";
13
+
14
+ export interface GamificationConfigProps extends Partial<Omit<GamificationScreenProps, 'config'>> {
15
+ config: GamificationConfig;
16
+ }
12
17
 
13
18
  export interface GamificationScreenProps {
14
19
  // Section titles (all via props)
@@ -11,4 +11,3 @@ export { StreakDisplay, type StreakDisplayProps } from "./StreakDisplay";
11
11
  export { StatsCard, type StatsCardProps } from "./StatsCard";
12
12
  export { AchievementItem, type AchievementItemProps } from "./AchievementItem";
13
13
  export { GamificationScreen, type GamificationScreenProps } from "./GamificationScreen/index";
14
- export { GamificationScreenWrapper } from "./GamificationScreenWrapper";
@@ -54,5 +54,4 @@ export {
54
54
  type AchievementItemProps,
55
55
  GamificationScreen,
56
56
  type GamificationScreenProps,
57
- GamificationScreenWrapper,
58
57
  } from "./components";
@@ -3,7 +3,7 @@
3
3
  * Pure utility functions - NO side effects
4
4
  */
5
5
 
6
- import type { LevelDefinition, LevelState, Achievement, AchievementDefinition } from "../types";
6
+ import type { LevelDefinition, LevelState, AchievementDefinition } from "../types";
7
7
 
8
8
  export const calculateLevel = (
9
9
  points: number,
@@ -42,7 +42,7 @@ export const LegalItem: React.FC<LegalItemProps> = React.memo(({
42
42
  title,
43
43
  description,
44
44
  onPress,
45
- testID,
45
+ testID: _testID,
46
46
  }) => {
47
47
  // Use iconName if provided, otherwise fallback to default
48
48
  const finalIcon = iconName || icon || "shield-checkmark";
@@ -22,8 +22,6 @@ export type { LanguageSectionProps, LanguageSectionConfig } from './presentation
22
22
  export { default as i18n } from './infrastructure/config/i18n';
23
23
  export { I18nInitializer } from './infrastructure/config/I18nInitializer';
24
24
  export {
25
- SUPPORTED_LANGUAGES,
26
- LANGUAGES,
27
25
  DEFAULT_LANGUAGE,
28
26
  getLanguageByCode,
29
27
  isLanguageSupported,
@@ -17,7 +17,24 @@ export const useLanguageSwitcher = ({ onPress, disabled }: UseLanguageSwitcherPr
17
17
  const { currentLanguage } = useLocalization();
18
18
 
19
19
  const currentLang = useMemo((): Language => {
20
- return languageRepository.getLanguageByCode(currentLanguage) || languageRepository.getDefaultLanguage();
20
+ // Double fallback to ensure we always have a valid language
21
+ const lang = languageRepository.getLanguageByCode(currentLanguage)
22
+ || languageRepository.getDefaultLanguage()
23
+ || languageRepository.getLanguages()[0]; // Final fallback
24
+
25
+ if (!lang) {
26
+ // This should never happen if repository is set up correctly
27
+ console.error('[useLanguageSwitcher] No valid language found. Check language repository configuration.');
28
+ // Return a minimal fallback language object
29
+ return {
30
+ code: 'en',
31
+ name: 'English',
32
+ nativeName: 'English',
33
+ flag: 'πŸ‡ΊπŸ‡Έ',
34
+ };
35
+ }
36
+
37
+ return lang;
21
38
  }, [currentLanguage]);
22
39
 
23
40
  const handlePress = useCallback(() => {
@@ -3,9 +3,6 @@
3
3
  * Central export point for all language-related functionality
4
4
  */
5
5
 
6
- import { languageRepository } from '../repository/LanguageRepository';
7
- import type { Language } from '../storage/types/Language';
8
-
9
6
  // Re-export from DeviceLocale
10
7
  export { DEFAULT_LANGUAGE, getDeviceLocale } from './DeviceLocale';
11
8
 
@@ -20,9 +17,3 @@ export {
20
17
 
21
18
  // Re-export from LocaleMapping
22
19
  export { LOCALE_MAPPING } from './LocaleMapping';
23
-
24
- // Backward compatibility
25
- export const getSUPPORTED_LANGUAGES = () => languageRepository.getLanguages();
26
- export const getLANGUAGES = () => languageRepository.getLanguages();
27
- export const SUPPORTED_LANGUAGES: Language[] = languageRepository.getLanguages();
28
- export const LANGUAGES = SUPPORTED_LANGUAGES;
@@ -10,30 +10,28 @@
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import i18n from '../config/i18n';
12
12
 
13
- export class TranslationHook {
14
- /**
15
- * Get translation function with proper fallbacks
16
- */
17
- static useTranslationFunction(): (key: string, options?: Record<string, unknown>) => string {
18
- // Always call useTranslation hook (React hooks rules)
19
- const translationResult = useTranslation(undefined, { i18n });
13
+ /**
14
+ * Custom hook that provides translation function with proper fallbacks
15
+ */
16
+ export const useTranslationFunction = (): ((key: string, options?: Record<string, unknown>) => string) => {
17
+ // Always call useTranslation hook (React hooks rules)
18
+ const translationResult = useTranslation(undefined, { i18n });
20
19
 
21
- // Use react-i18next if available, otherwise fallback to direct i18n
22
- if (translationResult?.t && typeof translationResult.t === 'function' && i18n.isInitialized) {
23
- return (key: string, options?: Record<string, unknown>): string => {
24
- const result = translationResult.t(key, options);
20
+ // Use react-i18next if available, otherwise fallback to direct i18n
21
+ if (translationResult?.t && typeof translationResult.t === 'function' && i18n.isInitialized) {
22
+ return (key: string, options?: Record<string, unknown>): string => {
23
+ const result = translationResult.t(key, options);
24
+ return typeof result === 'string' ? result : String(result);
25
+ };
26
+ } else {
27
+ return (key: string, options?: Record<string, unknown>): string => {
28
+ // Fallback to direct i18n.t
29
+ if (i18n.isInitialized && typeof i18n.t === 'function') {
30
+ const result = i18n.t(key, options);
25
31
  return typeof result === 'string' ? result : String(result);
26
- };
27
- } else {
28
- return (key: string, options?: Record<string, unknown>): string => {
29
- // Fallback to direct i18n.t
30
- if (i18n.isInitialized && typeof i18n.t === 'function') {
31
- const result = i18n.t(key, options);
32
- return typeof result === 'string' ? result : String(result);
33
- }
34
- // Final fallback: return key
35
- return key;
36
- };
37
- }
32
+ }
33
+ // Final fallback: return key
34
+ return key;
35
+ };
38
36
  }
39
- }
37
+ };