Compare commits
No commits in common. "844264fb6254526abc2ce331cb4b31c225dc9d9d" and "4dc6060fb346066a3e4a3b2dbbae446f0aa1e3f2" have entirely different histories.
844264fb62
...
4dc6060fb3
@ -11,7 +11,6 @@ final readonly class StoreUpdate extends Dto
|
|||||||
private string $title,
|
private string $title,
|
||||||
private string $description,
|
private string $description,
|
||||||
private Storages $storages,
|
private Storages $storages,
|
||||||
private bool $isTranslateAutomatically = false,
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public function getTitle(): string
|
public function getTitle(): string
|
||||||
@ -28,9 +27,4 @@ final readonly class StoreUpdate extends Dto
|
|||||||
{
|
{
|
||||||
return $this->storages;
|
return $this->storages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isTranslateAutomatically(): bool
|
|
||||||
{
|
|
||||||
return $this->isTranslateAutomatically;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
|||||||
'title' => ['required', 'string', 'max:255',],
|
'title' => ['required', 'string', 'max:255',],
|
||||||
'description' => ['nullable', 'string',],
|
'description' => ['nullable', 'string',],
|
||||||
'storage.content_images.*.file' => ['numeric', 'min:1'],
|
'storage.content_images.*.file' => ['numeric', 'min:1'],
|
||||||
'translate-automatically' => ['nullable', 'boolean'],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +35,6 @@ final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
|||||||
title: $this->input('title'),
|
title: $this->input('title'),
|
||||||
description: $this->input('description'),
|
description: $this->input('description'),
|
||||||
storages: $this->storages(),
|
storages: $this->storages(),
|
||||||
isTranslateAutomatically: (bool) $this->input('translate-automatically', false),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Jobs\Translate;
|
|
||||||
|
|
||||||
use App\Services\Translate\Project\ProjectContentService;
|
|
||||||
use Exception;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
|
||||||
use Illuminate\Foundation\Queue\Queueable;
|
|
||||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
|
||||||
|
|
||||||
final class ProcessProjectContent implements ShouldQueue, ShouldBeEncrypted
|
|
||||||
{
|
|
||||||
use Dispatchable, Queueable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the middleware the job should pass through.
|
|
||||||
*
|
|
||||||
* @return array<int, object>
|
|
||||||
*/
|
|
||||||
public function middleware(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
(new WithoutOverlapping($this->uniqueId()))->expireAfter(180),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new job instance.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
private readonly int $projectId,
|
|
||||||
private readonly array $contentIds = [],
|
|
||||||
private readonly array $exceptLanguages = [],
|
|
||||||
) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the unique ID for the job.
|
|
||||||
*/
|
|
||||||
public function uniqueId(): string
|
|
||||||
{
|
|
||||||
return 'project-' . $this->projectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the job.
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function handle(ProjectContentService $projectContentService): void
|
|
||||||
{
|
|
||||||
$result = $projectContentService->translate($this->projectId, $this->contentIds, $this->exceptLanguages);
|
|
||||||
if ($result->isError() && $result->getCode() !== 404) {
|
|
||||||
cache()->lock($this->uniqueId())->forceRelease();
|
|
||||||
throw new Exception($result->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,6 +17,5 @@ final class ProjectContent extends Model implements StorageContract
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'title',
|
'title',
|
||||||
'description',
|
'description',
|
||||||
'language_id',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ namespace App\Services\Admin\Project;
|
|||||||
use App\Contracts\ServiceResultError;
|
use App\Contracts\ServiceResultError;
|
||||||
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
||||||
use App\Enums\Morph;
|
use App\Enums\Morph;
|
||||||
use App\Jobs\Translate\ProcessProjectContent;
|
|
||||||
use App\Models\ProjectContent;
|
use App\Models\ProjectContent;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Repositories\ProjectContentRepository;
|
use App\Repositories\ProjectContentRepository;
|
||||||
@ -70,7 +69,6 @@ final class AboutService extends Service
|
|||||||
'project' => $project,
|
'project' => $project,
|
||||||
'language' => $language,
|
'language' => $language,
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'serviceTranslationEnable' => config('translation_service.enable', false),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,9 +105,6 @@ final class AboutService extends Service
|
|||||||
$this->storageService->saveAndDelete($aboutProject, $storages);
|
$this->storageService->saveAndDelete($aboutProject, $storages);
|
||||||
return $aboutProject;
|
return $aboutProject;
|
||||||
});
|
});
|
||||||
if (\config('translation_service.enable', false)) {
|
|
||||||
$this->translateContent($aboutProject, $data);
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@ -138,9 +133,6 @@ final class AboutService extends Service
|
|||||||
|
|
||||||
return $aboutProject;
|
return $aboutProject;
|
||||||
});
|
});
|
||||||
if (\config('translation_service.enable', false)) {
|
|
||||||
$this->translateContent($aboutProject, $data);
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
report($e);
|
report($e);
|
||||||
return $this->errService(__('Server Error'));
|
return $this->errService(__('Server Error'));
|
||||||
@ -156,15 +148,4 @@ final class AboutService extends Service
|
|||||||
'description' => $data->getDescription(),
|
'description' => $data->getDescription(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function translateContent(ProjectContent $projectContent, StoreUpdate $data): void
|
|
||||||
{
|
|
||||||
if (! $data->isTranslateAutomatically()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$translateExceptLanguages = [$projectContent->language_id];
|
|
||||||
$translateContentIds = [$projectContent->id];
|
|
||||||
|
|
||||||
ProcessProjectContent::dispatch($projectContent->project_id, $translateContentIds, $translateExceptLanguages);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ use App\Services\ProjectTranslationServiceHash\CompletionChecker;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use KorElf\TranslateLaravel\Contracts\TranslationCompletedListener;
|
use KorElf\TranslateLaravel\Contracts\TranslationCompletedListener;
|
||||||
|
|
||||||
final readonly class DocumentationCategoryContentService implements TranslationCompletedListener
|
final class DocumentationCategoryContentService implements TranslationCompletedListener
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private DocumentationCategoryRepository $categoryRepository,
|
private DocumentationCategoryRepository $categoryRepository,
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Services\Translate\Completed\Project;
|
|
||||||
|
|
||||||
use App\Dto\Service\ProjectTranslationServiceHash\Hashes;
|
|
||||||
use App\Exceptions\Services\Translate\CompletedException;
|
|
||||||
use App\Jobs\Translate\ProcessProjectContent;
|
|
||||||
use App\Models\Project;
|
|
||||||
use App\Models\ProjectContent;
|
|
||||||
use App\Repositories\ProjectRepository;
|
|
||||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
|
||||||
use App\Services\ProjectTranslationServiceHash\CompletionChecker;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use KorElf\TranslateLaravel\Contracts\TranslationCompletedListener;
|
|
||||||
|
|
||||||
final readonly class ProjectContentService implements TranslationCompletedListener
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private ProjectRepository $projectRepository,
|
|
||||||
private NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
|
||||||
private CompletionChecker $completionChecker,
|
|
||||||
) { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws CompletedException
|
|
||||||
*/
|
|
||||||
public function onTranslationCompleted(array $translatedText, array $data = []): void
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
!isset($data['projectId'])
|
|
||||||
|| !isset($data['languageId'])
|
|
||||||
|| !isset($data['hashes'])
|
|
||||||
) {
|
|
||||||
throw new CompletedException('Required data is missing: projectId, languageId, or hashes.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$project = $this->projectRepository->getProjectById((int) $data['projectId']);
|
|
||||||
if ($project === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$projectContent = DB::transaction(function () use ($data, $project, $translatedText) {
|
|
||||||
$values = [];
|
|
||||||
$hashes = $this->completionChecker->execute(
|
|
||||||
$this->getHashes($data['hashes']),
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($translatedText as $translatedTextKey => $translatedTextValue) {
|
|
||||||
if ($hashes->isStatusWaiting($translatedTextKey) !== true) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$translatedTextValue = $this->noTranslateAttributeHandler->handleRemoveAttribute($translatedTextValue);
|
|
||||||
$values[$translatedTextKey] = $translatedTextValue;
|
|
||||||
}
|
|
||||||
if (\count($values) === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$projectContent = $project->contents()->where('language_id', $data['languageId'])->first();
|
|
||||||
if ($projectContent !== null) {
|
|
||||||
$projectContent->update($values);
|
|
||||||
return $projectContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
$values['language_id'] = $data['languageId'];
|
|
||||||
return $project->contents()->create($values);
|
|
||||||
});
|
|
||||||
if (\config('translation_service.enable', false) && $projectContent !== null) {
|
|
||||||
$this->translateContent($project, $projectContent, $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function translateContent(Project $project, ProjectContent $projectContent, array $data): void
|
|
||||||
{
|
|
||||||
$translateExceptLanguages = $data['exceptLanguages'] ?? [];
|
|
||||||
$translateExceptLanguages[] = $data['languageId'];
|
|
||||||
ProcessProjectContent::dispatch($project->id, [$projectContent->id], $translateExceptLanguages);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getHashes(array $hashes): Hashes
|
|
||||||
{
|
|
||||||
$hashesDto = new Hashes();
|
|
||||||
foreach ($hashes as $field => $hash) {
|
|
||||||
$hashesDto->add((int) $hash['hashId'], $field, $hash['hash']);
|
|
||||||
}
|
|
||||||
return $hashesDto;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Services\Translate\Project;
|
|
||||||
|
|
||||||
use App\Dto\Service\ProjectTranslationServiceHash\Fields;
|
|
||||||
use App\Dto\Service\ProjectTranslationServiceHash\TranslateFields;
|
|
||||||
use App\Enums\Morph;
|
|
||||||
use App\Repositories\ProjectRepository;
|
|
||||||
use App\Repositories\ProjectTranslationServiceRepository;
|
|
||||||
use App\ServiceResults\ServiceResultError;
|
|
||||||
use App\ServiceResults\ServiceResultSuccess;
|
|
||||||
use App\Services\ProjectTranslationService\NoTranslateAttributeHandler;
|
|
||||||
use App\Services\ProjectTranslationServiceHash\HashTrackerCommand;
|
|
||||||
use App\Services\Service;
|
|
||||||
use App\Services\Translate\Completed\Project\ProjectContentService as ProjectContentServiceCompleted;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use KorElf\TranslateLaravel\DTO\AfterTranslateDto;
|
|
||||||
use KorElf\TranslateLaravel\Facades\Translate;
|
|
||||||
|
|
||||||
final class ProjectContentService extends Service
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private readonly ProjectRepository $projectRepository,
|
|
||||||
private readonly ProjectTranslationServiceRepository $projectTranslationServiceRepository,
|
|
||||||
private readonly HashTrackerCommand $hashTrackerCommand,
|
|
||||||
private readonly NoTranslateAttributeHandler $noTranslateAttributeHandler,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function translate(int $projectId, array $contentIds, array $exceptLanguages): ServiceResultSuccess | ServiceResultError
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$project = $this->projectRepository->getProjectById($projectId);
|
|
||||||
if ($project === null) {
|
|
||||||
return $this->errNotFound(__('Project not found'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$contents = $project->contents()
|
|
||||||
->whereIn('id', $contentIds)
|
|
||||||
->cursor();
|
|
||||||
|
|
||||||
$sourceLanguagesCode = [];
|
|
||||||
foreach ($contents as $content) {
|
|
||||||
if (!isset($sourceLanguagesCode[$content->language_id])) {
|
|
||||||
$sourceLanguagesCode[$content->language_id] = $this->projectTranslationServiceRepository->getLanguageCodeByLanguageId($content->language_id) ?? 'null';
|
|
||||||
}
|
|
||||||
$translateIntoLanguage = $this->projectTranslationServiceRepository
|
|
||||||
->getLanguagesBySourceLanguage($content->language_id, $exceptLanguages)
|
|
||||||
->all()->pluck('code', 'language_id')->toArray();
|
|
||||||
|
|
||||||
if (empty($translateIntoLanguage)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fields = (new Fields())
|
|
||||||
->add('title', $content->title ?? '')
|
|
||||||
->add('description', $content->description ?? '');
|
|
||||||
$translations = DB::transaction(function () use ($content, $translateIntoLanguage, $fields) {
|
|
||||||
return $this->hashTrackerCommand->execute(Morph::ProjectContent, $content->id, \array_keys($translateIntoLanguage), $fields);
|
|
||||||
});
|
|
||||||
/** @var TranslateFields $translations */
|
|
||||||
unset($fields);
|
|
||||||
|
|
||||||
foreach ($translations->getFields() as $languageId => $fields) {
|
|
||||||
$params = new \KorElf\TranslateLaravel\DTO\RunTranslateDto();
|
|
||||||
$sourceLanguageCode = null;
|
|
||||||
if (isset($sourceLanguagesCode[$content->language_id]) && $sourceLanguagesCode[$content->language_id] !== 'null') {
|
|
||||||
$sourceLanguageCode = $sourceLanguagesCode[$content->language_id];
|
|
||||||
}
|
|
||||||
foreach ($fields as $field) {
|
|
||||||
$text = $this->noTranslateAttributeHandler->handleAddAttribute((string) $content->{$field} ?? '');
|
|
||||||
$params->addParamHtml($field, $text, $translateIntoLanguage[$languageId], $sourceLanguageCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
$afterTranslateDto = new AfterTranslateDto(ProjectContentServiceCompleted::class, [
|
|
||||||
'projectId' => $projectId,
|
|
||||||
'languageId' => $languageId,
|
|
||||||
'hashes' => $translations->getHashesByLanguage($languageId),
|
|
||||||
'exceptLanguages' => $exceptLanguages,
|
|
||||||
]);
|
|
||||||
Translate::runJob($params, $afterTranslateDto);
|
|
||||||
unset($params, $afterTranslateDto);
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($translations, $translateIntoLanguage);
|
|
||||||
}
|
|
||||||
unset($contents, $documentation);
|
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
\report($e);
|
|
||||||
return $this->errService($e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->ok();
|
|
||||||
}
|
|
||||||
}
|
|
@ -310,7 +310,6 @@ return [
|
|||||||
'content_images' => 'content images',
|
'content_images' => 'content images',
|
||||||
'source_language_id' => 'source language identifier',
|
'source_language_id' => 'source language identifier',
|
||||||
'translate_from_language' => 'translate from language',
|
'translate_from_language' => 'translate from language',
|
||||||
'translate-automatically' => 'translate automatically',
|
|
||||||
'translate-automatically.*' => 'translate automatically',
|
'translate-automatically.*' => 'translate automatically',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -310,7 +310,6 @@ return [
|
|||||||
'content_images' => 'изображения контента',
|
'content_images' => 'изображения контента',
|
||||||
'source_language_id' => 'идентификатор исходного языка',
|
'source_language_id' => 'идентификатор исходного языка',
|
||||||
'translate_from_language' => 'перевести с языка',
|
'translate_from_language' => 'перевести с языка',
|
||||||
'translate-automatically' => 'переводить автоматически',
|
|
||||||
'translate-automatically.*' => 'переводить автоматически',
|
'translate-automatically.*' => 'переводить автоматически',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
@csrf
|
@csrf
|
||||||
@if($serviceTranslationEnable)
|
|
||||||
<x-volt.forms.checkbox :title="__('Automatic translation')" name="translate-automatically"
|
|
||||||
:user-value="1" class="language-content" checkbox-value="1"
|
|
||||||
not-checked-value="0"/>
|
|
||||||
@endif
|
|
||||||
<x-volt.forms.input :title="__('validation.attributes.title')" name="title" type="text" :value="$content->title" required autofocus />
|
<x-volt.forms.input :title="__('validation.attributes.title')" name="title" type="text" :value="$content->title" required autofocus />
|
||||||
<x-volt.forms.textarea-wysiwyg
|
<x-volt.forms.textarea-wysiwyg
|
||||||
:title="__('validation.attributes.description')"
|
:title="__('validation.attributes.description')"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user