async function inspectPersianVoices(waitMs = 2000) { if (!("speechSynthesis" in window)) return {apiAvailable:false, voices:[]}; if (!Number.isInteger(waitMs) || waitMs < 0 || waitMs > 5000) throw new Error("waitMs 0..5000"); const synth = window.speechSynthesis; const snapshot = () => synth.getVoices().map(v => ({name:v.name, lang:v.lang, localService:v.localService, default:v.default})); const hasFa = rows => rows.some(v => /^fa(?:-|$)/i.test(v.lang)); const initial = snapshot(); if (hasFa(initial)) return {apiAvailable:true, reason:"persian_reported", voices:initial}; return await new Promise(resolve => { let timer; const finish = reason => {clearTimeout(timer); synth.removeEventListener("voiceschanged", changed); resolve({apiAvailable:true,reason,voices:snapshot()});}; const changed = () => {if (hasFa(snapshot())) finish("persian_reported_after_change");}; synth.addEventListener("voiceschanged", changed); timer = setTimeout(() => finish("bounded_check_finished"), waitMs); changed(); }); } // In your browser console: inspectPersianVoices().then(console.log) // This function never calls speak(), asks for a microphone, or uploads text.