Files
citrus/app/Traits/HasTranslations.php

310 lines
8.7 KiB
PHP

<?php
namespace App\Traits;
use App\Models\Language;
use App\Models\Translation;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Collection;
trait HasTranslations
{
/**
* Get all translations for this model
*/
public function translations(): MorphMany
{
return $this->morphMany(Translation::class, 'translatable');
}
/**
* Get translation for a specific field and language
*/
public function getTranslation(string $fieldName, ?string $languageCode = null, bool $published = true): ?Translation
{
$languageCode = $languageCode ?? $this->getCurrentLanguageCode();
$query = $this->translations()
->where('field_name', $fieldName)
->where('language_code', $languageCode);
if ($published) {
$query->where('status', 'published');
}
return $query->first();
}
/**
* Get translation value for a specific field and language
*/
public function translate(string $fieldName, ?string $languageCode = null, bool $fallback = true): mixed
{
$languageCode = $languageCode ?? $this->getCurrentLanguageCode();
$translation = $this->getTranslation($fieldName, $languageCode);
if ($translation) {
return $translation->field_value;
}
// Fallback to default language
if ($fallback && $languageCode !== $this->getDefaultLanguageCode()) {
$defaultTranslation = $this->getTranslation($fieldName, $this->getDefaultLanguageCode());
if ($defaultTranslation) {
return $defaultTranslation->field_value;
}
}
// Fallback to original field value
return $this->{$fieldName} ?? null;
}
/**
* Set translation for a specific field and language
*/
public function setTranslation(
string $fieldName,
string $languageCode,
mixed $value,
string $status = 'draft',
?int $userId = null
): Translation {
$userId = $userId ?? auth()->id();
return $this->translations()->updateOrCreate(
[
'field_name' => $fieldName,
'language_code' => $languageCode,
],
[
'field_value' => $value,
'status' => $status,
'created_by' => $userId,
'updated_by' => $userId,
]
);
}
/**
* Get all translations for a specific field
*/
public function getTranslations(string $fieldName, bool $published = true): Collection
{
$query = $this->translations()
->where('field_name', $fieldName);
if ($published) {
$query->where('status', 'published');
}
return $query->get()->keyBy('language_code');
}
/**
* Get all translations for a specific language
*/
public function getTranslationsForLanguage(string $languageCode, bool $published = true): Collection
{
$query = $this->translations()
->where('language_code', $languageCode);
if ($published) {
$query->where('status', 'published');
}
return $query->get()->keyBy('field_name');
}
/**
* Check if translation exists for a field and language
*/
public function hasTranslation(string $fieldName, string $languageCode, bool $published = true): bool
{
$query = $this->translations()
->where('field_name', $fieldName)
->where('language_code', $languageCode);
if ($published) {
$query->where('status', 'published');
}
return $query->exists();
}
/**
* Get translation progress for all languages
*/
public function getTranslationProgress(): array
{
$languages = Language::active()->get();
$translatableFields = $this->getTranslatableFields();
$totalFields = count($translatableFields);
$progress = [];
foreach ($languages as $language) {
$translatedCount = $this->translations()
->where('language_code', $language->code)
->where('status', 'published')
->whereIn('field_name', $translatableFields)
->count();
$progress[$language->code] = $totalFields > 0
? round(($translatedCount / $totalFields) * 100)
: 0;
}
return $progress;
}
/**
* Get missing translations for a specific language
*/
public function getMissingTranslations(string $languageCode): array
{
$translatableFields = $this->getTranslatableFields();
$existingTranslations = $this->translations()
->where('language_code', $languageCode)
->where('status', 'published')
->pluck('field_name')
->toArray();
return array_diff($translatableFields, $existingTranslations);
}
/**
* Duplicate translation from one language to another
*/
public function duplicateTranslation(string $fromLanguage, string $toLanguage, ?int $userId = null): int
{
$userId = $userId ?? auth()->id();
$count = 0;
$sourceTranslations = $this->getTranslationsForLanguage($fromLanguage, false);
foreach ($sourceTranslations as $fieldName => $translation) {
$this->setTranslation(
$fieldName,
$toLanguage,
$translation->field_value,
'draft',
$userId
);
$count++;
}
return $count;
}
/**
* Get translation status for a language
*/
public function getTranslationStatus(string $languageCode): string
{
$progress = $this->getTranslationProgress();
$percentage = $progress[$languageCode] ?? 0;
if ($percentage === 100) {
return 'complete';
} elseif ($percentage > 0) {
return 'partial';
} else {
return 'missing';
}
}
/**
* Sync translations for all translatable fields
*/
public function syncTranslations(array $languageCodes, ?int $userId = null): void
{
$userId = $userId ?? auth()->id();
$translatableFields = $this->getTranslatableFields();
foreach ($languageCodes as $languageCode) {
foreach ($translatableFields as $field) {
if (!$this->hasTranslation($field, $languageCode, false)) {
$this->setTranslation(
$field,
$languageCode,
$this->{$field} ?? '',
'draft',
$userId
);
}
}
}
}
/**
* Get translatable fields for this model
*/
public function getTranslatableFields(): array
{
return $this->translatable ?? [];
}
/**
* Get current language code
*/
protected function getCurrentLanguageCode(): string
{
return app()->getLocale();
}
/**
* Get default language code
*/
protected function getDefaultLanguageCode(): string
{
$defaultLanguage = Language::where('is_default', true)->first();
return $defaultLanguage ? $defaultLanguage->code : config('app.locale');
}
/**
* Scope: Filter by language
*/
public function scopeWhereHasTranslation($query, string $languageCode, bool $published = true)
{
return $query->whereHas('translations', function ($q) use ($languageCode, $published) {
$q->where('language_code', $languageCode);
if ($published) {
$q->where('status', 'published');
}
});
}
/**
* Scope: Filter by user's languages
*/
public function scopeWhereHasTranslationForUser($query, $user)
{
$languageCodes = $user->userLanguages()->pluck('language_code')->toArray();
return $query->whereHas('translations', function ($q) use ($languageCodes) {
$q->whereIn('language_code', $languageCodes);
});
}
/**
* Get translated model for current language
*/
public function translated(?string $languageCode = null): self
{
$languageCode = $languageCode ?? $this->getCurrentLanguageCode();
$translations = $this->getTranslationsForLanguage($languageCode);
foreach ($translations as $fieldName => $translation) {
if (in_array($fieldName, $this->getTranslatableFields())) {
$this->{$fieldName} = $translation->field_value;
}
}
return $this;
}
}