Skip to content Skip to sidebar

This is the technical support forum for WPML - the multilingual WordPress plugin.

Everyone can read, but only WPML clients can post here. WPML team is replying on the forum 6 days per week, 22 hours per day.

This topic contains 9 replies, has 0 voices.

Last updated by henrikO-8 3 hours, 34 minutes ago.

Assisted by: Bobby.

Author Posts
August 14, 2025 at 6:11 pm #17321891

henrikO-8

Background of the issue:
I am trying to translate shortcode content. I have added the texts via 'Admin Texts Translation' so they are visible in the String Translation Manager. From here, I have updated one language, but there is no effect on the front end. The issue can be seen on this page: hidden link.

Symptoms:
I expected to see 'Estimated delivery: 2-4 days' translated to Danish, but instead, I got nothing.

Questions:
Why are the translated strings not appearing on the front end?
Is there a step I am missing in the translation process for shortcode content?

August 15, 2025 at 9:42 pm #17324263

Bobby
WPML Supporter since 04/2015

Languages: English (English )

Timezone: America/Los_Angeles (GMT-07:00)

Hi there,

Please go to WPML String Translation and in the admin strings screen search for this string once again:

Estimated delivery: 2-4 days

If located, verify that every instance of it is added in case of duplicates.

Once added go back to WPML-> String Translation and verify that all duplicates/instances of it are all translated.

---> Clear the cache.

Let me know your results, please.

August 18, 2025 at 12:49 pm #17327803

henrikO-8

Please see attached screenshots. Do we really need to "translate" the PHP block as seen here?

It looks like WPML recognizes the shortcode inserted... that is just not working though.

Screenshot 2025-08-18 at 14.48.05.png
Screenshot 2025-08-18 at 14.47.28.png
August 18, 2025 at 10:12 pm #17329536

Bobby
WPML Supporter since 04/2015

Languages: English (English )

Timezone: America/Los_Angeles (GMT-07:00)

What are your results if you click on the + for the code block, then duplicate it into the translation and within it simply translate ONLY the string.

Let me know your results, please.

While this is rare I have seen a similar case in the past.

August 19, 2025 at 6:45 am #17329785

henrikO-8

I have tried that as well now, with no luck. Do we need to do anything special for a translation to take effect? We have no caching on at the moment.

August 19, 2025 at 7:17 pm #17332637

Bobby
WPML Supporter since 04/2015

Languages: English (English )

Timezone: America/Los_Angeles (GMT-07:00)

Nothing special should be done, just translation.

I would like to request temporary access (wp-admin and FTP) to your site to test the issue.
(preferably to a test site where the problem has been replicated if possible)

**Before we proceed It is necessary to take FULL BACKUP of your database and your website. Providing us with access, you agree that a backup has been taken **

I often use the Duplicator plugin for this purpose: http://wordpress.org/plugins/duplicator/
You will find the needed fields for this below the comment area when you log in to leave your next reply.
The information you enter is private which means only you and I have access to it.

NOTE: If access to the live site is not possible and the staging site does not exist please provide me with a duplicator package created with the duplicator plugin.

Thank you,
Bobby

August 22, 2025 at 11:45 pm #17341505

Bobby
WPML Supporter since 04/2015

Languages: English (English )

Timezone: America/Los_Angeles (GMT-07:00)

Thank you for the access details!

I can see the shortcode [estimated_delivery] is used.

Where is there shortcode pulling the data from?

This is important because WPML by itself will not know to register this content.

Typically admin texts are reserved for strings coming from theme options.

I have additionally attempted registering the shortcode via wpml-config.xml but this did not help.

<wpml-config>
  <shortcodes>
    <shortcode>
      <tag>estimated_delivery</tag>
      <attributes>
        <attribute>text</attribute>
      </attributes>
    </shortcode>
  </shortcodes>
</wpml-config>

If you could please sahre with me where this shortcode is pulling the data from it will help me better understand how we can register it.

August 24, 2025 at 9:59 am #17343044

henrikO-8

Hi Bobby,

Thanks for getting back to me.

The shortcode is pulling data from here: See image.

Please check the Code snippet: Estimated Delivery.

Looking forward to hearing from you.

Screenshot 2025-08-24 at 11.58.01.png
August 25, 2025 at 8:37 pm #17346322

Bobby
WPML Supporter since 04/2015

Languages: English (English )

Timezone: America/Los_Angeles (GMT-07:00)

Thank you for sharing this.

I can see the following code.

function get_html_in_stock() {
return '<p class="estimated-delivery in-stock"><span class="stock-indicator" style="background-color: #008000;"></span> Estimated delivery: 2-4 days</p>';
}

The HTML string should be wrapped in a gettext call in order to correctly register for translation.

If possible, please remove the code snippet and add it directly to your theme, code snippets typically do not work well with WPML.

Related Documentation:
https://wpml.org/documentation/support/how-to-use-gettext/

Below find an altered version of your code:

<?php
// Pick a text domain for gettext scanning (theme or plugin)
if ( ! defined( 'ED_TEXTDOMAIN' ) ) {
    define( 'ED_TEXTDOMAIN', 'my-estimated-delivery' );
}

/**
 * Register translatable strings with WPML at init.
 * (This is optional if you rely purely on gettext + WPML's string scan,
 * but it makes the strings appear immediately in String Translation.)
 */
add_action( 'init', function () {
    if ( function_exists( 'do_action' ) ) {
        // Labels
        do_action( 'wpml_register_single_string', 'Estimated Delivery', 'label', 'Estimated delivery' );
        do_action( 'wpml_register_single_string', 'Estimated Delivery', 'in_stock_range', '2–4 days' );
        do_action( 'wpml_register_single_string', 'Estimated Delivery', 'not_in_stock_range', '8–10 weeks' );
    }
});

/**
 * Helpers that return translated bits
 */
function ed_get_label() {
    $default = __( 'Estimated delivery', ED_TEXTDOMAIN );
    if ( function_exists( 'apply_filters' ) ) {
        $translated = apply_filters(
            'wpml_translate_single_string',
            $default,
            'Estimated Delivery',
            'label'
        );
        return $translated ?: $default;
    }
    return $default;
}
function ed_get_in_stock_range() {
    $default = _x( '2–4 days', 'delivery time when in stock', ED_TEXTDOMAIN );
    if ( function_exists( 'apply_filters' ) ) {
        $translated = apply_filters(
            'wpml_translate_single_string',
            $default,
            'Estimated Delivery',
            'in_stock_range'
        );
        return $translated ?: $default;
    }
    return $default;
}
function ed_get_not_in_stock_range() {
    $default = _x( '8–10 weeks', 'delivery time when NOT in stock', ED_TEXTDOMAIN );
    if ( function_exists( 'apply_filters' ) ) {
        $translated = apply_filters(
            'wpml_translate_single_string',
            $default,
            'Estimated Delivery',
            'not_in_stock_range'
        );
        return $translated ?: $default;
    }
    return $default;
}

/**
 * HTML builders (already translated)
 */
function get_html_in_stock() {
    $label = ed_get_label();
    $range = ed_get_in_stock_range();
    $html  = sprintf(
        '<p class="estimated-delivery in-stock"><span class="stock-indicator" style="background-color:#008000;"></span> %s: %s</p>',
        esc_html( $label ),
        esc_html( $range )
    );
    return $html;
}
function get_html_not_in_stock() {
    $label = ed_get_label();
    $range = ed_get_not_in_stock_range();
    $html  = sprintf(
        '<p class="estimated-delivery low-stock"><span class="stock-indicator" style="background-color:#cc0000;"></span> %s: %s</p>',
        esc_html( $label ),
        esc_html( $range )
    );
    return $html;
}

/**
 * Core logic
 */
function get_estimated_delivery_for_product( $product ) {
    if ( ! $product ) {
        return '';
    }

    // Handle bundled products
    if ( class_exists( 'WC_Product_Bundle' ) && $product->is_type( 'bundle' ) ) {
        $bundled_items     = $product->get_bundled_items();
        $max_delivery_time = 0;

        foreach ( $bundled_items as $bundled_item ) {
            $bundled_product  = $bundled_item->get_product();
            $bundled_quantity = $bundled_item->get_quantity();

            if ( $bundled_quantity > 0 ) {
                $stock_quantity = $bundled_product->get_stock_quantity();

                if ( $stock_quantity !== null ) {
                    $delivery_time     = ( $stock_quantity > 0 ) ? 4 : 10; // 2–4 days or 8–10 weeks
                    $max_delivery_time = max( $max_delivery_time, $delivery_time );
                }
            }
        }

        if ( $max_delivery_time === 0 ) {
            return '';
        }

        return ( $max_delivery_time <= 4 ) ? get_html_in_stock() : get_html_not_in_stock();
    }

    // Variation quantity map
    $variations_stock = array();
    if ( method_exists( $product, 'get_available_variations' ) ) {
        $variations = $product->get_available_variations();
        foreach ( $variations as $variation ) {
            $variation_product                                = new WC_Product_Variation( $variation['variation_id'] );
            $variations_stock[ $variation['variation_id'] ]   = $variation_product->get_stock_quantity();
        }
    }

    // Precompute translated HTML for JS usage and safely JSON-encode it
    $html_in_stock     = get_html_in_stock();
    $html_not_in_stock = get_html_not_in_stock();

    wc_enqueue_js(
        'var variations_stock = ' . wp_json_encode( $variations_stock ) . ';' .
        'var ed_html_in_stock = ' . wp_json_encode( $html_in_stock ) . ';' .
        'var ed_html_not_in_stock = ' . wp_json_encode( $html_not_in_stock ) . ';'
    );

    // Simple & variable products
    $stock_quantity = $product->get_stock_quantity();

    if ( $stock_quantity !== null ) {
        return ( $stock_quantity > 0 ) ? $html_in_stock : $html_not_in_stock;
    }

    return '';
}

// Shortcode for product pages
function estimated_delivery_shortcode() {
    global $product;
    return get_estimated_delivery_for_product( $product );
}
add_shortcode( 'estimated_delivery', 'estimated_delivery_shortcode' );

// Add estimated delivery message to cart & checkout
function add_estimated_delivery_to_cart_item_name( $product_name, $cart_item, $cart_item_key ) {
    $product          = $cart_item['data'];
    $delivery_message = get_estimated_delivery_for_product( $product );
    return $product_name . $delivery_message;
}
add_filter( 'woocommerce_cart_item_name', 'add_estimated_delivery_to_cart_item_name', 10, 3 );

// Update the message on variation change (uses the already-translated HTML variables)
add_action( 'woocommerce_before_variations_form', function () {
    wc_enqueue_js( "
        jQuery('form.variations_form').on('show_variation', function(event, data){
            if (typeof variations_stock !== 'undefined' && variations_stock[data.variation_id] > 0) {
                jQuery('.estimated-delivery').replaceWith(ed_html_in_stock);
            } else {
                jQuery('.estimated-delivery').replaceWith(ed_html_not_in_stock);
            }
        });
    " );
} );

NOTE: Custom work is outside the scope of this forum, however, I am sharing this code under my own discretion to help you move your project along. Before using this please have a backup in place and understand that it is a piece of code that has not been tested officially by our team.

After you implement it, go to WPML->Theme and Plugins localization and scan your theme for strings, then go to WPML-> String translation locate it and translate it.

Let me know your results, please.

August 26, 2025 at 12:36 pm #17348315

henrikO-8

Thank you Bobby. I also had to check this one to make it work. See attached.

It works now - thanks!

Screenshot 2025-08-26 at 14.33.00.png
August 26, 2025 at 12:37 pm #17348332

henrikO-8

A little slow support - but top-class response and problem-solving from Bobby.