Skip to content

WPML

WPML support

When WPML is active, Smart Add-Ons passes addon labels through WPML’s string translation API. Dynamic addon strings still need to be registered with String Translation before they actually translate.

What “supports WPML” means here. The lookup filter is real and wired up. String registration is not — without it, WPML returns the original addon labels. Plugin UI strings (wp-admin menus, settings labels) are a separate .pot file and are not covered by this guide.

What the plugin does

When WPML is active (ICL_SITEPRESS_VERSION is defined), WpmlIntegration attaches to saue_translate_string and calls apply_filters( 'wpml_translate_single_string', $value, $context, $name ). You do not add that filter yourself.

StringTranslator runs every addon group’s name and description, and every field’s label, description, placeholder, and option labels, through that filter when a group is formatted for output.

Context and name values

Registration must use the same context and name values the plugin uses to look translations up. If they do not match, WPML will not apply the translation.

StringContextName
Group namesaue_groupname_{group_id}
Group descriptionsaue_groupdescription_{group_id}
Field labelsaue_fieldlabel_{field_id}
Field descriptionsaue_fielddescription_{field_id}
Field placeholdersaue_fieldplaceholder_{field_id}
Option labelsaue_field_optionoption_{field_id}_{value} (or option_{field_id} if the value is empty)

What is missing

Nowhere in the plugin does it call icl_register_string() (or WPML’s newer wpml_register_single_string action) for these dynamic per-group and per-field strings. wpml_translate_single_string only returns a translation for an already-registered string; otherwise it echoes the original back. As shipped, addon labels will likely not be translated by WPML until you register them.

Register strings when a group is saved

Add this to a mu-plugin or the theme functions.php. saue_addon_group_saved receives the group ID only. Save groups while the site is in its default language so WPML registers the original strings, not a translation. After this is in place, save each existing group once so its strings appear in WPML → String Translation.

/**
 * Register Smart Add-Ons group/field strings with WPML String Translation.
 *
 * The plugin looks translations up via wpml_translate_single_string
 * (contexts saue_group, saue_field, saue_field_option). It does not
 * call icl_register_string(), so WPML will not list these strings
 * until you register them. Context and name must match StringTranslator.
 */
add_action( 'saue_addon_group_saved', function ( $group_id ) {
    if ( ! function_exists( 'icl_register_string' ) ) {
        return;
    }

    if ( ! class_exists( SmartAddonsUpsellEngineCorePlugin::class ) ) {
        return;
    }

    $group = SmartAddonsUpsellEngineCorePlugin::getInstance()
        ->getContainer()
        ->make( SmartAddonsUpsellEngineAddonsAddonRepository::class )
        ->getGroupById( (int) $group_id );

    if ( ! is_array( $group ) ) {
        return;
    }

    saue_wpml_register_group_strings( $group );
} );

function saue_wpml_register_group_strings( array $group ) {
    $group_id = (int) ( $group['id'] ?? 0 );

    if ( $group_id <= 0 ) {
        return;
    }

    if ( ! empty( $group['name'] ) && is_string( $group['name'] ) ) {
        icl_register_string( 'saue_group', 'name_' . $group_id, $group['name'] );
    }

    if ( ! empty( $group['description'] ) && is_string( $group['description'] ) ) {
        icl_register_string( 'saue_group', 'description_' . $group_id, $group['description'] );
    }

    foreach ( $group['fields'] ?? [] as $field ) {
        $field_id = (int) ( $field['id'] ?? 0 );

        if ( $field_id <= 0 ) {
            continue;
        }

        foreach ( [ 'label', 'description', 'placeholder' ] as $key ) {
            if ( empty( $field[ $key ] ) || ! is_string( $field[ $key ] ) ) {
                continue;
            }

            icl_register_string( 'saue_field', $key . '_' . $field_id, $field[ $key ] );
        }

        foreach ( $field['options'] ?? [] as $option ) {
            if ( empty( $option['label'] ) || ! is_string( $option['label'] ) ) {
                continue;
            }

            $value = (string) ( $option['value'] ?? '' );
            $name  = $value !== ''
                ? 'option_' . $field_id . '_' . $value
                : 'option_' . $field_id;

            icl_register_string( 'saue_field_option', $name, $option['label'] );
        }
    }
}

After registration

  • Open WPML → String Translation and filter by domain/context saue_group, saue_field, or saue_field_option.
  • Enter translations for each language. On the storefront, the built-in filter returns those translations when the visitor’s language matches.
  • WPML’s newer equivalent of icl_register_string() is do_action( 'wpml_register_single_string', $context, $name, $value ) — either works; the context and name values must still match the table above.
  • Plugin chrome (menus, settings, notices) uses the saue text domain and the .pot file in /languages/. That is unrelated to these dynamic addon strings.

Hook signatures: Hooks & Filters.