export function uniqueLocationParts(parts) { const seen = new Set(); return parts.map((part) => String(part || "").trim()).filter((part) => { if (!part) return false; const key = normalizeLocationName(part); if (seen.has(key)) return false; seen.add(key); return true; }); } export function locationHeading(city, lang) { const country = lang === "zh" ? city?.countryZh : city?.countryEn; const place = lang === "zh" ? city?.cityZh : city?.cityEn; return uniqueLocationParts([country, place]).join(" · "); } export function combinedLocationHeading(city) { if (hasCountryCityEcho(city)) { return uniqueLocationParts([ city?.countryZh || city?.cityZh, city?.countryEn || city?.cityEn, ]).join(" | "); } return uniqueLocationParts([ locationHeading(city, "zh"), locationHeading(city, "en"), ]).join(" | "); } export function bookshelfLocationLabel(city) { return uniqueLocationParts([ city?.countryEn || city?.countryZh, city?.cityEn || city?.cityZh, ]).join(" · "); } function hasCountryCityEcho(city) { return sameLocationName(city?.countryZh, city?.cityZh) || sameLocationName(city?.countryEn, city?.cityEn) || cityNameExtendsCountry(city?.countryZh, city?.cityZh); } function sameLocationName(left, right) { const normalizedLeft = normalizeLocationName(left); const normalizedRight = normalizeLocationName(right); return Boolean(normalizedLeft && normalizedRight && normalizedLeft === normalizedRight); } function normalizeLocationName(value) { return String(value || "") .trim() .toLocaleLowerCase() .replace(/\s+/g, " "); } function cityNameExtendsCountry(country, city) { const normalizedCountry = normalizeLocationName(country); const normalizedCity = normalizeLocationName(city); return Boolean(normalizedCountry && normalizedCity && normalizedCity === `${normalizedCountry}城`); }