每个详情路由都要自己把「这个条目在这个语言下长什么样」重新拼一遍。拼法一样,
拼的人是七个不同的文件。
详情页:同一段前言抄了五遍
people/[slug].astro、projects/[slug].astro、research/[slug].astro、
resources/[slug].astro、publications/[slug].astro 的 getStaticPaths() 开头
都是同一段:
const entries = await getCollection('X', ({ data }) => data.publish !== false);
const { translations, forLocale } = await relationSources();
return LOCALES.flatMap((lang) => {
const overlays = translationIndex(translations, COLLECTION_VAULT_PATHS.X, lang);
const relations = forLocale(lang);
...
frontmatter 里也是同一段:
const lang = Astro.params.lang as Lang;
const { Content } = await render(overlay ?? entry);
const isFallback = lang !== 'zh' && !overlay;
const data = localizedData(entry.data, overlay?.data, lang);
为了写出这十行,每个页面要 import 六个名字:entrySlug、translationIndex、
localizedData、isMachineTranslated、COLLECTION_VAULT_PATHS、relationSources
(再加 coverFor / personAssets)。六个浅接口拼出一个答案,而这个答案对五个
路由是同一个。
(#42 §3 记的 isFallback 抄了七遍,是这个问题的一个切面。这里说的是整段装配。)
列表页:view model 映射也各写各的
更糟的是列表页对同一个概念用了不同的helper:
people/index.astro 用 translationIndex + localizedData,映射出 11 个字段的 Person
projects/index.astro 用 localizedIndex,映射出 4 个字段的 Person
index.astro(首页)又映射一次 Project
三处都在算 entrySlug(p)、personAssets(data, slug).avatar、
normalizeRole(data.role)、detailUrl。三处都可能少算一个字段——
src/components/react/types.ts 的注释就记着这么一次:「restating is how
Project.summary came to exist here, be rendered by HomePage, and be supplied
by nothing.」
建议
缺的是一层深模块:给它一个 collection 和一个 lang,还回来一个可以直接渲染的
条目。接口小,实现里吃掉 overlay 配对、字段合并、fallback 判定、slug、封面、
URL:
// src/content-layer/localizedEntries.ts
interface LocalizedEntry<T> {
entry: T; // 用于 render()
data: MergedData; // overlay 覆盖后的 frontmatter
slug: string;
href: string;
cover?: string;
overlay?: Entry; // TranslationNotice 自己判断 fallback,不再由页面算
}
// 详情页
const entries = await localizedEntries('research', lang);
// 列表页
const cards = await localizedEntries('people', lang);
判断标准:拆完之后,五个 [slug].astro 的 getStaticPaths 应该只剩「取集合、
配 relations、组 props」,六个 util import 变成一个;三处 view model 映射变成
一处。
依赖顺序:这件事建议排在 src/utils/ 拆分(另一个 issue)之后做,新模块直接
落在 src/content-layer/。
每个详情路由都要自己把「这个条目在这个语言下长什么样」重新拼一遍。拼法一样,
拼的人是七个不同的文件。
详情页:同一段前言抄了五遍
people/[slug].astro、projects/[slug].astro、research/[slug].astro、resources/[slug].astro、publications/[slug].astro的getStaticPaths()开头都是同一段:
frontmatter 里也是同一段:
为了写出这十行,每个页面要 import 六个名字:
entrySlug、translationIndex、localizedData、isMachineTranslated、COLLECTION_VAULT_PATHS、relationSources(再加
coverFor/personAssets)。六个浅接口拼出一个答案,而这个答案对五个路由是同一个。
(#42 §3 记的
isFallback抄了七遍,是这个问题的一个切面。这里说的是整段装配。)列表页:view model 映射也各写各的
更糟的是列表页对同一个概念用了不同的helper:
people/index.astro用translationIndex+localizedData,映射出 11 个字段的Personprojects/index.astro用localizedIndex,映射出 4 个字段的Personindex.astro(首页)又映射一次Project三处都在算
entrySlug(p)、personAssets(data, slug).avatar、normalizeRole(data.role)、detailUrl。三处都可能少算一个字段——src/components/react/types.ts的注释就记着这么一次:「restating is howProject.summarycame to exist here, be rendered by HomePage, and be suppliedby nothing.」
建议
缺的是一层深模块:给它一个 collection 和一个 lang,还回来一个可以直接渲染的
条目。接口小,实现里吃掉 overlay 配对、字段合并、fallback 判定、slug、封面、
URL:
判断标准:拆完之后,五个
[slug].astro的getStaticPaths应该只剩「取集合、配 relations、组 props」,六个 util import 变成一个;三处 view model 映射变成
一处。
依赖顺序:这件事建议排在
src/utils/拆分(另一个 issue)之后做,新模块直接落在
src/content-layer/。