Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions Classes/Controller/CkEditorController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace WebVision\DeeplWrite\Controller;

use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextFactory;
use TYPO3\CMS\Fluid\View\StandaloneView;
use WebVision\DeeplWrite\Configuration\ConfigurationInterface;
use WebVision\DeeplWrite\Domain\Enum\RephraseToneDeepL;
use WebVision\DeeplWrite\Domain\Enum\RephraseWritingStyleDeepL;
use WebVision\DeeplWrite\Service\DeeplService;
use WebVision\DeeplWrite\Service\HtmlParser;

/**
* @internal
* This class is meant to be used within the DeepL write extension and therefore
* no public API. Endpoints can change without further information.
*/
final class CkEditorController
{
public function __construct(
private readonly ResponseFactoryInterface $responseFactory,
private readonly ConfigurationInterface $configuration,
private readonly DeeplService $deeplService,
private readonly HtmlParser $htmlParser,
) {
}

public function deeplConfiguredAction(ServerRequestInterface $request): ResponseInterface
{
$configured = true;
if ($this->configuration->getApiKey() === '') {
$configured = false;
}
$response = $this->responseFactory->createResponse()
->withHeader('Content-Type', 'application/json; charset=utf-8');
$response->getBody()->write(
json_encode(['configured' => $configured], JSON_THROW_ON_ERROR),
);
return $response;
}

public function optimizeTextAction(ServerRequestInterface $request): ResponseInterface
{
$data = $request->getParsedBody();
$splittedText = $this->htmlParser->splitHtml($data['text']);
foreach ($splittedText as $node => $text) {
$optimizedText = $this->deeplService->rephraseText(
$data['text'],
null,
RephraseWritingStyleDeepL::tryFrom($data['style']),
RephraseToneDeepL::tryFrom($data['tone'])
);
$splittedText[$node] = $optimizedText;
}
$response = $this->responseFactory->createResponse()
->withHeader('Content-Type', 'application/json; charset=utf-8');
$response->getBody()->write(
json_encode(['result' => $this->htmlParser->buildHtml($splittedText)], JSON_THROW_ON_ERROR),
);
return $response;
}

public function getEditMaskAction(ServerRequestInterface $request): ResponseInterface
{
$renderingContext = GeneralUtility::makeInstance(RenderingContextFactory::class)->create(
templatePathsArray: [
'templateRootPaths' => ['EXT:deepl_write/Resources/Private/Backend/Templates/'],
]
);
$renderingContext->setRequest($request);
$renderingContext->setControllerAction('Edit');
$renderingContext->setControllerName('CkEditor');
$view = GeneralUtility::makeInstance(StandaloneView::class, $renderingContext);
$view->assignMultiple([
'styles' => RephraseWritingStyleDeepL::cases(),
'tones' => RephraseToneDeepL::cases(),
]);
$response = $this->responseFactory->createResponse()
->withHeader('Content-Type', 'text/html; charset=utf-8');
$response->getBody()->write($view->render());
return $response;
}
}
23 changes: 23 additions & 0 deletions Classes/Hooks/PageRendererHook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace WebVision\DeeplWrite\Hooks;

use TYPO3\CMS\Core\Page\PageRenderer;

final class PageRendererHook
{
/**
* Ensure backend javascript module is required and loaded.
*
* @param array<string, mixed> $params
*/
public function renderPreProcess(array $params, PageRenderer $pageRenderer): void
{
if ($pageRenderer->getApplicationType() === 'BE') {
// For some reason, the labels are not available in JavaScript object `TYPO3.lang`. So we add them manually.
$pageRenderer->addInlineLanguageLabelFile('EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf');
}
}
}
20 changes: 16 additions & 4 deletions Classes/Service/DeeplService.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function __construct(
*/
public function rephraseText(
string $text,
string $targetLanguage,
?string $targetLanguage = null,
?RephraseWritingStyleDeepL $writingStyle = null,
?RephraseToneDeepL $tone = null
): string {
Expand All @@ -47,10 +47,22 @@ public function rephraseText(
1741344565
);
}
if ($writingStyle instanceof RephraseWritingStyleDeepL && RephraseSupportedDeepLLanguage::isWritingStyleSupported($targetLanguage)) {
if (
$writingStyle instanceof RephraseWritingStyleDeepL
&& (
$targetLanguage === null ||
RephraseSupportedDeepLLanguage::isWritingStyleSupported($targetLanguage)
)
) {
$options[RephraseTextOptions::WRITING_STYLE] = $writingStyle->value;
}
if ($tone instanceof RephraseToneDeepL && RephraseSupportedDeepLLanguage::isToneSupportedByLanguage($targetLanguage)) {
if (
$tone instanceof RephraseToneDeepL
&& (
$targetLanguage === null ||
RephraseSupportedDeepLLanguage::isToneSupportedByLanguage($targetLanguage)
)
) {
$options[RephraseTextOptions::TONE] = $tone->value;
}

Expand All @@ -72,7 +84,7 @@ public function rephraseText(
*/
private function optimizeText(
string $text,
string $targetLanguage,
?string $targetLanguage = null,
array $options = []
): string {
$rephrased = $this->deeplClient->rephraseText(
Expand Down
21 changes: 21 additions & 0 deletions Configuration/Backend/AjaxRoutes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use WebVision\DeeplWrite\Controller\CkEditorController;

return [
'deeplwrite_ckeditor_configuration' => [
'path' => '/deepl-write/ckeditor/configuration',
'target' => CkEditorController::class . '::deeplConfiguredAction',
'methods' => ['GET'],
],
'deeplwrite_ckeditor_optimize' => [
'path' => '/deepl-write/ckeditor/optimize',
'target' => CkEditorController::class . '::optimizeTextAction',
'methods' => ['POST'],
],
'deeplwrite_ckeditor_edit' => [
'path' => '/deepl-write/ckeditor/edit',
'target' => CkEditorController::class . '::getEditMaskAction',
'methods' => ['GET'],
],
];
11 changes: 11 additions & 0 deletions Configuration/JavaScriptModules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

return [
'dependencies' => ['backend'],
'tags' => [
'backend.form',
],
'imports' => [
'@web-vision/deepl-write/deeplwrite-plugin.js' => 'EXT:deepl_write/Resources/Public/JavaScript/Ckeditor/deeplwrite-plugin.js',
],
];
14 changes: 14 additions & 0 deletions Configuration/RTE/DeeplWritePreset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
editor:
config:
importModules:
- { module: '@web-vision/deepl-write/deeplwrite-plugin.js', exports: [ 'Deeplwrite' ] }
toolbar:
items:
- bold
- italic
- '|'
- clipboard
- undo
- redo
- '|'
- deeplwrite
3 changes: 3 additions & 0 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ services:
WebVision\DeeplWrite\Hooks\WriteHook:
public: true

WebVision\DeeplWrite\Controller\CkEditorController:
public: true

WebVision\DeeplWrite\Configuration\ConfigurationInterface:
class: WebVision\DeeplWrite\Configuration\Configuration

Expand Down
83 changes: 83 additions & 0 deletions Resources/Private/Backend/Templates/CkEditor/Edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:core="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers"
xmlns:deeplWrite="http://typo3.org/ns/WebVision/DeeplWrite/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<div class="col-md-12">
<div class="row">
<p>
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.description"/>
</p>
</div>
<div role="tabpanel">
<ul class="nav nav-tabs form-section-tabs t3js-tabs" role="tablist" id="tabs-DPLWRITE-format">
<li role="presentation" class="t3js-tabmenu-item nav-item">
<button
class="nav-link active"
data-bs-toggle="tab"
data-bs-target="#DPLWRITE-format-1"
aria-controls="#DPLWRITE-format-1"
role="tab"
aria-selected="true"
>
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.heading.style"/>
</button>
</li>
<li role="presentation" class="t3js-tabmenu-item nav-item">
<button
class="nav-link"
data-bs-toggle="tab"
data-bs-target="#DPLWRITE-format-2"
aria-controls="#DPLWRITE-format-2"
role="tab"
>
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.heading.tone"/>
</button>
</li>
</ul>
</div>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="DPLWRITE-format-1">
<h2 class="visually-hidden">
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.heading.style"/>
</h2>
<fieldset class="form-section">
<p>
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.description.style"/>
</p>
<f:for each="{styles}" as="style" iteration="i">
<input type="radio" value="{style.value}" name="format" class="style btn-check" id="style-{i.index}">
<label class="btn btn-default col-md-3 col-xs-4" for="style-{i.index}">{style.name}</label>
</f:for>
</fieldset>
</div>
<div role="tabpanel" class="tab-pane" id="DPLWRITE-format-2">
<h2 class="visually-hidden">
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.heading.tone"/>
</h2>
<fieldset class="form-section">
<p>
<f:translate key="LLL:EXT:deepl_write/Resources/Private/Language/locallang_cke.xlf:cke.modal.description.tone"/>
</p>
<f:for each="{tones}" as="tone" iteration="i">
<input type="radio" value="{tone.value}" name="format" class="tone btn-check" id="tone-{i.index}">
<label class="btn btn-default col-md-3 col-xs-4" for="tone-{i.index}">{tone.name}</label>
</f:for>
</fieldset>
</div>
</div>
<fieldset class="form-section">
<div class="row">
<div class="col-md-6 col-xs-12">
<label for="original" class="form-label t3js-formengine-label">Original</label>
<textarea rows="20" class="form-control t3js-formengine-textarea formengine-textarea" id="original"></textarea>
</div>
<div class="col-md-6 col-xs-12">
<label for="optimized" class="form-label t3js-formengine-label">Optimized</label>
<textarea rows="20" class="form-control t3js-formengine-textarea formengine-textarea" id="optimized"></textarea>
</div>
</div>
</fieldset>
</div>
</html>
42 changes: 42 additions & 0 deletions Resources/Private/Language/locallang_cke.xlf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
<file
t3:id="1750181628"
source-language="en"
datatype="plaintext"
original="messages"
date="2025-06-17T19:34:32Z"
product-name="deepl_write"
>
<header/>
<body>
<trans-unit id="cke.button.title">
<source>DeepL Write</source>
</trans-unit>
<trans-unit id="cke.modal.title">
<source>Optimize text with DeepL Write</source>
</trans-unit>
<trans-unit id="cke.modal.heading.style">
<source>Style</source>
</trans-unit>
<trans-unit id="cke.modal.heading.tone">
<source>Tone</source>
</trans-unit>
<trans-unit id="cke.modal.description.style">
<source>Select a style to rewrite your text in a way that fits your audience and goals.</source>
</trans-unit>
<trans-unit id="cke.modal.description.tone">
<source>Select the desired tone for your text</source>
</trans-unit>
<trans-unit id="cke.modal.description">
<source>You can optimize your text by using the DeepL Write API. For some languages, you can choose the style or tone. Only one or the other is possible.</source>
</trans-unit>
<trans-unit id="cke.modal.button.optimize">
<source>Optimize text</source>
</trans-unit>
<trans-unit id="cke.modal.button.save">
<source>Apply changes</source>
</trans-unit>
</body>
</file>
</xliff>
Loading