Added the ability to dynamically translate on the project website.

This commit is contained in:
2024-04-22 23:52:04 +05:00
parent a648ba3db9
commit 491249c8d8
55 changed files with 867 additions and 119 deletions

View File

@@ -0,0 +1,91 @@
<?php declare(strict_types=1);
namespace App\Services\Admin\Project;
use App\Dto\Service\Admin\Project\Translation\Translations;
use App\Dto\Service\Admin\Project\Translation\Update;
use App\Enums\CacheTag;
use App\Models\ProjectTranslation;
use App\Models\User;
use App\Repositories\ProjectRepository;
use App\Repositories\ProjectTranslationRepository;
use App\ServiceResults\ServiceResultArray;
use App\ServiceResults\ServiceResultError;
use App\ServiceResults\ServiceResultSuccess;
use App\Services\ClearCacheCommandHandler;
use App\Services\ProjectTranslation\ModelSyncCommand;
use App\Services\Service;
use Illuminate\Support\Facades\DB;
final class TranslationService extends Service
{
public function __construct(
private readonly ProjectRepository $projectRepository,
private readonly ProjectTranslationRepository $projectTranslationRepository,
private readonly ClearCacheCommandHandler $clearCacheCommandHandler,
private readonly ModelSyncCommand $translationModelSyncCommand,
) { }
public function languages(int $projectId, User $user): ServiceResultError | ServiceResultArray
{
$project = $this->projectRepository->getProjectById($projectId);
if (\is_null($project)) {
return $this->errNotFound(__('Not Found'));
}
if ($user->cannot('viewAny', ProjectTranslation::class)) {
return $this->errFobidden(__('Access is denied'));
}
return $this->result([
'project' => $project,
]);
}
public function edit(int $projectId, int $languageId, User $user): ServiceResultError | ServiceResultArray
{
$project = $this->projectRepository->getProjectById($projectId);
$language = $project?->languages()->firstWhere('id', $languageId);
if (\is_null($project) || \is_null($language)) {
return $this->errNotFound(__('Not Found'));
}
if ($user->cannot('viewAny', ProjectTranslation::class)) {
return $this->errFobidden(__('Access is denied'));
}
return $this->result([
'project' => $project,
'language' => $language,
'projectTranslations' => $this->projectTranslationRepository->getProjectTranslations($projectId, $languageId)->all()->pluck('text', 'code')->toArray(),
'translations' => Translations::getTranslationCodes(),
]);
}
public function update(int $projectId, int $languageId, Update $data, User $user): ServiceResultError | ServiceResultSuccess
{
$project = $this->projectRepository->getProjectById($projectId);
$language = $project?->languages()->firstWhere('id', $languageId);
if (\is_null($project) || \is_null($language)) {
return $this->errNotFound(__('Not Found'));
}
if ($user->cannot('update', ProjectTranslation::class)) {
return $this->errFobidden(__('Access is denied'));
}
try {
DB::transaction(function () use ($data, $project, $language) {
$this->translationModelSyncCommand->execute($project, $language, $data->getTranslations());
});
$this->clearCacheCommandHandler->byTag(CacheTag::ProjectTranslation);
} catch (\Throwable $e) {
report($e);
return $this->errService(__('Server Error'));
}
return $this->ok(__('Translations successfully updated'));
}
}

View File

@@ -23,6 +23,8 @@ final readonly class ModelSyncCommand
'title' => $language->getTitle(),
'sort' => $language->getSort(),
'is_default' => $language->isDefault(),
'system_lang' => $language->getSystemLang(),
'iso_code' => $language->getIsoCode(),
];
if ($language->getId() !== null) {

View File

@@ -0,0 +1,60 @@
<?php declare(strict_types=1);
namespace App\Services\ProjectTranslation;
use App\Dto\Service\Admin\Project\Translation\Translation;
use App\Dto\Service\Admin\Project\Translation\Translations;
use App\Models\Project;
use App\Models\ProjectLanguage;
use App\Models\ProjectTranslation;
use App\Repositories\ProjectTranslationRepository;
use Illuminate\Support\Carbon;
final readonly class ModelSyncCommand
{
public function __construct(
private ProjectTranslationRepository $projectTranslationRepository,
) { }
public function execute(Project $project, ProjectLanguage $language, Translations $update): void
{
$modelTranslations = $this->projectTranslationRepository->getProjectTranslationsWithTrashed($project->id, $language->id)->all();
$insert = [];
foreach ($update->getTranslations() as $translation) {
/** @var Translation $translation */
/** @var ProjectTranslation $modelTranslation */
$modelTranslation = $modelTranslations->firstWhere('code', $translation->getCode());
if ($modelTranslation === null && $translation->getText() === null) {
continue;
}
if ($modelTranslation === null && $translation->getText() !== null) {
$insert[] = [
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
'project_id' => $project->id,
'language_id' => $language->id,
'code' => $translation->getCode(),
'text' => $translation->getText(),
];
continue;
}
if ($modelTranslation !== null && $translation->getText() === null) {
$modelTranslation->delete();
continue;
}
if ($modelTranslation->trashed()) {
$modelTranslation->deleted_at = null;
}
$modelTranslation->text = $translation->getText();
$modelTranslation->save();
}
if (!empty($insert)) {
ProjectTranslation::query()->insert($insert);
}
}
}

View File

@@ -3,7 +3,6 @@
namespace App\Services;
use App\Models\Project;
use App\Models\ProjectLanguage;
use App\ServiceResults\ServiceResultArray;
use App\ServiceResults\ServiceResultError;
use App\ServiceResults\ServiceResultSuccess;
@@ -11,7 +10,6 @@ use App\ServiceResults\Site\PagePossibleWithoutTranslation;
use App\ServiceResults\StoreUpdateResult;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache;
abstract class Service
{
@@ -60,9 +58,9 @@ abstract class Service
return new ServiceResultArray(data: $data);
}
final protected function resultSitePage(Project $project, ProjectLanguage $language, array $data, bool $isTranslation): PagePossibleWithoutTranslation
final protected function resultSitePage(Project $project, WebsiteTranslations $websiteTranslations, array $data, bool $isTranslation): PagePossibleWithoutTranslation
{
return new PagePossibleWithoutTranslation($project, $language, $data, $isTranslation);
return new PagePossibleWithoutTranslation($project, $websiteTranslations, $data, $isTranslation);
}
final protected function error(int $code, string $message, array $errors = []): ServiceResultError

View File

@@ -6,19 +6,18 @@ use App\Models\Project;
use App\Models\User;
use App\Dto\Builder\Project as ProjectBuilderDto;
use App\Repositories\ProjectContentRepository;
use App\Repositories\ProjectLanguageRepository;
use App\Repositories\ProjectLinkRepository;
use App\Repositories\ProjectRepository;
use App\ServiceResults\ServiceResultArray;
use App\ServiceResults\ServiceResultError;
use App\ServiceResults\Site\PagePossibleWithoutTranslation;
use App\Services\Service;
use App\Services\WebsiteTranslations;
final class ProjectService extends Service
{
public function __construct(
private readonly ProjectRepository $projectRepository,
private readonly ProjectLanguageRepository $projectLanguageRepository,
private readonly ProjectContentRepository $projectContentRepository,
private readonly ProjectLinkRepository $projectLinkRepository,
) { }
@@ -39,7 +38,7 @@ final class ProjectService extends Service
]);
}
public function getAboutByProject(Project $project, ?string $languageCode, ?User $user = null): ServiceResultError | PagePossibleWithoutTranslation
public function getAboutByProject(Project $project, WebsiteTranslations $websiteTranslations, ?User $user = null): ServiceResultError | PagePossibleWithoutTranslation
{
if (
$project->is_public === false
@@ -47,17 +46,13 @@ final class ProjectService extends Service
) {
return $this->errFobidden(__('Access is denied'));
}
$language = $this->projectLanguageRepository->getProjectLanguageByCodeOrDefault($project, $languageCode);
if (!$language) {
return $this->errNotFound(__('Language not found'));
}
$data = [
'project' => $project,
'language' => $language,
'content' => $this->projectContentRepository->getContentByLanguageId($project->id, $language->id),
'links' => $this->projectLinkRepository->getLinksByProject($project, $language->id),
'websiteTranslations' => $websiteTranslations,
'content' => $this->projectContentRepository->getContentByLanguageId($project->id, $websiteTranslations->getLanguage()->id),
'links' => $this->projectLinkRepository->getLinksByProject($project, $websiteTranslations->getLanguage()->id),
];
return $this->resultSitePage($project, $language, $data, \is_null($data['content']));
return $this->resultSitePage($project, $websiteTranslations, $data, \is_null($data['content']));
}
}

View File

@@ -0,0 +1,23 @@
<?php declare(strict_types=1);
namespace App\Services;
use App\Models\ProjectLanguage;
final readonly class WebsiteTranslations
{
public function __construct(
private ProjectLanguage $language,
private array $transactions,
) { }
public function translate(string $text): string
{
return $this->transactions[$text] ?? __($text);
}
public function getLanguage(): ProjectLanguage
{
return $this->language;
}
}