@i18n-micro/astro 1.3.25 → 2.0.1
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/LICENSE +1 -1
- package/README.md +8 -64
- package/dist/auto-middleware.d.ts +1 -0
- package/dist/client/core.d.cts +86 -0
- package/dist/client/core.d.ts +71 -8
- package/dist/client/index.d.cts +2 -6
- package/dist/client/index.d.ts +2 -6
- package/dist/client/index.js +1 -6
- package/dist/client/preact.d.ts +12 -23
- package/dist/client/preact.js +13 -37
- package/dist/client/react.d.ts +14 -21
- package/dist/client/react.js +12 -37
- package/dist/client/svelte.d.ts +14 -14
- package/dist/client/svelte.js +17 -47
- package/dist/client/vue.d.ts +15 -15
- package/dist/client/vue.js +37 -2
- package/dist/composer.d.cts +16 -0
- package/dist/composer.d.ts +10 -30
- package/dist/core-CgrgdmGy.cjs +1 -0
- package/dist/core-DTAcn2yF.js +72 -0
- package/dist/env.d.ts +8 -2
- package/dist/index.cjs +11 -3
- package/dist/index.d.cts +10 -13
- package/dist/index.d.ts +10 -13
- package/dist/index.mjs +171 -482
- package/dist/integration.d.cts +26 -0
- package/dist/integration.d.ts +14 -15
- package/dist/middleware-BT3OA5YL.cjs +1 -0
- package/dist/middleware-Ch8E0iV_.js +257 -0
- package/dist/middleware.d.cts +34 -0
- package/dist/middleware.d.ts +17 -8
- package/dist/middleware.js +23 -0
- package/dist/node.d.ts +26 -0
- package/dist/node.js +22 -0
- package/dist/request.d.ts +69 -0
- package/dist/router/adapter.d.cts +9 -0
- package/dist/router/adapter.d.ts +5 -4
- package/dist/router/types.d.cts +66 -0
- package/dist/router/types.d.ts +3 -2
- package/dist/utils.d.cts +112 -0
- package/dist/utils.d.ts +39 -35
- package/dist/virtual.d.ts +21 -0
- package/package.json +28 -15
- package/src/auto-middleware.ts +26 -0
- package/src/client/core.ts +124 -59
- package/src/client/index.ts +2 -11
- package/src/client/preact.tsx +12 -127
- package/src/client/react.tsx +11 -123
- package/src/client/svelte.ts +6 -69
- package/src/client/vue.ts +22 -77
- package/src/components/i18n-link.astro +2 -59
- package/src/components/i18n-switcher.astro +46 -149
- package/src/components/i18n-t.astro +6 -54
- package/src/composer.ts +25 -115
- package/src/env.d.ts +7 -0
- package/src/index.ts +1 -6
- package/src/integration.ts +113 -52
- package/src/middleware.ts +174 -120
- package/src/node.ts +51 -0
- package/src/request.ts +177 -0
- package/src/router/adapter.ts +47 -149
- package/src/router/types.ts +3 -2
- package/src/utils.ts +149 -323
- package/src/virtual.d.ts +21 -0
- package/dist/components/index.d.cts +0 -0
- package/dist/components/index.d.ts +0 -0
- package/dist/core-DwkpOV-H.cjs +0 -1
- package/dist/core-VSnkGRWR.js +0 -28
- package/dist/load-translations.d.ts +0 -43
- package/dist/routing-context.d.ts +0 -4
- package/dist/vue-BNiEwZr2.cjs +0 -5
- package/dist/vue-CWHjIOiS.js +0 -1724
- package/src/components/index.ts +0 -11
- package/src/load-translations.ts +0 -106
- package/src/routing-context.ts +0 -23
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { Locale } from '@i18n-micro/types'
|
|
3
|
-
import
|
|
4
|
-
import { useI18n } from '../utils'
|
|
3
|
+
import { localeLabel, useI18n } from '../utils'
|
|
5
4
|
|
|
6
5
|
interface Props {
|
|
7
6
|
class?: string
|
|
@@ -12,90 +11,31 @@ interface Props {
|
|
|
12
11
|
|
|
13
12
|
const { class: className, activeClass = 'active', customLabels = {}, ...attrs } = Astro.props
|
|
14
13
|
|
|
15
|
-
const { locale: currentLocale, locales,
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const currentLocaleObj = availableLocales.find((loc: Locale) => loc.code === currentLocale)
|
|
27
|
-
const currentLocaleLabel = currentLocaleObj
|
|
28
|
-
? customLabels[currentLocaleObj.code] || currentLocaleObj.displayName || currentLocaleObj.code
|
|
29
|
-
: currentLocale
|
|
30
|
-
|
|
31
|
-
// Generate locale links
|
|
32
|
-
const localeLinks = availableLocales.map((loc: Locale) => {
|
|
33
|
-
const isActive = loc.code === currentLocale
|
|
34
|
-
// Get base path without locale
|
|
35
|
-
const basePath = getBasePath()
|
|
36
|
-
// Generate path with locale
|
|
37
|
-
let switchPath = basePath
|
|
38
|
-
if (routingStrategy?.switchLocalePath) {
|
|
39
|
-
switchPath = routingStrategy.switchLocalePath(basePath, loc.code, localeCodes, defaultLocale)
|
|
40
|
-
} else {
|
|
41
|
-
// Fallback: basic locale switching
|
|
42
|
-
const segments = basePath.split('/').filter(Boolean)
|
|
43
|
-
const firstSegment = segments[0]
|
|
44
|
-
if (firstSegment && localeCodes.includes(firstSegment)) {
|
|
45
|
-
segments.shift()
|
|
46
|
-
}
|
|
47
|
-
if (loc.code !== defaultLocale) {
|
|
48
|
-
segments.unshift(loc.code)
|
|
49
|
-
}
|
|
50
|
-
switchPath = `/${segments.join('/')}`
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const label = customLabels[loc.code] || loc.displayName || loc.code
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
code: loc.code,
|
|
57
|
-
displayName: label,
|
|
58
|
-
path: switchPath,
|
|
59
|
-
isActive,
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
// Generate unique ID for this component instance
|
|
64
|
-
const componentId = `i18n-switcher-${Math.random().toString(36).substring(2, 9)}`
|
|
14
|
+
const { locale: currentLocale, locales, switchLocalePath } = useI18n(Astro)
|
|
15
|
+
const { search } = Astro.locals.currentUrl ?? Astro.url
|
|
16
|
+
|
|
17
|
+
const links = locales
|
|
18
|
+
.filter((locale: Locale) => !locale.disabled)
|
|
19
|
+
.map((locale: Locale) => ({
|
|
20
|
+
code: locale.code,
|
|
21
|
+
label: localeLabel(locale, customLabels),
|
|
22
|
+
href: switchLocalePath(locale.code) + search,
|
|
23
|
+
isActive: locale.code === currentLocale,
|
|
24
|
+
}))
|
|
65
25
|
---
|
|
66
26
|
|
|
67
|
-
{
|
|
68
|
-
<div
|
|
69
|
-
class=
|
|
70
|
-
|
|
71
|
-
{...attrs}
|
|
72
|
-
>
|
|
73
|
-
<button
|
|
74
|
-
type="button"
|
|
75
|
-
class="i18n-switcher-button"
|
|
76
|
-
aria-haspopup="true"
|
|
77
|
-
aria-expanded="false"
|
|
78
|
-
data-switcher-button={componentId}
|
|
79
|
-
>
|
|
80
|
-
<span>{currentLocaleLabel}</span>
|
|
27
|
+
{links.length > 0 && (
|
|
28
|
+
<div class={`i18n-switcher-wrapper ${className || ''}`} data-i18n-switcher {...attrs}>
|
|
29
|
+
<button type="button" class="i18n-switcher-button" aria-expanded="false">
|
|
30
|
+
<span>{links.find((link) => link.isActive)?.label ?? currentLocale}</span>
|
|
81
31
|
<span class="i18n-switcher-icon">▼</span>
|
|
82
32
|
</button>
|
|
83
33
|
|
|
84
|
-
<ul
|
|
85
|
-
|
|
86
|
-
data-switcher-dropdown={componentId}
|
|
87
|
-
style="display: none;"
|
|
88
|
-
>
|
|
89
|
-
{localeLinks.map((link) => (
|
|
34
|
+
<ul class="i18n-switcher-dropdown" hidden>
|
|
35
|
+
{links.map((link) => (
|
|
90
36
|
<li>
|
|
91
|
-
<a
|
|
92
|
-
|
|
93
|
-
class={link.isActive ? activeClass : ''}
|
|
94
|
-
aria-current={link.isActive ? 'page' : undefined}
|
|
95
|
-
data-locale={link.code}
|
|
96
|
-
data-switcher-link={componentId}
|
|
97
|
-
>
|
|
98
|
-
{link.displayName}
|
|
37
|
+
<a href={link.href} class={link.isActive ? activeClass : ''} aria-current={link.isActive ? 'page' : undefined} data-locale={link.code}>
|
|
38
|
+
{link.label}
|
|
99
39
|
</a>
|
|
100
40
|
</li>
|
|
101
41
|
))}
|
|
@@ -103,73 +43,27 @@ const componentId = `i18n-switcher-${Math.random().toString(36).substring(2, 9)}
|
|
|
103
43
|
</div>
|
|
104
44
|
)}
|
|
105
45
|
|
|
106
|
-
<script
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (icon) {
|
|
128
|
-
icon.style.transform = isOpen ? 'rotate(180deg)' : 'rotate(0deg)'
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const closeDropdown = () => {
|
|
133
|
-
if (isOpen) {
|
|
134
|
-
isOpen = false
|
|
135
|
-
dropdown.style.display = 'none'
|
|
136
|
-
button.setAttribute('aria-expanded', 'false')
|
|
137
|
-
if (icon) {
|
|
138
|
-
icon.style.transform = 'rotate(0deg)'
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
button.addEventListener('click', toggleDropdown)
|
|
144
|
-
|
|
145
|
-
// Close on outside click
|
|
146
|
-
const handleClickOutside = (event) => {
|
|
147
|
-
const target = event.target
|
|
148
|
-
if (!target || !wrapper.contains(target)) {
|
|
149
|
-
closeDropdown()
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
document.addEventListener('click', handleClickOutside)
|
|
154
|
-
|
|
155
|
-
// Close on Escape key
|
|
156
|
-
const handleEscape = (event) => {
|
|
157
|
-
if (event.key === 'Escape' && isOpen) {
|
|
158
|
-
closeDropdown()
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
document.addEventListener('keydown', handleEscape)
|
|
163
|
-
|
|
164
|
-
// Handle locale link clicks
|
|
165
|
-
const links = wrapper.querySelectorAll(`[data-switcher-link="${componentId}"]`)
|
|
166
|
-
links.forEach((link) => {
|
|
167
|
-
link.addEventListener('click', () => {
|
|
168
|
-
// Let the link navigate naturally, but close dropdown
|
|
169
|
-
closeDropdown()
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
})()
|
|
46
|
+
<script>
|
|
47
|
+
// One module for every switcher on every page: it runs once, also under <ClientRouter>, and the
|
|
48
|
+
// listeners on `document` reach the switchers a navigation swaps in.
|
|
49
|
+
const setOpen = (wrapper: Element, open: boolean) => {
|
|
50
|
+
wrapper.querySelector('button')?.setAttribute('aria-expanded', String(open))
|
|
51
|
+
wrapper.querySelector('ul')?.toggleAttribute('hidden', !open)
|
|
52
|
+
}
|
|
53
|
+
const closeAll = (except?: Element | null) => {
|
|
54
|
+
for (const wrapper of document.querySelectorAll('[data-i18n-switcher]')) if (wrapper !== except) setOpen(wrapper, false)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
document.addEventListener('click', (event) => {
|
|
58
|
+
const target = event.target as Element
|
|
59
|
+
const wrapper = target.closest('[data-i18n-switcher]')
|
|
60
|
+
closeAll(wrapper)
|
|
61
|
+
if (wrapper && target.closest('button')) setOpen(wrapper, wrapper.querySelector('button')?.getAttribute('aria-expanded') !== 'true')
|
|
62
|
+
else if (wrapper && target.closest('a')) setOpen(wrapper, false)
|
|
63
|
+
})
|
|
64
|
+
document.addEventListener('keydown', (event) => {
|
|
65
|
+
if (event.key === 'Escape') closeAll()
|
|
66
|
+
})
|
|
173
67
|
</script>
|
|
174
68
|
|
|
175
69
|
<style>
|
|
@@ -203,6 +97,10 @@ const componentId = `i18n-switcher-${Math.random().toString(36).substring(2, 9)}
|
|
|
203
97
|
font-size: 12px;
|
|
204
98
|
}
|
|
205
99
|
|
|
100
|
+
.i18n-switcher-button[aria-expanded='true'] .i18n-switcher-icon {
|
|
101
|
+
transform: rotate(180deg);
|
|
102
|
+
}
|
|
103
|
+
|
|
206
104
|
.i18n-switcher-dropdown {
|
|
207
105
|
position: absolute;
|
|
208
106
|
top: 100%;
|
|
@@ -214,7 +112,7 @@ const componentId = `i18n-switcher-${Math.random().toString(36).substring(2, 9)}
|
|
|
214
112
|
padding: 0;
|
|
215
113
|
margin: 4px 0 0 0;
|
|
216
114
|
min-width: 150px;
|
|
217
|
-
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
115
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
218
116
|
}
|
|
219
117
|
|
|
220
118
|
.i18n-switcher-dropdown li {
|
|
@@ -241,4 +139,3 @@ const componentId = `i18n-switcher-${Math.random().toString(36).substring(2, 9)}
|
|
|
241
139
|
background-color: #f0f0f0;
|
|
242
140
|
}
|
|
243
141
|
</style>
|
|
244
|
-
|
|
@@ -1,61 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
import type
|
|
3
|
-
import { useI18n } from '../utils'
|
|
2
|
+
import { resolveI18nT, type I18nTProps } from '../utils'
|
|
4
3
|
|
|
5
|
-
interface Props {
|
|
6
|
-
keypath: string
|
|
7
|
-
plural?: number | string
|
|
8
|
-
params?: Params
|
|
9
|
-
defaultValue?: string
|
|
4
|
+
interface Props extends Omit<I18nTProps, 'customPluralRule'> {
|
|
10
5
|
tag?: string
|
|
11
|
-
|
|
12
|
-
number?: number | string
|
|
13
|
-
date?: Date | string | number
|
|
14
|
-
relativeDate?: Date | string | number
|
|
6
|
+
[key: string]: unknown
|
|
15
7
|
}
|
|
16
8
|
|
|
17
|
-
const { keypath, plural, params
|
|
18
|
-
|
|
19
|
-
const { t, tc, tn, td, tdr } = useI18n(Astro)
|
|
20
|
-
|
|
21
|
-
let translation: string = ''
|
|
22
|
-
|
|
23
|
-
// Handle number formatting
|
|
24
|
-
if (number !== undefined) {
|
|
25
|
-
const numberValue = Number(number)
|
|
26
|
-
const formattedNumber = tn(numberValue)
|
|
27
|
-
translation = t(keypath, { number: formattedNumber, ...params }, defaultValue)?.toString() || defaultValue || keypath
|
|
28
|
-
}
|
|
29
|
-
// Handle date formatting
|
|
30
|
-
else if (date !== undefined) {
|
|
31
|
-
const formattedDate = td(date)
|
|
32
|
-
translation = t(keypath, { date: formattedDate, ...params }, defaultValue)?.toString() || defaultValue || keypath
|
|
33
|
-
}
|
|
34
|
-
// Handle relative date formatting
|
|
35
|
-
else if (relativeDate !== undefined) {
|
|
36
|
-
const formattedRelativeDate = tdr(relativeDate)
|
|
37
|
-
translation = t(keypath, { relativeDate: formattedRelativeDate, ...params }, defaultValue)?.toString() || defaultValue || keypath
|
|
38
|
-
}
|
|
39
|
-
// Handle pluralization
|
|
40
|
-
else if (plural !== undefined) {
|
|
41
|
-
const count = Number.parseInt(plural.toString(), 10)
|
|
42
|
-
translation = tc(keypath, { count, ...params }, defaultValue)
|
|
43
|
-
}
|
|
44
|
-
// Regular translation
|
|
45
|
-
else {
|
|
46
|
-
translation = t(keypath, params, defaultValue)?.toString() || defaultValue || keypath
|
|
47
|
-
}
|
|
9
|
+
const { keypath, plural, params, defaultValue, tag: Tag = 'span', html = false, hideIfEmpty, number, date, relativeDate, ...attrs } = Astro.props
|
|
10
|
+
const text = resolveI18nT(Astro, { keypath, plural, params, defaultValue, html, hideIfEmpty, number, date, relativeDate })
|
|
48
11
|
---
|
|
49
12
|
|
|
50
|
-
{
|
|
51
|
-
<Fragment set:html={`<${tag}>${translation}</${tag}>`} />
|
|
52
|
-
) : tag === 'span' ? (
|
|
53
|
-
<span>{translation}</span>
|
|
54
|
-
) : tag === 'div' ? (
|
|
55
|
-
<div>{translation}</div>
|
|
56
|
-
) : tag === 'p' ? (
|
|
57
|
-
<p>{translation}</p>
|
|
58
|
-
) : (
|
|
59
|
-
<span>{translation}</span>
|
|
60
|
-
)}
|
|
61
|
-
|
|
13
|
+
{text === null ? defaultValue : html ? <Tag {...attrs} set:html={text} /> : <Tag {...attrs}>{text}</Tag>}
|
package/src/composer.ts
CHANGED
|
@@ -1,135 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import { I18n, type I18nOptions } from '@i18n-micro/runtime'
|
|
2
|
+
import type { Translations } from '@i18n-micro/types'
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
locale: string
|
|
6
|
-
fallbackLocale?: string
|
|
7
|
-
messages?: Record<string, Translations>
|
|
8
|
-
plural?: PluralFunc
|
|
9
|
-
missingWarn?: boolean
|
|
10
|
-
missingHandler?: (locale: string, key: string, routeName: string) => void
|
|
11
|
-
numberFormats?: NumberFormatsConfig
|
|
12
|
-
datetimeFormats?: DateTimeFormatsConfig
|
|
13
|
-
_storage?: TranslationStorage
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class AstroI18n extends BaseI18n {
|
|
17
|
-
private _locale: string
|
|
18
|
-
private _fallbackLocale: string
|
|
19
|
-
private _currentRoute: string
|
|
4
|
+
export type AstroI18nOptions = I18nOptions
|
|
20
5
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
private initialMessages: Record<string, Translations>
|
|
6
|
+
/** The runtime instance, plus what Astro pages use on top: per-route reads and a reset that keeps `messages`. */
|
|
7
|
+
export class AstroI18n extends I18n {
|
|
8
|
+
private initialMessages: Record<string, Translations>
|
|
9
|
+
private initialRouteMessages: Record<string, Record<string, Translations>>
|
|
24
10
|
|
|
25
11
|
constructor(options: AstroI18nOptions) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
plural: options.plural,
|
|
33
|
-
missingWarn: options.missingWarn,
|
|
34
|
-
missingHandler: options.missingHandler,
|
|
35
|
-
numberFormats: options.numberFormats,
|
|
36
|
-
datetimeFormats: options.datetimeFormats,
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
this.storage = storage
|
|
40
|
-
this._locale = options.locale
|
|
41
|
-
this._fallbackLocale = options.fallbackLocale || options.locale
|
|
42
|
-
this._currentRoute = 'index'
|
|
43
|
-
|
|
44
|
-
if (options.messages) {
|
|
45
|
-
this.initialMessages = { ...options.messages }
|
|
46
|
-
for (const [lang, msgs] of Object.entries(options.messages)) {
|
|
47
|
-
this.helper.loadTranslations(lang, msgs)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private cloneStorage(source: TranslationStorage): TranslationStorage {
|
|
53
|
-
const translations = new Map<string, Translations>()
|
|
54
|
-
for (const [key, value] of source.translations) {
|
|
55
|
-
translations.set(key, { ...value })
|
|
56
|
-
}
|
|
57
|
-
return { translations }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public clone(newLocale?: string): AstroI18n {
|
|
61
|
-
const isolatedStorage = this.cloneStorage(this.storage)
|
|
62
|
-
|
|
63
|
-
return new AstroI18n({
|
|
64
|
-
locale: newLocale || this._locale,
|
|
65
|
-
fallbackLocale: this._fallbackLocale,
|
|
66
|
-
plural: this.pluralFunc,
|
|
67
|
-
missingWarn: this.missingWarn,
|
|
68
|
-
missingHandler: this.missingHandler,
|
|
69
|
-
numberFormats: this.formatter.getNumberFormats(),
|
|
70
|
-
datetimeFormats: this.formatter.getDateTimeFormats(),
|
|
71
|
-
_storage: isolatedStorage,
|
|
72
|
-
})
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
get locale(): string {
|
|
76
|
-
return this._locale
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
set locale(val: string) {
|
|
80
|
-
this._locale = val
|
|
12
|
+
super(options)
|
|
13
|
+
const parent = options.parent as AstroI18n | undefined
|
|
14
|
+
this.initialMessages = parent?.initialMessages ?? { ...options.messages }
|
|
15
|
+
this.initialRouteMessages =
|
|
16
|
+
parent?.initialRouteMessages ??
|
|
17
|
+
Object.fromEntries(Object.entries(options.routeMessages ?? {}).map(([route, locales]) => [route, { ...locales }]))
|
|
81
18
|
}
|
|
82
19
|
|
|
83
|
-
|
|
84
|
-
|
|
20
|
+
/** Dictionaries loaded after the constructor that `clearCache` keeps too: a directory read at runtime. */
|
|
21
|
+
public keepMessages(messages: Record<string, Translations>, routeMessages: Record<string, Record<string, Translations>>): void {
|
|
22
|
+
this.initialMessages = { ...this.initialMessages, ...messages }
|
|
23
|
+
const routes = { ...this.initialRouteMessages }
|
|
24
|
+
for (const [route, locales] of Object.entries(routeMessages)) routes[route] = { ...routes[route], ...locales }
|
|
25
|
+
this.initialRouteMessages = routes
|
|
85
26
|
}
|
|
86
27
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
setRoute(routeName: string): void {
|
|
92
|
-
this._currentRoute = routeName
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
public getLocale(): string {
|
|
96
|
-
return this._locale
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public getFallbackLocale(): string {
|
|
100
|
-
return this._fallbackLocale
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public getRoute(): string {
|
|
104
|
-
return this._currentRoute
|
|
28
|
+
/** The `localeIso` option: what an island needs to plural in another locale. */
|
|
29
|
+
public getLocaleIso(): Record<string, string> {
|
|
30
|
+
return this.localeIso
|
|
105
31
|
}
|
|
106
32
|
|
|
107
33
|
public getRouteTranslations(locale: string, routeName: string): Translations | null {
|
|
108
|
-
|
|
109
|
-
return this.storage.translations.get(cacheKey) ?? null
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public addTranslations(locale: string, translations: Translations, merge = true): void {
|
|
113
|
-
super.loadTranslationsCore(locale, translations, merge)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
public addRouteTranslations(locale: string, routeName: string, translations: Translations, merge = true): void {
|
|
117
|
-
super.loadRouteTranslationsCore(locale, routeName, translations, merge)
|
|
34
|
+
return this.helper.getCache(locale, routeName) ?? null
|
|
118
35
|
}
|
|
119
36
|
|
|
120
37
|
public mergeTranslations(locale: string, routeName: string, translations: Translations): void {
|
|
121
|
-
this.
|
|
38
|
+
this.addRouteTranslations(locale, routeName, translations)
|
|
122
39
|
}
|
|
123
40
|
|
|
124
41
|
public override clearCache(): void {
|
|
125
|
-
const initialMessages = { ...this.initialMessages }
|
|
126
|
-
|
|
127
42
|
super.clearCache()
|
|
128
|
-
|
|
129
|
-
if (Object.keys(initialMessages).length > 0) {
|
|
130
|
-
for (const [lang, msgs] of Object.entries(initialMessages)) {
|
|
131
|
-
this.helper.loadTranslations(lang, msgs)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
43
|
+
this.loadMessages(this.initialMessages, this.initialRouteMessages)
|
|
134
44
|
}
|
|
135
45
|
}
|
package/src/env.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
// It's loaded directly by Astro via entrypoint in integration config
|
|
1
2
|
/// <reference types="astro/client" />
|
|
2
3
|
|
|
3
4
|
import type { AstroI18n } from './composer'
|
|
4
5
|
import type { Locale } from '@i18n-micro/types'
|
|
6
|
+
import type { MiddlewareHandler } from 'astro'
|
|
5
7
|
import type { I18nRoutingStrategy } from './router/types'
|
|
6
8
|
|
|
7
9
|
declare global {
|
|
@@ -11,8 +13,13 @@ declare global {
|
|
|
11
13
|
locale: string
|
|
12
14
|
defaultLocale: string
|
|
13
15
|
locales: Locale[]
|
|
16
|
+
/** The page's URL: the original one after `Astro.rewrite`, the page's one in a server island. */
|
|
14
17
|
currentUrl: URL
|
|
15
18
|
routingStrategy?: I18nRoutingStrategy
|
|
19
|
+
/** Astro's `base`, no trailing slash. */
|
|
20
|
+
i18nBase?: string
|
|
21
|
+
/** The middleware that handled this request. */
|
|
22
|
+
i18nHandler?: MiddlewareHandler
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,17 +2,13 @@
|
|
|
2
2
|
import './env.d'
|
|
3
3
|
|
|
4
4
|
// Re-export utilities from core
|
|
5
|
-
export { defaultPlural, FormatService, interpolate } from '@i18n-micro/core'
|
|
5
|
+
export { defaultPlural, FormatService, interpolate, localeIsoMap } from '@i18n-micro/core'
|
|
6
6
|
// Re-export types from @i18n-micro/types
|
|
7
7
|
export type { CleanTranslation, Getter, Locale, LocaleCode, Params, PluralFunc, Translations } from '@i18n-micro/types'
|
|
8
8
|
export { AstroI18n, type AstroI18nOptions } from './composer'
|
|
9
9
|
// Export integration options type
|
|
10
10
|
export type { I18nIntegrationOptions } from './integration'
|
|
11
11
|
export { createI18n, i18nIntegration } from './integration'
|
|
12
|
-
export { getGlobalRoutingStrategy, runWithRoutingStrategy, setGlobalRoutingStrategy } from './routing-context'
|
|
13
|
-
export type { LoadedTranslations, LoadTranslationsOptions } from './load-translations'
|
|
14
|
-
// Export translation loading utilities
|
|
15
|
-
export { loadTranslationsFromDir, loadTranslationsIntoI18n } from './load-translations'
|
|
16
12
|
export type { I18nMiddlewareOptions } from './middleware'
|
|
17
13
|
export { createI18nMiddleware, detectLocale } from './middleware'
|
|
18
14
|
export { createAstroRouterAdapter } from './router/adapter'
|
|
@@ -31,4 +27,3 @@ export {
|
|
|
31
27
|
useI18n,
|
|
32
28
|
useLocaleHead,
|
|
33
29
|
} from './utils'
|
|
34
|
-
// It's loaded directly by Astro via entrypoint in integration config
|