LangSelect's scoped CSS landed on the shared store chunk, whose stylesheet the editor never loaded (only the public selector path injected it), so the editor's language selector rendered unstyled on untranslated pages. Collect editor stylesheets from the entry's imported chunks too (same traversal as the langselect assets). Also: hreflang alternates now skip languages disabled site-wide, and all selectors (page editor, structure tab, public selector) order languages the same way — primary first, then the lang tab's geographic grouping.
72 lines
2.9 KiB
JavaScript
72 lines
2.9 KiB
JavaScript
// Language helpers shared by the editors (the PageEditor language picker,
|
|
// the localization settings tab). Flags come from the country-flag-icons
|
|
// set, same as the analytics visitor cells.
|
|
import * as flagSvgs from 'country-flag-icons/string/3x2'
|
|
|
|
// The Seed-X reference translator's languages (scripts/translator.py) — the
|
|
// translation-target ceiling — each mapped to the language's home country
|
|
// flag (England for English, Portugal for Portuguese — not the most
|
|
// populous variant). Internal tags are the bare 2-letter base subtags; the
|
|
// translator decides the variant. A variant tag (en-US, pt-BR) is still a
|
|
// valid explicit selection for a future translator that distinguishes them
|
|
// — flagFor shows its own region then.
|
|
export const TRANSLATABLE = {
|
|
ar: 'EG', cs: 'CZ', da: 'DK', de: 'DE', el: 'GR', en: 'GB', es: 'ES',
|
|
fa: 'IR', fi: 'FI', fr: 'FR', hu: 'HU', id: 'ID', it: 'IT', ja: 'JP',
|
|
ko: 'KR', ms: 'MY', nl: 'NL', no: 'NO', pl: 'PL', pt: 'PT', ro: 'RO',
|
|
ru: 'RU', sv: 'SE', th: 'TH', tr: 'TR', uk: 'UA', vi: 'VN', zh: 'CN',
|
|
}
|
|
|
|
// The languages in geographic/cultural groups (the lang tab's flag grid
|
|
// lays them out one group per row, in this order): English with the
|
|
// Nordics, then Western/Central and Eastern Europe, Southern Europe with
|
|
// the Middle East, and Asia.
|
|
export const LANG_GROUPS = [
|
|
['en', 'nl', 'da', 'no', 'sv', 'fi', 'ru'],
|
|
['fr', 'de', 'pl', 'cs', 'hu', 'ro', 'uk'],
|
|
['es', 'pt', 'it', 'el', 'tr', 'ar', 'fa'],
|
|
['zh', 'ja', 'ko', 'vi', 'th', 'id', 'ms'],
|
|
]
|
|
|
|
const displayNames = new Intl.DisplayNames(['en'], { type: 'language' })
|
|
|
|
// Consistent menu ordering for language selectors: the geographic/cultural
|
|
// grouping above (similar languages sit together, and it does not vary with
|
|
// the display language the way alphabetical-by-name would). Tags outside
|
|
// the groups trail, ordered by tag. The primary language is not special
|
|
// here — callers put it first themselves.
|
|
const groupOrder = new Map(LANG_GROUPS.flat().map((c, i) => [c, i]))
|
|
export function langSort(codes) {
|
|
return [...codes].sort(
|
|
(a, b) =>
|
|
(groupOrder.get(a) ?? groupOrder.size) - (groupOrder.get(b) ?? groupOrder.size)
|
|
|| a.localeCompare(b),
|
|
)
|
|
}
|
|
|
|
// English display name for a language tag ("fi" -> "Finnish").
|
|
export function langName(tag) {
|
|
try {
|
|
return displayNames.of(tag) || tag
|
|
} catch {
|
|
return tag
|
|
}
|
|
}
|
|
|
|
// Flag SVG string for a language tag: an explicit region variant (en-US)
|
|
// gets its own region's flag; a bare base tag maps to the language's home
|
|
// country (en → GB, pt → PT); languages outside the list fall back to the
|
|
// tag's most likely region.
|
|
export function flagFor(tag) {
|
|
tag = tag || ''
|
|
if (!tag.includes('-')) {
|
|
const country = TRANSLATABLE[tag.split('-')[0].toLowerCase()]
|
|
if (country) return flagSvgs[country] || ''
|
|
}
|
|
try {
|
|
return flagSvgs[new Intl.Locale(tag).maximize().region] || ''
|
|
} catch {
|
|
return ''
|
|
}
|
|
}
|