Skip to content

Commit 43d6f94

Browse files
committed
fix: update sitemap hreflang format to iso-639
1 parent f13aedb commit 43d6f94

File tree

9 files changed

+221
-18
lines changed

9 files changed

+221
-18
lines changed

.changeset/happy-falcons-act.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@blinkk/root': patch
3+
---
4+
5+
fix: update sitemap hreflang format to iso-639

packages/root/src/cli/build.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -399,15 +399,13 @@ export async function build(rootProjectDir?: string, options?: BuildOptions) {
399399
sitemapXmlItems.push('<url>');
400400
sitemapXmlItems.push(` <loc>${domain}${urlPath}</loc>`);
401401
if (sitemapItem.alts) {
402-
Object.entries(sitemapItem.alts).forEach(
403-
([altLocale, altUrlPath]) => {
404-
if (sitemapItem.locale !== altLocale) {
405-
sitemapXmlItems.push(
406-
` <xhtml:link rel="alternate" hreflang="${altLocale}" href="${domain}${altUrlPath}" />`
407-
);
408-
}
402+
Object.entries(sitemapItem.alts).forEach(([altLocale, item]) => {
403+
if (sitemapItem.locale !== altLocale) {
404+
sitemapXmlItems.push(
405+
` <xhtml:link rel="alternate" hreflang="${item.hrefLang}" href="${domain}${item.urlPath}" />`
406+
);
409407
}
410-
);
408+
});
411409
}
412410
sitemapXmlItems.push('</url>');
413411
}

packages/root/src/core/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,9 @@ export interface SitemapItem {
231231
route: Route;
232232
params: Record<string, string>;
233233
locale: string;
234+
hrefLang: string;
234235
/**
235236
* Hreflang alts.
236237
*/
237-
alts: Record<string, string>;
238+
alts: Record<string, {hrefLang: string; urlPath: string}>;
238239
}

packages/root/src/render/render.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '../core/types.js';
3131
import type {ElementGraph} from '../node/element-graph.js';
3232
import {parseTagNames} from '../utils/elements.js';
33+
import {toHrefLang} from '../utils/i18n.js';
3334
import {AssetMap} from './asset-map/asset-map.js';
3435
import {htmlMinify} from './html-minify.js';
3536
import {htmlPretty} from './html-pretty.js';
@@ -386,29 +387,38 @@ export class Renderer {
386387

387388
async getSitemap(): Promise<Sitemap> {
388389
const sitemap: Sitemap = {};
389-
const sitemapItemAlts: Record<string, Record<string, string>> = {};
390+
const sitemapItemAlts: Record<
391+
string,
392+
Record<string, {hrefLang: string; urlPath: string}>
393+
> = {};
390394
const trailingSlash = this.rootConfig.server?.trailingSlash || false;
391395

392396
await this.router.walk(async (urlPath: string, route: Route) => {
393397
const routePaths = await this.router.getAllPathsForRoute(urlPath, route);
394398
routePaths.forEach((routePath) => {
395399
const routeLocale = route.isDefaultLocale ? 'x-default' : route.locale;
400+
const hrefLang = route.isDefaultLocale
401+
? 'x-default'
402+
: toHrefLang(route.locale);
396403
const defaultUrlPath = normalizeUrlPath(
397404
replaceParams(route.routePath, routePath.params),
398405
{trailingSlash: trailingSlash}
399406
);
400407
if (!sitemapItemAlts[defaultUrlPath]) {
401408
sitemapItemAlts[defaultUrlPath] = {};
402409
}
403-
sitemapItemAlts[defaultUrlPath][routeLocale] = normalizeUrlPath(
404-
replaceParams(urlPath, routePath.params),
405-
{trailingSlash: trailingSlash}
406-
);
410+
sitemapItemAlts[defaultUrlPath][routeLocale] = {
411+
hrefLang: hrefLang,
412+
urlPath: normalizeUrlPath(replaceParams(urlPath, routePath.params), {
413+
trailingSlash: trailingSlash,
414+
}),
415+
};
407416
const sitemapItem: SitemapItem = {
408417
urlPath: routePath.urlPath,
409418
route,
410419
params: routePath.params,
411420
locale: routeLocale,
421+
hrefLang: hrefLang,
412422
alts: sitemapItemAlts[defaultUrlPath],
413423
};
414424
sitemap[routePath.urlPath] = sitemapItem;
@@ -423,7 +433,8 @@ export class Renderer {
423433
.forEach((urlPath: string) => {
424434
// console.log(urlPath);
425435
const sitemapItem = sitemap[urlPath];
426-
const orderedAlts: Record<string, string> = {};
436+
const orderedAlts: Record<string, {hrefLang: string; urlPath: string}> =
437+
{};
427438
Object.keys(sitemapItem.alts)
428439
.sort(sortLocales)
429440
.forEach((locale) => {

packages/root/src/utils/i18n.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Converts a locale (e.g. `fr_CA`) to an ISO-639 href language code (e.g.
3+
* `fr-CA`). This can be used tags like `<html lang="">` and
4+
* `<link rel="alternate" hreflang="">`.
5+
*
6+
* https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
7+
*/
8+
export function toHrefLang(locale: string): string {
9+
if (locale === 'es_419') {
10+
return 'es-419';
11+
}
12+
if (locale.startsWith('ALL_')) {
13+
// Convert `ALL_xx` to `en-xx`. It is currently presumed that whenever
14+
// `ALL_xx` is used, the content may be untranslated but shows
15+
// country-specific information.
16+
const regionCode = locale.split('_')[1];
17+
if (regionCode === 'uk') {
18+
return 'en-GB';
19+
}
20+
return `en-${regionCode.toUpperCase()}`;
21+
}
22+
let hrefLang = locale.replace('_ALL', '').replace('_', '-');
23+
if (hrefLang.includes('-')) {
24+
// Capitalize the countryCode, e.g. `en-US`.
25+
const [langCode, countryCode] = hrefLang.split('-');
26+
hrefLang = `${langCode}-${countryCode.toUpperCase()}`;
27+
}
28+
return hrefLang;
29+
}

packages/root/test/fixtures/sitemap/root.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export default {
33
domain: 'https://www.example.com',
44
i18n: {
55
urlPath: '/intl/[path]',
6-
locales: ['en', 'de', 'fr'],
6+
locales: ['de', 'en', 'en_ca', 'en_gb', 'fr', 'fr_ca'],
77
},
88
server: {
99
trailingSlash: true,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <h1>Not found!</h1>;
3+
}

0 commit comments

Comments
 (0)