103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
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];
|
|
} |