Initial web

This commit is contained in:
Caesar2011
2026-05-17 19:55:53 +02:00
parent 6e3499812e
commit 20ed6ee9fb
58 changed files with 8541 additions and 0 deletions

103
web/lib/localization.ts Normal file
View File

@@ -0,0 +1,103 @@
export type LocaleMap = Map<string, string>;
export type ReverseMap = Map<string, string>;
/** Parses a 3-column CSV (section, item_key, localized_name). */
export function parseCsv(text: string): LocaleMap {
const map: LocaleMap = new Map();
const lines = text.split(/\r?\n/).slice(1);
for (const line of lines) {
if (!line.trim()) continue;
const cols = splitCsvLine(line);
if (cols.length >= 3) map.set(cols[1], cols[2]);
}
return map;
}
function splitCsvLine(line: string): string[] {
const cols: string[] = [];
let cur = '';
let inQuote = false;
for (let i = 0; i < line.length; i++) {
const ch = line[i];
if (ch === '"') {
if (inQuote && line[i + 1] === '"') { cur += '"'; i++; }
else inQuote = !inQuote;
} else if (ch === ',' && !inQuote) {
cols.push(cur);
cur = '';
} else {
cur += ch;
}
}
cols.push(cur);
return cols;
}
declare global { var __localeCache: LocaleMap | undefined; }
async function loadCsv(path: string): Promise<LocaleMap> {
try {
const res = await fetch(path);
return res.ok ? parseCsv(await res.text()) : new Map();
} catch {
return new Map();
}
}
/**
* Fetches and merges DE + EN locale CSVs.
* Result is cached for the lifetime of the page.
*/
export async function getLocaleMap(): Promise<LocaleMap> {
if (globalThis.__localeCache) return globalThis.__localeCache;
const [en, de] = await Promise.all([
loadCsv('/factorio_english_items.csv'),
loadCsv('/factorio_german_items.csv'),
]);
const merged: LocaleMap = new Map([...en, ...de]);
globalThis.__localeCache = merged;
return merged;
}
/** Resolves an `item_key` to its localized display name, falling back to the key itself. */
export function resolveName(key: string, map: LocaleMap): string {
return map.get(key) ?? key;
}
/**
* Builds a reverse lookup: lowercased localized name → item_key.
* Used for normalizing user input back to raw keys.
*/
export function buildReverseMap(map: LocaleMap): ReverseMap {
const rev: ReverseMap = new Map();
for (const [key, name] of map) {
rev.set(name.toLowerCase(), key);
}
return rev;
}
/**
* Resolves a single user-typed token (localized name or raw key) to a raw item_key.
* Falls back to the input itself if no match is found.
*/
export function resolveKey(input: string, rev: ReverseMap): string {
return rev.get(input.toLowerCase()) ?? input;
}
/**
* Applies a regex pattern against both raw item_keys and localized names,
* returning the union of all matching raw item_keys.
*/
export function matchKeys(pattern: string, map: LocaleMap): string[] {
let re: RegExp;
try {
re = new RegExp(pattern, 'i');
} catch {
return [];
}
const result = new Set<string>();
for (const [key, name] of map) {
if (re.test(key) || re.test(name)) result.add(key);
}
return [...result];
}