Follow-up to #31, which shipped in 0.7.0 and works exactly as requested — thanks. Verified against nacebel.codes:
hreflangAlternates({ baseUrl, locales: ["en","nl","fr","de"], strategy: "prefix",
defaultLocale: "en", prefixDefaultLocale: true, hrefLangTags: {…} }, "/en/about").languages
// { en: …/en/about, nl-BE: …/nl/about, fr-BE: …/fr/about, de: …/de/about, x-default: …/en/about }
That is byte-identical to the hand-rolled helper it would replace, and the new languages return field drops straight into Metadata.alternates.languages. For pages whose path is the same in every locale, this is now a clean swap.
The remaining gap
hreflangAlternates derives every alternate from one pathname by swapping the locale prefix, so the path after the prefix is assumed identical across locales. Sites that translate the slug can't use it:
/en/01/crop-and-animal-production-hunting-and-related-service-activities
/fr/01/culture-et-production-animale-chasse-et-services-annexes
/nl/01/teelt-van-gewassen-en-veeteelt-jacht-en-diensten-in-verband-met-deze-activiteiten
Feeding the English path in yields …/fr/01/crop-and-animal-production-hunting-and-related-service-activities for French — a URL our proxy 308-redirects to the canonical localized slug. Annotating hreflang with redirecting URLs is the exact failure mode prefixDefaultLocale was added to avoid, so we kept our own helper for all page types rather than split the invariant across two mechanisms by page type. This affects ~13k of the site's ~13k content pages.
Suggestion
Accept a per-locale path resolver alongside the current string form:
export declare function hreflangAlternates(
config: HreflangConfig,
pathname: string | ((locale: string) => string),
): HreflangAlternates;
With a function, the prefix/prefixDefaultLocale logic is bypassed and each locale's URL is absoluteUrl(resolver(locale), baseUrl); x-default uses resolver(defaultLocale) and the canonical uses resolver(currentLocale). currentLocale would have to be passed explicitly, since it can no longer be sniffed from the pathname — worth throwing on that rather than guessing, in the spirit of the existing guards.
That would cover both shapes with one entry point, and <HreflangLinks> could take the same union.
Follow-up to #31, which shipped in 0.7.0 and works exactly as requested — thanks. Verified against nacebel.codes:
That is byte-identical to the hand-rolled helper it would replace, and the new
languagesreturn field drops straight intoMetadata.alternates.languages. For pages whose path is the same in every locale, this is now a clean swap.The remaining gap
hreflangAlternatesderives every alternate from onepathnameby swapping the locale prefix, so the path after the prefix is assumed identical across locales. Sites that translate the slug can't use it:Feeding the English path in yields
…/fr/01/crop-and-animal-production-hunting-and-related-service-activitiesfor French — a URL our proxy 308-redirects to the canonical localized slug. Annotating hreflang with redirecting URLs is the exact failure modeprefixDefaultLocalewas added to avoid, so we kept our own helper for all page types rather than split the invariant across two mechanisms by page type. This affects ~13k of the site's ~13k content pages.Suggestion
Accept a per-locale path resolver alongside the current string form:
With a function, the prefix/
prefixDefaultLocalelogic is bypassed and each locale's URL isabsoluteUrl(resolver(locale), baseUrl);x-defaultusesresolver(defaultLocale)and the canonical usesresolver(currentLocale).currentLocalewould have to be passed explicitly, since it can no longer be sniffed from the pathname — worth throwing on that rather than guessing, in the spirit of the existing guards.That would cover both shapes with one entry point, and
<HreflangLinks>could take the same union.