SEO: templates, config, and the generators that feed them - #852
Merged
Conversation
Template and configuration half of #850, split out so it can be reviewed without the ~1,400 generated content files it implies. Metadata: - The description fallback in site_head.html tested branch order rather than the result, so widget-composed pages — whose `.Summary` is empty despite `.IsPage` being true — never reached the site-wide default. 58 of 119 real pages shipped an empty meta description; now 0. - Descriptions render Markdown before flattening, so `**bold**` and link syntax stop reaching search results. - Pages use a short `short_title` suffix instead of spending 61 of the ~60 displayed characters on the full organisation name. The home page still spells it out. - Organization JSON-LD pointed `image` at a 404 (`media_dir` prefixed onto a path already under `static/`) and omitted `sameAs`. Indexation: - Tags are free text on curated resources and ~74% are used exactly once, so ~1,500 near-empty archives dominated the sitemap. Terms below `tag_index_min_pages` are now `noindex, follow` and excluded from the sitemap, both reading one shared predicate so they cannot disagree. Sitemap: 4,631 -> 3,174 URLs. - resource.py drops placeholder tag entries ("", "-", ".") at the source. These were aggregating into a meaningless /tag/-/ archive. Fixing the generator rather than its output matters because content/curated_resources is deleted and rewritten by the daily data-processing workflow. Internationalisation: - ~1,000 glossary terms in German, Arabic, Turkish and Chinese were served as `lang="en-US"`, with Arabic rendering left-to-right. Language is resolved per page, so Arabic gets `lang="ar" dir="rtl"`. - The five glossaries are parallel directories rather than Hugo language sites, so `.Translations` is empty and no hreflang existed. Terms are grouped on `en_title` instead, which _create_glossaries.py now emits from the EN_title column it already reads. The content that carries the field follows in its own PR. Structured data and page weight: - BreadcrumbList extended from 11 cluster pages to 3,784. - Section landings emit CollectionPage + ItemList; they had none. - Added preconnect for cdnjs and Google Fonts; gated Leaflet to the pages that actually render a map. - Post banners fell back to an empty `alt`; now use the post title. The sitemap override writes its XML declaration through `safeHTML`; emitted as literal template text, Hugo escapes it to `<?xml` and the whole document stops parsing as XML. Not addressed: og:image remains a 4000x2250 WebP (needs a generated 1200x630 raster), og:locale still emits bare language tags where Open Graph wants language_TERRITORY, and highlight.js loads on every page. Co-Authored-By: Keegan Vaz <keegangeorgevaz@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
|
👍 All image files/references (if any) are in webp format, in line with our policy. |
Contributor
✅ Spell Check PassedNo spelling issues found when checking 13 changed file(s)! 🎉 |
Contributor
Author
|
✅ Staging Deployment Status This PR has been successfully deployed to staging as part of an aggregated deployment. Deployed at: 2026-08-05 14:25:18 UTC The staging site shows the combined state of all compatible open PRs. |
Contributor
Author
|
@richarddushime I believe this is ready to merge (and then #853 that is implied by it) |
richarddushime
approved these changes
Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Splits #850 into a reviewable template change (this PR) and the generated content it implies (#853). Original work by @Keegan-Vaz; this PR carries the same templates plus the generator fixes that make them stick.
Why the split
#850 was 1,450 files. Twelve of them were the actual change; the other 1,438 were generated output — and neither set of edits survived.
content/curated_resources/is deleted and rewritten from a Google Sheet by the dailydata-processingworkflow, so the 136 tag cleanups would have reverted within 24 hours.content/glossary/is deleted and rewritten by_create_glossaries.py, which was never taught to emiten_title, so the next regeneration would have silently dropped hreflang from all 1,300 glossary pages.Both are now fixed at the source.
What's here
Templates and config — metadata fallbacks, short titles, Markdown-free descriptions, Organization JSON-LD, thin-tag
noindex+ sitemap exclusion behind one shared predicate, per-page language resolution, glossary hreflang, BreadcrumbList, CollectionPage/ItemList, preconnect hints, per-page Leaflet, banneralt. See the commit message for the detail.content/resources/resource.py— drops placeholder tag entries ("","-",".") as the resources are generated. Verified against the live sheet: 0 placeholder entries remain, 135 rows gettags: [], 1,686 real terms and all other columns untouched.content/glossary/_create_glossaries.py— emitsen_titlefrom theEN_titlecolumn it already reads.Fixes found while reviewing #850
<?xml …?>declaration was written as literal template text; Hugo escapes it to<?xml, andxmllintrejects the whole document. That made the headline indexation fix strictly worse than the sitemap it replaced. Now emitted throughsafeHTML.layouts/404.htmlwas reverted to its pre-Fix 404 redirects appending 'undefined' (e.g. /replication_handbook) #851 state. Restored.Testing
Full
hugobuild, no warnings. Sitemap validates (xmllint), 3,174 URLs, down from 4,631. 0 empty meta descriptions, down from 58. BreadcrumbList on 3,784 pages with no double-emission on/clusters/. Arabic glossary pages render<html lang="ar" dir="rtl">./tag/-/is gone.Merge order
Either order works — hreflang no-ops until
en_titleexists in the content, so nothing breaks. #853 is stacked on this branch.Not addressed
og:imageis still a 4000x2250 WebP (needs a generated 1200x630 raster);og:localeemits bare language tags where Open Graph wantslanguage_TERRITORY; highlight.js still loads on every page. Thetag_index_min_pages = 5floor de-indexes ~1,500 tag archives — that's a content-strategy call worth a maintainer's explicit sign-off rather than something to inherit from a bug-fix PR.🤖 Generated with Claude Code