Files
planet/frontend/public/earth/js/brand.js
linkong 5bdb55f3f1
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
release: bump version to 0.74.0
2026-06-30 13:52:52 +08:00

172 lines
5.7 KiB
JavaScript

const DEFAULT_BRAND_LANGUAGE = "zh";
const EARTH_BRAND_API = "/api/v1/earth/brand";
const BRANDS = {
zh: {
ariaLabel: "智能星球计划品牌标识",
titleAlt: "智能星球计划",
logoSrc: "/earth/assets/brand/earth-logo.png",
titleSrc: "/earth/assets/brand/title-zh.png",
titleText: "智能星球计划",
subtitle: "现实层宇宙全息感知系统",
description: "卫星 · 海底光缆 · 算力基础设施",
},
en: {
ariaLabel: "Intelligent Planet Program brand banner",
titleAlt: "Intelligent Planet Program",
logoSrc: "/earth/assets/brand/earth-logo.png",
titleSrc: "/earth/assets/brand/title-en.png",
titleText: "Intelligent Planet Program",
subtitle: "Reality Layer Situational Awareness System",
description: "Satellites · Subsea Cables · Compute Infrastructure",
},
};
const DEFAULT_BRAND_TITLE_BY_VARIANT = {
zh: BRANDS.zh.titleSrc,
en: BRANDS.en.titleSrc,
};
const LOCALIZED_FIELD_NAMES = {
ariaLabel: ["aria_label", "ariaLabel"],
titleAlt: ["title_alt", "titleAlt"],
titleSrc: ["title_src", "titleSrc"],
titleText: ["title_text", "titleText"],
subtitle: ["subtitle"],
description: ["description"],
};
export function getDefaultBrandConfig(variant = DEFAULT_BRAND_LANGUAGE) {
return BRANDS[variant] ?? BRANDS[DEFAULT_BRAND_LANGUAGE];
}
function escapeHtml(value = "") {
return String(value)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function hasCjkText(value = "") {
return /[\u3400-\u9fff]/.test(String(value ?? ""));
}
function readConfigValue(config, keys = []) {
for (const key of keys) {
if (config[key] !== undefined && config[key] !== null && config[key] !== "") {
return config[key];
}
}
return undefined;
}
function readLocalizedConfigValue(config, fieldName, variant, fallback) {
const keys = LOCALIZED_FIELD_NAMES[fieldName] || [fieldName];
const localeSuffix = variant === "en" ? "en" : "zh";
const localeKeys = keys.flatMap((key) => [
`${key}_${localeSuffix}`,
`${key}${localeSuffix.charAt(0).toUpperCase()}${localeSuffix.slice(1)}`,
]);
const localized = readConfigValue(config, localeKeys);
if (localized !== undefined) return localized;
const generic = readConfigValue(config, keys);
return generic ?? fallback;
}
function normalizeBrandConfig(config = {}, variant = DEFAULT_BRAND_LANGUAGE) {
const defaults = getDefaultBrandConfig(variant);
const sourceTitleSrc = readLocalizedConfigValue(config, "titleSrc", variant, undefined);
const normalized = {
...defaults,
...config,
ariaLabel: readLocalizedConfigValue(config, "ariaLabel", variant, defaults.ariaLabel),
titleAlt: readLocalizedConfigValue(config, "titleAlt", variant, defaults.titleAlt),
logoSrc: config.logo_src ?? config.logoSrc ?? defaults.logoSrc,
titleSrc: sourceTitleSrc ?? defaults.titleSrc,
titleText: readLocalizedConfigValue(config, "titleText", variant, defaults.titleText),
subtitle: readLocalizedConfigValue(config, "subtitle", variant, defaults.subtitle),
description: readLocalizedConfigValue(config, "description", variant, defaults.description),
};
if (
variant === "en" &&
(
!sourceTitleSrc ||
sourceTitleSrc === DEFAULT_BRAND_TITLE_BY_VARIANT.zh ||
hasCjkText(normalized.titleText) ||
hasCjkText(normalized.titleAlt) ||
hasCjkText(normalized.ariaLabel)
)
) {
normalized.titleSrc = DEFAULT_BRAND_TITLE_BY_VARIANT.en;
normalized.titleAlt = defaults.titleAlt;
normalized.titleText = defaults.titleText;
normalized.ariaLabel = defaults.ariaLabel;
normalized.subtitle = defaults.subtitle;
normalized.description = defaults.description;
}
if (!normalized.titleText) normalized.titleText = defaults.titleText;
if (!normalized.ariaLabel) normalized.ariaLabel = normalized.titleText;
if (!normalized.titleAlt) normalized.titleAlt = normalized.titleText;
return normalized;
}
export function renderBrand(configOrVariant = DEFAULT_BRAND_LANGUAGE) {
const variant =
typeof configOrVariant === "string"
? configOrVariant
: configOrVariant.variant ?? DEFAULT_BRAND_LANGUAGE;
const config =
typeof configOrVariant === "string"
? normalizeBrandConfig({}, variant)
: normalizeBrandConfig(configOrVariant, variant);
const titleMarkup = config.titleSrc
? `
<img
class="earth-brand__title"
src="${escapeHtml(config.titleSrc)}"
alt="${escapeHtml(config.titleAlt)}"
fetchpriority="high"
loading="eager"
>
`
: `<div class="earth-brand__title-text">${escapeHtml(config.titleText)}</div>`;
return `
<div class="earth-brand earth-brand--${escapeHtml(variant)}" aria-label="${escapeHtml(config.ariaLabel)}">
<img
class="earth-brand__logo"
src="${escapeHtml(config.logoSrc)}"
alt=""
aria-hidden="true"
fetchpriority="high"
loading="eager"
>
<div class="earth-brand__copy">
${titleMarkup}
<div class="earth-brand__meta">
<span class="earth-brand__subtitle">${escapeHtml(config.subtitle)}</span>
<span class="earth-brand__description">${escapeHtml(config.description)}</span>
</div>
</div>
</div>
`.trim();
}
export function mountBrand(target, configOrVariant = DEFAULT_BRAND_LANGUAGE) {
if (!target) return;
target.innerHTML = renderBrand(configOrVariant);
}
export async function fetchEarthBrandConfig() {
const response = await fetch(EARTH_BRAND_API, { cache: "no-store" });
if (!response.ok) {
throw new Error(`Earth brand config request failed: ${response.status}`);
}
const payload = await response.json();
return payload?.brand ?? getDefaultBrandConfig();
}