value maps that * spatie/laravel-translatable stores. * * Responses resolve each translatable field to the request's active locale, so * a plain client sees plain strings. Clients that need every language ask for * it explicitly with `?with=translations` and get a `translations` object * alongside. * * Requests accept either shape: a bare string writes the active locale only * and leaves other languages untouched, while an object writes exactly the * locales it names. */ final class TranslatablePayload { /** * Field values resolved to a single locale, with the configured fallback * filling any gaps. * * @param array $fields * @return array */ public static function resolve(Model $model, array $fields, ?string $locale = null): array { $locale ??= app()->getLocale(); $fallback = (string) config('locales.fallback'); $resolved = []; foreach ($fields as $field) { $value = $model->getTranslation($field, $locale, useFallbackLocale: false); if (blank($value) && $locale !== $fallback) { $value = $model->getTranslation($field, $fallback, useFallbackLocale: false); } $resolved[$field] = blank($value) ? null : $value; } return $resolved; } /** * The full locale => value map for each field, limited to supported * locales so retired languages do not leak into responses. * * @param array $fields * @return array> */ public static function all(Model $model, array $fields): array { $supported = array_keys(config('locales.supported')); $translations = []; foreach ($fields as $field) { $translations[$field] = array_filter( $model->getTranslations($field), fn (string $locale) => in_array($locale, $supported, true), ARRAY_FILTER_USE_KEY, ); } return $translations; } /** * Normalise an incoming value into a locale => value map. * * @param string|array $value * @return array */ public static function normalise(string|array $value, ?string $locale = null): array { if (is_string($value)) { return [$locale ?? app()->getLocale() => $value]; } $supported = array_keys(config('locales.supported')); return array_filter( $value, fn (string $key) => in_array($key, $supported, true), ARRAY_FILTER_USE_KEY, ); } /** * Apply validated input to a model, merging translatable fields into their * existing maps instead of replacing them wholesale — a request that only * sends Catalan must not wipe the Spanish text. * * @param array $data * @param array $translatable * @return array */ public static function merge(Model $model, array $data, array $translatable): array { foreach ($translatable as $field) { if (! array_key_exists($field, $data)) { continue; } $incoming = self::normalise($data[$field]); $existing = $model->exists ? $model->getTranslations($field) : []; // A null value for a locale clears that translation. $data[$field] = array_filter( array_merge($existing, $incoming), fn ($v) => filled($v), ); } return $data; } }