Add multilanguage catalog translations UI for products and categories.
Include AI translate-all with locale picker, count badges, MultiLanguage Data module gating, and shared modal action layout polish. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
8c187b18be
commit
2a32c46b2e
@@ -0,0 +1,96 @@
|
||||
import { apiRequest } from '../lib/api'
|
||||
import { getActiveBusinessId } from '../lib/businessContext'
|
||||
import type {
|
||||
EntityTranslation,
|
||||
EntityTranslationsResponse,
|
||||
TranslationEntityType,
|
||||
TranslationFields,
|
||||
TranslationLocale,
|
||||
} from '../types/translation'
|
||||
|
||||
function businessPath(entityType: TranslationEntityType, entityId: string, suffix = '') {
|
||||
const businessId = getActiveBusinessId()
|
||||
if (!businessId) {
|
||||
throw new Error('No active business selected. Please sign in again.')
|
||||
}
|
||||
const resource = entityType === 'product' ? 'products' : 'categories'
|
||||
return `/businesses/${businessId}/${resource}/${entityId}/translations${suffix}`
|
||||
}
|
||||
|
||||
export async function listEntityTranslations(
|
||||
entityType: TranslationEntityType,
|
||||
entityId: string,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
return apiRequest<EntityTranslationsResponse>(businessPath(entityType, entityId), {
|
||||
auth: true,
|
||||
signal,
|
||||
})
|
||||
}
|
||||
|
||||
export async function translateEntityWithAi(
|
||||
entityType: TranslationEntityType,
|
||||
entityId: string,
|
||||
locale: TranslationLocale,
|
||||
) {
|
||||
return apiRequest<{ message: string; translation: EntityTranslation }>(
|
||||
businessPath(entityType, entityId, `/${locale}/ai`),
|
||||
{
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: {},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export async function saveEntityTranslation(
|
||||
entityType: TranslationEntityType,
|
||||
entityId: string,
|
||||
locale: TranslationLocale,
|
||||
fields: TranslationFields,
|
||||
) {
|
||||
return apiRequest<{ message: string; translation: EntityTranslation }>(
|
||||
businessPath(entityType, entityId, `/${locale}`),
|
||||
{
|
||||
method: 'PUT',
|
||||
auth: true,
|
||||
body: { fields },
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
export async function translateAllProductCategories(locale: TranslationLocale) {
|
||||
const businessId = getActiveBusinessId()
|
||||
if (!businessId) {
|
||||
throw new Error('No active business selected. Please sign in again.')
|
||||
}
|
||||
return apiRequest<{
|
||||
message: string
|
||||
locale: TranslationLocale
|
||||
translated: number
|
||||
skipped: number
|
||||
failed: number
|
||||
}>(`/businesses/${businessId}/categories/translations/ai-all`, {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: { locale },
|
||||
})
|
||||
}
|
||||
|
||||
export async function translateAllProducts(locale: TranslationLocale) {
|
||||
const businessId = getActiveBusinessId()
|
||||
if (!businessId) {
|
||||
throw new Error('No active business selected. Please sign in again.')
|
||||
}
|
||||
return apiRequest<{
|
||||
message: string
|
||||
locale: TranslationLocale
|
||||
translated: number
|
||||
skipped: number
|
||||
failed: number
|
||||
}>(`/businesses/${businessId}/products/translations/ai-all`, {
|
||||
method: 'POST',
|
||||
auth: true,
|
||||
body: { locale },
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user