/*
Canonical slug generation strategies for locale-aware URL slug derivation (RFC-0915, DNA-88).
- Do not expose strategy classes directly — consumers use slugUrl() from slug-url.ts.
- RFC-0915: extracted from werkstatt-site/src/domain/geo/slug.ts as canonical slug strategies.
*/
import slugify from "@sindresorhus/slugify";
import CyrillicToTranslit from "cyrillic-to-translit-js";
export interface SlugStrategy {
slug(name: string): string;
}
interface CyrillicTranslit {
transform(value: string): string;
}
interface CyrillicTranslitConstructor {
new (options: { preset: "uk" }): CyrillicTranslit;
}
const germanReplacements: Array<[string, string]> = [
["ä", "ae"],
["ö", "oe"],
["ü", "ue"],
["ß", "ss"],
["Ä", "Ae"],
["Ö", "Oe"],
["Ü", "Ue"],
];
class GermanSlugStrategy implements SlugStrategy {
slug(name: string): string {
return slugify(name, { customReplacements: germanReplacements });
}
}
class UkrainianSlugStrategy implements SlugStrategy {
private readonly translit = new (CyrillicToTranslit as unknown as CyrillicTranslitConstructor)({
preset: "uk",
});
slug(name: string): string {
return slugify(this.translit.transform(name));
}
}
class DefaultSlugStrategy implements SlugStrategy {
slug(name: string): string {
return slugify(name);
}
}
const slugStrategies = new Map([
["de", new GermanSlugStrategy()],
["uk", new UkrainianSlugStrategy()],
]);
const defaultStrategy = new DefaultSlugStrategy();
export function resolveSlugStrategy(lang?: string): SlugStrategy {
return (lang ? slugStrategies.get(lang) : undefined) ?? defaultStrategy;
}