docs: docs for next release (#14456)

This commit is contained in:
Shahed Nasser
2026-01-06 17:40:26 +02:00
committed by GitHub
parent a960fb75c9
commit a2210ea5e7
19 changed files with 1434 additions and 64 deletions
+4 -4
View File
@@ -1497,9 +1497,9 @@ Refer to [this guide](!docs!/learn/customization/extend-features/extend-create-p
]}
/>
Medusa's Store APIs support localization. Currently, only product-related information can be localized, such as product titles and descriptions.
Medusa's Store APIs support localization of core and custom data models. Refer to the [Translation Module](!resources!/commerce-modules/translation) guide to learn about supported core data models, and how to [configure translations for custom data models](!resources!/commerce-modules/translation/custom-data-models).
By default, the Store APIs return data in the default locale configured in your Medusa application, and falls back to the original data of a resource.
By default, the Store APIs returns the original data without localization.
</DividedMarkdownContent>
@@ -1512,8 +1512,8 @@ By default, the Store APIs return data in the default locale configured in your
You can set the locale of a request using one of the following methods:
- Use the JS SDK's `setLocale` method. The JS SDK will automatically include the locale in the `x-medusa-locale` header of subsequent requests.
- Pass the `locale` query parameter to the [List Products API route](#products_getproducts).
- Set the `x-medusa-locale` header in the API request to the [List Products API route](#products_getproducts).
- Pass the `locale` query parameter to the relevant API route. By default, all `/store` API routes support the `locale` query parameter.
- Set the `x-medusa-locale` header in the API request to the relevant API route. By default, all `/store` API routes support the `x-medusa-locale` header.
Locales are in the [IETF BCP 47](https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1) format, such as `en-US` for American English, or `fr-FR` for French.
@@ -0,0 +1,211 @@
import { CodeTabs, CodeTab, Prerequisites } from "docs-ui"
export const metadata = {
title: `${pageNumber} Localization in API Routes`,
keywords: ["translation api-routes"]
}
# {metadata.title}
In this chapter, you'll learn how to handle localization in API routes of your Medusa application to serve content in different languages.
<Prerequisites
items={[
{
text: "Medusa v2.12.4 or later",
link: "https://github.com/medusajs/medusa/releases/tag/v2.12.4"
},
{
text: "Translation Module Configured",
link: "!resources!/commerce-modules/translation#configure-translation-module",
},
]}
/>
## Overview
Localization in API routes allows you to serve translated content based on the user's preferred language. The Medusa application provides built-in support for handling locale information in API requests and retrieving localized data.
When a locale is specified in a request, you can use it to retrieve translated versions of your data models' fields, providing a seamless multilingual experience for your users.
Learn more about translation, how to manage translations, and how to translate custom data models in the [Translation Module documentation](!resources!/commerce-modules/translation).
---
## Routes with Localization Enabled by Default
The Medusa application automatically supports retrieving localized content from all routes under the `/store` prefix, including both core and custom store API routes.
For example, the following store routes have localization enabled by default:
- `/store/products` -> Get products with translated fields
- `/store/collections` -> Get collections with translated fields
- `/store/categories` -> Get categories with translated fields
Refer to the [Translation Module](!resources!/commerce-modules/translation#supported-module-translations) documentation for a list of supported core data models with localization support.
### Apply Localization to Custom Routes
If you're creating custom API routes outside the `/store` prefix, you must manually apply the `applyLocale` [middleware](../middlewares/page.mdx) to enable localization support.
To apply the `applyLocale` middleware to all HTTP methods for a route, add it to the `src/api/middlewares.ts` file:
export const allMethodsHighlights = [
["7", "applyLocale", "Apply the middleware to the route"],
]
```ts title="src/api/middlewares.ts" highlights={allMethodsHighlights}
import { applyLocale, defineMiddlewares } from "@medusajs/framework/http"
export default defineMiddlewares({
routes: [
{
matcher: "/custom*",
middlewares: [applyLocale],
},
],
})
```
This applies the `applyLocale` middleware to all routes matching `/custom*`, regardless of the HTTP method.
Alternatively, you can apply the middleware only to specific HTTP methods using the `method` property:
export const specificMethodHighlights = [
["7", "method", "Apply the middleware only to `GET` requests"],
]
```ts title="src/api/middlewares.ts" highlights={specificMethodHighlights}
import { applyLocale, defineMiddlewares } from "@medusajs/framework/http"
export default defineMiddlewares({
routes: [
{
matcher: "/custom*",
method: ["GET"],
middlewares: [applyLocale],
},
],
})
```
<Note title="Tip">
Learn more about middlewares in the [Middlewares](../middlewares/page.mdx) chapter.
</Note>
---
## How to Pass Locale in API Requests
You can pass the locale in API requests to routes that support localization using either of the following methods:
1. The `locale` query parameter
2. The `x-medusa-locale` request header
The query parameter takes priority over the header if both are provided.
The locale must follow the [IETF BCP 47 standard](https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1), such as `en-US` for English (United States) or `fr-FR` for French (France).
<Note title="Using the JS SDK?">
Refer to the [JS SDK reference](!resources!/js-sdk#localization-with-js-sdk) for details on how to pass locale.
</Note>
For example:
<CodeTabs group="localization">
<CodeTab label="Query Parameter" value="query-param">
```bash
curl "http://localhost:9000/store/products?locale=fr-FR" \
-H 'x-publishable-api-key: {your_publishable_api_key}'
```
</CodeTab>
<CodeTab label="Header" value="header">
```bash
curl "http://localhost:9000/store/products" \
-H 'x-publishable-api-key: {your_publishable_api_key}' \
-H 'x-medusa-locale: fr-FR'
```
</CodeTab>
</CodeTabs>
The above examples retrieve products with their fields translated to French (France) if translations are available. If no translations exist for the requested locale, the original content stored in the data model is returned.
<Note>
Store API routes require a publishable API key in the request header. Learn more in the [Store API reference](!api!/store#publishable-api-key).
</Note>
---
## Access Request Locale in API Routes
After applying the `applyLocale` middleware, you can access the request's locale from the `locale` property of the `MedusaRequest` object.
For example:
export const accessLocaleHighlights = [
["10", "req.locale", "Access the request's locale"],
]
```ts title="src/api/custom/route.ts" highlights={accessLocaleHighlights}
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
export const GET = async (
req: MedusaRequest,
res: MedusaResponse
) => {
const locale = req.locale
// use locale to retrieve localized data...
}
```
The `req.locale` property contains the locale value from either the query parameter or the request header. If no locale is specified in the request, `req.locale` is `undefined`.
### Retrieve Localized Data with Query
To retrieve data models with translated fields, pass the `locale` option to [Query](/learn/fundamentals/module-links/query) when querying your data.
For example, to retrieve products with translated names and descriptions:
export const queryHighlights = [
["10", "locale", "Pass the request locale to retrieve localized data"],
]
```ts title="src/api/store/products/route.ts" highlights={queryHighlights}
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const query = req.scope.resolve("query")
const { data: products } = await query.graph({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: req.locale,
},
})
res.json({ products })
}
```
In this example, the products are retrieved with their `title` and `description` fields translated to the locale specified in the request.
Learn more in the [Query](../../module-links/query/page.mdx#retrieve-localized-data) chapter.
### Retrieve Localized Data for Custom Models
You can also retrieve localized data for custom data models. Learn more in the [Translate Custom Data Models](!resources!/commerce-modules/translation/custom-data-models) guide.
@@ -200,43 +200,43 @@ const Post = model.define("post", {
id: model.id().primaryKey(),
title: model.text(),
author: model.belongsTo(() => Author, {
mappedBy: "posts"
})
mappedBy: "posts",
}),
})
const Author = model.define("author", {
id: model.id().primaryKey(),
name: model.text(),
posts: model.hasMany(() => Post, {
mappedBy: "author"
})
mappedBy: "author",
}),
})
```
To create a migration that reflects this relationship in the database, you can create a migration file as follows:
```ts title="src/modules/blog/migrations/Migration202507021200_create_author_and_post.ts"
import { Migration } from "@medusajs/framework/mikro-orm/migrations";
import { Migration } from "@medusajs/framework/mikro-orm/migrations"
export class Migration20251230112505 extends Migration {
override async up(): Promise<void> {
this.addSql(`create table if not exists "author" ("id" text not null, "name" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "author_pkey" primary key ("id"));`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_author_deleted_at" ON "author" ("deleted_at") WHERE deleted_at IS NULL;`);
this.addSql(`create table if not exists "author" ("id" text not null, "name" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "author_pkey" primary key ("id"));`)
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_author_deleted_at" ON "author" ("deleted_at") WHERE deleted_at IS NULL;`)
this.addSql(`create table if not exists "post" ("id" text not null, "title" text not null, "author_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "post_pkey" primary key ("id"));`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_author_id" ON "post" ("author_id") WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_deleted_at" ON "post" ("deleted_at") WHERE deleted_at IS NULL;`);
this.addSql(`create table if not exists "post" ("id" text not null, "title" text not null, "author_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "post_pkey" primary key ("id"));`)
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_author_id" ON "post" ("author_id") WHERE deleted_at IS NULL;`)
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_deleted_at" ON "post" ("deleted_at") WHERE deleted_at IS NULL;`)
this.addSql(`alter table if exists "post" add constraint "post_author_id_foreign" foreign key ("author_id") references "author" ("id") on update cascade;`);
this.addSql(`alter table if exists "post" add constraint "post_author_id_foreign" foreign key ("author_id") references "author" ("id") on update cascade;`)
}
override async down(): Promise<void> {
this.addSql(`alter table if exists "post" drop constraint if exists "post_author_id_foreign";`);
this.addSql(`alter table if exists "post" drop constraint if exists "post_author_id_foreign";`)
this.addSql(`drop table if exists "author" cascade;`);
this.addSql(`drop table if exists "author" cascade;`)
this.addSql(`drop table if exists "post" cascade;`);
this.addSql(`drop table if exists "post" cascade;`)
}
}
@@ -251,16 +251,16 @@ Consider you have an existing `Post` data model, and you added a new `published_
You can create a migration file to add this new column as follows:
```ts title="src/modules/blog/migrations/Migration202507021300_add_published_at_to_post.ts"
import { Migration } from "@medusajs/framework/mikro-orm/migrations";
import { Migration } from "@medusajs/framework/mikro-orm/migrations"
export class Migration20251230113025 extends Migration {
override async up(): Promise<void> {
this.addSql(`alter table if exists "post" add column if not exists "published_at" timestamptz null;`);
this.addSql(`alter table if exists "post" add column if not exists "published_at" timestamptz null;`)
}
override async down(): Promise<void> {
this.addSql(`alter table if exists "post" drop column if exists "published_at";`);
this.addSql(`alter table if exists "post" drop column if exists "published_at";`)
}
}
@@ -275,16 +275,16 @@ Consider you have an existing `Post` data model, and you removed the `published_
You can create a migration file to remove this column as follows:
```ts title="src/modules/blog/migrations/Migration202507021400_remove_published_at_from_post.ts"
import { Migration } from "@medusajs/framework/mikro-orm/migrations";
import { Migration } from "@medusajs/framework/mikro-orm/migrations"
export class Migration20251230113125 extends Migration {
override async up(): Promise<void> {
this.addSql(`alter table if exists "post" drop column if exists "published_at";`);
this.addSql(`alter table if exists "post" drop column if exists "published_at";`)
}
override async down(): Promise<void> {
this.addSql(`alter table if exists "post" add column if not exists "published_at" timestamptz null;`);
this.addSql(`alter table if exists "post" add column if not exists "published_at" timestamptz null;`)
}
}
@@ -299,16 +299,16 @@ Consider you have an existing `Post` data model with a `title` column, and you r
You can create a migration file to rename this column as follows:
```ts title="src/modules/blog/migrations/Migration202507021500_rename_title_to_headline_in_post.ts"
import { Migration } from "@medusajs/framework/mikro-orm/migrations";
import { Migration } from "@medusajs/framework/mikro-orm/migrations"
export class Migration20251230113214 extends Migration {
override async up(): Promise<void> {
this.addSql(`alter table if exists "post" rename column "title" to "headline";`);
this.addSql(`alter table if exists "post" rename column "title" to "headline";`)
}
override async down(): Promise<void> {
this.addSql(`alter table if exists "post" rename column "headline" to "title";`);
this.addSql(`alter table if exists "post" rename column "headline" to "title";`)
}
}
@@ -332,24 +332,24 @@ export const Post = model.define("post", {
id: model.id().primaryKey(),
headline: model.text().index(),
author: model.belongsTo(() => Author, {
mappedBy: "posts"
})
mappedBy: "posts",
}),
})
```
You can create a migration file to create this index as follows:
```ts title="src/modules/blog/migrations/Migration202507021600_create_index_on_headline_in_post.ts"
import { Migration } from "@medusajs/framework/mikro-orm/migrations";
import { Migration } from "@medusajs/framework/mikro-orm/migrations"
export class Migration20251230113322 extends Migration {
override async up(): Promise<void> {
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_headline" ON "post" ("headline") WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_headline" ON "post" ("headline") WHERE deleted_at IS NULL;`)
}
override async down(): Promise<void> {
this.addSql(`drop index if exists "IDX_post_headline";`);
this.addSql(`drop index if exists "IDX_post_headline";`)
}
}
@@ -364,16 +364,16 @@ Consider you have an existing `Post` data model with an index on the `headline`
You can create a migration file to drop this index as follows:
```ts title="src/modules/blog/migrations/Migration202507021700_drop_index_on_headline_in_post.ts"
import { Migration } from "@medusajs/framework/mikro-orm/migrations";
import { Migration } from "@medusajs/framework/mikro-orm/migrations"
export class Migration20251230113350 extends Migration {
override async up(): Promise<void> {
this.addSql(`drop index if exists "IDX_post_headline";`);
this.addSql(`drop index if exists "IDX_post_headline";`)
}
override async down(): Promise<void> {
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_headline" ON "post" ("headline") WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_post_headline" ON "post" ("headline") WHERE deleted_at IS NULL;`)
}
}
@@ -23,13 +23,35 @@ Medusa automatically generates TypeScript types for:
## How to Trigger Type Generation?
The Medusa application generates these types automatically when you run the application with the `dev` command:
<Note>
As of [Medusa v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4), types are generated when you run the `build` command. Prior versions only generated types when running the `dev` command.
</Note>
The Medusa application generates these types automatically when you run the `build` or `dev` commands:
```bash npm2yarn
npm run build
```
So, if you add a new data model or module and you don't find it in auto-completion or type checking, you can run the `build` command to regenerate the types.
### How to Generate Types for Local Plugins?
<Note>
This feature is available as of [Medusa v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4).
</Note>
Local plugins are plugins installed in your Medusa application with the `plugin:develop` command. To generate types for those plugins, run the `dev` command in the Medusa application:
```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green"
npm run dev
```
So, if you add a new data model or module and you don't find it in auto-completion or type checking, you can run the `dev` command to regenerate the types.
Medusa will copy the generated types under the `.medusa/types` directory of the application to the local plugin's directory.
---
@@ -840,3 +840,36 @@ export const retrieveBrandsWorkflow = createWorkflow(
```
This will retrieve all brands that are linked to at least one product.
### Retrieve Localized Data
<Prerequisites
items={[
{
text: "Medusa v2.12.4 or later",
link: "https://github.com/medusajs/medusa/releases/tag/v2.12.4"
},
{
text: "Translation Module Configured",
link: "!resources!/commerce-modules/translation#configure-translation-module",
},
]}
/>
To retrieve localized data for data models that have translations, pass an `options.locale` property to the first parameter of the `query.index` method:
```ts highlights={[["5", "locale", "Pass the locale to retrieve localized data."]]}
const { data: products } = await query.index({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: "fr-FR",
},
})
```
The `options.locale` property is a string representing the locale code following the [IETF BCP 47 standard](https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1).
The returned products will have their `title` and `description` properties in French (`fr-FR`), if translations are available.
Learn more in the [Translation Module](!resources!/commerce-modules/translation) documentation.
@@ -854,6 +854,60 @@ In the example above, you retrieve only deleted posts by enabling the `withDelet
---
## Retrieve Localized Data
<Prerequisites
items={[
{
text: "Medusa v2.12.4 or later",
link: "https://github.com/medusajs/medusa/releases/tag/v2.12.4"
},
{
text: "Translation Module Configured",
link: "!resources!/commerce-modules/translation#configure-translation-module",
},
]}
/>
To retrieve localized data for data models that have translations, pass an `options.locale` property to the first parameter of the `query.graph` method.
<CodeTabs group="query">
<CodeTab label="query.graph" value="query.graph">
```ts highlights={[["5", "locale", "Pass the locale to retrieve localized data."]]}
const { data: products } = await query.graph({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: "fr-FR",
},
})
```
</CodeTab>
<CodeTab label="useQueryGraphStep" value="useQueryGraphStep">
```ts highlights={[["5", "locale", "Pass the locale to retrieve localized data."]]}
const { data: products } = useQueryGraphStep({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: "fr-FR",
},
})
```
</CodeTab>
</CodeTabs>
The `options.locale` property is a string representing the locale code following the [IETF BCP 47 standard](https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1).
The returned products will have their `title` and `description` properties in French (`fr-FR`), if translations are available.
Learn more in the [Translation Module](!resources!/commerce-modules/translation) documentation.
---
## Configure Query to Throw Error
By default, if Query doesn't find records matching your query, it returns an empty array. You can configure Query to throw an error when no records are found.
+5 -4
View File
@@ -65,7 +65,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/module-links/custom-columns/page.mdx": "2025-12-09T13:27:05.446Z",
"app/learn/fundamentals/module-links/directions/page.mdx": "2025-03-17T12:52:06.161Z",
"app/learn/fundamentals/module-links/page.mdx": "2025-11-28T13:45:06.742Z",
"app/learn/fundamentals/module-links/query/page.mdx": "2025-10-27T09:30:26.957Z",
"app/learn/fundamentals/module-links/query/page.mdx": "2026-01-06T10:34:40.077Z",
"app/learn/fundamentals/modules/db-operations/page.mdx": "2025-10-28T16:02:06.265Z",
"app/learn/fundamentals/modules/multiple-services/page.mdx": "2025-03-18T15:11:44.632Z",
"app/learn/fundamentals/modules/page.mdx": "2025-12-23T07:00:14.586Z",
@@ -121,10 +121,10 @@ export const generatedEditDates = {
"app/learn/fundamentals/workflows/errors/page.mdx": "2025-04-25T14:26:25.000Z",
"app/learn/fundamentals/api-routes/override/page.mdx": "2025-12-22T12:56:06.558Z",
"app/learn/fundamentals/module-links/index/page.mdx": "2025-05-23T07:57:58.958Z",
"app/learn/fundamentals/module-links/index-module/page.mdx": "2025-11-26T11:20:25.961Z",
"app/learn/fundamentals/module-links/index-module/page.mdx": "2026-01-06T10:34:35.671Z",
"app/learn/introduction/build-with-llms-ai/page.mdx": "2025-12-09T14:17:13.295Z",
"app/learn/installation/docker/page.mdx": "2025-12-18T11:18:25.856Z",
"app/learn/fundamentals/generated-types/page.mdx": "2025-07-25T13:17:35.319Z",
"app/learn/fundamentals/generated-types/page.mdx": "2026-01-06T06:38:15.719Z",
"app/learn/introduction/from-v1-to-v2/page.mdx": "2025-10-29T11:55:11.531Z",
"app/learn/debugging-and-testing/debug-workflows/page.mdx": "2025-07-30T13:45:14.117Z",
"app/learn/fundamentals/data-models/json-properties/page.mdx": "2025-07-31T14:25:01.268Z",
@@ -137,5 +137,6 @@ export const generatedEditDates = {
"app/learn/codemods/replace-imports/page.mdx": "2025-10-09T11:37:44.754Z",
"app/learn/fundamentals/admin/translations/page.mdx": "2025-12-16T13:56:21.400Z",
"app/learn/configurations/medusa-config/asymmetric-encryption/page.mdx": "2025-10-31T09:53:38.607Z",
"app/learn/best-practices/third-party-sync/page.mdx": "2025-12-03T11:48:58.209Z"
"app/learn/best-practices/third-party-sync/page.mdx": "2025-12-03T11:48:58.209Z",
"app/learn/fundamentals/api-routes/localization/page.mdx": "2026-01-06T10:34:44.853Z"
}
+12 -2
View File
@@ -694,6 +694,16 @@ export const generatedSidebars = [
"chapterTitle": "3.6.11. Retrieve Custom Links",
"number": "3.6.11."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/api-routes/localization",
"title": "Localization",
"children": [],
"chapterTitle": "3.6.12. Localization",
"number": "3.6.12."
},
{
"loaded": true,
"isPathHref": true,
@@ -701,8 +711,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/api-routes/override",
"title": "Override API Routes",
"children": [],
"chapterTitle": "3.6.12. Override API Routes",
"number": "3.6.12."
"chapterTitle": "3.6.13. Override API Routes",
"number": "3.6.13."
}
],
"chapterTitle": "3.6. API Routes",
+563 -8
View File
@@ -10390,6 +10390,169 @@ This file adds two API Routes:
- A `POST` API route at `http://localhost:9000/hello-world`.
# Localization in API Routes
In this chapter, you'll learn how to handle localization in API routes of your Medusa application to serve content in different languages.
### Prerequisites
- [Medusa v2.12.4 or later](https://github.com/medusajs/medusa/releases/tag/v2.12.4)
- [Translation Module Configured](https://docs.medusajs.com/resources/commerce-modules/translation#configure-translation-module/index.html.md)
## Overview
Localization in API routes allows you to serve translated content based on the user's preferred language. The Medusa application provides built-in support for handling locale information in API requests and retrieving localized data.
When a locale is specified in a request, you can use it to retrieve translated versions of your data models' fields, providing a seamless multilingual experience for your users.
Learn more about translation, how to manage translations, and how to translate custom data models in the [Translation Module documentation](https://docs.medusajs.com/resources/commerce-modules/translation/index.html.md).
***
## Routes with Localization Enabled by Default
The Medusa application automatically supports retrieving localized content from all routes under the `/store` prefix, including both core and custom store API routes.
For example, the following store routes have localization enabled by default:
- `/store/products` -> Get products with translated fields
- `/store/collections` -> Get collections with translated fields
- `/store/categories` -> Get categories with translated fields
### Apply Localization to Custom Routes
If you're creating custom API routes outside the `/store` prefix, you must manually apply the `applyLocale` [middleware](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md) to enable localization support.
To apply the `applyLocale` middleware to all HTTP methods for a route, add it to the `src/api/middlewares.ts` file:
```ts title="src/api/middlewares.ts" highlights={allMethodsHighlights}
import { applyLocale, defineMiddlewares } from "@medusajs/framework/http"
export default defineMiddlewares({
routes: [
{
matcher: "/custom*",
middlewares: [applyLocale],
},
],
})
```
This applies the `applyLocale` middleware to all routes matching `/custom*`, regardless of the HTTP method.
Alternatively, you can apply the middleware only to specific HTTP methods using the `method` property:
```ts title="src/api/middlewares.ts" highlights={specificMethodHighlights}
import { applyLocale, defineMiddlewares } from "@medusajs/framework/http"
export default defineMiddlewares({
routes: [
{
matcher: "/custom*",
method: ["GET"],
middlewares: [applyLocale],
},
],
})
```
Learn more about middlewares in the [Middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md) chapter.
***
## How to Pass Locale in API Requests
You can pass the locale in API requests to routes that support localization using either of the following methods:
1. The `locale` query parameter
2. The `x-medusa-locale` request header
The query parameter takes priority over the header if both are provided.
The locale must follow the [IETF BCP 47 standard](https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1), such as `en-US` for English (United States) or `fr-FR` for French (France).
Refer to the [JS SDK reference](https://docs.medusajs.com/resources/js-sdk#localization-with-js-sdk/index.html.md) for details on how to pass locale.
For example:
### Query Parameter
```bash
curl "http://localhost:9000/store/products?locale=fr-FR" \
-H 'x-publishable-api-key: {your_publishable_api_key}'
```
### Header
```bash
curl "http://localhost:9000/store/products" \
-H 'x-publishable-api-key: {your_publishable_api_key}' \
-H 'x-medusa-locale: fr-FR'
```
The above examples retrieve products with their fields translated to French (France) if translations are available. If no translations exist for the requested locale, the original content stored in the data model is returned.
Store API routes require a publishable API key in the request header. Learn more in the [Store API reference](https://docs.medusajs.com/api/store#publishable-api-key).
***
## Access Request Locale in API Routes
After applying the `applyLocale` middleware, you can access the request's locale from the `locale` property of the `MedusaRequest` object.
For example:
```ts title="src/api/custom/route.ts" highlights={accessLocaleHighlights}
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
export const GET = async (
req: MedusaRequest,
res: MedusaResponse
) => {
const locale = req.locale
// use locale to retrieve localized data...
}
```
The `req.locale` property contains the locale value from either the query parameter or the request header. If no locale is specified in the request, `req.locale` is `undefined`.
### Retrieve Localized Data with Query
To retrieve data models with translated fields, pass the `locale` option to [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) when querying your data.
For example, to retrieve products with translated names and descriptions:
```ts title="src/api/store/products/route.ts" highlights={queryHighlights}
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const query = req.scope.resolve("query")
const { data: products } = await query.graph({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: req.locale,
},
})
res.json({ products })
}
```
In this example, the products are retrieved with their `title` and `description` fields translated to the locale specified in the request.
Learn more in the [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query#retrieve-localized-data/index.html.md) chapter.
### Retrieve Localized Data for Custom Models
You can also retrieve localized data for custom data models. Learn more in the [Translate Custom Data Models](https://docs.medusajs.com/resources/commerce-modules/translation/custom-data-models/index.html.md) guide.
# Middlewares
In this chapter, youll learn about middlewares and how to create them.
@@ -15405,13 +15568,27 @@ Medusa automatically generates TypeScript types for:
## How to Trigger Type Generation?
The Medusa application generates these types automatically when you run the application with the `dev` command:
As of [Medusa v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4), types are generated when you run the `build` command. Prior versions only generated types when running the `dev` command.
The Medusa application generates these types automatically when you run the `build` or `dev` commands:
```bash npm2yarn
npm run build
```
So, if you add a new data model or module and you don't find it in auto-completion or type checking, you can run the `build` command to regenerate the types.
### How to Generate Types for Local Plugins?
This feature is available as of [Medusa v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4).
Local plugins are plugins installed in your Medusa application with the `plugin:develop` command. To generate types for those plugins, run the `dev` command in the Medusa application:
```bash npm2yarn badgeLabel="Medusa Application" badgeColor="green"
npm run dev
```
So, if you add a new data model or module and you don't find it in auto-completion or type checking, you can run the `dev` command to regenerate the types.
Medusa will copy the generated types under the `.medusa/types` directory of the application to the local plugin's directory.
***
@@ -16715,6 +16892,31 @@ export const retrieveBrandsWorkflow = createWorkflow(
This will retrieve all brands that are linked to at least one product.
### Retrieve Localized Data
### Prerequisites
- [Medusa v2.12.4 or later](https://github.com/medusajs/medusa/releases/tag/v2.12.4)
- [Translation Module Configured](https://docs.medusajs.com/resources/commerce-modules/translation#configure-translation-module/index.html.md)
To retrieve localized data for data models that have translations, pass an `options.locale` property to the first parameter of the `query.index` method:
```ts highlights={[["5", "options", "Pass the locale to retrieve localized data."]]}
const { data: products } = await query.index({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: "fr-FR",
},
})
```
The `options.locale` property is a string representing the locale code following the [IETF BCP 47 standard](https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1).
The returned products will have their `title` and `description` properties in French (`fr-FR`), if translations are available.
Learn more in the [Translation Module](https://docs.medusajs.com/resources/commerce-modules/translation/index.html.md) documentation.
# Link
@@ -18284,6 +18486,47 @@ In the example above, you retrieve only deleted posts by enabling the `withDelet
***
## Retrieve Localized Data
### Prerequisites
- [Medusa v2.12.4 or later](https://github.com/medusajs/medusa/releases/tag/v2.12.4)
- [Translation Module Configured](https://docs.medusajs.com/resources/commerce-modules/translation#configure-translation-module/index.html.md)
To retrieve localized data for data models that have translations, pass an `options.locale` property to the first parameter of the `query.graph` method.
### query.graph
```ts highlights={[["5", "options", "Pass the locale to retrieve localized data."]]}
const { data: products } = await query.graph({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: "fr-FR",
},
})
```
### useQueryGraphStep
```ts highlights={[["5", "options", "Pass the locale to retrieve localized data."]]}
const { data: products } = useQueryGraphStep({
entity: "product",
fields: ["id", "title", "description"],
options: {
locale: "fr-FR",
},
})
```
The `options.locale` property is a string representing the locale code following the [IETF BCP 47 standard](https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1).
The returned products will have their `title` and `description` properties in French (`fr-FR`), if translations are available.
Learn more in the [Translation Module](https://docs.medusajs.com/resources/commerce-modules/translation/index.html.md) documentation.
***
## Configure Query to Throw Error
By default, if Query doesn't find records matching your query, it returns an empty array. You can configure Query to throw an error when no records are found.
@@ -43176,6 +43419,301 @@ console.log(translations)
```
# Translate Custom Data Models
In this chapter, you'll learn how to support translations for your custom data models using the Translation Module.
### Prerequisites
- [Medusa v2.12.4 or later](https://github.com/medusajs/medusa/releases/tag/v2.12.4)
- [Translation Module Configured](https://docs.medusajs.com/commerce-modules/translation#configure-translation-module/index.html.md)
## Summary
The Translation Module allows you to extend translation capabilities to custom data models in your Medusa application. Then, you can [manage translations from the Medusa Admin](https://docs.medusajs.com/user-guide/settings/translations/index.html.md), and serve translated resources in your configured locales.
By following this guide, you'll learn how to:
- Configure the Translation Module to support translations for your custom data models.
- Manage translations for your custom data models from the Medusa Admin.
- Serve translated resources in your configured locales.
***
## Prerequisites: Custom Data Model
This guide assumes you already have a custom [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) with a data model. The guide will use a Blog Module with the following `Post` data model as an example:
```ts title="src/modules/blog/models/post.ts"
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
id: model.id().primaryKey(),
title: model.text(),
})
export default Post
```
The module must also be registered in `medusa-config.ts`. For example:
```ts title="medusa-config.ts"
module.exports = defineConfig({
// ...
modules: [
// other modules...
{
resolve: "./src/modules/blog",
},
],
})
```
***
## Step 1: Configure Translatable Entities
The first step is to configure the Translation Module with the custom entities you want to support translations for.
Before proceeding, run the `build` command to ensure the generated types are up-to-date:
```bash npm2yarn
npm run build
```
This will allow you to benefit from auto-completion when configuring the Translation Module.
Next, in `medusa-config.ts`, add the `options.entities` property to the Translation Module configuration:
```ts title="medusa-config.ts" highlights={configHighlights}
module.exports = defineConfig({
// ...
modules: [
// other modules...
{
resolve: "@medusajs/medusa/translation",
options: {
entities: [
{
type: "post",
fields: ["title"],
},
],
},
},
],
})
```
The `options.entities` option is an array of objects indicating the custom data models to support translations for. Each object has the following properties:
1. `type`: The name of the table where the custom data model is stored. This is the same value passed as the first parameter to `model.define`.
2. `fields`: An array of fields in the custom data model to support translations for.
***
## Step 2: Manage Translations from Medusa Admin
After configuring the Translation Module, you can manage translations for your custom data models from the Medusa Admin.
Run the following command to start the Medusa application:
```bash npm2yarn
npm run dev
```
Then, open the Medusa Admin and go to Settings -> Translations. You should see your custom data model in the list of translatable resources.
![Post data model highlighted in the list of translatable resources in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1767687396/Medusa%20Resources/CleanShot_2026-01-06_at_10.12.09_2x_w1hmua.png)
Click the Edit button for your custom data model to manage translations for its resources. You can manage translations for the configured locales and fields.
Learn more in the [Translations User Guide](https://docs.medusajs.com/user-guide/settings/translations/index.html.md).
***
## Step 3: Serve Translated Resources
Finally, you can serve translated resources for your custom data models in your configured locales. This section focuses on returning records with translated fields from API routes.
### Pass Locale in API Requests
Medusa supports passing the desired locale in API requests to `/store` routes using either:
1. The `locale` query parameter.
2. The `x-medusa-locale` header.
For example, assuming you have a `/store/posts` API route, you can pass the locale in the request as follows:
### Query Parameter
```bash
curl "http://localhost:9000/store/posts?locale=fr-FR" \
-H 'x-publishable-api-key: {your_publishable_api_key}'
```
### Request Header
```bash
curl "http://localhost:9000/store/posts" \
-H 'x-publishable-api-key: {your_publishable_api_key}' \
-H 'x-medusa-locale: fr-FR'
```
You must pass a publishable API key in the request header to store API routes. Learn more in the [Store API reference](https://docs.medusajs.com/api/store#publishable-api-key).
If your API route isn't under the `/store` prefix, you must apply the `applyLocale` middleware. For example, add the middleware to the `src/api/middlewares.ts` file:
```ts title="src/api/middlewares.ts"
import { applyLocale, defineMiddlewares } from "@medusajs/framework/http"
export default defineMiddlewares({
routes: [
{
matcher: "/posts",
middlewares: [applyLocale],
},
],
})
```
This allows you to pass the locale in the query parameter or request header for the `/posts` API route.
### Handle Translations in API Routes
In your custom API routes, you can retrieve the request's locale from the `locale` property of the `MedusaRequest` object. Pass that property to [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) to retrieve your data models with translated fields.
For example, to retrieve blog posts with translated titles in the `/store/posts` API route:
```ts title="src/api/routes/store/posts.ts" highlights={apiRouteHighlights}
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const query = req.scope.resolve("query")
const { data: posts } = await query.graph({
entity: "post",
fields: ["id", "title"],
options: {
locale: req.locale,
},
})
res.json({ posts })
}
```
In this example, the `locale` option is set to `req.locale`. Medusa will set the `title` field of each post to its translated value if a translation is available for the requested locale. Otherwise, it returns the original value stored in the data model.
***
## Step 4: Test the Implementation
To test the implementation, start the Medusa application if you haven't already:
```bash npm2yarn
npm run dev
```
Then, send a request to your custom API route with the desired locale. For example:
```bash
curl "http://localhost:9000/store/posts?locale=fr-FR" \
-H 'x-publishable-api-key: {your_publishable_api_key}'
```
This should return the list of blog posts with their French translations for the `title` field if available:
```json
{
"posts": [
{
"id": "post_123",
"title": "Titre de l'Article"
},
{
"id": "post_456",
"title": "Un Autre Titre"
}
]
}
```
***
## Pass Locale to useQueryGraphStep
If your API route executes a workflow and returns its result, you can retrieve data models with translated fields in the workflow by passing the locale to `useQueryGraphStep`.
For example:
```ts title="src/workflows/handle-posts.ts" highlights={workflowHighlights}
import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
type WorkflowInput = {
locale: string;
}
export const handlePostsWorkflow = createWorkflow(
"handle-posts",
(input: WorkflowInput) => {
// do something...
const { data: posts } = useQueryGraphStep({
entity: "post",
fields: ["id", "title"],
options: {
locale: input.locale,
},
})
return new WorkflowResponse({
posts,
})
}
)
```
In this example, the workflow accepts a `locale` input parameter. You then retrieve the posts with translated titles by passing the `locale` input to `useQueryGraphStep`.
The returned posts will have their `title` field set to the translated value if a translation is available for the requested locale.
You can execute the workflow in your custom API routes and pass the request locale as an input parameter:
```ts title="src/api/routes/store/posts.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { handlePostsWorkflow } from "../../workflows/handle-posts"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result } = await handlePostsWorkflow(req.scope)
.run({
input: {
locale: req.locale,
},
})
res.json(result)
}
```
The returned posts in the API response will have their `title` field set to the translated value if a translation is available for the requested locale.
***
## Manage Translations with the Translation Module Service
For more complex cases, you can manage translations for your custom data models programmatically using the Translation Module's service. You can create, update, delete, and retrieve translations of any resource using the service's methods.
Refer to the [Translation Module service reference](https://docs.medusajs.com/references/translation/index.html.md) for a full list of available methods and how to use them.
# Links between Translation Module and Other Modules
This document showcases the module links that Medusa defines between the Translation Module and other Commerce Modules.
@@ -43299,7 +43837,7 @@ In this section of the documentation, you will find resources to learn more abou
### Prerequisites
- [Medusa v2.12.3 or later](https://github.com/medusajs/medusa/releases/tag/v2.12.3)
- [Translation Feature Flag Enabled](#)
- [Translation Feature Flag Enabled](#configure-translation-module)
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/translations/index.html.md) to learn how to manage translations in the dashboard.
@@ -43311,6 +43849,7 @@ Refer to the [Module Isolation](https://docs.medusajs.com/docs/learn/fundamental
- [Translation and Locale Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/translation/concepts/index.html.md): Manage locales and add translations for different resources in your store.
- [Multi-Language Support](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/translation/storefront/index.html.md): Manage and serve resources like products in multiple languages to cater to a diverse customer base.
- [Translation for Custom Models](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/translation/custom-data-models/index.html.md): Extend translation capabilities to custom data models in your Medusa application.
***
@@ -43471,9 +44010,25 @@ Refer to the [Workflows](https://docs.medusajs.com/docs/learn/fundamentals/workf
## Supported Module Translations
The Translation Module currently supports translations for all data models in the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md), including products, product variants, and categories.
The Translation Module currently supports translations for the following data models:
Future versions of the Translation Module will include support for all Commerce Modules, as well as custom modules.
|Data Model|Translatable Fields|
|---|---|
|\`CustomerGroup\`|\`name\`|
|\`Product\`||
|\`ProductCategory\`||
|\`ProductCollection\`|\`title\`|
|\`ProductOption\`|\`title\`|
|\`ProductOptionValue\`|\`value\`|
|\`ProductTag\`|\`value\`|
|\`ProductType\`|\`value\`|
|\`ProductVariant\`||
|\`Region\`|\`name\`|
|\`ShippingOption\`|\`name\`|
|\`ShippingOptionType\`||
|\`TaxRate\`|\`name\`|
Future versions of the Translation Module will include support for all Commerce Modules.
***
@@ -43505,7 +44060,7 @@ You must pass a publishable API key in the request header to store API routes. L
## Retrieve Translations for Resources
Currently, you can retrieve translations using the [Store API routes](https://docs.medusajs.com/api/store) for product-related resources, such as products, product variants, and categories. Future releases will expand translation support to additional resources.
You can retrieve translations using the [Store API routes](https://docs.medusajs.com/api/store) for [supported](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/translation#supported-module-translations/index.html.md) and [custom](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/translation/custom-data-models/index.html.md) data models. Future releases will expand translation support to additional resources.
Medusa determines the locale for the request in the following order of priority:
@@ -43514,7 +44069,7 @@ Medusa determines the locale for the request in the following order of priority:
If translations aren't available for the selected locale, or no locale is selected, the original content stored in the resource's data model is returned.
For example:
For example, to retrieve products with translations in French (France):
```bash
curl "http://localhost:9000/store/products?locale=fr-FR" \
@@ -44607,7 +45162,7 @@ The Analytics Module and its providers are available starting [Medusa v2.8.3](ht
### Prerequisites
- [PostHog account](https://app.posthog.com/signup)
- [PostHog API Key](https://posthog.com/docs/getting-started/api-key)
- [PostHog API Key](https://posthog.com/docs/api)
Add the module into the `provider` object of the Analytics Module:
+5
View File
@@ -366,6 +366,11 @@ export const sidebars = [
path: "/learn/fundamentals/api-routes/retrieve-custom-links",
title: "Retrieve Custom Links",
},
{
type: "link",
path: "/learn/fundamentals/api-routes/localization",
title: "Localization",
},
{
type: "link",
path: "/learn/fundamentals/api-routes/override",
@@ -0,0 +1,327 @@
import { Prerequisites, CodeTabs, CodeTab } from "docs-ui"
export const metadata = {
title: `Translate Custom Data Models`,
}
# {metadata.title}
In this chapter, you'll learn how to support translations for your custom data models using the Translation Module.
<Prerequisites
items={[
{
text: "Medusa v2.12.4 or later",
link: "https://github.com/medusajs/medusa/releases/tag/v2.12.4"
},
{
text: "Translation Module Configured",
link: "/commerce-modules/translation#configure-translation-module",
},
]}
/>
## Summary
The Translation Module allows you to extend translation capabilities to custom data models in your Medusa application. Then, you can [manage translations from the Medusa Admin](!user-guide!/settings/translations), and serve translated resources in your configured locales.
By following this guide, you'll learn how to:
- Configure the Translation Module to support translations for your custom data models.
- Manage translations for your custom data models from the Medusa Admin.
- Serve translated resources in your configured locales.
---
## Prerequisites: Custom Data Model
This guide assumes you already have a custom [module](!docs!/learn/fundamentals/modules) with a data model. The guide will use a Blog Module with the following `Post` data model as an example:
```ts title="src/modules/blog/models/post.ts"
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
id: model.id().primaryKey(),
title: model.text(),
})
export default Post
```
The module must also be registered in `medusa-config.ts`. For example:
```ts title="medusa-config.ts"
module.exports = defineConfig({
// ...
modules: [
// other modules...
{
resolve: "./src/modules/blog",
},
],
})
```
---
## Step 1: Configure Translatable Entities
The first step is to configure the Translation Module with the custom entities you want to support translations for.
Before proceeding, run the `build` command to ensure the generated types are up-to-date:
```bash npm2yarn
npm run build
```
This will allow you to benefit from auto-completion when configuring the Translation Module.
Next, in `medusa-config.ts`, add the `options.entities` property to the Translation Module configuration:
export const configHighlights = [
["8", "entities", "The custom entities to support translations for."],
["10", "type", "The name of the table for the custom data model."],
["11", "fields", "The fields of the custom data model to support translations for."]
]
```ts title="medusa-config.ts" highlights={configHighlights}
module.exports = defineConfig({
// ...
modules: [
// other modules...
{
resolve: "@medusajs/medusa/translation",
options: {
entities: [
{
type: "post",
fields: ["title"],
},
],
},
},
],
})
```
The `options.entities` option is an array of objects indicating the custom data models to support translations for. Each object has the following properties:
1. `type`: The name of the table where the custom data model is stored. This is the same value passed as the first parameter to `model.define`.
2. `fields`: An array of fields in the custom data model to support translations for.
---
## Step 2: Manage Translations from Medusa Admin
After configuring the Translation Module, you can manage translations for your custom data models from the Medusa Admin.
Run the following command to start the Medusa application:
```bash npm2yarn
npm run dev
```
Then, open the Medusa Admin and go to Settings -> Translations. You should see your custom data model in the list of translatable resources.
![Post data model highlighted in the list of translatable resources in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1767687396/Medusa%20Resources/CleanShot_2026-01-06_at_10.12.09_2x_w1hmua.png)
Click the Edit button for your custom data model to manage translations for its resources. You can manage translations for the configured locales and fields.
Learn more in the [Translations User Guide](!user-guide!/settings/translations).
---
## Step 3: Serve Translated Resources
Finally, you can serve translated resources for your custom data models in your configured locales. This section focuses on returning records with translated fields from API routes.
### Pass Locale in API Requests
Medusa supports passing the desired locale in API requests to `/store` routes using either:
1. The `locale` query parameter.
2. The `x-medusa-locale` header.
For example, assuming you have a `/store/posts` API route, you can pass the locale in the request as follows:
<CodeTabs group="localization">
<CodeTab label="Query Parameter" value="query-parameter">
```bash
curl "http://localhost:9000/store/posts?locale=fr-FR" \
-H 'x-publishable-api-key: {your_publishable_api_key}'
```
</CodeTab>
<CodeTab label="Request Header" value="request-header">
```bash
curl "http://localhost:9000/store/posts" \
-H 'x-publishable-api-key: {your_publishable_api_key}' \
-H 'x-medusa-locale: fr-FR'
```
</CodeTab>
</CodeTabs>
<Note>
You must pass a publishable API key in the request header to store API routes. Learn more in the [Store API reference](!api!/store#publishable-api-key).
</Note>
If your API route isn't under the `/store` prefix, you must apply the `applyLocale` middleware. For example, add the middleware to the `src/api/middlewares.ts` file:
```ts title="src/api/middlewares.ts"
import { applyLocale, defineMiddlewares } from "@medusajs/framework/http"
export default defineMiddlewares({
routes: [
{
matcher: "/posts",
middlewares: [applyLocale],
},
],
})
```
This allows you to pass the locale in the query parameter or request header for the `/posts` API route.
### Handle Translations in API Routes
In your custom API routes, you can retrieve the request's locale from the `locale` property of the `MedusaRequest` object. Pass that property to [Query](!docs!/learn/fundamentals/module-links/query) to retrieve your data models with translated fields.
For example, to retrieve blog posts with translated titles in the `/store/posts` API route:
export const apiRouteHighlights = [
["13", "locale", "Pass the request locale to the query options."],
]
```ts title="src/api/routes/store/posts.ts" highlights={apiRouteHighlights}
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const query = req.scope.resolve("query")
const { data: posts } = await query.graph({
entity: "post",
fields: ["id", "title"],
options: {
locale: req.locale,
},
})
res.json({ posts })
}
```
In this example, the `locale` option is set to `req.locale`. Medusa will set the `title` field of each post to its translated value if a translation is available for the requested locale. Otherwise, it returns the original value stored in the data model.
---
## Step 4: Test the Implementation
To test the implementation, start the Medusa application if you haven't already:
```bash npm2yarn
npm run dev
```
Then, send a request to your custom API route with the desired locale. For example:
```bash
curl "http://localhost:9000/store/posts?locale=fr-FR" \
-H 'x-publishable-api-key: {your_publishable_api_key}'
```
This should return the list of blog posts with their French translations for the `title` field if available:
```json
{
"posts": [
{
"id": "post_123",
"title": "Titre de l'Article"
},
{
"id": "post_456",
"title": "Un Autre Titre"
}
]
}
```
---
## Pass Locale to useQueryGraphStep
If your API route executes a workflow and returns its result, you can retrieve data models with translated fields in the workflow by passing the locale to `useQueryGraphStep`.
For example:
export const workflowHighlights = [
["5", "locale", "Pass the locale to the workflow"],
["17", "locale", "Pass the request locale to the query options."],
]
```ts title="src/workflows/handle-posts.ts" highlights={workflowHighlights}
import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
type WorkflowInput = {
locale: string;
}
export const handlePostsWorkflow = createWorkflow(
"handle-posts",
(input: WorkflowInput) => {
// do something...
const { data: posts } = useQueryGraphStep({
entity: "post",
fields: ["id", "title"],
options: {
locale: input.locale,
},
})
return new WorkflowResponse({
posts,
})
}
)
```
In this example, the workflow accepts a `locale` input parameter. You then retrieve the posts with translated titles by passing the `locale` input to `useQueryGraphStep`.
The returned posts will have their `title` field set to the translated value if a translation is available for the requested locale.
You can execute the workflow in your custom API routes and pass the request locale as an input parameter:
```ts title="src/api/routes/store/posts.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { handlePostsWorkflow } from "../../workflows/handle-posts"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result } = await handlePostsWorkflow(req.scope)
.run({
input: {
locale: req.locale,
},
})
res.json(result)
}
```
The returned posts in the API response will have their `title` field set to the translated value if a translation is available for the requested locale.
---
## Manage Translations with the Translation Module Service
For more complex cases, you can manage translations for your custom data models programmatically using the Translation Module's service. You can create, update, delete, and retrieve translations of any resource using the service's methods.
Refer to the [Translation Module service reference](/references/translation) for a full list of available methods and how to use them.
@@ -2,7 +2,7 @@
generate_toc: true
---
import { CodeTabs, CodeTab, ChildDocs, Prerequisites } from "docs-ui"
import { CodeTabs, CodeTab, ChildDocs, Prerequisites, Table } from "docs-ui"
export const metadata = {
title: `Translation Module`,
@@ -20,7 +20,7 @@ In this section of the documentation, you will find resources to learn more abou
},
{
text: "Translation Feature Flag Enabled",
link: "#"
link: "#configure-translation-module"
}
]}
/>
@@ -43,6 +43,7 @@ Refer to the [Module Isolation](!docs!/learn/fundamentals/modules/isolation) gui
- [Translation and Locale Management](./concepts/page.mdx): Manage locales and add translations for different resources in your store.
- [Multi-Language Support](./storefront/page.mdx): Manage and serve resources like products in multiple languages to cater to a diverse customer base.
- [Translation for Custom Models](./custom-data-models/page.mdx): Extend translation capabilities to custom data models in your Medusa application.
---
@@ -213,9 +214,134 @@ Refer to the [Workflows](!docs!/learn/fundamentals/workflows) documentation to l
## Supported Module Translations
The Translation Module currently supports translations for all data models in the [Product Module](../product/page.mdx), including products, product variants, and categories.
The Translation Module currently supports translations for the following data models:
Future versions of the Translation Module will include support for all Commerce Modules, as well as custom modules.
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Data Model
</Table.HeaderCell>
<Table.HeaderCell>
Translatable Fields
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`CustomerGroup` ([Customer Module](../customer/page.mdx)) ([+v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4))
</Table.Cell>
<Table.Cell>
`name`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`Product` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
- `title`
- `description`
- `material`
- `subtitle`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ProductCategory` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
- `name`
- `description`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ProductCollection` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
`title`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ProductOption` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
`title`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ProductOptionValue` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
`value`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ProductTag` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
`value`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ProductType` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
`value`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ProductVariant` ([Product Module](../product/page.mdx))
</Table.Cell>
<Table.Cell>
- `title`
- `material`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`Region` ([Region Module](../region/page.mdx)) ([+v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4))
</Table.Cell>
<Table.Cell>
`name`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ShippingOption` ([Fulfillment Module](../fulfillment/page.mdx)) ([+v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4))
</Table.Cell>
<Table.Cell>
`name`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`ShippingOptionType` ([Fulfillment Module](../fulfillment/page.mdx)) ([+v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4))
</Table.Cell>
<Table.Cell>
- `label`
- `description`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`TaxRate` ([Tax Module](../tax/page.mdx)) ([+v2.12.4](https://github.com/medusajs/medusa/releases/tag/v2.12.4))
</Table.Cell>
<Table.Cell>
`name`
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
Future versions of the Translation Module will include support for all Commerce Modules.
---
@@ -47,7 +47,7 @@ You must pass a publishable API key in the request header to store API routes. L
## Retrieve Translations for Resources
Currently, you can retrieve translations using the [Store API routes](!api!/store) for product-related resources, such as products, product variants, and categories. Future releases will expand translation support to additional resources.
You can retrieve translations using the [Store API routes](!api!/store) for [supported](../page.mdx#supported-module-translations) and [custom](../custom-data-models/page.mdx) data models. Future releases will expand translation support to additional resources.
Medusa determines the locale for the request in the following order of priority:
@@ -56,7 +56,7 @@ Medusa determines the locale for the request in the following order of priority:
If translations aren't available for the selected locale, or no locale is selected, the original content stored in the resource's data model is returned.
For example:
For example, to retrieve products with translations in French (France):
```bash
curl "http://localhost:9000/store/products?locale=fr-FR" \
+4 -3
View File
@@ -6743,8 +6743,8 @@ export const generatedEditDates = {
"app/commerce-modules/store/locales/page.mdx": "2025-12-16T07:13:12.389Z",
"app/commerce-modules/translation/concepts/page.mdx": "2025-12-16T07:13:50.326Z",
"app/commerce-modules/translation/link-to-other-modules/page.mdx": "2025-12-10T13:23:44.262Z",
"app/commerce-modules/translation/page.mdx": "2025-12-10T15:31:44.251Z",
"app/commerce-modules/translation/storefront/page.mdx": "2025-12-16T07:15:02.192Z",
"app/commerce-modules/translation/page.mdx": "2026-01-06T08:32:43.208Z",
"app/commerce-modules/translation/storefront/page.mdx": "2026-01-06T08:50:03.221Z",
"app/storefront-development/localization/page.mdx": "2025-12-16T07:18:51.976Z",
"app/commerce-modules/translation/js-sdk/page.mdx": "2025-12-10T15:24:57.155Z",
"app/commerce-modules/translation/workflows/page.mdx": "2025-12-10T15:24:45.190Z",
@@ -6936,5 +6936,6 @@ export const generatedEditDates = {
"references/js_sdk/admin/ShippingOptionType/properties/js_sdk.admin.ShippingOptionType.client/page.mdx": "2025-12-17T14:31:14.186Z",
"app/how-to-tutorials/how-to/admin/auth/page.mdx": "2025-12-30T06:35:28.828Z",
"app/integrations/guides/okta/page.mdx": "2025-12-30T06:35:28.653Z",
"app/nextjs-starter/guides/remove-country-code/page.mdx": "2025-12-30T10:28:10.072Z"
"app/nextjs-starter/guides/remove-country-code/page.mdx": "2025-12-30T10:28:10.072Z",
"app/commerce-modules/translation/custom-data-models/page.mdx": "2026-01-06T08:49:12.704Z"
}
@@ -663,6 +663,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/translation/concepts/page.mdx",
"pathname": "/commerce-modules/translation/concepts"
},
{
"filePath": "/www/apps/resources/app/commerce-modules/translation/custom-data-models/page.mdx",
"pathname": "/commerce-modules/translation/custom-data-models"
},
{
"filePath": "/www/apps/resources/app/commerce-modules/translation/js-sdk/page.mdx",
"pathname": "/commerce-modules/translation/js-sdk"
@@ -17223,6 +17223,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
"title": "Concepts",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/commerce-modules/translation/custom-data-models",
"title": "Translate Custom Models",
"children": []
},
{
"loaded": true,
"isPathHref": true,
@@ -22,6 +22,11 @@ export const translationSidebar = [
path: "/commerce-modules/translation/concepts",
title: "Concepts",
},
{
type: "link",
path: "/commerce-modules/translation/custom-data-models",
title: "Translate Custom Models",
},
{
type: "link",
path: "/commerce-modules/translation/storefront",
@@ -36,21 +36,29 @@ You can set supported locales for your store in the [Store](../store/page.mdx#ma
Then, in the storefront, customers can view content in their preferred language based on the available locales.
You can view and manage translations in the Medusa Admin dashboard by going to Settings → Translations. This page allows you to manage translations of resources in bulk.
### View and Manage Translations
You can view and manage translations in the Medusa Admin dashboard by going to Settings → Translations. This page allows you to manage translations of core and custom resources in bulk.
Alternatively, you can manage translations for individual resources directly from their respective pages (for example, from a product's detail page).
![Translations settings page](https://res.cloudinary.com/dza7lstvk/image/upload/v1765888654/User%20Guide/CleanShot_2025-12-16_at_14.36.56_2x_tqvst2.png)
### Translate Custom Resources
Medusa's translation feature supports translating custom resources defined in your application. For example, if you have a blog feature, you can translate blog posts into multiple languages.
To translate custom resources, your technical team needs to configure the Translation Module as explained in the [Translation for Custom Models](!resources!/commerce-modules/translation/custom-data-models) guide.
---
## Manage Translations in Bulk
In the Translations settings page, you can view and manage translations in bulk for various resources in your store, including products and variants.
In the Translations settings page, you can view and manage translations in bulk for various core and custom resources in your store, including products and variants.
<Note>
Currently, Medusa supports translations for product-related resources only. Future versions will include support for translating more core resources, as well as custom resources.
Refer to the [Translation Module](!resources!/commerce-modules/translation#supported-module-translations) guide for a list of core resources that support translations out-of-the-box.
</Note>
@@ -1377,7 +1377,7 @@ class OasKindGenerator extends FunctionKindGenerator {
}),
this.getParameterObject({
type: "header",
name: "Content-Language",
name: "x-medusa-locale",
description:
"The locale in BCP 47 format to retrieve localized content.",
required: false,