buildHreflangAlternates
Builds the hreflang links that tell search engines the same page exists in each store language, plus an x-default.
import { buildHreflangAlternates } from '@salla.sa/twilight-theme-engine/utils/baseHead';In plain words
A multilingual store serves the same page at /ar/cart and /en/cart. Search engines should know these are one page in two languages, not two pages. buildHreflangAlternates(settings, path) returns one { hreflang, href } per store language, plus x-default for visitors in other languages, ready for alternateLanguages in a HeadDescriptor.
Signature
function buildHreflangAlternates(
settings: StoreContext | null | undefined,
path?: string // a router path or a full URL; a two-letter first segment is dropped
): Array<{ hreflang: string; href: string }> | undefinedTry it live
x-default | https://demostore.salla.sa/ar/cart |
ar | https://demostore.salla.sa/ar/cart |
en | https://demostore.salla.sa/en/cart |
import { buildHreflangAlternates } from '@salla.sa/twilight-theme-engine/utils/baseHead';
import type { HeadDescriptor } from '@salla.sa/twilight-theme-engine/utils/head';
import type { TwilightContext } from '@salla.sa/twilight-theme-engine/tanstack';
// A route module's head(): ctx.location.pathname is '/ar/cart' here.
export function head(ctx: TwilightContext): HeadDescriptor {
return {
title: 'Lookbook',
alternateLanguages: buildHreflangAlternates(ctx.settings, ctx.location.pathname),
};
}
Example
import type { TwilightContext } from '@salla.sa/twilight-theme-engine/tanstack';
import { buildHreflangAlternates } from '@salla.sa/twilight-theme-engine/utils/baseHead';
import type { HeadDescriptor } from '@salla.sa/twilight-theme-engine/utils/head';
// A route module's head(ctx, data), as every engine route writes it.
export function head(ctx: TwilightContext): HeadDescriptor {
return {
title: 'Lookbook',
alternateLanguages: buildHreflangAlternates(ctx.settings, ctx.location.pathname),
};
}
How it behaves
Every engine route module's
head()calls it withctx.location.pathname; the product listing passes the category or tag URL when there is one.A full URL is cut to its pathname. The first segment is dropped when it has exactly two characters, and the rest is appended to
<origin of store.url>/<language code>. The root path gives…/ar, with no trailing slash.x-defaultpoints at thearversion when the store has Arabic, otherwise at its first language.Languages come from
settings.languages. Under TanStack each item becomes<link rel="alternate" hrefLang=… href=…>.
Gotchas
It returns
undefinedwhen the store lists no languages.Any two-character first segment is treated as a locale, not only real ones:
/tv/p1builds…/ar/p1.Only the origin of
store.urlis used. For a store on a shared host, router paths lose the username: on the demo store/ar/cartgiveshttps://demostore.salla.sa/en/cart, an address of no store. A full URL from the API keeps the username, because it is still in the path.docs/03-routing-system.md and docs/07-data-types.md call it as
buildHreflangAlternates({ store, canonicalUrl }). The real signature is(settings, path).
Related
Builds the store-wide head defaults (title, description, canonical, Open Graph, Twitter card) from the store settings; the root route already applies them.
HeadDescriptorThe plain object a route's head() returns: title, description, Open Graph and Twitter cards, hreflang links, JSON-LD and extra tags.
Source and docs
- Engine source:
packages/theme-engine/src/utils/baseHead.ts