Skip to main content
Internationalization (i18n) is essential for making your blocks and editor components accessible to users worldwide. The @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' );

Resources

Build docs developers (and LLMs) love