PluginProbe
Polylang / 3.7.1
Polylang v3.7.1
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.7.1, at modules/wpml/wpml-api.php

501 lines 16.9 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( self::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
170 if ( empty( $lang ) ) {
171 return;
172 }
173
174 $field = sprintf( '<input type="hidden" name="lang" value="%s" />', esc_attr( $lang ) );
175 $field = apply_filters( 'wpml_language_form_input_field', $field, $lang );
176 echo $field; // phpcs:ignore WordPress.Security.EscapeOutput
177 }
178
179 /**
180 * Find out if a specific language is enabled for the site.
181 *
182 * @since 2.0
183 *
184 * @param mixed $null Not used.
185 * @param string $slug Language code.
186 * @return bool
187 */
188 public function wpml_language_is_active( $null, $slug ) {
189 $language = PLL()->model->get_language( $slug );
190 return ! empty( $language ) && $language->active;
191 }
192
193 /**
194 * Find out whether the current language text direction is RTL or not.
195 *
196 * @since 2.0
197 *
198 * @return bool
199 */
200 public function wpml_is_rtl() {
201 return pll_current_language( 'is_rtl' );
202 }
203
204 /**
205 * Returns the id of the translation group of a translated element.
206 *
207 * @since 3.4
208 *
209 * @param mixed $empty_value Not used.
210 * @param int $element_id The id of the item, post id for posts, term_taxonomy_id for terms.
211 * @param string $element_type Optional. The type of an element.
212 * @return int
213 */
214 public function wpml_element_trid( $empty_value, $element_id, $element_type = 'post_post' ) {
215 if ( 0 === strpos( $element_type, 'tax_' ) ) {
216 $element = get_term_by( 'term_taxonomy_id', $element_id );
217 if ( $element instanceof WP_Term ) {
218 $tr_term = PLL()->model->term->get_object_term( $element->term_id, 'term_translations' );
219 }
220 }
221
222 if ( 0 === strpos( $element_type, 'post_' ) ) {
223 $tr_term = PLL()->model->post->get_object_term( $element_id, 'post_translations' );
224 }
225
226 if ( isset( $tr_term ) && $tr_term instanceof WP_Term ) {
227 return $tr_term->term_id;
228 }
229
230 return 0;
231 }
232
233 /**
234 * Returns the element translations info using the ID of the translation group.
235 *
236 * @since 3.4
237 *
238 * @param mixed $empty_value Not used.
239 * @param int $trid The ID of the translation group.
240 * @param string $element_type Optional. The type of an element.
241 * @return stdClass[]
242 */
243 public function wpml_get_element_translations( $empty_value, $trid, $element_type = 'post_post' ) {
244 $return = array();
245
246 if ( 0 === strpos( $element_type, 'tax_' ) ) {
247 $translations = PLL()->model->term->get_translations_from_term_id( $trid );
248 if ( empty( $translations ) ) {
249 return array();
250 }
251
252 $original = min( $translations ); // We suppose that the original is the first term created.
253 $source_lang = array_search( $original, $translations );
254
255 $args = array(
256 'include' => $translations,
257 'hide_empty' => false,
258 );
259 $_terms = get_terms( $args );
260
261 if ( ! is_array( $_terms ) ) {
262 return array();
263 }
264
265 $terms = array();
266 foreach ( $_terms as $term ) {
267 $terms[ $term->term_id ] = $term;
268 }
269
270 foreach ( $translations as $lang => $term_id ) {
271 if ( empty( $terms[ $term_id ] ) ) {
272 continue;
273 }
274
275 /*
276 * It seems that WPML fills the `instances` property with the total number of posts
277 * related to this term, while `WP_Term::$count` includes only *published* posts.
278 * We intentionnally accept this difference to avoid extra DB queries.
279 */
280 $return[ $lang ] = (object) array(
281 'translation_id' => '0', // We have nothing equivalent.
282 'language_code' => $lang,
283 'element_id' => (string) $terms[ $term_id ]->term_taxonomy_id,
284 'source_language_code' => $source_lang === $lang ? null : $source_lang,
285 'element_type' => $element_type,
286 'original' => $original === $term_id ? '1' : '0',
287 'name' => $terms[ $term_id ]->name,
288 'term_id' => (string) $term_id,
289 'instances' => (string) $terms[ $term_id ]->count,
290 );
291 }
292 }
293
294 if ( 0 === strpos( $element_type, 'post_' ) ) {
295 $translations = PLL()->model->post->get_translations_from_term_id( $trid );
296 if ( empty( $translations ) ) {
297 return array();
298 }
299
300 $original = min( $translations ); // We suppose that the original is the first post created.
301 $source_lang = array_search( $original, $translations );
302
303 $args = array(
304 'post__in' => $translations,
305 'no_paging' => true,
306 'posts_per_page' => -1,
307 'update_post_meta_cache' => false,
308 'update_post_term_cache' => false,
309 'lang' => '',
310 );
311 $_posts = get_posts( $args );
312
313 $posts = array();
314 foreach ( $_posts as $post ) {
315 $posts[ $post->ID ] = $post;
316 }
317
318 foreach ( $translations as $lang => $post_id ) {
319 if ( empty( $posts[ $post_id ] ) ) {
320 continue;
321 }
322
323 $return[ $lang ] = (object) array(
324 'translation_id' => '0', // We have nothing equivalent.
325 'language_code' => $lang,
326 'element_id' => (string) $post_id,
327 'source_language_code' => $source_lang === $lang ? null : $source_lang,
328 'element_type' => $element_type,
329 'original' => $original === $post_id ? '1' : '0',
330 'post_title' => $posts[ $post_id ]->post_title,
331 'post_status' => $posts[ $post_id ]->post_status,
332 );
333 }
334 }
335
336 return $return;
337 }
338
339 /**
340 * Switches whole site to the given language or restores the language that was set when first calling this function.
341 * Unlike the WPML original action, it is not possible to set the current language and the cookie to different values.
342 *
343 * @since 2.7
344 *
345 * @param null|string $lang Language code to switch into, restores the original language if null.
346 * @param bool|string $cookie Optionally also switches the cookie.
347 * @return void
348 */
349 public static function wpml_switch_language( $lang = null, $cookie = false ) {
350 if ( null === self::$original_language ) {
351 self::$original_language = PLL()->curlang;
352 }
353
354 if ( empty( $lang ) ) {
355 PLL()->curlang = self::$original_language;
356 } elseif ( 'all' === $lang ) {
357 PLL()->curlang = null;
358 } elseif ( in_array( $lang, pll_languages_list() ) ) {
359 PLL()->curlang = PLL()->model->get_language( $lang );
360 }
361
362 if ( $cookie && isset( PLL()->choose_lang ) ) {
363 PLL()->choose_lang->maybe_setcookie();
364 }
365
366 do_action( 'wpml_language_has_switched', $lang, $cookie, self::$original_language );
367 }
368
369 /**
370 * Get the language code for a translatable element.
371 *
372 * @since 2.0
373 *
374 * @param mixed $language_code A 2-letter language code.
375 * @param array $args An array with two keys element_id => post_id or term_taxonomy_id, element_type => post type or taxonomy
376 * @return string|null
377 */
378 public function wpml_element_language_code( $language_code, $args ) {
379 $type = $args['element_type'];
380 $id = $args['element_id'];
381
382 if ( 'post' === $type || pll_is_translated_post_type( $type ) ) {
383 $language = pll_get_post_language( $id );
384 return is_string( $language ) ? $language : null;
385 }
386
387 if ( 'term' === $type || pll_is_translated_taxonomy( $type ) ) {
388 $term = get_term_by( 'term_taxonomy_id', $id );
389 if ( $term instanceof WP_Term ) {
390 $id = $term->term_id;
391 }
392 $language = pll_get_term_language( $id );
393 return is_string( $language ) ? $language : null;
394 }
395
396 return null;
397 }
398
399 /**
400 * Translates a string.
401 *
402 * @since 2.0
403 *
404 * @param string $string The string's original value.
405 * @param string $context The string's registered context.
406 * @param string $name The string's registered name.
407 * @param null|string $lang Optional, return the translation in this language, defaults to current language.
408 * @return string The translated string.
409 */
410 public function wpml_translate_single_string( $string, $context, $name, $lang = null ) {
411 $has_translation = null; // Passed by reference.
412 return icl_translate( $context, $name, $string, false, $has_translation, $lang );
413 }
414
415 /**
416 * Converts a permalink to a language specific permalink.
417 *
418 * @since 2.2
419 *
420 * @param string $url The url to filter.
421 * @param null|string $lang Language code, optional, defaults to the current language.
422 * @return string
423 */
424 public function wpml_permalink( $url, $lang = '' ) {
425 $lang = PLL()->model->get_language( $lang );
426
427 if ( empty( $lang ) && ! empty( PLL()->curlang ) ) {
428 $lang = PLL()->curlang;
429 }
430
431 return empty( $lang ) ? $url : PLL()->links_model->switch_language_in_link( $url, $lang );
432 }
433
434 /**
435 * Translates a post type slug.
436 *
437 * @since 2.2
438 *
439 * @param string $slug Post type slug.
440 * @param string $post_type Post type name.
441 * @param string $lang Optional language code (defaults to current language).
442 * @return string
443 */
444 public function wpml_get_translated_slug( $slug, $post_type, $lang = null ) {
445 if ( isset( PLL()->translate_slugs ) ) {
446 if ( empty( $lang ) ) {
447 $lang = pll_current_language();
448 }
449
450 $slug = PLL()->translate_slugs->slugs_model->get_translated_slug( $post_type, $lang );
451 }
452 return $slug;
453 }
454
455 /**
456 * Find out whether a post type or a taxonomy term is translated.
457 *
458 * @since 2.0
459 *
460 * @param mixed $null Not used.
461 * @param int $id The post_id or term_id.
462 * @param string $type The post type or taxonomy.
463 * @return bool
464 */
465 public function wpml_element_has_translations( $null, $id, $type ) {
466 if ( 'post' === $type || pll_is_translated_post_type( $type ) ) {
467 return count( pll_get_post_translations( $id ) ) > 1;
468 } elseif ( 'term' === $type || pll_is_translated_taxonomy( $type ) ) {
469 return count( pll_get_term_translations( $id ) ) > 1;
470 }
471
472 return false;
473 }
474
475 /**
476 * Returns true if languages and translations are managed for this post type.
477 *
478 * @since 3.4
479 *
480 * @param mixed $value Not used.
481 * @param string $post_type The post type name.
482 * @return bool
483 */
484 public function wpml_is_translated_post_type( $value, $post_type ) {
485 return pll_is_translated_post_type( $post_type );
486 }
487
488 /**
489 * Returns true if languages and translations are managed for this taxonomy.
490 *
491 * @since 3.4
492 *
493 * @param mixed $value Not used.
494 * @param string $taxonomy The taxonomy name.
495 * @return bool
496 */
497 public function wpml_is_translated_taxonomy( $value, $taxonomy ) {
498 return pll_is_translated_taxonomy( $taxonomy );
499 }
500 }
501