@i18n-micro/astro 1.0.1 → 1.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/dist/client/core.d.ts +23 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +17 -0
- package/dist/client/preact.d.ts +30 -0
- package/dist/client/preact.js +51 -0
- package/dist/client/react.d.ts +26 -0
- package/dist/client/react.js +51 -0
- package/dist/client/svelte.d.ts +28 -0
- package/dist/client/svelte.js +70 -0
- package/dist/client/vue.d.ts +27 -0
- package/dist/client/vue.js +1558 -0
- package/dist/composer.d.ts +9 -18
- package/dist/core-Bx9n-eFD.cjs +1 -0
- package/dist/core-D32Y48CN.js +42 -0
- package/dist/env.d.ts +2 -0
- package/dist/index.cjs +17 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.mjs +431 -297
- package/dist/integration.d.ts +5 -1
- package/dist/load-translations.d.ts +44 -0
- package/dist/middleware.d.ts +3 -1
- package/dist/router/adapter.d.ts +8 -0
- package/dist/router/types.d.ts +65 -0
- package/dist/utils.d.ts +17 -1
- package/package.json +57 -13
- package/src/client/core.ts +121 -0
- package/src/client/index.ts +15 -0
- package/src/client/preact.tsx +114 -0
- package/src/client/react.tsx +111 -0
- package/src/client/svelte.ts +124 -0
- package/src/client/vue.ts +128 -0
- package/src/components/i18n-link.astro +37 -4
- package/src/components/i18n-switcher.astro +209 -17
- package/src/components/index.ts +8 -2
- package/src/composer.ts +138 -0
- package/src/env.d.ts +20 -0
- package/src/index.ts +59 -0
- package/src/integration.ts +120 -0
- package/src/load-translations.ts +130 -0
- package/src/middleware.ts +203 -0
- package/src/router/adapter.ts +184 -0
- package/src/router/types.ts +66 -0
- package/src/routing.ts +108 -0
- package/src/utils.ts +401 -0
- package/dist/bridge/astro-bridge.d.ts +0 -13
- package/dist/index-C-UMdqSG.cjs +0 -1
- package/dist/index-CVhedN6W.js +0 -146
- package/dist/toolbar-app.d.ts +0 -2
package/src/utils.ts
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import type { AstroI18n } from './composer'
|
|
2
|
+
import type { Params, Locale, CleanTranslation, TranslationKey, Translations } from '@i18n-micro/types'
|
|
3
|
+
import type { AstroGlobal } from 'astro'
|
|
4
|
+
import type { I18nRoutingStrategy } from './router/types'
|
|
5
|
+
import './env.d'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get i18n instance from Astro context
|
|
9
|
+
*/
|
|
10
|
+
export function getI18n(astro: AstroGlobal): AstroI18n {
|
|
11
|
+
const i18n = astro.locals.i18n
|
|
12
|
+
if (!i18n) {
|
|
13
|
+
throw new Error('i18n instance not found. Make sure i18n middleware is configured.')
|
|
14
|
+
}
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
return i18n
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get current locale from Astro context
|
|
22
|
+
*/
|
|
23
|
+
export function getLocale(astro: AstroGlobal): string {
|
|
24
|
+
return astro.locals.locale || 'en'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get default locale from Astro context
|
|
29
|
+
*/
|
|
30
|
+
export function getDefaultLocale(astro: AstroGlobal): string {
|
|
31
|
+
return astro.locals.defaultLocale || 'en'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get all locales from Astro context
|
|
36
|
+
*/
|
|
37
|
+
export function getLocales(astro: AstroGlobal): Locale[] {
|
|
38
|
+
return astro.locals.locales || []
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get routing strategy from Astro locals
|
|
43
|
+
*/
|
|
44
|
+
function getRoutingStrategy(astro: AstroGlobal): I18nRoutingStrategy | null {
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
return (astro.locals.routingStrategy as I18nRoutingStrategy | undefined) || null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Use i18n in Astro pages/components
|
|
52
|
+
* Returns helper functions for translations
|
|
53
|
+
*/
|
|
54
|
+
export function useI18n(astro: AstroGlobal) {
|
|
55
|
+
const i18n = getI18n(astro)
|
|
56
|
+
const locale = getLocale(astro)
|
|
57
|
+
const defaultLocale = getDefaultLocale(astro)
|
|
58
|
+
const locales = getLocales(astro)
|
|
59
|
+
const localeCodes = locales.map(l => l.code)
|
|
60
|
+
const routingStrategy = getRoutingStrategy(astro)
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
// Current locale
|
|
64
|
+
locale,
|
|
65
|
+
defaultLocale,
|
|
66
|
+
locales,
|
|
67
|
+
|
|
68
|
+
// Translation methods
|
|
69
|
+
t: (key: TranslationKey, params?: Params, defaultValue?: string | null, routeName?: string): CleanTranslation => {
|
|
70
|
+
return i18n.t(key, params, defaultValue, routeName)
|
|
71
|
+
},
|
|
72
|
+
ts: (key: TranslationKey, params?: Params, defaultValue?: string, routeName?: string): string => {
|
|
73
|
+
return i18n.ts(key, params, defaultValue, routeName)
|
|
74
|
+
},
|
|
75
|
+
tc: (key: TranslationKey, count: number | Params, defaultValue?: string): string => {
|
|
76
|
+
return i18n.tc(key, count, defaultValue)
|
|
77
|
+
},
|
|
78
|
+
tn: (value: number, options?: Intl.NumberFormatOptions): string => {
|
|
79
|
+
return i18n.tn(value, options)
|
|
80
|
+
},
|
|
81
|
+
td: (value: Date | number | string, options?: Intl.DateTimeFormatOptions): string => {
|
|
82
|
+
return i18n.td(value, options)
|
|
83
|
+
},
|
|
84
|
+
tdr: (value: Date | number | string, options?: Intl.RelativeTimeFormatOptions): string => {
|
|
85
|
+
return i18n.tdr(value, options)
|
|
86
|
+
},
|
|
87
|
+
has: (key: TranslationKey, routeName?: string): boolean => {
|
|
88
|
+
return i18n.has(key, routeName)
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// Route management
|
|
92
|
+
getRoute: (): string => {
|
|
93
|
+
return i18n.getRoute()
|
|
94
|
+
},
|
|
95
|
+
getRouteName: (path?: string): string => {
|
|
96
|
+
const targetPath = path || astro.url.pathname
|
|
97
|
+
if (routingStrategy?.getRouteName) {
|
|
98
|
+
return routingStrategy.getRouteName(targetPath, localeCodes)
|
|
99
|
+
}
|
|
100
|
+
// Fallback: basic route name extraction
|
|
101
|
+
const cleanPath = targetPath.replace(/^\//, '').replace(/\/$/, '')
|
|
102
|
+
if (!cleanPath) return 'index'
|
|
103
|
+
const segments = cleanPath.split('/').filter(Boolean)
|
|
104
|
+
const firstSegment = segments[0]
|
|
105
|
+
if (firstSegment && localeCodes.includes(firstSegment)) {
|
|
106
|
+
segments.shift()
|
|
107
|
+
}
|
|
108
|
+
return segments.length === 0 ? 'index' : segments.join('-')
|
|
109
|
+
},
|
|
110
|
+
getLocaleFromPath: (path?: string): string => {
|
|
111
|
+
const targetPath = path || astro.url.pathname
|
|
112
|
+
if (routingStrategy?.getLocaleFromPath) {
|
|
113
|
+
return routingStrategy.getLocaleFromPath(targetPath, defaultLocale, localeCodes)
|
|
114
|
+
}
|
|
115
|
+
// Fallback: check first segment
|
|
116
|
+
const segments = targetPath.split('/').filter(Boolean)
|
|
117
|
+
const firstSegment = segments[0]
|
|
118
|
+
return (firstSegment && localeCodes.includes(firstSegment)) ? firstSegment : defaultLocale
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// Path utilities
|
|
122
|
+
switchLocalePath: (newLocale: string): string => {
|
|
123
|
+
if (routingStrategy?.switchLocalePath) {
|
|
124
|
+
return routingStrategy.switchLocalePath(astro.url.pathname, newLocale, localeCodes, defaultLocale)
|
|
125
|
+
}
|
|
126
|
+
// Fallback: basic locale switching
|
|
127
|
+
const segments = astro.url.pathname.split('/').filter(Boolean)
|
|
128
|
+
const firstSegment = segments[0]
|
|
129
|
+
if (firstSegment && localeCodes.includes(firstSegment)) {
|
|
130
|
+
segments.shift()
|
|
131
|
+
}
|
|
132
|
+
if (newLocale !== defaultLocale) {
|
|
133
|
+
segments.unshift(newLocale)
|
|
134
|
+
}
|
|
135
|
+
return `/${segments.join('/')}`
|
|
136
|
+
},
|
|
137
|
+
localizePath: (path: string, targetLocale?: string): string => {
|
|
138
|
+
if (routingStrategy?.localizePath) {
|
|
139
|
+
return routingStrategy.localizePath(path, targetLocale || locale, localeCodes, defaultLocale)
|
|
140
|
+
}
|
|
141
|
+
// Fallback: basic localization
|
|
142
|
+
const cleanPath = path.replace(/^\//, '').replace(/\/$/, '') || ''
|
|
143
|
+
const segments = cleanPath.split('/').filter(Boolean)
|
|
144
|
+
const firstSegment = segments[0]
|
|
145
|
+
if (firstSegment && localeCodes.includes(firstSegment)) {
|
|
146
|
+
segments.shift()
|
|
147
|
+
}
|
|
148
|
+
if (targetLocale && targetLocale !== defaultLocale) {
|
|
149
|
+
segments.unshift(targetLocale)
|
|
150
|
+
}
|
|
151
|
+
return `/${segments.join('/')}`
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Get i18n instance
|
|
155
|
+
getI18n: (): AstroI18n => {
|
|
156
|
+
return i18n
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// Get base path without locale (for rewrite)
|
|
160
|
+
getBasePath: (url?: URL): string => {
|
|
161
|
+
const targetUrl = url || astro.url
|
|
162
|
+
const pathname = targetUrl.pathname
|
|
163
|
+
const segments = pathname.split('/').filter(Boolean)
|
|
164
|
+
|
|
165
|
+
// Remove locale from path if present
|
|
166
|
+
const firstSegment = segments[0]
|
|
167
|
+
if (firstSegment && localeCodes.includes(firstSegment)) {
|
|
168
|
+
segments.shift()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const basePath = segments.length > 0 ? `/${segments.join('/')}` : '/'
|
|
172
|
+
return basePath
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// Translation management
|
|
176
|
+
addTranslations: (locale: string, translations: Record<string, unknown>, merge: boolean = true): void => {
|
|
177
|
+
i18n.addTranslations(locale, translations, merge)
|
|
178
|
+
},
|
|
179
|
+
addRouteTranslations: (locale: string, routeName: string, translations: Record<string, unknown>, merge: boolean = true): void => {
|
|
180
|
+
i18n.addRouteTranslations(locale, routeName, translations, merge)
|
|
181
|
+
},
|
|
182
|
+
mergeTranslations: (locale: string, routeName: string, translations: Record<string, unknown>): void => {
|
|
183
|
+
i18n.mergeTranslations(locale, routeName, translations)
|
|
184
|
+
},
|
|
185
|
+
mergeGlobalTranslations: (locale: string, translations: Record<string, unknown>): void => {
|
|
186
|
+
i18n.mergeGlobalTranslations(locale, translations)
|
|
187
|
+
},
|
|
188
|
+
clearCache: (): void => {
|
|
189
|
+
i18n.clearCache()
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Generate locale head meta tags for SEO
|
|
196
|
+
*/
|
|
197
|
+
export interface LocaleHeadOptions {
|
|
198
|
+
baseUrl?: string
|
|
199
|
+
addDirAttribute?: boolean
|
|
200
|
+
addSeoAttributes?: boolean
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface LocaleHeadResult {
|
|
204
|
+
htmlAttrs: {
|
|
205
|
+
lang?: string
|
|
206
|
+
dir?: 'ltr' | 'rtl' | 'auto'
|
|
207
|
+
}
|
|
208
|
+
link: Array<{
|
|
209
|
+
rel: string
|
|
210
|
+
href: string
|
|
211
|
+
hreflang?: string
|
|
212
|
+
}>
|
|
213
|
+
meta: Array<{
|
|
214
|
+
property: string
|
|
215
|
+
content: string
|
|
216
|
+
}>
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function useLocaleHead(astro: AstroGlobal, options: LocaleHeadOptions = {}): LocaleHeadResult {
|
|
220
|
+
const {
|
|
221
|
+
baseUrl = '/',
|
|
222
|
+
addDirAttribute = true,
|
|
223
|
+
addSeoAttributes = true,
|
|
224
|
+
} = options
|
|
225
|
+
|
|
226
|
+
const locale = getLocale(astro)
|
|
227
|
+
const defaultLocale = getDefaultLocale(astro)
|
|
228
|
+
const locales = getLocales(astro)
|
|
229
|
+
const currentLocaleObj = locales.find(l => l.code === locale)
|
|
230
|
+
|
|
231
|
+
if (!currentLocaleObj) {
|
|
232
|
+
return { htmlAttrs: {}, link: [], meta: [] }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const currentIso = currentLocaleObj.iso || locale
|
|
236
|
+
const currentDir = currentLocaleObj.dir || 'auto'
|
|
237
|
+
|
|
238
|
+
const result: LocaleHeadResult = {
|
|
239
|
+
htmlAttrs: {
|
|
240
|
+
lang: currentIso,
|
|
241
|
+
...(addDirAttribute ? { dir: currentDir } : {}),
|
|
242
|
+
},
|
|
243
|
+
link: [],
|
|
244
|
+
meta: [],
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!addSeoAttributes) {
|
|
248
|
+
return result
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Canonical URL
|
|
252
|
+
const canonicalUrl = `${baseUrl}${astro.url.pathname}`
|
|
253
|
+
result.link.push({
|
|
254
|
+
rel: 'canonical',
|
|
255
|
+
href: canonicalUrl,
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// Get routing strategy
|
|
259
|
+
const routingStrategy = getRoutingStrategy(astro)
|
|
260
|
+
const allLocaleCodes = locales.map(l => l.code)
|
|
261
|
+
|
|
262
|
+
// Alternate languages
|
|
263
|
+
for (const loc of locales) {
|
|
264
|
+
if (loc.code === locale) continue
|
|
265
|
+
|
|
266
|
+
let alternatePath = astro.url.pathname
|
|
267
|
+
if (routingStrategy?.switchLocalePath) {
|
|
268
|
+
alternatePath = routingStrategy.switchLocalePath(astro.url.pathname, loc.code, allLocaleCodes, defaultLocale)
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
// Fallback: basic locale switching
|
|
272
|
+
const segments = astro.url.pathname.split('/').filter(Boolean)
|
|
273
|
+
const firstSegment = segments[0]
|
|
274
|
+
if (firstSegment && allLocaleCodes.includes(firstSegment)) {
|
|
275
|
+
segments.shift()
|
|
276
|
+
}
|
|
277
|
+
if (loc.code !== defaultLocale) {
|
|
278
|
+
segments.unshift(loc.code)
|
|
279
|
+
}
|
|
280
|
+
alternatePath = `/${segments.join('/')}`
|
|
281
|
+
}
|
|
282
|
+
const alternateUrl = `${baseUrl}${alternatePath}`
|
|
283
|
+
|
|
284
|
+
result.link.push({
|
|
285
|
+
rel: 'alternate',
|
|
286
|
+
href: alternateUrl,
|
|
287
|
+
hreflang: loc.code,
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
if (loc.iso && loc.iso !== loc.code) {
|
|
291
|
+
result.link.push({
|
|
292
|
+
rel: 'alternate',
|
|
293
|
+
href: alternateUrl,
|
|
294
|
+
hreflang: loc.iso,
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Open Graph locale
|
|
300
|
+
result.meta.push({
|
|
301
|
+
property: 'og:locale',
|
|
302
|
+
content: currentIso,
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
result.meta.push({
|
|
306
|
+
property: 'og:url',
|
|
307
|
+
content: canonicalUrl,
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
// Alternate OG locales
|
|
311
|
+
for (const loc of locales) {
|
|
312
|
+
if (loc.code === locale) continue
|
|
313
|
+
result.meta.push({
|
|
314
|
+
property: 'og:locale:alternate',
|
|
315
|
+
content: loc.iso || loc.code,
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return result
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Props для передачи в клиентские острова (Vue, React, Svelte, Preact)
|
|
324
|
+
*/
|
|
325
|
+
export interface I18nClientProps {
|
|
326
|
+
locale: string
|
|
327
|
+
fallbackLocale: string
|
|
328
|
+
translations: Record<string, Translations> // routeName -> translations
|
|
329
|
+
currentRoute: string
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Создает вложенную структуру из ключа (например, 'islands.vue.title' -> { islands: { vue: { title: value } } })
|
|
334
|
+
*/
|
|
335
|
+
function setNestedValue(obj: Translations, key: string, value: unknown): void {
|
|
336
|
+
const parts = key.split('.')
|
|
337
|
+
let current: Translations = obj
|
|
338
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
339
|
+
const part = parts[i]!
|
|
340
|
+
if (!current[part]) {
|
|
341
|
+
current[part] = {}
|
|
342
|
+
}
|
|
343
|
+
current = current[part] as Translations
|
|
344
|
+
}
|
|
345
|
+
const last = parts[parts.length - 1]
|
|
346
|
+
if (last !== undefined) {
|
|
347
|
+
current[last] = value
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Подготавливает пропсы для передачи в клиентский остров.
|
|
353
|
+
* Принимает список ключей, которые нужно передать в остров.
|
|
354
|
+
* Использует методы i18n для правильной работы с routesLocaleLinks.
|
|
355
|
+
* currentRoute уже нормализован через middleware (getRouteName), поэтому используем его напрямую.
|
|
356
|
+
*/
|
|
357
|
+
export function getI18nProps(astro: AstroGlobal, keys?: string[]): I18nClientProps {
|
|
358
|
+
const i18n = getI18n(astro)
|
|
359
|
+
const locale = getLocale(astro)
|
|
360
|
+
const fallbackLocale = getDefaultLocale(astro)
|
|
361
|
+
// currentRoute уже нормализован через middleware.setRoute(getRouteName(...))
|
|
362
|
+
// getRouteName учитывает routesLocaleLinks, если они настроены
|
|
363
|
+
const currentRoute = i18n.getRoute()
|
|
364
|
+
|
|
365
|
+
const translations: Record<string, Translations> = {}
|
|
366
|
+
|
|
367
|
+
// Если указаны ключи, извлекаем только их из кэша
|
|
368
|
+
if (keys && keys.length > 0) {
|
|
369
|
+
const extracted: Translations = {}
|
|
370
|
+
|
|
371
|
+
// Используем методы i18n для получения переводов
|
|
372
|
+
// Это гарантирует правильную работу с routesLocaleLinks
|
|
373
|
+
for (const key of keys) {
|
|
374
|
+
// Используем i18n.t() который использует helper.getTranslation внутри
|
|
375
|
+
// и правильно резолвит ключ кэша с учетом routesLocaleLinks
|
|
376
|
+
const value = i18n.t(key, undefined, undefined, currentRoute)
|
|
377
|
+
if (value !== null && value !== undefined && value !== key) {
|
|
378
|
+
setNestedValue(extracted, key, value)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (Object.keys(extracted).length > 0) {
|
|
383
|
+
translations[currentRoute] = extracted
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
// Если ключи не указаны, берем route-specific переводы
|
|
388
|
+
// Используем публичный метод getRouteTranslations для безопасного доступа
|
|
389
|
+
const routeTrans = i18n.getRouteTranslations(locale, currentRoute)
|
|
390
|
+
if (routeTrans) {
|
|
391
|
+
translations[currentRoute] = routeTrans
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
locale,
|
|
397
|
+
fallbackLocale,
|
|
398
|
+
currentRoute,
|
|
399
|
+
translations,
|
|
400
|
+
}
|
|
401
|
+
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { I18nDevToolsBridge } from '@i18n-micro/devtools-ui';
|
|
2
|
-
/**
|
|
3
|
-
* Astro Toolbar Server interface (simplified)
|
|
4
|
-
*/
|
|
5
|
-
export interface AstroToolbarServer {
|
|
6
|
-
send(event: string, data?: unknown): void;
|
|
7
|
-
on(event: string, callback: (data: unknown) => void): void;
|
|
8
|
-
off(event: string, callback: (data: unknown) => void): void;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Create an Astro Toolbar bridge that adapts Astro Toolbar Server API to I18nDevToolsBridge interface
|
|
12
|
-
*/
|
|
13
|
-
export declare function createAstroBridge(server: AstroToolbarServer): I18nDevToolsBridge;
|
package/dist/index-C-UMdqSG.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("node:fs/promises"),u=require("node:path"),l=require("@i18n-micro/core");async function g(c,e=!1){const t={global:{},routes:{}};try{const a=await p.readdir(c,{recursive:!0,withFileTypes:!0});for(const o of a){if(!o.isFile()||!o.name.endsWith(".json"))continue;const r=u.join(o.path,o.name),s=u.relative(c,r).split(u.sep),i=u.basename(o.name,".json");try{const n=await p.readFile(r,"utf-8"),h=JSON.parse(n);if(!e&&s[0]==="pages"&&s.length>=2){const f=s.slice(1,-1);if(f.length>0){const d=f.join("-");t.routes[d]||(t.routes[d]={}),t.routes[d][i]=h}}else t.global[i]=h}catch(n){console.error(`Failed to load translation file ${r}:`,n)}}}catch(a){a&&typeof a=="object"&&"code"in a&&a.code!=="ENOENT"&&console.error(`Failed to read directory ${c}:`,a)}return t}class m{constructor(e){this.currentRoute="general",this.cache={generalLocaleCache:{},routeLocaleCache:{},dynamicTranslationsCaches:[],serverTranslationCache:{}},this.helper=l.useTranslationHelper(this.cache),this.formatter=new l.FormatService,this.locale=e.locale,this.fallbackLocale=e.fallbackLocale||e.locale,this.translationDir=e.translationDir,this.disablePageLocales=e.disablePageLocales??!1,this.pluralFunc=e.plural||l.defaultPlural,this.missingWarn=e.missingWarn??!1,this.missingHandler=e.missingHandler}setRoute(e){this.currentRoute=e}getRoute(){return this.currentRoute}t(e,t,a,o){if(!e)return"";const r=o||this.currentRoute;let s=this.helper.getTranslation(this.locale,r,e);return!s&&this.locale!==this.fallbackLocale&&(s=this.helper.getTranslation(this.fallbackLocale,r,e)),s||(this.missingHandler?this.missingHandler(this.locale,e,r):this.missingWarn&&console.warn(`[i18n] Translation key '${e}' not found for locale '${this.locale}' (route: '${r}').`),s=a===void 0?e:a||e),typeof s=="string"&&t?l.interpolate(s,t):s||e}ts(e,t,a,o){var r;return((r=this.t(e,t,a,o))==null?void 0:r.toString())??a??e}tc(e,t,a){const{count:o,...r}=typeof t=="number"?{count:t}:t;if(o===void 0)return a??e;const s=(i,n,h)=>this.t(i,n,h);return this.pluralFunc(e,Number.parseInt(o.toString()),r,this.locale,s)??a??e}tn(e,t){return this.formatter.formatNumber(e,this.locale,t)}td(e,t){return this.formatter.formatDate(e,this.locale,t)}tdr(e,t){return this.formatter.formatRelativeTime(e,this.locale,t)}async loadTranslations(e){const t=e||this.translationDir;if(!t){console.warn("[i18n-node] No translation directory specified");return}const{global:a,routes:o}=await g(t,this.disablePageLocales);for(const[r,s]of Object.entries(a))this.helper.mergeGlobalTranslation(r,s,!0);for(const[r,s]of Object.entries(o))for(const[i,n]of Object.entries(s))await this.helper.loadPageTranslations(i,r,n)}async reload(){this.helper.clearCache(),await this.loadTranslations(),console.log("[i18n-node] Cache cleared and translations reloaded.")}addTranslations(e,t,a=!0){a?this.helper.mergeGlobalTranslation(e,t,!0):this.helper.loadTranslations(e,t)}addRouteTranslations(e,t,a,o=!0){o?this.helper.mergeTranslation(e,t,a,!0):this.helper.loadPageTranslations(e,t,a)}hasTranslation(e){return this.helper.hasTranslation(this.locale,e)}clear(){this.helper.clearCache()}}Object.defineProperty(exports,"FormatService",{enumerable:!0,get:()=>l.FormatService});Object.defineProperty(exports,"interpolate",{enumerable:!0,get:()=>l.interpolate});exports.I18n=m;exports.loadTranslations=g;
|
package/dist/index-CVhedN6W.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { readdir as d, readFile as f } from "node:fs/promises";
|
|
2
|
-
import { join as m, relative as p, sep as g, basename as T } from "node:path";
|
|
3
|
-
import { useTranslationHelper as b, FormatService as y, defaultPlural as j, interpolate as w } from "@i18n-micro/core";
|
|
4
|
-
import { FormatService as k, interpolate as H } from "@i18n-micro/core";
|
|
5
|
-
async function F(i, t = !1) {
|
|
6
|
-
const e = {
|
|
7
|
-
global: {},
|
|
8
|
-
routes: {}
|
|
9
|
-
};
|
|
10
|
-
try {
|
|
11
|
-
const a = await d(i, { recursive: !0, withFileTypes: !0 });
|
|
12
|
-
for (const o of a) {
|
|
13
|
-
if (!o.isFile() || !o.name.endsWith(".json"))
|
|
14
|
-
continue;
|
|
15
|
-
const r = m(o.path, o.name), s = p(i, r).split(g), n = T(o.name, ".json");
|
|
16
|
-
try {
|
|
17
|
-
const l = await f(r, "utf-8"), c = JSON.parse(l);
|
|
18
|
-
if (!t && s[0] === "pages" && s.length >= 2) {
|
|
19
|
-
const u = s.slice(1, -1);
|
|
20
|
-
if (u.length > 0) {
|
|
21
|
-
const h = u.join("-");
|
|
22
|
-
e.routes[h] || (e.routes[h] = {}), e.routes[h][n] = c;
|
|
23
|
-
}
|
|
24
|
-
} else
|
|
25
|
-
e.global[n] = c;
|
|
26
|
-
} catch (l) {
|
|
27
|
-
console.error(`Failed to load translation file ${r}:`, l);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
} catch (a) {
|
|
31
|
-
a && typeof a == "object" && "code" in a && a.code !== "ENOENT" && console.error(`Failed to read directory ${i}:`, a);
|
|
32
|
-
}
|
|
33
|
-
return e;
|
|
34
|
-
}
|
|
35
|
-
class C {
|
|
36
|
-
constructor(t) {
|
|
37
|
-
this.currentRoute = "general", this.cache = {
|
|
38
|
-
generalLocaleCache: {},
|
|
39
|
-
routeLocaleCache: {},
|
|
40
|
-
dynamicTranslationsCaches: [],
|
|
41
|
-
serverTranslationCache: {}
|
|
42
|
-
}, this.helper = b(this.cache), this.formatter = new y(), this.locale = t.locale, this.fallbackLocale = t.fallbackLocale || t.locale, this.translationDir = t.translationDir, this.disablePageLocales = t.disablePageLocales ?? !1, this.pluralFunc = t.plural || j, this.missingWarn = t.missingWarn ?? !1, this.missingHandler = t.missingHandler;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Set the current route name context.
|
|
46
|
-
* Useful when processing a specific page request in Node.
|
|
47
|
-
*/
|
|
48
|
-
setRoute(t) {
|
|
49
|
-
this.currentRoute = t;
|
|
50
|
-
}
|
|
51
|
-
getRoute() {
|
|
52
|
-
return this.currentRoute;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Get translation for a key
|
|
56
|
-
*
|
|
57
|
-
* Search order:
|
|
58
|
-
* 1. Current Locale + Current Route (Specific)
|
|
59
|
-
* 2. Current Locale + General (Global)
|
|
60
|
-
* 3. Fallback Locale + Current Route
|
|
61
|
-
* 4. Fallback Locale + General
|
|
62
|
-
*/
|
|
63
|
-
t(t, e, a, o) {
|
|
64
|
-
if (!t) return "";
|
|
65
|
-
const r = o || this.currentRoute;
|
|
66
|
-
let s = this.helper.getTranslation(this.locale, r, t);
|
|
67
|
-
return !s && this.locale !== this.fallbackLocale && (s = this.helper.getTranslation(this.fallbackLocale, r, t)), s || (this.missingHandler ? this.missingHandler(this.locale, t, r) : this.missingWarn && console.warn(
|
|
68
|
-
`[i18n] Translation key '${t}' not found for locale '${this.locale}' (route: '${r}').`
|
|
69
|
-
), s = a === void 0 ? t : a || t), typeof s == "string" && e ? w(s, e) : s || t;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Get translation as string
|
|
73
|
-
*/
|
|
74
|
-
ts(t, e, a, o) {
|
|
75
|
-
var r;
|
|
76
|
-
return ((r = this.t(t, e, a, o)) == null ? void 0 : r.toString()) ?? a ?? t;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Plural translation
|
|
80
|
-
*/
|
|
81
|
-
tc(t, e, a) {
|
|
82
|
-
const { count: o, ...r } = typeof e == "number" ? { count: e } : e;
|
|
83
|
-
if (o === void 0)
|
|
84
|
-
return a ?? t;
|
|
85
|
-
const s = (n, l, c) => this.t(n, l, c);
|
|
86
|
-
return this.pluralFunc(
|
|
87
|
-
t,
|
|
88
|
-
Number.parseInt(o.toString()),
|
|
89
|
-
r,
|
|
90
|
-
this.locale,
|
|
91
|
-
s
|
|
92
|
-
) ?? a ?? t;
|
|
93
|
-
}
|
|
94
|
-
// --- Formatters ---
|
|
95
|
-
tn(t, e) {
|
|
96
|
-
return this.formatter.formatNumber(t, this.locale, e);
|
|
97
|
-
}
|
|
98
|
-
td(t, e) {
|
|
99
|
-
return this.formatter.formatDate(t, this.locale, e);
|
|
100
|
-
}
|
|
101
|
-
tdr(t, e) {
|
|
102
|
-
return this.formatter.formatRelativeTime(t, this.locale, e);
|
|
103
|
-
}
|
|
104
|
-
// --- Loader & Cache Management ---
|
|
105
|
-
/**
|
|
106
|
-
* Load translations from directory.
|
|
107
|
-
*/
|
|
108
|
-
async loadTranslations(t) {
|
|
109
|
-
const e = t || this.translationDir;
|
|
110
|
-
if (!e) {
|
|
111
|
-
console.warn("[i18n-node] No translation directory specified");
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const { global: a, routes: o } = await F(e, this.disablePageLocales);
|
|
115
|
-
for (const [r, s] of Object.entries(a))
|
|
116
|
-
this.helper.mergeGlobalTranslation(r, s, !0);
|
|
117
|
-
for (const [r, s] of Object.entries(o))
|
|
118
|
-
for (const [n, l] of Object.entries(s))
|
|
119
|
-
await this.helper.loadPageTranslations(n, r, l);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Clear cache and reload translations from disk
|
|
123
|
-
*/
|
|
124
|
-
async reload() {
|
|
125
|
-
this.helper.clearCache(), await this.loadTranslations(), console.log("[i18n-node] Cache cleared and translations reloaded.");
|
|
126
|
-
}
|
|
127
|
-
// --- Manual Manipulation ---
|
|
128
|
-
addTranslations(t, e, a = !0) {
|
|
129
|
-
a ? this.helper.mergeGlobalTranslation(t, e, !0) : this.helper.loadTranslations(t, e);
|
|
130
|
-
}
|
|
131
|
-
addRouteTranslations(t, e, a, o = !0) {
|
|
132
|
-
o ? this.helper.mergeTranslation(t, e, a, !0) : this.helper.loadPageTranslations(t, e, a);
|
|
133
|
-
}
|
|
134
|
-
hasTranslation(t) {
|
|
135
|
-
return this.helper.hasTranslation(this.locale, t);
|
|
136
|
-
}
|
|
137
|
-
clear() {
|
|
138
|
-
this.helper.clearCache();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
export {
|
|
142
|
-
k as FormatService,
|
|
143
|
-
C as I18n,
|
|
144
|
-
H as interpolate,
|
|
145
|
-
F as loadTranslations
|
|
146
|
-
};
|
package/dist/toolbar-app.d.ts
DELETED