@wordpress/i18n package provides client-side localization utilities.
Installation
Install the package:npm install @wordpress/i18n --save
Basic Usage
Import and use translation functions:import { __, _n, _x, sprintf } from '@wordpress/i18n';
// Simple translation
const message = __( 'Hello World', 'my-text-domain' );
// Plural forms
const count = 4;
const hats = sprintf(
_n( '%d hat', '%d hats', count, 'my-text-domain' ),
count
);
// Returns: "4 hats"
Always include a text domain as the last parameter to ensure your translations don’t conflict with other plugins or themes.
Translation Functions
__()
Translate a string:import { __ } from '@wordpress/i18n';
const buttonText = __( 'Save Changes', 'my-plugin' );
const helpText = __( 'Click to save your work', 'my-plugin' );
_x()
Translate with context for disambiguation:import { _x } from '@wordpress/i18n';
// "Post" as in a blog post
const postNoun = _x( 'Post', 'noun', 'my-plugin' );
// "Post" as in the submit action
const postVerb = _x( 'Post', 'verb', 'my-plugin' );
Use
_x() when the same English word has different meanings. The context helps translators provide accurate translations._n()
Handle singular and plural forms:import { _n, sprintf } from '@wordpress/i18n';
function getItemCount( count ) {
return sprintf(
_n(
'%d item',
'%d items',
count,
'my-plugin'
),
count
);
}
getItemCount( 1 ); // "1 item"
getItemCount( 5 ); // "5 items"
_nx()
Combine plural forms with context:import { _nx, sprintf } from '@wordpress/i18n';
const commentCount = sprintf(
_nx(
'%d comment',
'%d comments',
count,
'noun',
'my-plugin'
),
count
);
Using sprintf()
Format strings with placeholders:import { sprintf, __ } from '@wordpress/i18n';
// String placeholder
const greeting = sprintf(
__( 'Hello %s!', 'my-plugin' ),
userName
);
// Number placeholder
const progress = sprintf(
__( 'Step %d of %d', 'my-plugin' ),
currentStep,
totalSteps
);
// Multiple placeholders
const message = sprintf(
__( '%s published %d posts', 'my-plugin' ),
authorName,
postCount
);
Block Editor Examples
Block Registration
import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
registerBlockType( 'my-plugin/custom-block', {
title: __( 'Custom Block', 'my-plugin' ),
description: __( 'A custom block for special content', 'my-plugin' ),
category: 'widgets',
edit: ( { attributes, setAttributes } ) => {
return (
<div>
<h3>{ __( 'Block Settings', 'my-plugin' ) }</h3>
<p>{ __( 'Configure your block below', 'my-plugin' ) }</p>
</div>
);
},
} );
Inspector Controls
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl, ToggleControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
function BlockEdit( { attributes, setAttributes } ) {
return (
<>
<InspectorControls>
<PanelBody
title={ __( 'Block Settings', 'my-plugin' ) }
initialOpen={ true }
>
<TextControl
label={ __( 'Title', 'my-plugin' ) }
value={ attributes.title }
onChange={ ( title ) => setAttributes( { title } ) }
help={ __( 'Enter a title for this block', 'my-plugin' ) }
/>
<ToggleControl
label={ __( 'Show Border', 'my-plugin' ) }
checked={ attributes.showBorder }
onChange={ ( showBorder ) => setAttributes( { showBorder } ) }
/>
</PanelBody>
</InspectorControls>
{/* Block content */}
</>
);
}
Notices and Messages
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
import { __ } from '@wordpress/i18n';
function MyComponent() {
const { createNotice } = useDispatch( noticesStore );
const handleSave = () => {
// Show success message
createNotice(
'success',
__( 'Settings saved successfully', 'my-plugin' ),
{ type: 'snackbar', isDismissible: true }
);
};
const handleError = () => {
// Show error message
createNotice(
'error',
__( 'Failed to save settings', 'my-plugin' ),
{ isDismissible: true }
);
};
}
RTL Language Support
Check if the current locale uses right-to-left text:import { isRTL } from '@wordpress/i18n';
function MyComponent() {
const textAlign = isRTL() ? 'right' : 'left';
return (
<div style={ { textAlign } }>
{ __( 'Content here', 'my-plugin' ) }
</div>
);
}
Use
isRTL() to adjust layouts and styles for right-to-left languages like Arabic and Hebrew.Advanced Usage
Checking for Translations
import { hasTranslation } from '@wordpress/i18n';
if ( hasTranslation( 'Hello World', null, 'my-plugin' ) ) {
// Translation exists
}
Custom I18n Instance
import { createI18n } from '@wordpress/i18n';
const i18n = createI18n();
// Use custom instance
const message = i18n.__( 'Hello', 'my-domain' );
Setting Locale Data
import { setLocaleData } from '@wordpress/i18n';
setLocaleData(
{
'': {
domain: 'my-plugin',
lang: 'fr',
plural_forms: 'nplurals=2; plural=(n > 1);',
},
'Hello World': [ 'Bonjour le monde' ],
},
'my-plugin'
);
Best Practices
- Always use a unique text domain for your plugin or theme
- Keep translatable strings simple and avoid complex concatenation
- Provide context with
_x()for ambiguous terms - Use placeholders with
sprintf()instead of string concatenation - Never split sentences across multiple translation calls
- Include translator comments for complex strings
Good Examples
// Good: Simple, complete sentence
__( 'Save your changes before leaving', 'my-plugin' )
// Good: Using sprintf for dynamic content
sprintf(
__( 'Welcome back, %s!', 'my-plugin' ),
userName
)
// Good: Context for disambiguation
_x( 'Draft', 'post status', 'my-plugin' )
Bad Examples
// Bad: String concatenation
__( 'Hello', 'my-plugin' ) + ' ' + userName
// Bad: Split sentences
__( 'Click', 'my-plugin' ) + ' ' + __( 'here', 'my-plugin' )
// Bad: Missing text domain
__( 'Save' )
PHP Integration
Load JavaScript translations in PHP:function my_plugin_enqueue_scripts() {
wp_enqueue_script(
'my-plugin-script',
plugins_url( 'build/index.js', __FILE__ ),
[ 'wp-blocks', 'wp-i18n' ],
'1.0.0',
true
);
// Load translations
wp_set_script_translations(
'my-plugin-script',
'my-plugin',
plugin_dir_path( __FILE__ ) . 'languages'
);
}
add_action( 'enqueue_block_editor_assets', 'my_plugin_enqueue_scripts' );