@bndynet/vue-site 1.0.2 → 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/bin/vue-site.mjs CHANGED
@@ -5,7 +5,7 @@ import vue from '@vitejs/plugin-vue'
5
5
  import { resolve, dirname, basename } from 'path'
6
6
  import { fileURLToPath, pathToFileURL } from 'url'
7
7
  import { createRequire } from 'module'
8
- import { transform } from 'esbuild'
8
+ import { build as esbuild } from 'esbuild'
9
9
  import fs from 'fs'
10
10
 
11
11
  const __filename = fileURLToPath(import.meta.url)
@@ -166,6 +166,29 @@ function resolveBootstrapUrl(path) {
166
166
  return '/' + t.replace(/^\.\//, '')
167
167
  }
168
168
 
169
+ // Friendly display names for auto-discovered locale files (`/locales/<code>.json`). Used only when
170
+ // the config doesn't declare `i18n.locales`; unknown codes fall back to the code itself.
171
+ const LOCALE_LABELS = {
172
+ en: 'English',
173
+ zh: '简体中文',
174
+ 'zh-CN': '简体中文',
175
+ 'zh-TW': '繁體中文',
176
+ ja: '日本語',
177
+ ko: '한국어',
178
+ fr: 'Français',
179
+ de: 'Deutsch',
180
+ es: 'Español',
181
+ pt: 'Português',
182
+ ru: 'Русский',
183
+ it: 'Italiano',
184
+ nl: 'Nederlands',
185
+ pl: 'Polski',
186
+ tr: 'Türkçe',
187
+ vi: 'Tiếng Việt',
188
+ th: 'ไทย',
189
+ ar: 'العربية',
190
+ }
191
+
169
192
  /**
170
193
  * Bootstrap script shared by dev (virtual entry) and build (inlined in html).
171
194
  * `siteConfigSpecifier` differs because dev serves from Vite root (`/foo`)
@@ -173,6 +196,11 @@ function resolveBootstrapUrl(path) {
173
196
  *
174
197
  * Static import bundles `bootstrap` for production; dynamic import with
175
198
  * vite-ignore is not emitted.
199
+ *
200
+ * Convention: translations are auto-loaded from `/locales/<code>.json` (relative to the Vite root,
201
+ * i.e. the config's directory). The user writes zero glue code — no `index.ts`, no `messages` field.
202
+ * An explicit `i18n.messages` still works and overrides auto-loaded keys; an explicit `i18n.locales`
203
+ * still controls the label/icon/order, otherwise the locale list is derived from the file names.
176
204
  */
177
205
  function buildBootstrapScript({ siteConfig, siteConfigSpecifier }) {
178
206
  const bs = siteConfig?.bootstrap
@@ -189,6 +217,43 @@ function buildBootstrapScript({ siteConfig, siteConfigSpecifier }) {
189
217
  `import '${pkgDirUrl}/dist/style.css'`,
190
218
  `import siteConfig from '${siteConfigSpecifier}'`,
191
219
  `import { repositoryUrl } from '${VIRTUAL_PACKAGE}'`,
220
+ ``,
221
+ `// Auto-discover translations: /locales/<code>.json -> { [code]: { ...messages } }.`,
222
+ `const __localeFiles = import.meta.glob('/locales/*.json', { eager: true, import: 'default' })`,
223
+ `const __LOCALE_LABELS = ${JSON.stringify(LOCALE_LABELS)}`,
224
+ `const __autoMessages = {}`,
225
+ `for (const __p in __localeFiles) {`,
226
+ ` const __code = __p.slice(__p.lastIndexOf('/') + 1).replace(/\\.json$/, '')`,
227
+ ` __autoMessages[__code] = __localeFiles[__p]`,
228
+ `}`,
229
+ `const __autoCodes = Object.keys(__autoMessages).sort()`,
230
+ `function __deepMerge(base, override) {`,
231
+ ` const out = { ...base }`,
232
+ ` for (const k in (override || {})) {`,
233
+ ` const a = out[k], b = override[k]`,
234
+ ` out[k] = a && b && typeof a === 'object' && typeof b === 'object' && !Array.isArray(a) && !Array.isArray(b)`,
235
+ ` ? __deepMerge(a, b) : b`,
236
+ ` }`,
237
+ ` return out`,
238
+ `}`,
239
+ `function __mergeMessages(base, override) {`,
240
+ ` const out = {}`,
241
+ ` const keys = new Set([...Object.keys(base), ...Object.keys(override || {})])`,
242
+ ` for (const k of keys) out[k] = __deepMerge(base[k] || {}, (override || {})[k] || {})`,
243
+ ` return out`,
244
+ `}`,
245
+ `// Merge auto-loaded files into i18n. Explicit config wins: messages override per key, and an`,
246
+ `// explicit locales list controls label/icon/order (else it's derived from the file names).`,
247
+ `function __resolveI18n(cfg) {`,
248
+ ` const hasAuto = __autoCodes.length > 0`,
249
+ ` if (!cfg && !hasAuto) return cfg`,
250
+ ` const base = cfg || {}`,
251
+ ` let locales = base.locales`,
252
+ ` if ((!locales || !locales.length) && hasAuto) {`,
253
+ ` locales = __autoCodes.map((c) => ({ code: c, label: __LOCALE_LABELS[c] || c }))`,
254
+ ` }`,
255
+ ` return { ...base, locales, messages: __mergeMessages(__autoMessages, base.messages) }`,
256
+ `}`,
192
257
  `;(async () => {`,
193
258
  ` const searchParams = new URLSearchParams(window.location.search)`,
194
259
  ` const hasThemeQuery = searchParams.has('theme')`,
@@ -203,6 +268,7 @@ function buildBootstrapScript({ siteConfig, siteConfigSpecifier }) {
203
268
  ` }`,
204
269
  ` const app = await createSiteApp({`,
205
270
  ` ...siteConfig,`,
271
+ ` i18n: __resolveI18n(siteConfig.i18n),`,
206
272
  ` ...(hasThemeQuery ? { theme: { ...(siteConfig.theme || {}), default: resolvedTheme } } : {}),`,
207
273
  ` packageRepository: repositoryUrl,`,
208
274
  ` baseUrl: import.meta.env.BASE_URL,`,
@@ -238,39 +304,167 @@ const htmlTemplate = buildHtmlShell(
238
304
  `<script type="module" src="/@id/__x00__${VIRTUAL_ENTRY}"></script>`,
239
305
  )
240
306
 
307
+ // esbuild plugin stubbing asset / SFC / `?raw` imports (static or dynamic) to an empty default
308
+ // export, so bundling the config for pre-load doesn't choke on resources Node can't load. Page
309
+ // loaders are never invoked during pre-load (only build-time settings are read).
310
+ function preloadAssetStubPlugin() {
311
+ const NS = 'vue-site-asset'
312
+ const ASSET =
313
+ /\?raw(?:&\S*)?$|\.(?:vue|css|scss|sass|less|styl|md|markdown|png|jpe?g|gif|svg|webp|avif|ico)(?:\?\S*)?$/
314
+ return {
315
+ name: 'vue-site:preload-asset-stub',
316
+ setup(b) {
317
+ b.onResolve({ filter: ASSET }, (args) => ({ path: args.path, namespace: NS }))
318
+ b.onLoad({ filter: /.*/, namespace: NS }, () => ({
319
+ contents: 'export default ""',
320
+ loader: 'js',
321
+ }))
322
+ },
323
+ }
324
+ }
325
+
241
326
  async function loadSiteConfig() {
242
327
  const configPath = resolve(cwd, foundConfig)
243
328
  const raw = fs.readFileSync(configPath, 'utf-8')
244
329
 
245
- const isTs = /\.m?ts$/.test(foundConfig)
246
- const { code } = await transform(raw, {
247
- loader: isTs ? 'ts' : 'js',
248
- format: 'esm',
249
- })
250
-
251
- const stubbed = code.replace(
252
- /import\s*\{[^}]*defineConfig[^}]*\}\s*from\s*['"][^'"]*['"]\s*;?/g,
253
- 'const defineConfig = (c) => c;',
330
+ // Stub the framework's value imports so the config evaluates without the real (browser-only)
331
+ // package. `defineConfig` is identity (the config object passes through); every other named
332
+ // import (e.g. `tk`, `localizedPage`) becomes a callable no-op that returns a no-op, covering
333
+ // helpers used as values. Relative imports (e.g. `./locales`) are left intact and bundled below.
334
+ let stubbed = raw.replace(
335
+ /import\s*\{([^}]*)\}\s*from\s*['"][^'"]*vue-site['"]\s*;?/g,
336
+ (_match, names) => {
337
+ const ids = names
338
+ .split(',')
339
+ .map((part) => part.trim())
340
+ .filter(Boolean)
341
+ .map((part) => {
342
+ const segments = part.split(/\s+as\s+/)
343
+ return (segments[1] ?? segments[0]).trim()
344
+ })
345
+ .filter(Boolean)
346
+ return ids
347
+ .map((id) =>
348
+ id === 'defineConfig'
349
+ ? 'const defineConfig = (c) => c;'
350
+ : `const ${id} = (..._args) => (() => {});`,
351
+ )
352
+ .join('\n')
353
+ },
254
354
  )
255
355
 
256
- const tmpFile = resolve(cwd, `.site-config.${Date.now()}.tmp.mjs`)
257
- fs.writeFileSync(tmpFile, stubbed)
356
+ // `import.meta.glob(...)` is a Vite-only feature; in Node it would throw at config-eval time.
357
+ // Stub it to an empty map so configs using `localizedPageGlob(import.meta.glob(...))` pre-load
358
+ // (page loaders are never invoked here — only build-time settings are read). The real glob runs
359
+ // in the browser via Vite.
360
+ if (/import\.meta\.glob/.test(stubbed)) {
361
+ stubbed =
362
+ 'const __vueSiteGlobStub = (..._args) => ({});\n' +
363
+ stubbed.replace(/import\.meta\.glob/g, '__vueSiteGlobStub')
364
+ }
365
+
366
+ const isTs = /\.m?ts$/.test(foundConfig)
367
+ // Write the stubbed entry next to the original so its relative imports (`./locales`) resolve.
368
+ const entryFile = resolve(
369
+ dirname(configPath),
370
+ `.${basename(configPath)}.${Date.now()}.preload.${isTs ? 'ts' : 'js'}`,
371
+ )
372
+ const tmpDir = resolve(cwd, `.vue-site-preload-${Date.now()}`)
373
+ fs.writeFileSync(entryFile, stubbed)
258
374
 
259
375
  try {
260
- const mod = await import(pathToFileURL(tmpFile).href)
376
+ // Bundle so local modules the config imports (e.g. `./locales.ts`) are inlined and TS is
377
+ // handled; asset imports are stubbed; remaining bare deps stay external for Node to resolve.
378
+ await esbuild({
379
+ entryPoints: { 'site-config': entryFile },
380
+ outdir: tmpDir,
381
+ bundle: true,
382
+ format: 'esm',
383
+ platform: 'node',
384
+ splitting: true,
385
+ logLevel: 'silent',
386
+ packages: 'external',
387
+ outExtension: { '.js': '.mjs' },
388
+ plugins: [preloadAssetStubPlugin()],
389
+ })
390
+
391
+ const entry = resolve(tmpDir, 'site-config.mjs')
392
+ const mod = await import(pathToFileURL(entry).href)
261
393
  return mod.default || {}
262
394
  } catch (e) {
263
- console.warn(
264
- `[vue-site] Could not pre-load site config from ${foundConfig}: ${e.message}`,
395
+ throw new Error(
396
+ `[vue-site] Could not pre-load site config from ${foundConfig}: ${e.message}\n` +
397
+ ` This usually means your config imports modules Node can't resolve directly ` +
398
+ `(path aliases like @/..., or framework APIs other than defineConfig).`,
265
399
  )
266
- return {}
267
400
  } finally {
268
401
  try {
269
- fs.unlinkSync(tmpFile)
402
+ fs.unlinkSync(entryFile)
403
+ } catch {}
404
+ try {
405
+ fs.rmSync(tmpDir, { recursive: true, force: true })
270
406
  } catch {}
271
407
  }
272
408
  }
273
409
 
410
+ /**
411
+ * Turn a base file path into a `import.meta.glob(...)` expression matching the base file plus its
412
+ * per-locale siblings (`name.<code>.ext`) — but NOT unrelated `nameOther.ext`:
413
+ * `../README.md` -> import.meta.glob(["../README.md","../README.*.md"], { query: '?raw' })
414
+ * `./pages/Home.vue` -> import.meta.glob(["./pages/Home.vue","./pages/Home.*.vue"], {})
415
+ * Markdown is loaded `?raw` (string content); other files (e.g. `.vue`) export a component.
416
+ * Returns `null` when the path has no extension (can't build a sensible glob).
417
+ */
418
+ function fileToLocaleGlobExpr(rawPath) {
419
+ const qIdx = rawPath.indexOf('?')
420
+ const query = qIdx >= 0 ? rawPath.slice(qIdx + 1) : ''
421
+ const pathOnly = qIdx >= 0 ? rawPath.slice(0, qIdx) : rawPath
422
+ const dot = pathOnly.lastIndexOf('.')
423
+ if (dot <= pathOnly.lastIndexOf('/')) return null
424
+ const ext = pathOnly.slice(dot)
425
+ const globs = [pathOnly, `${pathOnly.slice(0, dot)}.*${ext}`]
426
+ const isMarkdown = /\.(?:md|markdown)$/i.test(ext) || /(?:^|&)raw(?:$|&)/.test(query)
427
+ const opts = isMarkdown ? `{ query: '?raw' }` : `{}`
428
+ return `import.meta.glob(${JSON.stringify(globs)}, ${opts})`
429
+ }
430
+
431
+ // Sugar so configs can name page files as plain strings; the CLI turns them into Vite globs so the
432
+ // per-locale files get bundled. Two shapes are rewritten in the user's JS/TS under `cwd`:
433
+ // localizedPage('./file.md') -> localizedPage(import.meta.glob([...], { query: '?raw' }))
434
+ // page: './file.md' -> page: import.meta.glob([...], { query: '?raw' })
435
+ // Loader functions, locale maps (`localizedPage({ en, zh })`) and explicit `import.meta.glob` are
436
+ // left untouched; the `page:` form only rewrites path-like values (starting with `.` or `/`).
437
+ function localizedPageSugarPlugin() {
438
+ const CALL_STRING_ARG = /(\blocalizedPage\s*\(\s*)(['"`])((?:\\.|(?!\2).)*)\2/g
439
+ const PAGE_STRING_FIELD = /(\bpage\s*:\s*)(['"`])((?:\\.|(?!\2).)*)\2/g
440
+ return {
441
+ name: 'vue-site:localized-page-sugar',
442
+ enforce: 'pre',
443
+ transform(code, id) {
444
+ const file = id.split('?')[0]
445
+ if (!/\.(?:[cm]?[jt]sx?)$/.test(file)) return
446
+ if (!file.startsWith(cwd) || file.includes('/node_modules/')) return
447
+ if (!code.includes('localizedPage(') && !/\bpage\s*:\s*['"`]/.test(code)) return
448
+ let changed = false
449
+ let out = code.replace(CALL_STRING_ARG, (match, head, _q, rawPath) => {
450
+ const expr = fileToLocaleGlobExpr(rawPath)
451
+ if (!expr) return match
452
+ changed = true
453
+ return `${head}${expr}`
454
+ })
455
+ out = out.replace(PAGE_STRING_FIELD, (match, head, _q, rawPath) => {
456
+ // Only rewrite path-like values to avoid touching unrelated `page: '...'` properties.
457
+ if (!/^[./]/.test(rawPath)) return match
458
+ const expr = fileToLocaleGlobExpr(rawPath)
459
+ if (!expr) return match
460
+ changed = true
461
+ return `${head}${expr}`
462
+ })
463
+ return changed ? { code: out, map: null } : undefined
464
+ },
465
+ }
466
+ }
467
+
274
468
  function vueSitePlugin(entryCode) {
275
469
  return [
276
470
  {
@@ -458,7 +652,7 @@ async function buildViteConfig(options = {}) {
458
652
 
459
653
  const baseConfig = {
460
654
  root: cwd,
461
- plugins: [vue(vueOpts), ...watchedScssPlugin, ...vueSitePlugin(entryCode), ...(userPlugins || [])],
655
+ plugins: [localizedPageSugarPlugin(), vue(vueOpts), ...watchedScssPlugin, ...vueSitePlugin(entryCode), ...(userPlugins || [])],
462
656
  resolve: {
463
657
  alias: {
464
658
  vue: resolve(vuePath, 'dist/vue.runtime.esm-bundler.js'),
@@ -0,0 +1,10 @@
1
+ type __VLS_Props = {
2
+ compact?: boolean;
3
+ };
4
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
5
+ compact: boolean;
6
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
7
+ detailsRef: HTMLDetailsElement;
8
+ summaryRef: HTMLElement;
9
+ }, HTMLDetailsElement>;
10
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import { InjectionKey, Ref } from 'vue';
2
+ /** Use `Symbol.for` so the key matches even if multiple copies of this package are resolved. */
3
+ export declare const localeRefKey: InjectionKey<Ref<string>>;
4
+ /**
5
+ * Resolve and apply the initial locale (stored > detected > default), then keep `localeRef`
6
+ * authoritative. Call once from `createSiteApp` when `i18n` is configured.
7
+ *
8
+ * @param localeRef — ref created next to `createApp` so it uses the same Vue runtime as the app.
9
+ * @param defaultLocale — used when nothing valid is in localStorage and detection finds no match.
10
+ * @param locales — full list of allowed locale codes.
11
+ * @param detectBrowser — try `navigator.language(s)` before falling back to `defaultLocale`.
12
+ * @param key — localStorage key for persistence (default `vue-site-locale`).
13
+ */
14
+ export declare function initLocale(localeRef: Ref<string>, defaultLocale: string, locales: readonly string[], detectBrowser?: boolean, key?: string): void;
15
+ export declare function useLocale(): {
16
+ locale: Ref<string, string>;
17
+ setLocale: (code: string) => void;
18
+ locales: () => string[];
19
+ };
@@ -0,0 +1,18 @@
1
+ import { LocalizedString } from '../types';
2
+ /**
3
+ * Reactively resolve localized content against the active locale. Returns:
4
+ *
5
+ * - `localize(value)` — resolve a `LocalizedString` (locale map, plain string, or a `tk()` key
6
+ * reference); plain strings pass through unchanged.
7
+ * - `t(id, params?)` — resolve a UI message id from `SiteConfig.i18n.messages` layered over the
8
+ * framework's built-in strings, with locale fallback (exact → primary-subtag → default → `en`)
9
+ * and `{name}` placeholder interpolation.
10
+ * - `locale` — the active locale ref.
11
+ *
12
+ * Reading these inside a template/computed makes the output update automatically on language change.
13
+ */
14
+ export declare function useLocalize(): {
15
+ localize: (value: LocalizedString | undefined) => string;
16
+ t: (id: string, params?: Record<string, string | number>) => string;
17
+ locale: import('vue').Ref<string, string>;
18
+ };
@@ -0,0 +1,9 @@
1
+ import { LocaleCode } from './types';
2
+ /**
3
+ * Built-in UI strings for framework chrome (theme/locale switchers, page errors). Keyed by locale
4
+ * then message id. Consumers override or extend these per locale via `SiteConfig.i18n.messages`,
5
+ * which is merged on top of these defaults. Unknown locales fall back to `en`.
6
+ *
7
+ * Body strings may contain `{name}` placeholders, interpolated by `useLocalize().t(id, params)`.
8
+ */
9
+ export declare const builtinMessages: Record<LocaleCode, Record<string, string>>;
@@ -0,0 +1,81 @@
1
+ import { Component } from 'vue';
2
+ import { LocaleCode, LocalizedString, MessageRef, MessageTree, PageLoader } from './types';
3
+ /** Merged, flattened message dictionaries keyed by locale then dotted message id. */
4
+ export type MessageCatalog = Record<LocaleCode, Record<string, string>>;
5
+ /**
6
+ * Flatten a (possibly nested) message tree to dotted ids: `{ site: { title: 'x' } }` → `site.title`.
7
+ * Flat dictionaries pass through unchanged, so both layouts can be mixed.
8
+ */
9
+ export declare function flattenMessages(tree: MessageTree | undefined, prefix?: string): Record<string, string>;
10
+ /** Type guard for a {@link MessageRef} (a `{ $t }` key reference). */
11
+ export declare function isMessageRef(value: unknown): value is MessageRef;
12
+ /** Build a `MessageRef` referencing a central message id; use in `LocalizedString` config fields. */
13
+ export declare function tk(id: string, params?: Record<string, string | number>): MessageRef;
14
+ /**
15
+ * Merge `override` message trees on top of `base`, per locale, flattening nested groups to dotted
16
+ * ids. Returns a flat {@link MessageCatalog} ready for {@link resolveMessage}.
17
+ */
18
+ export declare function mergeCatalog(base: Record<LocaleCode, MessageTree>, override?: Record<LocaleCode, MessageTree>): MessageCatalog;
19
+ /**
20
+ * Resolve a message id from a merged `catalog` for the active locale, with fallback
21
+ * (exact → primary-subtag → `defaultLocale` → `en` → the id itself) and `{name}` interpolation.
22
+ */
23
+ export declare function resolveMessage(catalog: MessageCatalog, id: string, locale: string, defaultLocale?: LocaleCode, params?: Record<string, string | number>): string;
24
+ /**
25
+ * Resolve a `LocalizedString` to a plain string for the active `locale`.
26
+ *
27
+ * A bare `string` is returned unchanged. For a locale map, resolution order is:
28
+ * exact locale → primary-subtag match (e.g. `en-US` → `en`) → `defaultLocale` → first entry → `''`.
29
+ * A {@link MessageRef} is returned as its raw id here (use {@link resolveField} with a catalog to
30
+ * resolve it). This keeps single-language configs (plain strings) working without locale context.
31
+ */
32
+ export declare function resolveLocalized(value: LocalizedString | undefined, locale: string, defaultLocale?: LocaleCode): string;
33
+ /**
34
+ * Resolve any `LocalizedString` — including a {@link MessageRef} — against the active locale.
35
+ * Plain strings and locale maps go through {@link resolveLocalized}; key references are resolved
36
+ * from the merged message `catalog` via {@link resolveMessage}.
37
+ */
38
+ export declare function resolveField(value: LocalizedString | undefined, locale: string, defaultLocale?: LocaleCode, catalog?: MessageCatalog): string;
39
+ /** Options for {@link localizedPage}. */
40
+ export interface LocalizedPageOptions {
41
+ /** Locale to fall back to when the active locale has no entry (else the first entry is used). */
42
+ defaultLocale?: LocaleCode;
43
+ }
44
+ /**
45
+ * Build a per-locale page loader for `NavItem.page` / `StandalonePage.page`. The returned loader
46
+ * receives the active locale and resolves the matching content, with graceful fallback. Three forms:
47
+ *
48
+ * - **File name** (recommended, simplest) — `localizedPage('../README.md')`. The **vue-site CLI**
49
+ * rewrites this to a glob, so every `README.<code>.md` sitting next to the base file is picked up
50
+ * automatically (e.g. `README.zh.md` → `zh`); a locale with no file falls back to the base file
51
+ * (`README.md`). Add a language by dropping in a file — no config edits. Works for Markdown
52
+ * (`.md`, loaded as `?raw`) and Vue pages (`.vue`). _Only the CLI understands this form; in
53
+ * library mode use the glob form below._
54
+ * - **Glob map** — `localizedPage(import.meta.glob('../README*.md', { query: '?raw' }))`. Same
55
+ * behavior as the file-name form, written explicitly (use this in library mode). Pass a **lazy**
56
+ * glob whose modules expose `{ default }`.
57
+ * - **Locale map** — `localizedPage({ en: () => import('...'), zh: () => import('...') })` for files
58
+ * that don't share a base name.
59
+ *
60
+ * @example
61
+ * // Simplest (via the CLI): ../README.md (base) + ../README.zh.md + ../README.ja.md + ...
62
+ * page: localizedPage('../README.md')
63
+ *
64
+ * @example
65
+ * // Explicit locale map
66
+ * page: localizedPage({
67
+ * en: () => import('./pages/guide.en.md?raw'),
68
+ * zh: () => import('./pages/guide.zh.md?raw'),
69
+ * })
70
+ */
71
+ export declare function localizedPage(file: string, options?: LocalizedPageOptions): PageLoader;
72
+ export declare function localizedPage(loaders: Record<LocaleCode, () => Promise<{
73
+ default: string;
74
+ }>>, options?: LocalizedPageOptions): (locale: LocaleCode) => Promise<{
75
+ default: string;
76
+ }>;
77
+ export declare function localizedPage(loaders: Record<LocaleCode, () => Promise<{
78
+ default: Component;
79
+ }>>, options?: LocalizedPageOptions): (locale: LocaleCode) => Promise<{
80
+ default: Component;
81
+ }>;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  import { SiteConfig } from './types';
2
2
  export { createSiteApp } from './create-app';
3
3
  export { useTheme, themeRefKey } from './composables/useTheme';
4
+ export { useLocale, localeRefKey } from './composables/useLocale';
5
+ export { useLocalize } from './composables/useLocalize';
6
+ export { resolveLocalized, resolveField, resolveMessage, mergeCatalog, flattenMessages, tk, isMessageRef, localizedPage } from './i18n-utils';
7
+ export type { LocalizedPageOptions, MessageCatalog } from './i18n-utils';
8
+ export { builtinMessages } from './i18n-messages';
4
9
  export { useSiteConfig } from './composables/useSiteConfig';
5
10
  export { builtinThemePalettes } from './theme/presets';
6
11
  export { ElMessage, ElMessageBox, ElNotification, } from 'element-plus';
7
- export type { SiteConfig, SiteEnvConfig, SiteViteConfig, SiteExternalLink, NavItem, StandalonePage, AuthRule, AuthContext, AuthConfig, RouterConfig, ThemeConfig, ThemeOption, ThemePaletteVars, ResolvedNavItem, } from './types';
12
+ export type { SiteConfig, SiteEnvConfig, SiteViteConfig, SiteExternalLink, NavItem, StandalonePage, AuthRule, AuthContext, AuthConfig, RouterConfig, ThemeConfig, ThemeOption, ThemePaletteVars, ResolvedNavItem, LocaleCode, LocalizedString, MessageRef, MessageTree, LocaleOption, I18nConfig, PageLoader, } from './types';
8
13
  export declare function defineConfig(config: SiteConfig): SiteConfig;