From 5347e01f0eeddc9c6dd61a9986d1b9177929ee46 Mon Sep 17 00:00:00 2001 From: Konstantinos Arvanitakis Date: Fri, 25 Sep 2026 08:39:15 +0300 Subject: [PATCH] Chore: Updating ProductDocumentLocalizer to translate Custom Fields --- .../Support/ProductDocumentLocalizer.php | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/Catalog/Support/ProductDocumentLocalizer.php b/src/Catalog/Support/ProductDocumentLocalizer.php index 5d154a3..d54ad0d 100644 --- a/src/Catalog/Support/ProductDocumentLocalizer.php +++ b/src/Catalog/Support/ProductDocumentLocalizer.php @@ -66,9 +66,47 @@ class ProductDocumentLocalizer } } + if (! empty($product['custom_fields'])) { + $product['custom_fields'] = $this->localizeCustomFields($product['custom_fields'], $locale, $fallbackLocale); + } + return $product; } + /** + * Product::$custom_fields isn't an AttributeManifest attribute (it's a + * plain JSON column, see Catalog\Models\Product's own docblock), so it + * never goes through the {handle}_{locale} explosion above — the + * indexer copies it straight through (see ProductIndexer), meaning + * each item's `label`/`help_text` still arrives here as a raw + * {locale: string} object (or, for a product saved before those + * became translatable, a plain string). Resolved the same filled()- + * over-?? way as every other translated field above, to the same + * single current-locale string the storefront/cart already expect + * (see product-custom-fields.blade.php and CartController:: + * customFieldsMeta()) — a repeater item has no other reason to reach + * the storefront untouched. + * + * @param array> $fields + * @return array> + */ + private function localizeCustomFields(array $fields, string $locale, ?string $fallbackLocale): array + { + return array_map(function (array $field) use ($locale, $fallbackLocale) { + foreach (['label', 'help_text'] as $key) { + if (! is_array($field[$key] ?? null)) { + continue; + } + + $field[$key] = filled($field[$key][$locale] ?? null) + ? $field[$key][$locale] + : ($field[$key][$fallbackLocale] ?? null); + } + + return $field; + }, $fields); + } + /** * For the Meilisearch driver, Scout's paginateRaw() puts the whole raw response * (hits, query, processingTimeMs, ...) in items(), not a plain list of hits - the