first commit
This commit is contained in:
176
bundles/KupShop/I18nBundle/Util/TranslationEngine.php
Normal file
176
bundles/KupShop/I18nBundle/Util/TranslationEngine.php
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KupShop\I18nBundle\Util;
|
||||
|
||||
use DeepL\DeepLException;
|
||||
use DeepL\Translator;
|
||||
|
||||
class TranslationEngine
|
||||
{
|
||||
private const GOOGLE_CHUNK = 3900;
|
||||
private static string $googleApiKey = 'AIzaSyCERmGVBqiuzN74C9l-CEMA08KwIgSQY_Y';
|
||||
private static string $deepLAuthKey = '491e37c3-4dc9-ed7b-7188-536b0598f034';
|
||||
|
||||
public function getTranslation(string $text, string $sourceLanguage, string $language, bool $html = false): array
|
||||
{
|
||||
if (isFunctionalTests()) {
|
||||
$responseData['translatedText'] = $sourceLanguage.'->'.$language.': '.$text;
|
||||
|
||||
return $responseData;
|
||||
}
|
||||
|
||||
$deepl = (\Settings::getDefault()->use_deepl ?? 'N');
|
||||
|
||||
getLogger()->notice('TranslationEngine:translate', ['char_count' => mb_strlen($text), 'from' => $sourceLanguage, 'to' => $language, 'deepl' => $deepl]);
|
||||
|
||||
if ($deepl == 'Y') {
|
||||
return $this->getTranslationWithDeepL($text, $sourceLanguage, $language, $html);
|
||||
}
|
||||
|
||||
return $this->getTranslationWithGoogle($text, $sourceLanguage, $language);
|
||||
}
|
||||
|
||||
public function getTranslationMulti(array $textMulti, string $sourceLanguage, string $language): array
|
||||
{
|
||||
foreach ($textMulti as $key => $text) {
|
||||
if (empty($text)) {
|
||||
continue;
|
||||
}
|
||||
$translation = $this->getTranslation($text, $sourceLanguage, $language);
|
||||
if (empty($translation['error'])) {
|
||||
$textMulti[$key] = $translation['translatedText'];
|
||||
} else {
|
||||
unset($textMulti[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $textMulti;
|
||||
}
|
||||
|
||||
private function getTranslationWithGoogle(string $text, string $sourceLanguage, string $language): array
|
||||
{
|
||||
// Google Translate nezná de-AT nebo de-CH, zná jen de-DE
|
||||
// potřebuju to teda, aby se použila němčina - jinak to nefunguje vubec
|
||||
$langAlias = [
|
||||
'at' => 'de',
|
||||
'ch' => 'de',
|
||||
];
|
||||
|
||||
foreach ([&$sourceLanguage, &$language] as &$lang) {
|
||||
if (isset($langAlias[$lang])) {
|
||||
$lang = $langAlias[$lang];
|
||||
}
|
||||
}
|
||||
|
||||
$baseUrl = 'https://www.googleapis.com/language/translate/v2?key='.self::$googleApiKey; // .'&q='.rawurlencode($text).'&source='.$sourceLanguage.'&target='.$language
|
||||
|
||||
$textChunks = $this->splitTextIntoChunks($text);
|
||||
$translatedChunks = [];
|
||||
|
||||
foreach ($textChunks as $chunk) {
|
||||
$url = $baseUrl.'&q='.rawurlencode($chunk).'&source='.$sourceLanguage.'&target='.$language;
|
||||
|
||||
$handle = curl_init($url);
|
||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
|
||||
$response = curl_exec($handle);
|
||||
$responseData = json_decode($response ?: '{}', true);
|
||||
|
||||
if (isset($responseData['error']) || curl_error($handle)) {
|
||||
curl_close($handle);
|
||||
|
||||
return [
|
||||
'error' => curl_error($handle) ?: ($responseData['error'] ?? 'Unknown error'),
|
||||
'translatedText' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$translatedChunks[] = html_entity_decode($responseData['data']['translations'][0]['translatedText'] ?? '', ENT_QUOTES);
|
||||
curl_close($handle);
|
||||
}
|
||||
|
||||
// Combine all translated chunks into a single result
|
||||
$translatedText = implode(' ', $translatedChunks);
|
||||
|
||||
// Replace malformed sequences
|
||||
$translatedText = str_replace(
|
||||
['> , ', '> .'],
|
||||
['>, ', '>.'],
|
||||
$translatedText
|
||||
);
|
||||
|
||||
return [
|
||||
'error' => '',
|
||||
'translatedText' => $translatedText,
|
||||
];
|
||||
}
|
||||
|
||||
private function getTranslationWithDeepL(string $text, string $sourceLanguage, string $language, bool $html = false): array
|
||||
{
|
||||
$responseData = ['translatedText' => ''];
|
||||
|
||||
try {
|
||||
$translator = new Translator(self::$deepLAuthKey);
|
||||
|
||||
$language = $this->deeplLanguageMapping($language);
|
||||
$sourceLanguage = $this->deeplLanguageMapping($sourceLanguage, true);
|
||||
|
||||
$options = [];
|
||||
|
||||
if ($html) {
|
||||
$options['tag_handling'] = 'html';
|
||||
$options['non_splitting_tags'] = ['strong', 'em', 'b', 'i'];
|
||||
}
|
||||
|
||||
$result = $translator->translateText($text, $sourceLanguage, $language, $options);
|
||||
if (!empty($result->text)) {
|
||||
$responseData['translatedText'] = $result->text;
|
||||
}
|
||||
} catch (DeepLException $exception) {
|
||||
$responseData['error'] = $exception->getMessage();
|
||||
}
|
||||
|
||||
return $responseData;
|
||||
}
|
||||
|
||||
protected function deeplLanguageMapping(string $language, bool $source = false): string
|
||||
{
|
||||
switch ($language) {
|
||||
case 'en':
|
||||
// if source language is 'en' => keep it 'en' otherwise use 'en-US'
|
||||
return $source ? 'en' : 'en-US';
|
||||
case 'at':
|
||||
return 'de';
|
||||
}
|
||||
|
||||
return $language;
|
||||
}
|
||||
|
||||
protected function splitTextIntoChunks(string $text): array
|
||||
{
|
||||
$chunks = [];
|
||||
$currentChunk = '';
|
||||
|
||||
// Split text into words to preserve boundaries
|
||||
$words = preg_split('/(\s+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
foreach ($words as $word) {
|
||||
// Add word to the current chunk if it fits
|
||||
if (mb_strlen($currentChunk.$word) <= self::GOOGLE_CHUNK) {
|
||||
$currentChunk .= $word;
|
||||
} else {
|
||||
// Save the current chunk and start a new one
|
||||
$chunks[] = trim($currentChunk);
|
||||
$currentChunk = $word;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the remaining chunk
|
||||
if (!empty($currentChunk)) {
|
||||
$chunks[] = trim($currentChunk);
|
||||
}
|
||||
|
||||
return $chunks;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user