أنشئ APK مجانًا

واجهة JavaScript bridge API

تحويل النص إلى كلام

أصوات الجهاز، تُنطق من الصفحة.

SpeechRecognition معيار الويب#

webkitSpeechRecognition / SpeechRecognition — speech to text

مثال

واجهة Web Speech القياسية للإملاء: يتكلم المستخدم وتحصل الصفحة على نص. يملكها Chrome على Android ولا يملكها WebView في Android، فيوفرها التطبيق فوق مُتعرِّف الكلام في Android نفسه. الشيفرة المكتوبة لـ Chrome تعمل دون تغيير.

يُرجع: start() يبدأ الاستماع؛ وأحداث result تحمل event.results[i][0].transcript (و.confidence و.isFinal)؛ وأحداث error تحمل event.error (not-allowed وno-speech وaudio-capture وnetwork وaborted وlanguage-not-supported وservice-not-allowed)؛ وend يأتي دائمًا أخيرًا. يتطلب: الميكروفون. يفعّله الإنشاء من تلقاء نفسه عندما تذكر شيفرتك SpeechRecognition، ويسأل Android المستخدم أول مرة يعمل فيها start().

<button id="mic">Dictate</button>
<textarea id="note"></textarea>
<script>
const Rec = window.SpeechRecognition || window.webkitSpeechRecognition;
const mic = document.getElementById('mic');
if (!Rec) {
  mic.hidden = true;                       // no speech recogniser on this phone
} else {
  mic.addEventListener('click', () => {
    const rec = new Rec();
    rec.lang = 'en-IN';                    // leave empty for the page / phone language
    rec.interimResults = true;             // live text while the user speaks
    rec.onresult = (e) => {
      let text = '';
      for (const r of e.results) text += r[0].transcript;
      document.getElementById('note').value = text;
    };
    rec.onerror = (e) => {
      if (e.error === 'not-allowed') alert('Allow the microphone to dictate.');
    };
    rec.onend = () => { mic.disabled = false; };
    mic.disabled = true;
    rec.start();
  });
}
</script>

إملاء متواصل يتوقف عند الطلب ويبلغ عن كل خطأ:

const Rec = window.SpeechRecognition || window.webkitSpeechRecognition;
let rec = null;
function toggleDictation() {
  if (!Rec) return showMessage('Voice typing is not available on this phone.');
  if (rec) { rec.stop(); return; }                   // stop() delivers what was heard
  rec = new Rec();
  rec.continuous = true;
  rec.onresult = (e) => {
    for (let i = e.resultIndex; i < e.results.length; i++) {
      if (e.results[i].isFinal) appendToNote(e.results[i][0].transcript);
    }
  };
  rec.onerror = (e) => {
    const msg = { 'not-allowed': 'Allow the microphone first.', 'no-speech': 'Nothing was heard.',
                  'audio-capture': 'No microphone found.', 'network': 'Speech service unreachable.' }[e.error];
    if (msg) showMessage(msg);
  };
  rec.onend = () => { rec = null; };
  rec.start();
}

قل أمرًا ونفّذه، بلغة الهاتف:

const Rec = window.SpeechRecognition || window.webkitSpeechRecognition;
function listenForCommand() {
  if (!Rec) return;
  const rec = new Rec();
  rec.lang = navigator.language;
  rec.onresult = (e) => {
    const said = e.results[0][0].transcript.toLowerCase();
    if (said.includes('next')) goNext();
    else if (said.includes('back')) goBack();
    else showMessage('Heard: ' + said);
  };
  rec.onerror = (e) => {
    if (e.error === 'language-not-supported') showMessage('This language is not supported here.');
  };
  rec.start();
}

ملاحظات: continuous = true يواصل الاستماع عبر الجمل حتى تستدعي stop()؛ وبعد ثلاث جولات صامتة ينتهي بـ no-speech، حتى لا يبقى مُتعرِّف منسي يستمع للأبد. stop() يسلّم ما سُمع؛ وabort() يتخلص منه. يتوقف التعرف عندما يذهب التطبيق إلى الخلفية، كما يحدث عند إخفاء تبويب Chrome. صفحات التطبيق فقط (ملفاته أو موقع المنشئ) يمكنها استخدام الميكروفون بهذه الطريقة؛ وأي موقع آخر يحصل على not-allowed. الواجهة غير موجودة عندما لا يملك الهاتف خدمة تعرف على الكلام، فافحصها كما سبق. النواقل الداخلية: __srStart و__srStop و__srAbort و__srAvailable؛ وتصل النتائج كـ appmint:sr. التطبيقات المبنية بالذكاء الاصطناعي تستخدم speech.start() من @/lib/appmintNative.

speechSynthesis معيار الويب#

window.speechSynthesis + SpeechSynthesisUtterance

مثال

يقرأ النص بصوت عالٍ بمحرك تحويل النص إلى كلام في الهاتف نفسه، باستخدام واجهة Web Speech العادية.

يُرجع: speechSynthesis.speak(u) لا يُرجع شيئًا؛ وأحداث النطق start / boundary / end / error / pause / resume (كخصائص onstart … أو بـ addEventListener) تخبرك بما حدث. getVoices() يُرجع مصفوفة من { voiceURI, name, lang, localService, default } بشكل متزامن. يتطلب: لا شيء. لا يملك Android WebView واجهة كلام عاملة خاصة به؛ ويثبّت التطبيق هذه عند بدء المستند في كل إطار.

انطق جملة واعرف متى تنتهي:

function say(text) {
  if (!('speechSynthesis' in window)) { alert('Speech is not available here'); return; }
  var u = new SpeechSynthesisUtterance(text);
  u.lang = 'en-US';   // BCP-47 tag: 'ta-IN', 'hi-IN', 'fr-FR' ...
  u.rate = 1.2;       // 0.1 .. 4 (1 = normal)
  u.pitch = 1;        // 0.1 .. 4 (1 = normal)
  u.onstart = function () { document.body.classList.add('speaking'); };
  u.onend = function () { document.body.classList.remove('speaking'); };
  u.onerror = function (e) {
    document.body.classList.remove('speaking');
    console.log('Speech failed:', e.error);   // 'synthesis-failed'
  };
  speechSynthesis.speak(u);   // queues after anything already speaking
}

document.getElementById('stop').onclick = function () {
  if ('speechSynthesis' in window) speechSynthesis.cancel();   // stops now, clears the queue
};

تصل الأصوات بعد لحظة من البدء. اقرأها مجددًا عندما ينطلق voiceschanged:

var voices = [];
function loadVoices() {
  voices = speechSynthesis.getVoices();   // [] until the engine is ready
  var tamil = voices.filter(function (v) { return v.lang.indexOf('ta') === 0; });
  document.getElementById('tamil-btn').hidden = tamil.length === 0;
}
if ('speechSynthesis' in window) {
  loadVoices();
  speechSynthesis.addEventListener('voiceschanged', loadVoices);
}

function sayWithVoice(text, voice) {
  var u = new SpeechSynthesisUtterance(text);
  u.voice = voice;           // a voice from getVoices(): that exact voice is used
  u.volume = 0.8;            // 0 .. 1
  speechSynthesis.speak(u);
}

قارئ مقالات بزر واحد إيقاف مؤقت / استئناف يبرز الفقرة التي تُقرأ:

var toggle = document.getElementById('pause-toggle');

toggle.onclick = function () {
  if (!('speechSynthesis' in window) || !speechSynthesis.speaking) return;
  if (speechSynthesis.paused) speechSynthesis.resume();   // continues from the last word
  else speechSynthesis.pause();
};

function readArticle() {
  if (!('speechSynthesis' in window)) return;
  speechSynthesis.cancel();                               // start over
  document.querySelectorAll('article p').forEach(function (p) {
    var u = new SpeechSynthesisUtterance(p.textContent);
    u.onstart = function () { p.classList.add('reading'); };
    u.onend = u.onerror = function () { p.classList.remove('reading'); };
    u.onpause = function () { toggle.textContent = 'Resume'; };
    u.onresume = function () { toggle.textContent = 'Pause'; };
    speechSynthesis.speak(u);                             // paragraphs queue in order
  });
  toggle.textContent = 'Pause';
}

ملاحظات:

  • volume (0-1), voice (الصوت بعينه من getVoices()) وrate وpitch وlang كلها تصل إلى محرك الهاتف. مع voice وlang يفوز الصوت؛ ومع غيابهما يتكلم صوت المحرك الافتراضي.
  • boundary ينطلق قبل كل كلمة مع charIndex / charLength (Android 8+؛ المحركات التي لا تُبلغ عن مواضع الكلمات لا ترسل شيئًا).
  • pause() / resume(): لا يستطيع محرك Android التوقف المؤقت في منتصف كلمة، فـ pause() يوقفه وresume() ينطق بقية الجملة من آخر كلمة أبلغ عنها المحرك (تنطلق أحداث pause / resume). على محرك لا يُبلغ عن مواضع الكلمات، يبدأ resume() ذلك النطق من أوله. يبقى speaking بقيمة true أثناء الإيقاف المؤقت؛ وpending بقيمة true بينما تنتظر أقوال في الطابور.
  • cancel() ينطلق error بـ error: 'interrupted' على النطق الجاري وبـ 'canceled' على المصفوفة في الطابور، كما يقول المعيار؛ فلا تعرضها كإخفاقات.
  • إذا لم يكن لدى الهاتف بيانات صوتية للغة، فقد يتكلم المحرك بلغته الافتراضية مع ذلك. افحص getVoices() للغة أولًا.
  • الأحداث الخام وراء هذا هي appmint:tts (راجع ttsSpeak). لا تحتاجها عند استخدام هذه الواجهة.

__srAbort الجسر (bridge)#

window.WebToApk.__srAbort(id: String)

SpeechRecognition.abort(): توقف فورًا وتخلص مما سُمع.

مثال

موثَّق مع WebToApk.__srStart - والمثال هناك يعرض هذا أيضًا.

ناقل داخلي وراء webkitSpeechRecognition / SpeechRecognition (البدء والإيقاف والإجهاض، وهل يوجد مُتعرِّف على الهاتف). الصفحات تستخدم الواجهة القياسية؛ لا تستدعِ هذه مباشرة، فقد تتغير الطرق ذات __ دون إشعار.

يُرجع: __srStart(id, options) يُرجع "" عندما يبدأ الاستماع، أو اسم خطأ Web Speech (not-allowed، service-not-allowed). كل ما بعد ذلك يصل كأحداث appmint:sr: {id, type} لـ start/audiostart/speechstart/speechend/audioend/end، و{id, type:'result', final, alternatives:[{transcript, confidence}]}، و{id, type:'error', error}. يتطلب: الميكروفون (يُفعَّل من شكل الاستدعاء).

استخدم الواجهة العامة:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

إملاء متواصل مع نص حي ولغة:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

أبلغ عن أخطاء مثل ميكروفون مرفوض:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

ملاحظات: مُنفَّذ في غلاف التطبيق (SpeechRecognitionBridge.kt) فوق SpeechRecognizer في Android. و__srStart مرفوض للصفحات التي لا تنتمي إلى التطبيق.

__srAvailable الجسر (bridge)#

window.WebToApk.__srAvailable(): Boolean

ما إذا كانت خدمة التعرف على الكلام مثبتة (لا يوجد webkitSpeechRecognition إلا حينها).

مثال

موثَّق مع WebToApk.__srStart - والمثال هناك يعرض هذا أيضًا.

ناقل داخلي وراء webkitSpeechRecognition / SpeechRecognition (البدء والإيقاف والإجهاض، وهل يوجد مُتعرِّف على الهاتف). الصفحات تستخدم الواجهة القياسية؛ لا تستدعِ هذه مباشرة، فقد تتغير الطرق ذات __ دون إشعار.

يُرجع: __srStart(id, options) يُرجع "" عندما يبدأ الاستماع، أو اسم خطأ Web Speech (not-allowed، service-not-allowed). كل ما بعد ذلك يصل كأحداث appmint:sr: {id, type} لـ start/audiostart/speechstart/speechend/audioend/end، و{id, type:'result', final, alternatives:[{transcript, confidence}]}، و{id, type:'error', error}. يتطلب: الميكروفون (يُفعَّل من شكل الاستدعاء).

استخدم الواجهة العامة:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

إملاء متواصل مع نص حي ولغة:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

أبلغ عن أخطاء مثل ميكروفون مرفوض:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

ملاحظات: مُنفَّذ في غلاف التطبيق (SpeechRecognitionBridge.kt) فوق SpeechRecognizer في Android. و__srStart مرفوض للصفحات التي لا تنتمي إلى التطبيق.

__srStart الجسر (bridge)#

window.WebToApk.__srStart(id: String, optionsJson: String): String

SpeechRecognition.start(): "" عند البدء (تصل الأحداث كـ `appmint:sr`)، وإلا اسم خطأ Web Speech. مقيّد بالنطاق: ما يقوله المستخدم بيانات مستخدم.

مثال

ناقل داخلي وراء webkitSpeechRecognition / SpeechRecognition (البدء والإيقاف والإجهاض، وهل يوجد مُتعرِّف على الهاتف). الصفحات تستخدم الواجهة القياسية؛ لا تستدعِ هذه مباشرة، فقد تتغير الطرق ذات __ دون إشعار.

يُرجع: __srStart(id, options) يُرجع "" عندما يبدأ الاستماع، أو اسم خطأ Web Speech (not-allowed، service-not-allowed). كل ما بعد ذلك يصل كأحداث appmint:sr: {id, type} لـ start/audiostart/speechstart/speechend/audioend/end، و{id, type:'result', final, alternatives:[{transcript, confidence}]}، و{id, type:'error', error}. يتطلب: الميكروفون (يُفعَّل من شكل الاستدعاء).

استخدم الواجهة العامة:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

إملاء متواصل مع نص حي ولغة:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

أبلغ عن أخطاء مثل ميكروفون مرفوض:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

ملاحظات: مُنفَّذ في غلاف التطبيق (SpeechRecognitionBridge.kt) فوق SpeechRecognizer في Android. و__srStart مرفوض للصفحات التي لا تنتمي إلى التطبيق.

__srStop الجسر (bridge)#

window.WebToApk.__srStop(id: String)

SpeechRecognition.stop(): توقف عن الاستماع وسلّم ما سُمع.

مثال

موثَّق مع WebToApk.__srStart - والمثال هناك يعرض هذا أيضًا.

ناقل داخلي وراء webkitSpeechRecognition / SpeechRecognition (البدء والإيقاف والإجهاض، وهل يوجد مُتعرِّف على الهاتف). الصفحات تستخدم الواجهة القياسية؛ لا تستدعِ هذه مباشرة، فقد تتغير الطرق ذات __ دون إشعار.

يُرجع: __srStart(id, options) يُرجع "" عندما يبدأ الاستماع، أو اسم خطأ Web Speech (not-allowed، service-not-allowed). كل ما بعد ذلك يصل كأحداث appmint:sr: {id, type} لـ start/audiostart/speechstart/speechend/audioend/end، و{id, type:'result', final, alternatives:[{transcript, confidence}]}، و{id, type:'error', error}. يتطلب: الميكروفون (يُفعَّل من شكل الاستدعاء).

استخدم الواجهة العامة:

const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.onresult = (e) => console.log(e.results[0][0].transcript);
rec.start();

إملاء متواصل مع نص حي ولغة:

const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SR) {
  const rec = new SR();
  rec.lang = 'en-US';
  rec.continuous = true;
  rec.interimResults = true;
  rec.onresult = (e) => {
    const r = e.results[e.results.length - 1];
    note.value = r[0].transcript + (r.isFinal ? '\n' : '');
  };
  micBtn.onclick = () => rec.start();
  stopBtn.onclick = () => rec.stop();
}

أبلغ عن أخطاء مثل ميكروفون مرفوض:

rec.onerror = (e) => {
  if (e.error === 'not-allowed' || e.error === 'service-not-allowed') showMessage('Allow the microphone to dictate.');
  else showMessage('Speech error: ' + e.error);
};
rec.onend = () => micBtn.classList.remove('live');

ملاحظات: مُنفَّذ في غلاف التطبيق (SpeechRecognitionBridge.kt) فوق SpeechRecognizer في Android. و__srStart مرفوض للصفحات التي لا تنتمي إلى التطبيق.

__ttsSpeak الجسر (bridge)#

window.WebToApk.__ttsSpeak(json: String)

speak الكاملة لبديل speechSynthesis: `{text, lang, rate, pitch, volume (0-1), voice (اسم من getVoices()، أو ''), id}`. يبقى [ttsSpeak] للصفحات التي تستدعي الجسر مباشرة؛ وليس له volume ولا voice.

مثال

ناقل داخلي وراء speechSynthesis.speak(utterance): النطق كاملًا (النص واللغة والسرعة والنبرة والمستوى والصوت المختار) كنص JSON واحد. الصفحات تستخدم واجهة speechSynthesis القياسية؛ لا تستدعِ هذا مباشرة.

يُرجع: لا شيء. يصل التقدم كأحداث appmint:tts، تحوّلها طبقة speechSynthesis إلى أحداث النطق start / boundary / end / error. يتطلب: لا شيء.

الواجهة القياسية التي يحملها هذا:

function readAloud(text) {
  if (!('speechSynthesis' in window)) return;
  var voice = speechSynthesis.getVoices().filter(function (v) { return v.lang === 'en-GB'; })[0];
  var u = new SpeechSynthesisUtterance(text);
  if (voice) u.voice = voice;   // that exact voice
  u.volume = 0.6;               // 0 .. 1
  u.addEventListener('boundary', function (e) { highlightWordAt(e.charIndex, e.charLength); });
  u.addEventListener('end', function () { clearHighlight(); });
  speechSynthesis.speak(u);
}

منتقي أصوات يختاره المستخدم مرة واحدة؛ وكل نطق لاحق يحمل ذلك الصوت بعينه:

var picker = document.getElementById('voice-picker');

function listVoices() {
  var saved = localStorage.getItem('voiceName') || '';
  picker.innerHTML = '';
  speechSynthesis.getVoices().forEach(function (v) {
    picker.add(new Option(v.name + ' (' + v.lang + ')', v.name, false, v.name === saved));
  });
}
if ('speechSynthesis' in window) {
  listVoices();                                   // [] until the engine is ready
  speechSynthesis.onvoiceschanged = listVoices;
}
picker.onchange = function () { localStorage.setItem('voiceName', picker.value); };

function speakWithSavedVoice(text) {
  var name = localStorage.getItem('voiceName');
  var voice = speechSynthesis.getVoices().filter(function (v) { return v.name === name; })[0];
  var u = new SpeechSynthesisUtterance(text);
  if (voice) u.voice = voice;                     // its language is used too when u.lang is empty
  speechSynthesis.speak(u);
}

إشارات التمرين: هادئة وسريعة للعد التنازلي، وبصوت كامل وأعلى نبرة عند التبديل:

function cue(text, loud) {
  if (!('speechSynthesis' in window)) return;
  var u = new SpeechSynthesisUtterance(text);
  u.lang = 'en-US';
  u.rate = 1.4;                  // 0.1 .. 4
  u.pitch = loud ? 1.3 : 1;      // 0.1 .. 4
  u.volume = loud ? 1 : 0.4;     // 0 .. 1
  u.onerror = function (e) {
    if (e.error === 'synthesis-failed') console.log('Cue not spoken:', text);
  };
  speechSynthesis.speak(u);      // queues after the previous cue
}

cue('Ten seconds left', false);
cue('Switch sides!', true);

ملاحظات: الـ JSON هو {text, lang, rate, pitch, volume, voice, id} (وvoice اسم من getVoices() أو ''). الأقدم ttsSpeak(text, lang, rate, pitch, id) يبقى للصفحات التي تستدعي الجسر مباشرة؛ وليس له volume ولا voice.

ttsCancel الجسر (bridge)#

window.WebToApk.ttsCancel()

يدعم بديل window.speechSynthesis الذي يُثبَّت عند بدء المستند في كل إطار؛ ينبغي للصفحات استخدام واجهة speechSynthesis القياسية وسيُستدعى هذا نيابةً عنها. لا يأتي Android WebView بـ speechSynthesis عامل خاص به، ولهذا وُجد إعادة التوجيه.

موصوف ضمن مجموعته، تحويل النص إلى كلام، وليس على حدة.

مثال

يوقف الكلام فورًا ويتخلص من كل ما ينتظر النطق. الصفحات تستدعي عادةً speechSynthesis.cancel() القياسي، الذي يستدعي هذا نيابةً عنك.

يعيد: لا شيء. يتطلب: لا شيء.

الطريقة العامة (استخدم هذه):

document.getElementById('stop-reading').addEventListener('click', function () {
  if ('speechSynthesis' in window) speechSynthesis.cancel();
});

الاستدعاء الخام:

if (window.WebToApk && typeof window.WebToApk.ttsCancel === 'function') {
  window.WebToApk.ttsCancel();
}

انطق الحالة الأحدث فقط وأسقط أي شيء أقدم ما زال في الطابور (متتبع توصيل، نتيجة مباشرة):

function announceLatest(text) {
  if (!('speechSynthesis' in window)) return;
  speechSynthesis.cancel();   // the old line gets error 'interrupted', queued ones 'canceled'
  var u = new SpeechSynthesisUtterance(text);
  u.onerror = function (e) {
    if (e.error === 'interrupted' || e.error === 'canceled') return;   // replaced, not a failure
    showToast('Could not read that aloud');
  };
  speechSynthesis.speak(u);
}

announceLatest('Your driver is 5 minutes away');
announceLatest('Your driver has arrived');   // cuts the first one off

ملاحظات: الكلام المقطوع أو المُسقَط لا يرسل أي حدث appmint:tts أو end أو error، لذا لا يعمل onend الخاص بالنطق بعد الإلغاء. وإذا تتبعت معرّفات ttsSpeak الخام بنفسك، فانسَها عندما تستدعي ttsCancel().

ttsGetVoices الجسر (bridge)#

window.WebToApk.ttsGetVoices(): String

مصفوفة JSON بالأصوات المثبتة: [{name, lang, default}]. فارغة حتى ينتهي المحرك من التهيئة، وحينها تتلقى الصفحة 'voiceschanged' وتعيد الاستعلام، تمامًا كعقد getVoices() غير المتزامن في Chrome.

مثال

يسرد أصوات تحويل النص إلى كلام المثبتة على الهاتف. الصفحات تستخدم عادةً speechSynthesis.getVoices() القياسي، الذي يستدعي هذا نيابةً عنك.

يُرجع: نص JSON، بشكل متزامن: [{ "name": "en-us-x-sfg-local", "lang": "en-US", "default": false }, ...]. وهو "[]" حتى يبدأ المحرك. يتطلب: لا شيء.

الطريقة العامة (استخدم هذه):

function fillLanguages() {
  var langs = {};
  speechSynthesis.getVoices().forEach(function (v) { langs[v.lang] = true; });
  document.getElementById('langs').textContent = Object.keys(langs).join(', ');
}
if ('speechSynthesis' in window) {
  fillLanguages();
  speechSynthesis.onvoiceschanged = fillLanguages;
}

الاستدعاء الخام (تذكّر JSON.parse):

function nativeVoices() {
  if (!(window.WebToApk && typeof window.WebToApk.ttsGetVoices === 'function')) return [];
  try { return JSON.parse(window.WebToApk.ttsGetVoices()); } catch (e) { return []; }
}

window.addEventListener('appmint:tts', function (e) {
  if (e.detail && e.detail.event === 'voiceschanged') {
    var hindi = nativeVoices().filter(function (v) { return v.lang.indexOf('hi') === 0; });
    document.getElementById('hindi-btn').hidden = hindi.length === 0;
  }
});

انطق بلغة الهاتف عندما يكون صوت لها مثبتًا، وإلا دع المحرك يستخدم صوته الافتراضي:

function bestSpeechLang() {
  if (!(window.WebToApk && typeof window.WebToApk.ttsGetVoices === 'function')) return '';
  var voices;
  try { voices = JSON.parse(window.WebToApk.ttsGetVoices()); } catch (e) { return ''; }
  var want = navigator.language || 'en-US';            // e.g. 'ta-IN'
  var exact = voices.filter(function (v) { return v.lang === want; })[0];
  if (exact) return exact.lang;
  var base = want.split('-')[0];
  var near = voices.filter(function (v) { return v.lang.split('-')[0] === base; })[0];
  return near ? near.lang : '';                        // '' = the engine's default voice
}

if (window.WebToApk && typeof window.WebToApk.ttsSpeak === 'function') {
  window.WebToApk.ttsSpeak(greetingText(), bestSpeechLang(), 1.0, 1.0, 'greeting');
}

ملاحظات: lang وسم BCP-47 مثل en-US أو ta-IN. وname الخاص بالصوت هو معرّف المحرك نفسه ولا يمكن اختيار الصوت به عند النطق؛ تُستخدم لغته فقط.

ttsIsSpeaking الجسر (bridge)#

window.WebToApk.ttsIsSpeaking(): Boolean

يدعم بديل window.speechSynthesis الذي يُثبَّت عند بدء المستند في كل إطار؛ ينبغي للصفحات استخدام واجهة speechSynthesis القياسية وسيُستدعى هذا نيابةً عنها. لا يأتي Android WebView بـ speechSynthesis عامل خاص به، ولهذا وُجد إعادة التوجيه.

موصوف ضمن مجموعته، تحويل النص إلى كلام، وليس على حدة.

مثال

يخبرك ما إذا كان محرك تحويل النص إلى كلام في الهاتف ينطق في هذه اللحظة. الصفحات تستخدم عادةً speechSynthesis.speaking أو onend الخاص بالنطق بدلًا منه.

يُرجع: true / false، بشكل متزامن. وfalse قبل أن يبدأ المحرك. يتطلب: لا شيء.

الطريقة العامة (استخدم هذه):

if ('speechSynthesis' in window && speechSynthesis.speaking) {
  speechSynthesis.cancel();
}

الاستدعاء الخام يسأل المحرك نفسه، فهو دقيق حتى لو بدأ الكلام بالاستدعاء الخام ttsSpeak:

function isSpeaking() {
  if (window.WebToApk && typeof window.WebToApk.ttsIsSpeaking === 'function') {
    return window.WebToApk.ttsIsSpeaking() === true;
  }
  return false;
}

var btn = document.getElementById('read-btn');
setInterval(function () {
  btn.textContent = isSpeaking() ? 'Stop' : 'Read aloud';
}, 500);

انتظر حتى يتوقف التطبيق عن الكلام قبل أن تبدأ التسجيل، حتى لا يلتقط الميكروفون صوته:

function whenQuiet(fn) {
  if (!(window.WebToApk && typeof window.WebToApk.ttsIsSpeaking === 'function')) { fn(); return; }
  (function check() {
    if (window.WebToApk.ttsIsSpeaking()) setTimeout(check, 300);
    else fn();
  })();
}

document.getElementById('answer-btn').addEventListener('click', function () {
  whenQuiet(startRecordingAnswer);   // e.g. after the question was read aloud
});

ملاحظات: لا تستعلم بسرعة؛ الحدث (onend، أو appmint:tts مع event: 'end') أفضل عندما بدأت الكلام بنفسك.

ttsSpeak الجسر (bridge)#

window.WebToApk.ttsSpeak(text: String, lang: String, rate: Double, pitch: Double, utteranceId: String)

يدعم بديل window.speechSynthesis الذي يُثبَّت عند بدء المستند في كل إطار؛ ينبغي للصفحات استخدام واجهة speechSynthesis القياسية وسيُستدعى هذا نيابةً عنها. لا يأتي Android WebView بـ speechSynthesis عامل خاص به، ولهذا وُجد إعادة التوجيه.

موصوف ضمن مجموعته، تحويل النص إلى كلام، وليس على حدة.

مثال

يبدأ نطق النص بمحرك تحويل النص إلى كلام في الهاتف. الصفحات تستخدم عادةً واجهة speechSynthesis القياسية، التي تستدعي هذا نيابةً عنك.

يُرجع: لا شيء. يصل التقدم لاحقًا كحدث appmint:tts (والخطّاف window.onAppMintTts(detail)) مع detail = { id, event }، حيث event هو 'start' أو 'end' أو 'error'، وid هو utteranceId الذي مررته. يتطلب: لا شيء.

الطريقة العامة (استخدم هذه):

if ('speechSynthesis' in window) {
  var u = new SpeechSynthesisUtterance('Your order is ready');
  u.lang = 'en-GB';
  u.onend = function () { console.log('done'); };
  speechSynthesis.speak(u);
}

الاستدعاء الخام، إن أردت معرّفاتك الخاصة. الوسائط: text, lang, rate, pitch, utteranceId:

var ttsWaiting = {};

window.addEventListener('appmint:tts', function (e) {
  var d = e.detail || {};
  if (d.event === 'voiceschanged') return;        // engine ready, id is ''
  var done = ttsWaiting[d.id];
  if (!done || d.event === 'start') return;
  delete ttsWaiting[d.id];
  done(d.event === 'end');                         // 'end' = spoke it, 'error' = failed
});

function speakRaw(text) {
  return new Promise(function (resolve) {
    if (!(window.WebToApk && typeof window.WebToApk.ttsSpeak === 'function')) { resolve(false); return; }
    var id = 'mine_' + Date.now();
    ttsWaiting[id] = resolve;
    window.WebToApk.ttsSpeak(String(text), 'en-US', 1.0, 1.0, id);
  });
}

speakRaw('Hello').then(function (ok) { console.log(ok ? 'spoken' : 'could not speak'); });

اقرأ قائمة خطوات بالترتيب بمعرّفاتك الخاصة، وأبرز الخطوة التي تُنطق:

var steps = Array.prototype.slice.call(document.querySelectorAll('.recipe-step'));

window.addEventListener('appmint:tts', function (e) {
  var d = e.detail || {};
  var m = /^recipe_(\d+)$/.exec(d.id || '');
  if (!m) return;                                  // not one of ours
  var el = steps[Number(m[1])];
  if (d.event === 'start') el.classList.add('reading');
  if (d.event === 'end' || d.event === 'error') el.classList.remove('reading');
});

document.getElementById('read-recipe').addEventListener('click', function () {
  if (!(window.WebToApk && typeof window.WebToApk.ttsSpeak === 'function')) return;
  steps.forEach(function (el, i) {
    // '' = the engine's default voice; 0.9 = a little slower for cooking
    window.WebToApk.ttsSpeak(el.textContent, '', 0.9, 1.0, 'recipe_' + i);
  });
});

ملاحظات: تصطف الأقوال واحدًا بعد الآخر. قد يكون lang هو '' (صوت المحرك الافتراضي)؛ وrate وpitch محصوران بين 0.1 و4. إذا كان المحرك ما زال يبدأ، ينتظر النص وينطق عندما يجهز؛ وإذا تعذّر بدء المحرك يحصل كل نطق منتظر على 'error'. استخدم بادئة معرّف خاصة بك؛ فالمعرّفات التي تبدأ بـ wta_u تخص طبقة speechSynthesis. على Android 8+ يحمل appmint:tts أيضًا {id, event: 'boundary', charIndex, charLength} قبل كل كلمة. هذا الاستدعاء الخام لا volume ولا voice فيه، أما speechSynthesis ففيه كلاهما.

ttsWarmUp الجسر (bridge)#

window.WebToApk.ttsWarmUp()

يدعم بديل window.speechSynthesis الذي يُثبَّت عند بدء المستند في كل إطار؛ ينبغي للصفحات استخدام واجهة speechSynthesis القياسية وسيُستدعى هذا نيابةً عنها. لا يأتي Android WebView بـ speechSynthesis عامل خاص به، ولهذا وُجد إعادة التوجيه.

موصوف ضمن مجموعته، تحويل النص إلى كلام، وليس على حدة.

مثال

يشغّل محرك تحويل النص إلى كلام في الهاتف مبكرًا، فلا يكون هناك تأخير في أول speak() ويمتلئ getVoices(). طبقة speechSynthesis في التطبيق تستدعي هذا أصلًا عند بدء الصفحة، فلا تحتاجه الصفحات تقريبًا أبدًا.

يُرجع: لا شيء. عندما يجهز المحرك، ينطلق الحدث appmint:tts مع detail = { id: '', event: 'voiceschanged' } (ويعمل speechSynthesis.onvoiceschanged). يتطلب: لا شيء.

الطريقة العامة (استخدم هذه): اقرأ الأصوات عندما تكون جاهزة.

if ('speechSynthesis' in window) {
  speechSynthesis.onvoiceschanged = function () {
    console.log('voices:', speechSynthesis.getVoices().length);
  };
}

الاستدعاء الخام، ثم انتظر حدث الجاهزية:

window.addEventListener('appmint:tts', function (e) {
  if (e.detail && e.detail.event === 'voiceschanged') {
    var list = JSON.parse(window.WebToApk.ttsGetVoices());
    console.log('engine ready,', list.length, 'voices');
  }
});

if (window.WebToApk && typeof window.WebToApk.ttsWarmUp === 'function') {
  window.WebToApk.ttsWarmUp();   // safe to call many times
}

شغّل المحرك عند فتح شاشة "القراءة بصوت عالٍ" وفعّل زرها عندما يجهز المحرك:

var readBtn = document.getElementById('read-aloud');

function engineReady() {
  try { return JSON.parse(window.WebToApk.ttsGetVoices()).length > 0; } catch (e) { return false; }
}

function openReaderScreen() {
  showReaderScreen();
  if (!(window.WebToApk && typeof window.WebToApk.ttsWarmUp === 'function')) return;
  readBtn.disabled = !engineReady();       // already started earlier? enable right away
  window.WebToApk.ttsWarmUp();             // no-op when the engine is already running
}

window.addEventListener('appmint:tts', function (e) {
  if (e.detail && e.detail.event === 'voiceschanged') readBtn.disabled = false;
});

ملاحظات: ينطلق voiceschanged مرة واحدة، عندما ينتهي المحرك من البدء. إذا كان قد بدأ أصلًا (مثلًا لأن طبقة speechSynthesis سخّنته) فلن تحصل عليه مجددًا؛ استدعِ ttsGetVoices() مباشرة.

يتم إنشاؤه من بيئة تشغيل التطبيق وملفات الأمثلة الخاصة به عند كل بناء للمستندات. اقرأه بصيغة Markdown · جميع العائلات.

تمت المراجعة مقابل الجسر المُرفق بتاريخ 2026-10-22.