spotters: most-active-in sort with country/region selector, collection tracking (seen marks, progress, filters), refresh scripts score activity and report new NL entries
Some checks failed
Deploy production / deploy (push) Has been cancelled

This commit is contained in:
2026-10-25 13:24:37 +00:00
parent 3f294df337
commit 253cd69424
58 changed files with 2151 additions and 72 deletions

View File

@@ -0,0 +1,172 @@
// Country name (as found in Wikidata labels, OpenSky, the train/plane/car data) -> lowercase
// ISO 3166-1 alpha-2 code, for scripts/spotters-activity.ts. Historic states map to their
// main successor (Deutsches Reich -> de, Soviet Union -> ru, Yugoslavia -> rs).
const ALIASES: Record<string, string> = {
'united states of america': 'us',
usa: 'us',
'u.s.': 'us',
'united states': 'us',
"people's republic of china": 'cn',
'republic of china': 'tw',
'taiwan (republic of china)': 'tw',
'taiwan under japanese rule': 'tw',
manchukuo: 'cn',
'hong kong': 'hk',
macau: 'mo',
macao: 'mo',
'russian federation': 'ru',
'soviet union': 'ru',
ussr: 'ru',
'soviet union (russia since 1991)': 'ru',
'russian empire': 'ru',
'russian sfsr': 'ru',
'deutsches reich': 'de',
'german reich': 'de',
'german empire': 'de',
'nazi germany': 'de',
'east germany': 'de',
'west germany': 'de',
'weimar republic': 'de',
'weimarer republik': 'de',
'deutsches kaiserreich': 'de',
'allied-occupied germany': 'de',
'grand duchy of baden': 'de',
'territory of the saar basin': 'de',
'saar protectorate': 'de',
'kingdom of bavaria': 'de',
'kingdom of prussia': 'de',
'kingdom of saxony': 'de',
'kingdom of württemberg': 'de',
'german reich (nazi period) deutsches reich': 'de',
germa: 'de',
czechoslovakia: 'cz',
'first czechoslovak republic': 'cz',
'czech republic': 'cz',
'protectorate of bohemia and moravia': 'cz',
'czechoslovak socialist republic': 'cz',
'korea under japanese rule': 'kr',
'korean empire': 'kr',
'south korea': 'kr',
'north korea': 'kp',
'republic of korea': 'kr',
"democratic people's republic of korea": 'kp',
'irish free state': 'ie',
'republic of ireland': 'ie',
yugoslavia: 'rs',
'kingdom of yugoslavia': 'rs',
'socialist federal republic of yugoslavia': 'rs',
'federal republic of yugoslavia': 'rs',
'serbia and montenegro': 'rs',
'empire of japan': 'jp',
'kingdom of italy': 'it',
'austria–hungary': 'at',
'austria-hungary': 'at',
cisleithania: 'at',
'first republic of austria': 'at',
'austrian empire': 'at',
'colony of natal': 'za',
'south african republic': 'za',
'orange free state': 'za',
'cape colony': 'za',
'union of south africa': 'za',
'dutch east indies': 'id',
'kingdom of the netherlands': 'nl',
netherlands: 'nl',
holland: 'nl',
england: 'gb',
scotland: 'gb',
wales: 'gb',
'northern ireland': 'gb',
'great britain': 'gb',
'united kingdom of great britain and ireland': 'gb',
'united kingdom': 'gb',
uk: 'gb',
tanganyika: 'tz',
'mandatory palestine': 'il',
'second polish republic': 'pl',
"polish people's republic": 'pl',
'polska rzeczpospolita ludowa': 'pl',
'second spanish republic': 'es',
'francoist spain': 'es',
'kingdom of romania': 'ro',
"ukrainian people's republic": 'ua',
'ukrainian ssr': 'ua',
turkey: 'tr',
türkiye: 'tr',
'ottoman empire': 'tr',
'kingdom of hungary': 'hu',
'kingdom of greece': 'gr',
'kingdom of bulgaria': 'bg',
'british raj': 'in',
'dominion of india': 'in',
ceylon: 'lk',
burma: 'mm',
'french third republic': 'fr',
'vichy france': 'fr',
'french fourth republic': 'fr',
'kingdom of egypt': 'eg',
rhodesia: 'zw',
'southern rhodesia': 'zw',
'northern rhodesia': 'zm',
'belgian congo': 'cd',
zaire: 'cd',
swaziland: 'sz',
eswatini: 'sz',
persia: 'ir',
iran: 'ir',
syria: 'sy',
vietnam: 'vn',
laos: 'la',
bolivia: 'bo',
venezuela: 've',
tanzania: 'tz',
moldova: 'md',
macedonia: 'mk',
'north macedonia': 'mk',
'bosnia and herzegovina': 'ba',
'ivory coast': 'ci',
"côte d'ivoire": 'ci',
'cape verde': 'cv',
'east timor': 'tl',
kosovo: 'xk',
'isle of man': 'im',
jersey: 'je',
guernsey: 'gg',
'state of palestine': 'ps',
brunei: 'bn',
'republic of the congo': 'cg',
'democratic republic of the congo': 'cd',
'the gambia': 'gm',
'the bahamas': 'bs',
'federated states of micronesia': 'fm',
'vatican city': 'va',
czechia: 'cz'
};
let byName: Map<string, string> | null = null;
function table() {
if (byName) return byName;
byName = new Map();
const dn = new Intl.DisplayNames(['en'], { type: 'region' });
for (let a = 65; a <= 90; a++)
for (let b = 65; b <= 90; b++) {
const code = String.fromCharCode(a, b);
try {
const name = dn.of(code);
if (name && name !== code) byName.set(name.toLowerCase(), code.toLowerCase());
} catch {
/* not a region */
}
}
for (const [k, v] of Object.entries(ALIASES)) byName.set(k, v);
return byName;
}
/** Country label -> ISO2 (lowercase), or null when unknown / multinational. */
export function countryCode(name: string | null | undefined): string | null {
if (!name) return null;
const key = name.trim().toLowerCase().replace(/\s+/g, ' ');
return table().get(key) ?? table().get(key.replace(/^the /, '')) ?? null;
}