SAP Spartacus Translation(翻译) 相关话题

255 阅读3分钟

官网地址

在典型的 Spartacus 店面中,大部分内容要么来自 CMS,要么来自产品内容。 但是,对于店面站点标签(例如按钮中的文本),内容存储在单独的文件中,并且可以对这些文件进行本地化(即翻译)。

Spartacus 使用 i18next 库作为其翻译机制,并使用 i18next-xhr-backend 延迟加载翻译块。 这两个库都有丰富的 API,但 Spartacus 只支持其中的一部分,并将它们视为实现细节。 因此,Spartacus 不支持在您的应用程序中自定义使用 i18next。

为了快速开始,从@spartacus/assets 导入预定义的 Spartacus 翻译(目前只有英文),并在 B2cStorefrontModule 的配置中注册它们。 下面是一个例子:

import { translations, translationChunksConfig } from '@spartacus/assets';

// ...

imports: [
  B2cStorefrontModule.withConfig({
    i18n: {
        resources: translations,
        chunks: translationChunksConfig
    }
  })
];

Adding Translations for Other Languages

除了使用预定义的 Spartacus 翻译,您还可以提供自己的英语翻译,并添加其他语言的翻译。 下面是一个例子:

import { translations, translationChunksConfig } from '@spartacus/assets';

// ...

imports: [
  B2cStorefrontModule.withConfig({
    i18n: {
        resources: {
            en: translations, // or YOUR_ENGLISH_TRANSLATIONS,
            de: YOUR_GERMAN_TRANSLATIONS,
            ...
        },
        chunks: translationChunksConfig
    }
  })
];

这会将翻译编译到您的应用程序 JS 包中。 尽管这对于快速启动来说很好,但在生产中,您可能希望对翻译块利用延迟加载。

Overwriting Individual Translations

要覆盖单个翻译,需要在默认翻译之后提供具有覆盖的对象。 下面是一个例子:

// app.module

import { translations } from '@spartacus/assets';

// ...

export const translationOverwrites = {
  en: { // lang
    cart: { // chunk
      cartDetails: { // keys (nested)
        proceedToCheckout: 'Proceed to Checkout',
      },
    },
  },
};

// ...

imports: [
    B2cStorefrontModule.withConfig({
        i18n: { resources: translations }
    }),
    ConfigModule.withConfig({
        i18n: { resources: translationOverwrites }
    })
]

Fallback Language

如果缺少特定键的翻译,生产模式下的店面将显示不间断的空格字符。 为了更容易捕获丢失的键,在开发模式下,Spartacus 显示翻译键前面带有块的名称和冒号(例如,[common:form.confirm])。

为了在缺少翻译时提供更好的用户体验,您可以指定后备语言。 设置 fallbackLang 选项可确保对于每个丢失的翻译,使用后备语言的等效项。

以下是使用英语作为后备语言的示例配置:

import { translations, translationChunksConfig } from '@spartacus/assets';

// ...

imports: [
  B2cStorefrontModule.withConfig({
    i18n: {
        resources: translations,
        chunks: translationChunksConfig,
        fallbackLang: 'en',
    }
  })
];

Lazy Loading

翻译按语言和命名块进行结构化,因此您只能加载当前语言和当前页面的翻译资源。 以下是翻译资源的结构示例:

interface TranslationResources {
  [lang: string]: {
    [chunkName: string]: {
      [key: string]: any; // value or nested object with keys
    };
  };
}

要利用延迟加载,您需要为每种特定语言和块提供不同的 JSON 文件,并使用 {{lng}} 语言占位符和 {{ns}} 占位符为块配置 JSON 文件的 URL。 下面是一个例子:

imports: [
  B2cStorefrontModule.withConfig({
    i18n: {
        backend: {
            loadPath: 'assets/i18n-assets/{{lng}}/{{ns}}.json'
        },
        chunks: translationChunksConfig
    }
  })
];

对于 Spartacus,您可以在 @spartacus/storefront 的 /i18n-assets 文件夹中找到带有翻译的预定义 JSON 文件。 您需要从您自己的自定义端点或通过将它们复制到 Angular 应用程序的 /assets 文件夹中来提供这些文件。

cp ./node_modules/@spartacus/assets/i18n-assets ./src/assets -r

Handling Translations in HTML

要处理 HTML 中的翻译,您可以使用 cxTranslate 管道。 下面是一个例子:

<input placeholder="{{ 'searchBox.searchHere' | cxTranslate }}" />

Configuring Chunks and Namespace Mapping

每个 key 都属于一个命名空间,每个命名空间都封装在一个chunk中(比如下面例子中的i18n.chunks)。 需要配置来解析哪个键属于哪个命名空间和块。 下面是一个例子

imports: [
  B2cStorefrontModule.withConfig({
    i18n: {
        backend: {
            loadPath: 'assets/i18n-assets/{{lng}}/{{ns}}.json'
        },
        chunks: {
            ...
            common: ['searchBox', 'sorting', ...],
            cart: ['cartDetails', 'cartItems', ...]
            product: ['productDetails', 'productList', ...]
            ...
        }
    }
  })
];

对应的 common.ts 的例子:

cxTranslate 也支持传入参数:

Using Translations in TypeScript Code

也可以使用 TypeScript 代码进行翻译,即利用 Spartacus 的 TranslationService 服务。

import { TranslationService } from '@spartacus/core';

constructor(
    private translation: TranslationService
) {}

getPaymentCardContent(payment: PaymentDetails): Observable<Card> {
   return combineLatest([
     this.translation.translate('paymentForm.payment'),
     this.translation.translate('paymentCard.expires', {
       month: payment.expiryMonth,
       year: payment.expiryYear,
     }),
   ]).pipe(
     map(([textTitle, textExpires]) => {
       return {
         title: textTitle,
         textBold: payment.accountHolderName,
         text: [payment.cardType.name, payment.cardNumber, textExpires],
       };
     })
   );
}

HTML 里的用法:

<cx-card
    [content]="getPaymentCardContent(order.paymentInfo) | async"
></cx-card>