@gravito/cosmos 1.0.0-alpha.2 → 1.0.0-alpha.3
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/dist/index.js +66 -25
- package/dist/src/index.js +49 -1
- package/ion/src/index.js +2249 -0
- package/package.json +1 -1
- package/signal/src/index.js +101809 -0
- package/src/I18nService.ts +101 -36
- package/src/index.ts +6 -13
package/src/I18nService.ts
CHANGED
|
@@ -14,17 +14,22 @@ export interface I18nService {
|
|
|
14
14
|
getLocale(): string
|
|
15
15
|
t(key: string, replacements?: Record<string, string | number>): string
|
|
16
16
|
has(key: string): boolean
|
|
17
|
+
// Create a request-scoped instance
|
|
18
|
+
clone(locale?: string): I18nService
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Request-scoped I18n Instance
|
|
23
|
+
* Holds the state (locale) for a single request, but shares the heavy resources (translations)
|
|
24
|
+
*/
|
|
25
|
+
export class I18nInstance implements I18nService {
|
|
20
26
|
private _locale: string
|
|
21
|
-
private translations: Record<string, Record<string, string>> = {}
|
|
22
27
|
|
|
23
|
-
constructor(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
constructor(
|
|
29
|
+
private manager: I18nManager,
|
|
30
|
+
initialLocale: string
|
|
31
|
+
) {
|
|
32
|
+
this._locale = initialLocale
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
get locale(): string {
|
|
@@ -36,7 +41,7 @@ export class I18nManager implements I18nService {
|
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
setLocale(locale: string) {
|
|
39
|
-
if (this.
|
|
44
|
+
if (this.manager.getConfig().supportedLocales.includes(locale)) {
|
|
40
45
|
this._locale = locale
|
|
41
46
|
}
|
|
42
47
|
}
|
|
@@ -45,9 +50,71 @@ export class I18nManager implements I18nService {
|
|
|
45
50
|
return this._locale
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
t(key: string, replacements?: Record<string, string | number>): string {
|
|
54
|
+
return this.manager.translate(this._locale, key, replacements)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
has(key: string): boolean {
|
|
58
|
+
return this.t(key) !== key
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
clone(locale?: string): I18nService {
|
|
62
|
+
return new I18nInstance(this.manager, locale || this._locale)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Global I18n Manager
|
|
68
|
+
* Holds shared configuration and translation resources
|
|
69
|
+
*/
|
|
70
|
+
export class I18nManager implements I18nService {
|
|
71
|
+
private translations: Record<string, Record<string, string>> = {}
|
|
72
|
+
// Default instance for global usage (e.g. CLI or background jobs)
|
|
73
|
+
private globalInstance: I18nInstance
|
|
74
|
+
|
|
75
|
+
constructor(private config: I18nConfig) {
|
|
76
|
+
if (config.translations) {
|
|
77
|
+
this.translations = config.translations
|
|
78
|
+
}
|
|
79
|
+
this.globalInstance = new I18nInstance(this, config.defaultLocale)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// --- I18nService Implementation (Delegates to global instance) ---
|
|
83
|
+
|
|
84
|
+
get locale(): string {
|
|
85
|
+
return this.globalInstance.locale
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
set locale(value: string) {
|
|
89
|
+
this.globalInstance.locale = value
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
setLocale(locale: string): void {
|
|
93
|
+
this.globalInstance.setLocale(locale)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getLocale(): string {
|
|
97
|
+
return this.globalInstance.getLocale()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
t(key: string, replacements?: Record<string, string | number>): string {
|
|
101
|
+
return this.globalInstance.t(key, replacements)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
has(key: string): boolean {
|
|
105
|
+
return this.globalInstance.has(key)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
clone(locale?: string): I18nService {
|
|
109
|
+
return new I18nInstance(this, locale || this.config.defaultLocale)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// --- Manager Internal API ---
|
|
113
|
+
|
|
114
|
+
getConfig(): I18nConfig {
|
|
115
|
+
return this.config
|
|
116
|
+
}
|
|
117
|
+
|
|
51
118
|
addResource(locale: string, translations: Record<string, string>) {
|
|
52
119
|
this.translations[locale] = {
|
|
53
120
|
...(this.translations[locale] || {}),
|
|
@@ -56,16 +123,15 @@ export class I18nManager implements I18nService {
|
|
|
56
123
|
}
|
|
57
124
|
|
|
58
125
|
/**
|
|
59
|
-
*
|
|
60
|
-
* t('messages.welcome', { name: 'Carl' })
|
|
61
|
-
* Supports nested keys via dot notation: t('auth.errors.invalid')
|
|
126
|
+
* Internal translation logic used by instances
|
|
62
127
|
*/
|
|
63
|
-
|
|
128
|
+
translate(
|
|
129
|
+
locale: string,
|
|
130
|
+
key: string,
|
|
131
|
+
replacements?: Record<string, string | number>
|
|
132
|
+
): string {
|
|
64
133
|
const keys = key.split('.')
|
|
65
|
-
let value: any = this.translations[
|
|
66
|
-
|
|
67
|
-
// Fallback to default locale if not found in current locale?
|
|
68
|
-
// Implementation: Try current locale, then fallback.
|
|
134
|
+
let value: any = this.translations[locale]
|
|
69
135
|
|
|
70
136
|
// 1. Try current locale
|
|
71
137
|
for (const k of keys) {
|
|
@@ -78,7 +144,7 @@ export class I18nManager implements I18nService {
|
|
|
78
144
|
}
|
|
79
145
|
|
|
80
146
|
// 2. If not found, try fallback (defaultLocale)
|
|
81
|
-
if (value === undefined &&
|
|
147
|
+
if (value === undefined && locale !== this.config.defaultLocale) {
|
|
82
148
|
let fallbackValue: any = this.translations[this.config.defaultLocale]
|
|
83
149
|
for (const k of keys) {
|
|
84
150
|
if (fallbackValue && typeof fallbackValue === 'object' && k in fallbackValue) {
|
|
@@ -104,11 +170,6 @@ export class I18nManager implements I18nService {
|
|
|
104
170
|
|
|
105
171
|
return value
|
|
106
172
|
}
|
|
107
|
-
|
|
108
|
-
has(key: string): boolean {
|
|
109
|
-
// Simplistic check
|
|
110
|
-
return this.t(key) !== key
|
|
111
|
-
}
|
|
112
173
|
}
|
|
113
174
|
|
|
114
175
|
/**
|
|
@@ -118,21 +179,25 @@ export class I18nManager implements I18nService {
|
|
|
118
179
|
* 1. Route Parameter (e.g. /:locale/foo) - Recommended for SEO
|
|
119
180
|
* 2. Header (Accept-Language) - Recommended for APIs
|
|
120
181
|
*/
|
|
121
|
-
export const localeMiddleware = (
|
|
182
|
+
export const localeMiddleware = (i18nManager: I18nService): MiddlewareHandler => {
|
|
122
183
|
return async (c, next) => {
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
184
|
+
// Determine initial locale
|
|
185
|
+
// Priority: 1. Route Param 2. Query ?lang= 3. Header 4. Default
|
|
186
|
+
let locale = c.req.param('locale') || c.req.query('lang')
|
|
187
|
+
|
|
188
|
+
if (!locale) {
|
|
189
|
+
const acceptLang = c.req.header('Accept-Language')
|
|
190
|
+
if (acceptLang) {
|
|
191
|
+
// Simple extraction: 'en-US,en;q=0.9' -> 'en-US'
|
|
192
|
+
locale = acceptLang.split(',')[0]?.trim()
|
|
193
|
+
}
|
|
127
194
|
}
|
|
128
195
|
|
|
129
|
-
//
|
|
130
|
-
|
|
196
|
+
// Clone a request-scoped instance
|
|
197
|
+
const i18n = i18nManager.clone(locale)
|
|
131
198
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
// For Inertia, we might want to share it as a prop.
|
|
135
|
-
// This part depends on how the user sets up their detailed pipeline, but setting it in 'c' is the start.
|
|
199
|
+
// Inject into context
|
|
200
|
+
c.set('i18n', i18n)
|
|
136
201
|
|
|
137
202
|
await next()
|
|
138
203
|
}
|
package/src/index.ts
CHANGED
|
@@ -11,21 +11,14 @@ export class I18nOrbit implements GravitoOrbit {
|
|
|
11
11
|
constructor(private config: I18nConfig) {}
|
|
12
12
|
|
|
13
13
|
install(core: PlanetCore): void {
|
|
14
|
-
const
|
|
14
|
+
const i18nManager = new I18nManager(this.config)
|
|
15
15
|
|
|
16
|
-
// Register globally if needed
|
|
17
|
-
//
|
|
18
|
-
// Ideally we attach it to the core instance or inject it into every request.
|
|
16
|
+
// Register globally if needed (for CLI/Jobs)
|
|
17
|
+
// core.services.set('i18n', i18nManager);
|
|
19
18
|
|
|
20
|
-
// Inject into every request
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
await next()
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
// Register a helper if using Orbit View (View Rendering)
|
|
27
|
-
// We can check if 'view' exists or we can register a global view helper if that API exists.
|
|
28
|
-
// For now, context injection is sufficient.
|
|
19
|
+
// Inject locale middleware into every request
|
|
20
|
+
// This middleware handles cloning the i18n instance per request
|
|
21
|
+
core.adapter.use('*', localeMiddleware(i18nManager) as any)
|
|
29
22
|
|
|
30
23
|
core.logger.info(`I18n Orbit initialized with locale: ${this.config.defaultLocale}`)
|
|
31
24
|
}
|