poe-i18n
Version:
i18n utility for Path of Exile
117 lines (104 loc) • 3.57 kB
text/typescript
import { inverseFactory } from '../localize/formatters';
import { asRegexp, matchesTranslation, Stat } from '../translate';
import {
Description,
Descriptions,
StatLocaleDatas,
Translation,
UnaryFormatter
} from '../types/StatDescription';
import { deterministicValueForMatcher } from '../util/symbolicStats';
import { getDescriptions } from './util';
export type Options = {
datas: StatLocaleDatas;
start_file: string;
};
/**
* finds every stat or list of stats that could produce this text with its values
*
* use {textToStatsSingle} if you just want the first match
* use {textToStatsArray} if you want the generator values as an array
*
* @param text the stat text
* @param options see type definition
*/
export default function* textToStats(
text: string,
options: Partial<Options> = {}
): IterableIterator<Stat[]> {
const { datas = {}, start_file = 'stat_descriptions' } = options;
for (const descriptions of getDescriptions(datas, start_file)) {
for (const description of Object.values(descriptions)) {
if (description.no_description) {
continue;
}
for (const translation of description.translations) {
const regexp = asRegexp(translation);
const match = regexp.match(text);
if (match !== null) {
const stats = description.stats.map((stat_id, i) => {
// arguments are 1-based
const arg_index = String(i + 1);
const matched_value = match[arg_index];
const matcher = translation.matchers[i];
let value: number = Number.NaN;
if (matched_value !== undefined) {
const formatter = translation.formatters
.filter((f): f is UnaryFormatter => typeof f !== 'string')
.find(({ arg }) => String(arg) === arg_index);
if (formatter === undefined) {
value = +matched_value;
} else {
value = inverseFactory(formatter.id)(matched_value);
if (Number.isNaN(value)) {
// otherwise the return value would be equal to the case
// where the value is not contained in the text
throw new Error('int parsing returned NaN');
}
}
} else if (typeof matcher === 'number') {
// value is not contained in the text
// guess (deterministically) a value for this instance
// that would have produced that translation
value = deterministicValueForMatcher(translation.matchers[i]);
}
return {
id: stat_id,
value
};
});
if (
matchesTranslation(translation, stats.map(({ value }) => value))
) {
yield stats;
}
}
}
}
}
}
/**
* @see {textToStats} as array
*
* @param text
* @param options
*/
export function textToStatsArray(text: string, options: Partial<Options> = {}) {
return Array.from(textToStats(text, options));
}
/**
* only first match of @see {textToStats} but throws if none was found
*
* @param text
* @param options
*/
export function textToStatsFirst(
text: string,
options: Partial<Options> = {}
): Stat[] {
const { done, value } = textToStats(text, options).next();
if (done) {
throw new Error('Could match a single stat');
}
return value;
}