Skip Navigation
Updated
March 24, 2025

Learn how to create string packages for your plugins and themes to help users easily translate content with WPML.

String packages are groups of strings “bundled” together in a specific context. Their purpose is to help end-users translate content more efficiently by eliminating the need to translate each string individually. 

For instance, here’s how users would translate custom user fields, with and without a string package:

Without a string package

Without a string package

Users need to translate strings one-by-one, without the option of automatic translation.

With a string package

With a String Package

Users can translate multiple strings together in one go, with the option of automatic translation. 

As you can see, string packages simplify the translation process by letting users translate multiple custom user fields at once. To provide your users with the best translation experience, follow the steps below to create your own string packages. 

If you want to see the complete string package implementation that we cover in the steps below, check out this GitHub repo containing the test plugin we used.

1. Identify Translatable Content

The first step is to identify content that users will want to translate, such as titles, labels, and user-generated content. 

2. Declare the Package Kind

Next, you need to declare your package kind. This step helps WPML detect which string packages are connected to active plugins, and only translate those packages. 

For comparison, this filter works for string packages just like register_post_types filter works for post types.

To declare a package kind, use the wpml_active_string_package_kinds filter:

define( 'FOO_USER_FIELD_PACKAGE_KIND_TITLE', 'OTGS Foo User Fields' );

define( 'FOO_USER_FIELD_PACKAGE_KIND_SLUG', 'otgs-foo-user-fields' );

add_filter( 'wpml_active_string_package_kinds', function( $kinds ) {

   $kinds[ FOO_USER_FIELD_PACKAGE_KIND_SLUG ] = [

            'title'  => FOO_USER_FIELD_PACKAGE_KIND_TITLE,

            'slug'   => FOO_USER_FIELD_PACKAGE_KIND_SLUG,

            'plural' => FOO_USER_FIELD_PACKAGE_KIND_TITLE,

    ];

    return $kinds;

} );

3. Create a Package and Register Strings

Next, you need to create a package and register the strings it’s going to group using the wpml_register_string hook:

$userId = 123;

$user   = get_user_by( 'id', $userId );

$package = [

    'kind'      => FOO_USER_FIELD_PACKAGE_KIND_TITLE, // The "namespace".

    'kind_slug' => FOO_USER_FIELD_PACKAGE_KIND_SLUG, // The "namespace" slug.

    'name'      => $userId, // Can be a string or an integer, but should be unique inside the "kind" namespace.

    'title'     => $user->display_name, // The title for the package entity inside the "kind" namespace.

];

$fooUserFields = get_option( 'otgs_foo_user_fields' );

foreach ( $fooUserFields as $fooUserField ) {

$fieldValue = get_user_meta( $userId, $fooUserField, true );

do_action( 'wpml_register_string', $fieldValue, sanitize_key( $fooUserField ), $package, $fooUserField, 'LINE' );

}

4. Retrieve Translations

Finally, to display translations on the frontend, you need to use the wpml_translate_string hook: 

$userId = 123;

$user   = get_user_by( 'id', $userId );

$package = [

        'kind'      => FOO_USER_FIELD_PACKAGE_KIND_TITLE, // The "namespace".

        'kind_slug' => FOO_USER_FIELD_PACKAGE_KIND_SLUG, // The "namespace" slug.

        'name'      => $userId, // Can be a string or an integer, but should be unique inside the "kind" namespace.

        'title'     => $user->display_name, // The title for the package entity inside the "kind" namespace.

];

$fooUserFields = get_option( 'otgs_foo_user_fields' );

$translatedFieldValues = [];

foreach ( $fooUserFields as $fooUserField ) {

    $fieldValue              = get_user_meta( $userId, $fooUserField, true );

    $translatedFieldValues[ $fooUserField ] = apply_filters( 'wpml_translate_string', $fieldValue, sanitize_key( $fooUserField ), $package );

}

Updating and Removing Strings

Whenever users update content, WPML needs to be able to identify the new strings, as well as those that were removed. To notify WPML of these changes, wrap your registration code with two functions: 

$userId = 123;

$package = [

        'kind'      => FOO_USER_FIELD_PACKAGE_KIND_TITLE, // The "namespace".

        'kind_slug' => FOO_USER_FIELD_PACKAGE_KIND_SLUG, // The "namespace" slug.

        'name'      => $userId, // Can be a string or an integer, but should be unique inside the "kind" namespace.

        // The 'title' key is not not required in the "translate" context.

];

do_action( 'wpml_start_string_package_registration', $package );

$fooUserFields = get_option( 'otgs_foo_user_fields' );

foreach ( $fooUserFields as $fooUserField ) {

    $fieldValue = get_user_meta( $userId, $fooUserField, true );

    do_action( 'wpml_register_string', $fieldValue, sanitize_key( $fooUserField ), $package, $fooUserField, 'LINE' );

}

do_action( 'wpml_delete_unused_package_strings',$package );

Deleting String Packages

When users remove content, you should also remove its associated string package. This includes all related strings, translations, and jobs. By doing this clean-up, you ensure the site remains efficient without any leftover data cluttering the database. 

To remove a string package, hook into an appropriate action within your plugin or theme and call the wpml_delete_package function: 

$userId = 123;

do_action( 'wpml_delete_package', $userId, FOO_USER_FIELD_PACKAGE_KIND_TITLE );

Additional Resources

  • WPML Language Configuration File – a guide about using wpml-config.xml, the file indicating which texts WPML should translate in plugins and themes. 
  • Multilingual Tools – a tool for testing your configuration setup and confirming compatibility with WPML.