// /composables/useLocalizedCollection.ts import type { Collections } from "@nuxt/content"; export type UseLocalizedOptions = { /** 默认 locale -> suffix 映射 */ localeMap?: Record; /** 回退 locale 的 suffix(例如 'en') */ fallbackSuffix?: string; /** 当找不到内容时是否抛错,默认 true */ throwOnMissing?: boolean; /** useAsyncData 的 key 前缀(默认等于 baseName) */ keyPrefix?: string; }; function asCollectionKey(key: string) { return key as keyof Collections; } /** * 带类型安全的多语言内容加载器 * @example * const { data: page } = await useLocalizedCollection('index') */ export function useLocalizedCollection< B extends string, // 基础名称 >(baseName: B, opts: UseLocalizedOptions = {}) { const { locale } = useI18n(); const localeMap = opts.localeMap ?? { en: "en", "zh-CN": "zh" }; const fallbackSuffix = opts.fallbackSuffix ?? "en"; const keyPrefix = opts.keyPrefix ?? baseName; const throwOnMissing = opts.throwOnMissing ?? true; // 🔥 自动推断对应集合类型 type LocalizedKey = keyof { [K in keyof Collections as K extends `${B}_${string}` ? K : never]: any; }; type Schema = Collections[LocalizedKey]; return useAsyncData( `${keyPrefix}-${locale.value}`, async () => { const suffix = localeMap[locale.value] ?? locale.value.split("-")[0] ?? "en"; const key = asCollectionKey(`${baseName}_${suffix}`); let content = (await queryCollection(key).first()) as Schema | null; if (!content && suffix !== fallbackSuffix) { const fallbackKey = asCollectionKey(`${baseName}_${fallbackSuffix}`); content = (await queryCollection(fallbackKey).first()) as Schema | null; } return content; }, { watch: [locale] } ).then((res) => { if (throwOnMissing && res && !res.data?.value) { throw createError({ statusCode: 404, statusMessage: `Page not found: ${baseName} for locale ${locale.value}`, fatal: true, }); } return res; }); }