import MAN_SUFFIXES from "./man_suffixes.json"; import MAN_VS_WOMAN_SUFFIXES from "./man_vs_woman_suffixes.json"; import WOMAN_F_VS_L_SUFFIXES from "./woman_first_vs_last_name_suffixes.json"; function _getMatchingSuffix( name: string, suffixes: { [key: string]: string }, ): [string, string] { let search = ""; for (let start = name.length; start > 0; start--) { search = name.slice(-start); const matchedSuffix = suffixes[search]; if (typeof matchedSuffix === "string") return [search, matchedSuffix]; } return ["", suffixes[""] || ""]; } function _isUpperCase(char: string) { return char.toLocaleLowerCase() !== char && char.toLocaleUpperCase() === char; } function _matchCase(template: string, value: string) { const templateChars = Array.from(template); const valueChars = Array.from(value); if (templateChars.length === 0) return value; return valueChars .map((char, index) => { const templateChar = templateChars[Math.min(index, templateChars.length - 1)] || ""; return _isUpperCase(templateChar) ? char.toLocaleUpperCase() : char.toLocaleLowerCase(); }) .join(""); } function _vokativWomanFirstName(originalName: string, normalizedName: string) { if (!normalizedName.endsWith("a")) return originalName; const ending = originalName.slice(-1); const suffix = _isUpperCase(ending) ? "O" : "o"; return `${originalName.slice(0, -1)}${suffix}`; } function _vokativWomanLastName(originalName: string) { return originalName; } function _vokativMan(originalName: string, normalizedName: string) { const [search, suffix] = _getMatchingSuffix(normalizedName, MAN_SUFFIXES); const prefix = originalName.slice( 0, originalName.length - (search?.length || 0), ); const originalSuffix = originalName.slice( originalName.length - (search?.length || 0), ); return `${prefix}${_matchCase(originalSuffix, suffix)}`; } export function isWoman(nameString: string): boolean { const name = nameString.toLowerCase(); return _getMatchingSuffix(name, MAN_VS_WOMAN_SUFFIXES)[1] === "w"; } export function vocative( nameString: string, womanBool?: boolean, lastName?: boolean, ): string { const normalizedName = nameString.toLowerCase(); let woman = womanBool === undefined ? isWoman(normalizedName) : womanBool; if (woman) { if (lastName === undefined) lastName = (_getMatchingSuffix(normalizedName, WOMAN_F_VS_L_SUFFIXES)[1] || "l") === "l"; return lastName ? _vokativWomanLastName(nameString) : _vokativWomanFirstName(nameString, normalizedName); } else { return _vokativMan(nameString, normalizedName); } }