PluginProbe
Polylang / 3.6
Polylang v3.6
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / modules / wpml / wpml-api.php

wpml-api.php in Polylang 3.6, at modules/wpml/wpml-api.php

496 lines 16.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * A class to handle the WPML API based on hooks, introduced since WPML 3.2.
8 * It partly relies on the legacy API.
9 *
10 * @see https://wpml.org/documentation/support/wpml-coding-api/wpml-hooks-reference/
11 *
12 * @since 2.0
13 */
14 class PLL_WPML_API {
15 /**
16 * Stores the original language when the language is switched.
17 *
18 * @var PLL_Language|null
19 */
20 private static $original_language = null;
21
22 /**
23 * Constructor.
24 *
25 * @since 2.0
26 */
27 public function __construct() {
28 /*
29 * Site Wide Language information.
30 */
31 add_filter( 'wpml_active_languages', array( $this, 'wpml_active_languages' ), 10, 2 );
32 add_filter( 'wpml_display_language_names', array( $this, 'wpml_display_language_names' ), 10, 2 ); // Because we don't translate language names, 3rd to 5th parameters are not supported.
33 // wpml_translated_language_name => not applicable.
34 add_filter( 'wpml_current_language', 'pll_current_language', 10, 0 );
35 add_filter( 'wpml_default_language', 'pll_default_language', 10, 0 );
36 // wpml_add_language_selector => not implemented.
37 // wpml_footer_language_selector => not applicable.
38 add_action( 'wpml_add_language_form_field', array( $this, 'wpml_add_language_form_field' ) );
39 add_filter( 'wpml_language_is_active', array( $this, 'wpml_language_is_active' ), 10, 2 );
40 add_filter( 'wpml_is_rtl', array( $this, 'wpml_is_rtl' ) );
41 // wpml_language_form_input_field => See wpml_add_language_form_field
42 // wpml_language_has_switched => See wpml_switch_language
43 add_filter( 'wpml_element_trid', array( $this, 'wpml_element_trid' ), 10, 3 );
44 add_filter( 'wpml_get_element_translations', array( $this, 'wpml_get_element_translations' ), 10, 3 );
45 // wpml_language_switcher => not implemented.
46 // wpml_browser_redirect_language_params => not implemented.
47 // wpml_enqueue_browser_redirect_language => not applicable.
48 // wpml_enqueued_browser_redirect_language => not applicable.
49 // wpml_encode_string => not applicable.
50 // wpml_decode_string => not applicable.
51
52 /*
53 * Retrieving Language Information for Content.
54 */
55 add_filter( 'wpml_post_language_details', 'wpml_get_language_information', 10, 2 );
56 add_action( 'wpml_switch_language', array( __CLASS__, 'wpml_switch_language' ), 10, 2 );
57 add_filter( 'wpml_element_language_code', array( $this, 'wpml_element_language_code' ), 10, 2 );
58 // wpml_element_language_details => not applicable.
59
60 /*
61 * Retrieving Localized Content.
62 */
63 add_filter( 'wpml_home_url', 'pll_home_url', 10, 0 );
64 add_filter( 'wpml_element_link', 'icl_link_to_element', 10, 7 );
65 add_filter( 'wpml_object_id', 'icl_object_id', 10, 4 );
66 add_filter( 'wpml_translate_single_string', array( $this, 'wpml_translate_single_string' ), 10, 4 );
67 // wpml_translate_string => not applicable.
68 // wpml_unfiltered_admin_string => not implemented.
69 add_filter( 'wpml_permalink', array( $this, 'wpml_permalink' ), 10, 2 );
70 // wpml_elements_without_translations => not implemented.
71 add_filter( 'wpml_get_translated_slug', array( $this, 'wpml_get_translated_slug' ), 10, 3 );
72
73 /*
74 * Finding the Translation State of Content.
75 */
76 // wpml_element_translation_type => not implemented.
77 add_filter( 'wpml_element_has_translations', array( $this, 'wpml_element_has_translations' ), 10, 3 );
78 // wpml_master_post_from_duplicate => not applicable.
79 // wpml_post_duplicates => not applicable.
80
81 /*
82 * Inserting Content.
83 */
84 // wpml_admin_make_post_duplicates => not applicable.
85 // wpml_make_post_duplicates => not applicable.
86 add_action( 'wpml_register_single_string', 'icl_register_string', 10, 3 );
87 // wpml_register_string => not applicable.
88 // wpml_register_string_packages => not applicable.
89 // wpml_delete_package_action => not applicable.
90 // wpml_show_package_language_ui => not applicable.
91 // wpml_set_element_language_details => not implemented.
92 // wpml_multilingual_options => not applicable.
93
94 /*
95 * Miscellaneous
96 */
97 // wpml_element_type => not applicable.
98 // wpml_setting => not applicable.
99 // wpml_sub_setting => not applicable.
100 // wpml_editor_cf_to_display => not applicable.
101 // wpml_tm_save_translation_cf => not implemented.
102 // wpml_tm_xliff_export_translated_cf => not applicable.
103 // wpml_tm_xliff_export_original_cf => not applicable.
104 // wpml_duplicate_generic_string => not applicable.
105 // wpml_translatable_user_meta_fields => not implemented.
106 // wpml_cross_domain_language_data => not applicable.
107 // wpml_get_cross_domain_language_data => not applicable.
108 // wpml_loaded => not applicable.
109 // wpml_st_loaded => not applicable.
110 // wpml_tm_loaded => not applicable.
111 // wpml_hide_management_column (3.4.1) => not applicable.
112 // wpml_ls_directories_to_scan => not applicable.
113 // wpml_ls_model_css_classes => not applicable.
114 // wpml_ls_model_language_css_classes => not applicable.
115 // wpml_tf_feedback_open_link => not applicable.
116 // wpml_sync_custom_field => not implemented.
117 // wpml_sync_all_custom_fields => not implemented.
118 // wpml_is_redirected => not implemented.
119
120 /*
121 * Updating Content
122 */
123 // wpml_set_translation_mode_for_post_type => not implemented.
124
125 /*
126 * Undocumented
127 */
128 add_filter( 'wpml_is_translated_post_type', array( $this, 'wpml_is_translated_post_type' ), 10, 2 );
129 add_filter( 'wpml_is_translated_taxonomy', array( $this, 'wpml_is_translated_taxonomy' ), 10, 2 );
130 }
131
132 /**
133 * Get a list of the languages enabled for a site.
134 *
135 * @since 2.0
136 *
137 * @param mixed $null Not used.
138 * @param array| string $args See arguments of icl_get_languages().
139 * @return array Array of arrays per language.
140 */
141 public function wpml_active_languages( $null, $args = '' ) {
142 return icl_get_languages( $args );
143 }
144
145 /**
146 * In WPML, get a language's native and translated name for display in a custom language switcher
147 * Since Polylang does not implement the translated name, always returns only the native name,
148 * so the 3rd, 4th and 5th parameters are not used.
149 *
150 * @since 2.2
151 *
152 * @param mixed $null Not used.
153 * @param string $native_name The language native name.
154 * @return string
155 */
156 public function wpml_display_language_names( $null, $native_name ) {
157 return $native_name;
158 }
159
160 /**
161 * Returns an HTML hidden input field with name=”lang” and as value the current language.
162 *
163 * @since 2.0
164 *
165 * @return void
166 */
167 public function wpml_add_language_form_field() {
168 $lang = pll_current_language();
169 $field = sprintf( '<input type="hidden" name="lang" value="%s" />', esc_attr( $lang ) );
170 $field = apply_filters( 'wpml_language_form_input_field', $field, $lang );
171 echo $field; // phpcs:ignore WordPress.Security.EscapeOutput
172 }
173
174 /**
175 * Find out if a specific language is enabled for the site.
176 *
177 * @since 2.0
178 *
179 * @param mixed $null Not used.
180 * @param string $slug Language code.
181 * @return bool
182 */
183 public function wpml_language_is_active( $null, $slug ) {
184 $language = PLL()->model->get_language( $slug );
185 return ! empty( $language ) && $language->active;
186 }
187
188 /**
189 * Find out whether the current language text direction is RTL or not.
190 *
191 * @since 2.0
192 *
193 * @return bool
194 */
195 public function wpml_is_rtl() {
196 return pll_current_language( 'is_rtl' );
197 }
198
199 /**
200 * Returns the id of the translation group of a translated element.
201 *
202 * @since 3.4
203 *
204 * @param mixed $empty_value Not used.
205 * @param int $element_id The id of the item, post id for posts, term_taxonomy_id for terms.
206 * @param string $element_type Optional. The type of an element.
207 * @return int
208 */
209 public function wpml_element_trid( $empty_value, $element_id, $element_type = 'post_post' ) {
210 if ( 0 === strpos( $element_type, 'tax_' ) ) {
211 $element = get_term_by( 'term_taxonomy_id', $element_id );
212 if ( $element instanceof WP_Term ) {
213 $tr_term = PLL()->model->term->get_object_term( $element->term_id, 'term_translations' );
214 }
215 }
216
217 if ( 0 === strpos( $element_type, 'post_' ) ) {
218 $tr_term = PLL()->model->post->get_object_term( $element_id, 'post_translations' );
219 }
220
221 if ( isset( $tr_term ) && $tr_term instanceof WP_Term ) {
222 return $tr_term->term_id;
223 }
224
225 return 0;
226 }
227
228 /**
229 * Returns the element translations info using the ID of the translation group.
230 *
231 * @since 3.4
232 *
233 * @param mixed $empty_value Not used.
234 * @param int $trid The ID of the translation group.
235 * @param string $element_type Optional. The type of an element.
236 * @return stdClass[]
237 */
238 public function wpml_get_element_translations( $empty_value, $trid, $element_type = 'post_post' ) {
239 $return = array();
240
241 if ( 0 === strpos( $element_type, 'tax_' ) ) {
242 $translations = PLL()->model->term->get_translations_from_term_id( $trid );
243 if ( empty( $translations ) ) {
244 return array();
245 }
246
247 $original = min( $translations ); // We suppose that the original is the first term created.
248 $source_lang = array_search( $original, $translations );
249
250 $args = array(
251 'include' => $translations,
252 'hide_empty' => false,
253 );
254 $_terms = get_terms( $args );
255
256 if ( ! is_array( $_terms ) ) {
257 return array();
258 }
259
260 $terms = array();
261 foreach ( $_terms as $term ) {
262 $terms[ $term->term_id ] = $term;
263 }
264
265 foreach ( $translations as $lang => $term_id ) {
266 if ( empty( $terms[ $term_id ] ) ) {
267 continue;
268 }
269
270 /*
271 * It seems that WPML fills the `instances` property with the total number of posts
272 * related to this term, while `WP_Term::$count` includes only *published* posts.
273 * We intentionnally accept this difference to avoid extra DB queries.
274 */
275 $return[ $lang ] = (object) array(
276 'translation_id' => '0', // We have nothing equivalent.
277 'language_code' => $lang,
278 'element_id' => (string) $terms[ $term_id ]->term_taxonomy_id,
279 'source_language_code' => $source_lang === $lang ? null : $source_lang,
280 'element_type' => $element_type,
281 'original' => $original === $term_id ? '1' : '0',
282 'name' => $terms[ $term_id ]->name,
283 'term_id' => (string) $term_id,
284 'instances' => (string) $terms[ $term_id ]->count,
285 );
286 }
287 }
288
289 if ( 0 === strpos( $element_type, 'post_' ) ) {
290 $translations = PLL()->model->post->get_translations_from_term_id( $trid );
291 if ( empty( $translations ) ) {
292 return array();
293 }
294
295 $original = min( $translations ); // We suppose that the original is the first post created.
296 $source_lang = array_search( $original, $translations );
297
298 $args = array(
299 'post__in' => $translations,
300 'no_paging' => true,
301 'posts_per_page' => -1,
302 'update_post_meta_cache' => false,
303 'update_post_term_cache' => false,
304 'lang' => '',
305 );
306 $_posts = get_posts( $args );
307
308 $posts = array();
309 foreach ( $_posts as $post ) {
310 $posts[ $post->ID ] = $post;
311 }
312
313 foreach ( $translations as $lang => $post_id ) {
314 if ( empty( $posts[ $post_id ] ) ) {
315 continue;
316 }
317
318 $return[ $lang ] = (object) array(
319 'translation_id' => '0', // We have nothing equivalent.
320 'language_code' => $lang,
321 'element_id' => (string) $post_id,
322 'source_language_code' => $source_lang === $lang ? null : $source_lang,
323 'element_type' => $element_type,
324 'original' => $original === $post_id ? '1' : '0',
325 'post_title' => $posts[ $post_id ]->post_title,
326 'post_status' => $posts[ $post_id ]->post_status,
327 );
328 }
329 }
330
331 return $return;
332 }
333
334 /**
335 * Switches whole site to the given language or restores the language that was set when first calling this function.
336 * Unlike the WPML original action, it is not possible to set the current language and the cookie to different values.
337 *
338 * @since 2.7
339 *
340 * @param null|string $lang Language code to switch into, restores the original language if null.
341 * @param bool|string $cookie Optionally also switches the cookie.
342 * @return void
343 */
344 public static function wpml_switch_language( $lang = null, $cookie = false ) {
345 if ( null === self::$original_language ) {
346 self::$original_language = PLL()->curlang;
347 }
348
349 if ( empty( $lang ) ) {
350 PLL()->curlang = self::$original_language;
351 } elseif ( 'all' === $lang ) {
352 PLL()->curlang = null;
353 } elseif ( in_array( $lang, pll_languages_list() ) ) {
354 PLL()->curlang = PLL()->model->get_language( $lang );
355 }
356
357 if ( $cookie && isset( PLL()->choose_lang ) ) {
358 PLL()->choose_lang->maybe_setcookie();
359 }
360
361 do_action( 'wpml_language_has_switched', $lang, $cookie, self::$original_language );
362 }
363
364 /**
365 * Get the language code for a translatable element.
366 *
367 * @since 2.0
368 *
369 * @param mixed $language_code A 2-letter language code.
370 * @param array $args An array with two keys element_id => post_id or term_taxonomy_id, element_type => post type or taxonomy
371 * @return string|null
372 */
373 public function wpml_element_language_code( $language_code, $args ) {
374 $type = $args['element_type'];
375 $id = $args['element_id'];
376
377 if ( 'post' === $type || pll_is_translated_post_type( $type ) ) {
378 $language = pll_get_post_language( $id );
379 return is_string( $language ) ? $language : null;
380 }
381
382 if ( 'term' === $type || pll_is_translated_taxonomy( $type ) ) {
383 $term = get_term_by( 'term_taxonomy_id', $id );
384 if ( $term instanceof WP_Term ) {
385 $id = $term->term_id;
386 }
387 $language = pll_get_term_language( $id );
388 return is_string( $language ) ? $language : null;
389 }
390
391 return null;
392 }
393
394 /**
395 * Translates a string.
396 *
397 * @since 2.0
398 *
399 * @param string $string The string's original value.
400 * @param string $context The string's registered context.
401 * @param string $name The string's registered name.
402 * @param null|string $lang Optional, return the translation in this language, defaults to current language.
403 * @return string The translated string.
404 */
405 public function wpml_translate_single_string( $string, $context, $name, $lang = null ) {
406 $has_translation = null; // Passed by reference.
407 return icl_translate( $context, $name, $string, false, $has_translation, $lang );
408 }
409
410 /**
411 * Converts a permalink to a language specific permalink.
412 *
413 * @since 2.2
414 *
415 * @param string $url The url to filter.
416 * @param null|string $lang Language code, optional, defaults to the current language.
417 * @return string
418 */
419 public function wpml_permalink( $url, $lang = '' ) {
420 $lang = PLL()->model->get_language( $lang );
421
422 if ( empty( $lang ) && ! empty( PLL()->curlang ) ) {
423 $lang = PLL()->curlang;
424 }
425
426 return empty( $lang ) ? $url : PLL()->links_model->switch_language_in_link( $url, $lang );
427 }
428
429 /**
430 * Translates a post type slug.
431 *
432 * @since 2.2
433 *
434 * @param string $slug Post type slug.
435 * @param string $post_type Post type name.
436 * @param string $lang Optional language code (defaults to current language).
437 * @return string
438 */
439 public function wpml_get_translated_slug( $slug, $post_type, $lang = null ) {
440 if ( isset( PLL()->translate_slugs ) ) {
441 if ( empty( $lang ) ) {
442 $lang = pll_current_language();
443 }
444
445 $slug = PLL()->translate_slugs->slugs_model->get_translated_slug( $post_type, $lang );
446 }
447 return $slug;
448 }
449
450 /**
451 * Find out whether a post type or a taxonomy term is translated.
452 *
453 * @since 2.0
454 *
455 * @param mixed $null Not used.
456 * @param int $id The post_id or term_id.
457 * @param string $type The post type or taxonomy.
458 * @return bool
459 */
460 public function wpml_element_has_translations( $null, $id, $type ) {
461 if ( 'post' === $type || pll_is_translated_post_type( $type ) ) {
462 return count( pll_get_post_translations( $id ) ) > 1;
463 } elseif ( 'term' === $type || pll_is_translated_taxonomy( $type ) ) {
464 return count( pll_get_term_translations( $id ) ) > 1;
465 }
466
467 return false;
468 }
469
470 /**
471 * Returns true if languages and translations are managed for this post type.
472 *
473 * @since 3.4
474 *
475 * @param mixed $value Not used.
476 * @param string $post_type The post type name.
477 * @return bool
478 */
479 public function wpml_is_translated_post_type( $value, $post_type ) {
480 return pll_is_translated_post_type( $post_type );
481 }
482
483 /**
484 * Returns true if languages and translations are managed for this taxonomy.
485 *
486 * @since 3.4
487 *
488 * @param mixed $value Not used.
489 * @param string $taxonomy The taxonomy name.
490 * @return bool
491 */
492 public function wpml_is_translated_taxonomy( $value, $taxonomy ) {
493 return pll_is_translated_taxonomy( $taxonomy );
494 }
495 }
496