bep40 commited on
Commit
ca69da8
·
verified ·
1 Parent(s): fbaea78

Upload src/viNumberFix.js

Browse files
Files changed (1) hide show
  1. src/viNumberFix.js +161 -0
src/viNumberFix.js ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Vietnamese text processing for both display and TTS.
3
+ *
4
+ * DESIGN:
5
+ * - Display text (chat & subtitles): numbers stay as digits for clean visual display
6
+ * - TTS audio: numbers are converted to Vietnamese words for proper pronunciation
7
+ * - Source links in greetings: rendered as clickable popup links
8
+ */
9
+
10
+ // Vietnamese number words
11
+ const DIGITS = [
12
+ "không", "một", "hai", "ba", "bốn", "năm", "sáu", "bảy", "tám", "chín",
13
+ ];
14
+
15
+ function numberToVietnamese(n) {
16
+ if (n === 0) return "không";
17
+ if (n < 0) return "âm " + numberToVietnamese(-n);
18
+
19
+ const units = ["", "nghìn", "triệu", "tỷ"];
20
+ const groups = [];
21
+ let temp = n;
22
+ while (temp > 0) {
23
+ groups.push(temp % 1000);
24
+ temp = Math.floor(temp / 1000);
25
+ }
26
+ if (groups.length === 0) groups.push(0);
27
+
28
+ const readGroup = (g) => {
29
+ if (g === 0) return "";
30
+ const h = Math.floor(g / 100);
31
+ const r = g % 100;
32
+ let s = "";
33
+ if (h > 0) s += DIGITS[h] + " trăm ";
34
+ else if (groups.length > 1) s += "không trăm ";
35
+ if (r === 0) return s.trim();
36
+ if (r < 10) {
37
+ s += (h > 0 && r === 5) ? "lẻ năm" : "lẻ " + DIGITS[r];
38
+ } else if (r < 20) {
39
+ s += "mười" + (r === 10 ? "" : (r === 15 ? " lăm" : " " + DIGITS[r % 10]));
40
+ } else {
41
+ const t = Math.floor(r / 10);
42
+ const o = r % 10;
43
+ s += ["", "", "hai mươi", "ba mươi", "bốn mươi", "năm mươi",
44
+ "sáu mươi", "bảy mươi", "tám mươi", "chín mươi"][t];
45
+ if (o === 1) s += " mốt";
46
+ else if (o === 5) s += " lăm";
47
+ else if (o > 0) s += " " + DIGITS[o];
48
+ }
49
+ return s.replace(/\s+/g, " ").trim();
50
+ };
51
+
52
+ let result = "";
53
+ for (let i = groups.length - 1; i >= 0; i--) {
54
+ const g = groups[i];
55
+ if (g === 0) continue;
56
+ result += readGroup(g);
57
+ if (i > 0) result += " " + units[i] + " ";
58
+ }
59
+ return result.replace(/\s+/g, " ").trim();
60
+ }
61
+
62
+ /**
63
+ * Normalize Vietnamese text for display.
64
+ * Numbers remain as digits for clean visual display.
65
+ */
66
+ export function normalizeVietnameseText(text) {
67
+ if (!text || typeof text !== "string") return text;
68
+ return text.replace(/\s+/g, " ").trim();
69
+ }
70
+
71
+ /**
72
+ * Check if text contains Vietnamese characters.
73
+ */
74
+ export function containsVietnamese(text) {
75
+ return /[àáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ]/i.test(text);
76
+ }
77
+
78
+ /**
79
+ * Convert Arabic numerals to Vietnamese words for TTS.
80
+ * Display texts keep the original digits.
81
+ */
82
+ export function numbersToVietnameseWords(text) {
83
+ if (!text || typeof text !== "string") return text;
84
+
85
+ const units = ["", "nghìn", "triệu", "tỷ"];
86
+
87
+ const numberToVietnameseLocal = (n) => {
88
+ if (n === 0) return "không";
89
+ if (n < 0) return "âm " + numberToVietnameseLocal(-n);
90
+
91
+ const groups = [];
92
+ let temp = n;
93
+ while (temp > 0) {
94
+ groups.push(temp % 1000);
95
+ temp = Math.floor(temp / 1000);
96
+ }
97
+ if (groups.length === 0) groups.push(0);
98
+
99
+ const readGroup = (g) => {
100
+ if (g === 0) return "";
101
+ const h = Math.floor(g / 100);
102
+ const r = g % 100;
103
+ let s = "";
104
+ if (h > 0) s += DIGITS[h] + " trăm ";
105
+ else if (groups.length > 1) s += "không trăm ";
106
+ if (r === 0) return s.trim();
107
+ if (r < 10) {
108
+ s += (h > 0 && r === 5) ? "lẻ năm" : "lẻ " + DIGITS[r];
109
+ } else if (r < 20) {
110
+ s += "mười" + (r === 10 ? "" : (r === 15 ? " lăm" : " " + DIGITS[r % 10]));
111
+ } else {
112
+ const t = Math.floor(r / 10);
113
+ const o = r % 10;
114
+ s += ["", "", "hai mươi", "ba mươi", "bốn mươi", "năm mươi",
115
+ "sáu mươi", "bảy mươi", "tám mươi", "chín mươi"][t];
116
+ if (o === 1) s += " mốt";
117
+ else if (o === 5) s += " lăm";
118
+ else if (o > 0) s += " " + DIGITS[o];
119
+ }
120
+ return s.replace(/\s+/g, " ").trim();
121
+ };
122
+
123
+ let result = "";
124
+ for (let i = groups.length - 1; i >= 0; i--) {
125
+ const g = groups[i];
126
+ if (g === 0) continue;
127
+ result += readGroup(g);
128
+ if (i > 0) result += " " + units[i] + " ";
129
+ }
130
+ return result.replace(/\s+/g, " ").trim();
131
+ };
132
+
133
+ // Match integers (including negative) - convert to Vietnamese words
134
+ return text.replace(/\b-?\d+\b/g, (match) => {
135
+ const num = parseInt(match);
136
+ return numberToVietnameseLocal(num);
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Smart normalizer: only run on Vietnamese text, only on final transcripts.
142
+ */
143
+ export function smartNormalize(text, partial = false) {
144
+ if (!text || partial) return text;
145
+ if (containsVietnamese(text)) {
146
+ return normalizeVietnameseText(text);
147
+ }
148
+ return text;
149
+ }
150
+
151
+ /**
152
+ * Convert text for TTS: Vietnamese numbers to words for proper pronunciation.
153
+ * Call this before sending text to TTS synthesis.
154
+ */
155
+ export function prepareForTTS(text) {
156
+ if (!text || typeof text !== "string") return text;
157
+ if (containsVietnamese(text)) {
158
+ return numbersToVietnameseWords(text);
159
+ }
160
+ return text;
161
+ }