Locale detection
Source: platform/apps/app/src/lib/rag/detect-locale.ts · rendered from main on every deploy — edit in the repo, not here
/** * Detect response language from the visitor's own words. ADR-0121 D1. * * Puccha serves exactly two response languages (Thai, English), so this is a * deliberately tiny dominant-language check, not a general language classifier — * no dependency, Workers-safe, and nothing added to the widget bundle (the * widget never calls this; the chat route does). * * We weigh Thai *characters* against English *words*, not character-vs-character. * Thai is written without spaces, so its natural unit is the character; English's * is the word. A strict char-vs-char count let one long English noun outvote a * whole Thai phrase — "สนใจทำ software" (6 Thai chars vs 8 Latin chars) was * classified English and got an English refusal (the bug that motivated this * refinement of ADR-0121 D1). Counting English by word, "สนใจทำ software" is * 6 Thai vs 1 English → Thai, while "What is the ค่ะ policy on refunds?" is * 2 Thai vs 8 English → English, both correct. * * Text with no letters at all (empty, digits / emoji / punctuation only) returns * `null`, so the caller falls back to the client-supplied locale. * * This only ever drives *generation* language (system prompt, refusal, canned * replies, handoff copy, query-rewrite, answer-cache key). It is never a * retrieval filter — hybridSearch / Vectorize / FTS do not gate on locale and * bge-m3 is multilingual, so an English query still retrieves Thai chunks. */export function detectLocale(text: string): 'th' | 'en' | null { if (!text) return null; let thaiChars = 0; let latinWords = 0; let inLatinWord = false; for (const ch of text) { if (ch >= '' && ch <= '') { thaiChars++; inLatinWord = false; } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { if (!inLatinWord) latinWords++; inLatinWord = true; } else { inLatinWord = false; } } if (thaiChars === 0 && latinWords === 0) return null; // no decidable script return thaiChars >= latinWords ? 'th' : 'en';}