Commit f50619ff by PLN (Algolia)

docs: per-language slugs and the root KEY namespace

parent a27b3821
......@@ -53,7 +53,9 @@ Markdown sections are loaded server-side at build via `gray-matter` (frontmatter
- `getAllContentIds(section)` — plain `string[]`; dynamic pages map it to params via `.map((id) => ({ id }))`.
- `getContentData(section, id)` — single item with rendered `contentHtml`.
*Blog — bilingual folder model:* each post is a folder `content/blog/<slug>/` holding `meta.md` (shared metadata: canonical `title`, `date`, `description`, `tags`, `draft`, optional `share` token) plus one `<lang>.md` per language (`fr.md` / `en.md`) whose frontmatter carries the localized `title` (and optionally `description`); the body is that language's content. The slug IS the folder name. A post missing one language file is a valid post in the other; the accessor flags it via `untranslated` and the UI shows an honest banner linking to the available language. Accessors live in **`lib/blog.js`** (the flat-collection API above does NOT serve blog): `getBlogPosts(lang)` (listing, default `fr`), `getBlogPost(slug, lang)`, `getAllBlogSlugs()`, `getBlogLanguages(slug)`; draft accessors (`getAllBlogDraftSlugs`/`getAllBlogDrafts`/`getAllBlogDraftParams`/`getDraftBlogPost`) keep minting `<slug>-<token>` share ids from meta.md's `share`. Draft filtering still runs through the shared `draftsVisible()` rule. Public routes: `/blog/[slug]` (default language, FR/EN switch when both exist), `/blog/[slug]/[lang]` (other language), `/blog/d/[slug]` (unlisted drafts, unchanged).
*Blog — bilingual folder model:* each post is a folder `content/blog/<slug>/` holding `meta.md` (shared metadata: canonical `title`, `date`, `description`, `tags`, `draft`, optional `share` token) plus one `<lang>.md` per language (`fr.md` / `en.md`) whose frontmatter carries the localized `title` (and optionally `description` and a localized `slug:`); the body is that language's content. The folder name is the canonical slug. A post missing one language file is a valid post in the other; the accessor flags it via `untranslated` and the UI shows an honest banner linking to the available language. Accessors live in **`lib/blog.js`** (the flat-collection API above does NOT serve blog): `getBlogPosts(lang)` (listing, default `fr`), `getBlogPost(slug, lang)`, `getAllBlogSlugs()`, `getBlogLanguages(slug)`; draft accessors (`getAllBlogDraftSlugs`/`getAllBlogDrafts`/`getAllBlogDraftParams`/`getDraftBlogPost`) keep minting `<slug>-<token>` share ids from meta.md's `share`. Draft filtering still runs through the shared `draftsVisible()` rule. Public routes: `/blog/[slug]` (default language, FR/EN switch when both exist), `/blog/[slug]/[lang]` (other language), `/blog/d/[slug]` (unlisted drafts, unchanged).
*Addressing — per-language slugs + the root KEY namespace* (contract: DESIGN.md "Addressing"): a post's resolvable slug set is the canonical folder slug (→ default language) plus any localized `slug:` in `fr.md`/`en.md` (→ that language). Every declared slug mints a page at the domain root — `app/[key]/page.js` — rendering the post via the same `PostArticle`; **the root form is canonical** (`blogCanonicalPath` in lib/blog.js): `/blog/<slug>` and language variants serve the same content with `alternates.canonical` pointing at `me.nech.pl/<slug>`. Drafts mint nothing at the root or `/blog` in prod — their only prod surface stays `/blog/d/<slug>-<token>`. Root keys are premium real estate: `lib/keys.js` gathers the reserved set at build time (top-level dirs under `app/` + legacy `pages/` + everything in `public/` + a hardcoded floor: blog, admin, api, _next, images, fonts, index) and `assertRootKeysAvailable` fails the build loudly on a colliding or twice-claimed key. Resolution accessors: `getBlogKeys(slug)` (key table), `resolveBlogKey(key)` → `{slug, lang}` or null, `getAllBlogKeys()` (root route's generateStaticParams).
Thin per-section wrappers (`lib/hydras.js`, `lib/poems.js`, `lib/talks.js`) just re-export from `@/lib/content`, binding a section name. Dynamic content pages (`app/blog/[slug]/page.js`, `app/poesie/[id]/page.js`, `app/hydra/[id]/page.js`) are async server components that pair `generateStaticParams()` + a `params`-driven fetch (`params` is a Promise — `await` it).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment