@jsarc/intl 0.0.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.
@@ -0,0 +1,138 @@
1
+ import { createContext, useContext, useState, useEffect, useMemo, useCallback } from 'react';
2
+ import { TranslationService } from '../core/TranslationService';
3
+ import { getSavedLocale, saveLocale, SUPPORTED_LOCALES, type TranslationsConfig } from '../config';
4
+ import type { Locale } from '../config';
5
+
6
+ const ArcIntlContext = createContext<ReturnType<typeof useArcIntlValue> | null>(null);
7
+
8
+ const useArcIntlValue = (
9
+ translationsConfig: TranslationsConfig,
10
+ supportedLocales: string[] = SUPPORTED_LOCALES
11
+ ) => {
12
+ const [service] = useState(() => new TranslationService(supportedLocales, translationsConfig));
13
+ const [currentLocale, setCurrentLocale] = useState<Locale>(getSavedLocale(supportedLocales));
14
+ const [isLoading, setIsLoading] = useState(true);
15
+ const [initialized, setInitialized] = useState(false);
16
+
17
+ useEffect(() => {
18
+ const initialize = async () => {
19
+ setIsLoading(true);
20
+ try {
21
+ if (!initialized) {
22
+ await service.initialize(currentLocale);
23
+ setInitialized(true);
24
+ } else {
25
+ await service.initialize(currentLocale);
26
+ }
27
+ } catch (error) {
28
+ console.error('❌ Failed to initialize translations:', error);
29
+ } finally {
30
+ setIsLoading(false);
31
+ }
32
+ };
33
+
34
+ const timer = setTimeout(() => {
35
+ initialize();
36
+ }, 0);
37
+
38
+ return () => clearTimeout(timer);
39
+ }, [currentLocale, service, initialized]);
40
+
41
+ useEffect(() => {
42
+ if (!initialized || isLoading || !translationsConfig.modules) return;
43
+
44
+ const loadAllModules = async () => {
45
+ const moduleNames = Object.keys(translationsConfig.modules || {});
46
+
47
+ for (const moduleName of moduleNames) {
48
+ try {
49
+ await service.loadModule(moduleName);
50
+ } catch (error) {
51
+ console.error(`❌ Failed to load module ${moduleName}:`, error);
52
+ }
53
+ }
54
+ };
55
+
56
+ loadAllModules();
57
+ }, [initialized, translationsConfig.modules, service, isLoading]);
58
+
59
+ const changeLocale = useCallback(async (locale: Locale) => {
60
+ if (!supportedLocales.includes(locale)) {
61
+ console.warn(`⚠️ Locale ${locale} is not supported`);
62
+ return;
63
+ }
64
+
65
+ if (locale === currentLocale) return;
66
+
67
+ setIsLoading(true);
68
+ try {
69
+ await service.initialize(locale);
70
+ setCurrentLocale(locale);
71
+ saveLocale(locale);
72
+ } catch (error) {
73
+ console.error('❌ Failed to change locale:', error);
74
+ } finally {
75
+ setIsLoading(false);
76
+ }
77
+ }, [currentLocale, supportedLocales, service]);
78
+
79
+ const loadModuleTranslations = useCallback(async (moduleName: string) => {
80
+ setIsLoading(true);
81
+ try {
82
+ await service.loadModule(moduleName);
83
+ } catch (error) {
84
+ console.error(`❌ Failed to load module ${moduleName}:`, error);
85
+ throw error;
86
+ } finally {
87
+ setIsLoading(false);
88
+ }
89
+ }, [service]);
90
+
91
+ const t = useCallback((key: string, params?: Record<string, any>, options?: any) => {
92
+ const result = service.t(key, params || {}, options || {});
93
+ return result;
94
+ }, [service]);
95
+
96
+ return {
97
+ t,
98
+ changeLocale,
99
+ currentLocale,
100
+ isLoading,
101
+ loadModuleTranslations,
102
+ isInitialized: initialized,
103
+ };
104
+ };
105
+
106
+ export const ArcIntlProvider: React.FC<{
107
+ children: React.ReactNode
108
+ translations: TranslationsConfig
109
+ supportedLocales?: string[];
110
+ }> = ({
111
+ translations,
112
+ supportedLocales,
113
+ children,
114
+ }) => {
115
+
116
+ const value = useArcIntlValue(
117
+ translations,
118
+ (typeof supportedLocales === 'object' &&
119
+ !!Array.isArray(supportedLocales) &&
120
+ supportedLocales.length > 0)
121
+ ? supportedLocales
122
+ : SUPPORTED_LOCALES
123
+ );
124
+
125
+ return <ArcIntlContext.Provider value={value}>{children}</ArcIntlContext.Provider>;
126
+ };
127
+
128
+ export const useArcIntl = () => {
129
+ const context = useContext(ArcIntlContext);
130
+ if (!context) throw new Error('useArcIntl must be used within an ArcIntlProvider');
131
+ return context;
132
+ };
133
+
134
+ export default {
135
+ useArcIntlValue,
136
+ ArcIntlProvider,
137
+ useArcIntl,
138
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable", "ESNext"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "isolatedModules": true,
12
+ "moduleDetection": "force",
13
+ "noEmit": true,
14
+ "jsx": "react-jsx",
15
+
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "noUncheckedSideEffectImports": true
21
+ },
22
+ "include": ["src/**/*"],
23
+ "exclude": [
24
+ "node_modules",
25
+ "**/*.d.ts"
26
+ ]
27
+ }
@@ -0,0 +1,79 @@
1
+ import type { TranslationsConfig } from '../config';
2
+
3
+ export const loadBaseTranslations = async (
4
+ locale: string,
5
+ translationsConfig: TranslationsConfig
6
+ ): Promise<Record<string, any>> => {
7
+ if (!translationsConfig?.base[locale]) {
8
+ console.warn(`❌ No base translations found for locale: ${locale}`);
9
+ console.warn('Available locales:', translationsConfig?.base ? Object.keys(translationsConfig.base) : 'none');
10
+ return {};
11
+ }
12
+
13
+ try {
14
+ const loader = translationsConfig.base[locale];
15
+ const result = await loader();
16
+ return result;
17
+ } catch (error) {
18
+ console.error(`❌ Failed to load base ${locale} translations`, error);
19
+ return {};
20
+ }
21
+ };
22
+
23
+ export const loadModulesTranslations = async (
24
+ locale: string,
25
+ translationsConfig: TranslationsConfig
26
+ ): Promise<Array<{ moduleName: string; translations: Record<string, any> }>> => {
27
+ const results: any[] = [];
28
+
29
+ if (!translationsConfig?.modules) {
30
+ console.warn(`⚠️ No modules configuration found for locale "${locale}"`);
31
+ return results;
32
+ }
33
+
34
+ for (const [moduleName, moduleTranslations] of Object.entries(translationsConfig.modules)) {
35
+ if (moduleTranslations[locale]) {
36
+ try {
37
+ const loader = moduleTranslations[locale];
38
+ const translations = await loader();
39
+
40
+ results.push({
41
+ moduleName,
42
+ translations
43
+ });
44
+ } catch (error) {
45
+ console.error(`❌ Failed to load translations for module "${moduleName}" and locale "${locale}"`, error);
46
+ }
47
+ } else {
48
+ console.warn(`⚠️ No translations for module "${moduleName}" in locale "${locale}"`);
49
+ }
50
+ }
51
+ return results;
52
+ };
53
+
54
+ export const loadModuleTranslations = async (
55
+ moduleName: string,
56
+ locale: string,
57
+ translationsConfig: TranslationsConfig
58
+ ): Promise<{ moduleName: string; translations: Record<string, any> } | undefined> => {
59
+ if (!translationsConfig?.modules?.[moduleName]?.[locale]) {
60
+ console.warn(`❌ No translations config found for module "${moduleName}" and locale "${locale}"`);
61
+ console.warn('Available modules:', translationsConfig?.modules ? Object.keys(translationsConfig.modules) : 'none');
62
+ if (translationsConfig?.modules?.[moduleName]) {
63
+ console.warn(`Available locales for ${moduleName}:`, Object.keys(translationsConfig.modules[moduleName]));
64
+ }
65
+ return undefined;
66
+ }
67
+
68
+ try {
69
+ const loader = translationsConfig.modules[moduleName][locale];
70
+ const translations = await loader();
71
+ return {
72
+ moduleName,
73
+ translations
74
+ };
75
+ } catch (error) {
76
+ console.error(`❌ Failed to load translations for module "${moduleName}" and locale "${locale}"`, error);
77
+ return undefined;
78
+ }
79
+ };
@@ -0,0 +1,15 @@
1
+ export const mergeDeep = (target: any, source: any): any => {
2
+ if (typeof target !== 'object' || typeof source !== 'object') return source;
3
+
4
+ const output = { ...target };
5
+ for (const key in source) {
6
+ if (source.hasOwnProperty(key)) {
7
+ if (target.hasOwnProperty(key)) {
8
+ output[key] = mergeDeep(target[key], source[key]);
9
+ } else {
10
+ output[key] = source[key];
11
+ }
12
+ }
13
+ }
14
+ return output;
15
+ };
package/utils.ts ADDED
File without changes