检测浏览器对SSML的支持及跨浏览器适配方案
检测SSML支持的可靠方法(无需浏览器嗅探)
不用纠结浏览器类型,我们可以直接检测SpeechSynthesis API对SSML的原生支持,这样更准确也更符合现代前端的最佳实践。下面是两种递进的方案:
方案1:利用原生ssmlSupported属性(优先推荐)
部分现代浏览器(比如安卓Chrome、新版Edge等)会给SpeechSynthesisVoice实例提供ssmlSupported属性,直接告诉你当前语音是否支持SSML解析。
实现代码:
function isSSMLSupported() { // 先检查浏览器是否支持SpeechSynthesis API if (!window.speechSynthesis) { return false; } // 获取所有可用语音 const voices = window.speechSynthesis.getVoices(); // 如果有至少一个语音支持SSML,就返回true return voices.some(voice => voice.ssmlSupported === true); } // 使用示例 if (isSSMLSupported()) { // 支持SSML,使用带标签的文本 const utterance = new SpeechSynthesisUtterance('<?xml version="1.0"?><speak>Intro <break time="200ms"/>the rest.</speak>'); window.speechSynthesis.speak(utterance); } else { // 不支持,使用普通文本 const utterance = new SpeechSynthesisUtterance('Intro the rest.'); window.speechSynthesis.speak(utterance); }
注意点:
speechSynthesis.getVoices()在页面加载时可能是空的,因为语音列表是异步加载的。如果遇到这种情况,可以监听voiceschanged事件再执行检测:
let voicesLoaded = false; window.speechSynthesis.onvoiceschanged = () => { voicesLoaded = true; }; // 调用检测前确保语音已加载 function checkSSMLSupport() { if (!voicesLoaded) { setTimeout(checkSSMLSupport, 100); return; } return isSSMLSupported(); }
方案2:Fallback测试(针对不支持ssmlSupported的浏览器,比如Linux Chrome)
如果浏览器没有提供ssmlSupported属性,我们可以通过对比SSML文本和普通文本的播放时长来间接判断:包含<break>的SSML文本播放时长应该比普通文本长。
实现代码:
async function testSSMLSupport() { if (!window.speechSynthesis) { return false; } // 等待语音加载完成 await new Promise(resolve => { if (window.speechSynthesis.getVoices().length > 0) { resolve(); } else { window.speechSynthesis.onvoiceschanged = resolve; } }); return new Promise(resolve => { // 测试用的SSML文本(带200ms停顿) const ssmlText = '<?xml version="1.0"?><speak>Test <break time="200ms"/>pause.</speak>'; // 对应的普通文本 const plainText = 'Test pause.'; let ssmlDuration = 0; let plainDuration = 0; let testsCompleted = 0; // 定义测试单个文本时长的函数 function testDuration(text, isSSML) { const utterance = new SpeechSynthesisUtterance(text); utterance.onstart = () => { utterance.startTime = Date.now(); }; utterance.onend = () => { const duration = Date.now() - utterance.startTime; if (isSSML) { ssmlDuration = duration; } else { plainDuration = duration; } testsCompleted++; if (testsCompleted === 2) { // 如果SSML时长比普通文本长至少150ms(考虑误差),说明支持SSML resolve(ssmlDuration > plainDuration + 150); } }; // 播放时静音,避免干扰用户 utterance.volume = 0; window.speechSynthesis.speak(utterance); } // 先测试普通文本,再测试SSML testDuration(plainText, false); setTimeout(() => testDuration(ssmlText, true), 1000); }); } // 使用示例 testSSMLSupport().then(supported => { if (supported) { // 支持SSML window.speechSynthesis.speak(new SpeechSynthesisUtterance('<?xml version="1.0"?><speak>Intro <break time="200ms"/>the rest.</speak>')); } else { // 不支持 window.speechSynthesis.speak(new SpeechSynthesisUtterance('Intro the rest.')); } });
注意点:
- 这个方法依赖于实际播放时长的对比,可能会有微小误差,所以设置了150ms的阈值(比200ms停顿略小)来过滤偶然情况。
- 测试时把音量设为0,避免打扰用户体验。
两种方案结合起来用的话,先检查ssmlSupported属性,不支持的话再用fallback测试,这样就能覆盖绝大多数浏览器场景了。
内容的提问来源于stack exchange,提问作者havlock
相关产品推荐
相关产品推荐

