ssml-builder-js 2.6.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -4
- package/dist/{chunk-GF4QZHLJ.mjs → chunk-4RQETJUI.mjs} +1 -1
- package/dist/chunk-4RQETJUI.mjs.map +1 -0
- package/dist/{chunk-GTARZC43.mjs → chunk-TKD3PU5Y.mjs} +146 -43
- package/dist/chunk-TKD3PU5Y.mjs.map +1 -0
- package/dist/core.d.mts +25 -1
- package/dist/core.d.ts +25 -1
- package/dist/core.js +147 -42
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +5 -1
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +147 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-GF4QZHLJ.mjs.map +0 -1
- package/dist/chunk-GTARZC43.mjs.map +0 -1
package/dist/elements.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AudioElement, AzureSsmlValidationOptions, AzureValidationOptions, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, parseSsml, validateAzureSsml, validateSsml } from './core.mjs';
|
|
1
|
+
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDiagnosticSource, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml } from './core.mjs';
|
|
2
2
|
|
|
3
3
|
interface TtsConfig {
|
|
4
4
|
signal?: AbortSignal;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AudioElement, AzureSsmlValidationOptions, AzureValidationOptions, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, parseSsml, validateAzureSsml, validateSsml } from './core.js';
|
|
1
|
+
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsSilenceElement, MsttsVisemeElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDiagnosticSource, SsmlDocument, SsmlElement, SsmlElementBase, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlText, SsmlTextNodeContext, SsmlValidationError, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml } from './core.js';
|
|
2
2
|
|
|
3
3
|
interface TtsConfig {
|
|
4
4
|
signal?: AbortSignal;
|
package/dist/index.js
CHANGED
|
@@ -40,10 +40,12 @@ __export(src_exports, {
|
|
|
40
40
|
AzureTtsClient: () => AzureTtsClient,
|
|
41
41
|
AzureTtsError: () => AzureTtsError,
|
|
42
42
|
AzureTtsSdkError: () => AzureTtsSdkError,
|
|
43
|
+
areAzureLanguagesEquivalent: () => areAzureLanguagesEquivalent,
|
|
43
44
|
buildPartialSsml: () => buildPartialSsml,
|
|
44
45
|
buildSsml: () => buildSsml,
|
|
45
46
|
extractSsmlText: () => extractSsmlText,
|
|
46
47
|
mapSsmlTextNodes: () => mapSsmlTextNodes,
|
|
48
|
+
normalizeAzureLanguage: () => normalizeAzureLanguage,
|
|
47
49
|
parseSsml: () => parseSsml,
|
|
48
50
|
synthesizeSpeech: () => synthesizeSpeech,
|
|
49
51
|
validateAzureSsml: () => validateAzureSsml,
|
|
@@ -1144,7 +1146,8 @@ var ALLOWED_SAY_AS = /* @__PURE__ */ new Set([
|
|
|
1144
1146
|
"fraction",
|
|
1145
1147
|
"address",
|
|
1146
1148
|
"name",
|
|
1147
|
-
"currency"
|
|
1149
|
+
"currency",
|
|
1150
|
+
"number_digit"
|
|
1148
1151
|
]);
|
|
1149
1152
|
var ALLOWED_ROLES = /* @__PURE__ */ new Set([
|
|
1150
1153
|
"Girl",
|
|
@@ -1241,51 +1244,137 @@ function location(source, offset) {
|
|
|
1241
1244
|
const line = before.split("\n").length;
|
|
1242
1245
|
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
1243
1246
|
}
|
|
1244
|
-
function addDiagnostic(diagnostics, source, offset, message, severity = "error") {
|
|
1245
|
-
diagnostics.push({
|
|
1247
|
+
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code) {
|
|
1248
|
+
diagnostics.push({
|
|
1249
|
+
...location(source, offset),
|
|
1250
|
+
message,
|
|
1251
|
+
severity,
|
|
1252
|
+
source: "ssml-static-validator",
|
|
1253
|
+
...code ? { code } : {}
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
function isSupportedProsodyRate(value) {
|
|
1257
|
+
const trimmed = value.trim();
|
|
1258
|
+
if (/^(x-slow|slow|medium|fast|x-fast|[+-]?\d+(?:\.\d+)?%)$/.test(trimmed)) return true;
|
|
1259
|
+
const multiplier = /^(\d+(?:\.\d+)?)(x)?$/i.exec(trimmed);
|
|
1260
|
+
if (!multiplier) return false;
|
|
1261
|
+
const numericValue = Number(multiplier[1]);
|
|
1262
|
+
return numericValue >= 0.5 && numericValue <= 2;
|
|
1246
1263
|
}
|
|
1247
1264
|
function attr(token, name) {
|
|
1248
1265
|
return token.attributes.get(name.toLowerCase());
|
|
1249
1266
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1267
|
+
var ADDITIONAL_VOICE_DEFINITIONS = [
|
|
1268
|
+
{ name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" },
|
|
1269
|
+
{ name: "es-ES-ElviraNeural", locale: "es-ES" },
|
|
1270
|
+
{ name: "th-TH-PremwadeeNeural", locale: "th-TH" },
|
|
1271
|
+
{ name: "fil-PH-AngeloNeural", locale: "fil-PH" },
|
|
1272
|
+
{ name: "vi-VN-HoaiMyNeural", locale: "vi-VN" },
|
|
1273
|
+
{ name: "id-ID-GadisNeural", locale: "id-ID" },
|
|
1274
|
+
{ name: "ms-MY-YasminNeural", locale: "ms-MY" }
|
|
1275
|
+
];
|
|
1276
|
+
var DEFAULT_LANGUAGE_ALIASES = {
|
|
1277
|
+
"zh-CN": ["zh-Hans"],
|
|
1278
|
+
"zh-TW": ["zh-Hant"]
|
|
1279
|
+
};
|
|
1280
|
+
function canonicalLanguageTag(language) {
|
|
1281
|
+
const trimmed = language.trim();
|
|
1282
|
+
if (!trimmed) return "";
|
|
1283
|
+
try {
|
|
1284
|
+
return new Intl.Locale(trimmed).toString().toLowerCase();
|
|
1285
|
+
} catch {
|
|
1286
|
+
return trimmed.toLowerCase();
|
|
1259
1287
|
}
|
|
1260
|
-
return map;
|
|
1261
1288
|
}
|
|
1262
|
-
function
|
|
1263
|
-
|
|
1264
|
-
|
|
1289
|
+
function createLanguageNormalizer(options) {
|
|
1290
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
1291
|
+
const addAliasGroup = (canonical, values) => {
|
|
1292
|
+
const normalizedCanonical = canonicalLanguageTag(canonical);
|
|
1293
|
+
if (!normalizedCanonical) return;
|
|
1294
|
+
aliases.set(normalizedCanonical, normalizedCanonical);
|
|
1295
|
+
for (const value of values) {
|
|
1296
|
+
const normalizedValue = canonicalLanguageTag(value);
|
|
1297
|
+
if (normalizedValue) aliases.set(normalizedValue, normalizedCanonical);
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
for (const [canonical, values] of Object.entries(DEFAULT_LANGUAGE_ALIASES)) addAliasGroup(canonical, values);
|
|
1301
|
+
for (const [canonical, valueOrValues] of Object.entries(options.languageAliases ?? {}))
|
|
1302
|
+
addAliasGroup(canonical, typeof valueOrValues === "string" ? [valueOrValues] : valueOrValues);
|
|
1303
|
+
return (language) => {
|
|
1304
|
+
const customValue = options.normalizeLanguage ? options.normalizeLanguage(language) : language;
|
|
1305
|
+
const normalized = canonicalLanguageTag(customValue);
|
|
1306
|
+
return aliases.get(normalized) ?? normalized;
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
function normalizeAzureLanguage(language, options = {}) {
|
|
1310
|
+
return createLanguageNormalizer(options)(language);
|
|
1311
|
+
}
|
|
1312
|
+
function areAzureLanguagesEquivalent(first, second, options = {}) {
|
|
1313
|
+
const normalize = createLanguageNormalizer(options);
|
|
1314
|
+
const normalizedFirst = normalize(first);
|
|
1315
|
+
const normalizedSecond = normalize(second);
|
|
1316
|
+
if (normalizedFirst === normalizedSecond) return true;
|
|
1317
|
+
return normalizedFirst === languagePart(normalizedFirst) && languagePart(normalizedFirst) === languagePart(normalizedSecond);
|
|
1265
1318
|
}
|
|
1266
1319
|
function voiceLocalePrefix(voiceName) {
|
|
1267
1320
|
const match = /^(?<language>[A-Za-z]{2,3})-(?<region>[A-Za-z]{2}|\d{3})(?:-|$)/.exec(voiceName.trim());
|
|
1268
1321
|
if (!match?.groups) return void 0;
|
|
1322
|
+
const tag = `${match.groups.language}-${match.groups.region}`;
|
|
1269
1323
|
return {
|
|
1270
1324
|
language: match.groups.language.toLowerCase(),
|
|
1271
|
-
region: match.groups.region.toLowerCase()
|
|
1325
|
+
region: match.groups.region.toLowerCase(),
|
|
1326
|
+
tag
|
|
1272
1327
|
};
|
|
1273
1328
|
}
|
|
1274
|
-
function
|
|
1275
|
-
const match = /^(?<language>[A-Za-z]{2,3})(?:-(?<region>[A-Za-z]{2}|\d{3}))?(?:-|$)/.exec(language.trim());
|
|
1276
|
-
if (!match?.groups) return void 0;
|
|
1329
|
+
function definitionFromStyleMap(voiceName, styles) {
|
|
1277
1330
|
return {
|
|
1278
|
-
|
|
1279
|
-
|
|
1331
|
+
name: voiceName,
|
|
1332
|
+
locale: voiceLocalePrefix(voiceName)?.tag ?? "",
|
|
1333
|
+
styles
|
|
1280
1334
|
};
|
|
1281
1335
|
}
|
|
1282
|
-
function
|
|
1283
|
-
const
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1336
|
+
function normalizeVoiceCatalog(options) {
|
|
1337
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
1338
|
+
for (const [name, styles] of Object.entries(EXPRESS_AS_STYLES)) {
|
|
1339
|
+
definitions.set(name.toLowerCase(), definitionFromStyleMap(name, styles));
|
|
1340
|
+
}
|
|
1341
|
+
for (const definition of ADDITIONAL_VOICE_DEFINITIONS) definitions.set(definition.name.toLowerCase(), definition);
|
|
1342
|
+
for (const definition of options.voiceCatalog ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
1343
|
+
for (const definition of options.voiceDefinitions ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
1344
|
+
for (const definition of options.customVoiceDefinitions ?? [])
|
|
1345
|
+
definitions.set(definition.name.toLowerCase(), definition);
|
|
1346
|
+
for (const [voiceName, styles] of Object.entries(options.customVoiceStyleMap ?? {})) {
|
|
1347
|
+
const key = voiceName.toLowerCase();
|
|
1348
|
+
const current = definitions.get(key);
|
|
1349
|
+
definitions.set(key, {
|
|
1350
|
+
...current ?? definitionFromStyleMap(voiceName, styles),
|
|
1351
|
+
name: current?.name ?? voiceName,
|
|
1352
|
+
styles: styles.map((style) => style.toLowerCase())
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
return definitions;
|
|
1356
|
+
}
|
|
1357
|
+
function diagnosticSeverity(policy) {
|
|
1358
|
+
if (policy === "ignore") return void 0;
|
|
1359
|
+
return policy === "error" ? "error" : "warning";
|
|
1360
|
+
}
|
|
1361
|
+
function languagePart(language) {
|
|
1362
|
+
try {
|
|
1363
|
+
return new Intl.Locale(language).language.toLowerCase();
|
|
1364
|
+
} catch {
|
|
1365
|
+
return language.split("-")[0]?.toLowerCase() ?? "";
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
function definitionMatchesLanguage(definition, voiceName, language, normalizeLanguage) {
|
|
1369
|
+
const candidateLanguages = definition ? [definition.locale, ...definition.secondaryLocales ?? []].filter(Boolean) : [voiceLocalePrefix(voiceName)?.tag ?? ""];
|
|
1370
|
+
if (candidateLanguages.length === 0 || !language.trim()) return void 0;
|
|
1371
|
+
const normalizedLanguage = normalizeLanguage(language);
|
|
1372
|
+
const normalizedCandidates = candidateLanguages.map(normalizeLanguage);
|
|
1373
|
+
if (normalizedCandidates.includes(normalizedLanguage)) return true;
|
|
1374
|
+
if (!normalizedLanguage || !normalizedCandidates.some(Boolean)) return void 0;
|
|
1375
|
+
return normalizedLanguage === languagePart(normalizedLanguage) ? normalizedCandidates.some((candidate) => languagePart(candidate) === normalizedLanguage) : false;
|
|
1287
1376
|
}
|
|
1288
|
-
function validateElement(token, source, diagnostics, voiceName, options,
|
|
1377
|
+
function validateElement(token, source, diagnostics, voiceName, options, voiceCatalog) {
|
|
1289
1378
|
const name = token.name.toLowerCase();
|
|
1290
1379
|
if (name === "voice" && !attr(token, "name")?.trim())
|
|
1291
1380
|
addDiagnostic(diagnostics, source, token.start, '<voice> requires a non-empty "name" attribute.');
|
|
@@ -1305,7 +1394,7 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceSt
|
|
|
1305
1394
|
const rate = attr(token, "rate");
|
|
1306
1395
|
const pitch = attr(token, "pitch");
|
|
1307
1396
|
const volume = attr(token, "volume");
|
|
1308
|
-
if (rate &&
|
|
1397
|
+
if (rate && !isSupportedProsodyRate(rate))
|
|
1309
1398
|
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody rate> value "${rate}".`);
|
|
1310
1399
|
if (pitch && !/^(x-low|low|medium|high|x-high|[+-]?\d+(?:\.\d+)?(?:st|Hz|%)?)$/.test(pitch.trim()))
|
|
1311
1400
|
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody pitch> value "${pitch}".`);
|
|
@@ -1327,17 +1416,19 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceSt
|
|
|
1327
1416
|
const role = attr(token, "role");
|
|
1328
1417
|
if (role && !ALLOWED_ROLES.has(role))
|
|
1329
1418
|
addDiagnostic(diagnostics, source, token.start, `Unsupported <mstts:express-as role> value "${role}".`);
|
|
1330
|
-
const
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1419
|
+
const definition = voiceName ? voiceCatalog.get(voiceName.toLowerCase()) : void 0;
|
|
1420
|
+
const supportedStyles = definition?.styles;
|
|
1421
|
+
const severity = diagnosticSeverity(options.unsupportedStylePolicy ?? options.unknownVoicePolicy ?? "warn");
|
|
1422
|
+
if (style && definition && !supportedStyles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase()) && severity)
|
|
1333
1423
|
addDiagnostic(
|
|
1334
1424
|
diagnostics,
|
|
1335
1425
|
source,
|
|
1336
1426
|
token.start,
|
|
1337
1427
|
`Unknown style "${style}" is not supported by voice "${voiceName}" according to the configured voice style map.`,
|
|
1338
|
-
severity
|
|
1428
|
+
severity,
|
|
1429
|
+
"azure-unsupported-style"
|
|
1339
1430
|
);
|
|
1340
|
-
if (style && voiceName && !
|
|
1431
|
+
if (style && voiceName && !definition && severity)
|
|
1341
1432
|
addDiagnostic(
|
|
1342
1433
|
diagnostics,
|
|
1343
1434
|
source,
|
|
@@ -1418,7 +1509,15 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceSt
|
|
|
1418
1509
|
function validateAzureSsml(ssml, options = {}) {
|
|
1419
1510
|
const diagnostics = [];
|
|
1420
1511
|
if (typeof ssml !== "string") {
|
|
1421
|
-
return [
|
|
1512
|
+
return [
|
|
1513
|
+
{
|
|
1514
|
+
line: 1,
|
|
1515
|
+
column: 1,
|
|
1516
|
+
message: "SSML input must be a string",
|
|
1517
|
+
severity: "error",
|
|
1518
|
+
source: "ssml-static-validator"
|
|
1519
|
+
}
|
|
1520
|
+
];
|
|
1422
1521
|
}
|
|
1423
1522
|
const maxLength = options.maxLength ?? 1e4;
|
|
1424
1523
|
if (ssml.length > maxLength)
|
|
@@ -1442,32 +1541,36 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
1442
1541
|
"Azure SSML requires at least one <voice> element under <speak>."
|
|
1443
1542
|
);
|
|
1444
1543
|
const voiceName = voices[0] ? attr(voices[0], "name") : void 0;
|
|
1445
|
-
const
|
|
1544
|
+
const voiceCatalog = normalizeVoiceCatalog(options);
|
|
1545
|
+
const normalizeLanguage = createLanguageNormalizer(options);
|
|
1446
1546
|
const policySeverity = diagnosticSeverity(options.unknownVoicePolicy ?? "warn");
|
|
1447
1547
|
const voicesToValidate = options.validateNestedVoices === false ? voices.slice(0, 1) : voices;
|
|
1448
1548
|
for (const token of voicesToValidate) {
|
|
1449
1549
|
const name = attr(token, "name")?.trim();
|
|
1450
1550
|
const language = attr(token, "xml:lang")?.trim() || (speak ? attr(speak, "xml:lang")?.trim() : void 0);
|
|
1451
|
-
|
|
1551
|
+
const definition = name ? voiceCatalog.get(name.toLowerCase()) : void 0;
|
|
1552
|
+
if (name && !definition && policySeverity)
|
|
1452
1553
|
addDiagnostic(
|
|
1453
1554
|
diagnostics,
|
|
1454
1555
|
ssml,
|
|
1455
1556
|
token.start,
|
|
1456
|
-
`Unknown voice "${name}" is not registered in the voice
|
|
1457
|
-
policySeverity
|
|
1557
|
+
`Unknown voice "${name}" is not registered in the voice catalog.`,
|
|
1558
|
+
policySeverity,
|
|
1559
|
+
"azure-unknown-voice"
|
|
1458
1560
|
);
|
|
1459
|
-
if (name && language &&
|
|
1561
|
+
if (name && language && definitionMatchesLanguage(definition, name, language, normalizeLanguage) === false)
|
|
1460
1562
|
addDiagnostic(
|
|
1461
1563
|
diagnostics,
|
|
1462
1564
|
ssml,
|
|
1463
1565
|
token.start,
|
|
1464
1566
|
`Voice "${name}" does not match language "${language}"; the voice name prefix indicates a different language or region.`,
|
|
1465
|
-
"warning"
|
|
1567
|
+
"warning",
|
|
1568
|
+
"azure-locale-mismatch"
|
|
1466
1569
|
);
|
|
1467
1570
|
}
|
|
1468
1571
|
for (const token of tokens) {
|
|
1469
1572
|
const tokenVoiceName = options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
1470
|
-
validateElement(token, ssml, diagnostics, tokenVoiceName, options,
|
|
1573
|
+
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
1471
1574
|
}
|
|
1472
1575
|
return diagnostics;
|
|
1473
1576
|
}
|
|
@@ -1658,10 +1761,12 @@ _options = new WeakMap();
|
|
|
1658
1761
|
AzureTtsClient,
|
|
1659
1762
|
AzureTtsError,
|
|
1660
1763
|
AzureTtsSdkError,
|
|
1764
|
+
areAzureLanguagesEquivalent,
|
|
1661
1765
|
buildPartialSsml,
|
|
1662
1766
|
buildSsml,
|
|
1663
1767
|
extractSsmlText,
|
|
1664
1768
|
mapSsmlTextNodes,
|
|
1769
|
+
normalizeAzureLanguage,
|
|
1665
1770
|
parseSsml,
|
|
1666
1771
|
synthesizeSpeech,
|
|
1667
1772
|
validateAzureSsml,
|