87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
/** مرد، زن، یا هیچکدام (مثلاً فروشگاه) */
|
|||
|
|
export type PersonGender = "male" | "female" | "none";
|
||
|
|
|
||
|
|
export const PERSON_GENDERS: PersonGender[] = ["male", "female", "none"];
|
||
|
|
|
||
|
|
export const PERSON_GENDER_LABELS: Record<PersonGender, string> = {
|
||
|
|
male: "مرد",
|
||
|
|
female: "زن",
|
||
|
|
none: "هیچکدام",
|
||
|
|
};
|
||
|
|
|
||
|
|
const MALE_TITLE = "جناب آقای";
|
||
|
|
const FEMALE_TITLE = "سرکار خانم";
|
||
|
|
|
||
|
|
function normalizeFa(value: string) {
|
||
|
|
return value.replace(/ي/g, "ی").replace(/ك/g, "ک").replace(/\s+/g, " ").trim();
|
||
|
|
}
|
||
|
|
|
||
|
|
function splitWords(value: string) {
|
||
|
|
const parts = normalizeFa(value).split(" ").filter(Boolean);
|
||
|
|
return {
|
||
|
|
firstName: parts[0] ?? "",
|
||
|
|
lastName: parts.slice(1).join(" "),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/** عنوان ابتدای نام را برمیدارد تا فیلدهای نام، عنوان را دوباره ذخیره نکنند. */
|
||
|
|
export function splitPersonHonorific(firstName: string, lastName: string): {
|
||
|
|
firstName: string;
|
||
|
|
lastName: string;
|
||
|
|
gender: Exclude<PersonGender, "none"> | null;
|
||
|
|
} {
|
||
|
|
let full = normalizeFa(`${firstName} ${lastName}`);
|
||
|
|
let gender: Exclude<PersonGender, "none"> | null = null;
|
||
|
|
// فاصله اختیاری است: «سرکارخانم» و «جناب آقاي» هر دو عنواناند.
|
||
|
|
for (;;) {
|
||
|
|
const male = full.match(/^جناب\s*آقای(?=\s|$)/);
|
||
|
|
const female = full.match(/^سرکار\s*خانم(?=\s|$)/);
|
||
|
|
const hit = male?.[0] ?? female?.[0];
|
||
|
|
if (!hit) break;
|
||
|
|
gender = male ? "male" : "female";
|
||
|
|
full = normalizeFa(full.slice(hit.length));
|
||
|
|
}
|
||
|
|
const plain = splitWords(full);
|
||
|
|
return { ...plain, gender };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* نام ذخیرهشده را با عنوان میسازد.
|
||
|
|
* اگر جنسیت یا تیک نفرستاده شود و خود نام عنوان داشته باشد، همان برداشت میشود.
|
||
|
|
*/
|
||
|
|
export function applyPersonGenderTitle(input: {
|
||
|
|
firstName: string;
|
||
|
|
lastName: string;
|
||
|
|
gender?: PersonGender | null;
|
||
|
|
showGenderInName?: boolean | null;
|
||
|
|
}): {
|
||
|
|
firstName: string;
|
||
|
|
lastName: string;
|
||
|
|
gender: PersonGender;
|
||
|
|
showGenderInName: boolean;
|
||
|
|
} {
|
||
|
|
const bare = splitPersonHonorific(input.firstName, input.lastName);
|
||
|
|
const gender: PersonGender =
|
||
|
|
input.gender === "male" || input.gender === "female" || input.gender === "none"
|
||
|
|
? input.gender
|
||
|
|
: (bare.gender ?? "none");
|
||
|
|
let show = input.showGenderInName;
|
||
|
|
if (typeof show !== "boolean") show = bare.gender != null && gender !== "none";
|
||
|
|
if (gender === "none") show = false;
|
||
|
|
|
||
|
|
const body = [bare.firstName, bare.lastName].filter(Boolean).join(" ");
|
||
|
|
const titled =
|
||
|
|
show && gender === "male"
|
||
|
|
? `${MALE_TITLE} ${body}`.trim()
|
||
|
|
: show && gender === "female"
|
||
|
|
? `${FEMALE_TITLE} ${body}`.trim()
|
||
|
|
: body;
|
||
|
|
const parts = splitWords(titled);
|
||
|
|
return {
|
||
|
|
firstName: parts.firstName || "—",
|
||
|
|
lastName: parts.lastName || "—",
|
||
|
|
gender,
|
||
|
|
showGenderInName: show,
|
||
|
|
};
|
||
|
|
}
|