@zachhandley/ez-i18n 0.3.4 → 0.3.5
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 +69 -0
- package/package.json +2 -2
- package/src/index.ts +0 -130
- package/src/middleware.ts +0 -82
- package/src/runtime/index.ts +0 -19
- package/src/runtime/store.ts +0 -122
- package/src/types.ts +0 -125
- package/src/utils/index.ts +0 -16
- package/src/utils/locales.ts +0 -240
- package/src/utils/translations.ts +0 -418
- package/src/virtual.d.ts +0 -70
- package/src/vite-plugin.ts +0 -716
package/README.md
CHANGED
|
@@ -76,6 +76,75 @@ t('common.countdown', { seconds: 5 }); // "Ready in 5 seconds"
|
|
|
76
76
|
await setLocale('es');
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
## Virtual Modules
|
|
80
|
+
|
|
81
|
+
### `ez-i18n:runtime`
|
|
82
|
+
|
|
83
|
+
Core translation functions:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { t, locale, setLocale, initLocale } from 'ez-i18n:runtime';
|
|
87
|
+
|
|
88
|
+
t('key'); // Translate a key
|
|
89
|
+
t('key', { name: 'World' }); // With interpolation
|
|
90
|
+
locale; // Reactive store with current locale
|
|
91
|
+
await setLocale('es'); // Change locale (persists to cookie)
|
|
92
|
+
initLocale('en', data); // Initialize with translations
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `ez-i18n:config`
|
|
96
|
+
|
|
97
|
+
Access your i18n configuration:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import {
|
|
101
|
+
locales, // ['en', 'es', 'fr']
|
|
102
|
+
defaultLocale, // 'en'
|
|
103
|
+
cookieName, // 'ez-locale'
|
|
104
|
+
localeNames, // { en: 'English', es: 'Español', fr: 'Français' }
|
|
105
|
+
localeToBCP47, // { en: 'en-US', es: 'es-ES', fr: 'fr-FR' }
|
|
106
|
+
localeDirections, // { en: 'ltr', es: 'ltr', ar: 'rtl' }
|
|
107
|
+
} from 'ez-i18n:config';
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `ez-i18n:translations`
|
|
111
|
+
|
|
112
|
+
Dynamic translation loading:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { loadTranslations, translationLoaders } from 'ez-i18n:translations';
|
|
116
|
+
|
|
117
|
+
const data = await loadTranslations('es');
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Locale Utilities
|
|
121
|
+
|
|
122
|
+
The package includes a comprehensive locale database with 100+ languages:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import {
|
|
126
|
+
LOCALE_DATABASE,
|
|
127
|
+
getLocaleInfo,
|
|
128
|
+
buildLocaleNames,
|
|
129
|
+
buildLocaleToBCP47,
|
|
130
|
+
buildLocaleDirections,
|
|
131
|
+
} from '@zachhandley/ez-i18n';
|
|
132
|
+
import type { LocaleInfo } from '@zachhandley/ez-i18n';
|
|
133
|
+
|
|
134
|
+
// Get info for any locale
|
|
135
|
+
const info = getLocaleInfo('es');
|
|
136
|
+
// { name: 'Español', englishName: 'Spanish', bcp47: 'es-ES', dir: 'ltr' }
|
|
137
|
+
|
|
138
|
+
// Build mappings for your supported locales
|
|
139
|
+
const names = buildLocaleNames(['en', 'es', 'ar']);
|
|
140
|
+
// { en: 'English', es: 'Español', ar: 'العربية' }
|
|
141
|
+
|
|
142
|
+
const directions = buildLocaleDirections(['en', 'es', 'ar']);
|
|
143
|
+
// { en: 'ltr', es: 'ltr', ar: 'rtl' }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Includes native display names, English names, BCP47 codes, and text direction (LTR/RTL) for all major languages and regional variants.
|
|
147
|
+
|
|
79
148
|
## Framework Bindings
|
|
80
149
|
|
|
81
150
|
- React: `@zachhandley/ez-i18n-react`
|
package/package.json
CHANGED
package/src/index.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import type { AstroIntegration, HookParameters } from 'astro';
|
|
2
|
-
import type { EzI18nConfig } from './types';
|
|
3
|
-
import { vitePlugin, resolveConfig } from './vite-plugin';
|
|
4
|
-
|
|
5
|
-
export type { EzI18nConfig, TranslateFunction } from './types';
|
|
6
|
-
export { LOCALE_DATABASE, getLocaleInfo, buildLocaleNames, buildLocaleToBCP47, buildLocaleDirections } from './utils/locales';
|
|
7
|
-
export type { LocaleInfo } from './utils/locales';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* ez-i18n Astro integration
|
|
11
|
-
*
|
|
12
|
-
* Provides cookie-based i18n without URL prefixes.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // astro.config.ts
|
|
16
|
-
* import ezI18n from '@zachhandley/ez-i18n';
|
|
17
|
-
*
|
|
18
|
-
* export default defineConfig({
|
|
19
|
-
* integrations: [
|
|
20
|
-
* ezI18n({
|
|
21
|
-
* locales: ['en', 'es', 'fr'],
|
|
22
|
-
* defaultLocale: 'en',
|
|
23
|
-
* translations: {
|
|
24
|
-
* en: './src/i18n/en.json',
|
|
25
|
-
* es: './src/i18n/es.json',
|
|
26
|
-
* },
|
|
27
|
-
* }),
|
|
28
|
-
* ],
|
|
29
|
-
* });
|
|
30
|
-
*/
|
|
31
|
-
export default function ezI18n(config: EzI18nConfig): AstroIntegration {
|
|
32
|
-
const resolved = resolveConfig(config);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
name: 'ez-i18n',
|
|
36
|
-
hooks: {
|
|
37
|
-
'astro:config:setup': ({
|
|
38
|
-
updateConfig,
|
|
39
|
-
addMiddleware,
|
|
40
|
-
injectScript,
|
|
41
|
-
}: HookParameters<'astro:config:setup'>) => {
|
|
42
|
-
// Add Vite plugin for virtual modules
|
|
43
|
-
updateConfig({
|
|
44
|
-
vite: {
|
|
45
|
-
plugins: [vitePlugin(config)],
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Add locale detection middleware
|
|
50
|
-
addMiddleware({
|
|
51
|
-
entrypoint: '@zachhandley/ez-i18n/middleware',
|
|
52
|
-
order: 'pre',
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Inject hydration script to sync localStorage with cookie
|
|
56
|
-
// This prevents hydration mismatch when server and client disagree
|
|
57
|
-
const hydrationScript = `
|
|
58
|
-
(function() {
|
|
59
|
-
try {
|
|
60
|
-
var cookieName = ${JSON.stringify(resolved.cookieName)};
|
|
61
|
-
var stored = localStorage.getItem(cookieName);
|
|
62
|
-
var cookieMatch = document.cookie.match(new RegExp(cookieName + '=([^;]+)'));
|
|
63
|
-
var cookie = cookieMatch ? cookieMatch[1] : null;
|
|
64
|
-
if (cookie && stored !== cookie) {
|
|
65
|
-
localStorage.setItem(cookieName, cookie);
|
|
66
|
-
}
|
|
67
|
-
} catch (e) {}
|
|
68
|
-
})();
|
|
69
|
-
`;
|
|
70
|
-
injectScript('head-inline', hydrationScript);
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
'astro:config:done': ({
|
|
74
|
-
injectTypes,
|
|
75
|
-
}: HookParameters<'astro:config:done'>) => {
|
|
76
|
-
// Inject type declarations for virtual modules
|
|
77
|
-
injectTypes({
|
|
78
|
-
filename: 'virtual.d.ts',
|
|
79
|
-
content: `\
|
|
80
|
-
declare module 'ez-i18n:config' {
|
|
81
|
-
/** List of all supported locale codes */
|
|
82
|
-
export const locales: readonly string[];
|
|
83
|
-
/** Default locale when no preference is detected */
|
|
84
|
-
export const defaultLocale: string;
|
|
85
|
-
/** Cookie name used to store locale preference */
|
|
86
|
-
export const cookieName: string;
|
|
87
|
-
/** Display names for each locale (in native language) */
|
|
88
|
-
export const localeNames: Record<string, string>;
|
|
89
|
-
/** BCP47 language tags for each locale */
|
|
90
|
-
export const localeToBCP47: Record<string, string>;
|
|
91
|
-
/** Text direction for each locale ('ltr' or 'rtl') */
|
|
92
|
-
export const localeDirections: Record<string, 'ltr' | 'rtl'>;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
declare module 'ez-i18n:runtime' {
|
|
96
|
-
import type { ReadableAtom } from 'nanostores';
|
|
97
|
-
/** Reactive store containing the current locale */
|
|
98
|
-
export const locale: ReadableAtom<string>;
|
|
99
|
-
/**
|
|
100
|
-
* Translate a key to the current locale
|
|
101
|
-
* @param key - Dot-notation key (e.g., 'common.welcome')
|
|
102
|
-
* @param params - Optional interpolation params for {placeholder} syntax
|
|
103
|
-
*/
|
|
104
|
-
export function t(key: string, params?: Record<string, string | number>): string;
|
|
105
|
-
/**
|
|
106
|
-
* Set the current locale and persist to cookie/localStorage
|
|
107
|
-
* @param locale - Locale code to switch to
|
|
108
|
-
* @param cookieName - Optional custom cookie name
|
|
109
|
-
*/
|
|
110
|
-
export function setLocale(locale: string, cookieName?: string): Promise<void>;
|
|
111
|
-
/**
|
|
112
|
-
* Initialize the locale store with translations
|
|
113
|
-
* @param locale - Initial locale code
|
|
114
|
-
* @param translations - Optional initial translations object
|
|
115
|
-
*/
|
|
116
|
-
export function initLocale(locale: string, translations?: Record<string, unknown>): void;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
declare module 'ez-i18n:translations' {
|
|
120
|
-
/** Load translations for a specific locale */
|
|
121
|
-
export function loadTranslations(locale: string): Promise<Record<string, unknown>>;
|
|
122
|
-
/** Get the translation loader map from config */
|
|
123
|
-
export const translationLoaders: Record<string, () => Promise<{ default: Record<string, unknown> }>>;
|
|
124
|
-
}
|
|
125
|
-
`,
|
|
126
|
-
});
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
};
|
|
130
|
-
}
|
package/src/middleware.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { defineMiddleware } from 'astro:middleware';
|
|
2
|
-
import type { TranslateFunction } from './types';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create a server-side translation function for the given translations object
|
|
6
|
-
*/
|
|
7
|
-
function createT(translations: Record<string, unknown>): TranslateFunction {
|
|
8
|
-
return (key: string, params?: Record<string, string | number>): string => {
|
|
9
|
-
const keys = key.split('.');
|
|
10
|
-
let value: unknown = translations;
|
|
11
|
-
for (const k of keys) {
|
|
12
|
-
if (value == null || typeof value !== 'object') return key;
|
|
13
|
-
value = (value as Record<string, unknown>)[k];
|
|
14
|
-
}
|
|
15
|
-
if (typeof value !== 'string') return key;
|
|
16
|
-
if (!params) return value;
|
|
17
|
-
return value.replace(/\{(\w+)\}/g, (_, p) => String(params[p] ?? `{${p}}`));
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Locale detection middleware for ez-i18n
|
|
23
|
-
*
|
|
24
|
-
* Detection priority:
|
|
25
|
-
* 1. ?lang query parameter (allows explicit switching)
|
|
26
|
-
* 2. Cookie value
|
|
27
|
-
* 3. Accept-Language header
|
|
28
|
-
* 4. Default locale
|
|
29
|
-
*/
|
|
30
|
-
export const onRequest = defineMiddleware(async ({ cookies, request, locals }, next) => {
|
|
31
|
-
// Import config from virtual module (provided by vite-plugin)
|
|
32
|
-
const { locales, defaultLocale, cookieName } = await import('ez-i18n:config');
|
|
33
|
-
|
|
34
|
-
const url = new URL(request.url);
|
|
35
|
-
|
|
36
|
-
// Priority 1: Query parameter
|
|
37
|
-
const langParam = url.searchParams.get('lang');
|
|
38
|
-
|
|
39
|
-
// Priority 2: Cookie
|
|
40
|
-
const cookieValue = cookies.get(cookieName)?.value;
|
|
41
|
-
|
|
42
|
-
// Priority 3: Accept-Language header
|
|
43
|
-
const acceptLang = request.headers.get('accept-language');
|
|
44
|
-
const browserLang = acceptLang?.split(',')[0]?.split('-')[0];
|
|
45
|
-
|
|
46
|
-
// Determine locale with priority
|
|
47
|
-
let locale = defaultLocale;
|
|
48
|
-
|
|
49
|
-
if (langParam && locales.includes(langParam)) {
|
|
50
|
-
locale = langParam;
|
|
51
|
-
} else if (cookieValue && locales.includes(cookieValue)) {
|
|
52
|
-
locale = cookieValue;
|
|
53
|
-
} else if (browserLang && locales.includes(browserLang)) {
|
|
54
|
-
locale = browserLang;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Set locale on locals for use in pages
|
|
58
|
-
locals.locale = locale;
|
|
59
|
-
|
|
60
|
-
// Load translations for the current locale
|
|
61
|
-
try {
|
|
62
|
-
const { loadTranslations } = await import('ez-i18n:translations');
|
|
63
|
-
locals.translations = await loadTranslations(locale);
|
|
64
|
-
} catch {
|
|
65
|
-
// Fallback to empty translations if loader not configured
|
|
66
|
-
locals.translations = {};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Create server-side translation function
|
|
70
|
-
locals.t = createT(locals.translations);
|
|
71
|
-
|
|
72
|
-
// Update cookie if changed via query param
|
|
73
|
-
if (langParam && langParam !== cookieValue && locales.includes(langParam)) {
|
|
74
|
-
cookies.set(cookieName, locale, {
|
|
75
|
-
path: '/',
|
|
76
|
-
maxAge: 60 * 60 * 24 * 365, // 1 year
|
|
77
|
-
sameSite: 'lax',
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return next();
|
|
82
|
-
});
|
package/src/runtime/index.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime exports for ez-i18n
|
|
3
|
-
*
|
|
4
|
-
* This module is imported by the ez-i18n:runtime virtual module
|
|
5
|
-
* and can also be used directly in Vue components
|
|
6
|
-
*/
|
|
7
|
-
export {
|
|
8
|
-
effectiveLocale,
|
|
9
|
-
translations,
|
|
10
|
-
localePreference,
|
|
11
|
-
localeLoading,
|
|
12
|
-
initLocale,
|
|
13
|
-
setLocale,
|
|
14
|
-
setTranslations,
|
|
15
|
-
getLocale,
|
|
16
|
-
getTranslations,
|
|
17
|
-
} from './store';
|
|
18
|
-
|
|
19
|
-
export type { TranslationLoader } from './store';
|
package/src/runtime/store.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { atom, computed } from 'nanostores';
|
|
2
|
-
import { persistentAtom } from '@nanostores/persistent';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Server-provided locale (set during SSR/hydration)
|
|
6
|
-
* Takes precedence over client preference to prevent hydration mismatch
|
|
7
|
-
*/
|
|
8
|
-
const serverLocale = atom<string | null>(null);
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Client-side locale preference (persisted to localStorage)
|
|
12
|
-
*/
|
|
13
|
-
export const localePreference = persistentAtom<string>('ez-locale', 'en', {
|
|
14
|
-
encode: (value) => value,
|
|
15
|
-
decode: (value) => value,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Effective locale - uses server locale if set, otherwise client preference
|
|
20
|
-
*/
|
|
21
|
-
export const effectiveLocale = computed(
|
|
22
|
-
[serverLocale, localePreference],
|
|
23
|
-
(server, client) => server ?? client
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Current translations object (reactive)
|
|
28
|
-
*/
|
|
29
|
-
export const translations = atom<Record<string, unknown>>({});
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Whether locale is currently being changed
|
|
33
|
-
*/
|
|
34
|
-
export const localeLoading = atom<boolean>(false);
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Initialize locale from server-provided value
|
|
38
|
-
* Called during hydration to sync server and client state
|
|
39
|
-
*/
|
|
40
|
-
export function initLocale(locale: string, trans?: Record<string, unknown>): void {
|
|
41
|
-
serverLocale.set(locale);
|
|
42
|
-
localePreference.set(locale);
|
|
43
|
-
if (trans) {
|
|
44
|
-
translations.set(trans);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Set the translations object
|
|
50
|
-
*/
|
|
51
|
-
export function setTranslations(trans: Record<string, unknown>): void {
|
|
52
|
-
translations.set(trans);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Type for translation loader function */
|
|
56
|
-
export type TranslationLoader = () => Promise<{ default?: Record<string, unknown> } | Record<string, unknown>>;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Change locale and update cookie
|
|
60
|
-
* Optionally loads new translations dynamically
|
|
61
|
-
* @param locale - New locale code
|
|
62
|
-
* @param options - Options object or cookie name for backwards compatibility
|
|
63
|
-
*/
|
|
64
|
-
export async function setLocale(
|
|
65
|
-
locale: string,
|
|
66
|
-
options: string | {
|
|
67
|
-
cookieName?: string;
|
|
68
|
-
loadTranslations?: TranslationLoader;
|
|
69
|
-
} = {}
|
|
70
|
-
): Promise<void> {
|
|
71
|
-
// Handle backwards compatibility with string cookieName
|
|
72
|
-
const opts = typeof options === 'string'
|
|
73
|
-
? { cookieName: options }
|
|
74
|
-
: options;
|
|
75
|
-
const { cookieName = 'ez-locale', loadTranslations } = opts;
|
|
76
|
-
|
|
77
|
-
localeLoading.set(true);
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
// Load new translations if loader provided
|
|
81
|
-
if (loadTranslations) {
|
|
82
|
-
const mod = await loadTranslations();
|
|
83
|
-
const trans = 'default' in mod ? mod.default : mod;
|
|
84
|
-
translations.set(trans as Record<string, unknown>);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Update stores
|
|
88
|
-
localePreference.set(locale);
|
|
89
|
-
serverLocale.set(locale);
|
|
90
|
-
|
|
91
|
-
// Update cookie
|
|
92
|
-
if (typeof document !== 'undefined') {
|
|
93
|
-
document.cookie = `${cookieName}=${locale}; path=/; max-age=31536000; samesite=lax`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Dispatch event for components that need to react
|
|
97
|
-
if (typeof document !== 'undefined') {
|
|
98
|
-
document.dispatchEvent(
|
|
99
|
-
new CustomEvent('ez-i18n:locale-changed', {
|
|
100
|
-
detail: { locale },
|
|
101
|
-
bubbles: true,
|
|
102
|
-
})
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
} finally {
|
|
106
|
-
localeLoading.set(false);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get current locale value (non-reactive)
|
|
112
|
-
*/
|
|
113
|
-
export function getLocale(): string {
|
|
114
|
-
return effectiveLocale.get();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get current translations (non-reactive)
|
|
119
|
-
*/
|
|
120
|
-
export function getTranslations(): Record<string, unknown> {
|
|
121
|
-
return translations.get();
|
|
122
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Translation path for a single locale:
|
|
3
|
-
* - Single file: `./src/i18n/en.json`
|
|
4
|
-
* - Folder: `./src/i18n/en/` (auto-discover all JSONs inside)
|
|
5
|
-
* - Glob: `./src/i18n/en/**.json` (recursive)
|
|
6
|
-
* - Array: `['./common.json', './auth.json']`
|
|
7
|
-
*/
|
|
8
|
-
export type LocaleTranslationPath = string | string[];
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Translation config can be:
|
|
12
|
-
* - A base directory string (auto-discovers locale folders): './public/i18n/'
|
|
13
|
-
* - Per-locale mapping: { en: './src/i18n/en/', es: './src/i18n/es.json' }
|
|
14
|
-
*/
|
|
15
|
-
export type TranslationsConfig = string | Record<string, LocaleTranslationPath>;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Configuration for ez-i18n Astro integration
|
|
19
|
-
*/
|
|
20
|
-
export interface EzI18nConfig {
|
|
21
|
-
/**
|
|
22
|
-
* List of supported locale codes (e.g., ['en', 'es', 'fr'])
|
|
23
|
-
* Optional if using directory-based auto-discovery - locales will be
|
|
24
|
-
* detected from folder names in the translations directory.
|
|
25
|
-
*/
|
|
26
|
-
locales?: string[];
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Default locale to use when no preference is detected.
|
|
30
|
-
* Required - this tells us what to fall back to.
|
|
31
|
-
*/
|
|
32
|
-
defaultLocale: string;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Cookie name for storing locale preference
|
|
36
|
-
* @default 'ez-locale'
|
|
37
|
-
*/
|
|
38
|
-
cookieName?: string;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Translation file paths configuration.
|
|
42
|
-
* Paths are relative to your project root.
|
|
43
|
-
*
|
|
44
|
-
* Can be:
|
|
45
|
-
* - A base directory (auto-discovers locale folders):
|
|
46
|
-
* translations: './public/i18n/'
|
|
47
|
-
* → Scans for en/, es/, fr/ folders and their JSON files
|
|
48
|
-
* → Auto-populates `locales` from discovered folders
|
|
49
|
-
*
|
|
50
|
-
* - Per-locale mapping with flexible path types:
|
|
51
|
-
* translations: {
|
|
52
|
-
* en: './src/i18n/en.json', // single file
|
|
53
|
-
* es: './src/i18n/es/', // folder (all JSONs)
|
|
54
|
-
* fr: './src/i18n/fr/**.json', // glob pattern
|
|
55
|
-
* de: ['./common.json', './auth.json'] // array of files
|
|
56
|
-
* }
|
|
57
|
-
*
|
|
58
|
-
* If not specified, auto-discovers from ./public/i18n/
|
|
59
|
-
*/
|
|
60
|
-
translations?: TranslationsConfig;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Derive namespace from file path relative to locale folder.
|
|
64
|
-
*
|
|
65
|
-
* When enabled:
|
|
66
|
-
* - `en/auth/login.json` with `{ "title": "..." }` → `$t('auth.login.title')`
|
|
67
|
-
* - `en/common.json` with `{ "actions": {...} }` → `$t('common.actions.save')`
|
|
68
|
-
*
|
|
69
|
-
* The file path (minus locale folder and .json extension) becomes the key prefix.
|
|
70
|
-
*
|
|
71
|
-
* @default true when using folder-based translations config
|
|
72
|
-
*/
|
|
73
|
-
pathBasedNamespacing?: boolean;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Resolved config with defaults applied.
|
|
78
|
-
* After resolution:
|
|
79
|
-
* - locales is always populated (from config or auto-discovered)
|
|
80
|
-
* - translations is normalized to arrays of absolute file paths
|
|
81
|
-
*/
|
|
82
|
-
export interface ResolvedEzI18nConfig {
|
|
83
|
-
locales: string[];
|
|
84
|
-
defaultLocale: string;
|
|
85
|
-
cookieName: string;
|
|
86
|
-
/** Normalized: locale → array of resolved absolute file paths */
|
|
87
|
-
translations: Record<string, string[]>;
|
|
88
|
-
/** Whether to derive namespace from file path */
|
|
89
|
-
pathBasedNamespacing: boolean;
|
|
90
|
-
/** Base directory for each locale (used for namespace calculation) */
|
|
91
|
-
localeBaseDirs: Record<string, string>;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Cache file structure (.ez-i18n.json)
|
|
96
|
-
* Used to speed up subsequent builds by caching discovered translations
|
|
97
|
-
*/
|
|
98
|
-
export interface TranslationCache {
|
|
99
|
-
version: number;
|
|
100
|
-
/** Discovered locale → file paths mapping */
|
|
101
|
-
discovered: Record<string, string[]>;
|
|
102
|
-
/** ISO timestamp of last scan */
|
|
103
|
-
lastScan: string;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Translation function type
|
|
108
|
-
*/
|
|
109
|
-
export type TranslateFunction = (key: string, params?: Record<string, string | number>) => string;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Augment Astro's locals type
|
|
113
|
-
*/
|
|
114
|
-
declare global {
|
|
115
|
-
namespace App {
|
|
116
|
-
interface Locals {
|
|
117
|
-
/** Current locale code */
|
|
118
|
-
locale: string;
|
|
119
|
-
/** Loaded translations for the current locale */
|
|
120
|
-
translations: Record<string, unknown>;
|
|
121
|
-
/** Server-side translation function */
|
|
122
|
-
t: TranslateFunction;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
package/src/utils/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
detectPathType,
|
|
3
|
-
resolveTranslationPaths,
|
|
4
|
-
autoDiscoverTranslations,
|
|
5
|
-
resolveTranslationsConfig,
|
|
6
|
-
deepMerge,
|
|
7
|
-
loadCache,
|
|
8
|
-
saveCache,
|
|
9
|
-
isCacheValid,
|
|
10
|
-
toRelativeImport,
|
|
11
|
-
toGlobPattern,
|
|
12
|
-
getNamespaceFromPath,
|
|
13
|
-
wrapWithNamespace,
|
|
14
|
-
generateNamespaceWrapperCode,
|
|
15
|
-
type PathType,
|
|
16
|
-
} from './translations';
|