feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat: add sitemap hreflang alternates for localized legal pages - #18

Open
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages
Open

feat: add sitemap hreflang alternates for localized legal pages#18
Addin wants to merge 1 commit into
fix/en-metadata-localizationfrom
feat/sitemap-hreflang-legal-pages

Conversation

@Addin

Copy link
Copy Markdown
Contributor

Closes sc-142 (Add sitemap hreflang alternates for localized legal pages)

Note

Stacked on #12 (fix/i18n-hreflang-all-pages). This branch is based on it; until #12 merges this PR shows both changesets. Once #12 lands in main, the diff here shrinks to the two files below.

Summary

Emit <xhtml:link rel="alternate" hreflang="..."> entries in the generated sitemap for the four localized legal pages, matching their <head> annotations exactly.

Problem

@astrojs/sitemap pairs hreflang alternates by stripping the locale prefix and grouping URLs with identical remaining paths. Legal slugs differ per locale (terminos vs terms, privacidad vs privacy), so /en/terms/ never paired with /terminos/ and all four legal URLs shipped with zero sitemap alternates — while their <head> (via Legal.astro) declared correct ones.

Fix

  • New src/lib/sitemap.ts: builds each legal page's alternate set from the existing legalSlugs map (single source of truth) plus the shared HREFLANG/DEFAULT_LOCALE consts, keyed by pathname.
  • astro.config.mjs: wires it via the integration's serialize hook. Items that already have built-in links (homepages) and pages without counterparts pass through untouched.

Before / after

Before: https://cotrasoft.co/terminos/ → no alternates.
After:

<url><loc>https://cotrasoft.co/terminos/</loc>
<xhtml:linkrel="alternate"hreflang="es-CO"href="https://cotrasoft.co/terminos/"/>
<xhtml:linkrel="alternate"hreflang="en-US"href="https://cotrasoft.co/en/terms/"/>
<xhtml:linkrel="alternate"hreflang="x-default"href="https://cotrasoft.co/terminos/"/>
</url>

Verification

  • Build output asserted programmatically: all 4 legal URLs carry complete bidirectional sets; homepages unchanged; /about/, /blog/ correctly have none (no English counterpart).
  • Sitemap sets verified 1:1 identical to the <head> annotations in the built HTML.
  • biome ci ./src green (pre-push hook); remaining warnings are pre-existing (sc-144).

@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 14c8782 to 377d367CompareAugust 24, 2026 17:38
@Addin
Addinforce-pushed the feat/sitemap-hreflang-legal-pages branch from 377d367 to 1c645fcCompareAugust 24, 2026 17:40
@Addin
Addin changed the base branch from main to fix/en-metadata-localizationAugust 24, 2026 18:09
@Addin

Copy link
Copy Markdown
ContributorAuthor

Rebased onto the sc-151 stack (stack #27: #25#26#21 → this) so the hreflang work composes with the entity-narrative changes. Conflicts resolved in consts.ts / BaseHead.astro / Home.astro (kept your HREFLANG centralization + default alternates; descriptions/titles now come from ENTITY_DESCRIPTION / SITE_TITLES). History squashed to a single commit since the old one contained merge commits referencing the since-removed ../i18n module. Verified: hreflang on every page, no bogus /en/ alternates for non-localized blog routes, sitemap legal alternates intact. This PR supersedes #12, which I've closed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.please ignore this .playwright-mcp folder created on use the skill

Comment threadabout-en.png

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also remove this images that is not part of the code.

Comment threadastro.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this code style part of the recent lint inclusion? if is possible can we make all this code styles part of another PR to not make this code hard to be reviewed?

@nathanredblur

Copy link
Copy Markdown
Collaborator

if you can, add a small unit test to validate the main problem that we try to solve on not including @astrojs/sitemap

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Addin@nathanredblur