2025-01-13 10:37:53 -08:00

133 lines
4.7 KiB
TypeScript

import assert from 'assert';
import { Router } from 'express';
import fetch from 'node-fetch';
import type { PronounExamplesData } from '../../locale/data.ts';
import { buildPronoun, buildPronounUsage, parsePronounGroups, parsePronouns } from '../../src/buildPronoun.ts';
import { Example, PronounLibrary } from '../../src/classes.ts';
import type { Pronoun } from '../../src/classes.ts';
import { buildList, handleErrorAsync } from '../../src/helpers.ts';
import { Translator } from '../../src/translator.ts';
import { loadTsv } from '../../src/tsv.ts';
import { loadSuml, loadSumlFromBase } from '../loader.ts';
import type { LocaleDescription } from '~/locale/locales.ts';
import { rootDir } from '~/server/paths.ts';
const translations = loadSuml('translations');
const baseTranslations = loadSumlFromBase('locale/_base/translations');
const translator = new Translator(translations, baseTranslations, global.config);
const pronouns = parsePronouns(global.config, loadTsv(`${rootDir}/data/pronouns/pronouns.tsv`));
const pronounGroups = parsePronounGroups(loadTsv(`${rootDir}/data/pronouns/pronounGroups.tsv`));
const pronounLibrary = new PronounLibrary(global.config, pronounGroups, pronouns);
const buildExample = (e: PronounExamplesData): Example => new Example(
Example.parse(e.singular),
Example.parse(e.plural || e.singular),
Example.parse(e.null || e.singular),
Example.parse(e.null_plural || e.plural || e.singular),
e.isHonorific,
);
const requestExamples = (r: string[] | undefined): PronounExamplesData[] => {
if (!r || !r.length) {
return loadTsv(`${rootDir}/data/pronouns/examples.tsv`);
}
return buildList(function* () {
for (const rr of r) {
const [singular, plural, isHonorific] = rr.split('|');
yield { singular, plural, isHonorific: !!isHonorific };
}
});
};
const addExamples = (pronoun: Pronoun, examples: PronounExamplesData[]): string[] => {
return buildList(function* () {
for (const example of examples) {
yield buildExample(example).format(pronoun);
}
});
};
interface PublicPronoun extends Omit<Pronoun, 'config' | 'hidden' | 'name'> {
examples: string[];
name: string;
}
const makePublicPronoun = (pronoun: Pronoun, examples: string[] | undefined): PublicPronoun => {
const publicPronoun: any = pronoun.clone();
publicPronoun.examples = addExamples(pronoun, requestExamples(examples));
publicPronoun.name = pronoun.name();
delete publicPronoun.config;
delete publicPronoun.hidden;
return publicPronoun;
};
const router = Router();
router.get('/pronouns', handleErrorAsync(async (req, res) => {
const publicPronouns: Record<string, PublicPronoun> = {};
for (const [name, pronoun] of Object.entries(pronouns)) {
if (pronoun.hidden) {
continue;
}
publicPronouns[name] = makePublicPronoun(pronoun, req.query.examples as string[] | undefined);
}
return res.json(publicPronouns);
}));
router.get('/pronouns/:pronoun*', handleErrorAsync(async (req, res) => {
const pronoun = buildPronoun(
pronouns,
req.params.pronoun + req.params[0],
global.config,
translator,
);
if (!pronoun) {
return res.status(404).json({ error: 'Not found' });
}
return res.json(makePublicPronoun(pronoun, req.query.examples as string[] | undefined));
}));
router.get('/pronouns-name/:pronoun*', handleErrorAsync(async (req, res) => {
const path = (req.params.pronoun + req.params[0]).split('/')
.map((p) => decodeURIComponent(p))
.join('/');
const usage = buildPronounUsage(pronounLibrary, path, global.config, translator);
if (usage === null) {
return res.status(404).json({ error: 'Not found' });
}
return res.json(usage.short.options.join(usage.short.glue));
}));
const getPronounName = defineCachedFunction(async (
locale: string,
pronoun: string,
locales: Record<string, LocaleDescription>,
) => {
const encodedPronoun = pronoun.split('/')
.map((p) => encodeURIComponent(p))
.join('/');
const res = await (await fetch(`${locales[locale].url}/api/pronouns-name/${encodedPronoun}`)).json();
if (typeof res === 'object' && res.error) {
return pronoun;
}
return res;
}, {
name: 'pronouns-name',
getKey: (locale, pronoun) => `${locale}:${pronoun}`,
maxAge: 24 * 60 * 60,
});
router.get('/remote-pronouns-name/:locale/:pronoun*', handleErrorAsync(async (req, res) => {
assert(Object.hasOwn(req.locales, req.params.locale));
const pronoun = req.params.pronoun + req.params[0];
return res.json(await getPronounName(req.params.locale, pronoun, req.locales));
}));
export default router;