diff --git a/api/[[...route]].ts b/api/[[...route]].ts index c812b9a7..4fe046cb 100644 --- a/api/[[...route]].ts +++ b/api/[[...route]].ts @@ -60,6 +60,46 @@ import { ProductsTranslations } from '../packages/products/dist/translations/ind import { SupportTranslations } from '../packages/support/dist/translations/index.js'; import { HRTranslations } from '../packages/hr/dist/translations/index.js'; +/** + * Merge multiple TranslationBundles into a single bundle by deep-merging each locale. + * This allows multiple plugins to contribute translations that are combined into one namespace. + */ +function mergeTranslationBundles(...bundles: any[]): any { + const merged: any = {}; + + for (const bundle of bundles) { + for (const locale in bundle) { + if (!merged[locale]) { + merged[locale] = {}; + } + + // Deep merge the translation data for this locale + const source = bundle[locale]; + const target = merged[locale]; + + for (const key in source) { + if (typeof source[key] === 'object' && !Array.isArray(source[key])) { + target[key] = { ...target[key], ...source[key] }; + } else { + target[key] = source[key]; + } + } + } + } + + return merged; +} + +// Merge all plugin translations into a single bundle +const HotCRMTranslations = mergeTranslationBundles( + CrmTranslations, + FinanceTranslations, + MarketingTranslations, + ProductsTranslations, + SupportTranslations, + HRTranslations +); + // --------------------------------------------------------------------------- // Timeout constants — protect against permanently-pending promises that would // cause Vercel's 60 s function timeout. @@ -481,17 +521,11 @@ async function bootstrap(): Promise { }, i18n: { defaultLocale: 'en', - supportedLocales: ['en', 'zh-CN', 'ja-JP'], + supportedLocales: ['en', 'zh', 'ja'], fallbackLocale: 'en', + namespace: 'hotcrm', + translations: HotCRMTranslations, }, - translations: [ - CrmTranslations, - FinanceTranslations, - MarketingTranslations, - ProductsTranslations, - SupportTranslations, - HRTranslations, - ], objects: [], plugins: [], })), PLUGIN_TIMEOUT_MS, 'AppPlugin-manifest'); diff --git a/objectstack.config.ts b/objectstack.config.ts index 1114a8b7..f22c0cc1 100644 --- a/objectstack.config.ts +++ b/objectstack.config.ts @@ -31,6 +31,46 @@ import { ProductsTranslations } from './packages/products/dist/translations/inde import { SupportTranslations } from './packages/support/dist/translations/index.js'; import { HRTranslations } from './packages/hr/dist/translations/index.js'; +/** + * Merge multiple TranslationBundles into a single bundle by deep-merging each locale. + * This allows multiple plugins to contribute translations that are combined into one namespace. + */ +function mergeTranslationBundles(...bundles: any[]): any { + const merged: any = {}; + + for (const bundle of bundles) { + for (const locale in bundle) { + if (!merged[locale]) { + merged[locale] = {}; + } + + // Deep merge the translation data for this locale + const source = bundle[locale]; + const target = merged[locale]; + + for (const key in source) { + if (typeof source[key] === 'object' && !Array.isArray(source[key])) { + target[key] = { ...target[key], ...source[key] }; + } else { + target[key] = source[key]; + } + } + } + } + + return merged; +} + +// Merge all plugin translations into a single bundle +const HotCRMTranslations = mergeTranslationBundles( + CrmTranslations, + FinanceTranslations, + MarketingTranslations, + ProductsTranslations, + SupportTranslations, + HRTranslations +); + /** * HotCRM Application Configuration * @@ -55,9 +95,10 @@ export default defineStack({ // Internationalization (i18n) configuration i18n: { defaultLocale: 'en', - supportedLocales: ['en', 'zh-CN', 'ja-JP'], + supportedLocales: ['en', 'zh', 'ja'], fallbackLocale: 'en', - fileOrganization: 'per_locale', + namespace: 'hotcrm', + translations: HotCRMTranslations, }, // Empty objects array triggers auto-loading of ObjectQL and the memory driver, @@ -75,18 +116,6 @@ export default defineStack({ LanguageDataset, ], - // Aggregated translations from all business plugins (TranslationBundle[]) - // Each plugin also registers its own translations for plugin-level loading. - // The root config merges them for the AppPlugin to load at startup. - translations: [ - CrmTranslations, - FinanceTranslations, - MarketingTranslations, - ProductsTranslations, - SupportTranslations, - HRTranslations, - ], - // Register all Business Plugins // Core clouds (6) plugins: [ diff --git a/packages/crm/__tests__/unit/schemas/i18n-translation.test.ts b/packages/crm/__tests__/unit/schemas/i18n-translation.test.ts index 2d296f77..da60bf5c 100644 --- a/packages/crm/__tests__/unit/schemas/i18n-translation.test.ts +++ b/packages/crm/__tests__/unit/schemas/i18n-translation.test.ts @@ -13,12 +13,12 @@ describe('CRM i18n Translations', () => { it('should contain all supported locales', () => { expect(CrmTranslations).toHaveProperty('en'); - expect(CrmTranslations).toHaveProperty('zh-CN'); - expect(CrmTranslations).toHaveProperty('ja-JP'); + expect(CrmTranslations).toHaveProperty('zh'); + expect(CrmTranslations).toHaveProperty('ja'); }); it('should have non-empty objects section for each locale', () => { - for (const locale of ['en', 'zh-CN', 'ja-JP']) { + for (const locale of ['en', 'zh', 'ja']) { const data = CrmTranslations[locale]; expect(data).toBeDefined(); expect(data.objects).toBeDefined(); @@ -36,8 +36,8 @@ describe('CRM i18n Translations', () => { it('should have consistent object keys across all locales', () => { const enKeys = Object.keys(CrmTranslations.en.objects ?? {}).sort(); - const zhKeys = Object.keys(CrmTranslations['zh-CN'].objects ?? {}).sort(); - const jaKeys = Object.keys(CrmTranslations['ja-JP'].objects ?? {}).sort(); + const zhKeys = Object.keys(CrmTranslations['zh'].objects ?? {}).sort(); + const jaKeys = Object.keys(CrmTranslations['ja'].objects ?? {}).sort(); expect(enKeys).toEqual(zhKeys); expect(enKeys).toEqual(jaKeys); }); diff --git a/packages/crm/src/translations/crm.translation.ts b/packages/crm/src/translations/crm.translation.ts index 9719800b..b3a5e5b9 100644 --- a/packages/crm/src/translations/crm.translation.ts +++ b/packages/crm/src/translations/crm.translation.ts @@ -1,13 +1,13 @@ import type { TranslationBundle } from '@objectstack/spec/system'; import { en } from './en.js'; -import { zhCN } from './zh-CN.js'; -import { jaJP } from './ja-JP.js'; +import { zh } from './zh.js'; +import { ja } from './ja.js'; /** * CRM Sales Cloud — Internationalization (i18n) * * Demonstrates **per-locale file splitting** convention: - * each language is defined in its own file (`en.ts`, `zh-CN.ts`, `ja-JP.ts`) + * each language is defined in its own file (`en.ts`, `zh.ts`, `ja.ts`) * and assembled into a single `TranslationBundle` here. * * Enterprise-grade multi-language translations covering: @@ -16,10 +16,10 @@ import { jaJP } from './ja-JP.js'; * - App & navigation group labels * - Common UI messages, validation messages * - * Supported locales: en, zh-CN, ja-JP + * Supported locales: en, zh, ja */ export const CrmTranslations: TranslationBundle = { en, - 'zh-CN': zhCN, - 'ja-JP': jaJP, + zh, + ja, }; diff --git a/packages/crm/src/translations/ja-JP.ts b/packages/crm/src/translations/ja.ts similarity index 99% rename from packages/crm/src/translations/ja-JP.ts rename to packages/crm/src/translations/ja.ts index a8a42515..54199b58 100644 --- a/packages/crm/src/translations/ja-JP.ts +++ b/packages/crm/src/translations/ja.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 日本語 (ja-JP) — CRM Sales Cloud Translations + * 日本語 (ja) — CRM Sales Cloud Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const jaJP: TranslationData = { +export const ja: TranslationData = { objects: { account: { label: '取引先', diff --git a/packages/crm/src/translations/zh-CN.ts b/packages/crm/src/translations/zh.ts similarity index 99% rename from packages/crm/src/translations/zh-CN.ts rename to packages/crm/src/translations/zh.ts index 1aca33ad..258d6bce 100644 --- a/packages/crm/src/translations/zh-CN.ts +++ b/packages/crm/src/translations/zh.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 简体中文 (zh-CN) — CRM Sales Cloud Translations + * 简体中文 (zh) — CRM Sales Cloud Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const zhCN: TranslationData = { +export const zh: TranslationData = { objects: { account: { label: '客户', diff --git a/packages/finance/__tests__/unit/schemas/i18n-translation.test.ts b/packages/finance/__tests__/unit/schemas/i18n-translation.test.ts index 85e72fb7..9986fc5d 100644 --- a/packages/finance/__tests__/unit/schemas/i18n-translation.test.ts +++ b/packages/finance/__tests__/unit/schemas/i18n-translation.test.ts @@ -13,12 +13,12 @@ describe('Finance i18n Translations', () => { it('should contain all supported locales', () => { expect(FinanceTranslations).toHaveProperty('en'); - expect(FinanceTranslations).toHaveProperty('zh-CN'); - expect(FinanceTranslations).toHaveProperty('ja-JP'); + expect(FinanceTranslations).toHaveProperty('zh'); + expect(FinanceTranslations).toHaveProperty('ja'); }); it('should have non-empty objects section for each locale', () => { - for (const locale of ['en', 'zh-CN', 'ja-JP']) { + for (const locale of ['en', 'zh', 'ja']) { const data = FinanceTranslations[locale]; expect(data).toBeDefined(); expect(data.objects).toBeDefined(); @@ -35,8 +35,8 @@ describe('Finance i18n Translations', () => { it('should have consistent object keys across all locales', () => { const enKeys = Object.keys(FinanceTranslations.en.objects ?? {}).sort(); - const zhKeys = Object.keys(FinanceTranslations['zh-CN'].objects ?? {}).sort(); - const jaKeys = Object.keys(FinanceTranslations['ja-JP'].objects ?? {}).sort(); + const zhKeys = Object.keys(FinanceTranslations['zh'].objects ?? {}).sort(); + const jaKeys = Object.keys(FinanceTranslations['ja'].objects ?? {}).sort(); expect(enKeys).toEqual(zhKeys); expect(enKeys).toEqual(jaKeys); }); diff --git a/packages/finance/src/translations/finance.translation.ts b/packages/finance/src/translations/finance.translation.ts index 6ff77199..b69329e8 100644 --- a/packages/finance/src/translations/finance.translation.ts +++ b/packages/finance/src/translations/finance.translation.ts @@ -1,16 +1,16 @@ import type { TranslationBundle } from '@objectstack/spec/system'; import { en } from './en.js'; -import { zhCN } from './zh-CN.js'; -import { jaJP } from './ja-JP.js'; +import { zh } from './zh.js'; +import { ja } from './ja.js'; /** * Finance — Internationalization (i18n) * * Per-locale file splitting convention. - * Supported locales: en, zh-CN, ja-JP + * Supported locales: en, zh, ja */ export const FinanceTranslations: TranslationBundle = { en, - 'zh-CN': zhCN, - 'ja-JP': jaJP, + zh, + ja, }; diff --git a/packages/finance/src/translations/ja-JP.ts b/packages/finance/src/translations/ja.ts similarity index 99% rename from packages/finance/src/translations/ja-JP.ts rename to packages/finance/src/translations/ja.ts index 2f271d60..1ef720a3 100644 --- a/packages/finance/src/translations/ja-JP.ts +++ b/packages/finance/src/translations/ja.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 日本語 (ja-JP) — Finance Cloud Translations + * 日本語 (ja) — Finance Cloud Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const jaJP: TranslationData = { +export const ja: TranslationData = { objects: { contract: { label: '契約', diff --git a/packages/finance/src/translations/zh-CN.ts b/packages/finance/src/translations/zh.ts similarity index 99% rename from packages/finance/src/translations/zh-CN.ts rename to packages/finance/src/translations/zh.ts index 3430acb2..daaba5a1 100644 --- a/packages/finance/src/translations/zh-CN.ts +++ b/packages/finance/src/translations/zh.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 简体中文 (zh-CN) — Finance Cloud Translations + * 简体中文 (zh) — Finance Cloud Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const zhCN: TranslationData = { +export const zh: TranslationData = { objects: { contract: { label: '合同', diff --git a/packages/hr/__tests__/unit/schemas/i18n-translation.test.ts b/packages/hr/__tests__/unit/schemas/i18n-translation.test.ts index 62bedb07..42f0039e 100644 --- a/packages/hr/__tests__/unit/schemas/i18n-translation.test.ts +++ b/packages/hr/__tests__/unit/schemas/i18n-translation.test.ts @@ -13,12 +13,12 @@ describe('HR i18n Translations', () => { it('should contain all supported locales', () => { expect(HRTranslations).toHaveProperty('en'); - expect(HRTranslations).toHaveProperty('zh-CN'); - expect(HRTranslations).toHaveProperty('ja-JP'); + expect(HRTranslations).toHaveProperty('zh'); + expect(HRTranslations).toHaveProperty('ja'); }); it('should have non-empty objects section for each locale', () => { - for (const locale of ['en', 'zh-CN', 'ja-JP']) { + for (const locale of ['en', 'zh', 'ja']) { const data = HRTranslations[locale]; expect(data).toBeDefined(); expect(data.objects).toBeDefined(); @@ -35,8 +35,8 @@ describe('HR i18n Translations', () => { it('should have consistent object keys across all locales', () => { const enKeys = Object.keys(HRTranslations.en.objects ?? {}).sort(); - const zhKeys = Object.keys(HRTranslations['zh-CN'].objects ?? {}).sort(); - const jaKeys = Object.keys(HRTranslations['ja-JP'].objects ?? {}).sort(); + const zhKeys = Object.keys(HRTranslations['zh'].objects ?? {}).sort(); + const jaKeys = Object.keys(HRTranslations['ja'].objects ?? {}).sort(); expect(enKeys).toEqual(zhKeys); expect(enKeys).toEqual(jaKeys); }); diff --git a/packages/hr/src/translations/hr.translation.ts b/packages/hr/src/translations/hr.translation.ts index 2890bf9d..6bcc21ec 100644 --- a/packages/hr/src/translations/hr.translation.ts +++ b/packages/hr/src/translations/hr.translation.ts @@ -1,16 +1,16 @@ import type { TranslationBundle } from '@objectstack/spec/system'; import { en } from './en.js'; -import { zhCN } from './zh-CN.js'; -import { jaJP } from './ja-JP.js'; +import { zh } from './zh.js'; +import { ja } from './ja.js'; /** * Human Capital Management — Internationalization (i18n) * * Per-locale file splitting convention. - * Supported locales: en, zh-CN, ja-JP + * Supported locales: en, zh, ja */ export const HRTranslations: TranslationBundle = { en, - 'zh-CN': zhCN, - 'ja-JP': jaJP, + zh, + ja, }; diff --git a/packages/hr/src/translations/ja-JP.ts b/packages/hr/src/translations/ja.ts similarity index 99% rename from packages/hr/src/translations/ja-JP.ts rename to packages/hr/src/translations/ja.ts index 3adcf5dd..8fe00f43 100644 --- a/packages/hr/src/translations/ja-JP.ts +++ b/packages/hr/src/translations/ja.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 日本語 (ja-JP) — Human Capital Management Translations + * 日本語 (ja) — Human Capital Management Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const jaJP: TranslationData = { +export const ja: TranslationData = { objects: { department: { label: '部門', diff --git a/packages/hr/src/translations/zh-CN.ts b/packages/hr/src/translations/zh.ts similarity index 99% rename from packages/hr/src/translations/zh-CN.ts rename to packages/hr/src/translations/zh.ts index cd507d16..41afabc9 100644 --- a/packages/hr/src/translations/zh-CN.ts +++ b/packages/hr/src/translations/zh.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 简体中文 (zh-CN) — Human Capital Management Translations + * 简体中文 (zh) — Human Capital Management Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const zhCN: TranslationData = { +export const zh: TranslationData = { objects: { department: { label: '部门', diff --git a/packages/marketing/__tests__/unit/schemas/i18n-translation.test.ts b/packages/marketing/__tests__/unit/schemas/i18n-translation.test.ts index a32d32c6..83ebbf24 100644 --- a/packages/marketing/__tests__/unit/schemas/i18n-translation.test.ts +++ b/packages/marketing/__tests__/unit/schemas/i18n-translation.test.ts @@ -13,12 +13,12 @@ describe('Marketing i18n Translations', () => { it('should contain all supported locales', () => { expect(MarketingTranslations).toHaveProperty('en'); - expect(MarketingTranslations).toHaveProperty('zh-CN'); - expect(MarketingTranslations).toHaveProperty('ja-JP'); + expect(MarketingTranslations).toHaveProperty('zh'); + expect(MarketingTranslations).toHaveProperty('ja'); }); it('should have non-empty objects section for each locale', () => { - for (const locale of ['en', 'zh-CN', 'ja-JP']) { + for (const locale of ['en', 'zh', 'ja']) { const data = MarketingTranslations[locale]; expect(data).toBeDefined(); expect(data.objects).toBeDefined(); @@ -35,8 +35,8 @@ describe('Marketing i18n Translations', () => { it('should have consistent object keys across all locales', () => { const enKeys = Object.keys(MarketingTranslations.en.objects ?? {}).sort(); - const zhKeys = Object.keys(MarketingTranslations['zh-CN'].objects ?? {}).sort(); - const jaKeys = Object.keys(MarketingTranslations['ja-JP'].objects ?? {}).sort(); + const zhKeys = Object.keys(MarketingTranslations['zh'].objects ?? {}).sort(); + const jaKeys = Object.keys(MarketingTranslations['ja'].objects ?? {}).sort(); expect(enKeys).toEqual(zhKeys); expect(enKeys).toEqual(jaKeys); }); diff --git a/packages/marketing/src/translations/ja-JP.ts b/packages/marketing/src/translations/ja.ts similarity index 99% rename from packages/marketing/src/translations/ja-JP.ts rename to packages/marketing/src/translations/ja.ts index e5037179..a82e1dba 100644 --- a/packages/marketing/src/translations/ja-JP.ts +++ b/packages/marketing/src/translations/ja.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 日本語 (ja-JP) — Marketing Cloud Translations + * 日本語 (ja) — Marketing Cloud Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const jaJP: TranslationData = { +export const ja: TranslationData = { objects: { campaign: { label: 'キャンペーン', diff --git a/packages/marketing/src/translations/marketing.translation.ts b/packages/marketing/src/translations/marketing.translation.ts index e88fed87..feceae6b 100644 --- a/packages/marketing/src/translations/marketing.translation.ts +++ b/packages/marketing/src/translations/marketing.translation.ts @@ -1,7 +1,7 @@ import type { TranslationBundle } from '@objectstack/spec/system'; import { en } from './en.js'; -import { zhCN } from './zh-CN.js'; -import { jaJP } from './ja-JP.js'; +import { zh } from './zh.js'; +import { ja } from './ja.js'; /** * Marketing Cloud — Internationalization (i18n) @@ -16,10 +16,10 @@ import { jaJP } from './ja-JP.js'; * - App & navigation group labels * - Common UI messages, validation messages * - * Supported locales: en, zh-CN, ja-JP + * Supported locales: en, zh, ja */ export const MarketingTranslations: TranslationBundle = { en, - 'zh-CN': zhCN, - 'ja-JP': jaJP, + zh, + ja, }; diff --git a/packages/marketing/src/translations/zh-CN.ts b/packages/marketing/src/translations/zh.ts similarity index 99% rename from packages/marketing/src/translations/zh-CN.ts rename to packages/marketing/src/translations/zh.ts index 3cd37749..cfefddf3 100644 --- a/packages/marketing/src/translations/zh-CN.ts +++ b/packages/marketing/src/translations/zh.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 简体中文 (zh-CN) — Marketing Cloud Translations + * 简体中文 (zh) — Marketing Cloud Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const zhCN: TranslationData = { +export const zh: TranslationData = { objects: { campaign: { label: '营销活动', diff --git a/packages/products/__tests__/unit/schemas/i18n-translation.test.ts b/packages/products/__tests__/unit/schemas/i18n-translation.test.ts index a073d8b6..968b3ec2 100644 --- a/packages/products/__tests__/unit/schemas/i18n-translation.test.ts +++ b/packages/products/__tests__/unit/schemas/i18n-translation.test.ts @@ -13,12 +13,12 @@ describe('Products i18n Translations', () => { it('should contain all supported locales', () => { expect(ProductsTranslations).toHaveProperty('en'); - expect(ProductsTranslations).toHaveProperty('zh-CN'); - expect(ProductsTranslations).toHaveProperty('ja-JP'); + expect(ProductsTranslations).toHaveProperty('zh'); + expect(ProductsTranslations).toHaveProperty('ja'); }); it('should have non-empty objects section for each locale', () => { - for (const locale of ['en', 'zh-CN', 'ja-JP']) { + for (const locale of ['en', 'zh', 'ja']) { const data = ProductsTranslations[locale]; expect(data).toBeDefined(); expect(data.objects).toBeDefined(); @@ -35,8 +35,8 @@ describe('Products i18n Translations', () => { it('should have consistent object keys across all locales', () => { const enKeys = Object.keys(ProductsTranslations.en.objects ?? {}).sort(); - const zhKeys = Object.keys(ProductsTranslations['zh-CN'].objects ?? {}).sort(); - const jaKeys = Object.keys(ProductsTranslations['ja-JP'].objects ?? {}).sort(); + const zhKeys = Object.keys(ProductsTranslations['zh'].objects ?? {}).sort(); + const jaKeys = Object.keys(ProductsTranslations['ja'].objects ?? {}).sort(); expect(enKeys).toEqual(zhKeys); expect(enKeys).toEqual(jaKeys); }); diff --git a/packages/products/src/translations/ja-JP.ts b/packages/products/src/translations/ja.ts similarity index 99% rename from packages/products/src/translations/ja-JP.ts rename to packages/products/src/translations/ja.ts index f8d901f3..78cf11a9 100644 --- a/packages/products/src/translations/ja-JP.ts +++ b/packages/products/src/translations/ja.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 日本語 (ja-JP) — Products & Pricing Translations + * 日本語 (ja) — Products & Pricing Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const jaJP: TranslationData = { +export const ja: TranslationData = { objects: { product: { label: '商品', diff --git a/packages/products/src/translations/products.translation.ts b/packages/products/src/translations/products.translation.ts index 59412799..08a2cccd 100644 --- a/packages/products/src/translations/products.translation.ts +++ b/packages/products/src/translations/products.translation.ts @@ -1,16 +1,16 @@ import type { TranslationBundle } from '@objectstack/spec/system'; import { en } from './en.js'; -import { zhCN } from './zh-CN.js'; -import { jaJP } from './ja-JP.js'; +import { zh } from './zh.js'; +import { ja } from './ja.js'; /** * Products & Pricing — Internationalization (i18n) * * Per-locale file splitting convention. - * Supported locales: en, zh-CN, ja-JP + * Supported locales: en, zh, ja */ export const ProductsTranslations: TranslationBundle = { en, - 'zh-CN': zhCN, - 'ja-JP': jaJP, + zh, + ja, }; diff --git a/packages/products/src/translations/zh-CN.ts b/packages/products/src/translations/zh.ts similarity index 99% rename from packages/products/src/translations/zh-CN.ts rename to packages/products/src/translations/zh.ts index 434a6459..a5affa04 100644 --- a/packages/products/src/translations/zh-CN.ts +++ b/packages/products/src/translations/zh.ts @@ -1,11 +1,11 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 简体中文 (zh-CN) — Products & Pricing Translations + * 简体中文 (zh) — Products & Pricing Translations * * Per-locale file: one file per language, following the `per_locale` convention. */ -export const zhCN: TranslationData = { +export const zh: TranslationData = { objects: { product: { label: '产品', diff --git a/packages/support/__tests__/unit/schemas/i18n-translation.test.ts b/packages/support/__tests__/unit/schemas/i18n-translation.test.ts index a0e17912..c72909ee 100644 --- a/packages/support/__tests__/unit/schemas/i18n-translation.test.ts +++ b/packages/support/__tests__/unit/schemas/i18n-translation.test.ts @@ -13,12 +13,12 @@ describe('Support i18n Translations', () => { it('should contain all supported locales', () => { expect(SupportTranslations).toHaveProperty('en'); - expect(SupportTranslations).toHaveProperty('zh-CN'); - expect(SupportTranslations).toHaveProperty('ja-JP'); + expect(SupportTranslations).toHaveProperty('zh'); + expect(SupportTranslations).toHaveProperty('ja'); }); it('should have non-empty objects section for each locale', () => { - for (const locale of ['en', 'zh-CN', 'ja-JP']) { + for (const locale of ['en', 'zh', 'ja']) { const data = SupportTranslations[locale]; expect(data).toBeDefined(); expect(data.objects).toBeDefined(); @@ -35,8 +35,8 @@ describe('Support i18n Translations', () => { it('should have consistent object keys across all locales', () => { const enKeys = Object.keys(SupportTranslations.en.objects ?? {}).sort(); - const zhKeys = Object.keys(SupportTranslations['zh-CN'].objects ?? {}).sort(); - const jaKeys = Object.keys(SupportTranslations['ja-JP'].objects ?? {}).sort(); + const zhKeys = Object.keys(SupportTranslations['zh'].objects ?? {}).sort(); + const jaKeys = Object.keys(SupportTranslations['ja'].objects ?? {}).sort(); expect(enKeys).toEqual(zhKeys); expect(enKeys).toEqual(jaKeys); }); diff --git a/packages/support/src/translations/ja-JP.ts b/packages/support/src/translations/ja.ts similarity index 99% rename from packages/support/src/translations/ja-JP.ts rename to packages/support/src/translations/ja.ts index 6341704d..de792955 100644 --- a/packages/support/src/translations/ja-JP.ts +++ b/packages/support/src/translations/ja.ts @@ -1,14 +1,14 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 日本語 (ja-JP) — カスタマーサポート翻訳 + * 日本語 (ja) — カスタマーサポート翻訳 * * Per-locale file: one file per language, following the `per_locale` convention. * Each file exports a single `TranslationData` object for its locale. * * Covers all 23 Support objects with field labels, select options, and help texts. */ -export const jaJP: TranslationData = { +export const ja: TranslationData = { objects: { case: { label: 'ケース', diff --git a/packages/support/src/translations/support.translation.ts b/packages/support/src/translations/support.translation.ts index 3b7e1464..c54757a8 100644 --- a/packages/support/src/translations/support.translation.ts +++ b/packages/support/src/translations/support.translation.ts @@ -1,7 +1,7 @@ import type { TranslationBundle } from '@objectstack/spec/system'; import { en } from './en.js'; -import { zhCN } from './zh-CN.js'; -import { jaJP } from './ja-JP.js'; +import { zh } from './zh.js'; +import { ja } from './ja.js'; /** * Customer Support — Internationalization (i18n) @@ -16,10 +16,10 @@ import { jaJP } from './ja-JP.js'; * - App & navigation group labels * - Common UI messages, validation messages * - * Supported locales: en, zh-CN, ja-JP + * Supported locales: en, zh, ja */ export const SupportTranslations: TranslationBundle = { en, - 'zh-CN': zhCN, - 'ja-JP': jaJP, + zh, + ja, }; diff --git a/packages/support/src/translations/zh-CN.ts b/packages/support/src/translations/zh.ts similarity index 99% rename from packages/support/src/translations/zh-CN.ts rename to packages/support/src/translations/zh.ts index 371b510d..fc6765ec 100644 --- a/packages/support/src/translations/zh-CN.ts +++ b/packages/support/src/translations/zh.ts @@ -1,14 +1,14 @@ import type { TranslationData } from '@objectstack/spec/system'; /** - * 简体中文 (zh-CN) — 客户支持翻译 + * 简体中文 (zh) — 客户支持翻译 * * Per-locale file: one file per language, following the `per_locale` convention. * Each file exports a single `TranslationData` object for its locale. * * Covers all 23 Support objects with field labels, select options, and help texts. */ -export const zhCN: TranslationData = { +export const zh: TranslationData = { objects: { case: { label: '工单',