Agent skill
b2c-localization
Localize templates, forms, and content in B2C Commerce. Use when adding translations, working with resource bundles (*.properties files), using Resource.msg or Resource.msgf, or implementing multi-locale/multi-language/multi-country support. Covers i18n, internationalization, translation files, locale folders, string externalization, and date/currency formatting.
Install this agent skill to your Project
npx add-skill https://github.com/SalesforceCommerceCloud/b2c-developer-tooling/tree/main/skills/b2c/skills/b2c-localization
SKILL.md
Localization Skill
This skill guides you through localizing B2C Commerce storefronts for multiple languages and regions.
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 |
Locale Format
Locales follow ISO standards: {language}_{country}
| Format | Example | Description |
|---|---|---|
en |
English | Language only |
en_US |
English/USA | Language + country |
fr_CA |
French/Canada | Language + country |
de_DE |
German/Germany | Language + country |
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
Property File Format
account.properties (default):
##############################################
# Account Pages
##############################################
account.title=My Account
account.greeting=Welcome back
account.logout=Sign Out
# Account Dashboard
dashboard.title=Dashboard
dashboard.orders=Order History
dashboard.addresses=Address Book
dashboard.wishlist=Wishlist
# Profile
profile.title=Profile
profile.firstName=First Name
profile.lastName=Last Name
profile.email=Email Address
profile.save=Save Changes
account_fr.properties (French):
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
<!-- 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)
Parameterized Messages
Property:
cart.itemCount=You have {0} items in your cart
greeting.personalized=Hello, {0} {1}!
order.confirmation=Order #{0} placed on {1}
Template:
${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:
- Look in
/resources/fr_CA/account.properties - If not found, look in
/resources/fr/account.properties - If not found, look in
/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
Referencing Static Files
<!-- 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 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:
form.email.label=Email Address
form.email.required=Email is required
form.email.invalid=Please enter a valid email address
forms_fr.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
Locale-Aware URLs
<!-- 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
<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
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
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
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:
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
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
- Use UTF-8 for all property files (required for non-ASCII characters)
- Organize bundles by page/feature not by language
- Keep keys descriptive -
account.profile.firstNamenotlabel1 - Use parameters for dynamic values - don't concatenate strings
- Test all locales - ensure fallback works correctly
- Don't hardcode text in templates or scripts
Detailed Reference
- Localization Patterns - Complete patterns and examples
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
api-client-development
Creating typed API clients with OpenAPI specs, authentication, and OAuth scopes for SCAPI and similar APIs. Use when adding a new SCAPI client, generating types from an OpenAPI spec, setting up OAuth middleware, or integrating a new Commerce API endpoint.
testing
Writing tests for the B2C CLI project using Mocha, Chai, and MSW. Use when writing unit or integration tests, mocking HTTP requests with MSW, testing CLI commands with oclif, isolating config in tests, setting up test coverage, using sinon stubs, c8 coverage, capturing or silencing stdout, or creating MSW handlers.
documentation
Updating user guides, CLI reference, and API documentation for the B2C CLI project. Use when adding or changing CLI command docs, writing JSDoc for TypeDoc generation, updating Vitepress sidebar config, or creating new guide pages.
cli-command-development
Creating new CLI commands and topics for the B2C CLI using oclif. Use when adding a new command, creating a topic, adding flags or arguments, implementing table output, or extending BaseCommand/OAuthCommand/InstanceCommand.
sdk-module-development
Adding new modules and exports to the @salesforce/b2c-tooling-sdk package. Use when creating a new SDK module, adding barrel file exports, configuring package.json exports, or building client factory functions.
b2c-code
Deploy and manage code versions/cartridges on B2C Commerce instances/sandboxes with the b2c cli. Always reference when using the CLI to upload cartridges, deploy code, activate code versions, manage code versions, or watch for file changes during development.
Didn't find tool you were looking for?