204 lines
8.9 KiB
PHP
204 lines
8.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KupShop\I18nBundle\Util\AutomaticImport;
|
|
|
|
use KupShop\I18nBundle\Translations\ParametersListTranslation;
|
|
use KupShop\I18nBundle\Translations\ParametersTranslation;
|
|
use KupShop\I18nBundle\Translations\ProductsTranslation;
|
|
use KupShop\I18nBundle\Util\TranslationEngine;
|
|
use KupShop\I18nBundle\Util\TranslationLocator;
|
|
use KupShop\KupShopBundle\Context\LanguageContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
|
use Query\Operator;
|
|
|
|
class AutoTranslateUtil
|
|
{
|
|
private const AUTO_TRANSLATE_KEY = 'AUTO_TRANSLATE';
|
|
|
|
private array $translationsCache = [];
|
|
|
|
public function __construct(
|
|
private readonly TranslationEngine $translationEngine,
|
|
private readonly TranslationLocator $translationLocator,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Fce ktera preklada zakladni data produktu.
|
|
*
|
|
* Vezme `$product` a projde pole, ktere jsou prekladatelne a pokud je potreba, tak je prelozi.
|
|
*/
|
|
public function preprocessProductData(array $config, array $product): array
|
|
{
|
|
// ulozim si puvodni produkt, abych ho pripadne mohl pouzit pokud bych potreboval
|
|
$product['original'] = $product;
|
|
|
|
// nactu si vsechny fieldy, ktere jsou pro produkt prekladatelne
|
|
$translatableColumns = $this->translationLocator->getTranslation(ProductsTranslation::class)->getColumns();
|
|
$translatableData = array_filter($product, fn ($k) => in_array($k, array_keys($translatableColumns)), ARRAY_FILTER_USE_KEY);
|
|
|
|
// vsechno si prelozim do vychoziho jazyka - v tomhle jazuce totiz chci ukladat produkt do DB
|
|
$result = $this->translate($translatableData, $config['source'], $this->getDefaultLanguageId());
|
|
|
|
// prerazim data produktu vychozim jazykem
|
|
foreach ($result as $field => $value) {
|
|
$product[$field] = $value;
|
|
}
|
|
|
|
// a ted jdu nagenerovat preklady (ulozi se mi to do prekladovy tabulky)
|
|
foreach ($config['to'] as $toLanguage) {
|
|
if ($toLanguage === $this->getDefaultLanguageId()) {
|
|
continue;
|
|
}
|
|
|
|
// do vsech poli, kde chci spustit automaticky preklad, si ulozim AUTO_TRANSLATE hodnotu, kdy na konci aut. importu vsechny tyhle radky projdu a prelozim
|
|
// automaticky preklad se provede az pozde pomoci `processAutoTranslate` metody
|
|
$product['translations'][$toLanguage]['products'] = $toLanguage === $config['source'] ? $translatableData : array_map(fn ($x) => self::AUTO_TRANSLATE_KEY, $translatableData);
|
|
}
|
|
|
|
return $product;
|
|
}
|
|
|
|
/**
|
|
* Fce ktera preklada nazev parametru a hodnotu parametru.
|
|
*
|
|
* Bohuzel to musi byt oddelene od `preprocessProductData`, protoze zalozeni parametru a jeho hodnoty se vola uz
|
|
* behem parsovani dat, takze by mi to jinak zalozilo parametry a hodnoty v tom jazyce, ve kterym feed je.
|
|
*
|
|
* Tohle se zavola v ramci parsovani feedu a pripravi to data parametru. Provede se preklad parametru a hodnoty do vychoziho
|
|
* jazyka a zaroven se nageneruji zaznamy do prekladove tabulky.
|
|
*/
|
|
public function preprocessParameterData(array $config, string $parameterName, string $parameterValue, array &$product): array
|
|
{
|
|
// provedu preklad parametru a hodnoty do vychoziho jazyka
|
|
$result = $this->translate(['name' => $parameterName, 'value' => $parameterValue], $config['source'], $this->getDefaultLanguageId());
|
|
|
|
foreach ($config['to'] as $toLanguage) {
|
|
if ($toLanguage === $this->getDefaultLanguageId()) {
|
|
continue;
|
|
}
|
|
|
|
// generuju zaznamy pro prekladove tabulky parametru
|
|
// pokud jeste neexistuje definice pro dany nazev parametru, tak si ji vytvorim at pak muzu pridavat jen hodnoty
|
|
if (!isset($product['translations'][$toLanguage]['parameters'][$result['name']])) {
|
|
$product['translations'][$toLanguage]['parameters'][$result['name']] = [
|
|
'name' => $toLanguage === $config['source'] ? $parameterName : self::AUTO_TRANSLATE_KEY,
|
|
'values' => [],
|
|
];
|
|
}
|
|
|
|
// pokud jeste neexistuje dana hodnota, tak ji pridam do `values` s AUTO_TRANSLATE, kdy preklad se provede az pozdeji pomoci `processAutoTranslate`
|
|
if (!isset($product['translations'][$toLanguage]['parameters'][$result['name']]['values'][$result['value']])) {
|
|
$product['translations'][$toLanguage]['parameters'][$result['name']]['values'][$result['value']] = $toLanguage === $config['source'] ? $parameterValue : self::AUTO_TRANSLATE_KEY;
|
|
}
|
|
}
|
|
|
|
// vracim parametr a hodnotu ve vychozim jazyce
|
|
return [$result['name'], $result['value']];
|
|
}
|
|
|
|
public function processAutoTranslate(): void
|
|
{
|
|
$this->processAutoTranslateForTranslation(ProductsTranslation::class);
|
|
$this->processAutoTranslateForTranslation(ParametersTranslation::class);
|
|
$this->processAutoTranslateForTranslation(ParametersListTranslation::class);
|
|
}
|
|
|
|
/**
|
|
* Fce ktera automaticky prelozi zaznamy s `AUTO_TRANSLATE_KEY` v prekladove tabulce.
|
|
*/
|
|
protected function processAutoTranslateForTranslation(string $translationClass): void
|
|
{
|
|
$translation = $this->translationLocator->getTranslation($translationClass);
|
|
|
|
// query builder pro nacteni prekladovych zaznamu
|
|
$qb = sqlQueryBuilder()
|
|
->select('o.id, t.id_language')
|
|
->from($translation->getTableName(), 'o')
|
|
->join('o', $translation->getTranslationTableName(), 't', "o.id = t.{$translation->getForeignKeyColumn()}")
|
|
->groupBy('o.id, t.id_language');
|
|
|
|
$orX = [];
|
|
// zaselectuju si vsechny prekladove sloupce
|
|
foreach ($translation->getColumns() as $column => $_) {
|
|
// potrebuju sloupec od originalniho objektu (ve vychozim jazyce)
|
|
// a potrebuju sloupec z prekladu
|
|
$qb->addSelect("o.{$column}, t.{$column} as 'translated/{$column}'");
|
|
// zaroven chci selectovat jen takove radky, ktere obsahuji `AUTO_TRANSLATE_KEY`
|
|
$orX[] = Operator::equals(["t.{$column}" => self::AUTO_TRANSLATE_KEY]);
|
|
}
|
|
|
|
$qb->andWhere(Operator::orX($orX));
|
|
|
|
foreach ($qb->execute() as $item) {
|
|
// z radku s datama si zafiltruju vsechny pole, ktere chci prekladat (maji hodnotu AUTO_TRANSLATE_KEY)
|
|
// vznikne mi pole [['field' => null], ...]
|
|
$translatableColumns = Mapping::mapKeys(array_filter($item, fn ($x) => $x === self::AUTO_TRANSLATE_KEY), fn ($k, $v) => [explode('/', $k)[1], null]);
|
|
|
|
// ted uz chci provest preklad
|
|
$translated = $this->translate(
|
|
// tady jeste z $item vezmu vsechny original pole podle prekladatelnych poli v $translatableColumns
|
|
array_filter($item, fn ($k) => in_array($k, array_keys($translatableColumns)), ARRAY_FILTER_USE_KEY),
|
|
$this->getDefaultLanguageId(),
|
|
$item['id_language']
|
|
);
|
|
|
|
// ulozim preklad a mam hotovo
|
|
$translation->saveSingleObject($item['id_language'], $item['id'], $translated);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fce pro provedeni prekladu hodnot.
|
|
*
|
|
* Generuje si i lokalni cache, aby se pro stejnou hodnotu nemusel volat preklad pres API vicekrat.
|
|
*/
|
|
private function translate(array $data, string $source, string $language): array
|
|
{
|
|
$translated = [];
|
|
|
|
$langKey = "{$source}-{$language}";
|
|
|
|
foreach ($data as $key => $value) {
|
|
// prazdnou hodnotu nedava smysl prekladat a nejak s ni pracovat
|
|
if (empty($value)) {
|
|
$translated[$key] = null;
|
|
continue;
|
|
}
|
|
|
|
// pokud je zdrojovy jazyk stejny jako jazyk do ktereho chci prekladat, tak nic neprekladam
|
|
if ($source === $language) {
|
|
$translated[$key] = $value;
|
|
continue;
|
|
}
|
|
|
|
// pokud mam preklad uz v lokalni cache, tak ho pouziju
|
|
if (!empty($this->translationsCache[$langKey][$value])) {
|
|
$translated[$key] = $this->translationsCache[$langKey][$value];
|
|
continue;
|
|
}
|
|
|
|
// ciselnou hodnotu nema smysl prekladat
|
|
if (is_numeric($value)) {
|
|
$this->translationsCache[$langKey][$value] = $value;
|
|
} else {
|
|
// zavolam preklad hodnoty a ulozim si ho do lokalni cache abych stejnou hodnotu neprekladal v jednom behu aut. importu vicekrat
|
|
$this->translationsCache[$langKey][$value] = $this->translationEngine->getTranslation($value, $source, $language)['translatedText'] ?? $value;
|
|
}
|
|
|
|
// do $translated ulozim prelozenou hodnotu
|
|
$translated[$key] = $this->translationsCache[$langKey][$value];
|
|
}
|
|
|
|
return $translated;
|
|
}
|
|
|
|
private function getDefaultLanguageId(): string
|
|
{
|
|
return Contexts::get(LanguageContext::class)->getDefaultId();
|
|
}
|
|
}
|