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

@@ -3,15 +3,18 @@
namespace App\Dto\Service\Admin\Project;
use App\Dto\Service\Dto;
use App\Enums\Lang;
final readonly class Language extends Dto
{
public function __construct(
private string $title,
private string $code,
private int $sort,
private bool $isDefault,
private ?int $id,
private string $title,
private string $code,
private int $sort,
private bool $isDefault,
private ?string $isoCode = null,
private ?Lang $systemLang = null,
private ?int $id = null,
) { }
public function getTitle(): string
@@ -38,4 +41,14 @@ final readonly class Language extends Dto
{
return $this->id;
}
public function getIsoCode(): ?string
{
return $this->isoCode;
}
public function getSystemLang(): ?Lang
{
return $this->systemLang;
}
}

View File

@@ -0,0 +1,23 @@
<?php declare(strict_types=1);
namespace App\Dto\Service\Admin\Project\Translation;
use App\Dto\Service\Dto;
final readonly class Translation extends Dto
{
public function __construct(
private string $code,
private ?string $text
) { }
public function getCode(): string
{
return $this->code;
}
public function getText(): ?string
{
return $this->text;
}
}

View File

@@ -0,0 +1,35 @@
<?php declare(strict_types=1);
namespace App\Dto\Service\Admin\Project\Translation;
use App\Exceptions\Dto\Admin\Project\Transaction\TranslationsException;
final class Translations
{
private array $translations = [];
public function addTranslation(string $code, ?string $text): void
{
if (!in_array($code, self::getTranslationCodes())) {
throw new TranslationsException('Translation code "' . $code . '" not available');
}
$this->translations[] = new Translation($code, $text);
}
public function getTranslations(): array
{
return $this->translations;
}
public static function getTranslationCodes(): array
{
return [
'site.Menu',
'site.Powered by service',
'site.About project',
'site.Choose language',
'site.Page without translation',
'site.Project',
];
}
}

View File

@@ -0,0 +1,17 @@
<?php declare(strict_types=1);
namespace App\Dto\Service\Admin\Project\Translation;
use App\Dto\Service\Dto;
final readonly class Update extends Dto
{
public function __construct(
private Translations $translations,
) { }
public function getTranslations(): Translations
{
return $this->translations;
}
}

View File

@@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Cache;
enum CacheTag: string
{
case Project = 'project';
case ProjectTranslation = 'project_translation';
public function getCache(): TaggedCache
{

View File

@@ -10,6 +10,7 @@ enum Permission: string
case Project = 'project';
case ProjectContent = 'project-content';
case ProjectLink = 'project-link';
case ProjectTranslation = 'project-translation';
public function getPermissions(): array
{
@@ -22,6 +23,10 @@ enum Permission: string
'create' => __('permissions.Allowed to create'),
'update' => __('permissions.Allowed to edit'),
],
self::ProjectTranslation => [
'view' => __('permissions.Allowed to watch'),
'update' => __('permissions.Allowed to edit'),
],
default => $this->getBasePermissions()
};

View File

@@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace App\Exceptions\Dto\Admin\Project\Transaction;
final class TranslationsException extends \Exception
{
}

View File

@@ -0,0 +1,54 @@
<?php declare(strict_types=1);
namespace App\Http\Controllers\Admin\Projects;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Projects\Translations\UpdateRequest;
use App\Services\Admin\Project\TranslationService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
final class TranslationsController extends Controller
{
public function __construct(
private readonly TranslationService $translationService,
) { }
public function languages(int $projectId, Request $request): View
{
$user = $request->user();
$result = $this->translationService->languages($projectId, $user);
if ($result->isError()) {
$this->errors($result);
}
return view('admin/projects/translations/languages', $result->getData());
}
public function edit(int $projectId, int $languageId, Request $request): View
{
$user = $request->user();
$result = $this->translationService->edit($projectId, $languageId, $user);
if ($result->isError()) {
$this->errors($result);
}
return view('admin/projects/translations/edit', $result->getData());
}
public function update(int $projectId, int $languageId, UpdateRequest $request): RedirectResponse
{
$user = $request->user();
$data = $request->getDto();
$result = $this->translationService->update($projectId, $languageId, $data, $user);
if ($result->isError()) {
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
}
return redirect()->route('admin.projects.translations.edit', [
'project' => $projectId,
'language' => $languageId,
])->withSuccess($result->getMessage());
}
}

View File

@@ -12,7 +12,7 @@ abstract class Controller extends BaseController
{
return \view('site.page-without-translation', [
'project' => $result->getProject(),
'language' => $result->getLanguage(),
'websiteTranslations' => $result->getWebsiteTranslations(),
]);
}
}

View File

@@ -2,7 +2,6 @@
namespace App\Http\Controllers\Site;
use App\Enums\Site\ProjectSection;
use App\Services\Site\ProjectService;
use Illuminate\Http\Request;
use Illuminate\View\View;
@@ -13,15 +12,13 @@ final class ProjectsController extends Controller
private readonly ProjectService $projectService,
) { }
public function index(Request $request, ?string $language = null): View
public function index(Request $request): View
{
$user = $request->user();
if (\is_null($request->project)) {
if (!\is_null($language)) {
abort(404);
}
$user = $request->user();
$project = $request->get('project');
$websiteTranslations = $request->get('websiteTranslations');
if (\is_null($project)) {
$with = ['storage'];
$result = $this->projectService->getProjects($user, $with);
if ($result->isError()) {
@@ -31,7 +28,7 @@ final class ProjectsController extends Controller
return \view('site.projects.index', $result->getData());
}
$result = $this->projectService->getAboutByProject($request->project, $language, $request->user());
$result = $this->projectService->getAboutByProject($project, $websiteTranslations, $request->user());
if ($result->isError()) {
$this->errors($result);
}

View File

@@ -3,23 +3,17 @@
namespace App\Http\Middleware;
use App\Enums\CacheTag;
use App\Repositories\ProjectRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
use Closure;
class Project
class ProjectAndLanguage extends ProjectLanguage
{
public function __construct(
private readonly ProjectRepository $projectRepository,
) { }
public function handle(Request $request, Closure $next): Response
{
$projectCode = $request->route()?->parameter('project');
if ($projectCode === null) {
abort(404);
abort(Response::HTTP_NOT_FOUND);
}
$seconds = 3600;
@@ -27,23 +21,27 @@ class Project
return $this->projectRepository->getProjectByCode($projectCode) ?? false;
});
if ($project === false) {
abort(404);
abort(Response::HTTP_NOT_FOUND);
}
if (
$project->http_host !== null
&& $project->http_host !== $request->getSchemeAndHttpHost()
) {
return redirect($project->http_host, 302);
}
$languageCode = $request->route()?->parameter('language');
$websiteTranslations = $this->getWebsiteTranslations($project, $languageCode);
if (\is_null($websiteTranslations)) {
abort(Response::HTTP_NOT_FOUND);
}
unset($request->route()->parameters['project']);
unset($request->route()->parameters['language']);
$routeName = $request->route()->getName();
if (
$routeName !== null
&& $project->http_host !== null
&& $project->http_host !== $request->getSchemeAndHttpHost()
) {
$routeName = Str::of($routeName)->replaceStart('project.', '')->toString();
$route = \route($routeName, $request->route()->parameters(), false);
return redirect($project->http_host . $route, 302);
}
$request->merge(['project' => $project]);
$request->attributes->set('project', $project);
$request->attributes->set('websiteTranslations', $websiteTranslations);
return $next($request);
}

View File

@@ -3,17 +3,12 @@
namespace App\Http\Middleware;
use App\Enums\CacheTag;
use App\Repositories\ProjectRepository;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
final class ProjectDomain
final class ProjectDomainAndLanguage extends ProjectLanguage
{
public function __construct(
private readonly ProjectRepository $projectRepository,
) { }
public function handle(Request $request, Closure $next): Response
{
$httpHost = $request->getSchemeAndHttpHost();
@@ -25,7 +20,19 @@ final class ProjectDomain
if ($project === false) {
$project = null;
}
$request->merge(['project' => $project]);
$websiteTranslations = null;
if ($project !== null) {
$languageCode = $request->route()?->parameter('language');
$websiteTranslations = $this->getWebsiteTranslations($project, $languageCode);
if (\is_null($websiteTranslations)) {
abort(Response::HTTP_NOT_FOUND);
}
}
unset($request->route()->parameters['language']);
$request->attributes->set('project', $project);
$request->attributes->set('websiteTranslations', $websiteTranslations);
return $next($request);
}

View File

@@ -0,0 +1,43 @@
<?php declare(strict_types=1);
namespace App\Http\Middleware;
use App\Enums\CacheTag;
use App\Models\Project;
use App\Repositories\ProjectLanguageRepository;
use App\Repositories\ProjectRepository;
use App\Repositories\ProjectTranslationRepository;
use App\Services\WebsiteTranslations;
use Illuminate\Support\Facades\App;
abstract class ProjectLanguage
{
public function __construct(
protected readonly ProjectRepository $projectRepository,
private readonly ProjectLanguageRepository $projectLanguageRepository,
private readonly ProjectTranslationRepository $projectTranslationRepository
) { }
protected function getWebsiteTranslations(Project $project, ?string $languageCode): ?WebsiteTranslations
{
$seconds = 3600 * 3;
$language = CacheTag::Project->getCache()->remember(self::class . $project->id . '-' . $languageCode, $seconds, function () use ($project, $languageCode) {
return $this->projectLanguageRepository->getProjectLanguageByCodeOrDefault($project, $languageCode) ?? false;
});
if ($language === false) {
return null;
}
if ($language !== null) {
if ($language->system_lang) {
App::setLocale($language->system_lang->getLocale());
}
}
$seconds = 3600 * 24;
$translations = CacheTag::ProjectTranslation->getCache()->remember(self::class . '-translations-' . $project->id . '-' . $language->id, $seconds, function () use ($project, $language) {
return $this->projectTranslationRepository->getProjectTranslations($project->id, $language->id)->all()->pluck('text', 'code')->toArray();
});
return new WebsiteTranslations($language, $translations);
}
}

View File

@@ -7,9 +7,11 @@ use App\Dto\Service\Admin\Project\Language;
use App\Dto\Service\Admin\Project\Languages;
use App\Dto\Service\Admin\Project\StoreUpdate;
use App\Dto\Service\Storage\Storages;
use App\Enums\Lang;
use App\Enums\StorageType;
use App\Rules\HttpHost;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class StoreUpdateRequest extends FormRequest implements FormRequestDto
{
@@ -33,6 +35,8 @@ class StoreUpdateRequest extends FormRequest implements FormRequestDto
'languages.items.*.title' => ['required', 'string', 'max:255'],
'languages.items.*.code' => ['required', 'string', 'min:2', 'max:30', 'regex:/^[a-z_]+$/'],
'languages.items.*.sort' => ['required', 'numeric', 'min:-1000', 'max:1000'],
'languages.items.*.system_lang' => ['nullable', new Enum(Lang::class)],
'languages.items.*.iso_code' => ['nullable', 'string', 'max:30', 'regex:/^[a-zA-Z-]+$/'],
'languages.default' => ['required', 'numeric', function (string $attribute, mixed $value, \Closure $fail) {
$languages = $this->input('languages.items', []);
if (!isset($languages[$value])) {
@@ -81,11 +85,17 @@ class StoreUpdateRequest extends FormRequest implements FormRequestDto
if ($languageId !== null) {
$languageId = (int) $languageId;
}
$systemLang = null;
if ($lang['system_lang'] !== null) {
$systemLang = Lang::tryFrom((int) $lang['system_lang']);
}
$language = new Language(
title: $lang['title'],
code: $lang['code'],
sort: (int) $lang['sort'],
isDefault: ($default === $index),
isoCode: $lang['iso_code'] ?? null,
systemLang: $systemLang,
id: $languageId,
);
$languages->addLanguage($language);

View File

@@ -0,0 +1,38 @@
<?php declare(strict_types=1);
namespace App\Http\Requests\Admin\Projects\Translations;
use App\Contracts\FormRequestDto;
use App\Dto\Service\Admin\Project\Translation\Translation;
use App\Dto\Service\Admin\Project\Translation\Translations;
use App\Dto\Service\Admin\Project\Translation\Update;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\In;
final class UpdateRequest extends FormRequest implements FormRequestDto
{
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'translations' => ['nullable', 'array'],
'translations.*.code' => ['required', 'string', new In(Translations::getTranslationCodes())],
'translations.*.text' => ['nullable', 'string', 'max:1000'],
];
}
public function getDto(): Update
{
$translations = new Translations();
foreach ($this->input('translations', []) as $translation) {
$translations->addTranslation(
code: $translation['code'],
text: $translation['text'] ?? null,
);
}
return new Update($translations);
}
}

View File

@@ -2,7 +2,9 @@
namespace App\Models;
use App\Enums\Lang;
use App\Models\Scopes\SortScope;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -33,6 +35,8 @@ final class ProjectLanguage extends Model
'code',
'is_default',
'sort',
'system_lang',
'iso_code',
];
/**
@@ -43,8 +47,27 @@ final class ProjectLanguage extends Model
protected function casts(): array
{
return [
'is_default' => 'boolean',
'sort' => 'integer',
'is_default' => 'boolean',
'sort' => 'integer',
'system_lang' => Lang::class,
];
}
protected function attributeLang(): Attribute
{
return Attribute::make(
get: function () {
if ($this->iso_code) {
return $this->iso_code;
}
if ($this->system_lang) {
return $this->system_lang->getLocale();
}
return $this->code;
},
)->shouldCache();
}
}

View File

@@ -0,0 +1,26 @@
<?php declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
final class ProjectTranslation extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'project_translations';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'project_id',
'language_id',
'text',
'code',
];
}

View File

@@ -0,0 +1,24 @@
<?php declare(strict_types=1);
namespace App\Policies;
use App\Models\ProjectTranslation;
use App\Models\User;
final readonly class ProjectTranslationPolicy extends Policy
{
public function viewAny(User $user): bool
{
return $user->hasPermission('project.view');
}
public function view(User $user, ProjectTranslation $translation): bool
{
return $user->hasPermission('project-translation.view');
}
public function update(User $user): bool
{
return $user->hasPermission('project-translation.update');
}
}

View File

@@ -0,0 +1,25 @@
<?php declare(strict_types=1);
namespace App\Repositories;
use App\Models\ProjectTranslation;
use App\Services\Search\CreateSearchInstanceCommand;
use App\Services\Search\Search;
final readonly class ProjectTranslationRepository
{
public function __construct(
private CreateSearchInstanceCommand $createSearchInstanceCommand,
) { }
public function getProjectTranslations(int $projectId, int $languageId): Search
{
$query = ProjectTranslation::query()->where('project_id', $projectId)->where('language_id', $languageId);
return $this->createSearchInstanceCommand->execute($query);
}
public function getProjectTranslationsWithTrashed(int $projectId, int $languageId): Search
{
$query = ProjectTranslation::query()->withTrashed()->where('project_id', $projectId)->where('language_id', $languageId);
return $this->createSearchInstanceCommand->execute($query);
}
}

View File

@@ -3,14 +3,14 @@
namespace app\ServiceResults\Site;
use App\Models\Project;
use App\Models\ProjectLanguage;
use App\ServiceResults\ServiceResult;
use App\Services\WebsiteTranslations;
final class PagePossibleWithoutTranslation extends ServiceResult
{
public function __construct(
private readonly Project $project,
private readonly ProjectLanguage $language,
private readonly WebsiteTranslations $websiteTranslations,
private readonly array $data,
private readonly bool $isTranslation,
) { }
@@ -30,8 +30,8 @@ final class PagePossibleWithoutTranslation extends ServiceResult
return $this->project;
}
public function getLanguage(): ProjectLanguage
public function getWebsiteTranslations(): WebsiteTranslations
{
return $this->language;
return $this->websiteTranslations;
}
}

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;
}
}

View File

@@ -2,8 +2,9 @@
namespace app\View\Components\Site;
use App\Models\ProjectLanguage;
use Illuminate\Support\Collection;
use App\Enums\CacheTag;
use App\Models\Project;
use App\Services\WebsiteTranslations;
use Illuminate\Support\Str;
use Illuminate\View\Component;
use Illuminate\View\View;
@@ -11,20 +12,25 @@ use Illuminate\View\View;
final class ChooseLanguage extends Component
{
public function __construct(
private readonly ProjectLanguage $language,
private readonly Collection $languages,
private readonly WebsiteTranslations $websiteTranslations,
private readonly Project $project,
) { }
public function render(): View
{
$link = Str::of( request()->getRequestUri() )->rtrim('/');
if ($link->endsWith('/language/' . $this->language->code)) {
$link = $link->replace('/language/' . $this->language->code, '', false);
$link = Str::of( request()->url() )->rtrim('/');
if ($link->endsWith('/language/' . $this->websiteTranslations->getLanguage()->code)) {
$link = $link->replace('/language/' . $this->websiteTranslations->getLanguage()->code, '', false);
}
$seconds = 3600 * 12;
$languages = CacheTag::Project->getCache()->remember(self::class . $this->project->id, $seconds, function () {
return $this->project->languages;
});
return view('components.site.choose-language', [
'language' => $this->language,
'languages' => $this->languages,
'websiteTranslations' => $this->websiteTranslations,
'languages' => $languages,
'link' => (string) $link,
]);
}

View File

@@ -4,7 +4,7 @@ namespace app\View\Components\Site;
use App\Enums\StorageType;
use App\Models\Project;
use App\Models\ProjectLanguage;
use App\Services\WebsiteTranslations;
use Illuminate\View\Component;
use Illuminate\View\View;
@@ -12,7 +12,7 @@ final class Layout extends Component
{
public function __construct(
private readonly Project $project,
private readonly ProjectLanguage $language,
private readonly WebsiteTranslations $websiteTranslations,
) { }
public function render(): View
@@ -20,7 +20,7 @@ final class Layout extends Component
return view('layout.site', [
'project' => $this->project,
'logo' => $this->project->getStorageOne(StorageType::Logo),
'language' => $this->language,
'websiteTranslations' => $this->websiteTranslations,
]);
}
}

View File

@@ -29,12 +29,18 @@ final class Languages extends Form
$default = (int) $default;
}
foreach ($value['items'] as $index => $lang) {
$systemLang = null;
if (!empty($lang['system_lang'])) {
$systemLang = (int) $lang['system_lang'];
}
$langs[$index] = [
'title' => $lang['title'] ?? '',
'code' => $lang['code'] ?? '',
'is_default' => ($index === $default),
'sort' => $lang['sort'] ?? '',
'id' => $lang['id'] ?? null,
'title' => $lang['title'] ?? '',
'code' => $lang['code'] ?? '',
'is_default' => ($index === $default),
'sort' => $lang['sort'] ?? '',
'system_lang' => $systemLang,
'iso_code' => $lang['iso_code'] ?? null,
'id' => $lang['id'] ?? null,
];
}