Add translations and multi-language support to B2C Commerce storefronts. Use this skill whenever the user needs to translate a storefront, add a new locale, create or edit .properties resource bundles, display translated strings in templates, format dates or currencies for different regions, or build a language switcher. Also use when they mention Resource.msg, Resource.msgf, locale fallback, or i18n — even if they just say "we need French translations" or "make this page work in multiple languages".
Add translations and multi-language support to B2C Commerce storefronts. Use this skill whenever the user needs to translate a storefront, add a new locale, create or edit .properties resource bundles, display translated strings in templates, format dates or currencies for different regions, or build a language switcher. Also use when they mention Resource.msg, Resource.msgf, locale fallback, or i18n — even if they just say "we need French translations" or "make this page work in multiple languages".
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}
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 --><imgsrc="${URLUtils.staticURL('/images/buttons/submit.png')}"alt="Submit"/><!-- CSS (usually not localized) --><linkrel="stylesheet"href="${URLUtils.staticURL('/css/style.css')}"/>
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 --><ahref="${URLUtils.url('Product-Show', 'pid', 'ABC123')}">View Product</a><!-- Specific locale URL --><ahref="${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><ulclass="language-switcher"><isloopitems="${Site.current.allowedLocales}"var="localeId"><isscript>
var locale = new Locale(localeId);
var url = URLUtils.url(new URLAction('Home-Show', Site.current.ID, localeId));
</isscript><liclass="${request.locale == localeId ? 'active' : ''}"><ahref="${url}">${locale.displayLanguage}</a></li></isloop></ul>
varTemplate = require('dw/util/Template');
varHashMap = require('dw/util/HashMap');
varMail = require('dw/net/Mail');
functionsendOrderConfirmation(order, locale) {
var template = newTemplate('mail/orderconfirmation', locale);
var model = newHashMap();
model.put('order', order);
var content = template.render(model).text;
var mail = newMail();
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:
varMoney = require('dw/value/Money');
varStringUtils = require('dw/util/StringUtils');
// Format with localevar price = newMoney(99.99, 'USD');
var formatted = StringUtils.formatMoney(price); // Uses current locale// In template<isprintvalue="${product.priceModel.price}"style="CURRENCY"/>
Date Formatting
varStringUtils = require('dw/util/StringUtils');
varCalendar = require('dw/util/Calendar');
var date = newCalendar();
var formatted = StringUtils.formatCalendar(date, 'yyyy-MM-dd'); // ISO formatvar 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.firstName not label1
Use parameters for dynamic values - don't concatenate strings
Test all locales - ensure fallback works correctly