@hua-labs/i18n-core 1.0.0 → 2.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.
@@ -1,255 +1,255 @@
1
- import { TranslationNamespace } from '../types';
2
- import { i18nResourceManager } from './i18n-resource';
3
-
4
- /**
5
- * 네임스페이스별 지연 로딩 전략
6
- * 대규모 앱에서 번역 단위 최적화를 위한 loadOnDemand 함수
7
- */
8
- export class LazyLoader {
9
- private static instance: LazyLoader;
10
- private loadingQueue = new Map<string, Promise<TranslationNamespace>>();
11
- private preloadCache = new Set<string>();
12
- private loadHistory = new Map<string, number>();
13
-
14
- private constructor() {}
15
-
16
- static getInstance(): LazyLoader {
17
- if (!LazyLoader.instance) {
18
- LazyLoader.instance = new LazyLoader();
19
- }
20
- return LazyLoader.instance;
21
- }
22
-
23
- /**
24
- * 필요할 때 네임스페이스 로딩
25
- */
26
- async loadOnDemand(
27
- language: string,
28
- namespace: string,
29
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
30
- ): Promise<TranslationNamespace> {
31
- const cacheKey = `${language}:${namespace}`;
32
-
33
- // 이미 로딩 중이면 기존 Promise 반환
34
- if (this.loadingQueue.has(cacheKey)) {
35
- return this.loadingQueue.get(cacheKey)!;
36
- }
37
-
38
- // 캐시에 있으면 반환
39
- const cached = i18nResourceManager.getCachedTranslationsSync(language, namespace);
40
- if (cached) {
41
- this.updateLoadHistory(cacheKey);
42
- return cached;
43
- }
44
-
45
- // 새로 로딩
46
- const loadPromise = this.performLoad(language, namespace, loader);
47
- this.loadingQueue.set(cacheKey, loadPromise);
48
-
49
- try {
50
- const result = await loadPromise;
51
- this.updateLoadHistory(cacheKey);
52
- return result;
53
- } finally {
54
- this.loadingQueue.delete(cacheKey);
55
- }
56
- }
57
-
58
- /**
59
- * 실제 로딩 수행
60
- */
61
- private async performLoad(
62
- language: string,
63
- namespace: string,
64
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
65
- ): Promise<TranslationNamespace> {
66
- const result = await i18nResourceManager.getCachedTranslations(language, namespace, loader);
67
- return result;
68
- }
69
-
70
- /**
71
- * 로딩 히스토리 업데이트
72
- */
73
- private updateLoadHistory(cacheKey: string): void {
74
- this.loadHistory.set(cacheKey, Date.now());
75
- }
76
-
77
- /**
78
- * 네임스페이스 사전 로딩
79
- */
80
- async preloadNamespace(
81
- language: string,
82
- namespace: string,
83
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
84
- ): Promise<void> {
85
- const cacheKey = `${language}:${namespace}`;
86
-
87
- if (this.preloadCache.has(cacheKey)) {
88
- return; // 이미 사전 로딩됨
89
- }
90
-
91
- try {
92
- await this.loadOnDemand(language, namespace, loader);
93
- this.preloadCache.add(cacheKey);
94
- } catch (error) {
95
- // Silent fail for preloading
96
- }
97
- }
98
-
99
- /**
100
- * 여러 네임스페이스 동시 사전 로딩
101
- */
102
- async preloadMultipleNamespaces(
103
- language: string,
104
- namespaces: string[],
105
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
106
- ): Promise<void> {
107
- const promises = namespaces.map(namespace =>
108
- this.preloadNamespace(language, namespace, loader)
109
- );
110
-
111
- await Promise.allSettled(promises);
112
- }
113
-
114
- /**
115
- * 사용 패턴 기반 자동 사전 로딩
116
- */
117
- async autoPreload(
118
- language: string,
119
- currentNamespace: string,
120
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
121
- ): Promise<void> {
122
- // 현재 네임스페이스와 관련된 네임스페이스들 자동 사전 로딩
123
- const relatedNamespaces = this.getRelatedNamespaces(currentNamespace);
124
-
125
- for (const namespace of relatedNamespaces) {
126
- await this.preloadNamespace(language, namespace, loader);
127
- }
128
- }
129
-
130
- /**
131
- * 관련 네임스페이스 추정
132
- */
133
- private getRelatedNamespaces(currentNamespace: string): string[] {
134
- // 실제 구현에서는 사용 패턴 분석을 통해 관련 네임스페이스 추정
135
- const namespacePatterns: Record<string, string[]> = {
136
- 'auth': ['common', 'validation', 'errors'],
137
- 'dashboard': ['common', 'navigation', 'stats'],
138
- 'profile': ['common', 'auth', 'validation'],
139
- 'settings': ['common', 'navigation', 'forms'],
140
- 'common': ['errors', 'validation']
141
- };
142
-
143
- return namespacePatterns[currentNamespace] || [];
144
- }
145
-
146
- /**
147
- * 로딩 우선순위 설정
148
- */
149
- setLoadPriority(namespaces: string[]): void {
150
- // 우선순위가 높은 네임스페이스들을 먼저 로딩
151
- }
152
-
153
- /**
154
- * 로딩 통계 가져오기
155
- */
156
- getLoadStats() {
157
- return {
158
- loadingQueueSize: this.loadingQueue.size,
159
- preloadedCount: this.preloadCache.size,
160
- loadHistorySize: this.loadHistory.size
161
- };
162
- }
163
-
164
- /**
165
- * 메모리 최적화
166
- */
167
- optimizeMemory(): void {
168
- // 오래된 로딩 히스토리 정리
169
- const now = Date.now();
170
- const oneHour = 60 * 60 * 1000;
171
-
172
- for (const [key, timestamp] of this.loadHistory.entries()) {
173
- if (now - timestamp > oneHour) {
174
- this.loadHistory.delete(key);
175
- }
176
- }
177
- }
178
-
179
- /**
180
- * 네임스페이스 사용 빈도 분석
181
- */
182
- analyzeUsagePatterns(): Record<string, number> {
183
- const usage: Record<string, number> = {};
184
-
185
- for (const [key, timestamp] of this.loadHistory.entries()) {
186
- const namespace = key.split(':')[1];
187
- usage[namespace] = (usage[namespace] || 0) + 1;
188
- }
189
-
190
- return usage;
191
- }
192
-
193
- /**
194
- * 캐시 무효화
195
- */
196
- invalidateCache(language?: string, namespace?: string): void {
197
- i18nResourceManager.invalidateCache(language, namespace);
198
-
199
- if (language && namespace) {
200
- const cacheKey = `${language}:${namespace}`;
201
- this.preloadCache.delete(cacheKey);
202
- this.loadHistory.delete(cacheKey);
203
- } else if (language) {
204
- // 특정 언어의 모든 캐시 무효화
205
- for (const key of this.preloadCache) {
206
- if (key.startsWith(`${language}:`)) {
207
- this.preloadCache.delete(key);
208
- this.loadHistory.delete(key);
209
- }
210
- }
211
- } else {
212
- // 전체 캐시 무효화
213
- this.preloadCache.clear();
214
- this.loadHistory.clear();
215
- }
216
- }
217
- }
218
-
219
- /**
220
- * 전역 지연 로더 인스턴스
221
- */
222
- export const lazyLoader = LazyLoader.getInstance();
223
-
224
- /**
225
- * 편의 함수: 필요할 때 로딩
226
- */
227
- export const loadOnDemand = (
228
- language: string,
229
- namespace: string,
230
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
231
- ) => {
232
- return lazyLoader.loadOnDemand(language, namespace, loader);
233
- };
234
-
235
- /**
236
- * 편의 함수: 사전 로딩
237
- */
238
- export const preloadNamespace = (
239
- language: string,
240
- namespace: string,
241
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
242
- ) => {
243
- return lazyLoader.preloadNamespace(language, namespace, loader);
244
- };
245
-
246
- /**
247
- * 편의 함수: 자동 사전 로딩
248
- */
249
- export const autoPreload = (
250
- language: string,
251
- currentNamespace: string,
252
- loader: (lang: string, ns: string) => Promise<TranslationNamespace>
253
- ) => {
254
- return lazyLoader.autoPreload(language, currentNamespace, loader);
1
+ import { TranslationNamespace } from '../types';
2
+ import { i18nResourceManager } from './i18n-resource';
3
+
4
+ /**
5
+ * 네임스페이스별 지연 로딩 전략
6
+ * 대규모 앱에서 번역 단위 최적화를 위한 loadOnDemand 함수
7
+ */
8
+ export class LazyLoader {
9
+ private static instance: LazyLoader;
10
+ private loadingQueue = new Map<string, Promise<TranslationNamespace>>();
11
+ private preloadCache = new Set<string>();
12
+ private loadHistory = new Map<string, number>();
13
+
14
+ private constructor() {}
15
+
16
+ static getInstance(): LazyLoader {
17
+ if (!LazyLoader.instance) {
18
+ LazyLoader.instance = new LazyLoader();
19
+ }
20
+ return LazyLoader.instance;
21
+ }
22
+
23
+ /**
24
+ * 필요할 때 네임스페이스 로딩
25
+ */
26
+ async loadOnDemand(
27
+ language: string,
28
+ namespace: string,
29
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
30
+ ): Promise<TranslationNamespace> {
31
+ const cacheKey = `${language}:${namespace}`;
32
+
33
+ // 이미 로딩 중이면 기존 Promise 반환
34
+ if (this.loadingQueue.has(cacheKey)) {
35
+ return this.loadingQueue.get(cacheKey)!;
36
+ }
37
+
38
+ // 캐시에 있으면 반환
39
+ const cached = i18nResourceManager.getCachedTranslationsSync(language, namespace);
40
+ if (cached) {
41
+ this.updateLoadHistory(cacheKey);
42
+ return cached;
43
+ }
44
+
45
+ // 새로 로딩
46
+ const loadPromise = this.performLoad(language, namespace, loader);
47
+ this.loadingQueue.set(cacheKey, loadPromise);
48
+
49
+ try {
50
+ const result = await loadPromise;
51
+ this.updateLoadHistory(cacheKey);
52
+ return result;
53
+ } finally {
54
+ this.loadingQueue.delete(cacheKey);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 실제 로딩 수행
60
+ */
61
+ private async performLoad(
62
+ language: string,
63
+ namespace: string,
64
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
65
+ ): Promise<TranslationNamespace> {
66
+ const result = await i18nResourceManager.getCachedTranslations(language, namespace, loader);
67
+ return result;
68
+ }
69
+
70
+ /**
71
+ * 로딩 히스토리 업데이트
72
+ */
73
+ private updateLoadHistory(cacheKey: string): void {
74
+ this.loadHistory.set(cacheKey, Date.now());
75
+ }
76
+
77
+ /**
78
+ * 네임스페이스 사전 로딩
79
+ */
80
+ async preloadNamespace(
81
+ language: string,
82
+ namespace: string,
83
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
84
+ ): Promise<void> {
85
+ const cacheKey = `${language}:${namespace}`;
86
+
87
+ if (this.preloadCache.has(cacheKey)) {
88
+ return; // 이미 사전 로딩됨
89
+ }
90
+
91
+ try {
92
+ await this.loadOnDemand(language, namespace, loader);
93
+ this.preloadCache.add(cacheKey);
94
+ } catch (error) {
95
+ // Silent fail for preloading
96
+ }
97
+ }
98
+
99
+ /**
100
+ * 여러 네임스페이스 동시 사전 로딩
101
+ */
102
+ async preloadMultipleNamespaces(
103
+ language: string,
104
+ namespaces: string[],
105
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
106
+ ): Promise<void> {
107
+ const promises = namespaces.map(namespace =>
108
+ this.preloadNamespace(language, namespace, loader)
109
+ );
110
+
111
+ await Promise.allSettled(promises);
112
+ }
113
+
114
+ /**
115
+ * 사용 패턴 기반 자동 사전 로딩
116
+ */
117
+ async autoPreload(
118
+ language: string,
119
+ currentNamespace: string,
120
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
121
+ ): Promise<void> {
122
+ // 현재 네임스페이스와 관련된 네임스페이스들 자동 사전 로딩
123
+ const relatedNamespaces = this.getRelatedNamespaces(currentNamespace);
124
+
125
+ for (const namespace of relatedNamespaces) {
126
+ await this.preloadNamespace(language, namespace, loader);
127
+ }
128
+ }
129
+
130
+ /**
131
+ * 관련 네임스페이스 추정
132
+ */
133
+ private getRelatedNamespaces(currentNamespace: string): string[] {
134
+ // 실제 구현에서는 사용 패턴 분석을 통해 관련 네임스페이스 추정
135
+ const namespacePatterns: Record<string, string[]> = {
136
+ 'auth': ['common', 'validation', 'errors'],
137
+ 'dashboard': ['common', 'navigation', 'stats'],
138
+ 'profile': ['common', 'auth', 'validation'],
139
+ 'settings': ['common', 'navigation', 'forms'],
140
+ 'common': ['errors', 'validation']
141
+ };
142
+
143
+ return namespacePatterns[currentNamespace] || [];
144
+ }
145
+
146
+ /**
147
+ * 로딩 우선순위 설정
148
+ */
149
+ setLoadPriority(namespaces: string[]): void {
150
+ // 우선순위가 높은 네임스페이스들을 먼저 로딩
151
+ }
152
+
153
+ /**
154
+ * 로딩 통계 가져오기
155
+ */
156
+ getLoadStats() {
157
+ return {
158
+ loadingQueueSize: this.loadingQueue.size,
159
+ preloadedCount: this.preloadCache.size,
160
+ loadHistorySize: this.loadHistory.size
161
+ };
162
+ }
163
+
164
+ /**
165
+ * 메모리 최적화
166
+ */
167
+ optimizeMemory(): void {
168
+ // 오래된 로딩 히스토리 정리
169
+ const now = Date.now();
170
+ const oneHour = 60 * 60 * 1000;
171
+
172
+ for (const [key, timestamp] of this.loadHistory.entries()) {
173
+ if (now - timestamp > oneHour) {
174
+ this.loadHistory.delete(key);
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * 네임스페이스 사용 빈도 분석
181
+ */
182
+ analyzeUsagePatterns(): Record<string, number> {
183
+ const usage: Record<string, number> = {};
184
+
185
+ for (const [key, timestamp] of this.loadHistory.entries()) {
186
+ const namespace = key.split(':')[1];
187
+ usage[namespace] = (usage[namespace] || 0) + 1;
188
+ }
189
+
190
+ return usage;
191
+ }
192
+
193
+ /**
194
+ * 캐시 무효화
195
+ */
196
+ invalidateCache(language?: string, namespace?: string): void {
197
+ i18nResourceManager.invalidateCache(language, namespace);
198
+
199
+ if (language && namespace) {
200
+ const cacheKey = `${language}:${namespace}`;
201
+ this.preloadCache.delete(cacheKey);
202
+ this.loadHistory.delete(cacheKey);
203
+ } else if (language) {
204
+ // 특정 언어의 모든 캐시 무효화
205
+ for (const key of this.preloadCache) {
206
+ if (key.startsWith(`${language}:`)) {
207
+ this.preloadCache.delete(key);
208
+ this.loadHistory.delete(key);
209
+ }
210
+ }
211
+ } else {
212
+ // 전체 캐시 무효화
213
+ this.preloadCache.clear();
214
+ this.loadHistory.clear();
215
+ }
216
+ }
217
+ }
218
+
219
+ /**
220
+ * 전역 지연 로더 인스턴스
221
+ */
222
+ export const lazyLoader = LazyLoader.getInstance();
223
+
224
+ /**
225
+ * 편의 함수: 필요할 때 로딩
226
+ */
227
+ export const loadOnDemand = (
228
+ language: string,
229
+ namespace: string,
230
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
231
+ ) => {
232
+ return lazyLoader.loadOnDemand(language, namespace, loader);
233
+ };
234
+
235
+ /**
236
+ * 편의 함수: 사전 로딩
237
+ */
238
+ export const preloadNamespace = (
239
+ language: string,
240
+ namespace: string,
241
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
242
+ ) => {
243
+ return lazyLoader.preloadNamespace(language, namespace, loader);
244
+ };
245
+
246
+ /**
247
+ * 편의 함수: 자동 사전 로딩
248
+ */
249
+ export const autoPreload = (
250
+ language: string,
251
+ currentNamespace: string,
252
+ loader: (lang: string, ns: string) => Promise<TranslationNamespace>
253
+ ) => {
254
+ return lazyLoader.autoPreload(language, currentNamespace, loader);
255
255
  };