Internationalization (hreflang)
Use hreflang annotations to tell Google about alternate language versions of your pages. This helps serve the correct language version to users in different regions.
Basic Usage
Add an alternates array to any route:
typescript
import type { Route } from "@pyyupsk/vite-plugin-sitemap";
export default [
{
url: "/products",
alternates: [
{ hreflang: "en", href: "https://example.com/products" },
{ hreflang: "es", href: "https://example.com/es/productos" },
{ hreflang: "fr", href: "https://example.com/fr/produits" },
],
},
] satisfies Route[];Alternate Properties
typescript
interface Alternate {
hreflang: string;
href: string;
}hreflang
Language and optional region code:
typescript
// Language only (ISO 639-1)
{
hreflang: "en";
}
{
hreflang: "es";
}
{
hreflang: "fr";
}
// Language and region (ISO 639-1 + ISO 3166-1 Alpha 2)
{
hreflang: "en-US";
}
{
hreflang: "en-GB";
}
{
hreflang: "es-MX";
}
{
hreflang: "zh-TW";
}
// Default fallback
{
hreflang: "x-default";
}href
The URL of the alternate version (must be absolute):
typescript
{
href: "https://example.com/es/productos";
}Full Example
typescript
import type { Route } from "@pyyupsk/vite-plugin-sitemap";
const languages = ["en", "es", "fr", "de", "ja"] as const;
export default [
// English version
{
url: "https://example.com/products",
alternates: [
{ hreflang: "en", href: "https://example.com/products" },
{ hreflang: "es", href: "https://example.com/es/productos" },
{ hreflang: "fr", href: "https://example.com/fr/produits" },
{ hreflang: "de", href: "https://example.com/de/produkte" },
{ hreflang: "ja", href: "https://example.com/ja/products" },
{ hreflang: "x-default", href: "https://example.com/products" },
],
},
// Spanish version
{
url: "https://example.com/es/productos",
alternates: [
{ hreflang: "en", href: "https://example.com/products" },
{ hreflang: "es", href: "https://example.com/es/productos" },
{ hreflang: "fr", href: "https://example.com/fr/produits" },
{ hreflang: "de", href: "https://example.com/de/produkte" },
{ hreflang: "ja", href: "https://example.com/ja/products" },
{ hreflang: "x-default", href: "https://example.com/products" },
],
},
// ... other language versions
] satisfies Route[];Generated XML
xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://example.com/products</loc>
<xhtml:link rel="alternate" hreflang="en" href="https://example.com/products"/>
<xhtml:link rel="alternate" hreflang="es" href="https://example.com/es/productos"/>
<xhtml:link rel="alternate" hreflang="fr" href="https://example.com/fr/produits"/>
<xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/produkte"/>
<xhtml:link rel="alternate" hreflang="ja" href="https://example.com/ja/products"/>
<xhtml:link rel="alternate" hreflang="x-default" href="https://example.com/products"/>
</url>
</urlset>Dynamic Multi-Language Routes
Generate routes for all language versions automatically:
typescript
import type { Route } from "@pyyupsk/vite-plugin-sitemap";
const hostname = "https://example.com";
const languages = [
{ code: "en", prefix: "" },
{ code: "es", prefix: "/es" },
{ code: "fr", prefix: "/fr" },
{ code: "de", prefix: "/de" },
] as const;
const pages = [
{ path: "/products", translations: { es: "/productos", fr: "/produits", de: "/produkte" } },
{ path: "/about", translations: { es: "/acerca", fr: "/a-propos", de: "/uber-uns" } },
{ path: "/contact", translations: { es: "/contacto", fr: "/contact", de: "/kontakt" } },
];
function getLocalizedPath(page: (typeof pages)[0], lang: (typeof languages)[0]): string {
if (lang.code === "en") return page.path;
return page.translations[lang.code as keyof typeof page.translations] || page.path;
}
function generateAlternates(page: (typeof pages)[0]) {
return [
...languages.map((lang) => ({
hreflang: lang.code,
href: `${hostname}${lang.prefix}${getLocalizedPath(page, lang)}`,
})),
{ hreflang: "x-default", href: `${hostname}${page.path}` },
];
}
export default languages.flatMap((lang) =>
pages.map((page) => ({
url: `${lang.prefix}${getLocalizedPath(page, lang)}`,
alternates: generateAlternates(page),
})),
) satisfies Route[];Region-Specific Variants
For content that varies by region within the same language:
typescript
import type { Route } from "@pyyupsk/vite-plugin-sitemap";
export default [
{
url: "/pricing",
alternates: [
// US English (default)
{ hreflang: "en-US", href: "https://example.com/pricing" },
// UK English (different prices/currency)
{ hreflang: "en-GB", href: "https://example.co.uk/pricing" },
// Australian English
{ hreflang: "en-AU", href: "https://example.com.au/pricing" },
// Canadian English
{ hreflang: "en-CA", href: "https://example.ca/pricing" },
// Canadian French
{ hreflang: "fr-CA", href: "https://example.ca/fr/tarification" },
// Default fallback
{ hreflang: "x-default", href: "https://example.com/pricing" },
],
},
] satisfies Route[];Using with CMS Data
typescript
import type { Route } from "@pyyupsk/vite-plugin-sitemap";
interface Page {
slug: string;
locale: string;
translations: Array<{ locale: string; slug: string }>;
}
export default async function getRoutes(): Promise<Route[]> {
const pages: Page[] = await fetch("https://cms.example.com/pages").then((r) => r.json());
return pages.map((page) => ({
url: `/${page.locale}/${page.slug}`,
alternates: [
{ hreflang: page.locale, href: `https://example.com/${page.locale}/${page.slug}` },
...page.translations.map((t) => ({
hreflang: t.locale,
href: `https://example.com/${t.locale}/${t.slug}`,
})),
{ hreflang: "x-default", href: `https://example.com/en/${page.slug}` },
],
}));
}Best Practices
- Include all alternates - Every language version should list all other versions
- Self-reference - Include the current page in its own alternates list
- Use x-default - Provide a fallback for users whose language isn't listed
- Consistent URLs - Use the same URL structure across languages
- Bidirectional links - If page A links to page B, page B must link back to A
- Absolute URLs - All href values must be absolute URLs
Common Language Codes
| Code | Language |
|---|---|
en | English |
es | Spanish |
fr | French |
de | German |
it | Italian |
pt | Portuguese |
ja | Japanese |
zh | Chinese |
ko | Korean |
ar | Arabic |
Common Region Codes
| Code | Region |
|---|---|
en-US | English (United States) |
en-GB | English (United Kingdom) |
es-ES | Spanish (Spain) |
es-MX | Spanish (Mexico) |
pt-BR | Portuguese (Brazil) |
zh-CN | Chinese (Simplified) |
zh-TW | Chinese (Traditional) |