Daniele commited on
Commit
d14edbf
Β·
1 Parent(s): cb42234
API_EXAMPLES.md ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Esempi di Richieste API - Local Translation Server
2
+
3
+ ## Health Check
4
+
5
+ ```bash
6
+ curl -X GET "http://localhost:8000/"
7
+ ```
8
+
9
+ ## Ottenere le lingue supportate
10
+
11
+ ```bash
12
+ curl -X GET "http://localhost:8000/languages"
13
+ ```
14
+
15
+ ## Traduzione singola (GET con parametri)
16
+
17
+ ```bash
18
+ # Traduzione semplice
19
+ curl -X POST "http://localhost:8000/translate-single?text=Ciao%20mondo&target_language=en"
20
+
21
+ # Traduzione con HTML
22
+ curl -X POST "http://localhost:8000/translate-single?text=%3Cp%3ECiao%20%3Cstrong%3Emondo%3C/strong%3E%3C/p%3E&target_language=fr"
23
+
24
+ # Traduzione con placeholder
25
+ curl -X POST "http://localhost:8000/translate-single?text=Ciao%20%7Bname%7D%2C%20come%20stai%3F&target_language=de"
26
+ ```
27
+
28
+ ## Traduzione di oggetti locale complessi
29
+
30
+ ### Esempio base
31
+
32
+ ```bash
33
+ curl -X POST "http://localhost:8000/translate?target_language=en" \
34
+ -H "Content-Type: application/json" \
35
+ -d '{
36
+ "locales": {
37
+ "welcome": "Benvenuto!",
38
+ "save_button": "Salva",
39
+ "cancel_button": "Annulla"
40
+ }
41
+ }'
42
+ ```
43
+
44
+ ### Esempio con HTML e placeholder
45
+
46
+ ```bash
47
+ curl -X POST "http://localhost:8000/translate?target_language=fr" \
48
+ -H "Content-Type: application/json" \
49
+ -d '{
50
+ "locales": {
51
+ "welcome_message": "Benvenuto nella nostra applicazione, {username}!",
52
+ "notification_html": "<div class=\"alert alert-info\"><strong>Info:</strong> Hai {count} nuovi messaggi</div>",
53
+ "button_save": "Salva documento",
54
+ "error_required": "Questo campo Γ¨ obbligatorio",
55
+ "success_message": "<p>Operazione completata con <em>successo</em>!</p>"
56
+ }
57
+ }'
58
+ ```
59
+
60
+ ### Esempio per applicazione web complessa
61
+
62
+ ```bash
63
+ curl -X POST "http://localhost:8000/translate?target_language=de" \
64
+ -H "Content-Type: application/json" \
65
+ -d '{
66
+ "locales": {
67
+ "nav_home": "Home",
68
+ "nav_products": "Prodotti",
69
+ "nav_contact": "Contatti",
70
+ "hero_title": "Benvenuto nel futuro",
71
+ "hero_subtitle": "La tecnologia che cambia il mondo",
72
+ "cta_button": "Scopri di piΓΉ",
73
+ "form_name_label": "Nome completo",
74
+ "form_email_label": "Indirizzo email",
75
+ "form_message_label": "Il tuo messaggio",
76
+ "form_submit": "Invia messaggio",
77
+ "validation_required": "Questo campo Γ¨ obbligatorio",
78
+ "validation_email": "Inserisci un indirizzo email valido",
79
+ "success_notification": "<div class=\"notification is-success\"><strong>Grazie!</strong> Il tuo messaggio Γ¨ stato inviato</div>",
80
+ "error_notification": "<div class=\"notification is-danger\"><strong>Errore:</strong> Si Γ¨ verificato un problema</div>",
81
+ "user_greeting": "Ciao {firstName}, bentornato!",
82
+ "items_count": "Hai {itemCount} elementi nel carrello",
83
+ "last_login": "Ultimo accesso: {lastLoginDate}",
84
+ "footer_copyright": "Β© 2024 La Nostra Azienda. Tutti i diritti riservati."
85
+ }
86
+ }'
87
+ ```
88
+
89
+ ### Traduzione verso lingue diverse
90
+
91
+ ```bash
92
+ # Inglese
93
+ curl -X POST "http://localhost:8000/translate?target_language=en" \
94
+ -H "Content-Type: application/json" \
95
+ -d '{"locales": {"greeting": "Buongiorno!", "goodbye": "Arrivederci!"}}'
96
+
97
+ # Francese
98
+ curl -X POST "http://localhost:8000/translate?target_language=fr" \
99
+ -H "Content-Type: application/json" \
100
+ -d '{"locales": {"greeting": "Buongiorno!", "goodbye": "Arrivederci!"}}'
101
+
102
+ # Tedesco
103
+ curl -X POST "http://localhost:8000/translate?target_language=de" \
104
+ -H "Content-Type: application/json" \
105
+ -d '{"locales": {"greeting": "Buongiorno!", "goodbye": "Arrivederci!"}}'
106
+
107
+ # Spagnolo
108
+ curl -X POST "http://localhost:8000/translate?target_language=es" \
109
+ -H "Content-Type: application/json" \
110
+ -d '{"locales": {"greeting": "Buongiorno!", "goodbye": "Arrivederci!"}}'
111
+
112
+ # Russo
113
+ curl -X POST "http://localhost:8000/translate?target_language=ru" \
114
+ -H "Content-Type: application/json" \
115
+ -d '{"locales": {"greeting": "Buongiorno!", "goodbye": "Arrivederci!"}}'
116
+
117
+ # Giapponese
118
+ curl -X POST "http://localhost:8000/translate?target_language=ja" \
119
+ -H "Content-Type: application/json" \
120
+ -d '{"locales": {"greeting": "Buongiorno!", "goodbye": "Arrivederci!"}}'
121
+
122
+ # Cinese
123
+ curl -X POST "http://localhost:8000/translate?target_language=zh" \
124
+ -H "Content-Type: application/json" \
125
+ -d '{"locales": {"greeting": "Buongiorno!", "goodbye": "Arrivederci!"}}'
126
+ ```
127
+
128
+ ## Test degli errori
129
+
130
+ ### Lingua non supportata
131
+
132
+ ```bash
133
+ curl -X POST "http://localhost:8000/translate?target_language=xyz" \
134
+ -H "Content-Type: application/json" \
135
+ -d '{"locales": {"test": "Ciao"}}'
136
+ ```
137
+
138
+ ### Body malformato
139
+
140
+ ```bash
141
+ curl -X POST "http://localhost:8000/translate?target_language=en" \
142
+ -H "Content-Type: application/json" \
143
+ -d '{"wrong_field": "test"}'
144
+ ```
145
+
146
+ ### Parametro mancante
147
+
148
+ ```bash
149
+ curl -X POST "http://localhost:8000/translate" \
150
+ -H "Content-Type: application/json" \
151
+ -d '{"locales": {"test": "Ciao"}}'
152
+ ```
153
+
154
+ ## Esempi usando JavaScript (fetch)
155
+
156
+ ```javascript
157
+ // Traduzione di oggetti locale
158
+ const translateLocales = async () => {
159
+ const response = await fetch(
160
+ "http://localhost:8000/translate?target_language=en",
161
+ {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/json",
165
+ },
166
+ body: JSON.stringify({
167
+ locales: {
168
+ welcome: "Benvenuto!",
169
+ save_button: "Salva",
170
+ cancel_button: "Annulla",
171
+ },
172
+ }),
173
+ }
174
+ );
175
+
176
+ const data = await response.json();
177
+ console.log(data);
178
+ };
179
+
180
+ // Ottenere lingue supportate
181
+ const getSupportedLanguages = async () => {
182
+ const response = await fetch("http://localhost:8000/languages");
183
+ const data = await response.json();
184
+ console.log("Lingue supportate:", data.supported_languages);
185
+ };
186
+
187
+ // Traduzione singola
188
+ const translateSingle = async () => {
189
+ const response = await fetch(
190
+ "http://localhost:8000/translate-single?text=Ciao mondo&target_language=en",
191
+ {
192
+ method: "POST",
193
+ }
194
+ );
195
+ const data = await response.json();
196
+ console.log(data);
197
+ };
198
+ ```
199
+
200
+ ## Esempi usando Python (requests)
201
+
202
+ ```python
203
+ import requests
204
+
205
+ # Traduzione di oggetti locale
206
+ def translate_locales():
207
+ url = "http://localhost:8000/translate"
208
+ params = {"target_language": "en"}
209
+ data = {
210
+ "locales": {
211
+ "welcome": "Benvenuto!",
212
+ "save_button": "Salva",
213
+ "cancel_button": "Annulla"
214
+ }
215
+ }
216
+
217
+ response = requests.post(url, json=data, params=params)
218
+ print(response.json())
219
+
220
+ # Ottenere lingue supportate
221
+ def get_supported_languages():
222
+ response = requests.get("http://localhost:8000/languages")
223
+ print(response.json())
224
+
225
+ # Traduzione singola
226
+ def translate_single():
227
+ url = "http://localhost:8000/translate-single"
228
+ params = {
229
+ "text": "Ciao mondo",
230
+ "target_language": "en"
231
+ }
232
+
233
+ response = requests.post(url, params=params)
234
+ print(response.json())
235
+ ```
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Local Translation Server Docker Image
2
+ FROM python:3.9-slim
3
+
4
+ # Installa le dipendenze di sistema necessarie
5
+ RUN apt-get update && apt-get install -y \
6
+ git \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ # Crea un utente non-root
10
+ RUN useradd -m -u 1000 user
11
+ USER user
12
+ ENV PATH="/home/user/.local/bin:$PATH"
13
+
14
+ WORKDIR /app
15
+
16
+ # Copia e installa i requirements
17
+ COPY --chown=user ./requirements.txt requirements.txt
18
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
19
+
20
+ # Copia il codice dell'applicazione
21
+ COPY --chown=user . /app
22
+
23
+ # Espone la porta 8000
24
+ EXPOSE 8000
25
+
26
+ # Avvia il server
27
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
README.md CHANGED
@@ -1,13 +1,196 @@
1
- ---
2
- title: Locale Translate Server
3
- emoji: 🌐
4
- colorFrom: blue
5
- colorTo: yellow
6
- sdk: static
7
- pinned: false
8
- models:
9
- - Xenova/detr-resnet-50
10
- short_description: server used to translate web app locales from italian
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Local Translation Server
2
+
3
+ Un servizio di traduzione locale che traduce oggetti JSON con locales dall'italiano ad altre lingue utilizzando il modello T5 di Hugging Face.
4
+
5
+ ## Caratteristiche
6
+
7
+ - ✨ Traduzione automatica dall'italiano a più di 30 lingue
8
+ - πŸ”§ Preserva i tag HTML e i placeholder con parentesi graffe
9
+ - πŸš€ API REST facile da usare
10
+ - 🎯 Ottimizzato per oggetti locale di applicazioni web
11
+ - 🏠 Completamente locale, nessun servizio esterno richiesto
12
+
13
+ ## Installazione
14
+
15
+ 1. Clona il repository:
16
+
17
+ ```bash
18
+ git clone <repository-url>
19
+ cd locale_translate_server
20
+ ```
21
+
22
+ 2. Installa le dipendenze:
23
+
24
+ ```bash
25
+ pip install -r requirements.txt
26
+ ```
27
+
28
+ 3. Avvia il server:
29
+
30
+ ```bash
31
+ python app.py
32
+ ```
33
+
34
+ Il server sarΓ  disponibile su `http://localhost:8000`
35
+
36
+ ## Uso dell'API
37
+
38
+ ### Endpoint Principali
39
+
40
+ #### 1. Traduci Oggetti Locale - `POST /translate`
41
+
42
+ Traduce un oggetto JSON contenente locales dall'italiano alla lingua specificata.
43
+
44
+ **Parametri:**
45
+
46
+ - `target_language` (query parameter): Codice della lingua target (es: "en", "fr", "de")
47
+
48
+ **Body della richiesta:**
49
+
50
+ ```json
51
+ {
52
+ "locales": {
53
+ "welcome_message": "Benvenuto nella nostra applicazione!",
54
+ "button_save": "Salva",
55
+ "error_required": "Questo campo Γ¨ obbligatorio",
56
+ "html_content": "<p>Clicca <a href='#'>qui</a> per continuare</p>",
57
+ "template_message": "Ciao {name}, hai {count} nuovi messaggi"
58
+ }
59
+ }
60
+ ```
61
+
62
+ **Esempio di richiesta:**
63
+
64
+ ```bash
65
+ curl -X POST "http://localhost:8000/translate?target_language=en" \
66
+ -H "Content-Type: application/json" \
67
+ -d '{
68
+ "locales": {
69
+ "welcome": "Benvenuto!",
70
+ "save_btn": "Salva documento"
71
+ }
72
+ }'
73
+ ```
74
+
75
+ **Risposta:**
76
+
77
+ ```json
78
+ {
79
+ "translated_locales": {
80
+ "welcome": "Welcome!",
81
+ "save_btn": "Save document"
82
+ },
83
+ "source_language": "it",
84
+ "target_language": "en"
85
+ }
86
+ ```
87
+
88
+ #### 2. Traduci Singolo Testo - `POST /translate-single`
89
+
90
+ Traduce un singolo testo per test rapidi.
91
+
92
+ **Parametri:**
93
+
94
+ - `text` (query parameter): Testo in italiano da tradurre
95
+ - `target_language` (query parameter): Codice della lingua target
96
+
97
+ **Esempio:**
98
+
99
+ ```bash
100
+ curl -X POST "http://localhost:8000/translate-single?text=Ciao mondo&target_language=en"
101
+ ```
102
+
103
+ #### 3. Lingue Supportate - `GET /languages`
104
+
105
+ Restituisce la lista delle lingue supportate.
106
+
107
+ **Risposta:**
108
+
109
+ ```json
110
+ {
111
+ "supported_languages": ["en", "fr", "de", "es", "pt", "ru", "ja", "ko", "zh", ...]
112
+ }
113
+ ```
114
+
115
+ ### Lingue Supportate
116
+
117
+ Il servizio supporta traduzione verso le seguenti lingue:
118
+
119
+ - **en** - English
120
+ - **fr** - French
121
+ - **de** - German
122
+ - **es** - Spanish
123
+ - **pt** - Portuguese
124
+ - **ru** - Russian
125
+ - **ja** - Japanese
126
+ - **ko** - Korean
127
+ - **zh** - Chinese
128
+ - **ar** - Arabic
129
+ - **hi** - Hindi
130
+ - **nl** - Dutch
131
+ - **sv** - Swedish
132
+ - **da** - Danish
133
+ - **no** - Norwegian
134
+ - **fi** - Finnish
135
+ - **pl** - Polish
136
+ - **cs** - Czech
137
+ - **hu** - Hungarian
138
+ - **ro** - Romanian
139
+ - **bg** - Bulgarian
140
+ - **hr** - Croatian
141
+ - **sk** - Slovak
142
+ - **sl** - Slovenian
143
+ - **et** - Estonian
144
+ - **lv** - Latvian
145
+ - **lt** - Lithuanian
146
+ - **mt** - Maltese
147
+ - **el** - Greek
148
+ - **tr** - Turkish
149
+
150
+ ## Caratteristiche Speciali
151
+
152
+ ### Preservazione di HTML e Placeholder
153
+
154
+ Il servizio preserva automaticamente:
155
+
156
+ 1. **Tag HTML**: `<p>`, `<a>`, `<strong>`, etc.
157
+ 2. **Placeholder con parentesi graffe**: `{name}`, `{count}`, `{date}`, etc.
158
+
159
+ **Esempio:**
160
+
161
+ ```
162
+ Input: "Ciao {name}, hai <strong>{count}</strong> messaggi"
163
+ Output: "Hello {name}, you have <strong>{count}</strong> messages"
164
+ ```
165
+
166
+ ## Configurazione
167
+
168
+ Puoi configurare il servizio tramite variabili d'ambiente:
169
+
170
+ - `MODEL_NAME`: Nome del modello T5 da utilizzare (default: "google/t5-v1_1-base")
171
+ - `ENVIRONMENT`: Ambiente di esecuzione (default: "development")
172
+ - `DEBUG`: Abilita debug logging (default: "true")
173
+
174
+ ## Documentazione API
175
+
176
+ Una volta avviato il server, puoi accedere alla documentazione interativa Swagger UI su:
177
+
178
+ `http://localhost:8000/docs`
179
+
180
+ ## Performance
181
+
182
+ Il modello T5-base offre un buon bilanciamento tra qualitΓ  di traduzione e velocitΓ . Per applicazioni che richiedono traduzione di alta qualitΓ , puoi configurare un modello piΓΉ grande tramite la variabile d'ambiente `MODEL_NAME`:
183
+
184
+ ```bash
185
+ export MODEL_NAME="google/t5-v1_1-large"
186
+ python app.py
187
+ ```
188
+
189
+ ## Docker
190
+
191
+ Il progetto include un Dockerfile per facilitΓ  di deployment:
192
+
193
+ ```bash
194
+ docker build -t locale-translate-server .
195
+ docker run -p 8000:8000 locale-translate-server
196
+ ```
__pycache__/app.cpython-311.pyc ADDED
Binary file (7.86 kB). View file
 
__pycache__/chat_service.cpython-311.pyc ADDED
Binary file (8.82 kB). View file
 
__pycache__/config.cpython-311.pyc ADDED
Binary file (902 Bytes). View file
 
app.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, Query
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel
4
+ from typing import Dict, Any, Optional, List
5
+ from chat_service import translate_locale, get_supported_languages
6
+ import logging
7
+
8
+ # Configura il logging
9
+ logging.basicConfig(level=logging.INFO)
10
+ logger = logging.getLogger(__name__)
11
+
12
+ app = FastAPI(
13
+ title="Local Translation Server",
14
+ description="API per tradurre oggetti locale dall'italiano ad altre lingue",
15
+ version="1.0.0"
16
+ )
17
+
18
+ # Abilita CORS per permettere richieste da altri domini
19
+ app.add_middleware(
20
+ CORSMiddleware,
21
+ allow_origins=["*"],
22
+ allow_credentials=True,
23
+ allow_methods=["*"],
24
+ allow_headers=["*"],
25
+ )
26
+
27
+
28
+ class LocaleRequest(BaseModel):
29
+ """Modello per la richiesta di traduzione di un oggetto locale."""
30
+ locales: Dict[str, str]
31
+
32
+ model_config = {
33
+ "json_schema_extra": {
34
+ "example": {
35
+ "locales": {
36
+ "welcome_message": "Benvenuto nella nostra applicazione!",
37
+ "button_save": "Salva",
38
+ "error_required": "Questo campo Γ¨ obbligatorio",
39
+ "html_content": "<p>Clicca <a href='#'>qui</a> per continuare</p>",
40
+ "template_message": "Ciao {name}, hai {count} nuovi messaggi"
41
+ }
42
+ }
43
+ }
44
+ }
45
+
46
+
47
+ class LocaleResponse(BaseModel):
48
+ """Modello per la risposta di traduzione."""
49
+ translated_locales: Dict[str, str]
50
+ source_language: str
51
+ target_language: str
52
+
53
+ model_config = {
54
+ "json_schema_extra": {
55
+ "example": {
56
+ "translated_locales": {
57
+ "welcome_message": "Welcome to our application!",
58
+ "button_save": "Save",
59
+ "error_required": "This field is required",
60
+ "html_content": "<p>Click <a href='#'>here</a> to continue</p>",
61
+ "template_message": "Hello {name}, you have {count} new messages"
62
+ },
63
+ "source_language": "it",
64
+ "target_language": "en"
65
+ }
66
+ }
67
+ }
68
+
69
+
70
+ class LanguagesResponse(BaseModel):
71
+ """Modello per la risposta delle lingue supportate."""
72
+ supported_languages: List[str]
73
+
74
+
75
+ @app.get("/", summary="Health Check")
76
+ def health_check():
77
+ """Endpoint per verificare che il server sia in esecuzione."""
78
+ return {
79
+ "service": "Local Translation Server",
80
+ "status": "running",
81
+ "version": "1.0.0"
82
+ }
83
+
84
+
85
+ @app.get("/languages", response_model=LanguagesResponse, summary="Lingue Supportate")
86
+ def get_languages():
87
+ """Restituisce la lista delle lingue supportate per la traduzione."""
88
+ return LanguagesResponse(supported_languages=get_supported_languages())
89
+
90
+
91
+ @app.post("/translate", response_model=LocaleResponse, summary="Traduci Locales")
92
+ async def translate_locales(
93
+ request: LocaleRequest,
94
+ target_language: str = Query(
95
+ ...,
96
+ description="Codice della lingua target (es: 'en', 'fr', 'de')",
97
+ example="en"
98
+ )
99
+ ):
100
+ """
101
+ Traduce un oggetto contenente locales dall'italiano alla lingua specificata.
102
+
103
+ - **locales**: Dizionario chiave-valore dove ogni chiave Γ¨ un identificatore
104
+ e ogni valore Γ¨ il testo in italiano da tradurre
105
+ - **target_language**: Codice della lingua target (parametro query)
106
+
107
+ Il servizio preserva:
108
+ - Tag HTML nel testo
109
+ - Placeholders con parentesi graffe (es: {name}, {count})
110
+ - Struttura delle chiavi originali
111
+ """
112
+ try:
113
+ logger.info(f"Richiesta traduzione per {len(request.locales)} locales verso {target_language}")
114
+
115
+ # Verifica che la lingua target sia supportata
116
+ supported_languages = get_supported_languages()
117
+ if target_language not in supported_languages:
118
+ raise HTTPException(
119
+ status_code=400,
120
+ detail=f"Lingua '{target_language}' non supportata. Lingue disponibili: {supported_languages}"
121
+ )
122
+
123
+ # Esegui la traduzione
124
+ translated_data = translate_locale(request.locales, target_language)
125
+
126
+ logger.info(f"Traduzione completata per {len(translated_data)} locales")
127
+
128
+ return LocaleResponse(
129
+ translated_locales=translated_data,
130
+ source_language="it",
131
+ target_language=target_language
132
+ )
133
+
134
+ except ValueError as e:
135
+ raise HTTPException(status_code=400, detail=str(e))
136
+ except Exception as e:
137
+ logger.error(f"Errore durante la traduzione: {str(e)}")
138
+ raise HTTPException(status_code=500, detail=f"Errore interno del server: {str(e)}")
139
+
140
+
141
+ @app.post("/translate-single", summary="Traduci Singolo Testo")
142
+ async def translate_single_text(
143
+ text: str = Query(..., description="Testo in italiano da tradurre"),
144
+ target_language: str = Query(
145
+ ...,
146
+ description="Codice della lingua target (es: 'en', 'fr', 'de')",
147
+ example="en"
148
+ )
149
+ ):
150
+ """
151
+ Traduce un singolo testo dall'italiano alla lingua specificata.
152
+ Utile per test rapidi o traduzioni singole.
153
+ """
154
+ try:
155
+ from chat_service import get_translation_service
156
+
157
+ # Verifica che la lingua target sia supportata
158
+ supported_languages = get_supported_languages()
159
+ if target_language not in supported_languages:
160
+ raise HTTPException(
161
+ status_code=400,
162
+ detail=f"Lingua '{target_language}' non supportata. Lingue disponibili: {supported_languages}"
163
+ )
164
+
165
+ # Ottieni il servizio di traduzione e traduci
166
+ translation_service = get_translation_service()
167
+ translated_text = translation_service.translate_text(text, target_language)
168
+
169
+ return {
170
+ "original_text": text,
171
+ "translated_text": translated_text,
172
+ "source_language": "it",
173
+ "target_language": target_language
174
+ }
175
+
176
+ except ValueError as e:
177
+ raise HTTPException(status_code=400, detail=str(e))
178
+ except Exception as e:
179
+ logger.error(f"Errore durante la traduzione: {str(e)}")
180
+ raise HTTPException(status_code=500, detail=f"Errore interno del server: {str(e)}")
181
+
182
+
183
+ if __name__ == "__main__":
184
+ import uvicorn
185
+ uvicorn.run(app, host="0.0.0.0", port=8000)
chat_service.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import pipeline
2
+ import torch
3
+ from config import Config
4
+ from typing import Dict, Any
5
+ import re
6
+
7
+ # Mapping delle lingue supportate con i loro nomi per i modelli Helsinki-NLP
8
+ LANGUAGE_CODES = {
9
+ "en": "English",
10
+ "fr": "French",
11
+ "de": "German",
12
+ "es": "Spanish",
13
+ "pt": "Portuguese",
14
+ "ru": "Russian",
15
+ "ja": "Japanese",
16
+ "ko": "Korean",
17
+ "zh": "Chinese",
18
+ "ar": "Arabic",
19
+ "hi": "Hindi",
20
+ "nl": "Dutch",
21
+ "sv": "Swedish",
22
+ "da": "Danish",
23
+ "no": "Norwegian",
24
+ "fi": "Finnish",
25
+ "pl": "Polish",
26
+ "cs": "Czech",
27
+ "hu": "Hungarian",
28
+ "ro": "Romanian",
29
+ "bg": "Bulgarian",
30
+ "hr": "Croatian",
31
+ "sk": "Slovak",
32
+ "sl": "Slovenian",
33
+ "et": "Estonian",
34
+ "lv": "Latvian",
35
+ "lt": "Lithuanian",
36
+ "mt": "Maltese",
37
+ "el": "Greek",
38
+ "tr": "Turkish"
39
+ }
40
+
41
+ # Mapping per i modelli di traduzione Helsinki-NLP (da italiano verso altre lingue)
42
+ HELSINKI_MODELS = {
43
+ "en": "Helsinki-NLP/opus-mt-it-en",
44
+ "fr": "Helsinki-NLP/opus-mt-it-fr",
45
+ "de": "Helsinki-NLP/opus-mt-it-de",
46
+ "es": "Helsinki-NLP/opus-mt-it-es",
47
+ "pt": "Helsinki-NLP/opus-mt-it-pt",
48
+ "ru": "Helsinki-NLP/opus-mt-it-ru",
49
+ "nl": "Helsinki-NLP/opus-mt-it-nl",
50
+ "sv": "Helsinki-NLP/opus-mt-it-sv",
51
+ "da": "Helsinki-NLP/opus-mt-it-da",
52
+ "no": "Helsinki-NLP/opus-mt-it-no",
53
+ "fi": "Helsinki-NLP/opus-mt-it-fi",
54
+ "pl": "Helsinki-NLP/opus-mt-it-pl",
55
+ "cs": "Helsinki-NLP/opus-mt-it-cs",
56
+ "hu": "Helsinki-NLP/opus-mt-it-hu",
57
+ "ro": "Helsinki-NLP/opus-mt-it-ro",
58
+ "bg": "Helsinki-NLP/opus-mt-it-bg",
59
+ "hr": "Helsinki-NLP/opus-mt-it-hr",
60
+ "sk": "Helsinki-NLP/opus-mt-it-sk",
61
+ "sl": "Helsinki-NLP/opus-mt-it-sl",
62
+ "et": "Helsinki-NLP/opus-mt-it-et",
63
+ "lv": "Helsinki-NLP/opus-mt-it-lv",
64
+ "lt": "Helsinki-NLP/opus-mt-it-lt",
65
+ "el": "Helsinki-NLP/opus-mt-it-el",
66
+ "tr": "Helsinki-NLP/opus-mt-it-tr"
67
+ }
68
+
69
+
70
+ class TranslationService:
71
+ def __init__(self, device: str = "cpu"):
72
+ self.device = device
73
+ self.translators = {} # Cache per i translator
74
+
75
+ def _get_translator(self, target_language: str):
76
+ """Ottiene o crea un translator per la lingua target."""
77
+ if target_language not in self.translators:
78
+ if target_language in HELSINKI_MODELS:
79
+ model_name = HELSINKI_MODELS[target_language]
80
+ try:
81
+ self.translators[target_language] = pipeline(
82
+ "translation",
83
+ model=model_name,
84
+ device=0 if self.device == "cuda" else -1,
85
+ torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
86
+ )
87
+ except Exception as e:
88
+ # Fallback per lingue senza modelli specifici
89
+ print(f"Modello non disponibile per {target_language}, usando fallback: {e}")
90
+ return None
91
+ else:
92
+ return None
93
+ return self.translators[target_language]
94
+
95
+ def _extract_placeholders(self, text: str) -> tuple[str, Dict[str, str]]:
96
+ """Estrae i placeholders HTML e delle parentesi graffe dal testo."""
97
+ placeholders = {}
98
+
99
+ # Pattern per HTML tags
100
+ html_pattern = r'<[^>]+>'
101
+ html_matches = re.findall(html_pattern, text)
102
+
103
+ # Pattern per parentesi graffe
104
+ brace_pattern = r'\{[^}]+\}'
105
+ brace_matches = re.findall(brace_pattern, text)
106
+
107
+ # Sostituisce HTML tags con placeholders
108
+ processed_text = text
109
+ for i, match in enumerate(html_matches):
110
+ placeholder = f"HTMLTAG{i}"
111
+ placeholders[placeholder] = match
112
+ processed_text = processed_text.replace(match, placeholder, 1)
113
+
114
+ # Sostituisce parentesi graffe con placeholders
115
+ for i, match in enumerate(brace_matches):
116
+ placeholder = f"PLACEHOLDER{i}"
117
+ placeholders[placeholder] = match
118
+ processed_text = processed_text.replace(match, placeholder, 1)
119
+
120
+ return processed_text, placeholders
121
+
122
+ def _restore_placeholders(self, text: str, placeholders: Dict[str, str]) -> str:
123
+ """Ripristina i placeholders nel testo tradotto."""
124
+ for placeholder, original in placeholders.items():
125
+ # Rimuovi spazi extra attorno ai placeholder
126
+ text = text.replace(f" {placeholder} ", original)
127
+ text = text.replace(f" {placeholder}", original)
128
+ text = text.replace(f"{placeholder} ", original)
129
+ text = text.replace(placeholder, original)
130
+ return text
131
+
132
+ def translate_text(self, text: str, target_language: str) -> str:
133
+ """Traduce il testo dall'italiano alla lingua target."""
134
+ if target_language not in LANGUAGE_CODES:
135
+ raise ValueError(f"Lingua non supportata: {target_language}")
136
+
137
+ # Estrai placeholders
138
+ clean_text, placeholders = self._extract_placeholders(text)
139
+
140
+ # Ottieni il translator
141
+ translator = self._get_translator(target_language)
142
+ if translator is None:
143
+ # Traduzione semplice di fallback (mantiene il testo originale)
144
+ return f"[TRANSLATION NOT AVAILABLE: {text}]"
145
+
146
+ try:
147
+ # Esegui la traduzione
148
+ result = translator(clean_text, max_length=512)
149
+
150
+ if isinstance(result, list) and len(result) > 0:
151
+ translated_text = result[0]['translation_text']
152
+ else:
153
+ translated_text = str(result)
154
+
155
+ # Ripristina i placeholders
156
+ translated_text = self._restore_placeholders(translated_text, placeholders)
157
+
158
+ return translated_text.strip()
159
+
160
+ except Exception as e:
161
+ print(f"Errore durante la traduzione: {e}")
162
+ return f"[TRANSLATION ERROR: {text}]"
163
+
164
+ def translate_locale_object(self, locale_data: Dict[str, Any], target_language: str) -> Dict[str, Any]:
165
+ """Traduce un oggetto locale completo."""
166
+ if target_language not in LANGUAGE_CODES:
167
+ raise ValueError(f"Lingua non supportata: {target_language}")
168
+
169
+ translated_data = {}
170
+
171
+ for key, content in locale_data.items():
172
+ if isinstance(content, str):
173
+ translated_data[key] = self.translate_text(content, target_language)
174
+ else:
175
+ # Mantieni il valore originale se non Γ¨ una stringa
176
+ translated_data[key] = content
177
+
178
+ return translated_data
179
+
180
+
181
+ # Istanza globale del servizio di traduzione
182
+ translation_service = None
183
+
184
+ def get_translation_service():
185
+ global translation_service
186
+ if translation_service is None:
187
+ config = Config()
188
+ translation_service = TranslationService(device=config.DEVICE)
189
+ return translation_service
190
+
191
+ def translate_locale(locale_data: Dict[str, Any], target_language: str) -> Dict[str, Any]:
192
+ """Funzione helper per tradurre un oggetto locale."""
193
+ service = get_translation_service()
194
+ return service.translate_locale_object(locale_data, target_language)
195
+
196
+ def get_supported_languages():
197
+ """Restituisce la lista delle lingue supportate."""
198
+ return list(HELSINKI_MODELS.keys())
config.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import torch
3
+
4
+ class Config:
5
+ PROJECT_NAME = "Local Translation Server"
6
+ VERSION = "1.0.0"
7
+ ENVIRONMENT = os.getenv("ENVIRONMENT", "development")
8
+ DEBUG = os.getenv("DEBUG", "true") == "true"
9
+ MODEL_NAME = os.getenv("MODEL_NAME", "google/mt5-small") # Usando mT5 per traduzione multilingue
10
+ DEVICE = "mps" if torch.mps.is_available() else "cpu"
index.html DELETED
@@ -1,29 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8" />
6
- <link rel="stylesheet" href="style.css" />
7
-
8
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9
- <title>Transformers.js - Object Detection</title>
10
- </head>
11
-
12
- <body>
13
- <h1>Object Detection w/ πŸ€— Transformers.js</h1>
14
- <label id="container" for="upload">
15
- <svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
16
- <path fill="#000"
17
- d="M3.5 24.3a3 3 0 0 1-1.9-.8c-.5-.5-.8-1.2-.8-1.9V2.9c0-.7.3-1.3.8-1.9.6-.5 1.2-.7 2-.7h18.6c.7 0 1.3.2 1.9.7.5.6.7 1.2.7 2v18.6c0 .7-.2 1.4-.7 1.9a3 3 0 0 1-2 .8H3.6Zm0-2.7h18.7V2.9H3.5v18.7Zm2.7-2.7h13.3c.3 0 .5 0 .6-.3v-.7l-3.7-5a.6.6 0 0 0-.6-.2c-.2 0-.4 0-.5.3l-3.5 4.6-2.4-3.3a.6.6 0 0 0-.6-.3c-.2 0-.4.1-.5.3l-2.7 3.6c-.1.2-.2.4 0 .7.1.2.3.3.6.3Z">
18
- </path>
19
- </svg>
20
- Click to upload image
21
- <label id="example">(or try example)</label>
22
- </label>
23
- <label id="status">Loading model...</label>
24
- <input id="upload" type="file" accept="image/*" />
25
-
26
- <script src="index.js" type="module"></script>
27
- </body>
28
-
29
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
index.js DELETED
@@ -1,76 +0,0 @@
1
- import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.4.1';
2
-
3
- // Reference the elements that we will need
4
- const status = document.getElementById('status');
5
- const fileUpload = document.getElementById('upload');
6
- const imageContainer = document.getElementById('container');
7
- const example = document.getElementById('example');
8
-
9
- const EXAMPLE_URL = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/city-streets.jpg';
10
-
11
- // Create a new object detection pipeline
12
- status.textContent = 'Loading model...';
13
- const detector = await pipeline('object-detection', 'Xenova/detr-resnet-50');
14
- status.textContent = 'Ready';
15
-
16
- example.addEventListener('click', (e) => {
17
- e.preventDefault();
18
- detect(EXAMPLE_URL);
19
- });
20
-
21
- fileUpload.addEventListener('change', function (e) {
22
- const file = e.target.files[0];
23
- if (!file) {
24
- return;
25
- }
26
-
27
- const reader = new FileReader();
28
-
29
- // Set up a callback when the file is loaded
30
- reader.onload = e2 => detect(e2.target.result);
31
-
32
- reader.readAsDataURL(file);
33
- });
34
-
35
-
36
- // Detect objects in the image
37
- async function detect(img) {
38
- imageContainer.innerHTML = '';
39
- imageContainer.style.backgroundImage = `url(${img})`;
40
-
41
- status.textContent = 'Analysing...';
42
- const output = await detector(img, {
43
- threshold: 0.5,
44
- percentage: true,
45
- });
46
- status.textContent = '';
47
- output.forEach(renderBox);
48
- }
49
-
50
- // Render a bounding box and label on the image
51
- function renderBox({ box, label }) {
52
- const { xmax, xmin, ymax, ymin } = box;
53
-
54
- // Generate a random color for the box
55
- const color = '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, 0);
56
-
57
- // Draw the box
58
- const boxElement = document.createElement('div');
59
- boxElement.className = 'bounding-box';
60
- Object.assign(boxElement.style, {
61
- borderColor: color,
62
- left: 100 * xmin + '%',
63
- top: 100 * ymin + '%',
64
- width: 100 * (xmax - xmin) + '%',
65
- height: 100 * (ymax - ymin) + '%',
66
- })
67
-
68
- // Draw label
69
- const labelElement = document.createElement('span');
70
- labelElement.textContent = label;
71
- labelElement.className = 'bounding-box-label';
72
- labelElement.style.backgroundColor = color;
73
-
74
- boxElement.appendChild(labelElement);
75
- imageContainer.appendChild(boxElement);
76
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ transformers
4
+ torch
5
+ sentencepiece
6
+ protobuf
start_server.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script per avviare il Local Translation Server
4
+ """
5
+
6
+ import uvicorn
7
+ from app import app
8
+
9
+ if __name__ == "__main__":
10
+ print("πŸš€ Avvio Local Translation Server...")
11
+ print("πŸ“‘ Server disponibile su: http://localhost:8000")
12
+ print("πŸ“š Documentazione API: http://localhost:8000/docs")
13
+ print("πŸ” Interfaccia Redoc: http://localhost:8000/redoc")
14
+ print("\n" + "="*50)
15
+
16
+ uvicorn.run(
17
+ app,
18
+ host="0.0.0.0",
19
+ port=8000,
20
+ log_level="info",
21
+ reload=False # Disabilita il reload automatico in produzione
22
+ )
style.css DELETED
@@ -1,76 +0,0 @@
1
- * {
2
- box-sizing: border-box;
3
- padding: 0;
4
- margin: 0;
5
- font-family: sans-serif;
6
- }
7
-
8
- html,
9
- body {
10
- height: 100%;
11
- }
12
-
13
- body {
14
- padding: 32px;
15
- }
16
-
17
- body,
18
- #container {
19
- display: flex;
20
- flex-direction: column;
21
- justify-content: center;
22
- align-items: center;
23
- }
24
-
25
- #container {
26
- position: relative;
27
- gap: 0.4rem;
28
-
29
- width: 640px;
30
- height: 640px;
31
- max-width: 100%;
32
- max-height: 100%;
33
-
34
- border: 2px dashed #D1D5DB;
35
- border-radius: 0.75rem;
36
- overflow: hidden;
37
- cursor: pointer;
38
- margin: 1rem;
39
-
40
- background-size: 100% 100%;
41
- background-position: center;
42
- background-repeat: no-repeat;
43
- font-size: 18px;
44
- }
45
-
46
- #upload {
47
- display: none;
48
- }
49
-
50
- svg {
51
- pointer-events: none;
52
- }
53
-
54
- #example {
55
- font-size: 14px;
56
- text-decoration: underline;
57
- cursor: pointer;
58
- }
59
-
60
- #example:hover {
61
- color: #2563EB;
62
- }
63
-
64
- .bounding-box {
65
- position: absolute;
66
- box-sizing: border-box;
67
- border: solid 2px;
68
- }
69
-
70
- .bounding-box-label {
71
- color: white;
72
- position: absolute;
73
- font-size: 12px;
74
- margin: -16px 0 0 -2px;
75
- padding: 1px;
76
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_api.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script di test per l'API di traduzione locale.
4
+ Questo script testa tutti gli endpoint principali del servizio.
5
+ """
6
+
7
+ import requests
8
+ import json
9
+ import time
10
+
11
+ # Configurazione
12
+ BASE_URL = "http://localhost:8000"
13
+
14
+ def test_health_check():
15
+ """Testa l'endpoint di health check."""
16
+ print("πŸ₯ Test Health Check...")
17
+ try:
18
+ response = requests.get(f"{BASE_URL}/")
19
+ print(f"Status: {response.status_code}")
20
+ print(f"Response: {response.json()}")
21
+ print("βœ… Health check OK\n")
22
+ return True
23
+ except Exception as e:
24
+ print(f"❌ Health check failed: {e}\n")
25
+ return False
26
+
27
+ def test_get_languages():
28
+ """Testa l'endpoint per ottenere le lingue supportate."""
29
+ print("🌍 Test Lingue Supportate...")
30
+ try:
31
+ response = requests.get(f"{BASE_URL}/languages")
32
+ data = response.json()
33
+ languages = data["supported_languages"]
34
+ print(f"Status: {response.status_code}")
35
+ print(f"Lingue supportate ({len(languages)}): {languages[:10]}...")
36
+ print("βœ… Lingue supportate OK\n")
37
+ return True
38
+ except Exception as e:
39
+ print(f"❌ Test lingue failed: {e}\n")
40
+ return False
41
+
42
+ def test_translate_single():
43
+ """Testa la traduzione di un singolo testo."""
44
+ print("πŸ“ Test Traduzione Singola...")
45
+ try:
46
+ params = {
47
+ "text": "Ciao mondo! Come stai oggi?",
48
+ "target_language": "en"
49
+ }
50
+ response = requests.post(f"{BASE_URL}/translate-single", params=params)
51
+ data = response.json()
52
+ print(f"Status: {response.status_code}")
53
+ print(f"Testo originale: {data['original_text']}")
54
+ print(f"Testo tradotto: {data['translated_text']}")
55
+ print("βœ… Traduzione singola OK\n")
56
+ return True
57
+ except Exception as e:
58
+ print(f"❌ Test traduzione singola failed: {e}\n")
59
+ return False
60
+
61
+ def test_translate_locales():
62
+ """Testa la traduzione di oggetti locale complessi."""
63
+ print("🌐 Test Traduzione Locales...")
64
+ try:
65
+ locale_data = {
66
+ "locales": {
67
+ "welcome_message": "Benvenuto nella nostra applicazione!",
68
+ "button_save": "Salva documento",
69
+ "error_required": "Questo campo Γ¨ obbligatorio",
70
+ "html_content": "<p>Clicca <a href='#'>qui</a> per continuare</p>",
71
+ "template_message": "Ciao {name}, hai {count} nuovi messaggi",
72
+ "complex_html": "<div class='alert'><strong>Attenzione:</strong> Operazione completata con successo!</div>"
73
+ }
74
+ }
75
+
76
+ params = {"target_language": "en"}
77
+ response = requests.post(
78
+ f"{BASE_URL}/translate",
79
+ json=locale_data,
80
+ params=params
81
+ )
82
+
83
+ data = response.json()
84
+ print(f"Status: {response.status_code}")
85
+ print("Traduzioni:")
86
+ for key, value in data["translated_locales"].items():
87
+ original = locale_data["locales"][key]
88
+ print(f" {key}:")
89
+ print(f" IT: {original}")
90
+ print(f" EN: {value}")
91
+
92
+ print("βœ… Traduzione locales OK\n")
93
+ return True
94
+ except Exception as e:
95
+ print(f"❌ Test traduzione locales failed: {e}\n")
96
+ return False
97
+
98
+ def test_multiple_languages():
99
+ """Testa la traduzione verso lingue diverse."""
100
+ print("πŸ—£οΈ Test Lingue Multiple...")
101
+ try:
102
+ locale_data = {
103
+ "locales": {
104
+ "greeting": "Buongiorno!",
105
+ "goodbye": "Arrivederci!"
106
+ }
107
+ }
108
+
109
+ languages_to_test = ["en", "fr", "de", "es"]
110
+
111
+ for lang in languages_to_test:
112
+ params = {"target_language": lang}
113
+ response = requests.post(
114
+ f"{BASE_URL}/translate",
115
+ json=locale_data,
116
+ params=params
117
+ )
118
+
119
+ data = response.json()
120
+ print(f"Traduzione in {lang}:")
121
+ for key, value in data["translated_locales"].items():
122
+ print(f" {key}: {value}")
123
+
124
+ print("βœ… Test lingue multiple OK\n")
125
+ return True
126
+ except Exception as e:
127
+ print(f"❌ Test lingue multiple failed: {e}\n")
128
+ return False
129
+
130
+ def test_error_handling():
131
+ """Testa la gestione degli errori."""
132
+ print("⚠️ Test Gestione Errori...")
133
+ try:
134
+ # Test lingua non supportata
135
+ locale_data = {"locales": {"test": "Ciao"}}
136
+ params = {"target_language": "xx"} # Lingua inesistente
137
+
138
+ response = requests.post(
139
+ f"{BASE_URL}/translate",
140
+ json=locale_data,
141
+ params=params
142
+ )
143
+
144
+ print(f"Status per lingua inesistente: {response.status_code}")
145
+ if response.status_code == 400:
146
+ print("βœ… Gestione errore lingua inesistente OK")
147
+
148
+ # Test body malformato
149
+ response = requests.post(
150
+ f"{BASE_URL}/translate",
151
+ json={"wrong_field": "test"},
152
+ params={"target_language": "en"}
153
+ )
154
+
155
+ print(f"Status per body malformato: {response.status_code}")
156
+ print("βœ… Test gestione errori OK\n")
157
+ return True
158
+ except Exception as e:
159
+ print(f"❌ Test gestione errori failed: {e}\n")
160
+ return False
161
+
162
+ def main():
163
+ """Esegue tutti i test."""
164
+ print("πŸš€ Avvio test del Local Translation Server\n")
165
+ print("=" * 50)
166
+
167
+ tests = [
168
+ test_health_check,
169
+ test_get_languages,
170
+ test_translate_single,
171
+ test_translate_locales,
172
+ test_multiple_languages,
173
+ test_error_handling
174
+ ]
175
+
176
+ passed = 0
177
+ total = len(tests)
178
+
179
+ for test in tests:
180
+ try:
181
+ if test():
182
+ passed += 1
183
+ time.sleep(1) # Pausa tra i test
184
+ except Exception as e:
185
+ print(f"❌ Test failed with exception: {e}\n")
186
+
187
+ print("=" * 50)
188
+ print(f"πŸ“Š Risultati: {passed}/{total} test passati")
189
+
190
+ if passed == total:
191
+ print("πŸŽ‰ Tutti i test sono passati!")
192
+ else:
193
+ print("⚠️ Alcuni test sono falliti. Controlla i log sopra.")
194
+
195
+ if __name__ == "__main__":
196
+ main()