@i18n-micro/core 1.0.27

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.
@@ -0,0 +1,24 @@
1
+ import { Translations } from '@i18n-micro/types';
2
+ export interface RefLike<T> {
3
+ value: T;
4
+ }
5
+ export interface TranslationCache {
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): {
12
+ hasCache(locale: string, page: string): boolean;
13
+ getCache(locale: string, routeName: string): Map<string, unknown> | undefined;
14
+ setCache(locale: string, routeName: string, cache: Map<string, Translations | unknown>): void;
15
+ mergeTranslation(locale: string, routeName: string, newTranslations: Translations, force?: boolean): void;
16
+ mergeGlobalTranslation(locale: string, newTranslations: Translations, force?: boolean): void;
17
+ hasGeneralTranslation(locale: string): boolean;
18
+ hasPageTranslation(locale: string, routeName: string): boolean;
19
+ hasTranslation: (locale: string, key: import('@i18n-micro/types').TranslationKey) => boolean;
20
+ getTranslation: <T = unknown>(locale: string, routeName: string, key: import('@i18n-micro/types').TranslationKey) => T | null;
21
+ loadPageTranslations(locale: string, routeName: string, translations: Translations): Promise<void>;
22
+ loadTranslations(locale: string, translations: Translations): Promise<void>;
23
+ clearCache(): void;
24
+ };
package/fix.ts ADDED
@@ -0,0 +1 @@
1
+ import '@types/jest'
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ roots: ['<rootDir>/tests'],
3
+ testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'],
4
+ transform: {
5
+ '^.+\\.ts$': 'ts-jest',
6
+ },
7
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@i18n-micro/core",
3
+ "version": "1.0.27",
4
+ "description": "",
5
+ "repository": "s00d/nuxt-i18n-micro",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "author": {
9
+ "name": "s00d",
10
+ "email": "Virus191288@gmail.com",
11
+ "url": "https://s00d.github.io/"
12
+ },
13
+ "homepage": "https://github.com/s00d/nuxt-i18n-micro",
14
+ "bugs": {
15
+ "url": "https://github.com/s00d/nuxt-i18n-micro/issues"
16
+ },
17
+ "main": "dist/index.cjs",
18
+ "module": "dist/index.mjs",
19
+ "types": "dist/index.d.ts",
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "keywords": [],
24
+ "dependencies": {
25
+ "@i18n-micro/types": "1.0.15"
26
+ },
27
+ "devDependencies": {
28
+ "vite": "^5.0.0",
29
+ "vite-plugin-dts": "^4.3.0"
30
+ },
31
+ "scripts": {
32
+ "build": "vite build",
33
+ "test": "jest"
34
+ }
35
+ }
@@ -0,0 +1,41 @@
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 ADDED
@@ -0,0 +1,53 @@
1
+ import type { Params, Strategies, PluralFunc, Getter, TranslationKey } from '@i18n-micro/types'
2
+
3
+ export function interpolate(template: string, params: Params): string {
4
+ let result = template
5
+
6
+ for (const key in params) {
7
+ result = result.split(`{${key}}`).join(String(params[key]))
8
+ }
9
+
10
+ return result
11
+ }
12
+
13
+ export function withPrefixStrategy(strategy: Strategies) {
14
+ return strategy === 'prefix' || strategy === 'prefix_and_default'
15
+ }
16
+
17
+ export function isNoPrefixStrategy(strategy: Strategies) {
18
+ return strategy === 'no_prefix'
19
+ }
20
+
21
+ export function isPrefixStrategy(strategy: Strategies) {
22
+ return strategy === 'prefix'
23
+ }
24
+
25
+ export function isPrefixExceptDefaultStrategy(strategy: Strategies) {
26
+ return strategy === 'prefix_except_default'
27
+ }
28
+
29
+ export function isPrefixAndDefaultStrategy(strategy: Strategies) {
30
+ return strategy === 'prefix_and_default'
31
+ }
32
+
33
+ /**
34
+ * Default pluralization function
35
+ * Splits translation by '|' and selects form based on count
36
+ * @param key - Translation key
37
+ * @param count - Count for pluralization
38
+ * @param params - Parameters for translation
39
+ * @param _locale - Current locale (unused in default implementation)
40
+ * @param getTranslation - Function to get translation value
41
+ * @returns Selected plural form or null if not found
42
+ */
43
+ export const defaultPlural: PluralFunc = (key: TranslationKey, count: number, params: Params, _locale: string, getTranslation: Getter) => {
44
+ const translation = getTranslation(key, params)
45
+ if (!translation) {
46
+ return null
47
+ }
48
+ const forms = translation.toString().split('|')
49
+ if (forms.length === 0) return null
50
+ const selectedForm = count < forms.length ? forms[count] : forms[forms.length - 1]
51
+ if (!selectedForm) return null
52
+ return selectedForm.trim().replace('{count}', count.toString())
53
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { useTranslationHelper, type TranslationCache } from './translation'
2
+ import { RouteService } from './route-service'
3
+ import { FormatService } from './format-service'
4
+ import { interpolate, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural } from './helpers'
5
+
6
+ export {
7
+ useTranslationHelper,
8
+ interpolate,
9
+ withPrefixStrategy,
10
+ isNoPrefixStrategy,
11
+ isPrefixStrategy,
12
+ isPrefixExceptDefaultStrategy,
13
+ isPrefixAndDefaultStrategy,
14
+ defaultPlural,
15
+ RouteService,
16
+ FormatService,
17
+ type TranslationCache,
18
+ }
@@ -0,0 +1,473 @@
1
+ import type {
2
+ NavigationFailure,
3
+ RouteLocationAsPath, RouteLocationAsPathGeneric,
4
+ RouteLocationAsRelative,
5
+ RouteLocationAsString,
6
+ RouteLocationNamedRaw,
7
+ RouteLocationNormalizedLoaded,
8
+ RouteLocationOptions,
9
+ RouteLocationRaw,
10
+ RouteLocationResolved,
11
+ RouteLocationResolvedGeneric,
12
+ RouteParamsRawGeneric,
13
+ Router,
14
+ } from 'vue-router'
15
+ import type { I18nRouteParams, Locale, ModuleOptionsExtend } from '@i18n-micro/types'
16
+ import { isNoPrefixStrategy, withPrefixStrategy } from './helpers'
17
+
18
+ interface NavigateToInterface {
19
+ replace?: boolean
20
+ redirectCode?: number
21
+ external?: boolean
22
+ }
23
+
24
+ export class RouteService {
25
+ constructor(
26
+ private i18nConfig: ModuleOptionsExtend,
27
+ private router: Router,
28
+ private hashLocaleDefault: string | null | undefined,
29
+ private noPrefixDefault: string | null | undefined,
30
+ private navigateTo: (to: RouteLocationRaw | undefined | null, options?: NavigateToInterface) => Promise<void | NavigationFailure | false> | false | void | RouteLocationRaw,
31
+ private setCookie: (name: string, value: string) => void,
32
+ private cookieLocaleDefault: string | null | undefined = null,
33
+ private cookieLocaleName: string | null | undefined = null,
34
+ ) {}
35
+
36
+ /**
37
+ * Extracts locale from URL path by checking the first path segment
38
+ * @param path - URL path (e.g., '/ru/sdfsdf' or '/en/about')
39
+ * @returns Locale code or null if not found
40
+ */
41
+ private extractLocaleFromPath(path: string): string | null {
42
+ if (!path) {
43
+ return null
44
+ }
45
+
46
+ // Remove query params and hash to ensure clean path comparison
47
+ // This is important when falling back to fullPath which might contain these
48
+ const querySplit = path.split('?')
49
+ const cleanPath = querySplit[0]?.split('#')[0]
50
+
51
+ if (!cleanPath || cleanPath === '/') {
52
+ return null
53
+ }
54
+
55
+ const pathSegments = cleanPath.split('/').filter(Boolean)
56
+ if (pathSegments.length === 0) {
57
+ return null
58
+ }
59
+
60
+ const firstSegment = pathSegments[0]
61
+ if (!firstSegment) {
62
+ return null
63
+ }
64
+
65
+ const availableLocales = this.i18nConfig.locales?.map(l => l.code) || []
66
+
67
+ // Check if the first segment is a valid locale
68
+ if (availableLocales.includes(firstSegment)) {
69
+ return firstSegment
70
+ }
71
+
72
+ return null
73
+ }
74
+
75
+ getCurrentLocale(route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string {
76
+ route = route ?? this.router.currentRoute.value
77
+
78
+ // 1. Check hashMode
79
+ if (this.i18nConfig.hashMode && this.hashLocaleDefault) {
80
+ return this.hashLocaleDefault
81
+ }
82
+
83
+ // 2. Check noPrefix strategy
84
+ if (isNoPrefixStrategy(this.i18nConfig.strategy!) && this.noPrefixDefault) {
85
+ return this.noPrefixDefault
86
+ }
87
+
88
+ // 3. Check route.params.locale (for existing routes)
89
+ if (route.params?.locale) {
90
+ return route.params.locale.toString()
91
+ }
92
+
93
+ // 4. Extract locale from URL path (for non-existent routes)
94
+ const path = route.path || route.fullPath || ''
95
+ const localeFromPath = this.extractLocaleFromPath(path)
96
+ if (localeFromPath) {
97
+ return localeFromPath
98
+ }
99
+
100
+ // 5. Check cookie (if provided)
101
+ if (this.cookieLocaleDefault) {
102
+ return this.cookieLocaleDefault
103
+ }
104
+
105
+ // 6. Return defaultLocale as fallback
106
+ return (this.i18nConfig.defaultLocale || 'en').toString()
107
+ }
108
+
109
+ getCurrentName(route: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string | null {
110
+ const currentLocaleCode = this.getCurrentLocale(route)
111
+ const checkLocale = this.i18nConfig.locales?.find(l => l.code === currentLocaleCode)
112
+ return checkLocale?.displayName ?? null
113
+ }
114
+
115
+ getRouteName(route: RouteLocationResolvedGeneric | RouteLocationNamedRaw, locale: string): string {
116
+ const name = (route.name ?? '').toString()
117
+ return name
118
+ .toString()
119
+ .replace('localized-', '')
120
+ .replace(new RegExp(`-${locale}$`), '')
121
+ }
122
+
123
+ getPluginRouteName(route: RouteLocationResolvedGeneric | RouteLocationNamedRaw, locale: string): string {
124
+ if (this.i18nConfig.disablePageLocales) {
125
+ return 'general'
126
+ }
127
+ return this.getRouteName(route, locale)
128
+ }
129
+
130
+ getFullPathWithBaseUrl(currentLocale: Locale, route: RouteLocationRaw): string {
131
+ const resolvedRoute = this.router.resolve(route)
132
+ let fullPath = resolvedRoute.fullPath
133
+
134
+ if (currentLocale?.baseDefault) {
135
+ fullPath = fullPath.replace(new RegExp(`^/${currentLocale!.code}`), '')
136
+ }
137
+
138
+ let baseUrl = currentLocale!.baseUrl
139
+ if (!baseUrl) baseUrl = ''
140
+ if (baseUrl?.endsWith('/')) {
141
+ baseUrl = baseUrl.slice(0, -1)
142
+ }
143
+
144
+ return baseUrl + fullPath
145
+ }
146
+
147
+ switchLocaleRoute(
148
+ fromLocale: string,
149
+ toLocale: string,
150
+ route: RouteLocationResolvedGeneric | RouteLocationNamedRaw,
151
+ i18nRouteParams: I18nRouteParams,
152
+ ): RouteLocationRaw {
153
+ const currentLocale = this.i18nConfig.locales?.find(l => l.code === toLocale)
154
+
155
+ const routeName = this.getRouteName(route, fromLocale)
156
+ if (this.router.hasRoute(`localized-${routeName}-${toLocale}`)) {
157
+ // If i18nRouteParams exist for target locale, use them as base, otherwise use route.params
158
+ const baseParams = i18nRouteParams?.[toLocale]
159
+ ? { ...i18nRouteParams[toLocale] }
160
+ : { ...route.params ?? {} }
161
+ // Merge remaining route.params that are not in i18nRouteParams
162
+ const newParams = { ...baseParams }
163
+ // Remove locale from params if it exists, we'll add it explicitly
164
+ delete newParams.locale
165
+ if (!isNoPrefixStrategy(this.i18nConfig.strategy!)) newParams.locale = toLocale
166
+
167
+ const newRoute = {
168
+ name: `localized-${routeName}-${toLocale}`,
169
+ params: newParams,
170
+ query: route.query,
171
+ hash: route.hash,
172
+ }
173
+
174
+ if (currentLocale?.baseUrl) {
175
+ return this.getFullPathWithBaseUrl(currentLocale, newRoute)
176
+ }
177
+
178
+ return newRoute
179
+ }
180
+
181
+ let newRouteName = routeName
182
+ // If i18nRouteParams exist for target locale, use them as base, otherwise use route.params
183
+ const baseParams = i18nRouteParams?.[toLocale]
184
+ ? { ...i18nRouteParams[toLocale] }
185
+ : { ...route.params ?? {} }
186
+ // Merge remaining route.params that are not in i18nRouteParams
187
+ const newParams = { ...baseParams }
188
+ delete newParams.locale
189
+
190
+ if (!isNoPrefixStrategy(this.i18nConfig.strategy!)) {
191
+ if (routeName === 'custom-fallback-route') {
192
+ newRouteName = routeName
193
+ }
194
+ else {
195
+ newRouteName
196
+ = toLocale !== this.i18nConfig.defaultLocale || withPrefixStrategy(this.i18nConfig.strategy!)
197
+ ? `localized-${routeName}`
198
+ : routeName
199
+ }
200
+
201
+ if (!isNoPrefixStrategy(this.i18nConfig.strategy!)) {
202
+ if (toLocale !== this.i18nConfig.defaultLocale || withPrefixStrategy(this.i18nConfig.strategy!)) {
203
+ newParams.locale = toLocale
204
+ }
205
+ }
206
+ }
207
+
208
+ const newRoute = {
209
+ name: newRouteName,
210
+ params: newParams,
211
+ query: route.query,
212
+ hash: route.hash,
213
+ }
214
+
215
+ if (isNoPrefixStrategy(this.i18nConfig.strategy!)) {
216
+ this.i18nConfig.locales?.forEach((locale, _index) => {
217
+ if (newRoute.name.endsWith(`-${locale.code}`)) {
218
+ newRoute.name = newRoute.name.slice(0, -locale.code - 1)
219
+ }
220
+ })
221
+ }
222
+
223
+ if (currentLocale?.baseUrl) {
224
+ return this.getFullPathWithBaseUrl(currentLocale, newRoute)
225
+ }
226
+
227
+ return newRoute
228
+ }
229
+
230
+ private resolveParams(to: RouteLocationAsString | RouteLocationAsRelative | RouteLocationAsPath): RouteParamsRawGeneric {
231
+ const params
232
+ = typeof to === 'object' && 'params' in to && typeof to.params === 'object'
233
+ ? { ...to.params }
234
+ : {}
235
+
236
+ if (typeof to === 'string') {
237
+ const resolved = this.router.resolve(to)
238
+ if (resolved && resolved.params) {
239
+ Object.assign(params, resolved.params)
240
+ }
241
+ }
242
+
243
+ return params
244
+ }
245
+
246
+ private handlePrefixStrategy(
247
+ to: RouteLocationResolvedGeneric | RouteLocationAsPathGeneric | RouteLocationNamedRaw | string,
248
+ ): RouteLocationResolvedGeneric | RouteLocationAsPathGeneric | RouteLocationNamedRaw | string {
249
+ if (!withPrefixStrategy(this.i18nConfig.strategy!)) {
250
+ return to
251
+ }
252
+
253
+ const defaultLocale = this.i18nConfig.defaultLocale!
254
+ let resolvedTo = to
255
+
256
+ if (typeof to === 'string') {
257
+ resolvedTo = this.router.resolve('/' + defaultLocale + to)
258
+ }
259
+
260
+ const defaultRouteName = this.getRouteName(resolvedTo as RouteLocationResolvedGeneric, defaultLocale)
261
+ const newParams = this.resolveParams(resolvedTo)
262
+
263
+ if (!isNoPrefixStrategy(this.i18nConfig.strategy!)) {
264
+ newParams.locale = defaultLocale
265
+ }
266
+
267
+ if (this.router.hasRoute(`localized-${defaultRouteName}`)) {
268
+ return this.router.resolve({
269
+ name: `localized-${defaultRouteName}`,
270
+ query: (resolvedTo as RouteLocationNormalizedLoaded).query,
271
+ hash: (resolvedTo as RouteLocationNormalizedLoaded).hash,
272
+ params: newParams,
273
+ })
274
+ }
275
+ else if (this.router.hasRoute(`localized-${defaultRouteName}-${defaultLocale}`)) {
276
+ return this.router.resolve({
277
+ name: `localized-${defaultRouteName}-${defaultLocale}`,
278
+ query: (resolvedTo as RouteLocationNormalizedLoaded).query,
279
+ hash: (resolvedTo as RouteLocationNormalizedLoaded).hash,
280
+ params: newParams,
281
+ })
282
+ }
283
+
284
+ return to
285
+ }
286
+
287
+ private createLocalizedRoute(
288
+ to: RouteLocationAsString | RouteLocationAsRelative | RouteLocationAsPath,
289
+ route: RouteLocationNormalizedLoaded,
290
+ locale: string,
291
+ ): RouteLocationResolved {
292
+ const selectRoute = this.router.resolve(to)
293
+ const routeName = this.getRouteName(selectRoute, locale)
294
+ .replace(new RegExp(`-${this.i18nConfig.defaultLocale!}$`), '')
295
+
296
+ if (!isNoPrefixStrategy(this.i18nConfig.strategy!)) {
297
+ if (!routeName || routeName === '') {
298
+ const resolved = this.router.resolve(to)
299
+ let url = resolved.path.replace(new RegExp(`^/${locale}/`), '/')
300
+ if (locale !== this.i18nConfig.defaultLocale || withPrefixStrategy(this.i18nConfig.strategy!)) {
301
+ url = '/' + locale + '' + url
302
+ }
303
+
304
+ return this.router.resolve({
305
+ path: url,
306
+ query: selectRoute.query,
307
+ hash: selectRoute.hash,
308
+ })
309
+ }
310
+ }
311
+
312
+ if (this.router.hasRoute(`localized-${routeName}-${locale}`)) {
313
+ const newParams = this.resolveParams(selectRoute)
314
+ if (!isNoPrefixStrategy(this.i18nConfig.strategy!)) newParams.locale = locale
315
+
316
+ return this.router.resolve({
317
+ name: `localized-${routeName}-${locale}`,
318
+ params: newParams,
319
+ query: selectRoute.query,
320
+ hash: selectRoute.hash,
321
+ })
322
+ }
323
+
324
+ const newRouteName
325
+ = locale !== this.i18nConfig.defaultLocale || withPrefixStrategy(this.i18nConfig.strategy!)
326
+ ? `localized-${routeName}`
327
+ : routeName
328
+
329
+ if (!this.router.hasRoute(newRouteName)) {
330
+ const newParams = this.resolveParams(to)
331
+ delete newParams.locale
332
+
333
+ if (!this.router.hasRoute(routeName)) {
334
+ return this.router.resolve('/')
335
+ }
336
+
337
+ return this.router.resolve({
338
+ name: routeName,
339
+ params: newParams,
340
+ query: selectRoute.query,
341
+ hash: selectRoute.hash,
342
+ })
343
+ }
344
+
345
+ const newParams = this.resolveParams(to)
346
+ delete newParams.locale
347
+
348
+ if (!isNoPrefixStrategy(this.i18nConfig.strategy!)) {
349
+ if (locale !== this.i18nConfig.defaultLocale || withPrefixStrategy(this.i18nConfig.strategy!)) {
350
+ newParams.locale = locale
351
+ }
352
+ }
353
+
354
+ return this.router.resolve({
355
+ name: newRouteName,
356
+ params: newParams,
357
+ query: selectRoute.query,
358
+ hash: selectRoute.hash,
359
+ })
360
+ }
361
+
362
+ getLocalizedRoute(
363
+ to: RouteLocationResolvedGeneric | RouteLocationAsPathGeneric | RouteLocationNamedRaw | string,
364
+ route: RouteLocationNormalizedLoaded,
365
+ locale?: string,
366
+ ): RouteLocationResolved {
367
+ const currentLocale = locale || this.getCurrentLocale(route)
368
+
369
+ // Handle prefix strategy
370
+ const processedTo = this.handlePrefixStrategy(to)
371
+
372
+ // Create localized route
373
+ return this.createLocalizedRoute(processedTo, route, currentLocale)
374
+ }
375
+
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
+ getCurrentRoute(): RouteLocationNormalizedLoaded {
395
+ return this.router.currentRoute.value
396
+ }
397
+
398
+ private resolveRouteWithStrategy(
399
+ route: string,
400
+ currentLocale: string,
401
+ fromLocale: string,
402
+ ): RouteLocationResolved {
403
+ if (isNoPrefixStrategy(this.i18nConfig.strategy!)) {
404
+ return this.router.resolve(route)
405
+ }
406
+ else if (currentLocale !== this.i18nConfig.defaultLocale || withPrefixStrategy(this.i18nConfig.strategy!)) {
407
+ return this.router.resolve(`/${fromLocale}${route}`)
408
+ }
409
+ else {
410
+ return this.router.resolve(route)
411
+ }
412
+ }
413
+
414
+ switchLocaleLogic(toLocale: string, i18nRouteParams: I18nRouteParams, to?: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string) {
415
+ const fromLocale = this.getCurrentLocale()
416
+
417
+ let current: RouteLocationResolved | RouteLocationNamedRaw
418
+ if (typeof to === 'string') {
419
+ current = this.resolveRouteWithStrategy(to, toLocale, fromLocale)
420
+ }
421
+ else {
422
+ current = to ?? this.getCurrentRoute() as RouteLocationResolved
423
+ }
424
+
425
+ this.updateCookies(toLocale)
426
+ const switchedRoute = this.switchLocaleRoute(fromLocale, toLocale, current, i18nRouteParams)
427
+
428
+ if (typeof switchedRoute === 'string' && switchedRoute.startsWith('http')) {
429
+ return this.navigateTo(switchedRoute, { redirectCode: 200, external: true })
430
+ }
431
+
432
+ if (isNoPrefixStrategy(this.i18nConfig.strategy!)) {
433
+ (switchedRoute as RouteLocationRaw & RouteLocationOptions).force = true
434
+ }
435
+
436
+ return this.router.push(switchedRoute)
437
+ }
438
+
439
+ resolveLocalizedRoute(
440
+ to: RouteLocationNamedRaw | RouteLocationAsPathGeneric | string,
441
+ locale?: string,
442
+ ): RouteLocationResolved {
443
+ const currentRoute = this.getCurrentRoute()
444
+ const fromLocale = this.getCurrentLocale()
445
+ const currentLocale = locale ?? fromLocale
446
+
447
+ let current: RouteLocationResolved | RouteLocationNamedRaw | RouteLocationAsPathGeneric
448
+ if (typeof to === 'string') {
449
+ // Try to resolve as route name first (if it doesn't start with / and exists as route name)
450
+ if (!to.startsWith('/')) {
451
+ const routeName = to
452
+ // Check if this is a route name (not a path)
453
+ if (this.router.hasRoute(routeName)) {
454
+ // Resolve by name to get the correct route object
455
+ current = this.router.resolve({ name: routeName })
456
+ }
457
+ else {
458
+ // Treat as path
459
+ to = `/${to}`
460
+ current = this.resolveRouteWithStrategy(to, currentLocale, fromLocale)
461
+ }
462
+ }
463
+ else {
464
+ current = this.resolveRouteWithStrategy(to, currentLocale, fromLocale)
465
+ }
466
+ }
467
+ else {
468
+ current = to
469
+ }
470
+
471
+ return this.getLocalizedRoute(current, currentRoute, currentLocale)
472
+ }
473
+ }