feat: Translations UI (#14217)
* Add Translations route and guard it with feature flag. Empty TranslationsList main component to test route. * Translation list component * Add translations namespace to js-sdk * Translations hook * Avoid incorrectly throwing when updating and locale not included * Translations bulk editor component v1 * Add batch method to translations namespace in js-sdk * Protect translations edit route with feature flag * Handle reference_id search param * Replace entity_type entity_id for reference reference_id * Manage translations from product detail page * Dynamically resolve base hook for retrieving translations * Fix navigation from outside settings/translations * Navigation to bulk editor from product list * Add Translations to various product module types * Type useVariants hook * Handle product module entities translations in bulk editor * Fix categories issue in datagrid due to column clash * Translations bulk navigation from remaining entities detail pages * Add remaining bulk editor navigation for list components. Fix invalidation query for variants * Expandable text cell v1 * Popover approach * Add *supported_locales.locale to default fields in stores list endpoint * Make popover more aligned to excell approach * Correctly tie the focused cell anchor to popover * Rework translations main component UI * Fix link def export * Swap axis for translations datagrid * Add original column to translations data grid * Remove is_default store locale from backend * Remove ldefault locale from ui * Type * Add changeset * Comments * Remove unused import * Add translations to admin product categories endpoint allowed fields * Default locale removal * Lazy loading with infinite scroll data grid * Infinite list hook and implementation for products and variants * Translation bulk editor lazy loaded datagrid * Prevent scroll when forcing focus, to avoid scrollTop reset on infinite loading * Confgiure placeholder data * Cleanup logs and refactor * Infinite query hooks for translatable entities * Batch requests for translation batch endpoint * Clean up * Update icon * Add query param validator in settings endpoint * Settings endpoint param type * JS sdk methods for translation settings and statistics * Retrieve translatable fields and entities dynamically. Remove hardcoded information from tranlations list * Resolve translation aggregate completion dynamically * Format label * Resolve bulk editor header label dynamically * Include type and collection in translations config * Avoid showing product option and option values in translatable entities list * Translations * Make translations bulk editor content columns wider * Disable hiding Original column in translations bulk editor * Adjust translations completion styles * Fix translations config screen * Locale selector switcher with conditional locale column rendering * Batch one locale at a time * Hooks save actions to footer buttons * Reset dirty state on save * Dynamic row heights for translations bulk editor. Replace expandable cell for text cell, with additional isMultiLine config * Make columns take as much responsive width as possible and divide equally * more padding to avoid unnecessary horizontal scrollbar * Update statistics graphs * Translations * Statistics graphs translations * Translation, text sizes and weight in stat graphs * Conditionally show/hide column visibility dropdown in datagrid * Allow to pass component to place in DataGrid header and use it in translations bulk editor * Center text regardless of multiLine config * Apply full height to datagrid cell regardles of multiSelect config * Colors and fonts * Handle key down for text area in text cell * MultilineCell with special keydown handling * Rework form schema to match new single locale edit flow * Update created translations to include id, to avoid duplication issue on subsequent calls * Handle space key for text cells * Finish hooking up multiline cell with key and mouse events * Disable remaining buttons when batch is ongoing * Style updates * Update style * Refactor to make form updates and sync/comparison with server data more comprehensive and robust * Update styles * Bars and labels alignment * Add languages tooltip * Styles and translation * Navigation update * Disable edit translations button when no reference count * Invert colors --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>
This commit is contained in:
@@ -61,7 +61,7 @@ export const validateTranslationsStep = createStep(
|
||||
const unsupportedLocales = normalizedInput
|
||||
.filter((translation) => Boolean(translation.locale_code))
|
||||
.map((translation) => translation.locale_code)
|
||||
.filter((locale) => !enabledLocales.includes(locale ?? ""))
|
||||
.filter((locale) => !enabledLocales.includes(locale))
|
||||
|
||||
if (unsupportedLocales.length) {
|
||||
throw new MedusaError(
|
||||
|
||||
@@ -46,6 +46,7 @@ import { Views } from "./views"
|
||||
import { WorkflowExecution } from "./workflow-execution"
|
||||
import { ShippingOptionType } from "./shipping-option-type"
|
||||
import { Locale } from "./locale"
|
||||
import { Translation } from "./translation"
|
||||
|
||||
export class Admin {
|
||||
/**
|
||||
@@ -225,6 +226,10 @@ export class Admin {
|
||||
* @tags tax
|
||||
*/
|
||||
public taxProvider: TaxProvider
|
||||
/**
|
||||
* @tags translations
|
||||
*/
|
||||
public translation: Translation
|
||||
/**
|
||||
* @tags promotion
|
||||
*/
|
||||
@@ -268,6 +273,7 @@ export class Admin {
|
||||
this.claim = new Claim(client)
|
||||
this.taxRate = new TaxRate(client)
|
||||
this.taxRegion = new TaxRegion(client)
|
||||
this.translation = new Translation(client)
|
||||
this.store = new Store(client)
|
||||
this.productTag = new ProductTag(client)
|
||||
this.user = new User(client)
|
||||
|
||||
206
packages/core/js-sdk/src/admin/translation.ts
Normal file
206
packages/core/js-sdk/src/admin/translation.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { Client } from "../client"
|
||||
import { ClientHeaders } from "../types"
|
||||
|
||||
export class Translation {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
private client: Client
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
constructor(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves a paginated list of translations. It sends a request to the
|
||||
* [List Translations](https://docs.medusajs.com/api/admin#translations_gettranslations)
|
||||
* API route.
|
||||
*
|
||||
* @param query - Filters and pagination configurations.
|
||||
* @param headers - Headers to pass in the request.
|
||||
* @returns The paginated list of translations.
|
||||
*
|
||||
* @example
|
||||
* To retrieve the list of translations:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.list()
|
||||
* .then(({ translations, count, limit, offset }) => {
|
||||
* console.log(translations)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To configure the pagination, pass the `limit` and `offset` query parameters.
|
||||
*
|
||||
* For example, to retrieve only 10 items and skip 10 items:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.list({
|
||||
* limit: 10,
|
||||
* offset: 10
|
||||
* })
|
||||
* .then(({ translations, count, limit, offset }) => {
|
||||
* console.log(translations)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Using the `fields` query parameter, you can specify the fields and relations to retrieve
|
||||
* in each translation:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.list({
|
||||
* fields: "id,name"
|
||||
* })
|
||||
* .then(({ translations, count, limit, offset }) => {
|
||||
* console.log(translations)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Learn more about the `fields` property in the [API reference](https://docs.medusajs.com/api/store#select-fields-and-relations).
|
||||
*/
|
||||
async list(
|
||||
query?: HttpTypes.AdminTranslationsListParams,
|
||||
headers?: ClientHeaders
|
||||
) {
|
||||
return await this.client.fetch<HttpTypes.AdminTranslationsListResponse>(
|
||||
`/admin/translations`,
|
||||
{
|
||||
headers,
|
||||
query,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows bulk operations on translations. It sends a request to the
|
||||
* [Manage Translations](https://docs.medusajs.com/api/admin#translations_posttranslationsbatch)
|
||||
* API route.
|
||||
*
|
||||
* @param payload - The translations to create, update, or delete.
|
||||
* @param headers - Headers to pass in the request.
|
||||
* @returns The translations' details.
|
||||
*
|
||||
* @example
|
||||
* sdk.admin.translation.batch({
|
||||
* create: [
|
||||
* {
|
||||
* reference_id: "prod_123",
|
||||
* reference: "product",
|
||||
* locale_code: "en-US",
|
||||
* translations: { title: "Shirt" }
|
||||
* }
|
||||
* ],
|
||||
* update: [
|
||||
* {
|
||||
* id: "trans_123",
|
||||
* translations: { title: "Pants" }
|
||||
* }
|
||||
* ],
|
||||
* delete: ["trans_321"]
|
||||
* })
|
||||
* .then(({ created, updated, deleted }) => {
|
||||
* console.log(created, updated, deleted)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
async batch(body: HttpTypes.AdminBatchTranslations, headers?: ClientHeaders) {
|
||||
return await this.client.fetch<HttpTypes.AdminTranslationsBatchResponse>(
|
||||
`/admin/translations/batch`,
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves the settings for the translations for a given entity type or all entity types if no entity type is provided.
|
||||
* It sends a request to the
|
||||
* [Get Translation Settings](https://docs.medusajs.com/api/admin#translations_gettranslationssettings) API route.
|
||||
*
|
||||
* @param query - The query parameters which can optionally include the entity type to get the settings for.
|
||||
* @param headers - Headers to pass in the request.
|
||||
* @returns The translation settings.
|
||||
*
|
||||
* @example
|
||||
* To retrieve the settings for the translations for a given entity type:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.settings({
|
||||
* entity_type: "product"
|
||||
* })
|
||||
* .then(({ translatable_fields }) => {
|
||||
* console.log(translatable_fields)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To retrieve the settings for all entity types:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.settings()
|
||||
* .then(({ translatable_fields }) => {
|
||||
* console.log(translatable_fields)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
async settings(
|
||||
query?: HttpTypes.AdminTranslationSettingsParams,
|
||||
headers?: ClientHeaders
|
||||
) {
|
||||
return await this.client.fetch<HttpTypes.AdminTranslationSettingsResponse>(
|
||||
`/admin/translations/settings`,
|
||||
{
|
||||
headers,
|
||||
query,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves the statistics for the translations for a given entity type or all entity types if no entity type is provided.
|
||||
* It sends a request to the
|
||||
* [Get Translation Statistics](https://docs.medusajs.com/api/admin#translations_gettranslationsstatistics) API route.
|
||||
*
|
||||
* @param query - The query parameters which can optionally include the entity type to get the statistics for.
|
||||
* @param headers - Headers to pass in the request.
|
||||
* @returns The translation statistics.
|
||||
*
|
||||
* @example
|
||||
* To retrieve the statistics for the translations for a given entity type:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.statistics({
|
||||
* entity_type: "product"
|
||||
* })
|
||||
* .then(({ statistics }) => {
|
||||
* console.log(statistics)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To retrieve the statistics for all entity types:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.statistics()
|
||||
* .then(({ statistics }) => {
|
||||
* console.log(statistics)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
async statistics(
|
||||
query?: HttpTypes.AdminTranslationStatisticsParams,
|
||||
headers?: ClientHeaders
|
||||
) {
|
||||
return await this.client.fetch<HttpTypes.AdminTranslationStatisticsResponse>(
|
||||
`/admin/translations/statistics`,
|
||||
{
|
||||
headers,
|
||||
query,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
import { BaseCollection } from "../common"
|
||||
import { AdminTranslation } from "../../translations"
|
||||
|
||||
export interface AdminCollection extends BaseCollection {}
|
||||
export interface AdminCollection extends BaseCollection {
|
||||
/**
|
||||
* The collection's translations.
|
||||
*/
|
||||
translations?: AdminTranslation[] | null
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AdminProduct } from "../../product"
|
||||
import { AdminTranslation } from "../../translations"
|
||||
import { BaseProductCategory } from "../common"
|
||||
|
||||
export interface AdminProductCategory
|
||||
@@ -18,4 +19,8 @@ export interface AdminProductCategory
|
||||
* The products that belong to this category.
|
||||
*/
|
||||
products?: AdminProduct[]
|
||||
/**
|
||||
* The category's translations.
|
||||
*/
|
||||
translations?: AdminTranslation[] | null
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { BaseProductTag } from "../common"
|
||||
import { AdminTranslation } from "../../translations"
|
||||
|
||||
export interface AdminProductTag extends BaseProductTag {}
|
||||
export interface AdminProductTag extends BaseProductTag {
|
||||
/**
|
||||
* The tag's translations.
|
||||
*/
|
||||
translations?: AdminTranslation[] | null
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { AdminTranslation } from "../../translations"
|
||||
import { BaseProductType } from "../common"
|
||||
|
||||
export interface AdminProductType extends BaseProductType {}
|
||||
export interface AdminProductType extends BaseProductType {
|
||||
/**
|
||||
* The product type's translations.
|
||||
*/
|
||||
translations?: AdminTranslation[] | null
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { AdminProductTag } from "../../product-tag"
|
||||
import { AdminProductType } from "../../product-type"
|
||||
import { AdminSalesChannel } from "../../sales-channel"
|
||||
import { AdminShippingProfile } from "../../shipping-profile"
|
||||
import { AdminTranslation } from "../../translations"
|
||||
import {
|
||||
BaseProduct,
|
||||
BaseProductImage,
|
||||
@@ -55,6 +56,10 @@ export interface AdminProductVariant extends BaseProductVariant {
|
||||
* The product that this variant belongs to.
|
||||
*/
|
||||
product?: AdminProduct | null
|
||||
/**
|
||||
* The product variant's translations.
|
||||
*/
|
||||
translations?: AdminTranslation[] | null
|
||||
/**
|
||||
* The variant's inventory items.
|
||||
*/
|
||||
@@ -108,6 +113,10 @@ export interface AdminProduct
|
||||
* The product's variants.
|
||||
*/
|
||||
variants: AdminProductVariant[] | null
|
||||
/**
|
||||
* The product's translations.
|
||||
*/
|
||||
translations?: AdminTranslation[] | null
|
||||
/**
|
||||
* The product's type.
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./queries"
|
||||
export * from "./responses"
|
||||
export * from "./entities"
|
||||
export * from "./payloads"
|
||||
|
||||
44
packages/core/types/src/http/translations/admin/payloads.ts
Normal file
44
packages/core/types/src/http/translations/admin/payloads.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
interface AdminCreateTranslation {
|
||||
/**
|
||||
* The ID of the entity being translated.
|
||||
*/
|
||||
reference_id: string
|
||||
/**
|
||||
* The type of entity being translated (e.g., "product", "product_variant").
|
||||
*/
|
||||
reference: string
|
||||
/**
|
||||
* The BCP 47 language tag code for this translation (e.g., "en-US", "fr-FR").
|
||||
*/
|
||||
locale_code: string
|
||||
/**
|
||||
* The translated fields as key-value pairs.
|
||||
*/
|
||||
translations: Record<string, unknown>
|
||||
}
|
||||
|
||||
interface AdminUpdateTranslation {
|
||||
/**
|
||||
* The ID of the translation.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The translated fields as key-value pairs.
|
||||
*/
|
||||
translations: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface AdminBatchTranslations {
|
||||
/**
|
||||
* The translations to create.
|
||||
*/
|
||||
create?: AdminCreateTranslation[]
|
||||
/**
|
||||
* The translations to update.
|
||||
*/
|
||||
update?: AdminUpdateTranslation[]
|
||||
/**
|
||||
* The translations to delete.
|
||||
*/
|
||||
delete?: string[]
|
||||
}
|
||||
@@ -51,3 +51,13 @@ export interface AdminTranslationStatisticsParams {
|
||||
*/
|
||||
entity_types: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Query parameters for translation settings endpoint.
|
||||
*/
|
||||
export interface AdminTranslationSettingsParams {
|
||||
/**
|
||||
* The entity type to get the settings for (e.g., "product").
|
||||
*/
|
||||
entity_type?: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user