From e94e1a4676d68950df76a496a30a9acc61e4b799 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Mon, 15 Dec 2025 17:13:32 +0100 Subject: [PATCH] feat(): Add more translatable core entity (#14311) * feat(): Add product type and collection translation support * Create sharp-poets-give.md * feat(): Add product type and collection translation support * feat(): Add product type and collection translation support * options * options * shipping options/type * return reason * fix * leave out shipping and return reason * leave out shipping and return reason * leave out shipping and return reason --- .changeset/sharp-poets-give.md | 6 +++ .../src/api/store/collections/[id]/route.ts | 7 +++ .../medusa/src/api/store/collections/route.ts | 7 +++ .../store/product-categories/[id]/route.ts | 9 +++- .../src/api/store/product-categories/route.ts | 19 ++++++-- .../src/api/store/product-tags/[id]/route.ts | 12 ++++- .../src/api/store/product-tags/route.ts | 11 ++++- .../src/api/store/product-types/[id]/route.ts | 12 ++++- .../src/api/store/product-types/route.ts | 11 ++++- .../api/store/product-variants/[id]/route.ts | 7 +++ .../src/api/store/product-variants/route.ts | 7 +++ .../api/store/return-reasons/[id]/route.ts | 4 +- .../src/api/store/return-reasons/route.ts | 4 +- .../shipping-options/[id]/calculate/route.ts | 4 +- .../src/api/store/shipping-options/route.ts | 6 +-- .../readonly/product-translation.ts | 8 +--- .../modules/translation/src/loaders/config.ts | 47 ++----------------- .../src/utils/translatable-fields.ts | 32 ++++++++++++- 18 files changed, 143 insertions(+), 70 deletions(-) create mode 100644 .changeset/sharp-poets-give.md diff --git a/.changeset/sharp-poets-give.md b/.changeset/sharp-poets-give.md new file mode 100644 index 0000000000..1739148aaf --- /dev/null +++ b/.changeset/sharp-poets-give.md @@ -0,0 +1,6 @@ +--- +"@medusajs/medusa": patch +"@medusajs/translation": patch +--- + +feat(): Add product type and collection translation support diff --git a/packages/medusa/src/api/store/collections/[id]/route.ts b/packages/medusa/src/api/store/collections/[id]/route.ts index 1ed26a7f16..5c5fdf08cd 100644 --- a/packages/medusa/src/api/store/collections/[id]/route.ts +++ b/packages/medusa/src/api/store/collections/[id]/route.ts @@ -4,6 +4,7 @@ import { MedusaResponse, } from "@medusajs/framework/http" import { refetchCollection } from "../helpers" +import { applyTranslations } from "@medusajs/framework/utils" export const GET = async ( req: AuthenticatedMedusaRequest, @@ -15,5 +16,11 @@ export const GET = async ( req.queryConfig.fields ) + await applyTranslations({ + localeCode: req.locale, + objects: [collection], + container: req.scope, + }) + res.status(200).json({ collection }) } diff --git a/packages/medusa/src/api/store/collections/route.ts b/packages/medusa/src/api/store/collections/route.ts index 85280ae77f..940a9d30d4 100644 --- a/packages/medusa/src/api/store/collections/route.ts +++ b/packages/medusa/src/api/store/collections/route.ts @@ -5,6 +5,7 @@ import { } from "@medusajs/framework/http" import { + applyTranslations, ContainerRegistrationKeys, remoteQueryObjectFromString, } from "@medusajs/framework/utils" @@ -26,6 +27,12 @@ export const GET = async ( const { rows: collections, metadata } = await remoteQuery(query) + await applyTranslations({ + localeCode: req.locale, + objects: collections, + container: req.scope, + }) + res.json({ collections, count: metadata.count, diff --git a/packages/medusa/src/api/store/product-categories/[id]/route.ts b/packages/medusa/src/api/store/product-categories/[id]/route.ts index c8ca9f674a..05c4f38045 100644 --- a/packages/medusa/src/api/store/product-categories/[id]/route.ts +++ b/packages/medusa/src/api/store/product-categories/[id]/route.ts @@ -1,5 +1,5 @@ import { StoreProductCategoryResponse } from "@medusajs/framework/types" -import { MedusaError } from "@medusajs/framework/utils" +import { applyTranslations, MedusaError } from "@medusajs/framework/utils" import { AuthenticatedMedusaRequest, MedusaResponse, @@ -24,5 +24,12 @@ export const GET = async ( `Product category with id: ${req.params.id} was not found` ) } + + await applyTranslations({ + localeCode: req.locale, + objects: [category], + container: req.scope, + }) + res.json({ product_category: category }) } diff --git a/packages/medusa/src/api/store/product-categories/route.ts b/packages/medusa/src/api/store/product-categories/route.ts index 111509fbdf..ff01518939 100644 --- a/packages/medusa/src/api/store/product-categories/route.ts +++ b/packages/medusa/src/api/store/product-categories/route.ts @@ -1,12 +1,15 @@ -import { - StoreProductCategoryListParams, - StoreProductCategoryListResponse, -} from "@medusajs/framework/types" -import { ContainerRegistrationKeys } from "@medusajs/framework/utils" import { AuthenticatedMedusaRequest, MedusaResponse, } from "@medusajs/framework/http" +import { + StoreProductCategoryListParams, + StoreProductCategoryListResponse, +} from "@medusajs/framework/types" +import { + applyTranslations, + ContainerRegistrationKeys, +} from "@medusajs/framework/utils" export const GET = async ( req: AuthenticatedMedusaRequest, @@ -21,6 +24,12 @@ export const GET = async ( pagination: req.queryConfig.pagination, }) + await applyTranslations({ + localeCode: req.locale, + objects: product_categories, + container: req.scope, + }) + res.json({ product_categories, count: metadata!.count, diff --git a/packages/medusa/src/api/store/product-tags/[id]/route.ts b/packages/medusa/src/api/store/product-tags/[id]/route.ts index 109cafe43e..2d0d608aad 100644 --- a/packages/medusa/src/api/store/product-tags/[id]/route.ts +++ b/packages/medusa/src/api/store/product-tags/[id]/route.ts @@ -1,5 +1,6 @@ import { StoreProductTagResponse } from "@medusajs/framework/types" import { + applyTranslations, ContainerRegistrationKeys, MedusaError, } from "@medusajs/framework/utils" @@ -30,5 +31,14 @@ export const GET = async ( `Product tag with id: ${req.params.id} was not found` ) } - res.json({ product_tag: data[0] }) + + const productTag = data[0] + + await applyTranslations({ + localeCode: req.locale, + objects: [productTag], + container: req.scope, + }) + + res.json({ product_tag: productTag }) } diff --git a/packages/medusa/src/api/store/product-tags/route.ts b/packages/medusa/src/api/store/product-tags/route.ts index 425a1953c1..d99f1d99dd 100644 --- a/packages/medusa/src/api/store/product-tags/route.ts +++ b/packages/medusa/src/api/store/product-tags/route.ts @@ -1,5 +1,8 @@ import { HttpTypes } from "@medusajs/framework/types" -import { ContainerRegistrationKeys } from "@medusajs/framework/utils" +import { + applyTranslations, + ContainerRegistrationKeys, +} from "@medusajs/framework/utils" import { AuthenticatedMedusaRequest, MedusaResponse, @@ -18,6 +21,12 @@ export const GET = async ( fields: req.queryConfig.fields, }) + await applyTranslations({ + localeCode: req.locale, + objects: product_tags, + container: req.scope, + }) + res.json({ product_tags, count: metadata?.count ?? 0, diff --git a/packages/medusa/src/api/store/product-types/[id]/route.ts b/packages/medusa/src/api/store/product-types/[id]/route.ts index 713811e9d6..51fa951793 100644 --- a/packages/medusa/src/api/store/product-types/[id]/route.ts +++ b/packages/medusa/src/api/store/product-types/[id]/route.ts @@ -1,5 +1,6 @@ import { StoreProductTypeResponse } from "@medusajs/framework/types" import { + applyTranslations, ContainerRegistrationKeys, MedusaError, } from "@medusajs/framework/utils" @@ -30,5 +31,14 @@ export const GET = async ( `Product type with id: ${req.params.id} was not found` ) } - res.json({ product_type: data[0] }) + + const productType = data[0] + + await applyTranslations({ + localeCode: req.locale, + objects: [productType], + container: req.scope, + }) + + res.json({ product_type: productType }) } diff --git a/packages/medusa/src/api/store/product-types/route.ts b/packages/medusa/src/api/store/product-types/route.ts index ab4d8462a0..250799c116 100644 --- a/packages/medusa/src/api/store/product-types/route.ts +++ b/packages/medusa/src/api/store/product-types/route.ts @@ -1,5 +1,8 @@ import { HttpTypes } from "@medusajs/framework/types" -import { ContainerRegistrationKeys } from "@medusajs/framework/utils" +import { + applyTranslations, + ContainerRegistrationKeys, +} from "@medusajs/framework/utils" import { AuthenticatedMedusaRequest, MedusaResponse, @@ -18,6 +21,12 @@ export const GET = async ( fields: req.queryConfig.fields, }) + await applyTranslations({ + localeCode: req.locale, + objects: product_types, + container: req.scope, + }) + res.json({ product_types, count: metadata?.count ?? 0, diff --git a/packages/medusa/src/api/store/product-variants/[id]/route.ts b/packages/medusa/src/api/store/product-variants/[id]/route.ts index 95d0238588..d196c6fda2 100644 --- a/packages/medusa/src/api/store/product-variants/[id]/route.ts +++ b/packages/medusa/src/api/store/product-variants/[id]/route.ts @@ -4,6 +4,7 @@ import { } from "@medusajs/framework/http" import { HttpTypes, QueryContextType } from "@medusajs/framework/types" import { + applyTranslations, ContainerRegistrationKeys, MedusaError, QueryContext, @@ -60,6 +61,12 @@ export const GET = async ( ) } + await applyTranslations({ + localeCode: req.locale, + objects: [variant], + container: req.scope, + }) + if (withInventoryQuantity) { await wrapVariantsWithInventoryQuantityForSalesChannel(req, [variant]) } diff --git a/packages/medusa/src/api/store/product-variants/route.ts b/packages/medusa/src/api/store/product-variants/route.ts index 60510fc5a4..28f171dfb0 100644 --- a/packages/medusa/src/api/store/product-variants/route.ts +++ b/packages/medusa/src/api/store/product-variants/route.ts @@ -4,6 +4,7 @@ import { } from "@medusajs/framework/http" import { HttpTypes, QueryContextType } from "@medusajs/framework/types" import { + applyTranslations, ContainerRegistrationKeys, QueryContext, } from "@medusajs/framework/utils" @@ -54,6 +55,12 @@ export const GET = async ( } ) + await applyTranslations({ + localeCode: req.locale, + objects: variants, + container: req.scope, + }) + if (withInventoryQuantity) { await wrapVariantsWithInventoryQuantityForSalesChannel(req, variants) } diff --git a/packages/medusa/src/api/store/return-reasons/[id]/route.ts b/packages/medusa/src/api/store/return-reasons/[id]/route.ts index 997d375005..10a82ccebd 100644 --- a/packages/medusa/src/api/store/return-reasons/[id]/route.ts +++ b/packages/medusa/src/api/store/return-reasons/[id]/route.ts @@ -1,10 +1,10 @@ +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { HttpTypes } from "@medusajs/framework/types" import { ContainerRegistrationKeys, remoteQueryObjectFromString, } from "@medusajs/framework/utils" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" import { StoreReturnReasonParamsType } from "../validators" -import { HttpTypes } from "@medusajs/framework/types" export const GET = async ( req: MedusaRequest, diff --git a/packages/medusa/src/api/store/return-reasons/route.ts b/packages/medusa/src/api/store/return-reasons/route.ts index 886562a3f0..d3db504b32 100644 --- a/packages/medusa/src/api/store/return-reasons/route.ts +++ b/packages/medusa/src/api/store/return-reasons/route.ts @@ -1,9 +1,9 @@ +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { HttpTypes } from "@medusajs/framework/types" import { ContainerRegistrationKeys, remoteQueryObjectFromString, } from "@medusajs/framework/utils" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" -import { HttpTypes } from "@medusajs/framework/types" export const GET = async ( req: MedusaRequest, diff --git a/packages/medusa/src/api/store/shipping-options/[id]/calculate/route.ts b/packages/medusa/src/api/store/shipping-options/[id]/calculate/route.ts index e56e23cf53..e9a10bd71f 100644 --- a/packages/medusa/src/api/store/shipping-options/[id]/calculate/route.ts +++ b/packages/medusa/src/api/store/shipping-options/[id]/calculate/route.ts @@ -1,6 +1,6 @@ -import { HttpTypes } from "@medusajs/framework/types" -import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" import { calculateShippingOptionsPricesWorkflow } from "@medusajs/core-flows" +import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" +import { HttpTypes } from "@medusajs/framework/types" import { ContainerRegistrationKeys } from "@medusajs/framework/utils" export const POST = async ( diff --git a/packages/medusa/src/api/store/shipping-options/route.ts b/packages/medusa/src/api/store/shipping-options/route.ts index afebe87cef..539b98ee9e 100644 --- a/packages/medusa/src/api/store/shipping-options/route.ts +++ b/packages/medusa/src/api/store/shipping-options/route.ts @@ -10,10 +10,10 @@ export const GET = async ( const workflow = listShippingOptionsForCartWorkflow(req.scope) const { result: shipping_options } = await workflow.run({ - input: { - cart_id, + input: { + cart_id, is_return: !!is_return, - fields: req.queryConfig.fields + fields: req.queryConfig.fields, }, }) diff --git a/packages/modules/link-modules/src/definitions/readonly/product-translation.ts b/packages/modules/link-modules/src/definitions/readonly/product-translation.ts index 8dcf419c93..5cd5fcf537 100644 --- a/packages/modules/link-modules/src/definitions/readonly/product-translation.ts +++ b/packages/modules/link-modules/src/definitions/readonly/product-translation.ts @@ -1,12 +1,8 @@ import { ModuleJoinerConfig } from "@medusajs/framework/types" -import { - FeatureFlag, - MEDUSA_SKIP_FILE, - Modules, -} from "@medusajs/framework/utils" +import { MEDUSA_SKIP_FILE, Modules } from "@medusajs/framework/utils" export const ProductTranslation: ModuleJoinerConfig = { - [MEDUSA_SKIP_FILE]: !FeatureFlag.isFeatureEnabled("translation"), + [MEDUSA_SKIP_FILE]: process.env.MEDUSA_FF_TRANSLATION !== "true", isLink: true, isReadOnlyLink: true, extends: [ diff --git a/packages/modules/translation/src/loaders/config.ts b/packages/modules/translation/src/loaders/config.ts index acde4737a9..8003d0c5a5 100644 --- a/packages/modules/translation/src/loaders/config.ts +++ b/packages/modules/translation/src/loaders/config.ts @@ -1,50 +1,9 @@ import { LoaderOptions } from "@medusajs/framework/types" -import { - PRODUCT_TRANSLATABLE_FIELDS, - PRODUCT_VARIANT_TRANSLATABLE_FIELDS, -} from "../utils/translatable-fields" -import { asValue } from "awilix" import { TRANSLATABLE_FIELDS_CONFIG_KEY } from "@utils/constants" +import { asValue } from "awilix" +import { translatableFieldsConfig } from "../utils/translatable-fields" -export default async ({ - container, - options, -}: LoaderOptions<{ - expandedTranslatableFields: { [key: string]: string[] } -}>): Promise => { - const { expandedTranslatableFields } = options ?? {} - - const { product, productVariant, ...others } = - expandedTranslatableFields ?? {} - - const translatableFieldsConfig: Record = { - product: PRODUCT_TRANSLATABLE_FIELDS, - product_variant: PRODUCT_VARIANT_TRANSLATABLE_FIELDS, - } - - if (product) { - const translatableFields = new Set([ - ...PRODUCT_TRANSLATABLE_FIELDS, - ...product, - ]) - translatableFieldsConfig.product = Array.from(translatableFields) - } - - if (productVariant) { - const translatableFields = new Set([ - ...PRODUCT_VARIANT_TRANSLATABLE_FIELDS, - ...productVariant, - ]) - translatableFieldsConfig.product_variant = Array.from(translatableFields) - } - - if (others) { - Object.entries(others).forEach(([key, value]) => { - const translatableFields = new Set([...value]) - translatableFieldsConfig[key] = Array.from(translatableFields) - }) - } - +export default async ({ container }: LoaderOptions): Promise => { container.register( TRANSLATABLE_FIELDS_CONFIG_KEY, asValue(translatableFieldsConfig) diff --git a/packages/modules/translation/src/utils/translatable-fields.ts b/packages/modules/translation/src/utils/translatable-fields.ts index 7276f8b26b..d0c787a691 100644 --- a/packages/modules/translation/src/utils/translatable-fields.ts +++ b/packages/modules/translation/src/utils/translatable-fields.ts @@ -5,5 +5,35 @@ export const PRODUCT_TRANSLATABLE_FIELDS = [ "subtitle", "status", ] - export const PRODUCT_VARIANT_TRANSLATABLE_FIELDS = ["title", "material"] +export const PRODUCT_TYPE_TRANSLATABLE_FIELDS = ["value"] +export const PRODUCT_COLLECTION_TRANSLATABLE_FIELDS = ["title"] +export const PRODUCT_CATEGORY_TRANSLATABLE_FIELDS = ["name", "description"] +export const PRODUCT_TAG_TRANSLATABLE_FIELDS = ["value"] +export const PRODUCT_OPTION_TRANSLATABLE_FIELDS = ["title"] +export const PRODUCT_OPTION_VALUE_TRANSLATABLE_FIELDS = ["value"] + +// export const SHIPPING_OPTION_TRANSLATABLE_FIELDS = ["name"] +// export const SHIPPING_OPTION_TYPE_TRANSLATABLE_FIELDS = ["label", "description"] + +// export const RETURN_REASON_TRANSLATABLE_FIELDS = [ +// "value", +// "label", +// "description", +// ] + +export const translatableFieldsConfig = { + product: PRODUCT_TRANSLATABLE_FIELDS, + product_variant: PRODUCT_VARIANT_TRANSLATABLE_FIELDS, + product_type: PRODUCT_TYPE_TRANSLATABLE_FIELDS, + product_collection: PRODUCT_COLLECTION_TRANSLATABLE_FIELDS, + product_category: PRODUCT_CATEGORY_TRANSLATABLE_FIELDS, + product_tag: PRODUCT_TAG_TRANSLATABLE_FIELDS, + product_option: PRODUCT_OPTION_TRANSLATABLE_FIELDS, + product_option_value: PRODUCT_OPTION_VALUE_TRANSLATABLE_FIELDS, + + // shipping_option: SHIPPING_OPTION_TRANSLATABLE_FIELDS, + // shipping_option_type: SHIPPING_OPTION_TYPE_TRANSLATABLE_FIELDS, + + // return_reason: RETURN_REASON_TRANSLATABLE_FIELDS, +}