dbXapp 2.0
RAD, CMS, Module und Runtime-IDE fuer dbXapp
Loading...
Searching...
No Matches
dbxContentTranslate.class.php
Go to the documentation of this file.
1<?php
2namespace dbx\dbxContent;
3
5
6 private static array $warnings = array();
7 private static array $logged = array();
8
9 public static function provider(): string {
10 $provider = strtolower(trim((string) dbx()->get_config('dbxContent', 'lng_translate_provider', 'copy')));
11 if ($provider === '' || $provider === 'undef') {
12 return 'copy';
13 }
14 return $provider;
15 }
16
17 public static function clearWarnings(): void {
18 self::$warnings = array();
19 self::$logged = array();
20 }
21
22 public static function warnings(): array {
23 return self::$warnings;
24 }
25
26 public static function translate(string $text, string $from, string $to, string $context = 'content'): string {
27 $text = trim($text);
28 if ($text === '' || $from === $to) {
29 return $text;
30 }
31
32 $provider = self::provider();
33 switch ($provider) {
34 case 'none':
35 return '';
36 case 'copy':
37 return $text;
38 case 'custom':
39 return self::translateCustom($text, $from, $to, $context);
40 case 'deepl':
41 return self::translateDeepL($text, $from, $to, $context);
42 case 'openai':
43 return self::translateOpenAi($text, $from, $to, $context);
44 default:
45 self::addWarning($provider, 'Unbekannter Provider, Text wird kopiert.', $context, $to);
46 return $text;
47 }
48 }
49
50 private static function addWarning(string $provider, string $message, string $context = '', string $lng = ''): void {
51 $provider = strtolower(trim($provider));
52 $message = trim($message);
53 if ($message === '') {
54 return;
55 }
56
57 $key = $provider . '|' . $message;
58 if (isset(self::$logged[$key])) {
59 return;
60 }
61 self::$logged[$key] = 1;
62 self::$warnings[] = array(
63 'provider' => $provider,
64 'message' => $message,
65 'context' => $context,
66 'lng' => $lng,
67 );
68
69 if (function_exists('dbx') && is_object(dbx())) {
70 dbx()->sys_msg(
71 'warning',
72 'dbxContentTranslate',
73 $lng !== '' ? $lng : $context,
74 $message,
75 $provider
76 );
77 }
78 }
79
80 private static function translateCustom(string $text, string $from, string $to, string $context): string {
81 $file = dbx()->os_path(dbx()->get_base_dir() . 'dbx/modules/dbxContent/translate.php');
82 if (!is_file($file)) {
83 self::addWarning('custom', 'translate.php fehlt, Text wird kopiert.', $context, $to);
84 return $text;
85 }
86
87 $result = $text;
88 include $file;
89 if (!is_string($result) || trim($result) === '') {
90 self::addWarning('custom', 'Custom-Translate lieferte kein Ergebnis.', $context, $to);
91 return $text;
92 }
93 return $result;
94 }
95
96 private static function translateDeepL(string $text, string $from, string $to, string $context): string {
97 $key = trim((string) dbx()->get_config('dbxContent', 'lng_translate_api_key', ''));
98 if ($key === '') {
99 self::addWarning('deepl', 'API-Key fehlt (lng_translate_api_key), Text wird kopiert.', $context, $to);
100 return $text;
101 }
102
103 $url = trim((string) dbx()->get_config('dbxContent', 'lng_translate_api_url', ''));
104 if ($url === '') {
105 $url = 'https://api-free.deepl.com/v2/translate';
106 }
107
108 $payload = http_build_query(array(
109 'auth_key' => $key,
110 'text' => $text,
111 'source_lang' => strtoupper($from),
112 'target_lang' => strtoupper($to),
113 ));
114
115 $response = self::httpPost($url, $payload, array('Content-Type: application/x-www-form-urlencoded'), false, 'deepl', $context, $to);
116 if (!is_array($response)) {
117 return $text;
118 }
119
120 if (isset($response['message']) && is_string($response['message'])) {
121 self::addWarning('deepl', $response['message'], $context, $to);
122 return $text;
123 }
124
125 $trans = $response['translations'][0]['text'] ?? '';
126 if (!is_string($trans) || trim($trans) === '') {
127 self::addWarning('deepl', 'DeepL-Antwort ohne Text, Original wird verwendet.', $context, $to);
128 return $text;
129 }
130 return $trans;
131 }
132
133 private static function translateOpenAi(string $text, string $from, string $to, string $context): string {
134 $key = trim((string) dbx()->get_config('dbxContent', 'lng_translate_api_key', ''));
135 if ($key === '') {
136 self::addWarning('openai', 'API-Key fehlt (lng_translate_api_key), Text wird kopiert.', $context, $to);
137 return $text;
138 }
139
140 $url = trim((string) dbx()->get_config('dbxContent', 'lng_translate_api_url', ''));
141 if ($url === '') {
142 $url = 'https://api.openai.com/v1/chat/completions';
143 }
144
145 $model = trim((string) dbx()->get_config('dbxContent', 'lng_translate_model', 'gpt-4o-mini'));
146 if ($model === '') {
147 $model = 'gpt-4o-mini';
148 }
149
150 $prompt = 'Translate the following ' . $context . ' from ' . $from . ' to ' . $to
151 . '. Preserve HTML tags and [modul=...][/modul] markers exactly. Return only the translation.';
152
153 $body = json_encode(array(
154 'model' => $model,
155 'messages' => array(
156 array('role' => 'system', 'content' => $prompt),
157 array('role' => 'user', 'content' => $text),
158 ),
159 'temperature' => 0.2,
160 ), JSON_UNESCAPED_UNICODE);
161
162 $response = self::httpPost($url, $body, array(
163 'Content-Type: application/json',
164 'Authorization: Bearer ' . $key,
165 ), true, 'openai', $context, $to);
166
167 if (!is_array($response)) {
168 return $text;
169 }
170
171 if (isset($response['error']['message']) && is_string($response['error']['message'])) {
172 self::addWarning('openai', $response['error']['message'], $context, $to);
173 return $text;
174 }
175
176 $trans = $response['choices'][0]['message']['content'] ?? '';
177 if (!is_string($trans) || trim($trans) === '') {
178 self::addWarning('openai', 'OpenAI-Antwort ohne Text, Original wird verwendet.', $context, $to);
179 return $text;
180 }
181 return trim($trans);
182 }
183
184 private static function httpPost(string $url, string $body, array $headers, bool $json = false, string $provider = '', string $context = '', string $lng = '') {
185 if (!function_exists('curl_init')) {
186 self::addWarning($provider !== '' ? $provider : 'translate', 'curl nicht verfuegbar, Text wird kopiert.', $context, $lng);
187 return null;
188 }
189
190 $ch = curl_init($url);
191 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
192 curl_setopt($ch, CURLOPT_POST, true);
193 curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
194 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
195 curl_setopt($ch, CURLOPT_TIMEOUT, 30);
196
197 $raw = curl_exec($ch);
198 $errno = curl_errno($ch);
199 $error = curl_error($ch);
200 $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
201 curl_close($ch);
202
203 if ($errno !== 0) {
204 self::addWarning($provider !== '' ? $provider : 'translate', 'HTTP-Fehler: ' . $error, $context, $lng);
205 return null;
206 }
207 if ($code >= 400) {
208 self::addWarning($provider !== '' ? $provider : 'translate', 'HTTP ' . $code . ' von API', $context, $lng);
209 }
210 if (!is_string($raw) || $raw === '') {
211 self::addWarning($provider !== '' ? $provider : 'translate', 'Leere API-Antwort', $context, $lng);
212 return null;
213 }
214
215 $decoded = json_decode($raw, true);
216 if (!is_array($decoded)) {
217 self::addWarning($provider !== '' ? $provider : 'translate', 'Ungueltige JSON-Antwort', $context, $lng);
218 return null;
219 }
220 return $decoded;
221 }
222}
static translate(string $text, string $from, string $to, string $context='content')
$response
Definition index.php:518
DBX schema administration.
if(! $db->connect_db_server($server)) $result