Files
moalem-shop/src/lib/mixedText.ts
T
Alireza HassaniandCursor e625354dc9 Add installment calculator and Latin digits in English product text.
Wire the product CTA to store-loan-methods calculate so shoppers can pick a term and see the plan, and render English model runs under Montserrat so fanum fonts stop painting 0-9 as Persian digits.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-25 00:21:40 +03:30

45 lines
1.7 KiB
TypeScript

// IRANYekan *fanum* maps Latin digit codepoints (0-9) to Persian glyphs.
// Montserrat (`.en`) keeps real Latin digits — so any Latin-letter run that
// also carries model numbers (HP R5, 8GB, EF2126WM…) must render under `.en`.
// Pure digit runs (e.g. "13" inside «آیفون 13 پرو») stay on the FA font and
// correctly show as Persian digits.
const EN_RUN = /[A-Za-z0-9][A-Za-z0-9\s\-_|./+#%]*/g;
export type TextSegment = { type: "fa" | "en"; text: string };
export function splitMixedText(text: string): TextSegment[] {
const segments: TextSegment[] = [];
let cursor = 0;
let match: RegExpExecArray | null;
const re = new RegExp(EN_RUN.source, "g");
while ((match = re.exec(text)) !== null) {
let start = match.index;
let end = start + match[0].length;
while (end > start && /\s/.test(text[end - 1]!)) end--;
const chunk = text.slice(start, end);
if (!/[A-Za-z]/.test(chunk)) continue;
if (start > cursor) segments.push({ type: "fa", text: text.slice(cursor, start) });
segments.push({ type: "en", text: chunk });
cursor = end;
}
if (cursor < text.length) segments.push({ type: "fa", text: text.slice(cursor) });
return segments;
}
/** Wrap Latin runs in a plain string as `.en` spans (text is assumed already HTML-safe). */
export function wrapLatinRuns(text: string): string {
return splitMixedText(text)
.map((seg) =>
seg.type === "en" ? `<span class="en" dir="ltr" lang="en">${seg.text}</span>` : seg.text,
)
.join("");
}
/** Walk HTML text nodes (content between tags) and wrap Latin runs. */
export function wrapLatinInHtml(html: string): string {
return html.replace(/(^|>)([^<]+)/g, (_, prefix: string, text: string) => prefix + wrapLatinRuns(text));
}