Translator-API / public /wiki.html
NathMen12's picture
Upload 18 files
d5d016f verified
Raw
History Blame Contribute Delete
30.1 kB
<!DOCTYPE html>
<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>