plan/react-rewrite #1

Merged
gnezim merged 138 commits from plan/react-rewrite into main 2026-04-15 12:21:16 +03:00
2 changed files with 103 additions and 0 deletions
Showing only changes of commit 8abe8acf70 - Show all commits
+71
View File
@@ -0,0 +1,71 @@
import { describe, expect, it } from "vitest";
import { buildHreflangSet } from "./hreflang.js";
describe("buildHreflangSet", () => {
const LANGUAGES = ["ru", "en", "es", "fr", "it", "ja", "ko", "zh", "de"] as const;
it("returns entries for all 9 languages plus x-default", () => {
const result = buildHreflangSet({
canonicalOrigin: "https://www.aeroflot.ru",
pathWithoutLocale: "/onlineboard/flight/SU100-2025-01-15",
});
expect(result).toHaveLength(10); // 9 languages + x-default
});
it("includes all 9 languages", () => {
const result = buildHreflangSet({
canonicalOrigin: "https://www.aeroflot.ru",
pathWithoutLocale: "/smoke",
});
const langs = result.map((entry) => entry.lang);
for (const lang of LANGUAGES) {
expect(langs).toContain(lang);
}
});
it("x-default points to the ru variant", () => {
const result = buildHreflangSet({
canonicalOrigin: "https://www.aeroflot.ru",
pathWithoutLocale: "/smoke",
});
const xDefault = result.find((entry) => entry.lang === "x-default");
expect(xDefault).toBeDefined();
expect(xDefault?.href).toBe("https://www.aeroflot.ru/ru/smoke");
});
it("builds correct href for each language", () => {
const result = buildHreflangSet({
canonicalOrigin: "https://www.aeroflot.ru",
pathWithoutLocale: "/onlineboard",
});
const en = result.find((entry) => entry.lang === "en");
expect(en?.href).toBe("https://www.aeroflot.ru/en/onlineboard");
const ja = result.find((entry) => entry.lang === "ja");
expect(ja?.href).toBe("https://www.aeroflot.ru/ja/onlineboard");
});
it("preserves paths with nested segments", () => {
const result = buildHreflangSet({
canonicalOrigin: "https://www.aeroflot.ru",
pathWithoutLocale: "/onlineboard/flight/SU100-2025-01-15",
});
const fr = result.find((entry) => entry.lang === "fr");
expect(fr?.href).toBe("https://www.aeroflot.ru/fr/onlineboard/flight/SU100-2025-01-15");
});
it("handles root path", () => {
const result = buildHreflangSet({
canonicalOrigin: "https://www.aeroflot.ru",
pathWithoutLocale: "",
});
const ru = result.find((entry) => entry.lang === "ru");
expect(ru?.href).toBe("https://www.aeroflot.ru/ru");
});
});
+32
View File
@@ -0,0 +1,32 @@
import type { Language } from "@/i18n/resolver";
const LANGUAGES: readonly Language[] = ["ru", "en", "es", "fr", "it", "ja", "ko", "zh", "de"];
const X_DEFAULT_LANGUAGE: Language = "ru";
export interface HreflangEntry {
lang: Language | "x-default";
href: string;
}
/**
* Builds the full set of reciprocal hreflang links for a given path.
* Returns 9 language entries + 1 x-default entry (pointing to ru).
*/
export function buildHreflangSet(args: {
canonicalOrigin: string;
pathWithoutLocale: string;
}): HreflangEntry[] {
const { canonicalOrigin, pathWithoutLocale } = args;
const entries: HreflangEntry[] = LANGUAGES.map((lang) => ({
lang,
href: `${canonicalOrigin}/${lang}${pathWithoutLocale}`,
}));
entries.push({
lang: "x-default",
href: `${canonicalOrigin}/${X_DEFAULT_LANGUAGE}${pathWithoutLocale}`,
});
return entries;
}