b2c-localization
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLocalization Skill
本地化技能指南
This skill guides you through localizing B2C Commerce storefronts for multiple languages and regions.
本指南将引导你为B2C Commerce店面实现多语言与多区域的本地化处理。
Overview
概述
B2C Commerce supports localization through:
| Component | Approach |
|---|---|
| Templates | Single template set + resource bundles |
| Forms | Shared definitions + locale-specific labels |
| Static content | Locale-specific folders |
| Product data | Localizable attributes |
B2C Commerce通过以下组件支持本地化:
| 组件 | 实现方式 |
|---|---|
| 模板 | 单一模板集 + 资源包 |
| 表单 | 共享定义 + 区域专属标签 |
| 静态内容 | 区域专属文件夹 |
| 商品数据 | 可本地化属性 |
Locale Format
区域语言格式
Locales follow ISO standards:
{language}_{country}| Format | Example | Description |
|---|---|---|
| English | Language only |
| English/USA | Language + country |
| French/Canada | Language + country |
| German/Germany | Language + country |
区域语言遵循ISO标准:
{language}_{country}| 格式 | 示例 | 说明 |
|---|---|---|
| English | 仅语言 |
| English/USA | 语言 + 国家 |
| French/Canada | 语言 + 国家 |
| 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 CanadianProperty 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 modificationsprofile.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 modificationsUsing 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:
- Key name
- Bundle name (filename without extension)
- 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() 参数说明:
- 键名
- 资源包名称(不带扩展名的文件名)
- 默认值(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_CAfrdefaultExample: Requesting :
fr_CA- Look in
/resources/fr_CA/account.properties - If not found, look in
/resources/fr/account.properties - If not found, look in
/resources/account.properties
B2C Commerce采用回退链: → →
fr_CAfrdefault示例:请求时的查找顺序
fr_CA- 查找
/resources/fr_CA/account.properties - 若未找到,查找
/resources/fr/account.properties - 若仍未找到,查找
/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 buttonReferencing 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 addressforms_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 valideforms.properties:
properties
form.email.label=Email Address
form.email.required=Email is required
form.email.invalid=Please enter a valid email addressforms_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 valideURL 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-awarejavascript
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-awareBest Practices
最佳实践
- Use UTF-8 for all property files (required for non-ASCII characters)
- Organize bundles by page/feature not by language
- Keep keys descriptive - not
account.profile.firstNamelabel1 - Use parameters for dynamic values - don't concatenate strings
- Test all locales - ensure fallback works correctly
- Don't hardcode text in templates or scripts
- 所有属性文件使用UTF-8编码(非ASCII字符必需)
- 按页面/功能组织资源包,而非按语言
- 保持键名具有描述性 - 使用而非
account.profile.firstNamelabel1 - 动态值使用参数传递 - 不要拼接字符串
- 测试所有区域语言 - 确保回退机制正常工作
- 不要在模板或脚本中硬编码文本
Detailed Reference
详细参考
- Localization Patterns - Complete patterns and examples
- Localization Patterns - 完整的模式与示例