@i18n-micro/astro 1.3.4 → 1.3.6
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/index.cjs +2 -2
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.mjs +264 -308
- package/dist/load-translations.d.ts +2 -3
- package/package.json +42 -41
- package/src/index.ts +3 -31
- package/src/load-translations.ts +34 -56
- package/src/middleware.ts +4 -35
- package/dist/routing.d.ts +0 -23
- package/src/routing.ts +0 -94
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Translations } from '@i18n-micro/types';
|
|
2
|
+
import { TranslationFileBuckets } from '@i18n-micro/utils/parse-path';
|
|
2
3
|
/**
|
|
3
4
|
* WARNING: Node.js-only functions
|
|
4
5
|
*
|
|
@@ -20,9 +21,7 @@ export interface LoadTranslationsOptions {
|
|
|
20
21
|
rootDir?: string;
|
|
21
22
|
disablePageLocales?: boolean;
|
|
22
23
|
}
|
|
23
|
-
export interface LoadedTranslations {
|
|
24
|
-
root: Record<string, Translations>;
|
|
25
|
-
routes: Record<string, Record<string, Translations>>;
|
|
24
|
+
export interface LoadedTranslations extends TranslationFileBuckets<Translations> {
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
28
27
|
* Load all translations from a directory
|
package/package.json
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i18n-micro/astro",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"description": "Astro integration for i18n-micro — translations, routing, and islands.",
|
|
5
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"astro",
|
|
7
|
+
"i18n",
|
|
8
|
+
"internationalization",
|
|
9
|
+
"translations"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/s00d/nuxt-i18n-micro/tree/main/packages/astro#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/s00d/nuxt-i18n-micro/issues"
|
|
14
|
+
},
|
|
6
15
|
"license": "MIT",
|
|
7
|
-
"sideEffects": false,
|
|
8
16
|
"repository": {
|
|
9
17
|
"type": "git",
|
|
10
18
|
"url": "git+https://github.com/s00d/nuxt-i18n-micro.git",
|
|
11
19
|
"directory": "packages/astro"
|
|
12
20
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
20
29
|
"main": "./dist/index.cjs",
|
|
21
30
|
"types": "./dist/index.d.ts",
|
|
22
31
|
"exports": {
|
|
@@ -64,32 +73,35 @@
|
|
|
64
73
|
},
|
|
65
74
|
"./package.json": "./package.json"
|
|
66
75
|
},
|
|
67
|
-
"files": [
|
|
68
|
-
"dist",
|
|
69
|
-
"src",
|
|
70
|
-
"README.md",
|
|
71
|
-
"LICENSE"
|
|
72
|
-
],
|
|
73
76
|
"publishConfig": {
|
|
74
77
|
"access": "public"
|
|
75
78
|
},
|
|
76
|
-
"keywords": [
|
|
77
|
-
"astro",
|
|
78
|
-
"i18n",
|
|
79
|
-
"internationalization",
|
|
80
|
-
"translations"
|
|
81
|
-
],
|
|
82
79
|
"dependencies": {
|
|
83
|
-
"@i18n-micro/
|
|
84
|
-
"@i18n-micro/
|
|
85
|
-
"@i18n-micro/
|
|
80
|
+
"@i18n-micro/utils": "1.0.1",
|
|
81
|
+
"@i18n-micro/core": "1.3.2",
|
|
82
|
+
"@i18n-micro/node": "1.2.3",
|
|
83
|
+
"@i18n-micro/types": "1.2.4"
|
|
86
84
|
},
|
|
87
|
-
"
|
|
85
|
+
"devDependencies": {
|
|
86
|
+
"@types/jest": "^29.5.14",
|
|
88
87
|
"astro": "^5.16.5",
|
|
88
|
+
"jest": "^29.7.0",
|
|
89
|
+
"preact": "^10.0.0",
|
|
90
|
+
"publint": "^0.3.17",
|
|
91
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
89
92
|
"svelte": "^4.0.0 || ^5.0.0",
|
|
90
|
-
"
|
|
93
|
+
"ts-jest": "^29.4.6",
|
|
94
|
+
"vite": "^7.3.1",
|
|
95
|
+
"vite-plugin-dts": "^4.5.4",
|
|
96
|
+
"vitest": "^3.2.4",
|
|
97
|
+
"vue": "^3.5.25"
|
|
98
|
+
},
|
|
99
|
+
"peerDependencies": {
|
|
100
|
+
"astro": "^5.16.5",
|
|
101
|
+
"preact": "^10.0.0",
|
|
91
102
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
92
|
-
"
|
|
103
|
+
"svelte": "^4.0.0 || ^5.0.0",
|
|
104
|
+
"vue": "^3.5.25"
|
|
93
105
|
},
|
|
94
106
|
"peerDependenciesMeta": {
|
|
95
107
|
"svelte": {
|
|
@@ -105,19 +117,8 @@
|
|
|
105
117
|
"optional": true
|
|
106
118
|
}
|
|
107
119
|
},
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"vite": "^7.3.1",
|
|
111
|
-
"vite-plugin-dts": "^4.5.4",
|
|
112
|
-
"jest": "^29.7.0",
|
|
113
|
-
"ts-jest": "^29.4.6",
|
|
114
|
-
"@types/jest": "^29.5.14",
|
|
115
|
-
"svelte": "^4.0.0 || ^5.0.0",
|
|
116
|
-
"vue": "^3.5.25",
|
|
117
|
-
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
118
|
-
"preact": "^10.0.0",
|
|
119
|
-
"publint": "^0.3.17",
|
|
120
|
-
"vitest": "^3.2.4"
|
|
120
|
+
"engines": {
|
|
121
|
+
"node": ">=18"
|
|
121
122
|
},
|
|
122
123
|
"scripts": {
|
|
123
124
|
"build": "vite build",
|
package/src/index.ts
CHANGED
|
@@ -4,15 +4,7 @@ import './env.d'
|
|
|
4
4
|
// Re-export utilities from core
|
|
5
5
|
export { defaultPlural, FormatService, interpolate } from '@i18n-micro/core'
|
|
6
6
|
// Re-export types from @i18n-micro/types
|
|
7
|
-
export type {
|
|
8
|
-
CleanTranslation,
|
|
9
|
-
Getter,
|
|
10
|
-
Locale,
|
|
11
|
-
LocaleCode,
|
|
12
|
-
Params,
|
|
13
|
-
PluralFunc,
|
|
14
|
-
Translations,
|
|
15
|
-
} from '@i18n-micro/types'
|
|
7
|
+
export type { CleanTranslation, Getter, Locale, LocaleCode, Params, PluralFunc, Translations } from '@i18n-micro/types'
|
|
16
8
|
export { AstroI18n, type AstroI18nOptions } from './composer'
|
|
17
9
|
// Export integration options type
|
|
18
10
|
export type { I18nIntegrationOptions } from './integration'
|
|
@@ -20,33 +12,13 @@ export type { I18nIntegrationOptions } from './integration'
|
|
|
20
12
|
export { createI18n, i18nIntegration } from './integration'
|
|
21
13
|
export type { LoadedTranslations, LoadTranslationsOptions } from './load-translations'
|
|
22
14
|
// Export translation loading utilities
|
|
23
|
-
export {
|
|
24
|
-
loadTranslationsFromDir,
|
|
25
|
-
loadTranslationsIntoI18n,
|
|
26
|
-
} from './load-translations'
|
|
15
|
+
export { loadTranslationsFromDir, loadTranslationsIntoI18n } from './load-translations'
|
|
27
16
|
export type { I18nMiddlewareOptions } from './middleware'
|
|
28
17
|
export { createI18nMiddleware, detectLocale } from './middleware'
|
|
29
18
|
export { createAstroRouterAdapter } from './router/adapter'
|
|
30
19
|
// Router abstraction
|
|
31
20
|
export type { I18nRoutingStrategy } from './router/types'
|
|
32
|
-
// Legacy routing utilities (deprecated, use routingStrategy instead)
|
|
33
|
-
// Kept for backward compatibility
|
|
34
|
-
export {
|
|
35
|
-
getLocaleFromPath,
|
|
36
|
-
getRouteName,
|
|
37
|
-
localizePath,
|
|
38
|
-
removeLocaleFromPath,
|
|
39
|
-
switchLocalePath,
|
|
40
|
-
} from './routing'
|
|
41
21
|
export type { I18nClientProps, LocaleHeadOptions, LocaleHeadResult } from './utils'
|
|
42
22
|
// Utilities
|
|
43
|
-
export {
|
|
44
|
-
getDefaultLocale,
|
|
45
|
-
getI18n,
|
|
46
|
-
getI18nProps,
|
|
47
|
-
getLocale,
|
|
48
|
-
getLocales,
|
|
49
|
-
useI18n,
|
|
50
|
-
useLocaleHead,
|
|
51
|
-
} from './utils'
|
|
23
|
+
export { getDefaultLocale, getI18n, getI18nProps, getLocale, getLocales, useI18n, useLocaleHead } from './utils'
|
|
52
24
|
// It's loaded directly by Astro via entrypoint in integration config
|
package/src/load-translations.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
|
|
2
|
-
import { join, resolve } from 'node:path'
|
|
2
|
+
import { join, relative, resolve, sep } from 'node:path'
|
|
3
3
|
import type { Translations } from '@i18n-micro/types'
|
|
4
|
+
import { mergeRouteTranslationsWithRoot, storeLoadedTranslationFile, type TranslationFileBuckets } from '@i18n-micro/utils/parse-path'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* WARNING: Node.js-only functions
|
|
@@ -25,9 +26,24 @@ export interface LoadTranslationsOptions {
|
|
|
25
26
|
disablePageLocales?: boolean
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export interface LoadedTranslations {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
export interface LoadedTranslations extends TranslationFileBuckets<Translations> {}
|
|
30
|
+
|
|
31
|
+
function walkTranslationFiles(dir: string, onFile: (fullPath: string) => void): void {
|
|
32
|
+
if (!existsSync(dir)) return
|
|
33
|
+
|
|
34
|
+
for (const entry of readdirSync(dir)) {
|
|
35
|
+
const fullPath = join(dir, entry)
|
|
36
|
+
const stat = statSync(fullPath)
|
|
37
|
+
|
|
38
|
+
if (stat.isDirectory()) {
|
|
39
|
+
walkTranslationFiles(fullPath, onFile)
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (entry.endsWith('.json')) {
|
|
44
|
+
onFile(fullPath)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
/**
|
|
@@ -46,56 +62,21 @@ export function loadTranslationsFromDir(options: LoadTranslationsOptions): Loade
|
|
|
46
62
|
return { root: {}, routes: {} }
|
|
47
63
|
}
|
|
48
64
|
|
|
49
|
-
const root:
|
|
50
|
-
const routes: Record<string, Record<string, Translations>> = {}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Recursively load translation files
|
|
54
|
-
*/
|
|
55
|
-
const loadFiles = (dir: string, routePrefix = ''): void => {
|
|
56
|
-
if (!existsSync(dir)) return
|
|
57
|
-
|
|
58
|
-
const entries = readdirSync(dir)
|
|
59
|
-
for (const entry of entries) {
|
|
60
|
-
const fullPath = join(dir, entry)
|
|
61
|
-
const stat = statSync(fullPath)
|
|
62
|
-
|
|
63
|
-
if (stat.isDirectory()) {
|
|
64
|
-
// If it's a 'pages' directory and page locales are enabled, treat as route-specific
|
|
65
|
-
if (entry === 'pages' && !disablePageLocales) {
|
|
66
|
-
loadFiles(fullPath, '')
|
|
67
|
-
} else if (routePrefix || disablePageLocales) {
|
|
68
|
-
loadFiles(fullPath, routePrefix)
|
|
69
|
-
} else {
|
|
70
|
-
// This is a route directory (e.g., pages/home/)
|
|
71
|
-
loadFiles(fullPath, entry)
|
|
72
|
-
}
|
|
73
|
-
} else if (entry.endsWith('.json')) {
|
|
74
|
-
const locale = entry.replace('.json', '')
|
|
75
|
-
try {
|
|
76
|
-
const content = readFileSync(fullPath, 'utf-8')
|
|
77
|
-
const translations = JSON.parse(content) as Translations
|
|
78
|
-
|
|
79
|
-
if (routePrefix && !disablePageLocales) {
|
|
80
|
-
// Route-specific translation
|
|
81
|
-
if (!routes[routePrefix]) {
|
|
82
|
-
routes[routePrefix] = {}
|
|
83
|
-
}
|
|
84
|
-
routes[routePrefix][locale] = translations
|
|
85
|
-
} else {
|
|
86
|
-
// Root-level translation
|
|
87
|
-
root[locale] = translations
|
|
88
|
-
}
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.error(`[i18n] Failed to load translation file: ${fullPath}`, error)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
65
|
+
const buckets: LoadedTranslations = { root: {}, routes: {} }
|
|
95
66
|
|
|
96
|
-
|
|
67
|
+
walkTranslationFiles(fullTranslationDir, (fullPath) => {
|
|
68
|
+
const relativePath = relative(fullTranslationDir, fullPath).split(sep).join('/')
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const content = readFileSync(fullPath, 'utf-8')
|
|
72
|
+
const translations = JSON.parse(content) as Translations
|
|
73
|
+
storeLoadedTranslationFile(buckets, relativePath, translations, disablePageLocales)
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(`[i18n] Failed to load translation file: ${fullPath}`, error)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
97
78
|
|
|
98
|
-
return
|
|
79
|
+
return buckets
|
|
99
80
|
}
|
|
100
81
|
|
|
101
82
|
/**
|
|
@@ -113,16 +94,13 @@ export function loadTranslationsIntoI18n(
|
|
|
113
94
|
): void {
|
|
114
95
|
const { root, routes } = loadTranslationsFromDir(options)
|
|
115
96
|
|
|
116
|
-
// Load root-level translations as index
|
|
117
97
|
for (const [locale, translations] of Object.entries(root)) {
|
|
118
98
|
i18n.addTranslations(locale, translations, false)
|
|
119
99
|
}
|
|
120
100
|
|
|
121
|
-
// Load route-specific translations (with root baked in)
|
|
122
101
|
for (const [routeName, routeTranslations] of Object.entries(routes)) {
|
|
123
102
|
for (const [locale, translations] of Object.entries(routeTranslations)) {
|
|
124
|
-
|
|
125
|
-
i18n.addRouteTranslations(locale, routeName, { ...base, ...translations }, false)
|
|
103
|
+
i18n.addRouteTranslations(locale, routeName, mergeRouteTranslationsWithRoot(root[locale], translations), false)
|
|
126
104
|
}
|
|
127
105
|
}
|
|
128
106
|
}
|
package/src/middleware.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Locale } from '@i18n-micro/types'
|
|
2
|
+
import { detectLocaleFromAcceptLanguage } from '@i18n-micro/utils/accept-language'
|
|
2
3
|
import type { MiddlewareHandler } from 'astro'
|
|
3
4
|
import type { AstroI18n } from './composer'
|
|
4
5
|
import { getGlobalRoutingStrategy } from './integration'
|
|
@@ -109,32 +110,6 @@ export function createI18nMiddleware(options: I18nMiddlewareOptions): Middleware
|
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
/**
|
|
113
|
-
* Parse Accept-Language header
|
|
114
|
-
*/
|
|
115
|
-
function parseAcceptLanguage(acceptLanguage: string): string[] {
|
|
116
|
-
const languages: string[] = []
|
|
117
|
-
const parts = acceptLanguage.split(',')
|
|
118
|
-
|
|
119
|
-
for (const part of parts) {
|
|
120
|
-
const [lang, q = '1.0'] = part.trim().split(';q=')
|
|
121
|
-
const quality = Number.parseFloat(q)
|
|
122
|
-
if (quality > 0 && lang) {
|
|
123
|
-
// Extract base language (e.g., 'en-US' -> 'en')
|
|
124
|
-
const baseLang = lang.split('-')[0]?.toLowerCase()
|
|
125
|
-
if (baseLang) {
|
|
126
|
-
languages.push(baseLang)
|
|
127
|
-
// Also add full locale if different
|
|
128
|
-
if (lang !== baseLang) {
|
|
129
|
-
languages.push(lang.toLowerCase())
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return languages
|
|
136
|
-
}
|
|
137
|
-
|
|
138
113
|
/**
|
|
139
114
|
* Detect locale from request
|
|
140
115
|
*/
|
|
@@ -173,15 +148,9 @@ export function detectLocale(
|
|
|
173
148
|
// 3. Try Accept-Language
|
|
174
149
|
if (locale === defaultLocale) {
|
|
175
150
|
try {
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
for (const preferred of preferredLocales) {
|
|
180
|
-
if (locales.includes(preferred)) {
|
|
181
|
-
locale = preferred
|
|
182
|
-
break
|
|
183
|
-
}
|
|
184
|
-
}
|
|
151
|
+
const detectedFromHeader = detectLocaleFromAcceptLanguage(headers.get('accept-language') ?? undefined, locales)
|
|
152
|
+
if (detectedFromHeader) {
|
|
153
|
+
locale = detectedFromHeader
|
|
185
154
|
}
|
|
186
155
|
} catch {
|
|
187
156
|
// Headers may be unavailable in static mode
|
package/dist/routing.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get route name from Astro path
|
|
3
|
-
* Extracts route name from path (e.g., /en/about -> about)
|
|
4
|
-
*/
|
|
5
|
-
export declare function getRouteName(path: string, locales?: string[]): string;
|
|
6
|
-
/**
|
|
7
|
-
* Get locale from path
|
|
8
|
-
* Checks if first segment is a locale code
|
|
9
|
-
*/
|
|
10
|
-
export declare function getLocaleFromPath(path: string, defaultLocale?: string, locales?: string[]): string;
|
|
11
|
-
/**
|
|
12
|
-
* Switch locale in path
|
|
13
|
-
* Replaces or adds locale prefix to path
|
|
14
|
-
*/
|
|
15
|
-
export declare function switchLocalePath(path: string, newLocale: string, locales?: string[], defaultLocale?: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Localize path with locale prefix
|
|
18
|
-
*/
|
|
19
|
-
export declare function localizePath(path: string, locale: string, locales?: string[], defaultLocale?: string): string;
|
|
20
|
-
/**
|
|
21
|
-
* Remove locale from path
|
|
22
|
-
*/
|
|
23
|
-
export declare function removeLocaleFromPath(path: string, locales?: string[]): string;
|
package/src/routing.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get route name from Astro path
|
|
3
|
-
* Extracts route name from path (e.g., /en/about -> about)
|
|
4
|
-
*/
|
|
5
|
-
export function getRouteName(path: string, locales: string[] = []): string {
|
|
6
|
-
// Normalize the path
|
|
7
|
-
const cleanPath = path.replace(/^\//, '').replace(/\/$/, '')
|
|
8
|
-
|
|
9
|
-
if (!cleanPath) {
|
|
10
|
-
return 'index'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const segments = cleanPath.split('/').filter(Boolean)
|
|
14
|
-
|
|
15
|
-
// Remove locale from path if present
|
|
16
|
-
const firstSegment = segments[0]
|
|
17
|
-
if (firstSegment && locales.includes(firstSegment)) {
|
|
18
|
-
segments.shift()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (segments.length === 0) {
|
|
22
|
-
return 'index'
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return segments.join('-')
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Get locale from path
|
|
30
|
-
* Checks if first segment is a locale code
|
|
31
|
-
*/
|
|
32
|
-
export function getLocaleFromPath(path: string, defaultLocale: string = 'en', locales: string[] = []): string {
|
|
33
|
-
const segments = path.split('/').filter(Boolean)
|
|
34
|
-
const firstSegment = segments[0]
|
|
35
|
-
if (firstSegment && locales.includes(firstSegment)) {
|
|
36
|
-
return firstSegment
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return defaultLocale
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Switch locale in path
|
|
44
|
-
* Replaces or adds locale prefix to path
|
|
45
|
-
*/
|
|
46
|
-
export function switchLocalePath(path: string, newLocale: string, locales: string[] = [], defaultLocale?: string): string {
|
|
47
|
-
const segments = path.split('/').filter(Boolean)
|
|
48
|
-
|
|
49
|
-
// Remove existing locale if present
|
|
50
|
-
const firstSegment = segments[0]
|
|
51
|
-
if (firstSegment && locales.includes(firstSegment)) {
|
|
52
|
-
segments.shift()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Add new locale if not default or if default should be included
|
|
56
|
-
if (newLocale !== defaultLocale || defaultLocale === undefined) {
|
|
57
|
-
segments.unshift(newLocale)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return `/${segments.join('/')}`
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Localize path with locale prefix
|
|
65
|
-
*/
|
|
66
|
-
export function localizePath(path: string, locale: string, locales: string[] = [], defaultLocale?: string): string {
|
|
67
|
-
const cleanPath = path.replace(/^\//, '').replace(/\/$/, '') || ''
|
|
68
|
-
const segments = cleanPath.split('/').filter(Boolean)
|
|
69
|
-
|
|
70
|
-
// Remove existing locale if present
|
|
71
|
-
const firstSegment = segments[0]
|
|
72
|
-
if (firstSegment && locales.includes(firstSegment)) {
|
|
73
|
-
segments.shift()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Add locale if not default or if default should be included
|
|
77
|
-
if (locale !== defaultLocale || defaultLocale === undefined) {
|
|
78
|
-
segments.unshift(locale)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return `/${segments.join('/')}`
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Remove locale from path
|
|
86
|
-
*/
|
|
87
|
-
export function removeLocaleFromPath(path: string, locales: string[] = []): string {
|
|
88
|
-
const segments = path.split('/').filter(Boolean)
|
|
89
|
-
const firstSegment = segments[0]
|
|
90
|
-
if (firstSegment && locales.includes(firstSegment)) {
|
|
91
|
-
segments.shift()
|
|
92
|
-
}
|
|
93
|
-
return `/${segments.join('/')}`
|
|
94
|
-
}
|