feat(): Introduce translation module and preliminary application of them (#14189)

* feat(): Translation first steps

* feat(): locale middleware

* feat(): readonly links

* feat(): feature flag

* feat(): modules sdk

* feat(): translation module re export

* start adding workflows

* update typings

* update typings

* test(): Add integration tests

* test(): centralize filters preparation

* test(): centralize filters preparation

* remove unnecessary importy

* fix workflows

* Define StoreLocale inside Store Module

* Link definition to extend Store with supported_locales

* store_locale migration

* Add supported_locales handling in Store Module

* Tests

* Accept supported_locales in Store endpoints

* Add locales to js-sdk

* Include locale list and default locale in Store Detail section

* Initialize local namespace in js-sdk

* Add locales route

* Make code primary key of locale table to facilitate upserts

* Add locales routes

* Show locale code as is

* Add list translations api route

* Batch endpoint

* Types

* New batchTranslationsWorkflow and various updates to existent ones

* Edit default locale UI

* WIP

* Apply translation agnostically

* middleware

* Apply translation agnostically

* fix Apply translation agnostically

* apply translations to product list

* Add feature flag

* fetch translations by batches of 250 max

* fix apply

* improve and test util

* apply to product list

* dont manage translations if no locale

* normalize locale

* potential todo

* Protect translations routes with feature flag

* Extract normalize locale util to core/utils

* Normalize locale on write

* Normalize locale for read

* Use feature flag to guard translations UI across the board

* Avoid throwing incorrectly when locale_code not present in partial updates

* move applyTranslations util

* remove old tests

* fix util tests

* fix(): product end points

* cleanup

* update lock

* remove unused var

* cleanup

* fix apply locale

* missing new dep for test utils

* Change entity_type, entity_id to reference, reference_id

* Remove comment

* Avoid registering translations route if ff not enabled

* Prevent registering express handler for disabled route via defineFileConfig

* Add tests

* Add changeset

* Update test

* fix integration tests, module and internals

* Add locale id plus fixed

* Allow to pass array of reference_id

* fix unit tests

* fix link loading

* fix store route

* fix sales channel test

* fix tests

---------

Co-authored-by: Nicolas Gorga <nicogorga11@gmail.com>
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Adrien de Peretti
2025-12-08 19:33:08 +01:00
committed by GitHub
parent fea3d4ec49
commit 6dc0b8bed8
130 changed files with 5649 additions and 112 deletions

View File

@@ -8,3 +8,7 @@ defineFileConfig({
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
res.json({ message: "Custom GET" })
}
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
res.json({ message: "Custom POST", body: req.validatedBody })
}

View File

@@ -0,0 +1,19 @@
import {
defineMiddlewares,
validateAndTransformBody,
} from "@medusajs/framework/http"
import { z } from "zod"
const CustomPostSchema = z.object({
foo: z.string(),
})
export default defineMiddlewares({
routes: [
{
method: ["POST"],
matcher: "/custom",
middlewares: [validateAndTransformBody(CustomPostSchema)],
},
],
})

View File

@@ -43,6 +43,23 @@ medusaIntegrationTestRunner({
it("should load endpoint when feature flag is enabled", async () => {
expect((await api.get("/custom")).status).toBe(200)
expect(
(
await api.post("/custom", {
foo: "test",
})
).status
).toBe(200)
})
it("should return 400 for POST route with invalid body when feature flag is enabled", async () => {
const response = await api
.post("/custom", {
invalid: 1,
})
.catch((e) => e)
expect(response.status).toBe(400)
})
})
},

View File

@@ -41,6 +41,16 @@ medusaIntegrationTestRunner({
it("should not load endpoint when feature flag is disabled", async () => {
expect(api.get("/custom")).rejects.toThrow()
})
it("should return 404 (not 400) for POST route with middleware when feature flag is disabled", async () => {
const { response } = await api
.post("/custom", {
invalid: "test",
})
.catch((e) => e)
expect(response.status).toBe(404)
})
})
},
})

View File

@@ -207,13 +207,20 @@ medusaIntegrationTestRunner({
})
)
expect(remainingPricePreferences).toEqual([
expect.objectContaining({
attribute: "currency_code",
value: "EUR",
is_tax_inclusive: true,
}),
])
expect(remainingPricePreferences).toEqual(
expect.arrayContaining([
expect.objectContaining({
attribute: "currency_code",
value: "EUR",
is_tax_inclusive: true,
}),
expect.objectContaining({
attribute: "currency_code",
value: "eur",
is_tax_inclusive: false,
}),
])
)
})
})
})

View File

@@ -67,7 +67,7 @@ medusaIntegrationTestRunner({
expect(response.status).toEqual(200)
expect(response.data.sales_channels).toBeTruthy()
expect(response.data.sales_channels.length).toBe(2)
expect(response.data.sales_channels.length).toBe(3) // includes the default sales channel
expect(response.data).toEqual(
expect.objectContaining({
sales_channels: expect.arrayContaining([

View File

@@ -31,6 +31,7 @@
"@medusajs/store": "workspace:^",
"@medusajs/tax": "workspace:^",
"@medusajs/test-utils": "workspace:*",
"@medusajs/translation": "workspace:*",
"@medusajs/user": "workspace:^",
"@medusajs/utils": "workspace:^",
"@medusajs/workflow-engine-inmemory": "workspace:*",