Many themes and plugins support WPML through its hooks, such as apply_filters('wpml_object_id', ...). Signocore Multilanguage answers the eight most common of them, so that code works on a site without WPML. This page lists what each one returns and where it behaves differently from WPML.
When the layer is active
The WPML hooks are registered while Signocore Multilanguage boots on plugins_loaded at priority 10, and only when WPML itself is not active: if the ICL_SITEPRESS_VERSION constant is defined at that point, Signocore Multilanguage registers none of them and WPML answers its own hooks. Before the layer is registered, for example in another plugin's main file, each filter returns the value you pass in.
The layer only answers hooks. It does not define WPML's constants, functions or classes, so code that checks for WPML with defined('ICL_SITEPRESS_VERSION'), defined('ICL_LANGUAGE_CODE'), function_exists('icl_object_id') or class_exists('SitePress') sees no WPML and skips its WPML code, even where the hooks below would have worked. To support both plugins in your own code, see Template functions.
Hooks that are answered
| Hook | Type | Returns |
|---|---|---|
wpml_object_id |
Filter | The ID of an element's translation. |
wpml_current_language |
Filter | The current language code. |
wpml_default_language |
Filter | The default language code. |
wpml_active_languages |
Filter | The active languages, keyed by code. |
wpml_switch_language |
Action | Switches the current language. |
wpml_element_has_translations |
Filter | Whether an element has translations. |
wpml_permalink |
Filter | A URL with its language part swapped. |
wpml_home_url |
Filter | The home URL of the current language. |
wpml_object_id
apply_filters('wpml_object_id', $elementId, $elementType, $returnOriginal, $langCode);
Returns the ID of the post, term or menu that translates $elementId into $langCode.
| Argument | Default | How it is read |
|---|---|---|
$elementId |
A post, term or menu ID. A value that is not numeric is returned unchanged. | |
$elementType |
post |
post for a post of any post type, a post type name such as page, a taxonomy name such as category, or nav_menu for a menu. The stored types from the data model, such as post_page, work too. |
$returnOriginal |
true |
What to return when there is no translation: the original ID when true, null when false. |
$langCode |
Current language | The language to look up. |
Differences from WPML:
$returnOriginaldefaults to true. WPML's default is false. A call that leaves out the third argument, such asapply_filters('wpml_object_id', $id, 'page'), gets the original ID here where WPML returnsnull. Pass the argument explicitly and both plugins behave the same.postmatches any post type. The ID is looked up as a post of whatever type it is, not only thepostpost type.anyis not recognized as an element type. It finds no translation, so you get the original ID ornull.- Menu items are not translated one by one.
nav_menu_itemfinds no translation; translate the whole menu and passnav_menu. - Translations of any status are returned, including drafts and private posts. Check the status when that matters.
A post or term whose type is not translated has no translations and follows the $returnOriginal rule.
// The Spanish version of the shop's terms page, or null when it has none. $termsPageId = apply_filters('wpml_object_id', 42, 'page', false, 'es');
wpml_current_language
apply_filters('wpml_current_language', null);
Returns the current language code, such as es, and ignores the value passed in. It always returns a single language code and never all. Before the language is detected on init at priority 1, it returns the default language. After wpml_switch_language or signocore_ml_switch_language(), it returns the language switched to.
wpml_default_language
apply_filters('wpml_default_language', null);
Returns the default language code and ignores the value passed in.
wpml_active_languages
apply_filters('wpml_active_languages', null, $args);
Returns every active language as an array keyed by language code, in the order the languages were added. Each entry has these keys:
| Key | Value |
|---|---|
code |
The language code, such as es. |
native_name |
The native name, such as Español. |
translated_name |
The English name, such as Spanish. |
default_locale |
The WordPress locale, such as es_ES. |
active |
Always true. |
url |
The current address with its language prefix, subdomain or domain swapped. |
Differences from WPML:
$argsis ignored. Options such asskip_missing,orderby,orderandlink_empty_tohave no effect, so every active language is always returned, also when the current page has no translation in it.activeis true for every language. In WPML it marks the current language. To find the current language, comparecodewithapply_filters('wpml_current_language', null).urldoes not point to the translation. It swaps the language part of the current address and keeps the rest, so on a translated post with its own slug it can lead to a page that does not exist. Usesignocore_ml_get_permalink()for the address of a translation, or the plugin's own switcher, whose links do point to the translations.translated_nameis always the English name, not the name in the current language.- Other keys WPML returns are missing, among them
id,language_code,missingandcountry_flag_url.
$languages = apply_filters('wpml_active_languages', null); $current = apply_filters('wpml_current_language', null); foreach ($languages as $code => $language) { if ($code !== $current) { printf('<a href="%s">%s</a>', esc_url($language['url']), esc_html($language['native_name'])); } }
wpml_switch_language
do_action('wpml_switch_language', $langCode);
Makes another language the current one, the same way as signocore_ml_switch_language(): language-filtered queries, string translations, the locale filter and, on the front end, home_url() follow it. It fires signocore_ml_language_switched.
Differences from WPML:
nulldoes not switch back. WPML treatsnullas a request to return to the request's own language. Here, anything that is not a string is ignored, so the language stays switched until the request ends.allis ignored. There is no mode that lists content from every language through this action; use thesml_filter_languagequery argument described in Template functions instead.- The code is not checked against your languages. An unknown code makes filtered queries return nothing.
- Switches nest. Each switch remembers the language before it, and
signocore_ml_restore_language()goes back one step.
Code that must run on both plugins can restore like this:
do_action('wpml_switch_language', 'es'); $spanishPosts = get_posts([ 'post_type' => 'post', 'suppress_filters' => false, 'sml_filter_language' => true, ]); if (!defined('ICL_SITEPRESS_VERSION') && function_exists('signocore_ml_restore_language')) { signocore_ml_restore_language(); } else { do_action('wpml_switch_language', null); }
Signocore Multilanguage does not filter secondary queries by itself, so the sml_filter_language argument is what limits that query to Spanish; WPML ignores it.
wpml_element_has_translations
apply_filters('wpml_element_has_translations', null, $elementId, $elementType);
Returns true when the element's translation group holds more than one post or term, and false otherwise, ignoring the value passed in. $elementType defaults to post and is read as in wpml_object_id. Translations count whatever their status, drafts included. An ID that is not a positive number, an element without a language record and a menu all return false.
wpml_permalink
apply_filters('wpml_permalink', $url, $langCode);
Returns $url with its language prefix, subdomain or domain swapped to $langCode, as signocore_ml_get_url_for_language() does. The path stays exactly as it is.
Differences from WPML:
- Only the language part changes. The filter does not look up the translated post or its translated slug, so on a translated post with its own slug the result can lead to a page that does not exist. For the address of a translation, get its ID with
wpml_object_idand pass it tosignocore_ml_get_permalink(). - The third argument,
$full_resolution, is ignored. - Without a language code, the URL is returned unchanged. Always pass the code.
wpml_home_url
apply_filters('wpml_home_url', null);
Returns home_url('/'), which on the front end is the home URL of the current language, such as https://example.com/es/. Any argument is ignored. In wp-admin, home_url() is not changed by the plugin, so the filter returns the site's own home URL there.
What is not provided
Everything else in WPML's API is missing, among others:
| Kind | Not provided |
|---|---|
| Functions | icl_object_id(), icl_get_languages(), icl_t(), icl_register_string() |
| Constants | ICL_SITEPRESS_VERSION, ICL_LANGUAGE_CODE, ICL_LANGUAGE_NAME, ICL_LANGUAGE_NAME_EN |
| Objects | The SitePress class and the global $sitepress |
| String hooks | wpml_register_single_string, wpml_translate_single_string |
| Element hooks | wpml_post_language_details, wpml_element_language_code, wpml_element_language_details, wpml_element_trid, wpml_get_element_translations, wpml_set_element_language_details, wpml_is_translated_post_type |
| Other hooks | wpml_loaded, wpml_language_has_switched, wpml_add_language_selector, wpml_language_is_active, wpml_display_language_names |
| Configuration | wpml-config.xml files in themes and plugins are not read. |
Most of these have a counterpart in Signocore Multilanguage's own API:
| WPML | Signocore Multilanguage |
|---|---|
ICL_LANGUAGE_CODE |
signocore_ml_get_current_language() |
icl_object_id() |
signocore_ml_get_translation() |
icl_get_languages() |
signocore_ml_get_active_languages() and signocore_ml_get_permalink() |
wpml_post_language_details, wpml_element_language_code |
signocore_ml_get_element_language() |
wpml_get_element_translations |
signocore_ml_get_translations() |
wpml_register_single_string |
The signocore_ml_register_string action |
wpml_translate_single_string, icl_t() |
signocore_ml_translate_string() |
wpml_add_language_selector |
signocore_language_switcher() |
wpml_language_has_switched |
The signocore_ml_language_switched action |
To move content, links and strings from a WPML site, see Switch from WPML or Polylang.