@i18n-micro/core 1.3.0 → 1.3.2
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/README.md +7 -8
- package/dist/base.d.ts +30 -5
- package/dist/helpers-entry.d.ts +1 -0
- package/dist/helpers.cjs +1 -0
- package/dist/helpers.d.cts +27 -0
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.mjs +74 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +130 -131
- package/package.json +60 -12
- package/fix.ts +0 -1
- package/jest.config.cjs +0 -7
- package/src/base.ts +0 -194
- package/src/format-service.ts +0 -41
- package/src/helpers.ts +0 -54
- package/src/index.ts +0 -27
- package/src/translation.ts +0 -95
- package/tests/base.test.ts +0 -464
- package/tests/core.test.ts +0 -81
- package/tests/format-service.test.ts +0 -101
- package/tests/helpers.test.ts +0 -103
- package/tsconfig.json +0 -24
- package/vite.config.mts +0 -22
package/package.json
CHANGED
|
@@ -1,35 +1,83 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i18n-micro/core",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "",
|
|
5
|
-
"
|
|
3
|
+
"version": "1.3.2",
|
|
4
|
+
"description": "Core utilities for translations, formatting, and locale routing in Nuxt I18n Micro.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"formatting",
|
|
7
|
+
"i18n",
|
|
8
|
+
"locale",
|
|
9
|
+
"nuxt",
|
|
10
|
+
"translations"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/s00d/nuxt-i18n-micro/tree/main/packages/core#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/s00d/nuxt-i18n-micro/issues"
|
|
15
|
+
},
|
|
6
16
|
"license": "MIT",
|
|
7
|
-
"type": "module",
|
|
8
17
|
"author": {
|
|
9
18
|
"name": "s00d",
|
|
10
19
|
"email": "Virus191288@gmail.com",
|
|
11
20
|
"url": "https://s00d.github.io/"
|
|
12
21
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
"url": "https://github.com/s00d/nuxt-i18n-micro
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/s00d/nuxt-i18n-micro.git",
|
|
25
|
+
"directory": "packages/core"
|
|
16
26
|
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
32
|
+
"type": "module",
|
|
33
|
+
"sideEffects": false,
|
|
17
34
|
"main": "dist/index.cjs",
|
|
18
|
-
"module": "dist/index.mjs",
|
|
19
35
|
"types": "dist/index.d.ts",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"default": "./dist/index.mjs"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./dist/index.d.cts",
|
|
44
|
+
"default": "./dist/index.cjs"
|
|
45
|
+
},
|
|
46
|
+
"default": "./dist/index.mjs"
|
|
47
|
+
},
|
|
48
|
+
"./helpers": {
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./dist/helpers.d.ts",
|
|
51
|
+
"default": "./dist/helpers.mjs"
|
|
52
|
+
},
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./dist/helpers.d.cts",
|
|
55
|
+
"default": "./dist/helpers.cjs"
|
|
56
|
+
},
|
|
57
|
+
"default": "./dist/helpers.mjs"
|
|
58
|
+
},
|
|
59
|
+
"./package.json": "./package.json"
|
|
60
|
+
},
|
|
20
61
|
"publishConfig": {
|
|
21
62
|
"access": "public"
|
|
22
63
|
},
|
|
23
|
-
"keywords": [],
|
|
24
64
|
"dependencies": {
|
|
25
|
-
"@i18n-micro/types": "1.2.
|
|
65
|
+
"@i18n-micro/types": "1.2.4"
|
|
26
66
|
},
|
|
27
67
|
"devDependencies": {
|
|
68
|
+
"publint": "^0.3.17",
|
|
28
69
|
"vite": "^7.3.1",
|
|
29
|
-
"vite-plugin-dts": "^4.5.4"
|
|
70
|
+
"vite-plugin-dts": "^4.5.4",
|
|
71
|
+
"vitest": "^3.2.4"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=18"
|
|
30
75
|
},
|
|
31
76
|
"scripts": {
|
|
32
77
|
"build": "vite build",
|
|
33
|
-
"
|
|
78
|
+
"check:package": "publint",
|
|
79
|
+
"test": "jest",
|
|
80
|
+
"test:perf": "jest --config jest.perf.config.cjs",
|
|
81
|
+
"test:dist": "vitest run --config vitest.dist.config.ts"
|
|
34
82
|
}
|
|
35
83
|
}
|
package/fix.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import '@types/jest'
|
package/jest.config.cjs
DELETED
package/src/base.ts
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import type { CleanTranslation, Getter, MissingHandler, Params, PluralFunc, TranslationKey, Translations } from '@i18n-micro/types'
|
|
2
|
-
import { FormatService } from './format-service'
|
|
3
|
-
import { defaultPlural, interpolate } from './helpers'
|
|
4
|
-
import { type TranslationStorage, useTranslationHelper } from './translation'
|
|
5
|
-
|
|
6
|
-
export interface BaseI18nOptions {
|
|
7
|
-
storage?: TranslationStorage
|
|
8
|
-
plural?: PluralFunc
|
|
9
|
-
missingWarn?: boolean
|
|
10
|
-
missingHandler?: (locale: string, key: string, routeName: string) => void
|
|
11
|
-
getCustomMissingHandler?: () => MissingHandler | null
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Abstract base class for i18n adapters
|
|
16
|
-
*
|
|
17
|
-
* Contains all common translation logic (t, ts, tc, tn, td, tdr, has).
|
|
18
|
-
* Adapters must implement abstract methods to provide current state (locale, fallbackLocale, route).
|
|
19
|
-
*/
|
|
20
|
-
export abstract class BaseI18n {
|
|
21
|
-
// Public fields (made public to allow type export in Nuxt plugins)
|
|
22
|
-
public helper: ReturnType<typeof useTranslationHelper>
|
|
23
|
-
public formatter = new FormatService()
|
|
24
|
-
public pluralFunc: PluralFunc
|
|
25
|
-
public missingWarn: boolean
|
|
26
|
-
public missingHandler?: (locale: string, key: string, routeName: string) => void
|
|
27
|
-
public getCustomMissingHandler?: () => MissingHandler | null
|
|
28
|
-
|
|
29
|
-
constructor(options: BaseI18nOptions = {}) {
|
|
30
|
-
this.helper = useTranslationHelper(options.storage)
|
|
31
|
-
this.formatter = new FormatService()
|
|
32
|
-
this.pluralFunc = options.plural || defaultPlural
|
|
33
|
-
this.missingWarn = options.missingWarn ?? true
|
|
34
|
-
this.missingHandler = options.missingHandler
|
|
35
|
-
this.getCustomMissingHandler = options.getCustomMissingHandler
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// --- Abstract methods (must be implemented by subclasses) ---
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Get current locale
|
|
42
|
-
*/
|
|
43
|
-
public abstract getLocale(): string
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get fallback locale
|
|
47
|
-
*/
|
|
48
|
-
public abstract getFallbackLocale(): string
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Get current route name
|
|
52
|
-
*/
|
|
53
|
-
public abstract getRoute(): string
|
|
54
|
-
|
|
55
|
-
// --- Public methods (implemented in base class) ---
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get translation for a key
|
|
59
|
-
* Based on logic from src/runtime/plugins/01.plugin.ts
|
|
60
|
-
*/
|
|
61
|
-
public t(key: TranslationKey, params?: Params, defaultValue?: string | null, routeName?: string): CleanTranslation {
|
|
62
|
-
if (!key) return ''
|
|
63
|
-
|
|
64
|
-
// Use abstract getters to get current state
|
|
65
|
-
const locale = this.getLocale()
|
|
66
|
-
const route = routeName || this.getRoute()
|
|
67
|
-
|
|
68
|
-
// 1. Try to find translation in current locale
|
|
69
|
-
let value = this.helper.getTranslation<string>(locale, route, key)
|
|
70
|
-
|
|
71
|
-
// 2. Fallback to fallbackLocale if not found and different
|
|
72
|
-
if (!value) {
|
|
73
|
-
const fallbackLocale = this.getFallbackLocale()
|
|
74
|
-
if (locale !== fallbackLocale) {
|
|
75
|
-
value = this.helper.getTranslation<string>(fallbackLocale, route, key)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// 3. Handle missing
|
|
80
|
-
if (!value) {
|
|
81
|
-
// Call custom handler if set (Nuxt runtime), otherwise use instance handler
|
|
82
|
-
const customHandler = this.getCustomMissingHandler?.()
|
|
83
|
-
if (customHandler) {
|
|
84
|
-
customHandler(locale, key, route)
|
|
85
|
-
} else if (this.missingHandler) {
|
|
86
|
-
this.missingHandler(locale, key as string, route)
|
|
87
|
-
} else if (this.missingWarn) {
|
|
88
|
-
const isDev = process.env.NODE_ENV !== 'production'
|
|
89
|
-
const isClient = typeof window !== 'undefined'
|
|
90
|
-
if (isDev && isClient) {
|
|
91
|
-
console.warn(`Not found '${key}' key in '${locale}' locale messages for route '${route}'.`)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
value = defaultValue === undefined ? key : defaultValue || key
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 4. Interpolate
|
|
98
|
-
return typeof value === 'string' && params ? interpolate(value, params) : (value as CleanTranslation)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Get translation as string
|
|
103
|
-
*/
|
|
104
|
-
public ts(key: TranslationKey, params?: Params, defaultValue?: string, routeName?: string): string {
|
|
105
|
-
const value = this.t(key, params, defaultValue, routeName)
|
|
106
|
-
return value?.toString() ?? defaultValue ?? key
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Plural translation
|
|
111
|
-
*/
|
|
112
|
-
public tc(key: TranslationKey, count: number | Params, defaultValue?: string): string {
|
|
113
|
-
const { count: countValue, ...params } = typeof count === 'number' ? { count } : count
|
|
114
|
-
|
|
115
|
-
if (countValue === undefined) {
|
|
116
|
-
return defaultValue ?? key
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Getter passed to plural function
|
|
120
|
-
const getter: Getter = (k: TranslationKey, p?: Params, dv?: string) => {
|
|
121
|
-
return this.t(k, p, dv)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const result = this.pluralFunc(key, Number.parseInt(countValue.toString(), 10), params, this.getLocale(), getter)
|
|
125
|
-
|
|
126
|
-
return result ?? defaultValue ?? key
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Format number
|
|
131
|
-
*/
|
|
132
|
-
public tn(value: number, options?: Intl.NumberFormatOptions): string {
|
|
133
|
-
return this.formatter.formatNumber(value, this.getLocale(), options)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Format date
|
|
138
|
-
*/
|
|
139
|
-
public td(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string {
|
|
140
|
-
return this.formatter.formatDate(value, this.getLocale(), options)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Format relative time
|
|
145
|
-
*/
|
|
146
|
-
public tdr(value: Date | number | string, options?: Intl.RelativeTimeFormatOptions): string {
|
|
147
|
-
return this.formatter.formatRelativeTime(value, this.getLocale(), options)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Check if translation exists
|
|
152
|
-
* Based on logic from src/runtime/plugins/01.plugin.ts
|
|
153
|
-
*/
|
|
154
|
-
public has(key: TranslationKey, routeName?: string): boolean {
|
|
155
|
-
const route = routeName || this.getRoute()
|
|
156
|
-
const locale = this.getLocale()
|
|
157
|
-
|
|
158
|
-
// Check only through getTranslation (as in plugin)
|
|
159
|
-
return !!this.helper.getTranslation(locale, route, key)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Clear cache
|
|
164
|
-
*/
|
|
165
|
-
public clearCache(): void {
|
|
166
|
-
this.helper.clearCache()
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// --- Public methods (for subclasses to use) ---
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Core translation loading logic (without reactivity)
|
|
173
|
-
* Subclasses can override addTranslations/addRouteTranslations to add reactivity
|
|
174
|
-
*/
|
|
175
|
-
public loadTranslationsCore(locale: string, translations: Translations, merge: boolean, routeName = 'index'): void {
|
|
176
|
-
if (merge) {
|
|
177
|
-
this.helper.mergeTranslation(locale, routeName, translations, true)
|
|
178
|
-
} else {
|
|
179
|
-
this.helper.setTranslations(locale, translations, routeName)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Core route translation loading logic (without reactivity)
|
|
185
|
-
* Subclasses can override addRouteTranslations to add reactivity
|
|
186
|
-
*/
|
|
187
|
-
public loadRouteTranslationsCore(locale: string, routeName: string, translations: Translations, merge: boolean): void {
|
|
188
|
-
if (merge) {
|
|
189
|
-
this.helper.mergeTranslation(locale, routeName, translations, true)
|
|
190
|
-
} else {
|
|
191
|
-
this.helper.loadPageTranslations(locale, routeName, translations)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
package/src/format-service.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export class FormatService {
|
|
2
|
-
formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string {
|
|
3
|
-
return new Intl.NumberFormat(locale, options).format(value)
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
formatDate(value: Date | number | string, locale: string, options?: Intl.DateTimeFormatOptions): string {
|
|
7
|
-
const date = new Date(value)
|
|
8
|
-
if (Number.isNaN(date.getTime())) {
|
|
9
|
-
return 'Invalid Date'
|
|
10
|
-
}
|
|
11
|
-
return new Intl.DateTimeFormat(locale, options).format(date)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
formatRelativeTime(value: Date | number | string, locale: string, options?: Intl.RelativeTimeFormatOptions): string {
|
|
15
|
-
const date = new Date(value)
|
|
16
|
-
if (Number.isNaN(date.getTime())) {
|
|
17
|
-
// Return "0 seconds ago" for invalid dates
|
|
18
|
-
return new Intl.RelativeTimeFormat(locale, options).format(0, 'second')
|
|
19
|
-
}
|
|
20
|
-
const now = new Date()
|
|
21
|
-
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000)
|
|
22
|
-
|
|
23
|
-
const units: { unit: Intl.RelativeTimeFormatUnit; seconds: number }[] = [
|
|
24
|
-
{ unit: 'year', seconds: 31536000 },
|
|
25
|
-
{ unit: 'month', seconds: 2592000 },
|
|
26
|
-
{ unit: 'day', seconds: 86400 },
|
|
27
|
-
{ unit: 'hour', seconds: 3600 },
|
|
28
|
-
{ unit: 'minute', seconds: 60 },
|
|
29
|
-
{ unit: 'second', seconds: 1 },
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
for (const { unit, seconds } of units) {
|
|
33
|
-
const diff = Math.floor(diffInSeconds / seconds)
|
|
34
|
-
if (diff >= 1) {
|
|
35
|
-
return new Intl.RelativeTimeFormat(locale, options).format(-diff, unit)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return new Intl.RelativeTimeFormat(locale, options).format(0, 'second')
|
|
40
|
-
}
|
|
41
|
-
}
|
package/src/helpers.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { Getter, Params, PluralFunc, Strategies, TranslationKey } from '@i18n-micro/types'
|
|
2
|
-
|
|
3
|
-
const RE_TOKEN = /\{(\w+)\}/g
|
|
4
|
-
|
|
5
|
-
export function interpolate(template: string, params: Params): string {
|
|
6
|
-
if (!params) return template
|
|
7
|
-
|
|
8
|
-
return template.replace(RE_TOKEN, (_, key) => {
|
|
9
|
-
const value = params[key]
|
|
10
|
-
return value !== undefined ? String(value) : `{${key}}`
|
|
11
|
-
})
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function withPrefixStrategy(strategy: Strategies) {
|
|
15
|
-
return strategy === 'prefix' || strategy === 'prefix_and_default'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function isNoPrefixStrategy(strategy: Strategies) {
|
|
19
|
-
return strategy === 'no_prefix'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function isPrefixStrategy(strategy: Strategies) {
|
|
23
|
-
return strategy === 'prefix'
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function isPrefixExceptDefaultStrategy(strategy: Strategies) {
|
|
27
|
-
return strategy === 'prefix_except_default'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function isPrefixAndDefaultStrategy(strategy: Strategies) {
|
|
31
|
-
return strategy === 'prefix_and_default'
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Default pluralization function
|
|
36
|
-
* Splits translation by '|' and selects form based on count
|
|
37
|
-
* @param key - Translation key
|
|
38
|
-
* @param count - Count for pluralization
|
|
39
|
-
* @param params - Parameters for translation
|
|
40
|
-
* @param _locale - Current locale (unused in default implementation)
|
|
41
|
-
* @param getTranslation - Function to get translation value
|
|
42
|
-
* @returns Selected plural form or null if not found
|
|
43
|
-
*/
|
|
44
|
-
export const defaultPlural: PluralFunc = (key: TranslationKey, count: number, params: Params, _locale: string, getTranslation: Getter) => {
|
|
45
|
-
const translation = getTranslation(key, params)
|
|
46
|
-
if (!translation) {
|
|
47
|
-
return null
|
|
48
|
-
}
|
|
49
|
-
const forms = translation.toString().split('|')
|
|
50
|
-
if (forms.length === 0) return null
|
|
51
|
-
const selectedForm = count < forms.length ? forms[count] : forms[forms.length - 1]
|
|
52
|
-
if (!selectedForm) return null
|
|
53
|
-
return selectedForm.trim().replace('{count}', count.toString())
|
|
54
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { BaseI18n, type BaseI18nOptions } from './base'
|
|
2
|
-
import { FormatService } from './format-service'
|
|
3
|
-
import {
|
|
4
|
-
defaultPlural,
|
|
5
|
-
interpolate,
|
|
6
|
-
isNoPrefixStrategy,
|
|
7
|
-
isPrefixAndDefaultStrategy,
|
|
8
|
-
isPrefixExceptDefaultStrategy,
|
|
9
|
-
isPrefixStrategy,
|
|
10
|
-
withPrefixStrategy,
|
|
11
|
-
} from './helpers'
|
|
12
|
-
import { type TranslationStorage, useTranslationHelper } from './translation'
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
useTranslationHelper,
|
|
16
|
-
interpolate,
|
|
17
|
-
withPrefixStrategy,
|
|
18
|
-
isNoPrefixStrategy,
|
|
19
|
-
isPrefixStrategy,
|
|
20
|
-
isPrefixExceptDefaultStrategy,
|
|
21
|
-
isPrefixAndDefaultStrategy,
|
|
22
|
-
defaultPlural,
|
|
23
|
-
FormatService,
|
|
24
|
-
BaseI18n,
|
|
25
|
-
type TranslationStorage,
|
|
26
|
-
type BaseI18nOptions,
|
|
27
|
-
}
|
package/src/translation.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { Translations } from '@i18n-micro/types'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Bare Metal: Simple translation storage without Ref, useState, devalue.
|
|
5
|
-
* Map key: `${locale}:${routeName}` (page-specific).
|
|
6
|
-
*/
|
|
7
|
-
export interface TranslationStorage {
|
|
8
|
-
translations: Map<string, Translations>
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function findValue<T = unknown>(data: Translations | null | undefined, key: string): T | null {
|
|
12
|
-
if (!data || typeof key !== 'string') return null
|
|
13
|
-
|
|
14
|
-
if (key in data) {
|
|
15
|
-
const value = data[key]
|
|
16
|
-
if (typeof value === 'object' && value !== null) {
|
|
17
|
-
return value as T
|
|
18
|
-
}
|
|
19
|
-
return value as T
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const parts = key.split('.')
|
|
23
|
-
let value: unknown = data
|
|
24
|
-
for (const part of parts) {
|
|
25
|
-
if (value && typeof value === 'object' && part in (value as object)) {
|
|
26
|
-
value = (value as Translations)[part]
|
|
27
|
-
} else {
|
|
28
|
-
return null
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return (value as T) ?? null
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function useTranslationHelper(storage?: TranslationStorage) {
|
|
35
|
-
const translations = storage?.translations ?? new Map<string, Translations>()
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
hasCache(locale: string, page: string) {
|
|
39
|
-
const p = page || 'index'
|
|
40
|
-
return translations.has(`${locale}:${p}`)
|
|
41
|
-
},
|
|
42
|
-
getCache(locale: string, routeName: string) {
|
|
43
|
-
const rn = routeName || 'index'
|
|
44
|
-
return translations.get(`${locale}:${rn}`)
|
|
45
|
-
},
|
|
46
|
-
setCache(_locale: string, _routeName: string, _cache: Map<string, unknown>) {
|
|
47
|
-
// No-op for bare metal
|
|
48
|
-
},
|
|
49
|
-
hasTranslation(locale: string, key: string): boolean {
|
|
50
|
-
for (const [k, v] of translations) {
|
|
51
|
-
if (k.startsWith(`${locale}:`) && findValue(v, key) !== null) {
|
|
52
|
-
return true
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return false
|
|
56
|
-
},
|
|
57
|
-
hasPageTranslation(locale: string, routeName: string): boolean {
|
|
58
|
-
const rn = routeName || 'index'
|
|
59
|
-
return translations.has(`${locale}:${rn}`)
|
|
60
|
-
},
|
|
61
|
-
getTranslation<T = unknown>(locale: string, routeName: string, key: string): T | null {
|
|
62
|
-
const rn = routeName || 'index'
|
|
63
|
-
return findValue<T>(translations.get(`${locale}:${rn}`), key)
|
|
64
|
-
},
|
|
65
|
-
loadTranslations(locale: string, data: Translations, routeName = 'index'): void {
|
|
66
|
-
const rn = routeName || 'index'
|
|
67
|
-
const key = `${locale}:${rn}`
|
|
68
|
-
const existing = translations.get(key) ?? {}
|
|
69
|
-
translations.set(key, { ...existing, ...data })
|
|
70
|
-
},
|
|
71
|
-
setTranslations(locale: string, data: Translations, routeName = 'index'): void {
|
|
72
|
-
const rn = routeName || 'index'
|
|
73
|
-
translations.set(`${locale}:${rn}`, data)
|
|
74
|
-
},
|
|
75
|
-
loadPageTranslations(locale: string, routeName: string, data: Translations): void {
|
|
76
|
-
const rn = routeName || 'index'
|
|
77
|
-
const key = `${locale}:${rn}`
|
|
78
|
-
const existing = translations.get(key)
|
|
79
|
-
if (!existing || Object.keys(existing).length === 0) {
|
|
80
|
-
translations.set(key, data)
|
|
81
|
-
} else {
|
|
82
|
-
translations.set(key, { ...existing, ...data })
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force = false): void {
|
|
86
|
-
const rn = routeName || 'index'
|
|
87
|
-
const key = `${locale}:${rn}`
|
|
88
|
-
const existing = translations.get(key) ?? {}
|
|
89
|
-
translations.set(key, { ...existing, ...newTranslations })
|
|
90
|
-
},
|
|
91
|
-
clearCache(): void {
|
|
92
|
-
translations.clear()
|
|
93
|
-
},
|
|
94
|
-
}
|
|
95
|
-
}
|