gleison commited on
Commit
671a925
·
1 Parent(s): f554f59

added frontend

Browse files
Files changed (2) hide show
  1. main.py +17 -1
  2. static/index.html +430 -0
main.py CHANGED
@@ -9,8 +9,17 @@ from enum import Enum
9
  from transformers import M2M100Tokenizer, M2M100ForConditionalGeneration
10
  import torch
11
  import uvicorn
 
12
  from fastapi.middleware.cors import CORSMiddleware
 
 
 
 
 
13
  app = FastAPI(docs_url="/")
 
 
 
14
  app.add_middleware(
15
  CORSMiddleware,
16
  allow_origins=["*"],
@@ -36,7 +45,14 @@ def load_model(model: str = 'facebook/m2m100_418M' , cache_dir: str = "models/")
36
  model.eval()
37
  return tokenizer, model
38
  # aparentemente temos um problema ao carregar o modelo então vou tentar carregar no start da aplicação para não dar time-out na request
39
- load_model()
 
 
 
 
 
 
 
40
 
41
  @app.post("/translate")
42
  async def translate(request: TranslationRequest):
 
9
  from transformers import M2M100Tokenizer, M2M100ForConditionalGeneration
10
  import torch
11
  import uvicorn
12
+ from fastapi.responses import HTMLResponse, FileResponse
13
  from fastapi.middleware.cors import CORSMiddleware
14
+ from enum import Enum
15
+ from fastapi.staticfiles import StaticFiles
16
+
17
+
18
+ os.makedirs("static", exist_ok=True)
19
  app = FastAPI(docs_url="/")
20
+
21
+ app.mount("/static", StaticFiles(directory="static"), name="static")
22
+
23
  app.add_middleware(
24
  CORSMiddleware,
25
  allow_origins=["*"],
 
45
  model.eval()
46
  return tokenizer, model
47
  # aparentemente temos um problema ao carregar o modelo então vou tentar carregar no start da aplicação para não dar time-out na request
48
+ # load_model()
49
+
50
+ @app.get("/", response_class=FileResponse)
51
+ async def read_index():
52
+ """
53
+ Serve o arquivo index.html da pasta static
54
+ """
55
+ return FileResponse("static/index.html")
56
 
57
  @app.post("/translate")
58
  async def translate(request: TranslationRequest):
static/index.html ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-br">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TranslateWorld - Traduza para qualquer idioma</title>
7
+ <style>
8
+ :root {
9
+ --primary: #4361ee;
10
+ --secondary: #3f37c9;
11
+ --light: #f8f9fa;
12
+ --dark: #212529;
13
+ --success: #4cc9f0;
14
+ --warning: #f72585;
15
+ --info: #4895ef;
16
+ }
17
+
18
+ * {
19
+ margin: 0;
20
+ padding: 0;
21
+ box-sizing: border-box;
22
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
+ }
24
+
25
+ body {
26
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
27
+ min-height: 100vh;
28
+ display: flex;
29
+ flex-direction: column;
30
+ color: var(--dark);
31
+ }
32
+
33
+ header {
34
+ background: var(--primary);
35
+ color: white;
36
+ padding: 1rem;
37
+ text-align: center;
38
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
39
+ }
40
+
41
+ .container {
42
+ width: 100%;
43
+ max-width: 1200px;
44
+ margin: 0 auto;
45
+ padding: 2rem;
46
+ flex-grow: 1;
47
+ }
48
+
49
+ .app-card {
50
+ background: white;
51
+ border-radius: 12px;
52
+ padding: 2rem;
53
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
54
+ margin-bottom: 2rem;
55
+ }
56
+
57
+ h1 {
58
+ font-size: 2.5rem;
59
+ margin-bottom: 1rem;
60
+ }
61
+
62
+ h2 {
63
+ font-size: 1.8rem;
64
+ margin-bottom: 1rem;
65
+ color: var(--primary);
66
+ }
67
+
68
+ p {
69
+ margin-bottom: 1rem;
70
+ line-height: 1.6;
71
+ }
72
+
73
+ .grid {
74
+ display: grid;
75
+ grid-template-columns: 1fr 1fr;
76
+ gap: 1.5rem;
77
+ }
78
+
79
+ @media (max-width: 768px) {
80
+ .grid {
81
+ grid-template-columns: 1fr;
82
+ }
83
+ }
84
+
85
+ textarea {
86
+ width: 100%;
87
+ height: 200px;
88
+ padding: 1rem;
89
+ border: 1px solid #ddd;
90
+ border-radius: 8px;
91
+ resize: vertical;
92
+ margin-bottom: 1rem;
93
+ font-size: 1rem;
94
+ }
95
+
96
+ .language-selector {
97
+ display: flex;
98
+ justify-content: space-between;
99
+ margin-bottom: 1rem;
100
+ gap: 1rem;
101
+ }
102
+
103
+ .language-selector select {
104
+ flex: 1;
105
+ padding: 0.8rem;
106
+ border: 1px solid #ddd;
107
+ border-radius: 8px;
108
+ font-size: 1rem;
109
+ background-color: white;
110
+ }
111
+
112
+ .model-selection {
113
+ margin-bottom: 1rem;
114
+ }
115
+
116
+ .model-selection label {
117
+ display: block;
118
+ margin-bottom: 0.5rem;
119
+ font-weight: bold;
120
+ }
121
+
122
+ .model-selection select {
123
+ width: 100%;
124
+ padding: 0.8rem;
125
+ border: 1px solid #ddd;
126
+ border-radius: 8px;
127
+ font-size: 1rem;
128
+ background-color: white;
129
+ }
130
+
131
+ .btn {
132
+ display: inline-block;
133
+ padding: 0.8rem 1.5rem;
134
+ background-color: var(--primary);
135
+ color: white;
136
+ border: none;
137
+ border-radius: 8px;
138
+ cursor: pointer;
139
+ font-size: 1rem;
140
+ transition: all 0.3s ease;
141
+ text-transform: uppercase;
142
+ letter-spacing: 1px;
143
+ font-weight: bold;
144
+ }
145
+
146
+ .btn:hover {
147
+ background-color: var(--secondary);
148
+ transform: translateY(-2px);
149
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
150
+ }
151
+
152
+ .btn:active {
153
+ transform: translateY(0);
154
+ }
155
+
156
+ .action-buttons {
157
+ display: flex;
158
+ justify-content: space-between;
159
+ gap: 1rem;
160
+ }
161
+
162
+ .loading {
163
+ display: none;
164
+ text-align: center;
165
+ margin: 1rem 0;
166
+ }
167
+
168
+ .loading-spinner {
169
+ width: 30px;
170
+ height: 30px;
171
+ border: 4px solid #f3f3f3;
172
+ border-top: 4px solid var(--primary);
173
+ border-radius: 50%;
174
+ animation: spin 1s linear infinite;
175
+ margin: 0 auto;
176
+ }
177
+
178
+ @keyframes spin {
179
+ 0% { transform: rotate(0deg); }
180
+ 100% { transform: rotate(360deg); }
181
+ }
182
+
183
+ .features {
184
+ display: grid;
185
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
186
+ gap: 1.5rem;
187
+ margin-top: 2rem;
188
+ }
189
+
190
+ .feature-card {
191
+ background: white;
192
+ border-radius: 12px;
193
+ padding: 1.5rem;
194
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
195
+ transition: all 0.3s ease;
196
+ }
197
+
198
+ .feature-card:hover {
199
+ transform: translateY(-5px);
200
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
201
+ }
202
+
203
+ .feature-card h3 {
204
+ color: var(--primary);
205
+ margin-bottom: 0.5rem;
206
+ }
207
+
208
+ .feature-icon {
209
+ font-size: 2rem;
210
+ color: var(--primary);
211
+ margin-bottom: 1rem;
212
+ }
213
+
214
+ footer {
215
+ background: var(--dark);
216
+ color: white;
217
+ text-align: center;
218
+ padding: 1rem;
219
+ }
220
+
221
+ .cta {
222
+ background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
223
+ color: white;
224
+ padding: 2rem;
225
+ text-align: center;
226
+ border-radius: 12px;
227
+ margin-top: 2rem;
228
+ }
229
+
230
+ .cta h2 {
231
+ color: white;
232
+ }
233
+
234
+ .cta .btn {
235
+ background: white;
236
+ color: var(--primary);
237
+ margin-top: 1rem;
238
+ }
239
+
240
+ .cta .btn:hover {
241
+ background: var(--light);
242
+ }
243
+
244
+ .badges {
245
+ display: flex;
246
+ flex-wrap: wrap;
247
+ gap: 0.5rem;
248
+ margin-bottom: 1rem;
249
+ }
250
+
251
+ .badge {
252
+ background: var(--info);
253
+ color: white;
254
+ padding: 0.2rem 0.5rem;
255
+ border-radius: 4px;
256
+ font-size: 0.8rem;
257
+ }
258
+ </style>
259
+ </head>
260
+ <body>
261
+ <header>
262
+ <h1>TranslateWorld</h1>
263
+ <p>Tradução avançada para mais de 100 idiomas</p>
264
+ </header>
265
+
266
+ <div class="container">
267
+ <div class="app-card">
268
+ <h2>Tradutor Universal</h2>
269
+ <p>Traduza qualquer texto para múltiplos idiomas com qualidade superior usando modelos de IA avançados.</p>
270
+
271
+ <div class="model-selection">
272
+ <label for="model">Selecione o modelo de tradução:</label>
273
+ <select id="model">
274
+ <option value="facebook/m2m100_418M">M2M100 Padrão (418M) - Mais rápido</option>
275
+ <option value="facebook/m2m100_1.2B">M2M100 Premium (1.2B) - Mais preciso</option>
276
+ </select>
277
+ </div>
278
+
279
+ <div class="grid">
280
+ <div>
281
+ <h3>Texto Original</h3>
282
+ <div class="language-selector">
283
+ <select id="sourceLanguage">
284
+ <option value="en">Inglês</option>
285
+ <option value="pt" selected>Português</option>
286
+ <option value="es">Espanhol</option>
287
+ <option value="fr">Francês</option>
288
+ <option value="de">Alemão</option>
289
+ <option value="it">Italiano</option>
290
+ <option value="ja">Japonês</option>
291
+ <option value="zh">Chinês</option>
292
+ <option value="ru">Russo</option>
293
+ <option value="ar">Árabe</option>
294
+ <!-- Adicione mais idiomas conforme necessário -->
295
+ </select>
296
+ </div>
297
+ <textarea id="sourceText" placeholder="Digite seu texto aqui..."></textarea>
298
+ </div>
299
+
300
+ <div>
301
+ <h3>Tradução</h3>
302
+ <div class="language-selector">
303
+ <select id="targetLanguage">
304
+ <option value="en" selected>Inglês</option>
305
+ <option value="pt">Português</option>
306
+ <option value="es">Espanhol</option>
307
+ <option value="fr">Francês</option>
308
+ <option value="de">Alemão</option>
309
+ <option value="it">Italiano</option>
310
+ <option value="ja">Japonês</option>
311
+ <option value="zh">Chinês</option>
312
+ <option value="ru">Russo</option>
313
+ <option value="ar">Árabe</option>
314
+ <!-- Adicione mais idiomas conforme necessário -->
315
+ </select>
316
+ </div>
317
+ <textarea id="targetText" placeholder="A tradução aparecerá aqui..." readonly></textarea>
318
+ </div>
319
+ </div>
320
+
321
+ <div class="loading">
322
+ <div class="loading-spinner"></div>
323
+ <p>Traduzindo...</p>
324
+ </div>
325
+
326
+ <div class="action-buttons">
327
+ <button id="translateBtn" class="btn">Traduzir</button>
328
+ <button id="copyBtn" class="btn">Copiar Tradução</button>
329
+ </div>
330
+ </div>
331
+
332
+ <div class="features">
333
+ <div class="feature-card">
334
+ <div class="feature-icon">🌍</div>
335
+ <h3>Mais de 100 Idiomas</h3>
336
+ <p>Traduza entre mais de 100 idiomas com precisão e naturalidade.</p>
337
+ </div>
338
+
339
+ <div class="feature-card">
340
+ <div class="feature-icon">🤖</div>
341
+ <h3>Modelos Premium</h3>
342
+ <p>Utilize modelos de IA avançados para traduções de alta qualidade.</p>
343
+ </div>
344
+
345
+ <div class="feature-card">
346
+ <div class="feature-icon">⚡</div>
347
+ <h3>Rápido e Eficiente</h3>
348
+ <p>Traduções instantâneas para textos de qualquer tamanho.</p>
349
+ </div>
350
+
351
+ <div class="feature-card">
352
+ <div class="feature-icon">🔍</div>
353
+ <h3>Preserva o Contexto</h3>
354
+ <p>Mantém o significado e as nuances do texto original.</p>
355
+ </div>
356
+ </div>
357
+
358
+ <div class="cta">
359
+ <h2>Aprenda a Criar Seu Próprio Tradutor com IA</h2>
360
+ <p>Adquira nosso eBook e aprenda a desenvolver aplicações de tradução avançadas utilizando modelos de IA.</p>
361
+ <button class="btn">Saiba Mais</button>
362
+ </div>
363
+ </div>
364
+
365
+ <footer>
366
+ <p>&copy; 2025 TranslateWorld. Todos os direitos reservados.</p>
367
+ </footer>
368
+
369
+ <script>
370
+ document.addEventListener('DOMContentLoaded', function() {
371
+ const translateBtn = document.getElementById('translateBtn');
372
+ const copyBtn = document.getElementById('copyBtn');
373
+ const sourceText = document.getElementById('sourceText');
374
+ const targetText = document.getElementById('targetText');
375
+ const sourceLanguage = document.getElementById('sourceLanguage');
376
+ const targetLanguage = document.getElementById('targetLanguage');
377
+ const modelSelect = document.getElementById('model');
378
+ const loading = document.querySelector('.loading');
379
+
380
+ translateBtn.addEventListener('click', async function() {
381
+ if (!sourceText.value.trim()) {
382
+ alert('Por favor, digite algum texto para traduzir.');
383
+ return;
384
+ }
385
+
386
+ loading.style.display = 'block';
387
+
388
+ try {
389
+ const response = await fetch('http://localhost:7860/translate', {
390
+ method: 'POST',
391
+ headers: {
392
+ 'Content-Type': 'application/json',
393
+ },
394
+ body: JSON.stringify({
395
+ user_input: sourceText.value,
396
+ source_lang: sourceLanguage.value,
397
+ target_lang: targetLanguage.value,
398
+ model: modelSelect.value
399
+ }),
400
+ });
401
+
402
+ const data = await response.json();
403
+
404
+ if (data.error) {
405
+ alert('Erro na tradução: ' + data.error);
406
+ } else {
407
+ targetText.value = data.translation;
408
+ }
409
+ } catch (error) {
410
+ alert('Erro ao conectar com o servidor de tradução. Verifique se o servidor está em execução.');
411
+ console.error('Error:', error);
412
+ } finally {
413
+ loading.style.display = 'none';
414
+ }
415
+ });
416
+
417
+ copyBtn.addEventListener('click', function() {
418
+ if (!targetText.value.trim()) {
419
+ alert('Não há tradução para copiar.');
420
+ return;
421
+ }
422
+
423
+ targetText.select();
424
+ document.execCommand('copy');
425
+ alert('Tradução copiada para a área de transferência!');
426
+ });
427
+ });
428
+ </script>
429
+ </body>
430
+ </html>