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
101 changes: 101 additions & 0 deletions app/Nova/TrainingResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,97 @@ private static function localesSorted(): array
return $locales;
}

/**
* Locales offered in the "Translated page content" panel.
*/
private static function pageTranslationLocales(): array
{
$configured = config('codeweek.training_translation_locales', ['it']);
if (is_string($configured)) {
$configured = array_map('trim', explode(',', $configured));
}

$available = self::localesSorted();

return array_values(array_filter(
(array) $configured,
fn ($locale) => $locale !== 'en' && in_array($locale, $available, true)
));
}

/**
* Build one translation field, stored under locale_overrides[$locale][$field].
*/
private function translationField(string $locale, string $field, string $label, string $type)
{
$novaField = match ($type) {
'trix' => Trix::make($label.' ('.strtoupper($locale).')', 'locale_'.$locale.'_'.$field),
'textarea' => Textarea::make($label.' ('.strtoupper($locale).')', 'locale_'.$locale.'_'.$field),
default => Text::make($label.' ('.strtoupper($locale).')', 'locale_'.$locale.'_'.$field),
};

return $novaField
->nullable()
->hideFromIndex()
->resolveUsing(function () use ($locale, $field) {
$overrides = $this->resource->locale_overrides ?? [];

return $overrides[$locale][$field] ?? '';
})
->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale, $field) {
$overrides = $model->locale_overrides ?? [];
if (! isset($overrides[$locale]) || ! is_array($overrides[$locale])) {
$overrides[$locale] = [];
}

$value = $request->get($requestAttribute);
if ($value === null || trim(strip_tags((string) $value)) === '') {
unset($overrides[$locale][$field]);
} else {
$overrides[$locale][$field] = $value;
}

if ($overrides[$locale] === []) {
unset($overrides[$locale]);
}

$model->locale_overrides = $overrides === [] ? null : $overrides;
});
}

/**
* Full-page translation fields for one locale, mirroring the English field types.
*/
private function pageTranslationFieldsFor(string $locale): array
{
$definitions = [
['card_title', 'Card title', 'text'],
['card_author', 'Card author', 'text'],
['page_title', 'Page title', 'text'],
['hero_author', 'Hero author', 'text'],
['hero_button_text', 'Hero button text', 'text'],
['hero_secondary_button_text', 'Hero secondary button text', 'text'],
['highlight_box', 'Highlight box', 'trix'],
['intro', 'Intro', 'trix'],
['content', 'Content', 'trix'],
['body_image_alt', 'Body image alt text', 'text'],
['video_script_text', 'Video script link text', 'text'],
['contacts_section', 'Contacts section', 'trix'],
['register_box_section', 'Register box section', 'trix'],
['about_box_section', 'About box section', 'trix'],
['button_text', 'Button text', 'text'],
['secondary_button_text', 'Secondary button text', 'text'],
['third_button_text', 'Third button text', 'text'],
['meta_title', 'Meta title', 'text'],
['meta_description', 'Meta description', 'textarea'],
];

return array_map(
fn (array $definition) => $this->translationField($locale, ...$definition),
$definitions
);
}

public function fields(Request $request): array
{
$pdfTranslationFields = [];
Expand Down Expand Up @@ -311,6 +402,16 @@ public function fields(Request $request): array
->collapsedByDefault();
}

foreach (self::pageTranslationLocales() as $locale) {
$fields[] = Panel::make(
'Translated page content ('.strtoupper($locale).')',
$this->pageTranslationFieldsFor($locale)
)
->help('Full page translation for '.strtoupper($locale).'. Any field left empty falls back to the English version above, so a partly translated page never renders blank. PDF download links live in the “Translated PDF links” panel.')
->collapsable()
->collapsedByDefault();
}

return $fields;
}

Expand Down
62 changes: 62 additions & 0 deletions app/TrainingResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,35 @@ class TrainingResource extends Model
{
use HasFactory;

/**
* Fields that can be translated per locale through locale_overrides.
*
* pdf_links_section is deliberately absent: it has its own resolution path
* in pdfLinksSectionForLocale(), which merges the English supporting-detail
* block and applies per-URL replacements.
*/
public const TRANSLATABLE_FIELDS = [
'card_title',
'card_author',
'page_title',
'hero_author',
'hero_button_text',
'hero_secondary_button_text',
'intro',
'highlight_box',
'content',
'body_image_alt',
'video_script_text',
'contacts_section',
'register_box_section',
'about_box_section',
'button_text',
'secondary_button_text',
'third_button_text',
'meta_title',
'meta_description',
];

protected $fillable = [
'slug',
'card_title',
Expand Down Expand Up @@ -108,6 +137,39 @@ protected function generateUniqueSlug(string $baseSlug): string
return $slug;
}

/**
* Value of a translatable field for the active (or given) locale.
*
* Falls back to the default (English) attribute per field, so a partly
* translated locale never renders blank sections.
*/
public function forLocale(string $field, ?string $locale = null): ?string
{
$default = $this->getAttribute($field);

if (! in_array($field, self::TRANSLATABLE_FIELDS, true)) {
return $default;
}

$locale = $locale ?? app()->getLocale();
$overrides = $this->locale_overrides ?? [];
$value = $overrides[$locale][$field] ?? null;

if (! is_string($value) || $this->isBlankHtml($value)) {
return $default;
}

return $value;
}

/**
* Trix stores an emptied editor as markup like "<div><br></div>".
*/
protected function isBlankHtml(string $value): bool
{
return trim(strip_tags($value)) === '' && ! str_contains($value, '<img');
}

public function getResolvedCardImageAttribute(): string
{
$image = trim((string) $this->card_image);
Expand Down
8 changes: 8 additions & 0 deletions config/codeweek.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,12 @@
'trim',
explode(',', (string) env('CERTIFICATE_ADMIN_EMAILS', ''))
))),

// Languages that get a full page-content translation panel on Nova training
// resources. Kept short on purpose: one panel per locale times ~13 fields
// would put hundreds of fields in a single Nova form.
'training_translation_locales' => array_values(array_filter(array_map(
'trim',
explode(',', (string) env('TRAINING_TRANSLATION_LOCALES', 'it'))
))),
];
Loading
Loading