@gravito/cosmos 3.0.1 → 3.2.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.
- package/MIGRATION.md +331 -0
- package/README.md +105 -45
- package/README.zh-TW.md +102 -22
- package/docs/plans/01-performance.md +187 -0
- package/docs/plans/02-architecture.md +309 -0
- package/docs/plans/03-api-enhancement.md +345 -0
- package/docs/plans/04-testing.md +431 -0
- package/docs/plans/README.md +47 -0
- package/ion/src/index.js +1179 -1138
- package/package.json +22 -6
- package/scripts/check-coverage.ts +64 -0
- package/src/HMRWatcher.ts +305 -0
- package/src/I18nService.ts +715 -91
- package/src/index.edge.ts +35 -0
- package/src/index.node.ts +20 -0
- package/src/index.ts +39 -6
- package/src/loader.ts +64 -14
- package/src/loaders/ChainedLoader.ts +117 -0
- package/src/loaders/CloudflareKVLoader.ts +194 -0
- package/src/loaders/EdgeKVLoader.ts +248 -0
- package/src/loaders/FileSystemLoader.ts +125 -0
- package/src/loaders/MemoryLoader.ts +161 -0
- package/src/loaders/RemoteLoader.ts +235 -0
- package/src/loaders/TranslationLoader.ts +98 -0
- package/src/loaders/VercelKVLoader.ts +192 -0
- package/src/runtime/detector.ts +97 -0
- package/src/runtime/path-utils.ts +169 -0
- package/tests/helpers/factory.ts +41 -0
- package/tests/performance/translate.bench.ts +27 -0
- package/tests/unit/api.test.ts +37 -0
- package/tests/unit/detector.test.ts +65 -0
- package/tests/unit/edge-kv-loader.test.ts +202 -0
- package/tests/unit/edge.test.ts +100 -0
- package/tests/unit/fallback.test.ts +66 -0
- package/tests/unit/hmr.test.ts +255 -0
- package/tests/unit/lazy.test.ts +35 -0
- package/tests/unit/loader.test.ts +72 -0
- package/tests/unit/loaders.test.ts +332 -0
- package/tests/{manager.test.ts → unit/manager.test.ts} +1 -1
- package/tests/unit/memory-loader.test.ts +130 -0
- package/tests/unit/path-utils.test.ts +135 -0
- package/tests/unit/plural.test.ts +58 -0
- package/tests/unit/runtime-detector.test.ts +86 -0
- package/tests/{service.test.ts → unit/service.test.ts} +2 -2
- package/tsconfig.json +12 -24
- package/.turbo/turbo-build.log +0 -20
- package/.turbo/turbo-test$colon$ci.log +0 -35
- package/.turbo/turbo-test$colon$coverage.log +0 -35
- package/.turbo/turbo-test.log +0 -27
- package/.turbo/turbo-typecheck.log +0 -2
- package/dist/index.cjs +0 -309
- package/dist/index.d.cts +0 -274
- package/dist/index.d.ts +0 -274
- package/dist/index.js +0 -277
- package/tests/loader.test.ts +0 -44
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/cosmos/src/loaders/EdgeKVLoader.ts
|
|
3
|
+
* @module @gravito/cosmos/loaders
|
|
4
|
+
* @description 通用 Edge KV 儲存載入器
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TranslationMap } from '../I18nService'
|
|
8
|
+
import type { LoaderConfig, TranslationLoader, TranslationLoaderChain } from './TranslationLoader'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* KV 儲存介面(通用抽象)
|
|
12
|
+
*
|
|
13
|
+
* 定義通用的 KV 儲存操作介面
|
|
14
|
+
* 可以被不同的 KV 儲存後端實現
|
|
15
|
+
*
|
|
16
|
+
* @public
|
|
17
|
+
* @since 3.2.0
|
|
18
|
+
*/
|
|
19
|
+
export interface KVStorage {
|
|
20
|
+
/**
|
|
21
|
+
* 從 KV 儲存讀取值
|
|
22
|
+
*
|
|
23
|
+
* @param key - 鍵
|
|
24
|
+
* @returns 值,如果不存在則返回 null
|
|
25
|
+
*/
|
|
26
|
+
get(key: string): Promise<string | null>
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 寫入值到 KV 儲存(可選)
|
|
30
|
+
*
|
|
31
|
+
* @param key - 鍵
|
|
32
|
+
* @param value - 值
|
|
33
|
+
*/
|
|
34
|
+
put?(key: string, value: string): Promise<void>
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 從 KV 儲存刪除值(可選)
|
|
38
|
+
*
|
|
39
|
+
* @param key - 鍵
|
|
40
|
+
*/
|
|
41
|
+
delete?(key: string): Promise<void>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Edge KV 載入器配置
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
* @since 3.2.0
|
|
49
|
+
*/
|
|
50
|
+
export interface EdgeKVLoaderConfig extends LoaderConfig {
|
|
51
|
+
/**
|
|
52
|
+
* KV 儲存實例
|
|
53
|
+
*/
|
|
54
|
+
storage: KVStorage
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Key 前綴
|
|
58
|
+
*
|
|
59
|
+
* @default 'i18n'
|
|
60
|
+
*/
|
|
61
|
+
prefix?: string
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Key 模板
|
|
65
|
+
*
|
|
66
|
+
* 可用的佔位符:
|
|
67
|
+
* - `:prefix` - Key 前綴
|
|
68
|
+
* - `:locale` - 語言代碼
|
|
69
|
+
*
|
|
70
|
+
* @default ':prefix::locale'
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // 使用冒號分隔
|
|
75
|
+
* keyTemplate: ':prefix::locale' // 'i18n:zh-TW'
|
|
76
|
+
*
|
|
77
|
+
* // 使用斜線分隔
|
|
78
|
+
* keyTemplate: ':prefix/:locale.json' // 'i18n/zh-TW.json'
|
|
79
|
+
*
|
|
80
|
+
* // 僅使用語言代碼
|
|
81
|
+
* keyTemplate: ':locale' // 'zh-TW'
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
keyTemplate?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 通用 Edge KV 載入器
|
|
89
|
+
*
|
|
90
|
+
* 支援任何符合 KVStorage 介面的儲存後端
|
|
91
|
+
* 適用於 Edge Runtime 環境的 KV 儲存
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
* @since 3.2.0
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // 自訂 KV 儲存實現
|
|
99
|
+
* const customStorage: KVStorage = {
|
|
100
|
+
* async get(key: string) {
|
|
101
|
+
* // 從你的 KV 儲存讀取
|
|
102
|
+
* return await myKV.get(key)
|
|
103
|
+
* }
|
|
104
|
+
* }
|
|
105
|
+
*
|
|
106
|
+
* const loader = new EdgeKVLoader({
|
|
107
|
+
* storage: customStorage,
|
|
108
|
+
* prefix: 'translations',
|
|
109
|
+
* keyTemplate: ':prefix/:locale.json'
|
|
110
|
+
* })
|
|
111
|
+
*
|
|
112
|
+
* const translations = await loader.load('zh-TW')
|
|
113
|
+
* // 讀取 key: 'translations/zh-TW.json'
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* ## 適用場景
|
|
117
|
+
*
|
|
118
|
+
* - **Cloudflare Workers KV**: 使用 CloudflareKVLoader
|
|
119
|
+
* - **Vercel KV**: 使用 VercelKVLoader
|
|
120
|
+
* - **自訂 KV 儲存**: 實現 KVStorage 介面
|
|
121
|
+
*
|
|
122
|
+
* ## 優缺點
|
|
123
|
+
*
|
|
124
|
+
* **優點**:
|
|
125
|
+
* - Edge Runtime 原生支援
|
|
126
|
+
* - 低延遲讀取(邊緣快取)
|
|
127
|
+
* - 支援動態更新
|
|
128
|
+
*
|
|
129
|
+
* **缺點**:
|
|
130
|
+
* - 需要額外的儲存服務
|
|
131
|
+
* - 可能有額外成本
|
|
132
|
+
* - 需要預先上傳翻譯到 KV
|
|
133
|
+
*/
|
|
134
|
+
export class EdgeKVLoader implements TranslationLoaderChain {
|
|
135
|
+
public readonly name: string
|
|
136
|
+
private storage: KVStorage
|
|
137
|
+
private prefix: string
|
|
138
|
+
private keyTemplate: string
|
|
139
|
+
private fallbackLoader?: TranslationLoader
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 建立 Edge KV 載入器實例
|
|
143
|
+
*
|
|
144
|
+
* @param config - 載入器配置
|
|
145
|
+
*/
|
|
146
|
+
constructor(config: EdgeKVLoaderConfig) {
|
|
147
|
+
this.name = config.name || 'EdgeKVLoader'
|
|
148
|
+
this.storage = config.storage
|
|
149
|
+
this.prefix = config.prefix || 'i18n'
|
|
150
|
+
this.keyTemplate = config.keyTemplate || ':prefix::locale'
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 載入指定語言的翻譯資源
|
|
155
|
+
*
|
|
156
|
+
* @param locale - 語言代碼
|
|
157
|
+
* @returns 翻譯資源,載入失敗則返回 null 或嘗試 fallback
|
|
158
|
+
*/
|
|
159
|
+
async load(locale: string): Promise<TranslationMap | null> {
|
|
160
|
+
try {
|
|
161
|
+
const key = this.buildKey(locale)
|
|
162
|
+
const value = await this.storage.get(key)
|
|
163
|
+
|
|
164
|
+
if (!value) {
|
|
165
|
+
return this.tryFallback(locale)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return JSON.parse(value) as TranslationMap
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error(`[EdgeKVLoader] Failed to load ${locale}:`, error)
|
|
171
|
+
return this.tryFallback(locale)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 建立 KV 儲存的 key
|
|
177
|
+
*
|
|
178
|
+
* @param locale - 語言代碼
|
|
179
|
+
* @returns KV key
|
|
180
|
+
*/
|
|
181
|
+
private buildKey(locale: string): string {
|
|
182
|
+
return this.keyTemplate.replace(':prefix', this.prefix).replace(':locale', locale)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 嘗試使用備用載入器
|
|
187
|
+
*
|
|
188
|
+
* @param locale - 語言代碼
|
|
189
|
+
* @returns 翻譯資源或 null
|
|
190
|
+
*/
|
|
191
|
+
private async tryFallback(locale: string): Promise<TranslationMap | null> {
|
|
192
|
+
if (this.fallbackLoader) {
|
|
193
|
+
return this.fallbackLoader.load(locale)
|
|
194
|
+
}
|
|
195
|
+
return null
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 設定備用載入器
|
|
200
|
+
*
|
|
201
|
+
* @param loader - 備用載入器
|
|
202
|
+
* @returns 當前實例,支援鏈式調用
|
|
203
|
+
*/
|
|
204
|
+
fallback(loader: TranslationLoader): TranslationLoaderChain {
|
|
205
|
+
this.fallbackLoader = loader
|
|
206
|
+
return this
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 儲存翻譯資源到 KV 儲存(如果儲存支援 put 操作)
|
|
211
|
+
*
|
|
212
|
+
* @param locale - 語言代碼
|
|
213
|
+
* @param translations - 翻譯資源
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* await loader.put('zh-TW', { hello: '你好' })
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
async put(locale: string, translations: TranslationMap): Promise<void> {
|
|
221
|
+
if (!this.storage.put) {
|
|
222
|
+
throw new Error('[EdgeKVLoader] Storage does not support put operation')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const key = this.buildKey(locale)
|
|
226
|
+
const value = JSON.stringify(translations)
|
|
227
|
+
await this.storage.put(key, value)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 從 KV 儲存刪除翻譯資源(如果儲存支援 delete 操作)
|
|
232
|
+
*
|
|
233
|
+
* @param locale - 語言代碼
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```typescript
|
|
237
|
+
* await loader.delete('zh-TW')
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
async delete(locale: string): Promise<void> {
|
|
241
|
+
if (!this.storage.delete) {
|
|
242
|
+
throw new Error('[EdgeKVLoader] Storage does not support delete operation')
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const key = this.buildKey(locale)
|
|
246
|
+
await this.storage.delete(key)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/cosmos/src/loaders/FileSystemLoader.ts
|
|
3
|
+
* @module @gravito/cosmos/loaders
|
|
4
|
+
* @description 從檔案系統載入翻譯資源的實現
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ **Node.js Only**
|
|
7
|
+
*
|
|
8
|
+
* 此載入器依賴 Node.js 的 fs 模組,無法在 Edge Runtime 使用
|
|
9
|
+
*
|
|
10
|
+
* Edge Runtime 替代方案:
|
|
11
|
+
* - MemoryLoader: 靜態翻譯
|
|
12
|
+
* - RemoteLoader: HTTP API
|
|
13
|
+
* - EdgeKVLoader: KV 儲存
|
|
14
|
+
*
|
|
15
|
+
* @since 3.1.0
|
|
16
|
+
* @platform node
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFile } from 'node:fs/promises'
|
|
20
|
+
import { join } from 'node:path'
|
|
21
|
+
import type { TranslationMap } from '../I18nService'
|
|
22
|
+
import { detectRuntime } from '../runtime/detector'
|
|
23
|
+
import type { LoaderConfig, TranslationLoader, TranslationLoaderChain } from './TranslationLoader'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 檔案系統載入器配置
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
* @since 3.1.0
|
|
30
|
+
*/
|
|
31
|
+
export interface FileSystemLoaderConfig extends LoaderConfig {
|
|
32
|
+
/**
|
|
33
|
+
* 翻譯檔案所在的基礎目錄
|
|
34
|
+
*
|
|
35
|
+
* @example '/app/lang'
|
|
36
|
+
*/
|
|
37
|
+
baseDir: string
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 檔案副檔名
|
|
41
|
+
*
|
|
42
|
+
* @default '.json'
|
|
43
|
+
*/
|
|
44
|
+
extension?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 檔案系統翻譯載入器
|
|
49
|
+
*
|
|
50
|
+
* 從本地檔案系統載入 JSON 格式的翻譯檔案
|
|
51
|
+
* 預設尋找 `{baseDir}/{locale}.json` 格式的檔案
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
* @since 3.1.0
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const loader = new FileSystemLoader({
|
|
59
|
+
* baseDir: '/app/lang',
|
|
60
|
+
* name: 'fs-loader'
|
|
61
|
+
* })
|
|
62
|
+
*
|
|
63
|
+
* const translations = await loader.load('zh-TW')
|
|
64
|
+
* // 載入 /app/lang/zh-TW.json
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export class FileSystemLoader implements TranslationLoaderChain {
|
|
68
|
+
public readonly name: string
|
|
69
|
+
private baseDir: string
|
|
70
|
+
private extension: string
|
|
71
|
+
private fallbackLoader?: TranslationLoader
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 建立檔案系統載入器實例
|
|
75
|
+
*
|
|
76
|
+
* @param config - 載入器配置
|
|
77
|
+
* @throws {Error} 如果在 Edge Runtime 環境中使用
|
|
78
|
+
*/
|
|
79
|
+
constructor(config: FileSystemLoaderConfig) {
|
|
80
|
+
// 運行時檢查
|
|
81
|
+
const runtime = detectRuntime()
|
|
82
|
+
if (runtime === 'edge') {
|
|
83
|
+
throw new Error(
|
|
84
|
+
'[FileSystemLoader] This loader requires Node.js and cannot run in Edge Runtime. ' +
|
|
85
|
+
'Use MemoryLoader, RemoteLoader, or EdgeKVLoader instead.'
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.name = config.name || 'FileSystemLoader'
|
|
90
|
+
this.baseDir = config.baseDir
|
|
91
|
+
this.extension = config.extension || '.json'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 載入指定語言的翻譯資源
|
|
96
|
+
*
|
|
97
|
+
* @param locale - 語言代碼
|
|
98
|
+
* @returns 翻譯資源,載入失敗則返回 null
|
|
99
|
+
*/
|
|
100
|
+
async load(locale: string): Promise<TranslationMap | null> {
|
|
101
|
+
try {
|
|
102
|
+
const filePath = join(this.baseDir, `${locale}${this.extension}`)
|
|
103
|
+
const content = await readFile(filePath, 'utf-8')
|
|
104
|
+
const translations = JSON.parse(content) as TranslationMap
|
|
105
|
+
return translations
|
|
106
|
+
} catch (_error) {
|
|
107
|
+
// 如果有備用載入器,嘗試使用備用載入器
|
|
108
|
+
if (this.fallbackLoader) {
|
|
109
|
+
return this.fallbackLoader.load(locale)
|
|
110
|
+
}
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 設定備用載入器
|
|
117
|
+
*
|
|
118
|
+
* @param loader - 備用載入器
|
|
119
|
+
* @returns 當前實例,支援鏈式調用
|
|
120
|
+
*/
|
|
121
|
+
fallback(loader: TranslationLoader): TranslationLoaderChain {
|
|
122
|
+
this.fallbackLoader = loader
|
|
123
|
+
return this
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/cosmos/src/loaders/MemoryLoader.ts
|
|
3
|
+
* @module @gravito/cosmos/loaders
|
|
4
|
+
* @description 從記憶體載入翻譯資源的實現
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TranslationMap } from '../I18nService'
|
|
8
|
+
import type { LoaderConfig, TranslationLoader, TranslationLoaderChain } from './TranslationLoader'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 記憶體載入器配置
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
* @since 3.2.0
|
|
15
|
+
*/
|
|
16
|
+
export interface MemoryLoaderConfig extends LoaderConfig {
|
|
17
|
+
/**
|
|
18
|
+
* 預先載入的翻譯資源
|
|
19
|
+
*
|
|
20
|
+
* Key 為語言代碼,Value 為翻譯 Map
|
|
21
|
+
*/
|
|
22
|
+
translations: Record<string, TranslationMap>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 記憶體翻譯載入器
|
|
27
|
+
*
|
|
28
|
+
* 從記憶體載入預先打包的翻譯資源
|
|
29
|
+
* 適用於 Edge Runtime 環境或靜態翻譯場景
|
|
30
|
+
*
|
|
31
|
+
* @public
|
|
32
|
+
* @since 3.2.0
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import { MemoryLoader } from '@gravito/cosmos'
|
|
37
|
+
* import en from './lang/en.json'
|
|
38
|
+
* import zhTW from './lang/zh-TW.json'
|
|
39
|
+
*
|
|
40
|
+
* const loader = new MemoryLoader({
|
|
41
|
+
* translations: {
|
|
42
|
+
* en,
|
|
43
|
+
* 'zh-TW': zhTW
|
|
44
|
+
* }
|
|
45
|
+
* })
|
|
46
|
+
*
|
|
47
|
+
* const translations = await loader.load('zh-TW')
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* ## 適用場景
|
|
51
|
+
*
|
|
52
|
+
* - **靜態翻譯**: Build-time 打包的翻譯資源
|
|
53
|
+
* - **Edge Runtime**: 無檔案系統的邊緣環境
|
|
54
|
+
* - **測試環境**: 快速的測試資料載入
|
|
55
|
+
* - **小型應用**: 翻譯量較少的應用(<100KB)
|
|
56
|
+
*
|
|
57
|
+
* ## 優缺點
|
|
58
|
+
*
|
|
59
|
+
* **優點**:
|
|
60
|
+
* - 極快的載入速度(記憶體讀取)
|
|
61
|
+
* - 無需網路請求
|
|
62
|
+
* - Edge Runtime 相容
|
|
63
|
+
*
|
|
64
|
+
* **缺點**:
|
|
65
|
+
* - 增加 Bundle Size
|
|
66
|
+
* - 無法動態更新翻譯
|
|
67
|
+
* - 不適合大量翻譯
|
|
68
|
+
*/
|
|
69
|
+
export class MemoryLoader implements TranslationLoaderChain {
|
|
70
|
+
public readonly name: string
|
|
71
|
+
private translations: Record<string, TranslationMap>
|
|
72
|
+
private fallbackLoader?: TranslationLoader
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 建立記憶體載入器實例
|
|
76
|
+
*
|
|
77
|
+
* @param config - 載入器配置
|
|
78
|
+
*/
|
|
79
|
+
constructor(config: MemoryLoaderConfig) {
|
|
80
|
+
this.name = config.name || 'MemoryLoader'
|
|
81
|
+
this.translations = config.translations
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 載入指定語言的翻譯資源
|
|
86
|
+
*
|
|
87
|
+
* @param locale - 語言代碼
|
|
88
|
+
* @returns 翻譯資源,如果不存在則嘗試使用 fallback loader
|
|
89
|
+
*/
|
|
90
|
+
async load(locale: string): Promise<TranslationMap | null> {
|
|
91
|
+
const translations = this.translations[locale]
|
|
92
|
+
|
|
93
|
+
if (translations) {
|
|
94
|
+
return translations
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 嘗試使用備用載入器
|
|
98
|
+
if (this.fallbackLoader) {
|
|
99
|
+
return this.fallbackLoader.load(locale)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 設定備用載入器
|
|
107
|
+
*
|
|
108
|
+
* @param loader - 備用載入器
|
|
109
|
+
* @returns 當前實例,支援鏈式調用
|
|
110
|
+
*/
|
|
111
|
+
fallback(loader: TranslationLoader): TranslationLoaderChain {
|
|
112
|
+
this.fallbackLoader = loader
|
|
113
|
+
return this
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 新增或更新語言的翻譯資源
|
|
118
|
+
*
|
|
119
|
+
* @param locale - 語言代碼
|
|
120
|
+
* @param translations - 翻譯資源
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* loader.addTranslations('fr', { hello: 'Bonjour' })
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
addTranslations(locale: string, translations: TranslationMap): void {
|
|
128
|
+
this.translations[locale] = {
|
|
129
|
+
...(this.translations[locale] || {}),
|
|
130
|
+
...translations,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 移除語言的翻譯資源
|
|
136
|
+
*
|
|
137
|
+
* @param locale - 語言代碼
|
|
138
|
+
*/
|
|
139
|
+
removeTranslations(locale: string): void {
|
|
140
|
+
delete this.translations[locale]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 取得所有已載入的語言列表
|
|
145
|
+
*
|
|
146
|
+
* @returns 語言代碼陣列
|
|
147
|
+
*/
|
|
148
|
+
getLoadedLocales(): string[] {
|
|
149
|
+
return Object.keys(this.translations)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 檢查是否已載入指定語言
|
|
154
|
+
*
|
|
155
|
+
* @param locale - 語言代碼
|
|
156
|
+
* @returns 是否已載入
|
|
157
|
+
*/
|
|
158
|
+
hasLocale(locale: string): boolean {
|
|
159
|
+
return locale in this.translations
|
|
160
|
+
}
|
|
161
|
+
}
|