b2c-localization

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Localization Skill

本地化技能指南

This skill guides you through localizing B2C Commerce storefronts for multiple languages and regions.
本指南将引导你为B2C Commerce店面实现多语言与多区域的本地化处理。

Overview

概述

B2C Commerce supports localization through:
ComponentApproach
TemplatesSingle template set + resource bundles
FormsShared definitions + locale-specific labels
Static contentLocale-specific folders
Product dataLocalizable attributes
B2C Commerce通过以下组件支持本地化:
组件实现方式
模板单一模板集 + 资源包
表单共享定义 + 区域专属标签
静态内容区域专属文件夹
商品数据可本地化属性

Locale Format

区域语言格式

Locales follow ISO standards:
{language}_{country}
FormatExampleDescription
en
EnglishLanguage only
en_US
English/USALanguage + country
fr_CA
French/CanadaLanguage + country
de_DE
German/GermanyLanguage + country
区域语言遵循ISO标准:
{language}_{country}
格式示例说明
en
English仅语言
en_US
English/USA语言 + 国家
fr_CA
French/Canada语言 + 国家
de_DE
German/Germany语言 + 国家

Resource Bundles

资源包

Directory Structure

目录结构

/cartridge
    /templates
        /resources
            account.properties          # Default (English)
            checkout.properties
            /fr
                account.properties      # French
                checkout.properties
            /de
                account.properties      # German
                checkout.properties
            /fr_CA
                account.properties      # French Canadian
/cartridge
    /templates
        /resources
            account.properties          # Default (English)
            checkout.properties
            /fr
                account.properties      # French
                checkout.properties
            /de
                account.properties      # German
                checkout.properties
            /fr_CA
                account.properties      # French Canadian

Property File Format

属性文件格式

account.properties (default):
properties
##############################################
account.properties(默认):
properties
##############################################

Account Pages

Account Pages

############################################## account.title=My Account account.greeting=Welcome back account.logout=Sign Out
############################################## account.title=My Account account.greeting=Welcome back account.logout=Sign Out

Account Dashboard

Account Dashboard

dashboard.title=Dashboard dashboard.orders=Order History dashboard.addresses=Address Book dashboard.wishlist=Wishlist
dashboard.title=Dashboard dashboard.orders=Order History dashboard.addresses=Address Book dashboard.wishlist=Wishlist

Profile

Profile

profile.title=Profile profile.firstName=First Name profile.lastName=Last Name profile.email=Email Address profile.save=Save Changes

**account_fr.properties (French):**
```properties
account.title=Mon compte
account.greeting=Bon retour
account.logout=Se déconnecter

dashboard.title=Tableau de bord
dashboard.orders=Historique des commandes
dashboard.addresses=Carnet d'adresses
dashboard.wishlist=Liste de souhaits

profile.title=Profil
profile.firstName=Prénom
profile.lastName=Nom
profile.email=Adresse e-mail
profile.save=Enregistrer les modifications
profile.title=Profile profile.firstName=First Name profile.lastName=Last Name profile.email=Email Address profile.save=Save Changes

**account_fr.properties(法语):**
```properties
account.title=Mon compte
account.greeting=Bon retour
account.logout=Se déconnecter

dashboard.title=Tableau de bord
dashboard.orders=Historique des commandes
dashboard.addresses=Carnet d'adresses
dashboard.wishlist=Liste de souhaits

profile.title=Profil
profile.firstName=Prénom
profile.lastName=Nom
profile.email=Adresse e-mail
profile.save=Enregistrer les modifications

Using Resources in Templates

在模板中使用资源

html
<!-- Simple message -->
<h1>${Resource.msg('account.title', 'account', null)}</h1>

<!-- With fallback -->
<p>${Resource.msg('account.greeting', 'account', 'Welcome')}</p>

<!-- With parameters -->
<p>${Resource.msgf('cart.items', 'cart', null, cartCount)}</p>
Resource.msg() parameters:
  1. Key name
  2. Bundle name (filename without extension)
  3. Default value (null = use key if not found)
html
<!-- Simple message -->
<h1>${Resource.msg('account.title', 'account', null)}</h1>

<!-- With fallback -->
<p>${Resource.msg('account.greeting', 'account', 'Welcome')}</p>

<!-- With parameters -->
<p>${Resource.msgf('cart.items', 'cart', null, cartCount)}</p>
Resource.msg() 参数说明:
  1. 键名
  2. 资源包名称(不带扩展名的文件名)
  3. 默认值(null表示未找到时使用键名)

Parameterized Messages

带参数的消息

Property:
properties
cart.itemCount=You have {0} items in your cart
greeting.personalized=Hello, {0} {1}!
order.confirmation=Order #{0} placed on {1}
Template:
html
${Resource.msgf('cart.itemCount', 'cart', null, itemCount)}
${Resource.msgf('greeting.personalized', 'common', null, firstName, lastName)}
属性配置:
properties
cart.itemCount=You have {0} items in your cart
greeting.personalized=Hello, {0} {1}!
order.confirmation=Order #{0} placed on {1}
模板调用:
html
${Resource.msgf('cart.itemCount', 'cart', null, itemCount)}
${Resource.msgf('greeting.personalized', 'common', null, firstName, lastName)}

Locale Fallback

区域语言回退机制

B2C Commerce uses a fallback chain:
fr_CA
fr
default
Example: Requesting
fr_CA
:
  1. Look in
    /resources/fr_CA/account.properties
  2. If not found, look in
    /resources/fr/account.properties
  3. If not found, look in
    /resources/account.properties
B2C Commerce采用回退链:
fr_CA
fr
default
示例:请求
fr_CA
时的查找顺序
  1. 查找
    /resources/fr_CA/account.properties
  2. 若未找到,查找
    /resources/fr/account.properties
  3. 若仍未找到,查找
    /resources/account.properties

Static Files

静态文件

Directory Structure

目录结构

/cartridge
    /static
        /default
            /css
                style.css
            /images
                logo.png
                buttons/
                    submit.png
            /js
                main.js
        /fr
            /images
                buttons/
                    submit.png      # French text on button
        /de
            /images
                buttons/
                    submit.png      # German text on button
/cartridge
    /static
        /default
            /css
                style.css
            /images
                logo.png
                buttons/
                    submit.png
            /js
                main.js
        /fr
            /images
                buttons/
                    submit.png      # French text on button
        /de
            /images
                buttons/
                    submit.png      # German text on button

Referencing Static Files

引用静态文件

html
<!-- Uses locale-specific version if available -->
<img src="${URLUtils.staticURL('/images/buttons/submit.png')}" alt="Submit"/>

<!-- CSS (usually not localized) -->
<link rel="stylesheet" href="${URLUtils.staticURL('/css/style.css')}"/>
html
<!-- Uses locale-specific version if available -->
<img src="${URLUtils.staticURL('/images/buttons/submit.png')}" alt="Submit"/>

<!-- CSS (usually not localized) -->
<link rel="stylesheet" href="${URLUtils.staticURL('/css/style.css')}"/>

Forms Localization

表单本地化

Form Definition

表单定义

xml
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.demandware.com/xml/form/2008-04-19">
    <field formid="email" label="form.email.label" type="string"
           mandatory="true"
           missing-error="form.email.required"
           parse-error="form.email.invalid"/>
</form>
xml
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.demandware.com/xml/form/2008-04-19">
    <field formid="email" label="form.email.label" type="string"
           mandatory="true"
           missing-error="form.email.required"
           parse-error="form.email.invalid"/>
</form>

Resource Bundle

资源包配置

forms.properties:
properties
form.email.label=Email Address
form.email.required=Email is required
form.email.invalid=Please enter a valid email address
forms_fr.properties:
properties
form.email.label=Adresse e-mail
form.email.required=L'email est requis
form.email.invalid=Veuillez entrer une adresse e-mail valide
forms.properties:
properties
form.email.label=Email Address
form.email.required=Email is required
form.email.invalid=Please enter a valid email address
forms_fr.properties:
properties
form.email.label=Adresse e-mail
form.email.required=L'email est requis
form.email.invalid=Veuillez entrer une adresse e-mail valide

URL Localization

URL本地化

Locale-Aware URLs

支持区域语言的URL

html
<!-- Current locale URL -->
<a href="${URLUtils.url('Product-Show', 'pid', 'ABC123')}">View Product</a>

<!-- Specific locale URL -->
<a href="${URLUtils.url(new URLAction('Product-Show', 'MySite', 'fr'))}">
    Voir le produit
</a>
html
<!-- Current locale URL -->
<a href="${URLUtils.url('Product-Show', 'pid', 'ABC123')}">View Product</a>

<!-- Specific locale URL -->
<a href="${URLUtils.url(new URLAction('Product-Show', 'MySite', 'fr'))}">
    Voir le produit
</a>

Language Switcher

语言切换器

html
<isscript>
    var Site = require('dw/system/Site');
    var URLAction = require('dw/web/URLAction');
    var URLUtils = require('dw/web/URLUtils');
    var Locale = require('dw/util/Locale');
</isscript>

<ul class="language-switcher">
    <isloop items="${Site.current.allowedLocales}" var="localeId">
        <isscript>
            var locale = new Locale(localeId);
            var url = URLUtils.url(new URLAction('Home-Show', Site.current.ID, localeId));
        </isscript>
        <li class="${request.locale == localeId ? 'active' : ''}">
            <a href="${url}">${locale.displayLanguage}</a>
        </li>
    </isloop>
</ul>
html
<isscript>
    var Site = require('dw/system/Site');
    var URLAction = require('dw/web/URLAction');
    var URLUtils = require('dw/web/URLUtils');
    var Locale = require('dw/util/Locale');
</isscript>

<ul class="language-switcher">
    <isloop items="${Site.current.allowedLocales}" var="localeId">
        <isscript>
            var locale = new Locale(localeId);
            var url = URLUtils.url(new URLAction('Home-Show', Site.current.ID, localeId));
        </isscript>
        <li class="${request.locale == localeId ? 'active' : ''}">
            <a href="${url}">${locale.displayLanguage}</a>
        </li>
    </isloop>
</ul>

Controller Localization

控制器本地化

Accessing Current Locale

获取当前区域语言

javascript
var Locale = require('dw/util/Locale');

server.get('Show', function (req, res, next) {
    var currentLocale = Locale.getLocale(req.locale.id);

    res.render('mytemplate', {
        locale: req.locale.id,
        language: currentLocale.language,
        country: currentLocale.country,
        displayLanguage: currentLocale.displayLanguage,
        displayCountry: currentLocale.displayCountry
    });
    next();
});
javascript
var Locale = require('dw/util/Locale');

server.get('Show', function (req, res, next) {
    var currentLocale = Locale.getLocale(req.locale.id);

    res.render('mytemplate', {
        locale: req.locale.id,
        language: currentLocale.language,
        country: currentLocale.country,
        displayLanguage: currentLocale.displayLanguage,
        displayCountry: currentLocale.displayCountry
    });
    next();
});

Locale-Specific Logic

区域专属逻辑

javascript
server.get('Checkout', function (req, res, next) {
    var locale = req.locale.id;

    // Locale-specific date format
    var dateFormat = locale.startsWith('en_US') ? 'MM/dd/yyyy' : 'dd/MM/yyyy';

    // Locale-specific content
    var termsContentId = 'terms-' + locale.replace('_', '-').toLowerCase();

    res.render('checkout', {
        dateFormat: dateFormat,
        termsContentId: termsContentId
    });
    next();
});
javascript
server.get('Checkout', function (req, res, next) {
    var locale = req.locale.id;

    // Locale-specific date format
    var dateFormat = locale.startsWith('en_US') ? 'MM/dd/yyyy' : 'dd/MM/yyyy';

    // Locale-specific content
    var termsContentId = 'terms-' + locale.replace('_', '-').toLowerCase();

    res.render('checkout', {
        dateFormat: dateFormat,
        termsContentId: termsContentId
    });
    next();
});

Email Templates

邮件模板

Setting Locale

设置区域语言

javascript
var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var Mail = require('dw/net/Mail');

function sendOrderConfirmation(order, locale) {
    var template = new Template('mail/orderconfirmation', locale);
    var model = new HashMap();
    model.put('order', order);

    var content = template.render(model).text;

    var mail = new Mail();
    mail.addTo(order.customerEmail);
    mail.setFrom('orders@example.com');
    mail.setSubject(Resource.msg('email.order.subject', 'email', null));
    mail.setContent(content, 'text/html', 'UTF-8');
    mail.send();
}
javascript
var Template = require('dw/util/Template');
var HashMap = require('dw/util/HashMap');
var Mail = require('dw/net/Mail');

function sendOrderConfirmation(order, locale) {
    var template = new Template('mail/orderconfirmation', locale);
    var model = new HashMap();
    model.put('order', order);

    var content = template.render(model).text;

    var mail = new Mail();
    mail.addTo(order.customerEmail);
    mail.setFrom('orders@example.com');
    mail.setSubject(Resource.msg('email.order.subject', 'email', null));
    mail.setContent(content, 'text/html', 'UTF-8');
    mail.send();
}

Currency Formatting

货币格式化

Currency is tied to locale:
javascript
var Money = require('dw/value/Money');
var StringUtils = require('dw/util/StringUtils');

// Format with locale
var price = new Money(99.99, 'USD');
var formatted = StringUtils.formatMoney(price);  // Uses current locale

// In template
<isprint value="${product.priceModel.price}" style="CURRENCY"/>
货币与区域语言绑定:
javascript
var Money = require('dw/value/Money');
var StringUtils = require('dw/util/StringUtils');

// Format with locale
var price = new Money(99.99, 'USD');
var formatted = StringUtils.formatMoney(price);  // Uses current locale

// In template
<isprint value="${product.priceModel.price}" style="CURRENCY"/>

Date Formatting

日期格式化

javascript
var StringUtils = require('dw/util/StringUtils');
var Calendar = require('dw/util/Calendar');

var date = new Calendar();
var formatted = StringUtils.formatCalendar(date, 'yyyy-MM-dd');  // ISO format
var localized = StringUtils.formatCalendar(date, 'MMMM d, yyyy');  // Locale-aware
javascript
var StringUtils = require('dw/util/StringUtils');
var Calendar = require('dw/util/Calendar');

var date = new Calendar();
var formatted = StringUtils.formatCalendar(date, 'yyyy-MM-dd');  // ISO format
var localized = StringUtils.formatCalendar(date, 'MMMM d, yyyy');  // Locale-aware

Best Practices

最佳实践

  1. Use UTF-8 for all property files (required for non-ASCII characters)
  2. Organize bundles by page/feature not by language
  3. Keep keys descriptive -
    account.profile.firstName
    not
    label1
  4. Use parameters for dynamic values - don't concatenate strings
  5. Test all locales - ensure fallback works correctly
  6. Don't hardcode text in templates or scripts
  1. 所有属性文件使用UTF-8编码(非ASCII字符必需)
  2. 按页面/功能组织资源包,而非按语言
  3. 保持键名具有描述性 - 使用
    account.profile.firstName
    而非
    label1
  4. 动态值使用参数传递 - 不要拼接字符串
  5. 测试所有区域语言 - 确保回退机制正常工作
  6. 不要在模板或脚本中硬编码文本

Detailed Reference

详细参考

  • Localization Patterns - Complete patterns and examples
  • Localization Patterns - 完整的模式与示例