115 lines
4.1 KiB
PHP
115 lines
4.1 KiB
PHP
<?php
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
function translate_response(int $status, array $body): never {
|
|
http_response_code($status);
|
|
echo json_encode($body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
function clean_translate_text(string $value, int $maxLength = 4000): string {
|
|
$value = strip_tags($value);
|
|
$value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $value) ?? '';
|
|
$value = trim($value);
|
|
if (strlen($value) > $maxLength) {
|
|
$value = substr($value, 0, $maxLength);
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
translate_response(405, ['error' => 'Method not allowed']);
|
|
}
|
|
|
|
if ((int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > 131072) {
|
|
translate_response(413, ['error' => 'Request too large']);
|
|
}
|
|
|
|
$body = json_decode(file_get_contents('php://input'), true);
|
|
if (!is_array($body) || !isset($body['targetLang'], $body['texts']) || !is_array($body['texts'])) {
|
|
translate_response(400, ['error' => 'Missing target language or texts']);
|
|
}
|
|
|
|
$targetLang = preg_replace('/[^a-z]/', '', strtolower((string) $body['targetLang']));
|
|
if (strlen($targetLang) < 2 || strlen($targetLang) > 5) {
|
|
translate_response(400, ['error' => 'Invalid target language']);
|
|
}
|
|
|
|
$targetName = clean_translate_text((string) ($body['targetName'] ?? strtoupper($targetLang)), 80);
|
|
$texts = [];
|
|
$totalLength = 0;
|
|
foreach ($body['texts'] as $text) {
|
|
if (!is_string($text) && !is_numeric($text)) {
|
|
continue;
|
|
}
|
|
$cleanText = clean_translate_text((string) $text);
|
|
$totalLength += strlen($cleanText);
|
|
$texts[] = $cleanText;
|
|
}
|
|
|
|
if (!$texts || count($texts) > 240 || $totalLength > 80000) {
|
|
translate_response(400, ['error' => 'Invalid translation batch']);
|
|
}
|
|
|
|
$prompt = "Translate each item to {$targetName}.\n"
|
|
. "Return ONLY a valid JSON array of translated strings in the same order, no explanations, no markdown.\n"
|
|
. "Example input: [\"Hello\", \"How are you\"]\n"
|
|
. "Example output: [\"Bonjour\", \"Comment allez-vous\"]\n\n"
|
|
. 'Input: ' . json_encode($texts, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
|
|
|
$ollamaHost = rtrim(getenv('OLLAMA_HOST') ?: 'http://192.168.100.68:11434', '/');
|
|
$ollamaModel = getenv('OLLAMA_MODEL') ?: 'llama3.2:3b';
|
|
$ollamaPayload = json_encode([
|
|
'model' => $ollamaModel,
|
|
'prompt' => $prompt,
|
|
'stream' => false,
|
|
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
|
|
|
if ($ollamaPayload === false) {
|
|
translate_response(500, ['error' => 'Could not encode Ollama request']);
|
|
}
|
|
|
|
$curl = curl_init($ollamaHost . '/api/generate');
|
|
curl_setopt_array($curl, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
|
CURLOPT_POSTFIELDS => $ollamaPayload,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_CONNECTTIMEOUT => 5,
|
|
CURLOPT_TIMEOUT => 130,
|
|
]);
|
|
|
|
$rawResponse = curl_exec($curl);
|
|
$curlError = curl_error($curl);
|
|
$statusCode = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
|
|
curl_close($curl);
|
|
|
|
if ($rawResponse === false || $statusCode < 200 || $statusCode >= 300) {
|
|
translate_response(502, ['error' => 'Ollama request failed', 'status' => $statusCode, 'detail' => $curlError]);
|
|
}
|
|
|
|
$ollamaResponse = json_decode($rawResponse, true);
|
|
if (!is_array($ollamaResponse) || !isset($ollamaResponse['response']) || !is_string($ollamaResponse['response'])) {
|
|
translate_response(502, ['error' => 'Invalid Ollama response']);
|
|
}
|
|
|
|
$translationJson = trim(str_replace(['```json', '```'], '', $ollamaResponse['response']));
|
|
$translations = json_decode($translationJson, true);
|
|
if (!is_array($translations) || count($translations) !== count($texts)) {
|
|
translate_response(502, ['error' => 'Unexpected translation response']);
|
|
}
|
|
|
|
$cleanTranslations = [];
|
|
foreach ($translations as $translation) {
|
|
if (!is_string($translation) && !is_numeric($translation)) {
|
|
translate_response(502, ['error' => 'Unexpected translation item']);
|
|
}
|
|
$cleanTranslations[] = clean_translate_text((string) $translation);
|
|
}
|
|
|
|
echo json_encode([
|
|
'translations' => $cleanTranslations,
|
|
'model' => $ollamaModel,
|
|
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|