mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-24 05:05:20 -04:00
feat(rewrite): Start implementing name algorithm
This commit is contained in:
parent
29fe79bdf7
commit
bc2abe50fa
@ -87,8 +87,8 @@ export function isLocaleActive(localeCode: string): boolean {
|
|||||||
|
|
||||||
export interface Pronoun {
|
export interface Pronoun {
|
||||||
keys: Array<string>;
|
keys: Array<string>;
|
||||||
description: string;
|
description?: string;
|
||||||
history: string;
|
history?: string;
|
||||||
source?: string;
|
source?: string;
|
||||||
isNormative: boolean;
|
isNormative: boolean;
|
||||||
isPlural: boolean;
|
isPlural: boolean;
|
||||||
@ -99,6 +99,39 @@ export interface Pronoun {
|
|||||||
smallForm?: string;
|
smallForm?: string;
|
||||||
|
|
||||||
forms: Record<string, { written: string; pronounced: string }>;
|
forms: Record<string, { written: string; pronounced: string }>;
|
||||||
|
|
||||||
|
canonicalName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsePronounFromParts(
|
||||||
|
parts: Array<string>,
|
||||||
|
forms: Array<string>
|
||||||
|
): Pronoun | null {
|
||||||
|
if (parts.length !== forms.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pronounForms: Pronoun["forms"] = {};
|
||||||
|
for (let i = 0; i < forms.length; i++) {
|
||||||
|
const form = parts[i].split("|");
|
||||||
|
pronounForms[forms[i]] = {
|
||||||
|
written: form[0],
|
||||||
|
pronounced: form[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
forms: pronounForms,
|
||||||
|
isNormative: false,
|
||||||
|
keys: [],
|
||||||
|
isPronounceable: false,
|
||||||
|
isPlural: false,
|
||||||
|
isPluralHonorific: false,
|
||||||
|
canonicalName: `${parts[0]}/${parts[1]}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function parsePronounFromString(s: string, forms: Array<string>) {
|
||||||
|
return parsePronounFromParts(s.split("/"), forms);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PronounExample {
|
export interface PronounExample {
|
||||||
@ -162,7 +195,7 @@ export class Locale {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _config: unknown | null = null;
|
private _config: any | null = null;
|
||||||
private _translations: unknown | null = null;
|
private _translations: unknown | null = null;
|
||||||
private _pronouns: Array<Pronoun> = [];
|
private _pronouns: Array<Pronoun> = [];
|
||||||
private _pronounsByAlias: Record<string, number> = {};
|
private _pronounsByAlias: Record<string, number> = {};
|
||||||
@ -261,6 +294,7 @@ export class Locale {
|
|||||||
for (const key of obj.keys) {
|
for (const key of obj.keys) {
|
||||||
this._pronounsByAlias[key] = i;
|
this._pronounsByAlias[key] = i;
|
||||||
}
|
}
|
||||||
|
obj.canonicalName = obj.keys[0];
|
||||||
}
|
}
|
||||||
const examples = await this.readFile(
|
const examples = await this.readFile(
|
||||||
["pronouns/examples.tsv"],
|
["pronouns/examples.tsv"],
|
||||||
@ -302,9 +336,67 @@ export class Locale {
|
|||||||
return this._pronouns[index];
|
return this._pronouns[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public pronounNameVariants(pronoun: Pronoun): Array<string> {
|
||||||
|
// NOTE(tecc): This is an attempt at decoding the code from classes.js.
|
||||||
|
// I make no guarantee regarding the accuracy.
|
||||||
|
const variants: Set<string> = new Set();
|
||||||
|
|
||||||
|
const firstMorpheme =
|
||||||
|
pronoun.forms[this.morphemes[0]].written.split("&");
|
||||||
|
if (this.morphemes.length === 1) {
|
||||||
|
return firstMorpheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
const secondMorpheme =
|
||||||
|
pronoun.forms[this.morphemes[1]].written.split("&");
|
||||||
|
const thirdMorpheme =
|
||||||
|
this.morphemes.length > 2
|
||||||
|
? pronoun.forms[this.morphemes[2]].written.split("&")
|
||||||
|
: [];
|
||||||
|
|
||||||
|
let thirdFormMorpheme = pronoun.thirdForm
|
||||||
|
? pronoun.forms[pronoun.thirdForm].written.split("&")
|
||||||
|
: [];
|
||||||
|
|
||||||
|
let thirdMorphemeThreeForms = thirdMorpheme;
|
||||||
|
if (this.code === "ru" || this.code === "ua") {
|
||||||
|
thirdMorphemeThreeForms = thirdMorpheme.map((x) => `[-${x}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let istr in firstMorpheme) {
|
||||||
|
const i = istr as unknown as number;
|
||||||
|
let firstPart = firstMorpheme[i];
|
||||||
|
let secondPart =
|
||||||
|
secondMorpheme[Math.max(i, secondMorpheme.length - 1)];
|
||||||
|
if (
|
||||||
|
firstPart === secondPart &&
|
||||||
|
thirdMorpheme.length &&
|
||||||
|
!this.config.pronouns.threeForms
|
||||||
|
) {
|
||||||
|
secondPart =
|
||||||
|
thirdMorpheme[Math.max(i, thirdMorpheme.length - 1)];
|
||||||
|
}
|
||||||
|
let variant = firstPart + "/" + secondPart;
|
||||||
|
if (this.config.pronouns.threeForms) {
|
||||||
|
variant += "/" + thirdMorphemeThreeForms[i];
|
||||||
|
} else if (pronoun.thirdForm) {
|
||||||
|
variant += "/" + thirdFormMorpheme[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
variants.add(variant);
|
||||||
|
}
|
||||||
|
return [...variants];
|
||||||
|
}
|
||||||
|
public pronounName(pronoun: Pronoun, separator: string = ","): string {
|
||||||
|
return this.pronounNameVariants(pronoun).join(separator);
|
||||||
|
}
|
||||||
|
|
||||||
public get examples() {
|
public get examples() {
|
||||||
return this._examples;
|
return this._examples;
|
||||||
}
|
}
|
||||||
|
public get morphemes() {
|
||||||
|
return this._morphemes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function examplesFor(
|
export function examplesFor(
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "@sinclair/typebox";
|
||||||
import { examplesFor, getLocale, Pronoun, PronounExample } from "#self/locales";
|
import {
|
||||||
|
examplesFor,
|
||||||
|
getLocale,
|
||||||
|
Locale,
|
||||||
|
Pronoun,
|
||||||
|
PronounExample,
|
||||||
|
} from "#self/locales";
|
||||||
import pronouns from "#self/server/v1/pronouns";
|
import pronouns from "#self/server/v1/pronouns";
|
||||||
|
|
||||||
export type V1AppInstance = AppInstance;
|
export type V1AppInstance = AppInstance;
|
||||||
@ -28,7 +34,7 @@ export function error<T extends ApiErrorOptions>(options: T) {
|
|||||||
|
|
||||||
export function replyError<T extends ApiErrorOptions>(
|
export function replyError<T extends ApiErrorOptions>(
|
||||||
reply: AppReply,
|
reply: AppReply,
|
||||||
options: T,
|
options: T
|
||||||
): AppReply {
|
): AppReply {
|
||||||
const response = error(options);
|
const response = error(options);
|
||||||
return reply.status(options.code).send(response);
|
return reply.status(options.code).send(response);
|
||||||
@ -41,7 +47,8 @@ export const ApiError = {
|
|||||||
},
|
},
|
||||||
UNKNOWN_PRONOUN: {
|
UNKNOWN_PRONOUN: {
|
||||||
code: 404,
|
code: 404,
|
||||||
message: "We aren't aware of any such pronoun. Perhaps there's a typo?\nNote that for custom pronouns, you need to specify all necessary forms: https://en.pronouns.page/faq#custom-pronouns",
|
message:
|
||||||
|
"We aren't aware of any such pronoun. Perhaps there's a typo?\nNote that for custom pronouns, you need to specify all necessary forms: https://en.pronouns.page/faq#custom-pronouns",
|
||||||
},
|
},
|
||||||
} satisfies Record<string, ApiErrorOptions>;
|
} satisfies Record<string, ApiErrorOptions>;
|
||||||
|
|
||||||
@ -52,6 +59,8 @@ export const localeSpecific = Type.Object({
|
|||||||
export function transformPronoun(
|
export function transformPronoun(
|
||||||
pronoun: Pronoun,
|
pronoun: Pronoun,
|
||||||
examples: Array<PronounExample>,
|
examples: Array<PronounExample>,
|
||||||
|
locale: Locale,
|
||||||
|
processName = false
|
||||||
) {
|
) {
|
||||||
const morphemes: Record<string, string> = {};
|
const morphemes: Record<string, string> = {};
|
||||||
const pronunciations: Record<string, string> = {};
|
const pronunciations: Record<string, string> = {};
|
||||||
@ -60,7 +69,7 @@ export function transformPronoun(
|
|||||||
pronunciations[name] = form.pronounced;
|
pronunciations[name] = form.pronounced;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
canonicalName: pronoun.keys[0],
|
canonicalName: pronoun.canonicalName,
|
||||||
description: pronoun.description,
|
description: pronoun.description,
|
||||||
normative: pronoun.isNormative,
|
normative: pronoun.isNormative,
|
||||||
morphemes,
|
morphemes,
|
||||||
@ -74,6 +83,7 @@ export function transformPronoun(
|
|||||||
smallForm: pronoun.smallForm ?? null,
|
smallForm: pronoun.smallForm ?? null,
|
||||||
sourcesInfo: pronoun.source ?? null,
|
sourcesInfo: pronoun.source ?? null,
|
||||||
examples: examplesFor(pronoun, examples),
|
examples: examplesFor(pronoun, examples),
|
||||||
|
name: processName ? locale.pronounName(pronoun) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +93,7 @@ export function transformPronoun(
|
|||||||
* every route is prefixed with the respective locale ID.
|
* every route is prefixed with the respective locale ID.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const routes = async function(app: AppInstance) {
|
export const routes = async function (app: AppInstance) {
|
||||||
app.register(pronouns);
|
app.register(pronouns);
|
||||||
} satisfies AppPluginAsync;
|
} satisfies AppPluginAsync;
|
||||||
export default routes;
|
export default routes;
|
||||||
|
@ -4,7 +4,12 @@ import {
|
|||||||
replyError,
|
replyError,
|
||||||
transformPronoun,
|
transformPronoun,
|
||||||
} from "#self/server/v1";
|
} from "#self/server/v1";
|
||||||
import { getLocale, PronounExample } from "#self/locales";
|
import {
|
||||||
|
getLocale,
|
||||||
|
parsePronounFromParts,
|
||||||
|
parsePronounFromString,
|
||||||
|
PronounExample,
|
||||||
|
} from "#self/locales";
|
||||||
import { isNotBlank, parseBool } from "@pronounspage/common/util";
|
import { isNotBlank, parseBool } from "@pronounspage/common/util";
|
||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "@sinclair/typebox";
|
||||||
|
|
||||||
@ -27,7 +32,8 @@ export const plugin = async function (app: AppInstance) {
|
|||||||
for (const pronoun of locale.pronouns) {
|
for (const pronoun of locale.pronouns) {
|
||||||
obj[pronoun.keys[0]] = transformPronoun(
|
obj[pronoun.keys[0]] = transformPronoun(
|
||||||
pronoun,
|
pronoun,
|
||||||
locale.examples
|
locale.examples,
|
||||||
|
locale
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +70,15 @@ export const plugin = async function (app: AppInstance) {
|
|||||||
return replyError(reply, ApiError.INVALID_LOCALE);
|
return replyError(reply, ApiError.INVALID_LOCALE);
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = req.params["*"].split("/").filter(isNotBlank).join("/"); // This is to get rid of any extraneous parts
|
const keyParts: Array<string> = req.params["*"]
|
||||||
const found = locale.pronoun(key);
|
.split("/")
|
||||||
|
.filter(isNotBlank);
|
||||||
|
const key = keyParts.join("/");
|
||||||
|
let found = locale.pronoun(key);
|
||||||
|
if (found == null) {
|
||||||
|
found = parsePronounFromParts(keyParts, locale.morphemes);
|
||||||
|
req.log.info(`${JSON.stringify(keyParts)} - ${found}`);
|
||||||
|
}
|
||||||
if (found == null) {
|
if (found == null) {
|
||||||
return replyError(reply, ApiError.UNKNOWN_PRONOUN);
|
return replyError(reply, ApiError.UNKNOWN_PRONOUN);
|
||||||
}
|
}
|
||||||
@ -76,7 +89,9 @@ export const plugin = async function (app: AppInstance) {
|
|||||||
found,
|
found,
|
||||||
examples.length < 1
|
examples.length < 1
|
||||||
? locale.examples
|
? locale.examples
|
||||||
: examples.map(parseExample)
|
: examples.map(parseExample),
|
||||||
|
locale,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user