Skip to content

Commit 36b8c00

Browse files
committed
[TASK] Respect rte allow tags in translate request
With this change the RTE configuration is determined from the field to be translated. To give deepl a list of allowed HTML tags. To allow a correct separation in the translated text with HTML tag.
1 parent ffbcc23 commit 36b8c00

File tree

8 files changed

+277
-8
lines changed

8 files changed

+277
-8
lines changed

Classes/Client.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Psr\Http\Message\ResponseInterface;
88
use TYPO3\CMS\Core\Http\RequestFactory;
99
use TYPO3\CMS\Core\Utility\GeneralUtility;
10+
use WebVision\WvDeepltranslate\Domain\Dto\TranslateOptions;
1011
use WebVision\WvDeepltranslate\Exception\ClientNotValidUrlException;
1112

1213
final class Client
@@ -31,21 +32,29 @@ public function __construct()
3132
$this->requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
3233
}
3334

34-
public function translate(string $content, string $sourceLang, string $targetLang, string $glossary = ''): ResponseInterface
35-
{
35+
public function translate(
36+
string $content,
37+
string $sourceLang,
38+
string $targetLang,
39+
string $glossary = '',
40+
?TranslateOptions $configuration = null
41+
): ResponseInterface {
3642
$baseUrl = $this->buildBaseUrl('translate');
3743

3844
$postFields = [
3945
'text' => $content,
4046
'source_lang' => $sourceLang,
4147
'target_lang' => $targetLang,
42-
'tag_handling' => 'xml',
4348
];
4449

4550
if (!empty($glossary)) {
4651
$postFields['glossary_id'] = $glossary;
4752
}
4853

54+
if ($configuration instanceof TranslateOptions) {
55+
$postFields = array_merge($postFields, $configuration->toArray());
56+
}
57+
4958
$postFields['formality'] = $this->configuration->getFormality();
5059

5160
return $this->requestFactory->request($baseUrl, 'POST', $this->mergeRequiredRequestOptions([
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WebVision\WvDeepltranslate\Domain\Dto;
6+
7+
final class TranslateOptions
8+
{
9+
protected string $splitSentences = 'nonewlines';
10+
11+
protected bool $outlineDetection = false;
12+
13+
protected array $splittingTags = [];
14+
15+
protected array $nonSplittingTags = [];
16+
17+
protected array $ignoreTags = [];
18+
19+
protected string $tagHandling = 'xml';
20+
21+
public function getSplitSentences(): string
22+
{
23+
return $this->splitSentences;
24+
}
25+
26+
public function setSplitSentences(string $splitSentences): void
27+
{
28+
$this->splitSentences = $splitSentences;
29+
}
30+
31+
public function isOutlineDetection(): bool
32+
{
33+
return $this->outlineDetection;
34+
}
35+
36+
public function setOutlineDetection(bool $outlineDetection): void
37+
{
38+
$this->outlineDetection = $outlineDetection;
39+
}
40+
41+
public function getSplittingTags(): array
42+
{
43+
return $this->splittingTags;
44+
}
45+
46+
public function setSplittingTags(array $splittingTags): void
47+
{
48+
$this->splittingTags = $splittingTags;
49+
}
50+
51+
public function getNonSplittingTags(): array
52+
{
53+
return $this->nonSplittingTags;
54+
}
55+
56+
public function setNonSplittingTags(array $nonSplittingTags): void
57+
{
58+
$this->nonSplittingTags = $nonSplittingTags;
59+
}
60+
61+
public function getIgnoreTags(): array
62+
{
63+
return $this->ignoreTags;
64+
}
65+
66+
public function setIgnoreTags(array $ignoreTags): void
67+
{
68+
$this->ignoreTags = $ignoreTags;
69+
}
70+
71+
public function getTagHandling(): string
72+
{
73+
return $this->tagHandling;
74+
}
75+
76+
public function setTagHandling(string $tagHandling): void
77+
{
78+
$this->tagHandling = $tagHandling;
79+
}
80+
81+
public function toArray(): array
82+
{
83+
$param = [];
84+
$param['tag_handling'] = $this->tagHandling;
85+
86+
if (!empty($this->splittingTags)) {
87+
$param['outlineDetection'] = $this->outlineDetection;
88+
$param['split_sentences'] = $this->splitSentences;
89+
$param['splitting_tags'] = $this->splittingTags;
90+
}
91+
92+
return $param;
93+
}
94+
}

Classes/Hooks/TranslateHook.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
use TYPO3\CMS\Core\Messaging\FlashMessageService;
1010
use TYPO3\CMS\Core\Utility\GeneralUtility;
1111
use TYPO3\CMS\Extbase\Object\ObjectManager;
12+
use WebVision\WvDeepltranslate\Domain\Dto\TranslateOptions;
1213
use WebVision\WvDeepltranslate\Domain\Repository\PageRepository;
1314
use WebVision\WvDeepltranslate\Domain\Repository\SettingsRepository;
1415
use WebVision\WvDeepltranslate\Exception\LanguageIsoCodeNotFoundException;
1516
use WebVision\WvDeepltranslate\Exception\LanguageRecordNotFoundException;
17+
use WebVision\WvDeepltranslate\Resolver\RichtextAllowTagsResolver;
1618
use WebVision\WvDeepltranslate\Service\DeeplService;
1719
use WebVision\WvDeepltranslate\Service\GoogleTranslateService;
1820
use WebVision\WvDeepltranslate\Service\LanguageService;
@@ -62,12 +64,7 @@ public function processTranslateTo_copyAction(string &$content, array $languageR
6264
break;
6365
}
6466

65-
if (!isset($cmdmap['localization']['custom']['srcLanguageId'])) {
66-
$cmdmap['localization']['custom']['srcLanguageId'] = '';
67-
}
68-
6967
$customMode = $cmdmap['localization']['custom']['mode'] ?? null;
70-
[$sourceLanguage,] = explode('-', (string)$cmdmap['localization']['custom']['srcLanguageId']);
7168

7269
//translation mode set to deepl or google translate
7370
if ($customMode === null) {
@@ -79,6 +76,10 @@ public function processTranslateTo_copyAction(string &$content, array $languageR
7976
$translatedContent = '';
8077
$targetLanguageRecord = [];
8178

79+
$translateConfiguration = GeneralUtility::makeInstance(TranslateOptions::class);
80+
$richtextAllowTagsResolver = GeneralUtility::makeInstance(RichtextAllowTagsResolver::class);
81+
$translateConfiguration->setSplittingTags($richtextAllowTagsResolver->resolve($tableName, $currentRecordId));
82+
8283
try {
8384
$sourceLanguageRecord = $this->languageService->getSourceLanguage(
8485
$siteInformation['site']
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WebVision\WvDeepltranslate\Resolver;
6+
7+
use TYPO3\CMS\Backend\Utility\BackendUtility;
8+
use TYPO3\CMS\Core\Configuration\Richtext;
9+
use TYPO3\CMS\Core\Utility\GeneralUtility;
10+
11+
class RichtextAllowTagsResolver
12+
{
13+
private Richtext $richtext;
14+
15+
public function __construct()
16+
{
17+
$this->richtext = GeneralUtility::makeInstance(Richtext::class);
18+
}
19+
20+
public function resolve(string $tableName, int $recordId): array
21+
{
22+
if (!isset($GLOBALS['TCA'][$tableName]['columns'])) {
23+
throw new \RuntimeException('TCA Columns ist not defined', 1689950520561);
24+
}
25+
26+
$columns = $GLOBALS['TCA'][$tableName]['columns'];
27+
28+
$rteTextFields = [];
29+
foreach ($columns as $fieldName => $config) {
30+
if (!isset($config['config']['type'])) {
31+
continue;
32+
}
33+
34+
if ($config['config']['type'] !== 'text') {
35+
continue;
36+
}
37+
38+
if (!isset($config['config']['enableRichtext'])) {
39+
if ($config['config']['enableRichtext'] === false) {
40+
continue;
41+
}
42+
} elseif (!isset($GLOBALS['TCA'][$tableName]['types']['columnsOverrides'][$fieldName]['config']['enableRichtext'])) {
43+
if ($GLOBALS['TCA'][$tableName]['types']['columnsOverrides'][$fieldName]['config']['enableRichtext'] === false) {
44+
continue;
45+
}
46+
}
47+
48+
$rteTextFields[$fieldName] = $config['config'];
49+
}
50+
51+
if (empty($rteTextFields)) {
52+
return [];
53+
}
54+
55+
$record = BackendUtility::getRecord($tableName, $recordId);
56+
57+
$allowTags = [];
58+
foreach ($rteTextFields as $fieldName => $config) {
59+
$rteConfig = $this->richtext->getConfiguration($tableName, $fieldName, $record['pid'], $record['CType'], $config);
60+
if (isset($rteConfig['processing']['allowTags'])) {
61+
$allowTags = array_unique(array_merge($allowTags, $rteConfig['processing']['allowTags']), SORT_REGULAR);
62+
}
63+
}
64+
65+
return $allowTags;
66+
}
67+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<dataset>
3+
<pages>
4+
<uid>1</uid>
5+
<pid>0</pid>
6+
<title>DeepL-Functional-Tests</title>
7+
<doktype>1</doktype>
8+
<is_siteroot>1</is_siteroot>
9+
</pages>
10+
<pages>
11+
<uid>3</uid>
12+
<pid>0</pid>
13+
<title>DeepL-Functional-Tests BS</title>
14+
<doktype>1</doktype>
15+
<is_siteroot>1</is_siteroot>
16+
</pages>
17+
<tt_content>
18+
<uid>1</uid>
19+
<pid>1</pid>
20+
<header>DeepL-Functional-Test Element</header>
21+
<CType>text</CType>
22+
<bodytext></bodytext>
23+
</tt_content>
24+
</dataset>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Functional\Resolver;
6+
7+
use Nimut\TestingFramework\TestCase\FunctionalTestCase;
8+
use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
9+
use TYPO3\CMS\Core\Utility\GeneralUtility;
10+
use WebVision\WvDeepltranslate\Resolver\RichtextAllowTagsResolver;
11+
12+
class RichtextAllowTagsResolverTest extends FunctionalTestCase
13+
{
14+
/**
15+
* @var string[]
16+
*/
17+
protected $testExtensionsToLoad = [
18+
'typo3conf/ext/wv_deepltranslate',
19+
];
20+
21+
/**
22+
* @var string[]
23+
*/
24+
protected $coreExtensionsToLoad = [
25+
'rte_ckeditor',
26+
];
27+
28+
protected function setUp(): void
29+
{
30+
parent::setUp();
31+
32+
$this->importDataSet(__DIR__ . '/Fixtures/Pages.xml');
33+
}
34+
35+
/**
36+
* @test
37+
*/
38+
public function findRteConfigurationAllowTagsByRecords(): void
39+
{
40+
$richtextAllowTagsResolver = GeneralUtility::makeInstance(RichtextAllowTagsResolver::class);
41+
$allowTags = $richtextAllowTagsResolver->resolve('tt_content', 1);
42+
43+
static::assertTrue((bool)array_search('em', $allowTags));
44+
45+
$yamlLoader = GeneralUtility::makeInstance(YamlFileLoader::class);
46+
$config = $yamlLoader->load('EXT:rte_ckeditor/Configuration/RTE/Processing.yaml');
47+
48+
static::assertSame($config['processing']['allowTags'], $allowTags);
49+
}
50+
}

Tests/Functional/Services/DeeplServiceTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,29 @@ public function translateContentFromDeToEn(): void
5353
static::assertSame('I would like to be translated!', $responseObject['translations'][0]['text']);
5454
}
5555

56+
/**
57+
* @test
58+
*/
59+
public function translateContentAndRespectHtmlTags(): void
60+
{
61+
if (defined('DEEPL_MOCKSERVER_USED') && DEEPL_MOCKSERVER_USED === true) {
62+
static::markTestSkipped(__METHOD__ . ' skipped, because DEEPL MOCKSERVER do not support EN as TARGET language.');
63+
}
64+
65+
$deeplService = GeneralUtility::makeInstance(DeeplService::class);
66+
67+
$responseObject = $deeplService->translateRequest(
68+
'<p>Important species in blueberry include the Western flower thrips (<em>Frankliniella occidentalis</em>) and Chilli thrips (<em>Scirtothrips dorsalis</em>).</p>',
69+
'DE',
70+
'EN'
71+
);
72+
73+
static::assertSame(
74+
'<p>Zu den für Heidelbeeren wichtigen Arten gehören der Westliche Blütenthrips (<em>Frankliniella occidentalis</em>) und der Chili-Thrips (<em>Scirtothrips dorsalis</em>).</p>',
75+
$responseObject['translations'][0]['text']
76+
);
77+
}
78+
5679
/**
5780
* @test
5881
*/

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"typo3/cms-frontend": "^9.5 || ^10.4 || ^11.5",
9292
"typo3/cms-info": "^9.5 || ^10.4 || ^11.5",
9393
"typo3/cms-lowlevel": "^9.5 || ^10.4 || ^11.5",
94+
"typo3/cms-rte-ckeditor": "^9.5 || ^10.4 || ^11.5",
9495
"typo3/cms-tstemplate": "^9.5 || ^10.4 || ^11.5",
9596
"typo3/cms-workspaces": "^9.5 || ^10.4 || ^11.5",
9697
"typo3/coding-standards": "^0.5"

0 commit comments

Comments
 (0)