solid-translate 1.1.0 → 1.2.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/README.md +161 -6
- package/dist/chunk-2BKJUY37.js +82 -0
- package/dist/cli.js +351 -163
- package/dist/index.d.ts +33 -5
- package/dist/index.js +65 -9
- package/dist/index.js.map +1 -1
- package/dist/translate-M737VQHG.js +10 -0
- package/dist/vite.js +262 -113
- package/dist/vite.js.map +1 -1
- package/package.json +6 -2
- package/virtual.d.ts +45 -0
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,21 @@ interface SolidTranslatePluginConfig {
|
|
|
31
31
|
type TranslationDictionary = Record<string, string>;
|
|
32
32
|
/** All translations keyed by locale code */
|
|
33
33
|
type Translations = Record<string, TranslationDictionary>;
|
|
34
|
+
/**
|
|
35
|
+
* Lazy translation manifest, as exported by `virtual:solid-translate/lazy`.
|
|
36
|
+
* Each loader dynamically imports one locale's dictionary so it becomes its
|
|
37
|
+
* own chunk instead of being inlined into the main bundle.
|
|
38
|
+
*/
|
|
39
|
+
interface LazyTranslations {
|
|
40
|
+
/** Source locale code */
|
|
41
|
+
sourceLocale: string;
|
|
42
|
+
/** All available locale codes (source + targets) */
|
|
43
|
+
locales: string[];
|
|
44
|
+
/** Per-locale dictionary loaders (dynamic imports) */
|
|
45
|
+
loaders: Record<string, () => Promise<TranslationDictionary>>;
|
|
46
|
+
}
|
|
47
|
+
/** Either an eager translations record or a lazy manifest */
|
|
48
|
+
type TranslationsInput = Translations | LazyTranslations;
|
|
34
49
|
|
|
35
50
|
interface TranslationContextValue {
|
|
36
51
|
/** Current locale as a reactive signal */
|
|
@@ -43,8 +58,8 @@ interface TranslationContextValue {
|
|
|
43
58
|
sourceLocale: string;
|
|
44
59
|
/** All available locale codes (reactive) */
|
|
45
60
|
availableLocales: () => string[];
|
|
46
|
-
/** Raw translations object */
|
|
47
|
-
translations:
|
|
61
|
+
/** Raw translations object (eager record or lazy manifest) */
|
|
62
|
+
translations: TranslationsInput;
|
|
48
63
|
}
|
|
49
64
|
|
|
50
65
|
interface VarProps {
|
|
@@ -193,8 +208,21 @@ interface TranslationProviderProps {
|
|
|
193
208
|
locale?: string;
|
|
194
209
|
/** Source locale code (default: "en") */
|
|
195
210
|
sourceLocale?: string;
|
|
196
|
-
/**
|
|
197
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Translation dictionaries keyed by locale (from `virtual:solid-translate`),
|
|
213
|
+
* or a lazy manifest (from `virtual:solid-translate/lazy`) whose per-locale
|
|
214
|
+
* dictionaries are loaded on demand via dynamic import.
|
|
215
|
+
*/
|
|
216
|
+
translations: TranslationsInput;
|
|
217
|
+
/**
|
|
218
|
+
* Persist the active locale to `localStorage` (default: false).
|
|
219
|
+
* When enabled, the initial locale is read from storage (if still valid)
|
|
220
|
+
* before falling back to browser detection, and `setLocale` writes through.
|
|
221
|
+
* Pass `{ key: "..." }` to customize the storage key.
|
|
222
|
+
*/
|
|
223
|
+
persistLocale?: boolean | {
|
|
224
|
+
key?: string;
|
|
225
|
+
};
|
|
198
226
|
children: JSX.Element;
|
|
199
227
|
}
|
|
200
228
|
declare function TranslationProvider(props: TranslationProviderProps): JSX.Element;
|
|
@@ -236,4 +264,4 @@ interface TProps {
|
|
|
236
264
|
*/
|
|
237
265
|
declare function T(props: TProps): JSX.Element;
|
|
238
266
|
|
|
239
|
-
export { Currency, type CurrencyProps, DateTime, type DateTimeProps, LocaleSelector, type LocaleSelectorProps, Num, type NumProps, Plural, type PluralProps, type SolidTranslatePluginConfig, T, type TProps, type TranslationContextValue, type TranslationDictionary, TranslationProvider, type TranslationProviderProps, type Translations, Var, type VarProps, detectLocale, msg, useLocale, useTranslation };
|
|
267
|
+
export { Currency, type CurrencyProps, DateTime, type DateTimeProps, type LazyTranslations, LocaleSelector, type LocaleSelectorProps, Num, type NumProps, Plural, type PluralProps, type SolidTranslatePluginConfig, T, type TProps, type TranslationContextValue, type TranslationDictionary, TranslationProvider, type TranslationProviderProps, type Translations, type TranslationsInput, Var, type VarProps, detectLocale, msg, useLocale, useTranslation };
|
package/dist/index.js
CHANGED
|
@@ -18,13 +18,19 @@ function detectLocale(availableLocales) {
|
|
|
18
18
|
if (!availableLocales || availableLocales.length === 0) {
|
|
19
19
|
return normalizeLocale(browserLocales[0] || "en");
|
|
20
20
|
}
|
|
21
|
+
const canonical = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const al of availableLocales) {
|
|
23
|
+
const normalized = normalizeLocale(al);
|
|
24
|
+
if (!canonical.has(normalized)) canonical.set(normalized, al);
|
|
25
|
+
}
|
|
21
26
|
for (const bl of browserLocales) {
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
27
|
+
const match = canonical.get(normalizeLocale(bl));
|
|
28
|
+
if (match) return match;
|
|
24
29
|
}
|
|
25
30
|
for (const bl of browserLocales) {
|
|
26
|
-
const lang = bl.split("-")[0]
|
|
27
|
-
|
|
31
|
+
const lang = normalizeLocale(bl).split("-")[0];
|
|
32
|
+
const match = canonical.get(lang);
|
|
33
|
+
if (match) return match;
|
|
28
34
|
}
|
|
29
35
|
return availableLocales[0] || "en";
|
|
30
36
|
}
|
|
@@ -115,15 +121,65 @@ function msg(text, _params) {
|
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
// src/index.ts
|
|
124
|
+
var DEFAULT_PERSIST_KEY = "solid-translate:locale";
|
|
125
|
+
function isLazyTranslations(input) {
|
|
126
|
+
return typeof input === "object" && input !== null && Array.isArray(input.locales) && typeof input.loaders === "object" && input.loaders !== null;
|
|
127
|
+
}
|
|
128
|
+
function readPersistedLocale(key) {
|
|
129
|
+
try {
|
|
130
|
+
if (typeof localStorage === "undefined") return void 0;
|
|
131
|
+
return localStorage.getItem(key) ?? void 0;
|
|
132
|
+
} catch {
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function writePersistedLocale(key, locale) {
|
|
137
|
+
try {
|
|
138
|
+
if (typeof localStorage === "undefined") return;
|
|
139
|
+
localStorage.setItem(key, locale);
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
}
|
|
118
143
|
function TranslationProvider(props) {
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
144
|
+
const lazy = isLazyTranslations(props.translations) ? props.translations : void 0;
|
|
145
|
+
const sourceLocale = props.sourceLocale || lazy?.sourceLocale || "en";
|
|
146
|
+
const availableLocales = createMemo2(
|
|
147
|
+
() => lazy ? lazy.locales : Object.keys(props.translations)
|
|
148
|
+
);
|
|
149
|
+
const persistKey = props.persistLocale ? (typeof props.persistLocale === "object" ? props.persistLocale.key : void 0) || DEFAULT_PERSIST_KEY : void 0;
|
|
150
|
+
const persisted = persistKey ? readPersistedLocale(persistKey) : void 0;
|
|
151
|
+
const persistedValid = persisted !== void 0 && (persisted === sourceLocale || availableLocales().includes(persisted));
|
|
152
|
+
const initialLocale = props.locale || (persistedValid ? persisted : void 0) || detectLocale(availableLocales()) || sourceLocale;
|
|
153
|
+
const [locale, setLocaleSignal] = createSignal(initialLocale);
|
|
154
|
+
const [loadedDicts, setLoadedDicts] = createSignal({});
|
|
155
|
+
const pendingLoads = /* @__PURE__ */ new Set();
|
|
156
|
+
const loadLocale = (target) => {
|
|
157
|
+
if (!lazy) return;
|
|
158
|
+
const loader = lazy.loaders[target];
|
|
159
|
+
if (!loader) return;
|
|
160
|
+
if (target in loadedDicts() || pendingLoads.has(target)) return;
|
|
161
|
+
pendingLoads.add(target);
|
|
162
|
+
loader().then((dict) => {
|
|
163
|
+
setLoadedDicts((prev) => ({ ...prev, [target]: dict }));
|
|
164
|
+
}).catch((err) => {
|
|
165
|
+
console.warn(
|
|
166
|
+
`[solid-translate] Failed to load locale "${target}":`,
|
|
167
|
+
err
|
|
168
|
+
);
|
|
169
|
+
}).finally(() => {
|
|
170
|
+
pendingLoads.delete(target);
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
const setLocale = (next) => {
|
|
174
|
+
loadLocale(next);
|
|
175
|
+
setLocaleSignal(next);
|
|
176
|
+
if (persistKey) writePersistedLocale(persistKey, next);
|
|
177
|
+
};
|
|
178
|
+
loadLocale(initialLocale);
|
|
123
179
|
const t = (key, params) => {
|
|
124
180
|
const cur = locale();
|
|
125
181
|
let text = key;
|
|
126
|
-
const dict = props.translations[cur];
|
|
182
|
+
const dict = lazy ? loadedDicts()[cur] : props.translations[cur];
|
|
127
183
|
if (dict && key in dict) {
|
|
128
184
|
text = dict[key];
|
|
129
185
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/locale-detect.ts","../src/components.tsx","../src/msg.ts"],"sourcesContent":["import {\n createComponent,\n useContext,\n createSignal,\n createMemo,\n children as resolveChildren,\n type JSX,\n} from \"solid-js\";\nimport {\n TranslationContext,\n type TranslationContextValue,\n} from \"./context.js\";\nimport { detectLocale } from \"./locale-detect.js\";\nimport type { TranslationDictionary, Translations } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport type { TranslationContextValue } from \"./context.js\";\nexport type { TranslationDictionary, Translations } from \"./types.js\";\nexport type { SolidTranslatePluginConfig } from \"./types.js\";\nexport { Var, Num, Currency, DateTime, Plural, LocaleSelector } from \"./components.js\";\nexport type {\n VarProps,\n NumProps,\n CurrencyProps,\n DateTimeProps,\n PluralProps,\n LocaleSelectorProps,\n} from \"./components.js\";\nexport { msg } from \"./msg.js\";\nexport { detectLocale } from \"./locale-detect.js\";\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport interface TranslationProviderProps {\n /**\n * Initial locale. If omitted, auto-detects from the browser's\n * `navigator.languages` header, falling back to `sourceLocale`.\n */\n locale?: string;\n /** Source locale code (default: \"en\") */\n sourceLocale?: string;\n /** Translation dictionaries keyed by locale */\n translations: Translations;\n children: JSX.Element;\n}\n\nexport function TranslationProvider(props: TranslationProviderProps) {\n const sourceLocale = props.sourceLocale || \"en\";\n const availableLocales = createMemo(() => Object.keys(props.translations));\n\n // Auto-detect locale from browser if not explicitly provided\n const initialLocale =\n props.locale || detectLocale(availableLocales()) || sourceLocale;\n const [locale, setLocale] = createSignal(initialLocale);\n\n const t = (\n key: string,\n params?: Record<string, string | number>,\n ): string => {\n const cur = locale();\n let text = key;\n\n // Look up in translation dictionary (works for both source and target locales)\n const dict = props.translations[cur];\n if (dict && key in dict) {\n text = dict[key]!;\n }\n\n // Interpolate {{variable}} and {variable} placeholders\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n text = text.replace(\n new RegExp(`\\\\{\\\\{${k}\\\\}\\\\}|\\\\{${k}\\\\}`, \"g\"),\n String(v),\n );\n }\n }\n\n return text;\n };\n\n const value: TranslationContextValue = {\n locale,\n setLocale,\n t,\n sourceLocale,\n availableLocales,\n translations: props.translations,\n };\n\n return createComponent(TranslationContext.Provider, {\n value,\n get children() {\n return props.children;\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Hooks\n// ---------------------------------------------------------------------------\n\n/** Access the full translation context. Must be inside a TranslationProvider. */\nexport function useTranslation(): TranslationContextValue {\n const ctx = useContext(TranslationContext);\n if (!ctx) {\n throw new Error(\n \"useTranslation() must be used within a <TranslationProvider>\",\n );\n }\n return ctx;\n}\n\n/** Access just the current locale and setter. */\nexport function useLocale(): {\n locale: () => string;\n setLocale: (locale: string) => void;\n sourceLocale: string;\n availableLocales: () => string[];\n} {\n const ctx = useTranslation();\n return {\n locale: ctx.locale,\n setLocale: ctx.setLocale,\n sourceLocale: ctx.sourceLocale,\n availableLocales: ctx.availableLocales,\n };\n}\n\n// ---------------------------------------------------------------------------\n// <T> Component\n// ---------------------------------------------------------------------------\n\nexport interface TProps {\n /** Explicit translation key. If omitted, children text is used as the key. */\n id?: string;\n /** Interpolation parameters */\n params?: Record<string, string | number>;\n /**\n * AI context hint — tells the AI translator about the meaning of this text.\n * Only used at build time for disambiguation; has no runtime effect.\n *\n * ```tsx\n * <T context=\"Button to save a document, not save money\">Save</T>\n * ```\n */\n context?: string;\n /** Source text / JSX content */\n children?: JSX.Element;\n}\n\n/**\n * Translatable content component.\n *\n * ```tsx\n * <T>Hello world</T>\n * <T id=\"greeting\" params={{ name: \"Alice\" }}>Hello {{name}}</T>\n * <T context=\"the physical bank\">Bank</T>\n * <T>Welcome <Var>{userName()}</Var>, you have <Num>{count()}</Num> items</T>\n * ```\n */\nexport function T(props: TProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n const resolved = resolveChildren(() => props.children);\n\n return createMemo(() => {\n const kids = resolved.toArray();\n\n // No context — just render children\n if (!ctx) return kids.length === 1 ? kids[0] : kids;\n\n // Simple case: single text child\n if (kids.length === 1 && typeof kids[0] === \"string\") {\n const key = props.id || (kids[0] as string);\n return ctx.t(key, props.params);\n }\n\n // Explicit id with non-text children — translate via id\n if (props.id) {\n const translated = ctx.t(props.id, props.params);\n\n // If translation is just text (no slot placeholders), return it\n if (!/{(\\d+)}/.test(translated)) return translated;\n\n // Collect non-text children (Var, Num, etc.) as ordered slots\n const slots: JSX.Element[] = [];\n for (const kid of kids) {\n if (typeof kid !== \"string\" && typeof kid !== \"number\") {\n slots.push(kid as JSX.Element);\n }\n }\n\n return interpolateSlots(translated, slots);\n }\n\n // Mixed children without explicit id — build a template key\n const slots: JSX.Element[] = [];\n let template = \"\";\n for (const kid of kids) {\n if (typeof kid === \"string\") {\n template += kid;\n } else if (typeof kid === \"number\") {\n template += String(kid);\n } else {\n template += `{${slots.length}}`;\n slots.push(kid as JSX.Element);\n }\n }\n\n const translated = ctx.t(template, props.params);\n if (slots.length === 0) return translated;\n return interpolateSlots(translated, slots);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Split a translated string by `{0}`, `{1}`, etc. and interleave with slots */\nfunction interpolateSlots(\n text: string,\n slots: JSX.Element[],\n): (string | JSX.Element)[] {\n const parts = text.split(/\\{(\\d+)\\}/);\n const result: (string | JSX.Element)[] = [];\n for (let i = 0; i < parts.length; i++) {\n if (i % 2 === 0) {\n if (parts[i]) result.push(parts[i]!);\n } else {\n const idx = parseInt(parts[i]!, 10);\n if (slots[idx] !== undefined) result.push(slots[idx]!);\n }\n }\n return result;\n}\n","import { createContext } from \"solid-js\";\nimport type { Translations } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Context value type\n// ---------------------------------------------------------------------------\n\nexport interface TranslationContextValue {\n /** Current locale as a reactive signal */\n locale: () => string;\n /** Switch to a different locale */\n setLocale: (locale: string) => void;\n /** Translate a key with optional interpolation params */\n t: (key: string, params?: Record<string, string | number>) => string;\n /** The source locale code */\n sourceLocale: string;\n /** All available locale codes (reactive) */\n availableLocales: () => string[];\n /** Raw translations object */\n translations: Translations;\n}\n\n// ---------------------------------------------------------------------------\n// Shared context instance\n// ---------------------------------------------------------------------------\n\nexport const TranslationContext = createContext<TranslationContextValue>();\n","/**\n * Detect the user's preferred locale from browser settings.\n *\n * Checks `navigator.languages` (and falls back to `navigator.language`)\n * then matches against the list of available locales. Tries exact match\n * first, then language-only match (e.g. \"en-US\" → \"en\").\n */\nexport function detectLocale(availableLocales?: string[]): string {\n if (typeof navigator === \"undefined\") return \"en\";\n\n const browserLocales = navigator.languages\n ? [...navigator.languages]\n : [navigator.language || \"en\"];\n\n if (!availableLocales || availableLocales.length === 0) {\n return normalizeLocale(browserLocales[0] || \"en\");\n }\n\n // Exact match\n for (const bl of browserLocales) {\n const normalized = normalizeLocale(bl);\n if (availableLocales.includes(normalized)) return normalized;\n }\n\n // Language-only match (e.g. \"en-US\" → \"en\")\n for (const bl of browserLocales) {\n const lang = bl.split(\"-\")[0]!.toLowerCase();\n if (availableLocales.includes(lang)) return lang;\n }\n\n return availableLocales[0] || \"en\";\n}\n\nfunction normalizeLocale(locale: string): string {\n return locale.toLowerCase().replace(\"_\", \"-\");\n}\n","import { useContext, type JSX, For, createMemo } from \"solid-js\";\nimport { TranslationContext } from \"./context.js\";\n\n// ---------------------------------------------------------------------------\n// <Var> — protect dynamic content from translation\n// ---------------------------------------------------------------------------\n\nexport interface VarProps {\n /** Optional name for the variable (used as placeholder in templates) */\n name?: string;\n children: JSX.Element;\n}\n\n/**\n * Marks content as untranslatable. When used inside `<T>`, the content\n * is preserved as-is while surrounding text is translated.\n *\n * ```tsx\n * <T>Hello <Var>{userName()}</Var>, welcome!</T>\n * ```\n */\nexport function Var(props: VarProps): JSX.Element {\n return (() => props.children) as unknown as JSX.Element;\n}\n\n// Mark Var for identification by T component\n(Var as any).__st_var = true;\n\n// ---------------------------------------------------------------------------\n// <Num> — locale-aware number formatting\n// ---------------------------------------------------------------------------\n\nexport interface NumProps {\n /** The number to format */\n children: number;\n /** Intl.NumberFormat options */\n options?: Intl.NumberFormatOptions;\n}\n\n/**\n * Formats a number according to the current locale using `Intl.NumberFormat`.\n *\n * ```tsx\n * <Num>{1000000}</Num> // \"1,000,000\" in en, \"1.000.000\" in de\n * <Num options={{ style: \"percent\" }}>{0.42}</Num> // \"42%\"\n * ```\n */\nexport function Num(props: NumProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n return new Intl.NumberFormat(locale, props.options).format(props.children);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <Currency> — locale-aware currency formatting\n// ---------------------------------------------------------------------------\n\nexport interface CurrencyProps {\n /** The numeric value */\n children: number;\n /** ISO 4217 currency code (e.g. \"USD\", \"EUR\") */\n currency: string;\n /** Additional Intl.NumberFormat options */\n options?: Intl.NumberFormatOptions;\n}\n\n/**\n * Formats a number as currency according to the current locale.\n *\n * ```tsx\n * <Currency currency=\"USD\">{29.99}</Currency> // \"$29.99\" in en-US\n * <Currency currency=\"EUR\">{29.99}</Currency> // \"29,99 €\" in de\n * ```\n */\nexport function Currency(props: CurrencyProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: props.currency,\n ...props.options,\n }).format(props.children);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <DateTime> — locale-aware date/time formatting\n// ---------------------------------------------------------------------------\n\nexport interface DateTimeProps {\n /** The date to format (Date object, timestamp, or ISO string) */\n children: Date | number | string;\n /** Intl.DateTimeFormat options */\n options?: Intl.DateTimeFormatOptions;\n}\n\n/**\n * Formats a date/time according to the current locale using `Intl.DateTimeFormat`.\n *\n * ```tsx\n * <DateTime>{new Date()}</DateTime>\n * <DateTime options={{ dateStyle: \"long\" }}>{new Date()}</DateTime>\n * ```\n */\nexport function DateTime(props: DateTimeProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n const date =\n props.children instanceof Date\n ? props.children\n : new Date(props.children);\n return new Intl.DateTimeFormat(locale, props.options).format(date);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <Plural> — CLDR plural rules\n// ---------------------------------------------------------------------------\n\nexport interface PluralProps {\n /** The count value to determine which plural form to use */\n n: number;\n /** Form for zero items */\n zero?: JSX.Element;\n /** Form for exactly one item */\n one?: JSX.Element;\n /** Form for exactly two items */\n two?: JSX.Element;\n /** Form for \"few\" items (language-dependent) */\n few?: JSX.Element;\n /** Form for \"many\" items (language-dependent) */\n many?: JSX.Element;\n /** Default/fallback form */\n other: JSX.Element;\n}\n\n/**\n * Renders the appropriate plural form based on CLDR plural rules for the current locale.\n *\n * ```tsx\n * <Plural n={count()}\n * zero=\"No items\"\n * one=\"1 item\"\n * other={`${count()} items`}\n * />\n * ```\n */\nexport function Plural(props: PluralProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n const rules = new Intl.PluralRules(locale);\n const category = rules.select(props.n);\n\n const forms: Record<string, JSX.Element | undefined> = {\n zero: props.zero,\n one: props.one,\n two: props.two,\n few: props.few,\n many: props.many,\n other: props.other,\n };\n\n return forms[category] ?? props.other;\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <LocaleSelector> — drop-in locale picker\n// ---------------------------------------------------------------------------\n\nexport interface LocaleSelectorProps {\n /** Override which locales to show (defaults to all available) */\n locales?: string[];\n /** Map locale codes to display names, e.g. { en: \"English\", es: \"Español\" } */\n labels?: Record<string, string>;\n /** Additional CSS class */\n class?: string;\n}\n\n/**\n * A ready-to-use locale selector dropdown.\n *\n * ```tsx\n * <LocaleSelector labels={{ en: \"English\", es: \"Español\", fr: \"Français\" }} />\n * ```\n */\nexport function LocaleSelector(props: LocaleSelectorProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n if (!ctx) {\n throw new Error(\n \"<LocaleSelector> must be used within a <TranslationProvider>\",\n );\n }\n\n const locales = createMemo(() => props.locales || ctx.availableLocales());\n\n const displayName = (code: string): string => {\n if (props.labels?.[code]) return props.labels[code]!;\n try {\n const dn = new Intl.DisplayNames([code], { type: \"language\" });\n return dn.of(code) || code;\n } catch {\n return code;\n }\n };\n\n return (\n <select\n class={props.class}\n value={ctx.locale()}\n onChange={(e) => ctx.setLocale(e.currentTarget.value)}\n >\n <For each={locales()}>\n {(code) => <option value={code}>{displayName(code)}</option>}\n </For>\n </select>\n ) as JSX.Element;\n}\n","/**\n * Mark a string for translation extraction.\n *\n * At build time, the Vite plugin and CLI scan for `msg()` calls and add\n * the strings to the source locale file for AI translation.\n *\n * At runtime, `msg()` is a no-op — it returns the source text as-is.\n * Use `t()` from `useTranslation()` for runtime translation.\n *\n * ```ts\n * // Marks \"Save changes\" for extraction\n * const label = msg(\"Save changes\");\n *\n * // With interpolation template\n * const greeting = msg(\"Hello {{name}}\", { name: \"World\" });\n *\n * // In a component, translate at runtime:\n * const { t } = useTranslation();\n * <button>{t(label)}</button>\n * ```\n */\nexport function msg(\n text: string,\n _params?: Record<string, string | number>,\n): string {\n return text;\n}\n"],"mappings":"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","names":["useContext","createMemo","createMemo","useContext","translated","slots"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/locale-detect.ts","../src/components.tsx","../src/msg.ts"],"sourcesContent":["import {\n createComponent,\n useContext,\n createSignal,\n createMemo,\n children as resolveChildren,\n type JSX,\n} from \"solid-js\";\nimport {\n TranslationContext,\n type TranslationContextValue,\n} from \"./context.js\";\nimport { detectLocale } from \"./locale-detect.js\";\nimport type {\n LazyTranslations,\n TranslationDictionary,\n Translations,\n TranslationsInput,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Re-exports\n// ---------------------------------------------------------------------------\n\nexport type { TranslationContextValue } from \"./context.js\";\nexport type {\n LazyTranslations,\n TranslationDictionary,\n Translations,\n TranslationsInput,\n} from \"./types.js\";\nexport type { SolidTranslatePluginConfig } from \"./types.js\";\nexport { Var, Num, Currency, DateTime, Plural, LocaleSelector } from \"./components.js\";\nexport type {\n VarProps,\n NumProps,\n CurrencyProps,\n DateTimeProps,\n PluralProps,\n LocaleSelectorProps,\n} from \"./components.js\";\nexport { msg } from \"./msg.js\";\nexport { detectLocale } from \"./locale-detect.js\";\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport interface TranslationProviderProps {\n /**\n * Initial locale. If omitted, auto-detects from the browser's\n * `navigator.languages` header, falling back to `sourceLocale`.\n */\n locale?: string;\n /** Source locale code (default: \"en\") */\n sourceLocale?: string;\n /**\n * Translation dictionaries keyed by locale (from `virtual:solid-translate`),\n * or a lazy manifest (from `virtual:solid-translate/lazy`) whose per-locale\n * dictionaries are loaded on demand via dynamic import.\n */\n translations: TranslationsInput;\n /**\n * Persist the active locale to `localStorage` (default: false).\n * When enabled, the initial locale is read from storage (if still valid)\n * before falling back to browser detection, and `setLocale` writes through.\n * Pass `{ key: \"...\" }` to customize the storage key.\n */\n persistLocale?: boolean | { key?: string };\n children: JSX.Element;\n}\n\nconst DEFAULT_PERSIST_KEY = \"solid-translate:locale\";\n\nfunction isLazyTranslations(\n input: TranslationsInput,\n): input is LazyTranslations {\n return (\n typeof input === \"object\" &&\n input !== null &&\n Array.isArray((input as LazyTranslations).locales) &&\n typeof (input as LazyTranslations).loaders === \"object\" &&\n (input as LazyTranslations).loaders !== null\n );\n}\n\nfunction readPersistedLocale(key: string): string | undefined {\n try {\n if (typeof localStorage === \"undefined\") return undefined;\n return localStorage.getItem(key) ?? undefined;\n } catch {\n // SSR / storage disabled\n return undefined;\n }\n}\n\nfunction writePersistedLocale(key: string, locale: string): void {\n try {\n if (typeof localStorage === \"undefined\") return;\n localStorage.setItem(key, locale);\n } catch {\n // SSR / storage disabled / quota exceeded — ignore\n }\n}\n\nexport function TranslationProvider(props: TranslationProviderProps) {\n const lazy = isLazyTranslations(props.translations)\n ? props.translations\n : undefined;\n const sourceLocale = props.sourceLocale || lazy?.sourceLocale || \"en\";\n const availableLocales = createMemo(() =>\n lazy ? lazy.locales : Object.keys(props.translations),\n );\n\n const persistKey = props.persistLocale\n ? (typeof props.persistLocale === \"object\"\n ? props.persistLocale.key\n : undefined) || DEFAULT_PERSIST_KEY\n : undefined;\n\n // Initial locale: explicit prop > persisted value (if valid) > detection\n const persisted = persistKey ? readPersistedLocale(persistKey) : undefined;\n const persistedValid =\n persisted !== undefined &&\n (persisted === sourceLocale || availableLocales().includes(persisted));\n const initialLocale =\n props.locale ||\n (persistedValid ? persisted : undefined) ||\n detectLocale(availableLocales()) ||\n sourceLocale;\n const [locale, setLocaleSignal] = createSignal(initialLocale);\n\n // Lazily loaded dictionaries, keyed by locale (lazy manifest mode only).\n // Loading NEVER throws or suspends — while a dictionary is in flight,\n // t() falls back to the source text.\n const [loadedDicts, setLoadedDicts] = createSignal<Translations>({});\n const pendingLoads = new Set<string>();\n\n const loadLocale = (target: string): void => {\n if (!lazy) return;\n const loader = lazy.loaders[target];\n if (!loader) return;\n if (target in loadedDicts() || pendingLoads.has(target)) return;\n pendingLoads.add(target);\n loader()\n .then((dict) => {\n setLoadedDicts((prev) => ({ ...prev, [target]: dict }));\n })\n .catch((err) => {\n console.warn(\n `[solid-translate] Failed to load locale \"${target}\":`,\n err,\n );\n })\n .finally(() => {\n pendingLoads.delete(target);\n });\n };\n\n const setLocale = (next: string): void => {\n loadLocale(next);\n setLocaleSignal(next);\n if (persistKey) writePersistedLocale(persistKey, next);\n };\n\n // Kick off loading for the initial locale (no-op in eager mode, or when\n // the locale has no loader — e.g. the source locale without a dict).\n loadLocale(initialLocale);\n\n const t = (\n key: string,\n params?: Record<string, string | number>,\n ): string => {\n const cur = locale();\n let text = key;\n\n // Look up in translation dictionary (works for both source and target locales)\n const dict = lazy\n ? loadedDicts()[cur]\n : (props.translations as Translations)[cur];\n if (dict && key in dict) {\n text = dict[key]!;\n }\n\n // Interpolate {{variable}} and {variable} placeholders\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n text = text.replace(\n new RegExp(`\\\\{\\\\{${k}\\\\}\\\\}|\\\\{${k}\\\\}`, \"g\"),\n String(v),\n );\n }\n }\n\n return text;\n };\n\n const value: TranslationContextValue = {\n locale,\n setLocale,\n t,\n sourceLocale,\n availableLocales,\n translations: props.translations,\n };\n\n return createComponent(TranslationContext.Provider, {\n value,\n get children() {\n return props.children;\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Hooks\n// ---------------------------------------------------------------------------\n\n/** Access the full translation context. Must be inside a TranslationProvider. */\nexport function useTranslation(): TranslationContextValue {\n const ctx = useContext(TranslationContext);\n if (!ctx) {\n throw new Error(\n \"useTranslation() must be used within a <TranslationProvider>\",\n );\n }\n return ctx;\n}\n\n/** Access just the current locale and setter. */\nexport function useLocale(): {\n locale: () => string;\n setLocale: (locale: string) => void;\n sourceLocale: string;\n availableLocales: () => string[];\n} {\n const ctx = useTranslation();\n return {\n locale: ctx.locale,\n setLocale: ctx.setLocale,\n sourceLocale: ctx.sourceLocale,\n availableLocales: ctx.availableLocales,\n };\n}\n\n// ---------------------------------------------------------------------------\n// <T> Component\n// ---------------------------------------------------------------------------\n\nexport interface TProps {\n /** Explicit translation key. If omitted, children text is used as the key. */\n id?: string;\n /** Interpolation parameters */\n params?: Record<string, string | number>;\n /**\n * AI context hint — tells the AI translator about the meaning of this text.\n * Only used at build time for disambiguation; has no runtime effect.\n *\n * ```tsx\n * <T context=\"Button to save a document, not save money\">Save</T>\n * ```\n */\n context?: string;\n /** Source text / JSX content */\n children?: JSX.Element;\n}\n\n/**\n * Translatable content component.\n *\n * ```tsx\n * <T>Hello world</T>\n * <T id=\"greeting\" params={{ name: \"Alice\" }}>Hello {{name}}</T>\n * <T context=\"the physical bank\">Bank</T>\n * <T>Welcome <Var>{userName()}</Var>, you have <Num>{count()}</Num> items</T>\n * ```\n */\nexport function T(props: TProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n const resolved = resolveChildren(() => props.children);\n\n return createMemo(() => {\n const kids = resolved.toArray();\n\n // No context — just render children\n if (!ctx) return kids.length === 1 ? kids[0] : kids;\n\n // Simple case: single text child\n if (kids.length === 1 && typeof kids[0] === \"string\") {\n const key = props.id || (kids[0] as string);\n return ctx.t(key, props.params);\n }\n\n // Explicit id with non-text children — translate via id\n if (props.id) {\n const translated = ctx.t(props.id, props.params);\n\n // If translation is just text (no slot placeholders), return it\n if (!/{(\\d+)}/.test(translated)) return translated;\n\n // Collect non-text children (Var, Num, etc.) as ordered slots\n const slots: JSX.Element[] = [];\n for (const kid of kids) {\n if (typeof kid !== \"string\" && typeof kid !== \"number\") {\n slots.push(kid as JSX.Element);\n }\n }\n\n return interpolateSlots(translated, slots);\n }\n\n // Mixed children without explicit id — build a template key\n const slots: JSX.Element[] = [];\n let template = \"\";\n for (const kid of kids) {\n if (typeof kid === \"string\") {\n template += kid;\n } else if (typeof kid === \"number\") {\n template += String(kid);\n } else {\n template += `{${slots.length}}`;\n slots.push(kid as JSX.Element);\n }\n }\n\n const translated = ctx.t(template, props.params);\n if (slots.length === 0) return translated;\n return interpolateSlots(translated, slots);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Split a translated string by `{0}`, `{1}`, etc. and interleave with slots */\nfunction interpolateSlots(\n text: string,\n slots: JSX.Element[],\n): (string | JSX.Element)[] {\n const parts = text.split(/\\{(\\d+)\\}/);\n const result: (string | JSX.Element)[] = [];\n for (let i = 0; i < parts.length; i++) {\n if (i % 2 === 0) {\n if (parts[i]) result.push(parts[i]!);\n } else {\n const idx = parseInt(parts[i]!, 10);\n if (slots[idx] !== undefined) result.push(slots[idx]!);\n }\n }\n return result;\n}\n","import { createContext } from \"solid-js\";\nimport type { TranslationsInput } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Context value type\n// ---------------------------------------------------------------------------\n\nexport interface TranslationContextValue {\n /** Current locale as a reactive signal */\n locale: () => string;\n /** Switch to a different locale */\n setLocale: (locale: string) => void;\n /** Translate a key with optional interpolation params */\n t: (key: string, params?: Record<string, string | number>) => string;\n /** The source locale code */\n sourceLocale: string;\n /** All available locale codes (reactive) */\n availableLocales: () => string[];\n /** Raw translations object (eager record or lazy manifest) */\n translations: TranslationsInput;\n}\n\n// ---------------------------------------------------------------------------\n// Shared context instance\n// ---------------------------------------------------------------------------\n\nexport const TranslationContext = createContext<TranslationContextValue>();\n","/**\n * Detect the user's preferred locale from browser settings.\n *\n * Checks `navigator.languages` (and falls back to `navigator.language`)\n * then matches against the list of available locales. Tries exact match\n * first, then language-only match (e.g. \"en-US\" → \"en\").\n */\nexport function detectLocale(availableLocales?: string[]): string {\n if (typeof navigator === \"undefined\") return \"en\";\n\n const browserLocales = navigator.languages\n ? [...navigator.languages]\n : [navigator.language || \"en\"];\n\n if (!availableLocales || availableLocales.length === 0) {\n return normalizeLocale(browserLocales[0] || \"en\");\n }\n\n // Map normalized available locales back to their canonical casing so a\n // browser \"pt-br\" can match an available \"pt-BR\" (and return \"pt-BR\").\n const canonical = new Map<string, string>();\n for (const al of availableLocales) {\n const normalized = normalizeLocale(al);\n if (!canonical.has(normalized)) canonical.set(normalized, al);\n }\n\n // Exact match (case-insensitive)\n for (const bl of browserLocales) {\n const match = canonical.get(normalizeLocale(bl));\n if (match) return match;\n }\n\n // Language-only match (e.g. \"en-US\" → \"en\")\n for (const bl of browserLocales) {\n const lang = normalizeLocale(bl).split(\"-\")[0]!;\n const match = canonical.get(lang);\n if (match) return match;\n }\n\n return availableLocales[0] || \"en\";\n}\n\nfunction normalizeLocale(locale: string): string {\n return locale.toLowerCase().replace(\"_\", \"-\");\n}\n","import { useContext, type JSX, For, createMemo } from \"solid-js\";\nimport { TranslationContext } from \"./context.js\";\n\n// ---------------------------------------------------------------------------\n// <Var> — protect dynamic content from translation\n// ---------------------------------------------------------------------------\n\nexport interface VarProps {\n /** Optional name for the variable (used as placeholder in templates) */\n name?: string;\n children: JSX.Element;\n}\n\n/**\n * Marks content as untranslatable. When used inside `<T>`, the content\n * is preserved as-is while surrounding text is translated.\n *\n * ```tsx\n * <T>Hello <Var>{userName()}</Var>, welcome!</T>\n * ```\n */\nexport function Var(props: VarProps): JSX.Element {\n return (() => props.children) as unknown as JSX.Element;\n}\n\n// Mark Var for identification by T component\n(Var as any).__st_var = true;\n\n// ---------------------------------------------------------------------------\n// <Num> — locale-aware number formatting\n// ---------------------------------------------------------------------------\n\nexport interface NumProps {\n /** The number to format */\n children: number;\n /** Intl.NumberFormat options */\n options?: Intl.NumberFormatOptions;\n}\n\n/**\n * Formats a number according to the current locale using `Intl.NumberFormat`.\n *\n * ```tsx\n * <Num>{1000000}</Num> // \"1,000,000\" in en, \"1.000.000\" in de\n * <Num options={{ style: \"percent\" }}>{0.42}</Num> // \"42%\"\n * ```\n */\nexport function Num(props: NumProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n return new Intl.NumberFormat(locale, props.options).format(props.children);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <Currency> — locale-aware currency formatting\n// ---------------------------------------------------------------------------\n\nexport interface CurrencyProps {\n /** The numeric value */\n children: number;\n /** ISO 4217 currency code (e.g. \"USD\", \"EUR\") */\n currency: string;\n /** Additional Intl.NumberFormat options */\n options?: Intl.NumberFormatOptions;\n}\n\n/**\n * Formats a number as currency according to the current locale.\n *\n * ```tsx\n * <Currency currency=\"USD\">{29.99}</Currency> // \"$29.99\" in en-US\n * <Currency currency=\"EUR\">{29.99}</Currency> // \"29,99 €\" in de\n * ```\n */\nexport function Currency(props: CurrencyProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: props.currency,\n ...props.options,\n }).format(props.children);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <DateTime> — locale-aware date/time formatting\n// ---------------------------------------------------------------------------\n\nexport interface DateTimeProps {\n /** The date to format (Date object, timestamp, or ISO string) */\n children: Date | number | string;\n /** Intl.DateTimeFormat options */\n options?: Intl.DateTimeFormatOptions;\n}\n\n/**\n * Formats a date/time according to the current locale using `Intl.DateTimeFormat`.\n *\n * ```tsx\n * <DateTime>{new Date()}</DateTime>\n * <DateTime options={{ dateStyle: \"long\" }}>{new Date()}</DateTime>\n * ```\n */\nexport function DateTime(props: DateTimeProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n const date =\n props.children instanceof Date\n ? props.children\n : new Date(props.children);\n return new Intl.DateTimeFormat(locale, props.options).format(date);\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <Plural> — CLDR plural rules\n// ---------------------------------------------------------------------------\n\nexport interface PluralProps {\n /** The count value to determine which plural form to use */\n n: number;\n /** Form for zero items */\n zero?: JSX.Element;\n /** Form for exactly one item */\n one?: JSX.Element;\n /** Form for exactly two items */\n two?: JSX.Element;\n /** Form for \"few\" items (language-dependent) */\n few?: JSX.Element;\n /** Form for \"many\" items (language-dependent) */\n many?: JSX.Element;\n /** Default/fallback form */\n other: JSX.Element;\n}\n\n/**\n * Renders the appropriate plural form based on CLDR plural rules for the current locale.\n *\n * ```tsx\n * <Plural n={count()}\n * zero=\"No items\"\n * one=\"1 item\"\n * other={`${count()} items`}\n * />\n * ```\n */\nexport function Plural(props: PluralProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n\n return createMemo(() => {\n const locale = ctx?.locale() || \"en\";\n const rules = new Intl.PluralRules(locale);\n const category = rules.select(props.n);\n\n const forms: Record<string, JSX.Element | undefined> = {\n zero: props.zero,\n one: props.one,\n two: props.two,\n few: props.few,\n many: props.many,\n other: props.other,\n };\n\n return forms[category] ?? props.other;\n }) as unknown as JSX.Element;\n}\n\n// ---------------------------------------------------------------------------\n// <LocaleSelector> — drop-in locale picker\n// ---------------------------------------------------------------------------\n\nexport interface LocaleSelectorProps {\n /** Override which locales to show (defaults to all available) */\n locales?: string[];\n /** Map locale codes to display names, e.g. { en: \"English\", es: \"Español\" } */\n labels?: Record<string, string>;\n /** Additional CSS class */\n class?: string;\n}\n\n/**\n * A ready-to-use locale selector dropdown.\n *\n * ```tsx\n * <LocaleSelector labels={{ en: \"English\", es: \"Español\", fr: \"Français\" }} />\n * ```\n */\nexport function LocaleSelector(props: LocaleSelectorProps): JSX.Element {\n const ctx = useContext(TranslationContext);\n if (!ctx) {\n throw new Error(\n \"<LocaleSelector> must be used within a <TranslationProvider>\",\n );\n }\n\n const locales = createMemo(() => props.locales || ctx.availableLocales());\n\n const displayName = (code: string): string => {\n if (props.labels?.[code]) return props.labels[code]!;\n try {\n const dn = new Intl.DisplayNames([code], { type: \"language\" });\n return dn.of(code) || code;\n } catch {\n return code;\n }\n };\n\n return (\n <select\n class={props.class}\n value={ctx.locale()}\n onChange={(e) => ctx.setLocale(e.currentTarget.value)}\n >\n <For each={locales()}>\n {(code) => <option value={code}>{displayName(code)}</option>}\n </For>\n </select>\n ) as JSX.Element;\n}\n","/**\n * Mark a string for translation extraction.\n *\n * At build time, the Vite plugin and CLI scan for `msg()` calls and add\n * the strings to the source locale file for AI translation.\n *\n * At runtime, `msg()` is a no-op — it returns the source text as-is.\n * Use `t()` from `useTranslation()` for runtime translation.\n *\n * ```ts\n * // Marks \"Save changes\" for extraction\n * const label = msg(\"Save changes\");\n *\n * // With interpolation template\n * const greeting = msg(\"Hello {{name}}\", { name: \"World\" });\n *\n * // In a component, translate at runtime:\n * const { t } = useTranslation();\n * <button>{t(label)}</button>\n * ```\n */\nexport function msg(\n text: string,\n _params?: Record<string, string | number>,\n): string {\n return text;\n}\n"],"mappings":"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","names":["useContext","createMemo","createMemo","useContext","translated","slots"]}
|