Rogerjs commited on
Commit
4ca439a
1 Parent(s): 4ea8f09

Add initial version of the translator app

Browse files
Files changed (3) hide show
  1. app.py +23 -0
  2. requirements.txt +9 -0
  3. templates/index.html +387 -0
app.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from transformers import pipeline
3
+
4
+ # Initialize the translation pipelines
5
+ translator_en_fi = pipeline("translation", model="Helsinki-NLP/opus-mt-en-fi")
6
+ translator_fi_en = pipeline("translation", model="Helsinki-NLP/opus-mt-fi-en")
7
+
8
+ def translate(text, direction):
9
+ if direction == 'en-fi':
10
+ result = translator_en_fi(text)[0]['translation_text']
11
+ else:
12
+ result = translator_fi_en(text)[0]['translation_text']
13
+ return result
14
+
15
+ iface = gr.Interface(
16
+ fn=translate,
17
+ inputs=[gr.inputs.Textbox(lines=2, placeholder="Enter text here..."), gr.inputs.Radio(["en-fi", "fi-en"])],
18
+ outputs="text",
19
+ title="Translation App",
20
+ description="Translate text between English and Finnish using Hugging Face Transformers."
21
+ )
22
+
23
+ iface.launch()
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ transformers
3
+ flask # for local testing
4
+ flask==2.3.3
5
+ transformers==4.33.2
6
+ torch==2.2.1
7
+ sentencepiece
8
+ gradio
9
+ transformers
templates/index.html ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Translator</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --primary-color: #F26522; /* Orange */
11
+ --primary-hover: #E05B1F; /* Slightly darker orange for hover */
12
+ --bg-color: #FFFFFF; /* White */
13
+ --text-color: #78797D; /* Grey */
14
+ --border-color: #E5E7EB; /* Light grey for borders */
15
+ --success-color: #F26522; /* Use primary color for success */
16
+ --error-color: #DC2626; /* Red for errors */
17
+ --container-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
18
+ }
19
+
20
+ [data-theme='dark'] {
21
+ --bg-color: #1A1A1A;
22
+ --text-color: #E5E5E5;
23
+ --border-color: #333333;
24
+ --container-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1);
25
+ }
26
+
27
+ * {
28
+ margin: 0;
29
+ padding: 0;
30
+ box-sizing: border-box;
31
+ transition: background-color 0.3s ease, color 0.3s ease;
32
+ }
33
+
34
+ body {
35
+ font-family: 'Inter', sans-serif;
36
+ background-color: var(--bg-color);
37
+ color: var(--text-color);
38
+ line-height: 1.5;
39
+ min-height: 100vh;
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: center;
43
+ padding: 20px;
44
+ }
45
+
46
+ .container {
47
+ background-color: var(--bg-color);
48
+ padding: 2.5rem;
49
+ border-radius: 1.5rem;
50
+ box-shadow: var(--container-shadow);
51
+ width: 100%;
52
+ max-width: 800px;
53
+ transition: all 0.3s ease;
54
+ }
55
+
56
+ .container:hover {
57
+ transform: translateY(-2px);
58
+ box-shadow: 0 6px 8px -1px rgba(0, 0, 0, 0.1), 0 4px 6px -1px rgba(0, 0, 0, 0.06);
59
+ }
60
+
61
+ h1 {
62
+ font-size: 2.25rem;
63
+ font-weight: 600;
64
+ color: var(--text-color);
65
+ margin-bottom: 2rem;
66
+ text-align: center;
67
+ letter-spacing: -0.5px;
68
+ }
69
+
70
+ .form-group {
71
+ margin-bottom: 2rem;
72
+ }
73
+
74
+ .input-container, .output-container {
75
+ position: relative;
76
+ margin-bottom: 1.5rem;
77
+ }
78
+
79
+ .label {
80
+ display: block;
81
+ font-size: 0.95rem;
82
+ font-weight: 500;
83
+ color: var(--text-color);
84
+ margin-bottom: 0.75rem;
85
+ letter-spacing: 0.2px;
86
+ }
87
+
88
+ textarea {
89
+ width: 100%;
90
+ padding: 1rem;
91
+ border: 2px solid var(--border-color);
92
+ border-radius: 0.75rem;
93
+ font-size: 1rem;
94
+ font-family: inherit;
95
+ min-height: 120px;
96
+ resize: vertical;
97
+ transition: all 0.2s ease;
98
+ background-color: var(--bg-color);
99
+ color: var(--text-color);
100
+ }
101
+
102
+ textarea:focus {
103
+ outline: none;
104
+ border-color: var(--primary-color);
105
+ box-shadow: 0 0 0 3px rgba(242, 101, 34, 0.1);
106
+ }
107
+
108
+ .theme-toggle {
109
+ position: fixed;
110
+ top: 20px;
111
+ right: 20px;
112
+ padding: 12px 24px;
113
+ background-color: var(--primary-color);
114
+ color: white;
115
+ border: none;
116
+ border-radius: 8px;
117
+ cursor: pointer;
118
+ font-size: 0.95rem;
119
+ font-weight: 500;
120
+ transition: all 0.3s ease;
121
+ z-index: 1000;
122
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
123
+ }
124
+
125
+ .theme-toggle:hover {
126
+ background-color: var(--primary-hover);
127
+ transform: translateY(-2px);
128
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
129
+ }
130
+
131
+ .theme-toggle:active {
132
+ transform: translateY(0);
133
+ }
134
+
135
+ .button-container {
136
+ display: flex;
137
+ gap: 1rem;
138
+ margin-top: 1rem;
139
+ }
140
+
141
+ .translate-btn, .speak-btn {
142
+ padding: 0.75rem 1.5rem;
143
+ background-color: var(--primary-color);
144
+ color: white;
145
+ border: none;
146
+ border-radius: 8px;
147
+ cursor: pointer;
148
+ font-size: 0.95rem;
149
+ font-weight: 500;
150
+ transition: all 0.3s ease;
151
+ flex: 1;
152
+ }
153
+
154
+ .translate-btn:hover, .speak-btn:hover {
155
+ background-color: var(--primary-hover);
156
+ transform: translateY(-2px);
157
+ }
158
+
159
+ .translate-btn:active, .speak-btn:active {
160
+ transform: translateY(0);
161
+ }
162
+
163
+ .char-count {
164
+ position: absolute;
165
+ bottom: -1.5rem;
166
+ right: 0;
167
+ font-size: 0.85rem;
168
+ color: var(--text-color);
169
+ opacity: 0.8;
170
+ }
171
+
172
+ .chat-history {
173
+ margin-top: 2rem;
174
+ padding: 1.5rem;
175
+ border-radius: 1rem;
176
+ background-color: var(--bg-color);
177
+ border: 2px solid var(--border-color);
178
+ }
179
+
180
+ .chat-history h2 {
181
+ font-size: 1.25rem;
182
+ margin-bottom: 1rem;
183
+ color: var(--text-color);
184
+ }
185
+
186
+ .chat-item {
187
+ padding: 1rem;
188
+ margin-bottom: 1rem;
189
+ border-radius: 0.75rem;
190
+ background-color: rgba(242, 101, 34, 0.1);
191
+ border: 1px solid var(--border-color);
192
+ }
193
+
194
+ .chat-item:last-child {
195
+ margin-bottom: 0;
196
+ }
197
+
198
+ .translation-bubble {
199
+ margin-top: 1.5rem;
200
+ padding: 1rem;
201
+ border-radius: 1rem;
202
+ background-color: var(--bg-color);
203
+ border: 2px solid var(--border-color);
204
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
205
+ max-width: 600px;
206
+ margin-left: auto;
207
+ margin-right: auto;
208
+ }
209
+
210
+ .translation-bubble h2 {
211
+ font-size: 1.25rem;
212
+ margin-bottom: 0.5rem;
213
+ color: var(--text-color);
214
+ }
215
+
216
+ .translation-bubble p {
217
+ font-size: 1rem;
218
+ color: var(--text-color);
219
+ line-height: 1.5;
220
+ }
221
+
222
+ @media (max-width: 640px) {
223
+ .container {
224
+ padding: 1.5rem;
225
+ }
226
+
227
+ h1 {
228
+ font-size: 1.75rem;
229
+ }
230
+
231
+ .button-container {
232
+ flex-direction: column;
233
+ }
234
+ }
235
+ </style>
236
+ </head>
237
+ <body data-theme="light">
238
+ <button class="theme-toggle">Switch to Dark Mode</button>
239
+ <div class="container">
240
+ <h1>Translator</h1>
241
+ <div class="form-group">
242
+ <div class="input-container">
243
+ <label class="label" for="inputText">Original</label>
244
+ <textarea
245
+ id="inputText"
246
+ placeholder="Enter text to translate..."
247
+ maxlength="500"
248
+ oninput="updateCharCount(this)"
249
+ ></textarea>
250
+ <div class="char-count">0/500</div>
251
+ </div>
252
+ <div class="direction-container">
253
+ <label class="label" for="direction">Translation Direction</label>
254
+ <select id="direction">
255
+ <option value="en-fi">English to Finnish</option>
256
+ <option value="fi-en">Finnish to English</option>
257
+ </select>
258
+ </div>
259
+ </div>
260
+ <div class="button-container">
261
+ <button class="translate-btn" onclick="translateText()">
262
+ Translate
263
+ </button>
264
+ <button class="speak-btn" onclick="speakText()">
265
+ Speak Translation
266
+ </button>
267
+ </div>
268
+ <div class="error" id="errorMsg"></div>
269
+ <div class="translation-bubble">
270
+ <h2>Translation</h2>
271
+ <p id="result"></p>
272
+ </div>
273
+
274
+ <div class="chat-history" id="chatHistory">
275
+ <h2>Chat History</h2>
276
+ <ul class="chat-list" id="chatList">
277
+ </ul>
278
+ </div>
279
+ </div>
280
+
281
+ <script>
282
+ document.addEventListener('DOMContentLoaded', () => {
283
+ const themeToggleButton = document.querySelector('.theme-toggle');
284
+ if (themeToggleButton) {
285
+ console.log('Theme toggle button found');
286
+ themeToggleButton.addEventListener('click', () => {
287
+ console.log('Toggle theme button clicked');
288
+ const body = document.body;
289
+ const currentTheme = body.getAttribute('data-theme');
290
+ console.log('Current theme:', currentTheme);
291
+ const newTheme = currentTheme === 'light' ? 'dark' : 'light';
292
+ console.log('Switching to theme:', newTheme);
293
+ body.setAttribute('data-theme', newTheme);
294
+ themeToggleButton.textContent = `Switch to ${newTheme === 'light' ? 'Dark' : 'Light'} Mode`;
295
+ });
296
+ } else {
297
+ console.error('Theme toggle button not found');
298
+ }
299
+ });
300
+
301
+ function updateCharCount(textarea) {
302
+ const maxLength = textarea.getAttribute('maxlength');
303
+ const currentLength = textarea.value.length;
304
+ const charCountElement = textarea.parentElement.querySelector('.char-count');
305
+ charCountElement.textContent = `${currentLength}/${maxLength}`;
306
+ }
307
+
308
+ async function translateText() {
309
+ const inputText = document.getElementById('inputText').value;
310
+ const direction = document.getElementById('direction').value;
311
+ const translateBtn = document.querySelector('.translate-btn');
312
+ const loading = document.querySelector('.loading');
313
+ const result = document.getElementById('result');
314
+ const resultContainer = document.querySelector('.translation-bubble');
315
+ const errorMsg = document.getElementById('errorMsg');
316
+
317
+ if (!inputText.trim()) {
318
+ errorMsg.textContent = 'Please enter some text to translate';
319
+ errorMsg.style.display = 'block';
320
+ return;
321
+ }
322
+
323
+ // Reset states
324
+ errorMsg.style.display = 'none';
325
+ translateBtn.disabled = true;
326
+ result.textContent = '';
327
+ resultContainer.classList.remove('show');
328
+
329
+ try {
330
+ const response = await fetch('/translate', {
331
+ method: 'POST',
332
+ headers: {
333
+ 'Content-Type': 'application/json',
334
+ },
335
+ body: JSON.stringify({ text: inputText, direction: direction })
336
+ });
337
+
338
+ const data = await response.json();
339
+
340
+ if (response.ok) {
341
+ result.textContent = data.translation;
342
+ resultContainer.classList.add('show');
343
+ // Update chat history
344
+ updateChatHistory(inputText, data.translation);
345
+ } else {
346
+ errorMsg.textContent = data.error || 'Failed to translate';
347
+ errorMsg.style.display = 'block';
348
+ }
349
+ } catch (error) {
350
+ console.error('Translation error:', error);
351
+ errorMsg.textContent = 'Failed to connect to the server';
352
+ errorMsg.style.display = 'block';
353
+ } finally {
354
+ translateBtn.disabled = false;
355
+ }
356
+ }
357
+
358
+ function updateChatHistory(input, output) {
359
+ const chatList = document.getElementById('chatList');
360
+ const messageLi = document.createElement('li');
361
+ messageLi.classList.add('chat-item');
362
+
363
+ const originalDiv = document.createElement('div');
364
+ originalDiv.classList.add('chat-original');
365
+ originalDiv.textContent = `Original: ${input}`;
366
+
367
+ const translationDiv = document.createElement('div');
368
+ translationDiv.classList.add('chat-translation');
369
+ translationDiv.textContent = `Translation: ${output}`;
370
+
371
+ messageLi.appendChild(originalDiv);
372
+ messageLi.appendChild(translationDiv);
373
+
374
+ chatList.appendChild(messageLi);
375
+ chatList.scrollTop = chatList.scrollHeight;
376
+ }
377
+
378
+ function speakText() {
379
+ const text = document.getElementById('result').textContent;
380
+ if (!text) return;
381
+
382
+ const utterance = new SpeechSynthesisUtterance(text);
383
+ window.speechSynthesis.speak(utterance);
384
+ }
385
+ </script>
386
+ </body>
387
+ </html>