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.

Sun Mon Tue Wed Thu Fri Sat
10:00 – 14:00 10:00 – 14:00 10:00 – 14:00 10:00 – 14:00 10:00 – 14:00 - -
16:00 – 20:00 16:00 – 20:00 16:00 – 20:00 16:00 – 20:00 16:00 – 20:00 - -

Supporter timezone: Asia/Jerusalem (GMT+03:00)

This topic contains 2 replies, has 0 voices.

Last updated by Itamar 6 months, 3 weeks ago.

Assisted by: Itamar.

Author Posts
October 21, 2025 at 12:30 pm #17503060

witoldR

Background of the issue:
I am trying to ensure that my URLs reference the correct language when I translate automatically using WPML. I have had contact with Bruno this morning, who helped me solve my issue, but I am experiencing a similar issue on other parts of my site. The site I need help with is hidden link.

In my screenshot when I access the "View iPhone screen repairs" link, it goes to : hidden link instead of hidden link

Symptoms:
My URLs are not referencing the right language when I translate automatically. They remain in the default language.

Questions:
Why are my URLs not updating to the correct language when I use automatic translation with WPML?
How can I resolve the issue of URLs remaining in the default language on other parts of my site?

October 21, 2025 at 2:07 pm #17503408

witoldR

This issue is also happening for my USP's ( The 6 white checkmarks in the hero banner), where they also are going to the dutch translation, instead of their english version.

This is the code for that:

<ul>
                    <?php
                    $usps = get_field( 'usps', (int) $template );
                    if ( empty( $usps ) ) {
                        $usps = get_field( 'usps_top_bar', 'option' );
                    }
                    if ($usps) {
                        foreach ($usps AS $usp) {
                            $usp_text = $usp['usp'];
                            $the_link = $usp['link'] ?? '';
                            if ( is_array( $the_link ) ) {
                                $translated = tpl_translate_acf_link( $the_link );
                                $url = $translated['url'] ?? '';
                            } else {
                                $url = tpl_normalize_wpml_url( $the_link );
                            }
                            ?>
                            <li>
                                <em><u>hidden link</u></em>
                            </li>
                            <?php
                        }
                    }
                    ?>
                </ul>
Scherm­afbeelding 2025-10-21 om 16.07.01.png
October 21, 2025 at 4:49 pm #17504029

Itamar
WPML Supporter since 02/2016

Languages: Hebrew (עברית )

Timezone: Asia/Jerusalem (GMT+03:00)

Hi,

Please try this corrected code:

/**
 * Translate an ACF Link URL from default language to current language (WPML-aware) with per-request caching.
 * - Resolves posts/pages by ID to get the correct translated slug.
 * - Falls back to wpml_permalink for archives/terms/custom endpoints.
 * - Preserves query params and fragments.
 * - Skips external, mailto:, tel:, and fragment-only links.
 *
 * Expects ACF link array: ['url' => '', 'title' => '', 'target' => '']
 */
function tpl_translate_acf_link($link) {
    if (empty($link) || !is_array($link) || empty($link['url'])) {
        return $link;
    }

    $raw = trim((string) $link['url']);

    // Fast path: non-http(s) or fragment-only — no translation, but sanitize.
    if (preg_match('#^(mailto:|tel:|#)#i', $raw)) {
        $link['url'] = esc_url_raw($raw);
        return $link;
    }

    // Cache key: include current language + home URL to be safe across multisite / domains per language.
    static $cache = [];
    $lang = defined('ICL_LANGUAGE_CODE') ? ICL_LANGUAGE_CODE : '';
    $cache_key = md5(home_url('/') . '|' . $lang . '|' . $raw);

    if (isset($cache[$cache_key])) {
        $link['url'] = $cache[$cache_key];
        return $link;
    }

    // Parse once
    $home         = home_url('/');
    $home_host    = wp_parse_url($home, PHP_URL_HOST);
    $is_relative  = (strpos($raw, '/') === 0);
    $abs_for_host = $is_relative ? home_url($raw) : $raw;
    $abs_host     = wp_parse_url($abs_for_host, PHP_URL_HOST);

    // External host → leave untouched (but sanitize)
    if ($abs_host && $home_host && strcasecmp($abs_host, $home_host) !== 0) {
        $safe = esc_url_raw($raw);
        $cache[$cache_key] = $safe;
        $link['url'] = $safe;
        return $link;
    }

    // Remember original query/fragment to re-append later
    $orig_parts = wp_parse_url($raw);
    $orig_query = isset($orig_parts['query']) ? $orig_parts['query'] : '';
    $orig_frag  = isset($orig_parts['fragment']) ? $orig_parts['fragment'] : '';

    // 1) Try to resolve a post/page by ID (best for exact translated slugs)
    $abs_url  = $is_relative ? $abs_for_host : $raw;
    $post_id  = url_to_postid($abs_url);
    $translated_url = '';

    if ($post_id) {
        $ptype          = get_post_type($post_id) ?: 'post';
        $translated_id  = apply_filters('wpml_object_id', $post_id, $ptype, true, $lang);
        if ($translated_id) {
            $translated_url = get_permalink($translated_id);
        }
    }

    // 2) Fallback for archives/terms/custom endpoints
    if (!$translated_url) {
        $converted = apply_filters('wpml_permalink', $raw, $lang);
        if (is_string($converted) && strpos($converted, '/') === 0) {
            $converted = home_url($converted);
        }
        $translated_url = $converted ?: ($is_relative ? $abs_for_host : $raw);
    }

    // Re-append original query params if they existed (merge with any that translation added)
    if ($orig_query) {
        parse_str($orig_query, $orig_q);
        $translated_parts = wp_parse_url($translated_url);
        $translated_q     = [];
        if (!empty($translated_parts['query'])) {
            parse_str($translated_parts['query'], $translated_q);
        }
        // Original params win if keys overlap (change order if you want the opposite)
        $merged_q = array_merge($translated_q, $orig_q);
        $base     = isset($translated_parts['scheme'])
            ? ( $translated_parts['scheme'] . '://' . ($translated_parts['host'] ?? '') . (isset($translated_parts['port']) ? ':' . $translated_parts['port'] : '') . ($translated_parts['path'] ?? '/') )
            : ($translated_parts['path'] ?? '/');
        $translated_url = add_query_arg($merged_q, $base);
    }

    // Re-append fragment
    if ($orig_frag) {
        // Strip any existing fragment first
        $translated_url = preg_replace('/#.*$/', '', $translated_url) . '#' . rawurlencode($orig_frag);
        // If you don't want rawurlencode for fragments, swap to . $orig_frag
    }

    $safe = esc_url_raw($translated_url);
    $cache[$cache_key] = $safe;
    $link['url'] = $safe;
    return $link;
}

I hope this will work for you.

Please note that we do our best to help with custom code, but we can't guarantee it will work. We have extensive documentation for developers, which you can check here: https://wpml.org/documentation/support/.

Regards,
Itamar.

The topic ‘[Closed] My URLs are not referencing the right lang, when I translate automatically. They remain in the defau…’ is closed to new replies.