Start with the work, not the row
A bilingual article is one intellectual work with two complete presentations. The pages share an identity, but neither should become a bag of half-localized fields.
| Field | Shared or localized | Purpose |
|---|---|---|
| Slug | Shared | Stable identity across locales |
| Language | Per page | Language of this body |
| Origin Language | Shared | Language used by the author |
| Title and Summary | Localized | Reader-facing metadata |
| Status | Per page | Independent publication control |
| Other Version | Relation | Connects the complete pages |
The public lookup can stay deliberately strict:
type Locale = 'en' | 'zh'
async function findPublishedArticle(locale: Locale, slug: string) {
return repository.find({
language: locale,
slug,
status: 'Published',
})
}
The relation helps the interface offer another language, but it must never redefine the requested page.
Editorial sequence
- Write the origin page.
- Create the second page with the same slug.
- Set the same Origin Language on both pages.
- Add reciprocal relations.
- Review title, summary, and links in each language.
- Publish each page independently.
Writing view
Show only pages where Language equals Origin Language. The author sees one handwritten row per work.
Publishing view
Show every locale page, relation, status, and date so missing or incomplete translations remain visible.
Adding a future Japanese version should create a third complete locale page, not another cluster of language-specific columns squeezed into an existing row.