Implement localization for API responses and error messages

- Added localization support for API responses in English and Spanish.
- Introduced a middleware to set the API locale based on the Accept-Language header.
- Updated various exception messages and validation responses to use localized strings.
- Created new language files for English and Spanish translations.
- Refactored existing code to replace hardcoded messages with localized strings.
- Added tests to verify localization functionality and response correctness.
This commit is contained in:
2026-07-28 10:19:39 -03:00
parent 754aeebe3a
commit ff9d7728e8
34 changed files with 654 additions and 114 deletions

View File

@@ -45,7 +45,7 @@ class CatalogService
} else {
if (array_key_exists('components', $data)) {
throw ValidationException::withMessages([
'components' => ['Un item standard no puede tener componentes.'],
'components' => [__('api.catalog.standard_with_components')],
]);
}
@@ -265,7 +265,7 @@ class CatalogService
if (isset($seen[$key])) {
throw ValidationException::withMessages([
"components.{$index}" => ['El componente esta duplicado.'],
"components.{$index}" => [__('api.catalog.duplicate_component')],
]);
}
$seen[$key] = true;
@@ -278,7 +278,7 @@ class CatalogService
if ($componentItem === null) {
throw ValidationException::withMessages([
"components.{$index}.catalog_item_id" => [
'El item no pertenece al tenant del bundle.',
__('api.catalog.component_wrong_tenant'),
],
]);
}
@@ -286,7 +286,7 @@ class CatalogService
if ($componentItem->is($bundle) || $componentItem->type !== CatalogItemType::Standard) {
throw ValidationException::withMessages([
"components.{$index}.catalog_item_id" => [
'El componente debe ser un item standard distinto del bundle.',
__('api.catalog.invalid_component'),
],
]);
}
@@ -295,7 +295,7 @@ class CatalogService
if ($hasVariants && $variantId === null) {
throw ValidationException::withMessages([
"components.{$index}.variant_id" => [
'Debe seleccionar una variante para este componente.',
__('api.catalog.component_variant_required'),
],
]);
}
@@ -303,7 +303,7 @@ class CatalogService
if (! $hasVariants && $variantId !== null) {
throw ValidationException::withMessages([
"components.{$index}.variant_id" => [
'El componente con inventario directo no admite una variante.',
__('api.catalog.component_variant_forbidden'),
],
]);
}
@@ -311,7 +311,7 @@ class CatalogService
if ($variantId !== null && ! $componentItem->variants()->whereKey($variantId)->exists()) {
throw ValidationException::withMessages([
"components.{$index}.variant_id" => [
'La variante no pertenece al componente indicado.',
__('api.catalog.component_variant_invalid'),
],
]);
}
@@ -333,7 +333,7 @@ class CatalogService
{
if ($components === []) {
throw ValidationException::withMessages([
'components' => ['Un bundle debe tener al menos un componente.'],
'components' => [__('api.catalog.bundle_component_required')],
]);
}
@@ -348,7 +348,7 @@ class CatalogService
] as $field) {
if (array_key_exists($field, $data)) {
throw ValidationException::withMessages([
$field => ["{$field} no se admite para un bundle."],
$field => [__('api.catalog.bundle_field_forbidden', ['field' => $field])],
]);
}
}
@@ -376,7 +376,7 @@ class CatalogService
if ($attachment === null) {
throw ValidationException::withMessages([
$validationKey => ['El attachment indicado no existe.'],
$validationKey => [__('api.catalog.attachment_not_found')],
]);
}
@@ -408,7 +408,7 @@ class CatalogService
if ($attribute === null) {
throw ValidationException::withMessages([
'attribute_codes' => [
"El atributo {$attributeCode} no existe para el tenant del ítem.",
__('api.catalog.attribute_not_found', ['attribute' => $attributeCode]),
],
]);
}
@@ -442,7 +442,7 @@ class CatalogService
) {
throw ValidationException::withMessages([
"variants.{$index}.inventory" => [
'inventory_id, reserved_stock y sold_units son administrados internamente.',
__('api.catalog.managed_inventory_fields'),
],
]);
}
@@ -463,7 +463,7 @@ class CatalogService
if ($itemAttribute === null) {
throw ValidationException::withMessages([
"variants.{$index}.values.{$attributeCode}" => [
'El atributo no pertenece al ítem de catálogo.',
__('api.catalog.attribute_not_on_item'),
],
]);
}
@@ -489,7 +489,7 @@ class CatalogService
) {
throw ValidationException::withMessages([
"variants.{$index}.maximum_use_date" => [
'La fecha maxima efectiva debe ser posterior o igual a la fecha minima efectiva.',
__('api.catalog.invalid_effective_date_range'),
],
]);
}
@@ -508,7 +508,7 @@ class CatalogService
if ($hasVariants && $variants === []) {
throw ValidationException::withMessages([
'variants' => [
'Un ítem con attribute_codes debe tener variantes.',
__('api.catalog.variants_required'),
],
]);
}
@@ -516,7 +516,7 @@ class CatalogService
if (! $hasVariants && $variants !== []) {
throw ValidationException::withMessages([
'variants' => [
'Un ítem sin attribute_codes no puede tener variantes.',
__('api.catalog.variants_forbidden'),
],
]);
}
@@ -524,7 +524,7 @@ class CatalogService
if ($hasVariants && $hasDirectStock) {
throw ValidationException::withMessages([
'real_stock' => [
'Un ítem con variantes no puede tener inventario directo.',
__('api.catalog.direct_inventory_forbidden'),
],
]);
}
@@ -536,7 +536,7 @@ class CatalogService
) {
throw ValidationException::withMessages([
'inventory' => [
'inventory_id, reserved_stock y sold_units son administrados internamente.',
__('api.catalog.managed_inventory_fields'),
],
]);
}