.ai/skills/woocommerce-dev-cycle/js-i18n-patterns.md
WooCommerce uses WordPress i18n functions from @wordpress/i18n for
translatable strings. Brand names like "WooPayments" should use placeholders
to improve translation flexibility.
import { __, sprintf } from '@wordpress/i18n';
// Simple string
__( 'Save changes', 'woocommerce' )
// String with placeholder
sprintf(
/* translators: %s: Payment provider name (e.g., WooPayments) */
__( 'Set up %s', 'woocommerce' ),
'WooPayments'
)
_nimport { _n, sprintf } from '@wordpress/i18n';
sprintf(
/* translators: %d: Number of items */
_n(
'%d item selected',
'%d items selected',
count,
'woocommerce'
),
count
)
createInterpolateElementimport { createInterpolateElement } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
createInterpolateElement(
sprintf(
/* translators: 1: Payment provider name */
__( 'Enable <strong>%1$s</strong> for your store.', 'woocommerce' ),
'WooPayments'
),
{
strong: <strong />,
}
)
Use %s for a single placeholder:
sprintf(
/* translators: %s: Payment provider name (e.g., WooPayments) */
__( 'Get paid with %s', 'woocommerce' ),
'WooPayments'
)
Use numbered placeholders %1$s when the same value appears multiple times:
sprintf(
/* translators: 1: Payment provider name (e.g., WooPayments) */
__(
'By using %1$s you agree to our Terms. Payments via %1$s are secure.',
'woocommerce'
),
'WooPayments'
)
Use numbered placeholders %1$s, %2$s for different values:
sprintf(
/* translators: 1: Payment provider name, 2: Extension names */
_n(
'Installing %1$s will activate %2$s extension.',
'Installing %1$s will activate %2$s extensions.',
extensionCount,
'woocommerce'
),
'WooPayments',
extensionNames
)
The translator comment must be placed immediately before the __() or
_n() function, not before sprintf():
// ❌ WRONG - Comment before sprintf
/* translators: %s: Payment provider name */
sprintf(
__( 'Set up %s', 'woocommerce' ),
'WooPayments'
)
// ✅ CORRECT - Comment inside sprintf, before __()
sprintf(
/* translators: %s: Payment provider name */
__( 'Set up %s', 'woocommerce' ),
'WooPayments'
)
When using numbered placeholders like %1$s, use just the number in the comment:
// ❌ WRONG - Using %1$s in comment
/* translators: %1$s: Provider name, %2$s: Country */
// ✅ CORRECT - Using just numbers
/* translators: 1: Provider name, 2: Country */
Always provide context for translators:
// ❌ WRONG - No context
/* translators: %s: name */
// ✅ CORRECT - Clear context
/* translators: %s: Payment provider name (e.g., WooPayments) */
sprintf, _n, and createInterpolateElementinstallText: ( extensionsString: string ) => {
const count = extensionsString.split( ', ' ).length;
return createInterpolateElement(
sprintf(
/* translators: 1: Provider name, 2: Extension names */
_n(
'Installing <strong>%1$s</strong> activates <strong>%2$s</strong>.',
'Installing <strong>%1$s</strong> activates <strong>%2$s</strong>.',
count,
'woocommerce'
),
'WooPayments',
extensionsString
),
{ strong: <strong /> }
);
}
createInterpolateElement(
sprintf(
/* translators: 1: Payment provider name */
__(
'Learn more about <a>%1$s</a> features.',
'woocommerce'
),
'WooPayments'
),
{
a: (
<a
href="https://example.com"
target="_blank"
rel="noopener noreferrer"
/>
),
}
)
TypeScript files may use curly apostrophes (' U+2019) instead of straight
apostrophes (' U+0027). When editing, preserve the original character:
// Original uses curly apostrophe - preserve it
__( 'I don't want to install another plugin', 'woocommerce' )
// ^ This is U+2019, not U+0027
The @wordpress/i18n-translator-comments rule requires comments directly
before the translation function:
// ❌ ESLint error - comment not adjacent to __()
const title = sprintf(
/* translators: %s: Provider name */
__( 'Set up %s', 'woocommerce' ),
'WooPayments'
);
// ✅ Correct - comment directly before __()
const title = sprintf(
/* translators: %s: Provider name */
__( 'Set up %s', 'woocommerce' ),
'WooPayments'
);
Always use placeholders for brand names to improve translation flexibility:
// ✅ CORRECT - Use placeholder for brand name
title: sprintf(
/* translators: %s: Payment provider name */
__( 'Get paid with %s', 'woocommerce' ),
'WooPayments'
)
// ✅ CORRECT - Use placeholder in descriptions
description: sprintf(
/* translators: %s: Payment provider name */
__( 'Enable PayPal alongside %s', 'woocommerce' ),
'WooPayments'
)
// ❌ WRONG - Hardcoded brand name
title: __( 'Get paid with WooPayments', 'woocommerce' )
# Lint specific file
npx eslint client/path/to/file.tsx
# Fix specific file
npx eslint --fix client/path/to/file.tsx
# Type check
pnpm run ts:check
# ❌ NEVER lint entire codebase
pnpm run lint # NO - lints everything
| Pattern | Example |
|---|---|
| Single placeholder | sprintf( __( 'Set up %s' ), 'Name' ) |
| Repeated placeholder | sprintf( __( '%1$s via %1$s' ), 'Name' ) |
| Multiple placeholders | sprintf( __( '%1$s in %2$s' ), 'A', 'B' ) |
| Comment format (simple) | /* translators: %s: Provider name */ |
| Comment format (numbered) | /* translators: 1: Provider, 2: Country */ |