@ssml-builder-js/ssml-editor-react 2.12.0 → 2.14.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/dist/index.js CHANGED
@@ -33,6 +33,7 @@ var index_exports = {};
33
33
  __export(index_exports, {
34
34
  EDITOR_COPY: () => EDITOR_COPY,
35
35
  INLINE_BADGE_COPY: () => INLINE_BADGE_COPY,
36
+ SSML_ELEMENT_COPY: () => SSML_ELEMENT_COPY,
36
37
  SSML_HOVER_COPY: () => SSML_HOVER_COPY,
37
38
  SSML_INSERTIONS: () => SSML_INSERTIONS,
38
39
  SsmlEditor: () => SsmlEditor,
@@ -820,6 +821,152 @@ var SILENCE_VALUE_DESCRIPTIONS = {
820
821
  };
821
822
 
822
823
  // src/locales.ts
824
+ var SSML_ELEMENT_COPY = {
825
+ ja: {
826
+ dialog: {
827
+ label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
828
+ description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
829
+ validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
830
+ },
831
+ "mstts:dialog": {
832
+ label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
833
+ description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
834
+ validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
835
+ },
836
+ turn: {
837
+ label: "\u8A71\u8005\u30BF\u30FC\u30F3",
838
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
839
+ validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
840
+ },
841
+ "mstts:turn": {
842
+ label: "\u8A71\u8005\u30BF\u30FC\u30F3",
843
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
844
+ validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
845
+ },
846
+ backgroundaudio: {
847
+ label: "\u80CC\u666F\u97F3\u58F0",
848
+ description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
849
+ validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
850
+ },
851
+ "mstts:backgroundaudio": {
852
+ label: "\u80CC\u666F\u97F3\u58F0",
853
+ description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
854
+ validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
855
+ },
856
+ ttsembedding: {
857
+ label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
858
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
859
+ validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
860
+ },
861
+ "mstts:ttsembedding": {
862
+ label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
863
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
864
+ validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
865
+ },
866
+ embedding: {
867
+ label: "\u57CB\u3081\u8FBC\u307F",
868
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
869
+ validationErrors: {}
870
+ },
871
+ "mstts:embedding": {
872
+ label: "\u57CB\u3081\u8FBC\u307F",
873
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
874
+ validationErrors: {}
875
+ },
876
+ silence: {
877
+ label: "\u7121\u97F3",
878
+ description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
879
+ validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
880
+ },
881
+ "mstts:silence": {
882
+ label: "\u7121\u97F3",
883
+ description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
884
+ validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
885
+ },
886
+ voiceconversion: {
887
+ label: "\u97F3\u58F0\u5909\u63DB",
888
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
889
+ validationErrors: {}
890
+ },
891
+ "mstts:voiceconversion": {
892
+ label: "\u97F3\u58F0\u5909\u63DB",
893
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
894
+ validationErrors: {}
895
+ }
896
+ },
897
+ en: {
898
+ dialog: {
899
+ label: "Dialog",
900
+ description: "Groups multiple speaker turns.",
901
+ validationErrors: { turn: "Place turns directly inside a dialog." }
902
+ },
903
+ "mstts:dialog": {
904
+ label: "Dialog",
905
+ description: "Groups multiple speaker turns.",
906
+ validationErrors: { turn: "Place turns directly inside a dialog." }
907
+ },
908
+ turn: {
909
+ label: "Dialog turn",
910
+ description: "Specifies a speaker and utterance within a dialog.",
911
+ validationErrors: { voice: "Provide a voice or speaker." }
912
+ },
913
+ "mstts:turn": {
914
+ label: "Dialog turn",
915
+ description: "Specifies a speaker and utterance within a dialog.",
916
+ validationErrors: { voice: "Provide a voice or speaker." }
917
+ },
918
+ backgroundaudio: {
919
+ label: "Background audio",
920
+ description: "Specifies an audio file played behind synthesized speech.",
921
+ validationErrors: { src: "Provide a background audio URL." }
922
+ },
923
+ "mstts:backgroundaudio": {
924
+ label: "Background audio",
925
+ description: "Specifies an audio file played behind synthesized speech.",
926
+ validationErrors: { src: "Provide a background audio URL." }
927
+ },
928
+ ttsembedding: {
929
+ label: "TTS embedding",
930
+ description: "Embeds custom voice or speaker profile metadata.",
931
+ validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
932
+ },
933
+ "mstts:ttsembedding": {
934
+ label: "TTS embedding",
935
+ description: "Embeds custom voice or speaker profile metadata.",
936
+ validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
937
+ },
938
+ embedding: {
939
+ label: "Embedding",
940
+ description: "Specifies speaker embedding metadata.",
941
+ validationErrors: {}
942
+ },
943
+ "mstts:embedding": {
944
+ label: "Embedding",
945
+ description: "Specifies speaker embedding metadata.",
946
+ validationErrors: {}
947
+ },
948
+ silence: {
949
+ label: "Silence",
950
+ description: "Adds silence before, after, or around punctuation.",
951
+ validationErrors: { value: "Provide a silence duration." }
952
+ },
953
+ "mstts:silence": {
954
+ label: "Silence",
955
+ description: "Adds silence before, after, or around punctuation.",
956
+ validationErrors: { value: "Provide a silence duration." }
957
+ },
958
+ voiceconversion: {
959
+ label: "Voice conversion",
960
+ description: "Specifies custom voice conversion metadata.",
961
+ validationErrors: {}
962
+ },
963
+ "mstts:voiceconversion": {
964
+ label: "Voice conversion",
965
+ description: "Specifies custom voice conversion metadata.",
966
+ validationErrors: {}
967
+ }
968
+ }
969
+ };
823
970
  var EDITOR_COPY = {
824
971
  ja: {
825
972
  editorAriaLabel: "SSML\u30A8\u30C7\u30A3\u30BF\u30FC",
@@ -1037,6 +1184,42 @@ var SSML_HOVER_COPY = {
1037
1184
  parameters: {
1038
1185
  value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
1039
1186
  }
1187
+ },
1188
+ "mstts:dialog": { title: "\u30C0\u30A4\u30A2\u30ED\u30B0", description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002", parameters: {} },
1189
+ "mstts:turn": {
1190
+ title: "\u8A71\u8005\u30BF\u30FC\u30F3",
1191
+ description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1192
+ parameters: {
1193
+ voice: { title: "voice", description: "\u3053\u306E\u30BF\u30FC\u30F3\u3067\u4F7F\u7528\u3059\u308B Azure \u97F3\u58F0\u540D\u3002" },
1194
+ speaker: { title: "speaker", description: "\u8A71\u8005\u8B58\u5225\u5B50\u3002" }
1195
+ }
1196
+ },
1197
+ "mstts:backgroundaudio": {
1198
+ title: "\u80CC\u666F\u97F3\u58F0",
1199
+ description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1200
+ parameters: { src: { title: "src", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u306E HTTPS URL\u3002" } }
1201
+ },
1202
+ "mstts:ttsembedding": {
1203
+ title: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
1204
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
1205
+ parameters: { speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" } }
1206
+ },
1207
+ "mstts:embedding": {
1208
+ title: "\u57CB\u3081\u8FBC\u307F",
1209
+ description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1210
+ parameters: {
1211
+ id: { title: "id", description: "\u57CB\u3081\u8FBC\u307F ID\u3002" },
1212
+ speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
1213
+ }
1214
+ },
1215
+ "mstts:voiceconversion": {
1216
+ title: "\u97F3\u58F0\u5909\u63DB",
1217
+ description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
1218
+ parameters: {
1219
+ url: { title: "url", description: "\u97F3\u58F0\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB URL\u3002" },
1220
+ profile: { title: "profile", description: "\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u3002" },
1221
+ speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
1222
+ }
1040
1223
  }
1041
1224
  }
1042
1225
  },
@@ -1201,6 +1384,42 @@ var SSML_HOVER_COPY = {
1201
1384
  parameters: {
1202
1385
  value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
1203
1386
  }
1387
+ },
1388
+ "mstts:dialog": { title: "Dialog", description: "Groups multiple speaker turns.", parameters: {} },
1389
+ "mstts:turn": {
1390
+ title: "Dialog turn",
1391
+ description: "Specifies a speaker and utterance within a dialog.",
1392
+ parameters: {
1393
+ voice: { title: "voice", description: "The Azure voice used by this turn." },
1394
+ speaker: { title: "speaker", description: "The speaker identifier." }
1395
+ }
1396
+ },
1397
+ "mstts:backgroundaudio": {
1398
+ title: "Background audio",
1399
+ description: "Specifies an audio file played behind synthesized speech.",
1400
+ parameters: { src: { title: "src", description: "The HTTPS URL of the audio file." } }
1401
+ },
1402
+ "mstts:ttsembedding": {
1403
+ title: "TTS embedding",
1404
+ description: "Embeds custom voice or speaker profile metadata.",
1405
+ parameters: { speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." } }
1406
+ },
1407
+ "mstts:embedding": {
1408
+ title: "Embedding",
1409
+ description: "Specifies speaker embedding metadata.",
1410
+ parameters: {
1411
+ id: { title: "id", description: "The embedding ID." },
1412
+ speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
1413
+ }
1414
+ },
1415
+ "mstts:voiceconversion": {
1416
+ title: "Voice conversion",
1417
+ description: "Specifies custom voice conversion metadata.",
1418
+ parameters: {
1419
+ url: { title: "url", description: "The voice conversion profile URL." },
1420
+ profile: { title: "profile", description: "The conversion profile." },
1421
+ speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
1422
+ }
1204
1423
  }
1205
1424
  }
1206
1425
  }
@@ -4810,14 +5029,102 @@ var VISUAL_ELEMENT_FIELDS = {
4810
5029
  function getElementFields(element) {
4811
5030
  return VISUAL_ELEMENT_FIELDS[elementLabel(element)] ?? [];
4812
5031
  }
5032
+ function localizedElementLabel(element, locale) {
5033
+ return SSML_ELEMENT_COPY[locale][elementLabel(element)]?.label ?? elementLabel(element);
5034
+ }
5035
+ function filteredVoices(voiceCatalog, locale, region, style) {
5036
+ return (voiceCatalog ?? []).filter((voice) => {
5037
+ if (locale && voice.locale.toLowerCase() !== locale.toLowerCase()) return false;
5038
+ const regions = [voice.region, ...voice.regions ?? []].filter(
5039
+ (candidate) => Boolean(candidate)
5040
+ );
5041
+ if (region && regions.length > 0 && !regions.some((candidate) => candidate.toLowerCase() === region.toLowerCase()))
5042
+ return false;
5043
+ if (style && !voice.styles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase())) return false;
5044
+ return true;
5045
+ });
5046
+ }
5047
+ function VoiceCapabilityMatrix({
5048
+ voice,
5049
+ locale
5050
+ }) {
5051
+ const status = voice.status ?? (voice.preview ? "preview" : void 0);
5052
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ssml-editor-voice-capabilities", "data-voice-capabilities": "", children: [
5053
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
5054
+ locale === "ja" ? "\u30B9\u30C6\u30FC\u30BF\u30B9" : "Status",
5055
+ ": ",
5056
+ status?.toUpperCase() ?? (locale === "ja" ? "GA" : "GA"),
5057
+ status === "preview" || status === "deprecated" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { role: "status", "aria-label": status, className: "ssml-editor-voice-warning-badge", children: status === "preview" ? "\u26A0 Preview" : "\u26A0 Deprecated" }) : null
5058
+ ] }),
5059
+ (voice.regions?.length || voice.region) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
5060
+ locale === "ja" ? "\u30EA\u30FC\u30B8\u30E7\u30F3" : "Regions",
5061
+ ":",
5062
+ " ",
5063
+ [...voice.regions ?? [], ...voice.region ? [voice.region] : []].filter((region, index, all) => all.indexOf(region) === index).join(", ")
5064
+ ] }),
5065
+ voice.supportedTags && voice.supportedTags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
5066
+ locale === "ja" ? "\u5BFE\u5FDC\u30BF\u30B0" : "Supported tags",
5067
+ ": ",
5068
+ voice.supportedTags.join(", ")
5069
+ ] }),
5070
+ voice.unsupportedTags && voice.unsupportedTags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ssml-editor-voice-unsupported", children: [
5071
+ locale === "ja" ? "\u975E\u5BFE\u5FDC\u30BF\u30B0" : "Unsupported tags",
5072
+ ": ",
5073
+ voice.unsupportedTags.join(", ")
5074
+ ] })
5075
+ ] });
5076
+ }
5077
+ function DefaultVoiceSelector({
5078
+ value,
5079
+ voices,
5080
+ selectedVoice,
5081
+ readOnly,
5082
+ locale,
5083
+ onChange
5084
+ }) {
5085
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5086
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
5087
+ "select",
5088
+ {
5089
+ "aria-label": locale === "ja" ? "\u97F3\u58F0" : "Voice",
5090
+ value,
5091
+ disabled: readOnly,
5092
+ onChange: (event) => onChange(event.target.value),
5093
+ children: [
5094
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: locale === "ja" ? "\u97F3\u58F0\u3092\u9078\u629E" : "Select a voice" }),
5095
+ voices.map((voice) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("option", { value: voice.name, children: [
5096
+ voice.name,
5097
+ voice.preview || voice.status === "preview" ? " (preview)" : ""
5098
+ ] }, voice.name))
5099
+ ]
5100
+ }
5101
+ ),
5102
+ selectedVoice ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(VoiceCapabilityMatrix, { voice: selectedVoice, locale }) : null
5103
+ ] });
5104
+ }
4813
5105
  function VisualElementInspector({
4814
5106
  document: document2,
4815
5107
  element,
4816
5108
  path,
4817
5109
  readOnly,
4818
- commit
5110
+ commit,
5111
+ locale,
5112
+ customInspector,
5113
+ renderVoiceSelector,
5114
+ voiceCatalog,
5115
+ voiceLocale,
5116
+ voiceRegion,
5117
+ voiceStyle
4819
5118
  }) {
5119
+ if (customInspector) {
5120
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: customInspector({ document: document2, element, path, readOnly, onChange: commit, locale }) });
5121
+ }
4820
5122
  const fields = getElementFields(element);
5123
+ const availableVoices = filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle);
5124
+ const selectedVoice = voiceCatalog?.find(
5125
+ (voice) => voice.name === (element.type === "voice" ? element.name : void 0)
5126
+ );
5127
+ const renderSelector = renderVoiceSelector ?? DefaultVoiceSelector;
4821
5128
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
4822
5129
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: `<${elementLabel(element)}>` }),
4823
5130
  fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "This element is preserved in the visual tree. Edit its attributes in Code mode." }) : fields.map((field) => {
@@ -4825,8 +5132,48 @@ function VisualElementInspector({
4825
5132
  const inputValue = value === void 0 ? "" : String(value);
4826
5133
  const inputId = `ssml-visual-${field.key}`;
4827
5134
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { htmlFor: inputId, children: [
4828
- field.label,
4829
- field.multiline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5135
+ locale === "ja" ? {
5136
+ name: "\u540D\u524D",
5137
+ effect: "\u52B9\u679C",
5138
+ rate: "\u901F\u5EA6",
5139
+ pitch: "\u30D4\u30C3\u30C1",
5140
+ volume: "\u97F3\u91CF",
5141
+ contour: "\u30D4\u30C3\u30C1\u66F2\u7DDA",
5142
+ range: "\u7BC4\u56F2",
5143
+ style: "\u30B9\u30BF\u30A4\u30EB",
5144
+ styleDegree: "\u30B9\u30BF\u30A4\u30EB\u5F37\u5EA6",
5145
+ role: "\u5F79\u5272",
5146
+ interpretAs: "\u89E3\u91C8",
5147
+ alphabet: "\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8",
5148
+ ph: "\u767A\u97F3",
5149
+ level: "\u5F37\u8ABF\u30EC\u30D9\u30EB",
5150
+ src: "\u97F3\u58F0 URL",
5151
+ desc: "\u8AAC\u660E",
5152
+ clipBegin: "\u958B\u59CB\u4F4D\u7F6E",
5153
+ clipEnd: "\u7D42\u4E86\u4F4D\u7F6E",
5154
+ speed: "\u901F\u5EA6",
5155
+ repeatCount: "\u7E70\u308A\u8FD4\u3057\u56DE\u6570",
5156
+ repeatDuration: "\u7E70\u308A\u8FD4\u3057\u6642\u9593",
5157
+ soundLevel: "\u97F3\u91CF\u30EC\u30D9\u30EB",
5158
+ mark: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u540D",
5159
+ alias: "\u5225\u540D",
5160
+ lang: "\u8A00\u8A9E",
5161
+ uri: "\u8F9E\u66F8 URI",
5162
+ typeValue: "\u7121\u97F3\u306E\u7A2E\u985E",
5163
+ value: "\u5024",
5164
+ id: "\u57CB\u3081\u8FBC\u307F ID",
5165
+ speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID",
5166
+ url: "\u97F3\u58F0\u5909\u63DB URL",
5167
+ profile: "\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB"
5168
+ }[field.key] ?? field.label : field.label,
5169
+ field.key === "name" && elementLabel(element) === "voice" && voiceCatalog ? renderSelector({
5170
+ value: inputValue,
5171
+ voices: availableVoices,
5172
+ selectedVoice,
5173
+ readOnly,
5174
+ locale,
5175
+ onChange: (value2) => commit(updateOptionalElementProperty(document2, path, field.key, value2))
5176
+ }) : field.multiline ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4830
5177
  "textarea",
4831
5178
  {
4832
5179
  id: inputId,
@@ -4922,13 +5269,21 @@ function VisualSsmlEditor({
4922
5269
  document: document2,
4923
5270
  readOnly = false,
4924
5271
  onChange,
4925
- onPreviewSelection
5272
+ onPreviewSelection,
5273
+ locale = "en",
5274
+ customInspectors,
5275
+ renderVoiceSelector,
5276
+ voiceCatalog,
5277
+ voiceLocale,
5278
+ voiceRegion,
5279
+ voiceStyle
4926
5280
  }) {
4927
5281
  const [selectedPath, setSelectedPath] = (0, import_react3.useState)(null);
4928
5282
  const [selection, setSelection] = (0, import_react3.useState)({ start: 0, end: 0 });
4929
5283
  const textLeaves = (0, import_react3.useMemo)(() => collectTextLeaves(document2.children ?? []), [document2]);
4930
5284
  const selectedLeaf = textLeaves.find((leaf) => leaf.path.join(".") === selectedPath?.join(".")) ?? textLeaves[0];
4931
5285
  const selectedElement = selectedPath ? getNodeAtPath(document2.children ?? [], selectedPath) : void 0;
5286
+ const selectedCustomInspector = selectedElement && isElement(selectedElement) ? customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type] : void 0;
4932
5287
  const diagnostics = (0, import_react3.useMemo)(() => (0, import_ssml_core4.validateAzureSsml)((0, import_ssml_core4.buildSsml)(document2)), [document2]);
4933
5288
  const commit = (nextDocument) => onChange?.(nextDocument);
4934
5289
  const applyWrapper = (tag, attributes) => {
@@ -4963,25 +5318,40 @@ function VisualSsmlEditor({
4963
5318
  isElement(node) ? elementLabel(node) : JSON.stringify(node)
4964
5319
  )) })
4965
5320
  ] }),
4966
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ssml-editor-visual-form", children: selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
4967
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Dialog" }),
4968
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "Add and edit speaker turns in this dialog." }),
5321
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ssml-editor-visual-form", children: selectedCustomInspector && selectedElement && isElement(selectedElement) ? selectedCustomInspector({
5322
+ document: document2,
5323
+ element: selectedElement,
5324
+ path: selectedPath ?? [],
5325
+ readOnly,
5326
+ onChange: commit,
5327
+ locale
5328
+ }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:dialog" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
5329
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: localizedElementLabel(selectedElement, locale) }),
5330
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: SSML_ELEMENT_COPY[locale][elementLabel(selectedElement)]?.description }),
4969
5331
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4970
5332
  "button",
4971
5333
  {
4972
5334
  type: "button",
4973
5335
  disabled: readOnly,
4974
5336
  onClick: () => commit(addDialogTurn(document2, selectedPath ?? [])),
4975
- children: "Add turn"
5337
+ children: locale === "ja" ? "\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0" : "Add turn"
4976
5338
  }
4977
5339
  )
4978
5340
  ] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:turn" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
4979
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Dialog turn" }),
4980
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4981
- "Voice",
4982
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5341
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: localizedElementLabel(selectedElement, locale) }),
5342
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { htmlFor: "ssml-visual-turn-voice", children: [
5343
+ locale === "ja" ? "\u97F3\u58F0" : "Voice",
5344
+ voiceCatalog ? (renderVoiceSelector ?? DefaultVoiceSelector)({
5345
+ value: selectedElement.voice ?? "",
5346
+ voices: filteredVoices(voiceCatalog, voiceLocale, voiceRegion, voiceStyle),
5347
+ selectedVoice: voiceCatalog?.find((voice) => voice.name === selectedElement.voice),
5348
+ readOnly,
5349
+ locale,
5350
+ onChange: (value) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", value))
5351
+ }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4983
5352
  "input",
4984
5353
  {
5354
+ id: "ssml-visual-turn-voice",
4985
5355
  value: selectedElement.voice ?? "",
4986
5356
  readOnly,
4987
5357
  onChange: (event) => commit(updateOptionalElementProperty(document2, selectedPath ?? [], "voice", event.target.value))
@@ -4989,7 +5359,7 @@ function VisualSsmlEditor({
4989
5359
  )
4990
5360
  ] }),
4991
5361
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
4992
- "Speaker",
5362
+ locale === "ja" ? "\u8A71\u8005" : "Speaker",
4993
5363
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4994
5364
  "input",
4995
5365
  {
@@ -5000,7 +5370,7 @@ function VisualSsmlEditor({
5000
5370
  )
5001
5371
  ] }),
5002
5372
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
5003
- "Turn text",
5373
+ locale === "ja" ? "\u767A\u8A71\u30C6\u30AD\u30B9\u30C8" : "Turn text",
5004
5374
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5005
5375
  "textarea",
5006
5376
  {
@@ -5017,9 +5387,9 @@ function VisualSsmlEditor({
5017
5387
  )
5018
5388
  ] })
5019
5389
  ] }) : selectedElement && isElement(selectedElement) && selectedElement.type === "mstts:backgroundaudio" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("fieldset", { children: [
5020
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: "Background audio" }),
5390
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("legend", { children: localizedElementLabel(selectedElement, locale) }),
5021
5391
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
5022
- "Source URL",
5392
+ locale === "ja" ? "\u97F3\u58F0 URL" : "Source URL",
5023
5393
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5024
5394
  "input",
5025
5395
  {
@@ -5030,7 +5400,7 @@ function VisualSsmlEditor({
5030
5400
  )
5031
5401
  ] }),
5032
5402
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
5033
- "Volume",
5403
+ locale === "ja" ? "\u97F3\u91CF" : "Volume",
5034
5404
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5035
5405
  "input",
5036
5406
  {
@@ -5041,7 +5411,7 @@ function VisualSsmlEditor({
5041
5411
  )
5042
5412
  ] }),
5043
5413
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
5044
- "Fade in",
5414
+ locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A4\u30F3" : "Fade in",
5045
5415
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5046
5416
  "input",
5047
5417
  {
@@ -5052,7 +5422,7 @@ function VisualSsmlEditor({
5052
5422
  )
5053
5423
  ] }),
5054
5424
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
5055
- "Fade out",
5425
+ locale === "ja" ? "\u30D5\u30A7\u30FC\u30C9\u30A2\u30A6\u30C8" : "Fade out",
5056
5426
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5057
5427
  "input",
5058
5428
  {
@@ -5069,7 +5439,14 @@ function VisualSsmlEditor({
5069
5439
  element: selectedElement,
5070
5440
  path: selectedPath ?? [],
5071
5441
  readOnly,
5072
- commit
5442
+ commit,
5443
+ locale,
5444
+ customInspector: customInspectors?.[elementLabel(selectedElement)] ?? customInspectors?.[selectedElement.type],
5445
+ renderVoiceSelector,
5446
+ voiceCatalog,
5447
+ voiceLocale,
5448
+ voiceRegion,
5449
+ voiceStyle
5073
5450
  }
5074
5451
  ) : selectedLeaf ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5075
5452
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { children: [
@@ -5708,6 +6085,12 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
5708
6085
  insertionGroups,
5709
6086
  customInsertions,
5710
6087
  additionalInsertions,
6088
+ customInspectors,
6089
+ renderVoiceSelector,
6090
+ voiceCatalog,
6091
+ voiceLocale,
6092
+ voiceRegion,
6093
+ voiceStyle,
5711
6094
  emotionStyles,
5712
6095
  className,
5713
6096
  style,
@@ -6149,7 +6532,14 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
6149
6532
  document: draftDocument,
6150
6533
  readOnly: isReadOnly,
6151
6534
  onChange: commit,
6152
- onPreviewSelection
6535
+ onPreviewSelection,
6536
+ locale: localeProp ?? languageProp,
6537
+ customInspectors,
6538
+ renderVoiceSelector,
6539
+ voiceCatalog,
6540
+ voiceLocale,
6541
+ voiceRegion,
6542
+ voiceStyle
6153
6543
  }
6154
6544
  ) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...editorStyles.editor, minHeight: editorMinHeight }, children: [
6155
6545
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -6228,6 +6618,7 @@ var SsmlEditor = (0, import_react4.forwardRef)(function SsmlEditor2({
6228
6618
  0 && (module.exports = {
6229
6619
  EDITOR_COPY,
6230
6620
  INLINE_BADGE_COPY,
6621
+ SSML_ELEMENT_COPY,
6231
6622
  SSML_HOVER_COPY,
6232
6623
  SSML_INSERTIONS,
6233
6624
  SsmlEditor,