@i18n-micro/core 1.0.27 → 1.1.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/base.d.ts +91 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.mjs +329 -268
- package/dist/route-service.d.ts +6 -5
- package/dist/translation.d.ts +16 -17
- package/package.json +4 -4
- package/src/base.ts +250 -0
- package/src/helpers.ts +7 -6
- package/src/index.ts +5 -2
- package/src/route-service.ts +29 -33
- package/src/translation.ts +71 -181
- package/tests/base.test.ts +485 -0
- package/tests/route-service.test.ts +31 -61
package/dist/route-service.d.ts
CHANGED
|
@@ -5,16 +5,18 @@ interface NavigateToInterface {
|
|
|
5
5
|
redirectCode?: number;
|
|
6
6
|
external?: boolean;
|
|
7
7
|
}
|
|
8
|
+
export type GetDefaultLocale = () => string | null | undefined;
|
|
8
9
|
export declare class RouteService {
|
|
9
10
|
private i18nConfig;
|
|
10
11
|
private router;
|
|
11
12
|
private hashLocaleDefault;
|
|
12
13
|
private noPrefixDefault;
|
|
13
14
|
private navigateTo;
|
|
14
|
-
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/** Optional getter for current locale (used by hash/noPrefix when state changes). Plugin provides () => localeState.value */
|
|
16
|
+
private getDefaultLocale?;
|
|
17
|
+
constructor(i18nConfig: ModuleOptionsExtend, router: Router, hashLocaleDefault: string | null | undefined, noPrefixDefault: string | null | undefined, navigateTo: (to: RouteLocationRaw | undefined | null, options?: NavigateToInterface) => Promise<void | NavigationFailure | false> | false | void | RouteLocationRaw,
|
|
18
|
+
/** Optional getter for current locale (used by hash/noPrefix when state changes). Plugin provides () => localeState.value */
|
|
19
|
+
getDefaultLocale?: GetDefaultLocale | undefined);
|
|
18
20
|
/**
|
|
19
21
|
* Extracts locale from URL path by checking the first path segment
|
|
20
22
|
* @param path - URL path (e.g., '/ru/sdfsdf' or '/en/about')
|
|
@@ -31,7 +33,6 @@ export declare class RouteService {
|
|
|
31
33
|
private handlePrefixStrategy;
|
|
32
34
|
private createLocalizedRoute;
|
|
33
35
|
getLocalizedRoute(to: RouteLocationResolvedGeneric | RouteLocationAsPathGeneric | RouteLocationNamedRaw | string, route: RouteLocationNormalizedLoaded, locale?: string): RouteLocationResolved;
|
|
34
|
-
updateCookies(toLocale: string): void;
|
|
35
36
|
getCurrentRoute(): RouteLocationNormalizedLoaded;
|
|
36
37
|
private resolveRouteWithStrategy;
|
|
37
38
|
switchLocaleLogic(toLocale: string, i18nRouteParams: I18nRouteParams, to?: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string): string | false | void | import('vue-router').RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | Promise<false | void | NavigationFailure>;
|
package/dist/translation.d.ts
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import { Translations } from '@i18n-micro/types';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Bare Metal: Простое хранилище переводов без Ref, useState, devalue.
|
|
4
|
+
* Ключ Map: locale (general) или locale:routeName (page-specific).
|
|
5
|
+
*/
|
|
6
|
+
export interface TranslationStorage {
|
|
7
|
+
translations: Map<string, Translations>;
|
|
4
8
|
}
|
|
5
|
-
export
|
|
6
|
-
generalLocaleCache: RefLike<Record<string, Translations>> | Record<string, Translations>;
|
|
7
|
-
routeLocaleCache: RefLike<Record<string, Translations>> | Record<string, Translations>;
|
|
8
|
-
dynamicTranslationsCaches: RefLike<Record<string, Translations>[]> | Record<string, Translations>[];
|
|
9
|
-
serverTranslationCache: RefLike<Record<string, Map<string, Translations | unknown>>> | Record<string, Map<string, Translations | unknown>>;
|
|
10
|
-
}
|
|
11
|
-
export declare function useTranslationHelper(caches?: TranslationCache): {
|
|
9
|
+
export declare function useTranslationHelper(storage?: TranslationStorage): {
|
|
12
10
|
hasCache(locale: string, page: string): boolean;
|
|
13
|
-
getCache(locale: string, routeName: string):
|
|
14
|
-
setCache(
|
|
15
|
-
|
|
16
|
-
mergeGlobalTranslation(locale: string, newTranslations: Translations, force?: boolean): void;
|
|
11
|
+
getCache(locale: string, routeName: string): Translations | undefined;
|
|
12
|
+
setCache(_locale: string, _routeName: string, _cache: Map<string, unknown>): void;
|
|
13
|
+
hasTranslation(locale: string, key: string): boolean;
|
|
17
14
|
hasGeneralTranslation(locale: string): boolean;
|
|
18
15
|
hasPageTranslation(locale: string, routeName: string): boolean;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
getTranslation<T = unknown>(locale: string, routeName: string, key: string): T | null;
|
|
17
|
+
loadTranslations(locale: string, data: Translations): void;
|
|
18
|
+
setTranslations(locale: string, data: Translations): void;
|
|
19
|
+
loadPageTranslations(locale: string, routeName: string, data: Translations): void;
|
|
20
|
+
mergeTranslation(locale: string, routeName: string, newTranslations: Translations, _force?: boolean): void;
|
|
21
|
+
mergeGlobalTranslation(locale: string, newTranslations: Translations, _force?: boolean): void;
|
|
23
22
|
clearCache(): void;
|
|
24
23
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i18n-micro/core",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": "s00d/nuxt-i18n-micro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"keywords": [],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@i18n-micro/types": "1.0
|
|
25
|
+
"@i18n-micro/types": "1.1.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"vite": "^
|
|
29
|
-
"vite-plugin-dts": "^4.
|
|
28
|
+
"vite": "^7.3.1",
|
|
29
|
+
"vite-plugin-dts": "^4.5.4"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "vite build",
|
package/src/base.ts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { useTranslationHelper, type TranslationStorage } from './translation'
|
|
2
|
+
import { FormatService } from './format-service'
|
|
3
|
+
import { interpolate, defaultPlural } from './helpers'
|
|
4
|
+
import type {
|
|
5
|
+
Translations,
|
|
6
|
+
Params,
|
|
7
|
+
PluralFunc,
|
|
8
|
+
Getter,
|
|
9
|
+
CleanTranslation,
|
|
10
|
+
TranslationKey,
|
|
11
|
+
MissingHandler,
|
|
12
|
+
} from '@i18n-micro/types'
|
|
13
|
+
|
|
14
|
+
export interface BaseI18nOptions {
|
|
15
|
+
storage?: TranslationStorage
|
|
16
|
+
plural?: PluralFunc
|
|
17
|
+
missingWarn?: boolean
|
|
18
|
+
missingHandler?: (locale: string, key: string, routeName: string) => void
|
|
19
|
+
// Optional hooks for Nuxt runtime specific features
|
|
20
|
+
getPreviousPageInfo?: () => { locale: string, routeName: string } | null
|
|
21
|
+
getCustomMissingHandler?: () => MissingHandler | null
|
|
22
|
+
enablePreviousPageFallback?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Abstract base class for i18n adapters
|
|
27
|
+
*
|
|
28
|
+
* Contains all common translation logic (t, ts, tc, tn, td, tdr, has).
|
|
29
|
+
* Adapters must implement abstract methods to provide current state (locale, fallbackLocale, route).
|
|
30
|
+
*/
|
|
31
|
+
export abstract class BaseI18n {
|
|
32
|
+
// Public fields (made public to allow type export in Nuxt plugins)
|
|
33
|
+
public helper: ReturnType<typeof useTranslationHelper>
|
|
34
|
+
public formatter = new FormatService()
|
|
35
|
+
public pluralFunc: PluralFunc
|
|
36
|
+
public missingWarn: boolean
|
|
37
|
+
public missingHandler?: (locale: string, key: string, routeName: string) => void
|
|
38
|
+
// Optional hooks for Nuxt runtime specific features
|
|
39
|
+
public getPreviousPageInfo?: () => { locale: string, routeName: string } | null
|
|
40
|
+
public getCustomMissingHandler?: () => MissingHandler | null
|
|
41
|
+
public enablePreviousPageFallback: boolean
|
|
42
|
+
|
|
43
|
+
constructor(options: BaseI18nOptions = {}) {
|
|
44
|
+
this.helper = useTranslationHelper(options.storage)
|
|
45
|
+
this.formatter = new FormatService()
|
|
46
|
+
this.pluralFunc = options.plural || defaultPlural
|
|
47
|
+
this.missingWarn = options.missingWarn ?? true
|
|
48
|
+
this.missingHandler = options.missingHandler
|
|
49
|
+
this.getPreviousPageInfo = options.getPreviousPageInfo
|
|
50
|
+
this.getCustomMissingHandler = options.getCustomMissingHandler
|
|
51
|
+
this.enablePreviousPageFallback = options.enablePreviousPageFallback ?? false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// --- Abstract methods (must be implemented by subclasses) ---
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get current locale
|
|
58
|
+
*/
|
|
59
|
+
public abstract getLocale(): string
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get fallback locale
|
|
63
|
+
*/
|
|
64
|
+
public abstract getFallbackLocale(): string
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get current route name
|
|
68
|
+
*/
|
|
69
|
+
public abstract getRoute(): string
|
|
70
|
+
|
|
71
|
+
// --- Public methods (implemented in base class) ---
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get translation for a key
|
|
75
|
+
* Based on logic from src/runtime/plugins/01.plugin.ts
|
|
76
|
+
*/
|
|
77
|
+
public t(
|
|
78
|
+
key: TranslationKey,
|
|
79
|
+
params?: Params,
|
|
80
|
+
defaultValue?: string | null,
|
|
81
|
+
routeName?: string,
|
|
82
|
+
): CleanTranslation {
|
|
83
|
+
if (!key) return ''
|
|
84
|
+
|
|
85
|
+
// Use abstract getters to get current state
|
|
86
|
+
const locale = this.getLocale()
|
|
87
|
+
const route = routeName || this.getRoute()
|
|
88
|
+
|
|
89
|
+
// 1. Try to find translation in current locale
|
|
90
|
+
// Note: In Nuxt runtime, server already merges global translations, so we don't need explicit fallback
|
|
91
|
+
let value = this.helper.getTranslation<string>(locale, route, key)
|
|
92
|
+
|
|
93
|
+
// 2. If translation not found and there are saved previous translations, use them (only if enabled)
|
|
94
|
+
if (!value && this.enablePreviousPageFallback && this.getPreviousPageInfo) {
|
|
95
|
+
const prev = this.getPreviousPageInfo()
|
|
96
|
+
if (prev) {
|
|
97
|
+
const prevValue = this.helper.getTranslation<string>(prev.locale, prev.routeName, key)
|
|
98
|
+
if (prevValue) {
|
|
99
|
+
value = prevValue
|
|
100
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
101
|
+
console.log(`Using fallback translation from previous route: ${prev.routeName} -> ${key}`)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 3. Fallback to fallbackLocale if not found and different (for non-Nuxt adapters)
|
|
108
|
+
if (!value) {
|
|
109
|
+
const fallbackLocale = this.getFallbackLocale()
|
|
110
|
+
if (locale !== fallbackLocale) {
|
|
111
|
+
value = this.helper.getTranslation<string>(fallbackLocale, route, key)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 4. Handle missing
|
|
116
|
+
if (!value) {
|
|
117
|
+
// Call custom handler if set (Nuxt runtime), otherwise use instance handler
|
|
118
|
+
const customHandler = this.getCustomMissingHandler?.()
|
|
119
|
+
if (customHandler) {
|
|
120
|
+
customHandler(locale, key, route)
|
|
121
|
+
}
|
|
122
|
+
else if (this.missingHandler) {
|
|
123
|
+
this.missingHandler(locale, key as string, route)
|
|
124
|
+
}
|
|
125
|
+
else if (this.missingWarn) {
|
|
126
|
+
const isDev = process.env.NODE_ENV !== 'production'
|
|
127
|
+
const isClient = typeof window !== 'undefined'
|
|
128
|
+
if (isDev && isClient) {
|
|
129
|
+
console.warn(`Not found '${key}' key in '${locale}' locale messages for route '${route}'.`)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
value = defaultValue === undefined ? key : (defaultValue || key)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 5. Interpolate
|
|
136
|
+
return typeof value === 'string' && params ? interpolate(value, params) : value as CleanTranslation
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get translation as string
|
|
141
|
+
*/
|
|
142
|
+
public ts(
|
|
143
|
+
key: TranslationKey,
|
|
144
|
+
params?: Params,
|
|
145
|
+
defaultValue?: string,
|
|
146
|
+
routeName?: string,
|
|
147
|
+
): string {
|
|
148
|
+
const value = this.t(key, params, defaultValue, routeName)
|
|
149
|
+
return value?.toString() ?? defaultValue ?? key
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Plural translation
|
|
154
|
+
*/
|
|
155
|
+
public tc(key: TranslationKey, count: number | Params, defaultValue?: string): string {
|
|
156
|
+
const { count: countValue, ...params } = typeof count === 'number' ? { count } : count
|
|
157
|
+
|
|
158
|
+
if (countValue === undefined) {
|
|
159
|
+
return defaultValue ?? key
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Getter passed to plural function
|
|
163
|
+
const getter: Getter = (k: TranslationKey, p?: Params, dv?: string) => {
|
|
164
|
+
return this.t(k, p, dv)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const result = this.pluralFunc(
|
|
168
|
+
key,
|
|
169
|
+
Number.parseInt(countValue.toString()),
|
|
170
|
+
params,
|
|
171
|
+
this.getLocale(),
|
|
172
|
+
getter,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return result ?? defaultValue ?? key
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Format number
|
|
180
|
+
*/
|
|
181
|
+
public tn(value: number, options?: Intl.NumberFormatOptions): string {
|
|
182
|
+
return this.formatter.formatNumber(value, this.getLocale(), options)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Format date
|
|
187
|
+
*/
|
|
188
|
+
public td(value: Date | number | string, options?: Intl.DateTimeFormatOptions): string {
|
|
189
|
+
return this.formatter.formatDate(value, this.getLocale(), options)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Format relative time
|
|
194
|
+
*/
|
|
195
|
+
public tdr(value: Date | number | string, options?: Intl.RelativeTimeFormatOptions): string {
|
|
196
|
+
return this.formatter.formatRelativeTime(value, this.getLocale(), options)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Check if translation exists
|
|
201
|
+
* Based on logic from src/runtime/plugins/01.plugin.ts
|
|
202
|
+
*/
|
|
203
|
+
public has(key: TranslationKey, routeName?: string): boolean {
|
|
204
|
+
const route = routeName || this.getRoute()
|
|
205
|
+
const locale = this.getLocale()
|
|
206
|
+
|
|
207
|
+
// Check only through getTranslation (as in plugin)
|
|
208
|
+
return !!this.helper.getTranslation(locale, route, key)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Clear cache
|
|
213
|
+
*/
|
|
214
|
+
public clearCache(): void {
|
|
215
|
+
this.helper.clearCache()
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// --- Public methods (for subclasses to use) ---
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Core translation loading logic (without reactivity)
|
|
222
|
+
* Subclasses can override addTranslations/addRouteTranslations to add reactivity
|
|
223
|
+
*/
|
|
224
|
+
public loadTranslationsCore(locale: string, translations: Translations, merge: boolean): void {
|
|
225
|
+
if (merge) {
|
|
226
|
+
this.helper.mergeGlobalTranslation(locale, translations, true)
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
this.helper.setTranslations(locale, translations)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Core route translation loading logic (without reactivity)
|
|
235
|
+
* Subclasses can override addRouteTranslations to add reactivity
|
|
236
|
+
*/
|
|
237
|
+
public loadRouteTranslationsCore(
|
|
238
|
+
locale: string,
|
|
239
|
+
routeName: string,
|
|
240
|
+
translations: Translations,
|
|
241
|
+
merge: boolean,
|
|
242
|
+
): void {
|
|
243
|
+
if (merge) {
|
|
244
|
+
this.helper.mergeTranslation(locale, routeName, translations, true)
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
this.helper.loadPageTranslations(locale, routeName, translations)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
package/src/helpers.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Params, Strategies, PluralFunc, Getter, TranslationKey } from '@i18n-micro/types'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
let result = template
|
|
3
|
+
const RE_TOKEN = /\{(\w+)\}/g
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
5
|
+
export function interpolate(template: string, params: Params): string {
|
|
6
|
+
if (!params) return template
|
|
9
7
|
|
|
10
|
-
return
|
|
8
|
+
return template.replace(RE_TOKEN, (_, key) => {
|
|
9
|
+
const value = params[key]
|
|
10
|
+
return value !== undefined ? String(value) : `{${key}}`
|
|
11
|
+
})
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export function withPrefixStrategy(strategy: Strategies) {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useTranslationHelper, type
|
|
1
|
+
import { useTranslationHelper, type TranslationStorage } from './translation'
|
|
2
2
|
import { RouteService } from './route-service'
|
|
3
3
|
import { FormatService } from './format-service'
|
|
4
4
|
import { interpolate, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural } from './helpers'
|
|
5
|
+
import { BaseI18n, type BaseI18nOptions } from './base'
|
|
5
6
|
|
|
6
7
|
export {
|
|
7
8
|
useTranslationHelper,
|
|
@@ -14,5 +15,7 @@ export {
|
|
|
14
15
|
defaultPlural,
|
|
15
16
|
RouteService,
|
|
16
17
|
FormatService,
|
|
17
|
-
|
|
18
|
+
BaseI18n,
|
|
19
|
+
type TranslationStorage,
|
|
20
|
+
type BaseI18nOptions,
|
|
18
21
|
}
|
package/src/route-service.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
Router,
|
|
14
14
|
} from 'vue-router'
|
|
15
15
|
import type { I18nRouteParams, Locale, ModuleOptionsExtend } from '@i18n-micro/types'
|
|
16
|
-
import { isNoPrefixStrategy, withPrefixStrategy } from './helpers'
|
|
16
|
+
import { isNoPrefixStrategy, withPrefixStrategy, isPrefixAndDefaultStrategy, isPrefixExceptDefaultStrategy } from './helpers'
|
|
17
17
|
|
|
18
18
|
interface NavigateToInterface {
|
|
19
19
|
replace?: boolean
|
|
@@ -21,6 +21,8 @@ interface NavigateToInterface {
|
|
|
21
21
|
external?: boolean
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export type GetDefaultLocale = () => string | null | undefined
|
|
25
|
+
|
|
24
26
|
export class RouteService {
|
|
25
27
|
constructor(
|
|
26
28
|
private i18nConfig: ModuleOptionsExtend,
|
|
@@ -28,9 +30,8 @@ export class RouteService {
|
|
|
28
30
|
private hashLocaleDefault: string | null | undefined,
|
|
29
31
|
private noPrefixDefault: string | null | undefined,
|
|
30
32
|
private navigateTo: (to: RouteLocationRaw | undefined | null, options?: NavigateToInterface) => Promise<void | NavigationFailure | false> | false | void | RouteLocationRaw,
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
private cookieLocaleName: string | null | undefined = null,
|
|
33
|
+
/** Optional getter for current locale (used by hash/noPrefix when state changes). Plugin provides () => localeState.value */
|
|
34
|
+
private getDefaultLocale?: GetDefaultLocale,
|
|
34
35
|
) {}
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -75,14 +76,25 @@ export class RouteService {
|
|
|
75
76
|
getCurrentLocale(route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string {
|
|
76
77
|
route = route ?? this.router.currentRoute.value
|
|
77
78
|
|
|
78
|
-
// 1. Check hashMode
|
|
79
|
-
if (this.i18nConfig.hashMode
|
|
80
|
-
|
|
79
|
+
// 1. Check hashMode (getter overrides initial value when state changes)
|
|
80
|
+
if (this.i18nConfig.hashMode) {
|
|
81
|
+
const fromGetter = this.getDefaultLocale?.()
|
|
82
|
+
if (fromGetter) return fromGetter
|
|
83
|
+
if (this.hashLocaleDefault) return this.hashLocaleDefault
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
// 2. Check noPrefix strategy
|
|
84
|
-
if (isNoPrefixStrategy(this.i18nConfig.strategy!)
|
|
85
|
-
|
|
87
|
+
if (isNoPrefixStrategy(this.i18nConfig.strategy!)) {
|
|
88
|
+
const fromGetter = this.getDefaultLocale?.()
|
|
89
|
+
if (fromGetter) return fromGetter
|
|
90
|
+
if (this.noPrefixDefault) return this.noPrefixDefault
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const path = route.path || route.fullPath || ''
|
|
94
|
+
// 2b. prefix_and_default at /: useState/cookie can override — / is valid for any locale
|
|
95
|
+
if (isPrefixAndDefaultStrategy(this.i18nConfig.strategy!) && (path === '/' || path === '')) {
|
|
96
|
+
const fromGetter = this.getDefaultLocale?.()
|
|
97
|
+
if (fromGetter) return fromGetter
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
// 3. Check route.params.locale (for existing routes)
|
|
@@ -91,18 +103,21 @@ export class RouteService {
|
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
// 4. Extract locale from URL path (for non-existent routes)
|
|
94
|
-
const path = route.path || route.fullPath || ''
|
|
95
106
|
const localeFromPath = this.extractLocaleFromPath(path)
|
|
96
107
|
if (localeFromPath) {
|
|
97
108
|
return localeFromPath
|
|
98
109
|
}
|
|
99
110
|
|
|
100
|
-
// 5.
|
|
101
|
-
if (this.
|
|
102
|
-
return this.
|
|
111
|
+
// 5. For prefix_except_default: URL without locale prefix = defaultLocale
|
|
112
|
+
if (isPrefixExceptDefaultStrategy(this.i18nConfig.strategy!)) {
|
|
113
|
+
return (this.i18nConfig.defaultLocale || 'en').toString()
|
|
103
114
|
}
|
|
104
115
|
|
|
105
|
-
// 6.
|
|
116
|
+
// 6. Check getter (for no_prefix and other strategies)
|
|
117
|
+
const fromGetter = this.getDefaultLocale?.()
|
|
118
|
+
if (fromGetter) return fromGetter
|
|
119
|
+
|
|
120
|
+
// 7. Return defaultLocale as fallback
|
|
106
121
|
return (this.i18nConfig.defaultLocale || 'en').toString()
|
|
107
122
|
}
|
|
108
123
|
|
|
@@ -373,24 +388,6 @@ export class RouteService {
|
|
|
373
388
|
return this.createLocalizedRoute(processedTo, route, currentLocale)
|
|
374
389
|
}
|
|
375
390
|
|
|
376
|
-
updateCookies(toLocale: string): void {
|
|
377
|
-
if (this.i18nConfig.hashMode) {
|
|
378
|
-
this.setCookie('hash-locale', toLocale)
|
|
379
|
-
// useCookie('hash-locale').value = toLocale
|
|
380
|
-
this.hashLocaleDefault = toLocale
|
|
381
|
-
}
|
|
382
|
-
if (isNoPrefixStrategy(this.i18nConfig.strategy!)) {
|
|
383
|
-
this.setCookie('no-prefix-locale', toLocale)
|
|
384
|
-
// useCookie('no-prefix-locale').value = toLocale
|
|
385
|
-
this.noPrefixDefault = toLocale
|
|
386
|
-
}
|
|
387
|
-
// Update cookie for regular strategy (prefix or prefix_except_default)
|
|
388
|
-
if (!this.i18nConfig.hashMode && !isNoPrefixStrategy(this.i18nConfig.strategy!) && this.cookieLocaleName) {
|
|
389
|
-
this.setCookie(this.cookieLocaleName, toLocale)
|
|
390
|
-
this.cookieLocaleDefault = toLocale
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
391
|
getCurrentRoute(): RouteLocationNormalizedLoaded {
|
|
395
392
|
return this.router.currentRoute.value
|
|
396
393
|
}
|
|
@@ -422,7 +419,6 @@ export class RouteService {
|
|
|
422
419
|
current = to ?? this.getCurrentRoute() as RouteLocationResolved
|
|
423
420
|
}
|
|
424
421
|
|
|
425
|
-
this.updateCookies(toLocale)
|
|
426
422
|
const switchedRoute = this.switchLocaleRoute(fromLocale, toLocale, current, i18nRouteParams)
|
|
427
423
|
|
|
428
424
|
if (typeof switchedRoute === 'string' && switchedRoute.startsWith('http')) {
|