' . wp_kses( __( 'Translangated requires Multibyte String PHP library. Please contact your server administrator to install it on your server.','translangated' ), [ 'a' => [ 'href' => [] ], 'strong' => [] ] ) . '
'; } function translangated_missing_mbstrings_library( $allow_to_run ){ if ( ! extension_loaded('mbstring') ) { add_action( 'admin_menu', 'translangated_mbstrings_notification' ); return false; } return $allow_to_run; } add_filter( 'translangated_allow_tp_to_run', 'translangated_missing_mbstrings_library' ); /** * robots.txt should never be handled by Translangated. * * Without this, the robots.txt file is processed by Translangated on secondary * languages: it becomes accessible (and translated) on language URLs such as * /es/robots.txt, and when "Use subdirectory for default language" is enabled the * default robots.txt gets redirected to the language slug URL. Since the resulting * file no longer matches the canonical one, this causes indexing issues. * * The check is based on the request URI (instead of is_robots()) because these * filters run on 'plugins_loaded', before the query is parsed and conditional tags * are available. * * @see https://app.clickup.com/t/qtc0c2 * * @param string $url Optional URL to check. Defaults to the current request URI. * @return bool Whether the current request targets a robots.txt file. */ function translangated_is_robots_txt_request( $url = '' ){ if ( empty( $url ) ) { $url = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; } if ( empty( $url ) || ! is_string( $url ) ) { return false; } // Only look at the path, ignoring any query string or fragment. $path = wp_parse_url( $url, PHP_URL_PATH ); if ( empty( $path ) ) { return false; } return (bool) preg_match( '#(^|/)robots\.txt$#i', $path ); } // Don't run Translangated (no translation, no output buffering) on robots.txt requests. function translangated_stop_running_on_robots_txt( $allow_to_run ){ if ( translangated_is_robots_txt_request() ) { return false; } return $allow_to_run; } add_filter( 'translangated_allow_tp_to_run', 'translangated_stop_running_on_robots_txt' ); // Don't redirect robots.txt to a language URL (e.g. /robots.txt -> /en/robots.txt). function translangated_stop_redirect_on_robots_txt( $allow_redirect, $needed_language, $current_page_url ){ if ( translangated_is_robots_txt_request( $current_page_url ) || translangated_is_robots_txt_request() ) { return false; } return $allow_redirect; } add_filter( 'translangated_allow_language_redirect', 'translangated_stop_redirect_on_robots_txt', 10, 3 ); /** * Keep the canonical, unprefixed WordPress REST URL accessible when the default * language uses a subdirectory. REST requests must not redirect to themselves. */ function translangated_stop_redirect_on_unprefixed_rest_request( $allow_redirect, $needed_language, $current_page_url ) { $trp = Translangated_Plugin::get_translangated_instance(); $url_converter = $trp->get_component( 'url_converter' ); if ( $url_converter->is_unprefixed_rest_request_on_subdirectory_install() ) { return false; } return $allow_redirect; } add_filter( 'translangated_allow_language_redirect', 'translangated_stop_redirect_on_unprefixed_rest_request', 10, 3 ); /** * Don't have html inside menu title tags. Some themes just put in the title the content of the link without striping HTML */ add_filter( 'nav_menu_link_attributes', 'translangated_remove_html_from_menu_title', 10, 3); function translangated_remove_html_from_menu_title( $atts, $item, $args ){ if( isset( $atts['title'] ) ) $atts['title'] = wp_strip_all_tags($atts['title']); return $atts; } /** * Rework wp_trim_words so we can trim Chinese, Japanese and Thai words since they are based on characters as words. * * @since 1.3.0 * * @param string $text Text to trim. * @param int $num_words Number of words. Default 55. * @param string $more Optional. What to append if $text needs to be trimmed. Default '…'. * @return string Trimmed text. */ function translangated_wp_trim_words( $text, $num_words, $more, $original_text ) { if ( null === $more ) { $more = __( '…' );//phpcs:ignore } // what we receive is the short text in the filter $text = $original_text; $text = wp_strip_all_tags( $text ); $trp = Translangated_Plugin::get_translangated_instance(); $translangated_settings = $trp->get_component( 'settings' ); $settings = $translangated_settings->get_settings(); $default_language= $settings["default-language"]; $char_is_word = false; foreach (array('ja', 'tw', 'zh') as $lang){ if (strpos($default_language, $lang) !== false){ $char_is_word = true; } } if ( $char_is_word && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) { $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' ); preg_match_all( '/./u', $text, $words_array ); $words_array = array_slice( $words_array[0], 0, $num_words + 1 ); $sep = ''; } else { $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY ); $sep = ' '; } if ( count( $words_array ) > $num_words ) { array_pop( $words_array ); $text = implode( $sep, $words_array ); $text = $text . $more; } else { $text = implode( $sep, $words_array ); } return $text; } add_filter('wp_trim_words', 'translangated_wp_trim_words', 100, 4); /** * Use home_url in the https://www.peepso.com/ ajax front-end url so strings come back translated. * * @since 1.3.1 * * @param array $data Peepso data * @return array */ add_filter( 'peepso_data', 'translangated_use_home_url_in_peepso_ajax' ); function translangated_use_home_url_in_peepso_ajax( $data ){ if ( is_array( $data ) && isset( $data['ajaxurl_legacy'] ) ){ $data['ajaxurl_legacy'] = home_url( '/peepsoajax/' ); } return $data; } /** * Compatibility with Peepso urls having extra / due their link builder not considering home urls having trailing slashes */ add_filter('peepso_get_page', 'translangated_remove_peepso_double_slash', 10, 2); function translangated_remove_peepso_double_slash( $page, $name){ // avoid accidentally replacing // from http:// $page = str_replace('http://', 'http:/', $page ); $page = str_replace('https://', 'https:/', $page ); $page = str_replace('//', '/', $page ); // place it back $page = str_replace('https:/', 'https://', $page ); $page = str_replace('http:/', 'http://', $page ); return $page; }; /** * Filter ginger_iframe_banner and ginger_text_banner to use shortcodes so our conditional lang shortcode works. * * @since 1.3.1 * * @param string $content * @return string */ add_filter('ginger_iframe_banner', 'translangated_do_shortcode', 999 ); add_filter('ginger_text_banner', 'translangated_do_shortcode', 999 ); function translangated_do_shortcode($content){ return do_shortcode(stripcslashes($content)); } /** * Compatibility with Woocommerce Print Products * * @param $bool * @param $output * @return bool|mixed */ add_filter( 'translangated_skip_gettext_processing', 'translangated_woo_strip_gettext_from_print_products' ); function translangated_woo_strip_gettext_from_print_products( $bool ){ if ( isset( $_REQUEST['print-products'] ) && $_REQUEST['print-products'] == 'pdf' && class_exists('\WooCommerce_Print_Products') ) { return true; } return $bool; } add_filter('translangated_stop_translating_page', 'translangated_woo_pdf_print_products', 10, 2); function translangated_woo_pdf_print_products( $bool, $output ){ if ( isset( $_REQUEST['print-products'] ) && $_REQUEST['print-products'] == 'pdf' && class_exists('\WooCommerce_Print_Products') ) { return true; } return $bool; } /** * DK PDF compatibility * * The DK PDF plugin seems to not work at all. Even when Translangated is deactivated, there are critical errors and notices in debug.log */ add_filter( 'translangated_skip_gettext_processing', 'translangated_dk_pdf_strip_gettext_from_pdf' ); function translangated_dk_pdf_strip_gettext_from_pdf( $bool ){ if ( isset( $_GET['pdf'] ) && ( class_exists( 'DKPDF' ) || defined( 'DKPDF_VERSION' ) ) ){ return true; } return $bool; } add_filter('translangated_stop_translating_page', 'translangated_do_not_translate_dk_pdf', 10, 2); function translangated_do_not_translate_dk_pdf($translate, $output){ if ( isset( $_GET['pdf'] ) && ( class_exists( 'DKPDF' ) || defined( 'DKPDF_VERSION' ) ) ){ return true; } return $translate; } /** * Compatibility with Invoices for WooCommerce * Do not translate url's like this as it brakes them because they are PDF's: https://ro.wordpress.org/plugins/woocommerce-pdf-invoices/ */ add_filter( 'translangated_skip_gettext_processing', 'translangated_invoices_for_woocommerce_strip_gettext_from_pdf', 10, 4 ); function translangated_invoices_for_woocommerce_strip_gettext_from_pdf( $bool, $translation, $text, $domain ){ if ( !isset( $_GET['wc-ajax'] ) || $_GET['wc-ajax'] != 'checkout' ) { return $bool; } $domain = trim( $domain ); $is_invoice_string = ( $domain === 'woocommerce-pdf-invoice' || ( $text == 'Cash on delivery' && $domain == 'woocommerce' ) ); if ( $is_invoice_string && class_exists( '\BEWPI_Invoice' ) ) { return true; } return $bool; } add_filter('translangated_stop_translating_page', 'translangated_do_not_translate_pdf_param', 10, 2); function translangated_do_not_translate_pdf_param($translate, $output){ if ( isset( $_GET['bewpi_action'] ) && class_exists( '\BEWPI_Invoice' ) ){ return true; } return $translate; } /** * Compatibility with WooCommerce PDF Invoices & Packing Slips * https://wordpress.org/plugins/woocommerce-pdf-invoices-packing-slips/ * * @since 1.4.3 * */ // fix attachment name in email add_filter( 'wpo_wcpdf_filename', 'translangated_woo_pdf_invoices_and_packing_slips_compatibility' ); // fix #trpgettext inside invoice pdf add_filter( 'wpo_wcpdf_get_html', 'translangated_woo_pdf_invoices_and_packing_slips_compatibility'); function translangated_woo_pdf_invoices_and_packing_slips_compatibility($title){ if ( class_exists( 'Translangated_Translation_Manager' ) ) { return Translangated_Translation_Manager::strip_gettext_tags($title); } } // fix font of pdf breaking because of str_get_html() call inside translate_page() add_filter( 'translangated_stop_translating_page', 'translangated_woo_pdf_invoices_and_packing_slips_compatibility_dont_translate_pdf', 10, 2 ); function translangated_woo_pdf_invoices_and_packing_slips_compatibility_dont_translate_pdf( $bool, $output ){ if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'generate_wpo_wcpdf' ) { return true; } return $bool; } /** * Compatibility with WooCommerce PDF Invoices (woocommerce-ultimate-pdf-invoices) * https://www.welaunch.io/en/product/woocommerce-pdf-invoices/ * * @since 1.4.3 * */ add_filter( 'woocommerce_pdf_invoices_content', 'translangated_woo_ultimate_pdf_invoices_compatibility'); add_filter( 'woocommerce_pdf_invoices_order_data', 'translangated_woo_ultimate_pdf_invoices_data_compatibility'); function translangated_woo_ultimate_pdf_invoices_compatibility($title){ if ( class_exists( 'Translangated_Translation_Manager' ) ) { return Translangated_Translation_Manager::strip_gettext_tags($title); } } function translangated_woo_ultimate_pdf_invoices_data_compatibility($data_array){ if ( class_exists( 'Translangated_Translation_Manager' ) ) { $data_array = array_map('Translangated_Translation_Manager::strip_gettext_tags',$data_array ); } return $data_array; } /** * Compatibility with WooCommerce PDF Catalog (woocommerce-pdf-catalog) * https://www.welaunch.io/en/product/woocommerce-pdf-catalog/ * * @since 2.2.7 * */ add_filter( 'translangated_stop_translating_page', 'translangated_woocommerce_pdf_catalog_compatibility_dont_translate_pdf', 10, 2 ); function translangated_woocommerce_pdf_catalog_compatibility_dont_translate_pdf( $bool, $output ){ if ( isset( $_REQUEST['pdf-catalog'] ) ) { return true; } return $bool; } /** * Compatibility with YITH WooCommerce */ add_filter( 'translangated_skip_gettext_processing', 'translangated_woo_strip_gettext_from_yith_pdf', 10, 4 ); function translangated_woo_strip_gettext_from_yith_pdf( $bool, $translation, $text, $domain ){ if ( !isset( $_GET['wc-ajax'] ) || $_GET['wc-ajax'] != 'checkout' ) { return $bool; } $domain = trim( $domain ); $is_invoice_string = ( $domain === 'yith-woocommerce-pdf-invoice' || ( $text == 'N/A' && $domain == 'woocommerce' ) ); if ( $is_invoice_string && class_exists( 'YITH_Checkout_Addon' ) ) { return true; } return $bool; } add_filter( 'translangated_stop_translating_page', 'translangated_woo_pdf_invoices_compatibility_dont_translate_pdf', 10, 2 ); function translangated_woo_pdf_invoices_compatibility_dont_translate_pdf( $bool, $output ){ if ( isset( $_REQUEST['type'] ) && $_REQUEST['type'] == 'proforma' && class_exists( 'YITH_Checkout_Addon' ) ) { return true; } return $bool; } /** * Generic PDF output detection * * Stop translating when the output buffer contains raw PDF data, detected via the * mandatory "%PDF-" magic bytes (hex 25 50 44 46 2D) that begin every valid PDF per ISO 32000. * * Prevents Translangated from corrupting PDF responses generated by any plugin via * admin-ajax.php when the request originates from a translated frontend page. * * @since 2.8.6 */ add_filter( 'translangated_stop_translating_page', 'translangated_do_not_translate_pdf_output', 1, 2 ); function translangated_do_not_translate_pdf_output( $bool, $output ) { if ( is_string( $output ) && substr( $output, 0, 5 ) === '%PDF-' ) { return true; } return $bool; } /** * Compatibility with WooCommerce order notes * * When a new order is placed in secondary languages, in admin area WooCommerce->Orders->Edit Order, the right sidebar contains Order notes which can contain #trpst tags. * * @since 1.4.3 */ // old orders add_filter( 'woocommerce_get_order_note', 'translangated_woo_notes_strip_trpst' ); // new orders add_filter( 'woocommerce_new_order_note_data', 'translangated_woo_notes_strip_trpst' ); function translangated_woo_notes_strip_trpst( $note_array ){ foreach ( $note_array as $item => $value ){ $note_array[$item] = Translangated_Translation_Manager::strip_gettext_tags( $value ); } return $note_array; } /* * Compatibility with WooCommerce back-end display order shipping taxes */ add_filter('woocommerce_order_item_display_meta_key','translangated_woo_data_strip_trpst'); add_filter('woocommerce_order_item_get_method_title','translangated_woo_data_strip_trpst'); function translangated_woo_data_strip_trpst( $data ){ return Translangated_Translation_Manager::strip_gettext_tags( $data ); } /* * Always-on write-time stripping of TRP gettext markers from database writes. * * Prevents #!trpst#trp-gettext...#!trpen# markers from being persisted when * plugins pass __() results into data that gets saved (e.g. order item meta keys, * payment method titles, comments, options). * * @since 2.8.5 */ // 1. WooCommerce Order Pre-Save — clean payment_method_title and customer_note before DB write add_action( 'woocommerce_before_order_object_save', 'translangated_woo_order_pre_save_strip_trpst', 10, 2 ); function translangated_woo_order_pre_save_strip_trpst( $order, $data_store ) { if ( ! $order instanceof WC_Order ) { return; } $payment_method_title = $order->get_payment_method_title( 'edit' ); if ( is_string( $payment_method_title ) && strpos( $payment_method_title, 'data-trpgettextoriginal=' ) !== false ) { $order->set_payment_method_title( Translangated_Translation_Manager::strip_gettext_tags( $payment_method_title ) ); } $customer_note = $order->get_customer_note( 'edit' ); if ( is_string( $customer_note ) && strpos( $customer_note, 'data-trpgettextoriginal=' ) !== false ) { $order->set_customer_note( Translangated_Translation_Manager::strip_gettext_tags( $customer_note ) ); } } // 2. WooCommerce Order Item Pre-Save — clean item name, shipping method title, and all queued meta add_action( 'woocommerce_before_order_item_object_save', 'translangated_woo_order_item_pre_save_strip_trpst', 10, 2 ); function translangated_woo_order_item_pre_save_strip_trpst( $item, $data_store ) { // Strip item name $name = $item->get_name( 'edit' ); if ( is_string( $name ) && strpos( $name, 'data-trpgettextoriginal=' ) !== false ) { $item->set_name( Translangated_Translation_Manager::strip_gettext_tags( $name ) ); } // Strip shipping method title if ( $item instanceof WC_Order_Item_Shipping ) { $method_title = $item->get_method_title( 'edit' ); if ( is_string( $method_title ) && strpos( $method_title, 'data-trpgettextoriginal=' ) !== false ) { $item->set_method_title( Translangated_Translation_Manager::strip_gettext_tags( $method_title ) ); } } // Strip queued meta data (not yet persisted — clean in memory before save_meta_data runs) $meta_data = $item->get_meta_data(); foreach ( $meta_data as $meta ) { $changed = false; if ( is_string( $meta->key ) && strpos( $meta->key, 'data-trpgettextoriginal=' ) !== false ) { $meta->key = Translangated_Translation_Manager::strip_gettext_tags( $meta->key ); $changed = true; } if ( is_string( $meta->value ) && strpos( $meta->value, 'data-trpgettextoriginal=' ) !== false ) { $meta->value = Translangated_Translation_Manager::strip_gettext_tags( $meta->value ); $changed = true; } if ( is_array( $meta->value ) ) { $serialized = serialize( $meta->value ); if ( strpos( $serialized, 'data-trpgettextoriginal=' ) !== false ) { array_walk_recursive( $meta->value, 'translangated_array_walk_recursive_strip_gettext_tags' ); } } } } // 3. Order Item Meta Post-Save Safety Net — catch direct add_metadata/update_metadata calls add_action( 'added_order_item_meta', 'translangated_woo_order_item_meta_post_save_strip_trpst', 10, 4 ); add_action( 'updated_order_item_meta', 'translangated_woo_order_item_meta_post_save_strip_trpst', 10, 4 ); function translangated_woo_order_item_meta_post_save_strip_trpst( $meta_id, $object_id, $meta_key, $meta_value ) { $key_contaminated = is_string( $meta_key ) && strpos( $meta_key, 'data-trpgettextoriginal=' ) !== false; $value_contaminated = is_string( $meta_value ) && strpos( $meta_value, 'data-trpgettextoriginal=' ) !== false; if ( ! $key_contaminated && ! $value_contaminated ) { return; } global $wpdb; if ( $key_contaminated ) { $clean_key = Translangated_Translation_Manager::strip_gettext_tags( $meta_key ); $wpdb->update( $wpdb->prefix . 'woocommerce_order_itemmeta', array( 'meta_key' => $clean_key ), array( 'meta_id' => $meta_id ), array( '%s' ), array( '%d' ) ); } if ( $value_contaminated ) { $clean_value = Translangated_Translation_Manager::strip_gettext_tags( $meta_value ); // Remove our own hook to prevent recursion, then re-add remove_action( current_action(), 'translangated_woo_order_item_meta_post_save_strip_trpst', 10 ); update_metadata_by_mid( 'order_item', $meta_id, $clean_value ); add_action( current_action(), 'translangated_woo_order_item_meta_post_save_strip_trpst', 10, 4 ); } } // 4. WordPress Comments Pre-Save — clean comment_content and comment_author add_filter( 'preprocess_comment', 'translangated_comment_pre_save_strip_trpst' ); function translangated_comment_pre_save_strip_trpst( $commentdata ) { $fields = array( 'comment_content', 'comment_author' ); foreach ( $fields as $field ) { if ( isset( $commentdata[ $field ] ) && is_string( $commentdata[ $field ] ) && strpos( $commentdata[ $field ], 'data-trpgettextoriginal=' ) !== false ) { $commentdata[ $field ] = Translangated_Translation_Manager::strip_gettext_tags( $commentdata[ $field ] ); } } return $commentdata; } // 5. WordPress Options Pre-Save — clean option values (skip TRP's own options) add_filter( 'pre_update_option', 'translangated_option_pre_save_strip_trpst', 10, 3 ); function translangated_option_pre_save_strip_trpst( $value, $option, $old_value ) { // Skip TRP's own options if ( strpos( $option, 'translangated_' ) === 0 ) { return $value; } if ( is_string( $value ) ) { if ( strpos( $value, 'data-trpgettextoriginal=' ) !== false ) { $value = Translangated_Translation_Manager::strip_gettext_tags( $value ); } } elseif ( is_array( $value ) ) { $serialized = serialize( $value ); if ( strpos( $serialized, 'data-trpgettextoriginal=' ) !== false ) { array_walk_recursive( $value, 'translangated_array_walk_recursive_strip_gettext_tags' ); } } return $value; } /** * Compatibility with Modern Events Calendar (MEC). * * MEC reads price/total values out of DOM nodes and posts them back via AJAX, * which causes TRP gettext markers wrapping the rendered labels to end up in * post meta (e.g. mec_total_price = "85.00 #!trpst#trp-gettext...#!trpen#"). * That breaks external integrations expecting numeric values. * * Exclude the MEC price/total selectors from frontend translation so the * markers don't reach AJAX submissions, and strip any markers that still slip * through into the affected post meta keys. */ add_filter( 'translangated_no_translate_selectors', 'translangated_mec_no_translate_selectors' ); function translangated_mec_no_translate_selectors( $skip_selectors ) { $skip_selectors[] = '.mec-total-cost'; $skip_selectors[] = '.mec-book-price-total'; $skip_selectors[] = '#mec_total_price'; return $skip_selectors; } add_action( 'added_post_meta', 'translangated_mec_post_meta_strip_trpst', 10, 4 ); add_action( 'updated_postmeta', 'translangated_mec_post_meta_strip_trpst', 10, 4 ); function translangated_mec_post_meta_strip_trpst( $meta_id, $object_id, $meta_key, $meta_value ) { $mec_meta_keys = array( 'mec_total_price', 'mec_paid_amount', 'mec_gateway_label' ); if ( ! in_array( $meta_key, $mec_meta_keys, true ) ) { return; } if ( ! is_string( $meta_value ) || strpos( $meta_value, 'data-trpgettextoriginal=' ) === false ) { return; } if ( ! class_exists( 'Translangated_Translation_Manager' ) ) { return; } $clean_value = Translangated_Translation_Manager::strip_gettext_tags( $meta_value ); // Remove our own hooks to prevent recursion, then re-add remove_action( 'added_post_meta', 'translangated_mec_post_meta_strip_trpst', 10 ); remove_action( 'updated_postmeta', 'translangated_mec_post_meta_strip_trpst', 10 ); update_metadata_by_mid( 'post', $meta_id, $clean_value ); add_action( 'added_post_meta', 'translangated_mec_post_meta_strip_trpst', 10, 4 ); add_action( 'updated_postmeta', 'translangated_mec_post_meta_strip_trpst', 10, 4 ); } /** * Compatibility with WooCommerce country list on checkout. * * Skip detection by translate-dom-changes of the list of countries * */ add_filter( 'translangated_skip_selectors_from_dynamic_translation', 'translangated_woo_skip_dynamic_translation' ); function translangated_woo_skip_dynamic_translation( $skip_selectors ){ if( class_exists( 'WooCommerce' ) ) { $add_skip_selectors = array( '#billing_country', '#shipping_country', '#billing_state', '#shipping_state', '#select2-billing_country-results', '#select2-billing_state-results', '#select2-shipping_country-results', '#select2-shipping_state-results' ); return array_merge( $skip_selectors, $add_skip_selectors ); } return $skip_selectors; } /** * Prevent translation of names and addresses in WooCommerce emails. */ add_action( 'woocommerce_email_customer_details', 'translangated_woo_prevent_address_from_translation_in_emails' ); function translangated_woo_prevent_address_from_translation_in_emails(){ add_filter( 'woocommerce_order_get_formatted_shipping_address', 'translangated_woo_address_no_translate', 10, 3 ); add_filter( 'woocommerce_order_get_formatted_billing_address', 'translangated_woo_address_no_translate', 10, 3 ); } function translangated_woo_address_no_translate( $address, $raw_address, $order ){ return empty( $address ) ? $address : '' . $address . ''; } /** * Compatibility with WooCommerce product variation. * * Add span tag to woocommerce product variation name. * * Product variation name keep changes, but the prefix is the same. Wrap the prefix to allow translating that part separately. */ add_filter( 'woocommerce_product_variation_title', 'translangated_woo_wrap_variation', 8, 4); function translangated_woo_wrap_variation($name, $product, $title_base, $title_suffix){ $separator = ' - '; return $title_suffix ? $title_base . $separator . $title_suffix : $title_base; } /** * Compatibility with WooCommerce Layered Nav widget (Filter Products by Attribute) in dropdown mode. * * The "Any %s" gettext wraps a dynamic taxonomy label. On secondary languages TRP switches the * locale, so the target .mo is active (e.g. French translates "Any %s" → "%s", Spanish → * "Cualquier %s"), producing a mixed-language label. The trp-gettext wrapper also causes * data-no-translation on the