fix(sitemap): only advertise hreflang locales with real translations #161

Merged
ellamizuki merged 1 commit from fix/sitemap-hreflang-partial-translations into main 2026-09-09 06:18:52 +00:00
ellamizuki commented 2026-09-09 06:14:13 +00:00 (Migrated from github.com)

Summary

Follow-up to #160: the sitemap still advertised hreflang variants for locales that have no translation of a given post.

Evidence / Reproduction

The pt-only post coisas-que-eu-gostaria-que-tivessem-me-dito-quando-entrei-na-faculdade-de-ti (exists in pt, es, ja, zh-CN, fr, de — not en) had a sitemap cluster announcing:

  • hreflang="en" -> https://yuricunha.com/blog/coisas-... (404, confirmed by curl)
  • x-default -> same 404 URL

Root cause

build_absolute_language_alternates({ slug }) was called without the real translation set, so it always expanded to i18n.locales — including locales where the post does not exist.

Fix

  • addLocalizedEntry accepts locales: the actual translation set of the entry.
  • Posts, snippets, projects, and pages now pass the locales where the slug really exists (postLocalesBySlug, projectLocalesBySlug, etc.).
  • Base routes still emit all locales (they exist everywhere).
  • x-default falls back to the first available locale when the default locale has no translation (instead of pointing at a 404).

Tests

  • seo-alternates.test.ts: partial-translation set never advertises a missing locale; x-default points at an existing variant.
  • sitemap.test.ts: clusters reflect the real translation set.

Validation

pnpm check:ci   # exit 0
pnpm test:unit  # 69 files, 287 tests passed
## Summary Follow-up to #160: the sitemap still advertised hreflang variants for locales that have no translation of a given post. ## Evidence / Reproduction The pt-only post `coisas-que-eu-gostaria-que-tivessem-me-dito-quando-entrei-na-faculdade-de-ti` (exists in pt, es, ja, zh-CN, fr, de — not en) had a sitemap cluster announcing: - `hreflang="en"` -> `https://yuricunha.com/blog/coisas-...` (404, confirmed by curl) - `x-default` -> same 404 URL ## Root cause `build_absolute_language_alternates({ slug })` was called without the real translation set, so it always expanded to `i18n.locales` — including locales where the post does not exist. ## Fix - `addLocalizedEntry` accepts `locales`: the actual translation set of the entry. - Posts, snippets, projects, and pages now pass the locales where the slug really exists (`postLocalesBySlug`, `projectLocalesBySlug`, etc.). - Base routes still emit all locales (they exist everywhere). - `x-default` falls back to the first available locale when the default locale has no translation (instead of pointing at a 404). ## Tests - `seo-alternates.test.ts`: partial-translation set never advertises a missing locale; `x-default` points at an existing variant. - `sitemap.test.ts`: clusters reflect the real translation set. ## Validation ``` pnpm check:ci # exit 0 pnpm test:unit # 69 files, 287 tests passed ```
isyuricunha (Migrated from github.com) reviewed 2026-09-09 06:14:13 +00:00
vercel[bot] commented 2026-09-09 06:14:19 +00:00 (Migrated from github.com)

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
website-web Ready Ready Preview Sep 9, 2026 6:18am UTC
1 Skipped Deployment
Project Deployment Actions Updated
website-docs Ignored Ignored Sep 9, 2026 6:18am UTC
[vc]: #EmbjLwJWVOjhwsx1uNP5FmVztnZKF+TMZmKb8nw+HEo=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ3ZWJzaXRlLXdlYiIsInByb2plY3RJZCI6InByal9iNnJGaEFBUEhJWFI3M2Nlb2RFMHl2bEJ3MzgyIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvd2ViIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3l1cmktY3VuaGFzLXByb2plY3RzL3dlYnNpdGUtd2ViL0d3MXp1a3hZUHFyQmFXdDh1MlNoMmhwWXJtdnMiLCJwcmV2aWV3VXJsIjoid2Vic2l0ZS13ZWItZ2l0LWZpeC1zaXRlbWFwLWhyZWZsYW4tNmIyNDkwLXl1cmktY3VuaGFzLXByb2plY3RzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoid2Vic2l0ZS13ZWItZ2l0LWZpeC1zaXRlbWFwLWhyZWZsYW4tNmIyNDkwLXl1cmktY3VuaGFzLXByb2plY3RzLnZlcmNlbC5hcHAifX0seyJuYW1lIjoid2Vic2l0ZS1kb2NzIiwicHJvamVjdElkIjoicHJqXzJlVTJzVE0waWRZNzByVGJzY0NDdEV0Nk1GT3ciLCJyb290RGlyZWN0b3J5IjoiYXBwcy9kb2NzIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20veXVyaS1jdW5oYXMtcHJvamVjdHMvd2Vic2l0ZS1kb2NzLzR1M21XZkUxZVc1SE56V0p6RHhaQ0dReHZTSHQiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCJ9XX0= The latest updates on your projects. Learn more about [Vercel for GitHub](https://vercel.link/github-learn-more). | Project | Deployment | Actions | Updated | | :--- | :----- | :------ | :------ | | <a href="https://vercel.com/yuri-cunhas-projects/website-web"><sup><img src="https://vercel.com/api/www/avatar?projectId=prj_b6rFhAAPHIXR73ceodE0yvlBw382&teamId=team_g087rqPZJa9s4DWPeZtQhxa9&s=32" width="16" height="16" align="middle" alt="" /></sup></a> [website-web](https://vercel.com/yuri-cunhas-projects/website-web) | ![Ready](https://vercel.com/static/status/ready.svg) [Ready](https://vercel.com/yuri-cunhas-projects/website-web/Gw1zukxYPqrBaWt8u2Sh2hpYrmvs) | [Preview](https://website-web-git-fix-sitemap-hreflan-6b2490-yuri-cunhas-projects.vercel.app) | <relative-time datetime="2026-09-09T06:18:24.091Z">Sep 9, 2026 6:18am UTC</relative-time> | <details><summary>1 Skipped Deployment</summary> | Project | Deployment | Actions | Updated | | :--- | :----- | :------ | :------ | | <a href="https://vercel.com/yuri-cunhas-projects/website-docs"><sup><img src="https://vercel.com/api/www/avatar?projectId=prj_2eU2sTM0idY70rTbscCCtEt6MFOw&teamId=team_g087rqPZJa9s4DWPeZtQhxa9&s=32" width="16" height="16" align="middle" alt="" /></sup></a> [website-docs](https://vercel.com/yuri-cunhas-projects/website-docs) | ![Ignored](https://vercel.com/static/status/canceled.svg) [Ignored](https://vercel.com/yuri-cunhas-projects/website-docs/4u3mWfE1eW5HNzWJzDxZCGQxvSHt) | | <relative-time datetime="2026-09-09T06:18:24.091Z">Sep 9, 2026 6:18am UTC</relative-time> | </details>
Sign in to join this conversation.
No description provided.