@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.
Files changed (48) hide show
  1. package/dist/client/core.d.ts +23 -0
  2. package/dist/client/index.d.ts +6 -0
  3. package/dist/client/index.js +17 -0
  4. package/dist/client/preact.d.ts +30 -0
  5. package/dist/client/preact.js +51 -0
  6. package/dist/client/react.d.ts +26 -0
  7. package/dist/client/react.js +51 -0
  8. package/dist/client/svelte.d.ts +28 -0
  9. package/dist/client/svelte.js +70 -0
  10. package/dist/client/vue.d.ts +27 -0
  11. package/dist/client/vue.js +1558 -0
  12. package/dist/composer.d.ts +9 -18
  13. package/dist/core-Bx9n-eFD.cjs +1 -0
  14. package/dist/core-D32Y48CN.js +42 -0
  15. package/dist/env.d.ts +2 -0
  16. package/dist/index.cjs +17 -1
  17. package/dist/index.d.ts +6 -2
  18. package/dist/index.mjs +431 -297
  19. package/dist/integration.d.ts +5 -1
  20. package/dist/load-translations.d.ts +44 -0
  21. package/dist/middleware.d.ts +3 -1
  22. package/dist/router/adapter.d.ts +8 -0
  23. package/dist/router/types.d.ts +65 -0
  24. package/dist/utils.d.ts +17 -1
  25. package/package.json +57 -13
  26. package/src/client/core.ts +121 -0
  27. package/src/client/index.ts +15 -0
  28. package/src/client/preact.tsx +114 -0
  29. package/src/client/react.tsx +111 -0
  30. package/src/client/svelte.ts +124 -0
  31. package/src/client/vue.ts +128 -0
  32. package/src/components/i18n-link.astro +37 -4
  33. package/src/components/i18n-switcher.astro +209 -17
  34. package/src/components/index.ts +8 -2
  35. package/src/composer.ts +138 -0
  36. package/src/env.d.ts +20 -0
  37. package/src/index.ts +59 -0
  38. package/src/integration.ts +120 -0
  39. package/src/load-translations.ts +130 -0
  40. package/src/middleware.ts +203 -0
  41. package/src/router/adapter.ts +184 -0
  42. package/src/router/types.ts +66 -0
  43. package/src/routing.ts +108 -0
  44. package/src/utils.ts +401 -0
  45. package/dist/bridge/astro-bridge.d.ts +0 -13
  46. package/dist/index-C-UMdqSG.cjs +0 -1
  47. package/dist/index-CVhedN6W.js +0 -146
  48. 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;
@@ -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;
@@ -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
- };
@@ -1,2 +0,0 @@
1
- declare const _default: import('astro').DevToolbarApp;
2
- export default _default;