Spaces:
Sleeping
Sleeping
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Wiki - Translator API</title> | |
| <style> | |
| * { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; background: #f8f9fa; } | |
| .container { max-width: 900px; margin: 0 auto; padding: 40px 20px; } | |
| header { text-align: center; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 1px solid #e9ecef; } | |
| h1 { font-size: 36px; color: #1a73e8; margin-bottom: 10px; } | |
| .subtitle { color: #666; font-size: 18px; } | |
| .nav { display: flex; gap: 10px; justify-content: center; margin-bottom: 30px; flex-wrap: wrap; } | |
| .nav-btn { padding: 10px 20px; background: white; border: 1px solid #ddd; border-radius: 6px; cursor: pointer; transition: all 0.2s; } | |
| .nav-btn:hover, .nav-btn.active { background: #1a73e8; color: white; border-color: #1a73e8; } | |
| .section { background: white; border-radius: 12px; padding: 30px; margin-bottom: 30px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); display: none; } | |
| .section.active { display: block; animation: fadeIn 0.3s; } | |
| @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } | |
| h2 { color: #1a1a2e; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 2px solid #1a73e8; } | |
| h3 { color: #333; margin: 25px 0 15px; } | |
| p { margin-bottom: 15px; } | |
| code { background: #f1f3f4; padding: 2px 6px; border-radius: 4px; font-family: 'Monaco', 'Menlo', monospace; font-size: 0.9em; } | |
| pre { background: #1e1e1e; color: #d4d4d4; padding: 20px; border-radius: 8px; overflow-x: auto; margin: 15px 0; } | |
| pre code { background: none; padding: 0; color: inherit; } | |
| .token.comment { color: #6a9955; } | |
| .token.keyword { color: #c586c0; } | |
| .token.string { color: #ce9178; } | |
| .token.function { color: #dcdcaa; } | |
| .token.property { color: #9cdcfe; } | |
| .endpoint { background: #e8f0fe; border-left: 4px solid #1a73e8; padding: 15px; margin: 15px 0; border-radius: 0 8px 8px 0; } | |
| .endpoint.method { display: inline-block; background: #1a73e8; color: white; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; margin-right: 10px; } | |
| .endpoint.path { font-family: monospace; font-weight: bold; } | |
| table { width: 100%; border-collapse: collapse; margin: 15px 0; } | |
| th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e9ecef; } | |
| th { background: #f8f9fa; font-weight: 600; } | |
| .badge { display: inline-block; padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: bold; } | |
| .badge-success { background: #d4edda; color: #155724; } | |
| .badge-info { background: #d1ecf1; color: #0c5460; } | |
| .badge-warning { background: #fff3cd; color: #856404; } | |
| .lang-tabs { display: flex; gap: 8px; margin-bottom: 15px; flex-wrap: wrap; } | |
| .lang-tab { padding: 8px 16px; background: #f1f3f4; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; } | |
| .lang-tab.active { background: #1a73e8; color: white; } | |
| .code-block { display: none; } | |
| .code-block.active { display: block; } | |
| .back-link { display: inline-block; margin-bottom: 30px; color: #1a73e8; text-decoration: none; } | |
| .back-link:hover { text-decoration: underline; } | |
| .info-box { background: #e8f0fe; border-radius: 8px; padding: 20px; margin: 20px 0; } | |
| .info-box h4 { color: #1a73e8; margin-bottom: 10px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <a href="/" class="back-link">← Retour à l'accueil</a> | |
| <header> | |
| <h1>📖 Wiki - Translator API</h1> | |
| <p class="subtitle">Documentation complète pour intégrer l'API de traduction</p> | |
| </header> | |
| <nav class="nav"> | |
| <button class="nav-btn active" onclick="showSection('intro')">🚀 Démarrage</button> | |
| <button class="nav-btn" onclick="showSection('endpoints')">🔌 Endpoints</button> | |
| <button class="nav-btn" onclick="showSection('javascript')">📜 JavaScript</button> | |
| <button class="nav-btn" onclick="showSection('python')">🐍 Python</button> | |
| <button class="nav-btn" onclick="showSection('java')">☕ Java</button> | |
| <button class="nav-btn" onclick="showSection('curl')">🌐 cURL</button> | |
| <button class="nav-btn" onclick="showSection('limits')">⚙️ Limites</button> | |
| </nav> | |
| <!-- Intro Section --> | |
| <section id="intro" class="section active"> | |
| <h2>🚀 Démarrage rapide</h2> | |
| <p>Translator API est une alternative gratuite à Google Translate, accessible via une API REST simple. Pas de clé API requise, pas d'inscription nécessaire.</p> | |
| <div class="info-box"> | |
| <h4>✨ Fonctionnalités</h4> | |
| <ul style="margin-left: 20px;"> | |
| <li>🆓 Gratuit et sans limite de compte</li> | |
| <li>⚡ 30 requêtes/minute par IP</li> | |
| <li>🌍 100+ langues supportées</li> | |
| <li>🔒 Détection automatique de langue</li> | |
| <li>📊 Dashboard admin avec métriques temps réel</li> | |
| <li>⚖️ Architecture distribuée (Central + Workers)</li> | |
| </ul> | |
| </div> | |
| <h3>URL de base</h3> | |
| <p>Remplacez <code>VOTRE_HOTE</code> par l'adresse de votre instance :</p> | |
| <pre><code>https://VOTRE_HOTE:7860</code></pre> | |
| <h3>Test rapide</h3> | |
| <pre><code class="language-bash">curl -X POST https://VOTRE_HOTE:7860/translate \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"text": "Bonjour le monde", "source": "fr", "target": "en"}'</code></pre> | |
| <p><strong>Réponse :</strong></p> | |
| <pre><code>{ | |
| "translatedText": "Hello world", | |
| "source": "fr", | |
| "target": "en", | |
| "duration": 245 | |
| }</code></pre> | |
| </section> | |
| <!-- Endpoints Section --> | |
| <section id="endpoints" class="section"> | |
| <h2>🔌 Endpoints API</h2> | |
| <div class="endpoint"> | |
| <span class="endpoint.method">POST</span> | |
| <span class="endpoint.path">/translate</span> | |
| <p>Traduit un texte d'une langue vers une autre.</p> | |
| <h4>Paramètres (JSON body)</h4> | |
| <table> | |
| <thead><tr><th>Paramètre</th><th>Type</th><th>Requis</th><th>Description</th></tr></thead> | |
| <tbody> | |
| <tr><td>text</td><td>string</td><td>Oui</td><td>Texte à traduire (max 5000 caractères)</td></tr> | |
| <tr><td>source</td><td>string</td><td>Non</td><td>Langue source (code ISO 639-1). Défaut: "auto" (détection auto)</td></tr> | |
| <tr><td>target</td><td>string</td><td>Non</td><td>Langue cible (code ISO 639-1). Défaut: "fr"</td></tr> | |
| </tbody> | |
| </table> | |
| <h4>Réponse</h4> | |
| <table> | |
| <thead><tr><th>Champ</th><th>Type</th><th>Description</th></tr></thead> | |
| <tbody> | |
| <tr><td>translatedText</td><td>string</td><td>Texte traduit</td></tr> | |
| <tr><td>source</td><td>string</td><td>Langue source détectée/utilisée</td></tr> | |
| <tr><td>target</td><td>string</td><td>Langue cible</td></tr> | |
| <tr><td>duration</td><td>number</td><td>Temps de traitement en ms</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div class="endpoint"> | |
| <span class="endpoint.method">GET</span> | |
| <span class="endpoint.path">/api/health</span> | |
| <p>Vérifie l'état du service.</p> | |
| </div> | |
| <div class="endpoint"> | |
| <span class="endpoint.method">GET</span> | |
| <span class="endpoint.path">/admin</span> | |
| <p>Panel d'administration (nécessite code d'accès via header <code>x-admin-code</code> ou paramètre <code>admin_code</code>).</p> | |
| </div> | |
| <h3>Codes de langue supportés (exemples)</h3> | |
| <table> | |
| <thead><tr><th>Code</th><th>Langue</th><th>Code</th><th>Langue</th></tr></thead> | |
| <tbody> | |
| <tr><td>fr</td><td>Français</td><td>en</td><td>Anglais</td></tr> | |
| <tr><td>es</td><td>Espagnol</td><td>de</td><td>Allemand</td></tr> | |
| <tr><td>it</td><td>Italien</td><td>pt</td><td>Portugais</td></tr> | |
| <tr><td>ru</td><td>Russe</td><td>zh</td><td>Chinois</td></tr> | |
| <tr><td>ja</td><td>Japonais</td><td>ko</td><td>Coréen</td></tr> | |
| <tr><td>ar</td><td>Arabe</td><td>auto</td><td>Détection auto</td></tr> | |
| </tbody> | |
| </table> | |
| </section> | |
| <!-- JavaScript Section --> | |
| <section id="javascript" class="section"> | |
| <h2>📜 JavaScript / TypeScript</h2> | |
| <h3>Fetch API (Navigateur / Node 18+)</h3> | |
| <div class="lang-tabs"> | |
| <button class="lang-tab active" onclick="showCode('js-fetch')">Fetch</button> | |
| <button class="lang-tab" onclick="showCode('js-axios')">Axios</button> | |
| <button class="lang-tab" onclick="showCode('js-node')">Node.js (natif)</button> | |
| </div> | |
| <pre class="code-block active" id="js-fetch"><code class="language-javascript">async function translate(text, source = 'auto', target = 'fr') { | |
| const response = await fetch('https://VOTRE_HOTE:7860/translate', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ text, source, target }) | |
| }); | |
| if (!response.ok) { | |
| const error = await response.json(); | |
| throw new Error(error.error || 'Erreur de traduction'); | |
| } | |
| return response.json(); | |
| } | |
| // Utilisation | |
| translate('Bonjour le monde', 'fr', 'en') | |
| .then(result => console.log(result.translatedText)) // "Hello world" | |
| .catch(err => console.error(err));</code></pre> | |
| <pre class="code-block" id="js-axios"><code class="language-javascript">const axios = require('axios'); | |
| async function translate(text, source = 'auto', target = 'fr') { | |
| try { | |
| const { data } = await axios.post('https://VOTRE_HOTE:7860/translate', { | |
| text, source, target | |
| }); | |
| return data; | |
| } catch (error) { | |
| throw new Error(error.response?.data?.error || error.message); | |
| } | |
| } | |
| // Utilisation | |
| translate('Hello world', 'en', 'fr') | |
| .then(r => console.log(r.translatedText)); // "Bonjour le monde"</code></pre> | |
| <pre class="code-block" id="js-node"><code class="language-javascript">const https = require('https'); | |
| function translate(text, source = 'auto', target = 'fr') { | |
| return new Promise((resolve, reject) => { | |
| const postData = JSON.stringify({ text, source, target }); | |
| const options = { | |
| hostname: 'VOTRE_HOTE', | |
| port: 7860, | |
| path: '/translate', | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Content-Length': Buffer.byteLength(postData) | |
| } | |
| }; | |
| const req = https.request(options, (res) => { | |
| let data = ''; | |
| res.on('data', chunk => data += chunk); | |
| res.on('end', () => { | |
| try { | |
| const result = JSON.parse(data); | |
| if (res.statusCode >= 400) reject(new Error(result.error)); | |
| else resolve(result); | |
| } catch (e) { | |
| reject(new Error('Réponse invalide')); | |
| } | |
| }); | |
| }); | |
| req.on('error', reject); | |
| req.write(postData); | |
| req.end(); | |
| }); | |
| }</code></pre> | |
| <h3>Classe réutilisable (ES6)</h3> | |
| <pre><code class="language-javascript">class TranslatorAPI { | |
| constructor(baseUrl = 'https://VOTRE_HOTE:7860') { | |
| this.baseUrl = baseUrl; | |
| } | |
| async translate(text, source = 'auto', target = 'fr') { | |
| const res = await fetch(`${this.baseUrl}/translate`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ text, source, target }) | |
| }); | |
| if (!res.ok) { | |
| const err = await res.json(); | |
| throw new Error(err.error); | |
| } | |
| return res.json(); | |
| } | |
| async detectLanguage(text) { | |
| const result = await this.translate(text, 'auto', 'en'); | |
| return result.source; | |
| } | |
| async translateBatch(texts, source = 'auto', target = 'fr') { | |
| // Paralléliser avec Promise.all (respecter rate limit 30/min) | |
| const results = await Promise.all( | |
| texts.map(t => this.translate(t, source, target)) | |
| ); | |
| return results.map(r => r.translatedText); | |
| } | |
| } | |
| // Usage | |
| const translator = new TranslatorAPI(); | |
| const result = await translator.translate('Bonjour', 'fr', 'es'); | |
| console.log(result.translatedText); // "Hola"</code></pre> | |
| </section> | |
| <!-- Python Section --> | |
| <section id="python" class="section"> | |
| <h2>🐍 Python</h2> | |
| <div class="lang-tabs"> | |
| <button class="lang-tab active" onclick="showCode('py-requests')">Requests</button> | |
| <button class="lang-tab" onclick="showCode('py-httpx')">HTTPX (async)</button> | |
| <button class="lang-tab" onclick="showCode('py-stdlib')">stdlib (urllib)</button> | |
| </div> | |
| <pre class="code-block active" id="py-requests"><code class="language-python">import requests | |
| def translate(text, source='auto', target='fr', base_url='https://VOTRE_HOTE:7860'): | |
| \"\"\"Traduit un texte via l'API Translator.\"\"\" | |
| response = requests.post( | |
| f'{base_url}/translate', | |
| json={'text': text, 'source': source, 'target': target}, | |
| timeout=30 | |
| ) | |
| response.raise_for_status() | |
| return response.json() | |
| # Utilisation simple | |
| result = translate('Bonjour le monde', 'fr', 'en') | |
| print(result['translatedText']) # "Hello world" | |
| # Avec gestion d'erreurs | |
| try: | |
| result = translate('Hola', 'es', 'fr') | |
| print(f"Traduit: {result['translatedText']} (durée: {result['duration']}ms)") | |
| except requests.HTTPError as e: | |
| if e.response.status_code == 429: | |
| print("Rate limit dépassé (30 req/min)") | |
| else: | |
| print(f"Erreur: {e.response.json().get('error')}")</code></pre> | |
| <pre class="code-block" id="py-httpx"><code class="language-python">import httpx | |
| import asyncio | |
| class TranslatorClient: | |
| def __init__(self, base_url='https://VOTRE_HOTE:7860'): | |
| self.base_url = base_url | |
| self.client = httpx.AsyncClient(timeout=30.0) | |
| async def translate(self, text, source='auto', target='fr'): | |
| response = await self.client.post( | |
| f'{self.base_url}/translate', | |
| json={'text': text, 'source': source, 'target': target} | |
| ) | |
| response.raise_for_status() | |
| return response.json() | |
| async def translate_batch(self, texts, source='auto', target='fr', concurrency=5): | |
| \"\"\"Traduit plusieurs textes avec limite de concurrence.\"\"\" | |
| semaphore = asyncio.Semaphore(concurrency) | |
| async def limited_translate(text): | |
| async with semaphore: | |
| return await self.translate(text, source, target) | |
| tasks = [limited_translate(t) for t in texts] | |
| results = await asyncio.gather(*tasks, return_exceptions=True) | |
| return [r['translatedText'] if not isinstance(r, Exception) else str(r) for r in results] | |
| async def close(self): | |
| await self.client.aclose() | |
| # Usage | |
| async def main(): | |
| client = TranslatorClient() | |
| try: | |
| result = await client.translate('Hello world', 'en', 'fr') | |
| print(result['translatedText']) | |
| # Batch | |
| texts = ['Hello', 'World', 'Python'] | |
| translations = await client.translate_batch(texts, 'en', 'fr') | |
| print(translations) | |
| finally: | |
| await client.close() | |
| asyncio.run(main())</code></pre> | |
| <pre class="code-block" id="py-stdlib"><code class="language-python">import urllib.request | |
| import json | |
| def translate(text, source='auto', target='fr', base_url='https://VOTRE_HOTE:7860'): | |
| data = json.dumps({'text': text, 'source': source, 'target': target}).encode('utf-8') | |
| req = urllib.request.Request( | |
| f'{base_url}/translate', | |
| data=data, | |
| headers={'Content-Type': 'application/json'}, | |
| method='POST' | |
| ) | |
| with urllib.request.urlopen(req, timeout=30) as response: | |
| result = json.loads(response.read().decode('utf-8')) | |
| if response.status >= 400: | |
| raise Exception(result.get('error', 'Erreur inconnue')) | |
| return result | |
| # Utilisation | |
| try: | |
| result = translate('Bonjour', 'fr', 'de') | |
| print(result['translatedText']) # "Hallo" | |
| except Exception as e: | |
| print(f"Erreur: {e}")</code></pre> | |
| </section> | |
| <!-- Java Section --> | |
| <section id="java" class="section"> | |
| <h2>☕ Java</h2> | |
| <div class="lang-tabs"> | |
| <button class="lang-tab active" onclick="showCode('java-httpclient')">HttpClient (Java 11+)</button> | |
| <button class="lang-tab" onclick="showCode('java-okhttp')">OkHttp</button> | |
| <button class="lang-tab" onclick="showCode('java-spring')">Spring RestTemplate</button> | |
| </div> | |
| <pre class="code-block active" id="java-httpclient"><code class="language-java">import java.net.URI; | |
| import java.net.http.HttpClient; | |
| import java.net.http.HttpRequest; | |
| import java.net.http.HttpResponse; | |
| import java.time.Duration; | |
| import com.google.gson.Gson; | |
| import com.google.gson.JsonObject; | |
| public class TranslatorClient { | |
| private final HttpClient client; | |
| private final String baseUrl; | |
| private final Gson gson = new Gson(); | |
| public TranslatorClient(String baseUrl) { | |
| this.baseUrl = baseUrl; | |
| this.client = HttpClient.newBuilder() | |
| .connectTimeout(Duration.ofSeconds(30)) | |
| .build(); | |
| } | |
| public TranslationResult translate(String text, String source, String target) | |
| throws Exception { | |
| JsonObject requestBody = new JsonObject(); | |
| requestBody.addProperty("text", text); | |
| requestBody.addProperty("source", source); | |
| requestBody.addProperty("target", target); | |
| HttpRequest request = HttpRequest.newBuilder() | |
| .uri(URI.create(baseUrl + "/translate")) | |
| .header("Content-Type", "application/json") | |
| .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(requestBody))) | |
| .build(); | |
| HttpResponse<String> response = client.send(request, | |
| HttpResponse.BodyHandlers.ofString()); | |
| if (response.statusCode() >= 400) { | |
| JsonObject error = gson.fromJson(response.body(), JsonObject.class); | |
| throw new RuntimeException(error.get("error").getAsString()); | |
| } | |
| return gson.fromJson(response.body(), TranslationResult.class); | |
| } | |
| public static class TranslationResult { | |
| public String translatedText; | |
| public String source; | |
| public String target; | |
| public long duration; | |
| } | |
| // Usage | |
| public static void main(String[] args) throws Exception { | |
| TranslatorClient translator = new TranslatorClient("https://VOTRE_HOTE:7860"); | |
| TranslationResult result = translator.translate("Bonjour", "fr", "en"); | |
| System.out.println(result.translatedText); // "Hello" | |
| } | |
| }</code></pre> | |
| <pre class="code-block" id="java-okhttp"><code class="language-java">// Dependencies: OkHttp + Gson | |
| // implementation "com.squareup.okhttp3:okhttp:4.12.0" | |
| // implementation "com.google.code.gson:gson:2.10.1" | |
| import com.google.gson.Gson; | |
| import com.google.gson.JsonObject; | |
| import okhttp3.*; | |
| public class TranslatorClient { | |
| private final OkHttpClient client; | |
| private final String baseUrl; | |
| private final Gson gson = new Gson(); | |
| public TranslatorClient(String baseUrl) { | |
| this.baseUrl = baseUrl; | |
| this.client = new OkHttpClient.Builder() | |
| .connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS) | |
| .readTimeout(30, java.util.concurrent.TimeUnit.SECONDS) | |
| .build(); | |
| } | |
| public TranslationResult translate(String text, String source, String target) | |
| throws Exception { | |
| JsonObject json = new JsonObject(); | |
| json.addProperty("text", text); | |
| json.addProperty("source", source); | |
| json.addProperty("target", target); | |
| RequestBody body = RequestBody.create( | |
| gson.toJson(json), | |
| MediaType.get("application/json; charset=utf-8") | |
| ); | |
| Request request = new Request.Builder() | |
| .url(baseUrl + "/translate") | |
| .post(body) | |
| .build(); | |
| try (Response response = client.newCall(request).execute()) { | |
| String responseBody = response.body().string(); | |
| if (!response.isSuccessful()) { | |
| JsonObject error = gson.fromJson(responseBody, JsonObject.class); | |
| throw new RuntimeException(error.get("error").getAsString()); | |
| } | |
| return gson.fromJson(responseBody, TranslationResult.class); | |
| } | |
| } | |
| public static class TranslationResult { | |
| public String translatedText; | |
| public String source; | |
| public String target; | |
| public long duration; | |
| } | |
| }</code></pre> | |
| <pre class="code-block" id="java-spring"><code class="language-java">// Spring Boot RestTemplate | |
| // implementation 'org.springframework.boot:spring-boot-starter-web' | |
| import org.springframework.web.client.RestTemplate; | |
| import org.springframework.http.*; | |
| import java.util.Map; | |
| public class TranslatorClient { | |
| private final RestTemplate restTemplate = new RestTemplate(); | |
| private final String baseUrl; | |
| public TranslatorClient(String baseUrl) { | |
| this.baseUrl = baseUrl; | |
| } | |
| public TranslationResult translate(String text, String source, String target) { | |
| HttpHeaders headers = new HttpHeaders(); | |
| headers.setContentType(MediaType.APPLICATION_JSON); | |
| Map<String, String> body = Map.of( | |
| "text", text, | |
| "source", source, | |
| "target", target | |
| ); | |
| HttpEntity<Map<String, String>> request = new HttpEntity<>(body, headers); | |
| try { | |
| ResponseEntity<TranslationResult> response = restTemplate.postForEntity( | |
| baseUrl + "/translate", request, TranslationResult.class); | |
| if (!response.getStatusCode().is2xxSuccessful()) { | |
| throw new RuntimeException("Erreur HTTP: " + response.getStatusCode()); | |
| } | |
| return response.getBody(); | |
| } catch (Exception e) { | |
| throw new RuntimeException("Erreur traduction: " + e.getMessage()); | |
| } | |
| } | |
| public static class TranslationResult { | |
| private String translatedText; | |
| private String source; | |
| private String target; | |
| private long duration; | |
| // Getters/Setters | |
| public String getTranslatedText() { return translatedText; } | |
| public void setTranslatedText(String translatedText) { this.translatedText = translatedText; } | |
| public String getSource() { return source; } | |
| public void setSource(String source) { this.source = source; } | |
| public String getTarget() { return target; } | |
| public void setTarget(String target) { this.target = target; } | |
| public long getDuration() { return duration; } | |
| public void setDuration(long duration) { this.duration = duration; } | |
| } | |
| }</code></pre> | |
| </section> | |
| <!-- cURL Section --> | |
| <section id="curl" class="section"> | |
| <h2>🌐 cURL / Ligne de commande</h2> | |
| <h3>Traduction simple</h3> | |
| <pre><code class="language-bash">curl -X POST https://VOTRE_HOTE:7860/translate \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"text": "Bonjour le monde", "source": "fr", "target": "en"}'</code></pre> | |
| <h3>Avec détection automatique</h3> | |
| <pre><code class="language-bash">curl -X POST https://VOTRE_HOTE:7860/translate \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"text": "Hello world", "target": "fr"}'</code></pre> | |
| <h3>Script bash réutilisable</h3> | |
| <pre><code class="language-bash">#!/bin/bash | |
| # translate.sh - Script de traduction en ligne de commande | |
| BASE_URL="https://VOTRE_HOTE:7860" | |
| SOURCE="auto" | |
| TARGET="fr" | |
| usage() { | |
| echo "Usage: $0 [-s source] [-t target] \"texte à traduire\"" | |
| echo " -s Langue source (défaut: auto)" | |
| echo " -t Langue cible (défaut: fr)" | |
| exit 1 | |
| } | |
| while getopts "s:t:h" opt; do | |
| case $opt in | |
| s) SOURCE="$OPTARG" ;; | |
| t) TARGET="$OPTARG" ;; | |
| h) usage ;; | |
| *) usage ;; | |
| esac | |
| done | |
| shift $((OPTIND-1)) | |
| TEXT="$*" | |
| if [ -z "$TEXT" ]; then | |
| usage | |
| fi | |
| response=$(curl -s -X POST "$BASE_URL/translate" \ | |
| -H "Content-Type: application/json" \ | |
| -d "$(jq -n --arg text "$TEXT" --arg src "$SOURCE" --arg tgt "$TARGET" \ | |
| '{text: $text, source: $src, target: $tgt}')") | |
| # Extraire le texte traduit (nécessite jq) | |
| translated=$(echo "$response" | jq -r '.translatedText // .error // "Erreur"') | |
| echo "$translated"</code></pre> | |
| <h3>Test de charge simple</h3> | |
| <pre><code class="language-bash"># Tester le rate limit (30 req/min) | |
| for i in {1..35}; do | |
| curl -s -X POST https://VOTRE_HOTE:7860/translate \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"text\": \"Test $i\", \"target\": \"en\"}" \ | |
| -w " HTTP %{http_code}\\n" \ | |
| -o /dev/null | |
| sleep 1 | |
| done</code></pre> | |
| </section> | |
| <!-- Limits Section --> | |
| <section id="limits" class="section"> | |
| <h2>⚙️ Limites et bonnes pratiques</h2> | |
| <div class="info-box"> | |
| <h4>📋 Limites actuelles</h4> | |
| <table> | |
| <thead><tr><th>Limite</th><th>Valeur</th><th>Description</th></tr></thead> | |
| <tbody> | |
| <tr><td>Rate limit</td><td>30 req/min/IP</td><td>Bloque avec HTTP 429 si dépassé</td></tr> | |
| <tr><td>Taille max texte</td><td>5000 caractères</td><td>Erreur 400 si dépassé</td></tr> | |
| <tr><td>Timeout</td><td>30 secondes</td><td>Côté serveur et client recommandé</td></tr> | |
| <tr><td>Langues</td><td>100+</td><td>Codes ISO 639-1</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <h3>Gestion du rate limit (HTTP 429)</h3> | |
| <pre><code class="language-javascript">async function translateWithRetry(text, source, target, retries = 3) { | |
| for (let attempt = 0; attempt <= retries; attempt++) { | |
| const response = await fetch('/translate', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ text, source, target }) | |
| }); | |
| if (response.status === 429) { | |
| const waitTime = Math.min(1000 * Math.pow(2, attempt), 30000); | |
| console.log(`Rate limit, attente ${waitTime}ms...`); | |
| await new Promise(r => setTimeout(r, waitTime)); | |
| continue; | |
| } | |
| if (!response.ok) { | |
| const err = await response.json(); | |
| throw new Error(err.error); | |
| } | |
| return response.json(); | |
| } | |
| throw new Error('Max retries exceeded'); | |
| }</code></pre> | |
| <h3>Bonnes pratiques</h3> | |
| <ul> | |
| <li><strong>Mettez en cache</strong> les traductions récurrentes côté client</li> | |
| <li><strong>Regroupez</strong> les requêtes (batch) quand possible</li> | |
| <li><strong>Respectez</strong> le rate limit : implémentez un backoff exponentiel</li> | |
| <li><strong>Surveillez</strong> vos logs via le panel admin <code>/admin</code></li> | |
| <li><strong>Utilisez</strong> la détection auto (<code>source: "auto"</code>) seulement si nécessaire</li> | |
| </ul> | |
| <h3>Codes d'erreur HTTP</h3> | |
| <table> | |
| <thead><tr><th>Code</th><th>Signification</th><th>Action</th></tr></thead> | |
| <tbody> | |
| <tr><td>200</td><td>Succès</td><td>-</td></tr> | |
| <tr><td>400</td><td>Requête invalide</td><td>Vérifiez le JSON et la taille du texte</td></tr> | |
| <tr><td>429</td><td>Trop de requêtes</td><td>Attendez et réessayez (backoff)</td></tr> | |
| <tr><td>500</td><td>Erreur serveur</td><td>Réessayez plus tard, contactez admin si persiste</td></tr> | |
| </tbody> | |
| </table> | |
| </section> | |
| </div> | |
| <script> | |
| function showSection(sectionId) { | |
| document.querySelectorAll('.section').forEach(s => s.classList.remove('active')); | |
| document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); | |
| document.getElementById(sectionId).classList.add('active'); | |
| document.querySelector(`[onclick="showSection('${sectionId}')"]`).classList.add('active'); | |
| } | |
| function showCode(codeId) { | |
| document.querySelectorAll('.code-block').forEach(c => c.classList.remove('active')); | |
| document.querySelectorAll('.lang-tab').forEach(t => t.classList.remove('active')); | |
| document.getElementById(codeId).classList.add('active'); | |
| event.target.classList.add('active'); | |
| } | |
| </script> | |
| </body> | |
| </html> |