agora-appbuilder-core 4.1.9 → 4.1.11-beta.2

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.
Files changed (58) hide show
  1. package/package.json +2 -2
  2. package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -3
  3. package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -2
  4. package/template/agora-rn-uikit/src/Reducer/index.ts +0 -2
  5. package/template/agora-rn-uikit/src/Rtc/Join.tsx +11 -25
  6. package/template/agora-rn-uikit/src/RtcConfigure.tsx +1 -14
  7. package/template/agora-rn-uikit/src/Utils/isBotUser.ts +1 -1
  8. package/template/android/app/build.gradle +0 -7
  9. package/template/bridge/rtm/web/Types.ts +0 -183
  10. package/template/bridge/rtm/web/index.ts +491 -423
  11. package/template/defaultConfig.js +3 -3
  12. package/template/ios/Podfile +0 -41
  13. package/template/package.json +5 -5
  14. package/template/src/assets/font-styles.css +4 -0
  15. package/template/src/assets/fonts/icomoon.ttf +0 -0
  16. package/template/src/assets/selection.json +1 -1
  17. package/template/src/atoms/ActionMenu.tsx +93 -13
  18. package/template/src/atoms/CustomIcon.tsx +1 -0
  19. package/template/src/atoms/DropDownMulti.tsx +80 -29
  20. package/template/src/atoms/Input.tsx +2 -1
  21. package/template/src/components/Controls.tsx +148 -143
  22. package/template/src/components/EventsConfigure.tsx +152 -97
  23. package/template/src/components/RTMConfigure.tsx +426 -644
  24. package/template/src/components/precall/joinCallBtn.native.tsx +7 -2
  25. package/template/src/components/precall/joinCallBtn.tsx +7 -2
  26. package/template/src/components/precall/joinWaitingRoomBtn.native.tsx +8 -3
  27. package/template/src/components/precall/joinWaitingRoomBtn.tsx +22 -4
  28. package/template/src/components/precall/textInput.tsx +45 -22
  29. package/template/src/components/precall/usePreCall.tsx +7 -0
  30. package/template/src/components/room-info/useRoomInfo.tsx +5 -0
  31. package/template/src/language/default-labels/videoCallScreenLabels.ts +27 -4
  32. package/template/src/pages/video-call/ActionSheetContent.tsx +77 -77
  33. package/template/src/pages/video-call/SidePanelHeader.tsx +81 -36
  34. package/template/src/rtm/RTMEngine.ts +33 -130
  35. package/template/src/rtm-events/constants.ts +6 -0
  36. package/template/src/rtm-events-api/Events.ts +30 -106
  37. package/template/src/subComponents/caption/Caption.tsx +48 -7
  38. package/template/src/subComponents/caption/CaptionContainer.tsx +324 -51
  39. package/template/src/subComponents/caption/CaptionIcon.tsx +35 -34
  40. package/template/src/subComponents/caption/CaptionText.tsx +103 -2
  41. package/template/src/subComponents/caption/LanguageSelectorPopup.tsx +179 -69
  42. package/template/src/subComponents/caption/Transcript.tsx +46 -11
  43. package/template/src/subComponents/caption/TranscriptIcon.tsx +27 -35
  44. package/template/src/subComponents/caption/TranscriptText.tsx +78 -3
  45. package/template/src/subComponents/caption/proto/ptoto.js +38 -4
  46. package/template/src/subComponents/caption/proto/test.proto +34 -19
  47. package/template/src/subComponents/caption/useCaption.tsx +753 -10
  48. package/template/src/subComponents/caption/useSTTAPI.tsx +118 -205
  49. package/template/src/subComponents/caption/useStreamMessageUtils.native.ts +152 -33
  50. package/template/src/subComponents/caption/useStreamMessageUtils.ts +165 -34
  51. package/template/src/subComponents/caption/utils.ts +171 -3
  52. package/template/src/utils/SdkEvents.ts +3 -0
  53. package/template/src/utils/useEndCall.ts +3 -5
  54. package/template/src/utils/useSpeechToText.ts +31 -20
  55. package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +0 -11
  56. package/template/agora-rn-uikit/src/Reducer/UserBanned.ts +0 -11
  57. package/template/bridge/rtm/web/index-legacy.ts +0 -540
  58. package/template/src/components/RTMConfigure-legacy.tsx +0 -848
@@ -36,6 +36,7 @@ import DownloadTranscriptBtn from './DownloadTranscriptBtn';
36
36
  import {useString} from '../../../src/utils/useString';
37
37
  import {
38
38
  sttSettingSpokenLanguageText,
39
+ sttSettingTranslationLanguageText,
39
40
  sttTranscriptPanelNoSearchResultsFoundText,
40
41
  sttTranscriptPanelSearchText,
41
42
  sttTranscriptPanelViewLatestText,
@@ -50,11 +51,6 @@ type NativeStreamMessageArgs = [{}, number, number, Uint8Array, number, number];
50
51
  type StreamMessageArgs = WebStreamMessageArgs | NativeStreamMessageArgs;
51
52
 
52
53
  const Transcript = (props: TranscriptProps) => {
53
- const settingSpokenLanguageLabel = useString(sttSettingSpokenLanguageText)();
54
- const searchText = useString(sttTranscriptPanelSearchText)();
55
- const noresults = useString(sttTranscriptPanelNoSearchResultsFoundText)();
56
- const viewlatest = useString(sttTranscriptPanelViewLatestText)();
57
-
58
54
  const isSmall = useIsSmall();
59
55
  const {currentLayout} = useLayout();
60
56
  const {showHeader = true} = props;
@@ -63,9 +59,20 @@ const Transcript = (props: TranscriptProps) => {
63
59
  isLangChangeInProgress,
64
60
  isSTTListenerAdded,
65
61
  setIsSTTListenerAdded,
62
+ getBotOwnerUid,
63
+ isSTTActive,
66
64
  } = useCaption();
67
65
 
66
+ const settingSpokenLanguageLabel = useString(sttSettingSpokenLanguageText)();
67
+ const settingTranslationLanguageLabel = useString<boolean>(
68
+ sttSettingTranslationLanguageText,
69
+ )(isSTTActive);
70
+ const searchText = useString(sttTranscriptPanelSearchText)();
71
+ const noresults = useString(sttTranscriptPanelNoSearchResultsFoundText)();
72
+ const viewlatest = useString(sttTranscriptPanelViewLatestText)();
73
+
68
74
  const data = meetingTranscript; // Object.entries(transcript);
75
+ console.log('[STT_PER_USER_BOT] meetingTranscript data: ', data);
69
76
 
70
77
  const [showButton, setShowButton] = React.useState(false);
71
78
 
@@ -103,6 +110,19 @@ const Transcript = (props: TranscriptProps) => {
103
110
 
104
111
  const renderItem = ({item}) => {
105
112
  return item.uid.toString().indexOf('langUpdate') !== -1 ? (
113
+ <View style={styles.langChangeContainer}>
114
+ <ImageIcon
115
+ iconType="plain"
116
+ iconSize={20}
117
+ tintColor={$config.PRIMARY_ACTION_BRAND_COLOR}
118
+ name={'globe'}
119
+ />
120
+
121
+ <Text style={styles.langChange}>
122
+ {defaultContent[item?.uid?.split('-')[1]]?.name + ' ' + item.text}
123
+ </Text>
124
+ </View>
125
+ ) : item.uid.toString().indexOf('translationUpdate') !== -1 ? (
106
126
  <View style={styles.langChangeContainer}>
107
127
  <ImageIcon
108
128
  iconType="plain"
@@ -112,15 +132,17 @@ const Transcript = (props: TranscriptProps) => {
112
132
  />
113
133
 
114
134
  <Text style={styles.langChange}>
115
- {defaultContent[item?.uid?.split('-')[1]].name + ' ' + item.text}
135
+ {defaultContent[item?.uid?.split('-')[1]]?.name + ' has ' + item.text}
116
136
  </Text>
117
137
  </View>
118
138
  ) : (
119
139
  <TranscriptText
120
- user={defaultContent[item.uid].name}
140
+ user={defaultContent[getBotOwnerUid(item.uid)]?.name || 'Speaker'}
121
141
  time={item?.time}
122
142
  value={item.text}
143
+ translations={item.translations}
123
144
  searchQuery={searchQuery}
145
+ selectedTranslationLanguage={item.selectedTranslationLanguage}
124
146
  />
125
147
  );
126
148
  };
@@ -164,9 +186,20 @@ const Transcript = (props: TranscriptProps) => {
164
186
  const handleSearch = (text: string) => {
165
187
  setSearchQuery(text);
166
188
  // Filter the data based on the search query
167
- const filteredResults = meetingTranscript.filter(item =>
168
- item.text.toLowerCase().includes(text.toLowerCase()),
169
- );
189
+ const filteredResults = meetingTranscript.filter(item => {
190
+ const searchText = text.toLowerCase();
191
+ // Search in original text
192
+ if (item.text.toLowerCase().includes(searchText)) {
193
+ return true;
194
+ }
195
+ // Search in translations if available
196
+ if (item.translations) {
197
+ return item.translations.some(translation =>
198
+ translation.text.toLowerCase().includes(searchText),
199
+ );
200
+ }
201
+ return false;
202
+ });
170
203
  setShowButton(false);
171
204
  setSearchResults(filteredResults);
172
205
  // Scroll to the top of the FlatList when searching
@@ -180,6 +213,7 @@ const Transcript = (props: TranscriptProps) => {
180
213
  };
181
214
 
182
215
  const handleStreamMessageCallback = (...args: StreamMessageArgs) => {
216
+ console.log('[STT_PER_USER_BOT] handleStreamMessageCallback', args);
183
217
  setIsSTTListenerAdded(true);
184
218
  if (isWebInternal()) {
185
219
  const [uid, data] = args as WebStreamMessageArgs;
@@ -260,7 +294,7 @@ const Transcript = (props: TranscriptProps) => {
260
294
  {isLangChangeInProgress ? (
261
295
  <View style={{flex: 1}}>
262
296
  <Loading
263
- text={settingSpokenLanguageLabel}
297
+ text={settingTranslationLanguageLabel}
264
298
  background="transparent"
265
299
  indicatorColor={$config.FONT_COLOR + hexadecimalTransparency['70%']}
266
300
  textColor={$config.FONT_COLOR + hexadecimalTransparency['70%']}
@@ -438,6 +472,7 @@ export const styles = StyleSheet.create({
438
472
  langChangeContainer: {
439
473
  marginBottom: 20,
440
474
  flexDirection: 'row',
475
+ alignItems: 'center',
441
476
  },
442
477
  footer: {
443
478
  borderWidth: 1,
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import {SidePanelType, useSidePanel} from 'customization-api';
4
4
  import IconButton, {IconButtonProps} from '../../atoms/IconButton';
5
5
  import LanguageSelectorPopup from './LanguageSelectorPopup';
6
- import {useCaption} from './useCaption';
6
+ import {LanguageTranslationConfig, useCaption} from './useCaption';
7
7
  import useSTTAPI from './useSTTAPI';
8
8
  import {useString} from '../../utils/useString';
9
9
  import {toolbarItemTranscriptText} from '../../language/default-labels/videoCallScreenLabels';
@@ -30,28 +30,22 @@ const TranscriptIcon = (props: TranscriptIconProps) => {
30
30
  isMobileView = false,
31
31
  } = props;
32
32
 
33
- const {start, restart, isAuthorizedTranscriptUser} = useSTTAPI();
34
- const {isSTTActive, language: prevLang, isSTTError} = useCaption();
35
- const isDisabled = !isAuthorizedTranscriptUser();
33
+ // const {start, restart, isAuthorizedTranscriptUser} = useSTTAPI();
34
+ const {isSTTActive, isSTTError, handleTranslateConfigChange} = useCaption();
35
+ // const isDisabled = !isAuthorizedTranscriptUser();
36
36
  const [isLanguagePopupOpen, setLanguagePopup] =
37
37
  React.useState<boolean>(false);
38
- const isFirstTimePopupOpen = React.useRef(false);
38
+ // const isFirstTimePopupOpen = React.useRef(false);
39
39
 
40
40
  const isTranscriptON = sidePanel === SidePanelType.Transcript;
41
41
  const onPress = () => {
42
- if (isSTTError) {
42
+ if (isSTTError || !isSTTActive) {
43
+ setLanguagePopup(true);
44
+ } else {
45
+ // isFirstTimePopupOpen.current = true;
43
46
  setSidePanel(
44
47
  isTranscriptON ? SidePanelType.None : SidePanelType.Transcript,
45
48
  );
46
- return;
47
- }
48
- if (isSTTActive) {
49
- setSidePanel(
50
- isTranscriptON ? SidePanelType.None : SidePanelType.Transcript,
51
- );
52
- } else {
53
- isFirstTimePopupOpen.current = true;
54
- setLanguagePopup(true);
55
49
  }
56
50
  };
57
51
 
@@ -63,13 +57,11 @@ const TranscriptIcon = (props: TranscriptIconProps) => {
63
57
  iconBackgroundColor: isTranscriptON
64
58
  ? $config.PRIMARY_ACTION_BRAND_COLOR
65
59
  : '',
66
- tintColor: isDisabled
67
- ? $config.SEMANTIC_NEUTRAL
68
- : isTranscriptON
60
+ tintColor: isTranscriptON
69
61
  ? $config.PRIMARY_ACTION_TEXT_COLOR
70
62
  : $config.SECONDARY_ACTION_COLOR,
71
63
  },
72
- disabled: isDisabled,
64
+ disabled: false,
73
65
  btnTextProps: {
74
66
  text: showLabel
75
67
  ? isOnActionSheet
@@ -85,23 +77,23 @@ const TranscriptIcon = (props: TranscriptIconProps) => {
85
77
  iconButtonProps.toolTipMessage = label(isTranscriptON);
86
78
  }
87
79
 
88
- const onConfirm = async (langChanged, language) => {
80
+ const onConfirm = async (inputTranslateConfig: LanguageTranslationConfig) => {
81
+ // isFirstTimePopupOpen.current = false;
82
+ // const method = isTranscriptON ? 'stop' : 'start';
83
+ // if (method === 'stop') return; // not closing the stt service as it will stop for whole channel
84
+ // if (method === 'start' && isSTTActive === true) return; // not triggering the start service if STT Service already started by anyone else in the channel
89
85
  setLanguagePopup(false);
90
-
91
- isFirstTimePopupOpen.current = false;
92
- const method = isTranscriptON ? 'stop' : 'start';
93
- if (method === 'stop') return; // not closing the stt service as it will stop for whole channel
94
- if (method === 'start' && isSTTActive === true) return; // not triggering the start service if STT Service already started by anyone else in the channel
95
- if (!isTranscriptON) {
96
- setSidePanel(SidePanelType.Transcript);
97
- } else {
98
- setSidePanel(SidePanelType.None);
99
- }
100
86
  try {
101
- const res = await start(language);
102
- if (res?.message.includes('STARTED')) {
103
- // channel is already started now restart
104
- await restart(language);
87
+ // const res = await start(language, userOwnLanguages);
88
+ // if (res?.message.includes('STARTED')) {
89
+ // // channel is already started now restart
90
+ // await restart(language, userOwnLanguages);
91
+ // }
92
+ await handleTranslateConfigChange(inputTranslateConfig);
93
+ if (!isTranscriptON) {
94
+ setSidePanel(SidePanelType.Transcript);
95
+ } else {
96
+ setSidePanel(SidePanelType.None);
105
97
  }
106
98
  } catch (error) {
107
99
  console.log('eror in starting stt', error);
@@ -115,7 +107,7 @@ const TranscriptIcon = (props: TranscriptIconProps) => {
115
107
  modalVisible={isLanguagePopupOpen}
116
108
  setModalVisible={setLanguagePopup}
117
109
  onConfirm={onConfirm}
118
- isFirstTimePopupOpen={isFirstTimePopupOpen.current}
110
+ // isFirstTimePopupOpen={isFirstTimePopupOpen.current}
119
111
  />
120
112
  </View>
121
113
  );
@@ -3,24 +3,64 @@ import React from 'react';
3
3
 
4
4
  import ThemeConfig from '../../../src/theme';
5
5
  import hexadecimalTransparency from '../../../src/utils/hexadecimalTransparency';
6
- import {formatTime} from './utils';
6
+ import {formatTime, getLanguageLabel} from './utils';
7
+ import {useLocalUid} from '../../../agora-rn-uikit';
8
+
9
+ type TranslationItem = {
10
+ lang: string;
11
+ text: string;
12
+ isFinal: boolean;
13
+ };
7
14
 
8
15
  interface TranscriptTextProps {
9
16
  user: string;
10
17
  time: number;
11
18
  value: string;
19
+ translations?: TranslationItem[];
12
20
  searchQuery?: string;
21
+ selectedTranslationLanguage?: string;
13
22
  }
14
23
 
15
24
  export const TranscriptText = ({
16
25
  user,
17
26
  time,
18
27
  value,
28
+ translations = [],
19
29
  searchQuery = '',
30
+ selectedTranslationLanguage: storedTranslationLanguage,
31
+ transcriptViewMode = 'translated',
32
+ speakerUid,
33
+ localUserSpokenLanguage,
20
34
  }: TranscriptTextProps) => {
21
35
  const t = time ? formatTime(Number(time)) : '';
36
+
37
+ // text to display based on stored translation language
38
+ // const getDisplayText = () => {
39
+ // if (!storedTranslationLanguage) {
40
+ // return value; // no translation selected, show original
41
+ // }
42
+
43
+ // // find translation for the stored language
44
+ // const currentTranslation = translations.find(
45
+ // t => t.lang === storedTranslationLanguage,
46
+ // );
47
+ // if (currentTranslation?.text) {
48
+ // return currentTranslation.text;
49
+ // }
50
+
51
+ // // if stored language not available, show original
52
+ // return value;
53
+ // };
54
+
55
+ // const displayText = getDisplayText();
22
56
  const regex = searchQuery ? new RegExp(`(${searchQuery})`, 'gi') : ' ';
23
- const parts = value.split(regex);
57
+ const originalParts = value.split(regex);
58
+
59
+ // Prepare all translations with their parts for search highlighting
60
+ const translationsParts = translations.map(trans => ({
61
+ lang: trans.lang,
62
+ parts: trans.text.split(regex),
63
+ }));
24
64
 
25
65
  return (
26
66
  <View key={user} style={styles.transcriptTextContainer}>
@@ -36,9 +76,10 @@ export const TranscriptText = ({
36
76
  </View>
37
77
 
38
78
  <View>
79
+ {/* Original Text */}
39
80
  <Text style={[styles.transciptText]}>
40
81
  {/* If substring matches search query then highlight it */}
41
- {parts.map((part, index) =>
82
+ {originalParts.map((part, index) =>
42
83
  part.toLowerCase() === searchQuery.toLowerCase() &&
43
84
  searchQuery !== '' ? (
44
85
  <Text key={index} style={styles.highlightedText}>
@@ -49,6 +90,29 @@ export const TranscriptText = ({
49
90
  ),
50
91
  )}
51
92
  </Text>
93
+
94
+ {/* All Translations */}
95
+ {translationsParts.map((translation, translationIndex) => (
96
+ <Text
97
+ key={translation.lang}
98
+ style={[styles.transciptText, styles.translationText]}>
99
+ {/* lang code */}
100
+ <Text style={styles.languageLabel}>
101
+ ({getLanguageLabel([translation.lang])}):{' '}
102
+ </Text>
103
+ {/* lang */}
104
+ {translation.parts.map((part, index) =>
105
+ part.toLowerCase() === searchQuery.toLowerCase() &&
106
+ searchQuery !== '' ? (
107
+ <Text key={index} style={styles.highlightedText}>
108
+ {searchQuery ? part : part + ' '}
109
+ </Text>
110
+ ) : (
111
+ <Text key={index}>{searchQuery ? part : part + ' '}</Text>
112
+ ),
113
+ )}
114
+ </Text>
115
+ ))}
52
116
  </View>
53
117
  </View>
54
118
  );
@@ -95,4 +159,15 @@ const styles = StyleSheet.create({
95
159
  highlightedText: {
96
160
  backgroundColor: $config.SEMANTIC_NEUTRAL,
97
161
  },
162
+ translationText: {
163
+ fontStyle: 'italic',
164
+ marginTop: 8,
165
+ fontWeight: '400',
166
+ lineHeight: 24,
167
+ fontSize: ThemeConfig.FontSize.normal,
168
+ color: $config.FONT_COLOR + ThemeConfig.EmphasisPlus.medium,
169
+ },
170
+ languageLabel: {
171
+ color: $config.FONT_COLOR + ThemeConfig.EmphasisPlus.low,
172
+ },
98
173
  });
@@ -1,9 +1,9 @@
1
1
  /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/
2
2
  "use strict";
3
3
 
4
- var $protobuf = require("protobufjs/light");
4
+ var $protobuf = require("protobufjs");
5
5
 
6
- var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $protobuf.Root()))
6
+ var $protobufRoot = ($protobuf.roots.default || ($protobuf.roots.default = new $protobuf.Root()))
7
7
  .addJSON({
8
8
  agora: {
9
9
  nested: {
@@ -55,7 +55,24 @@ var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $pr
55
55
  rule: "repeated",
56
56
  type: "Word",
57
57
  id: 10
58
- }
58
+ },
59
+ end_of_segment: {
60
+ type: "bool",
61
+ id: 11
62
+ },
63
+ duration_ms: {
64
+ type: "int32",
65
+ id: 12
66
+ },
67
+ data_type: {
68
+ type: "string",
69
+ id: 13
70
+ },
71
+ trans: {
72
+ rule: "repeated",
73
+ type: "Translation",
74
+ id: 14
75
+ },
59
76
  }
60
77
  },
61
78
  Word: {
@@ -81,6 +98,23 @@ var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $pr
81
98
  id: 5
82
99
  }
83
100
  }
101
+ },
102
+ Translation: {
103
+ fields: {
104
+ isFinal: {
105
+ type: "bool",
106
+ id: 1
107
+ },
108
+ lang: {
109
+ type: "string",
110
+ id: 2
111
+ },
112
+ texts: {
113
+ rule: "repeated",
114
+ type: "string",
115
+ id: 3
116
+ }
117
+ }
84
118
  }
85
119
  }
86
120
  }
@@ -88,4 +122,4 @@ var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $pr
88
122
  }
89
123
  });
90
124
 
91
- module.exports = $root;
125
+ module.exports = $protobufRoot;
@@ -1,23 +1,38 @@
1
1
  syntax = "proto3";
2
- package agora.audio2text;
3
- option java_package = "io.agora.rtc.audio2text";
4
- option java_outer_classname = "Audio2TextProtobuffer";
2
+
3
+ package Agora.SpeechToText;
4
+ option objc_class_prefix = "Stt";
5
+ option csharp_namespace = "AgoraSTTSample.Protobuf";
6
+ option java_package = "io.agora.rtc.speech2text";
7
+ option java_outer_classname = "AgoraSpeech2TextProtobuffer";
8
+
5
9
  message Text {
6
- int32 vendor = 1;
7
- int32 version = 2;
8
- int32 seqnum = 3;
9
- uint32 uid = 4;
10
- int32 flag = 5;
11
- int64 time = 6;
12
- int32 lang = 7;
13
- int32 starttime = 8;
14
- int32 offtime = 9;
15
- repeated Word words = 10;
10
+ int32 vendor = 1;
11
+ int32 version = 2;
12
+ int32 seqnum = 3;
13
+ int64 uid = 4;
14
+ int32 flag = 5;
15
+ int64 time = 6;
16
+ int32 lang = 7;
17
+ int32 starttime = 8;
18
+ int32 offtime = 9;
19
+ repeated Word words = 10;
20
+ bool end_of_segment = 11;
21
+ int32 duration_ms = 12;
22
+ string data_type = 13;
23
+ repeated Translation trans = 14;
24
+ string culture = 15;
25
+ int64 text_ts = 16;
16
26
  }
17
27
  message Word {
18
- string text = 1;
19
- int32 start_ms = 2;
20
- int32 duration_ms = 3;
21
- bool is_final = 4;
22
- double confidence = 5;
23
- }
28
+ string text = 1;
29
+ int32 start_ms = 2;
30
+ int32 duration_ms = 3;
31
+ bool is_final = 4;
32
+ double confidence = 5;
33
+ }
34
+ message Translation {
35
+ bool is_final = 1;
36
+ string lang = 2;
37
+ repeated string texts = 3;
38
+ }