PluginProbe
Polylang / 0.8.3
Polylang v0.8.3
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 / include / core.php

core.php in Polylang 0.8.3, at include/core.php

699 lines 29.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // filters posts and terms by language, rewrites links to include the language, etc...
3 // used only for frontend
4 class Polylang_Core extends Polylang_base {
5 private $curlang; // current language
6 private $default_locale;
7 private $list_textdomains = array(); // all text domains
8 private $search_form_filter = false; // did we pass our get_search_form filter ?
9
10 // options often needed
11 private $page_for_posts;
12 private $page_on_front;
13
14 function __construct() {
15 parent::__construct();
16
17 // init options often needed
18 $this->page_for_posts = get_option('page_for_posts');
19 $this->page_on_front = get_option('page_on_front');
20
21 // text domain management
22 add_action('init', array(&$this, 'init'));
23 add_filter('override_load_textdomain', array(&$this, 'mofile'), 10, 3);
24 add_action('wp', array(&$this, 'load_textdomains'));
25 add_action('login_init', array(&$this, 'load_textdomains'));
26
27 // filters posts according to the language
28 add_filter('pre_get_posts', array(&$this, 'pre_get_posts'));;
29
30 // filter sticky posts by current language
31 add_filter('option_sticky_posts', array(&$this, 'option_sticky_posts'));
32
33 // translates page for posts and page on front
34 add_filter('option_page_for_posts', array(&$this, 'translate_page'));
35 add_filter('option_page_on_front', array(&$this, 'translate_page'));
36 }
37
38 // set these filters and actions only once the current language has been defined
39 function add_language_filters() {
40 // filters the WordPress locale
41 add_filter('locale', array(&$this, 'get_locale'));
42
43 // modifies the language information in rss feed
44 // useful if WP < 3.4
45 add_filter('option_rss_language', array(&$this, 'option_rss_language'));
46
47 // filters categories and post tags by language
48 add_filter('terms_clauses', array(&$this, 'terms_clauses'), 10, 3);
49
50 // meta in the html head section
51 add_action('wp_head', array(&$this, 'wp_head'));
52
53 // modifies the page link in case the front page is not in the default language
54 add_filter('page_link', array(&$this, 'page_link'), 10, 2);
55
56 // prevents redirection of the homepage
57 add_filter('redirect_canonical', array(&$this, 'redirect_canonical'), 10, 2);
58
59 // adds javascript at the end of the document
60 add_action('wp_print_footer_scripts', array(&$this, 'wp_print_footer_scripts'));
61
62 // adds the language information in the search form
63 // low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form
64 add_filter('get_search_form', array(&$this, 'get_search_form'), 99);
65
66 // filters the pages according to the current language in wp_list_pages
67 add_filter('wp_list_pages_excludes', array(&$this, 'wp_list_pages_excludes'));
68
69 // filters the comments according to the current language
70 add_filter('comments_clauses', array(&$this, 'comments_clauses'), 10, 2);
71
72 // rewrites archives, next and previous post links to filter them by language
73 foreach (array('getarchives', 'get_previous_post', 'get_next_post') as $filter)
74 foreach (array('_join', '_where') as $clause)
75 add_filter($filter.$clause, array(&$this, 'posts'.$clause));
76
77 // rewrites author and date links to filter them by language
78 foreach (array('feed_link', 'author_link', 'post_type_archive_link', 'year_link', 'month_link', 'day_link') as $filter)
79 add_filter($filter, array(&$this, 'archive_link'));
80
81 $this->add_post_term_link_filters(); // these filters are in base as they may be used on admin side too
82
83 // filters the nav menus according to the current language
84 add_filter('theme_mod_nav_menu_locations', array(&$this, 'nav_menu_locations'));
85 add_filter('wp_nav_menu_args', array(&$this, 'wp_nav_menu_args'));
86 add_filter('wp_nav_menu_items', array(&$this, 'wp_nav_menu_items'), 10, 2);
87
88 // filters the widgets according to the current language
89 add_filter('widget_display_callback', array(&$this, 'widget_display_callback'), 10, 3);
90
91 // strings translation (must be applied before WordPress applies its default formatting filters)
92 add_filter('widget_title', array(&$this, 'widget_title'), 1);
93 add_filter('bloginfo', array(&$this, 'bloginfo'), 1, 2);
94 add_filter('get_bloginfo_rss', array(&$this, 'bloginfo'), 1, 2);
95
96 // modifies the home url
97 if (PLL_FILTER_HOME_URL)
98 add_filter('home_url', array(&$this, 'home_url'));
99
100 // Template tag: displays the language switcher
101 // FIXME Backward compatibility for versions < 0.5 -> replaced by pll_the_languages
102 add_action('the_languages', array(&$this, 'the_languages'));
103 }
104
105 // returns the language according to browser preference or the default language
106 function get_preferred_language() {
107 // check first is the user was already browsing this site
108 if (isset($_COOKIE['wordpress_polylang']))
109 return $this->get_language($_COOKIE['wordpress_polylang']);
110
111 // sets the browsing language according to the browser preferences
112 // code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
113 if ($this->options['browser']) {
114 $accept_langs = array();
115
116 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
117 // break up string into pieces (languages and q factors)
118 preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
119
120 if (count($lang_parse[1])) {
121 // NOTE: array_combine => PHP5
122 $accept_langs = array_combine($lang_parse[1], $lang_parse[4]); // create a list like "en" => 0.8
123 // set default to 1 for any without q factor
124 foreach ($accept_langs as $accept_lang => $val) {
125 if ($val === '') $accept_langs[$accept_lang] = 1;
126 }
127 arsort($accept_langs, SORT_NUMERIC); // sort list based on value
128 }
129 }
130
131 // looks through sorted list and use first one that matches our language list
132 $listlanguages = $this->get_languages_list(true); // hides languages with no post
133 foreach ($accept_langs as $accept_lang => $val) {
134 foreach ($listlanguages as $language) {
135 if (strpos($accept_lang, $language->slug) === 0 && !isset($pref_lang)) {
136 $pref_lang = $language;
137 }
138 }
139 }
140 } // options['browser']
141
142 // return default if there is no preferences in the browser or preferences does not match our languages or it is requested not to use the browser preference
143 return isset($pref_lang) ? $pref_lang : $this->get_language($this->options['default_lang']);
144 }
145
146 // returns the current language
147 function get_current_language() {
148 if ($this->curlang)
149 return $this->curlang;
150
151 // no language set for 404 and attachment
152 if (is_404() || is_attachment() || current_filter() == 'login_init')
153 return $this->get_preferred_language();
154
155 if ($var = get_query_var('lang'))
156 $lang = $this->get_language($var);
157
158 elseif ((is_single() || is_page()) && ( ($var = get_queried_object_id()) || ($var = get_query_var('p')) || ($var = get_query_var('page_id')) ))
159 $lang = $this->get_post_language($var);
160
161 else {
162 foreach ($this->taxonomies as $taxonomy) {
163 if ($var = get_query_var(get_taxonomy($taxonomy)->query_var))
164 $lang = $this->get_term_language($var, $taxonomy);
165 }
166 }
167 return (isset($lang)) ? $lang : false;
168 }
169
170 // save the default locale before we start any language manipulation
171 function init() {
172 $this->default_locale = get_locale();
173 }
174
175 // returns the locale based on current language
176 function get_locale($locale) {
177 return $this->curlang->description;
178 }
179
180 // modifies the language information in rss feed
181 function option_rss_language($value) {
182 return get_bloginfo_rss('language');
183 }
184
185 // saves all text domains in a table for later usage
186 function mofile($bool, $domain, $mofile) {
187 $this->list_textdomains[] = array ('mo' => $mofile, 'domain' => $domain);
188 return true; // prevents WP loading text domains as we will load them all later
189 }
190
191 // NOTE: I believe there are two ways for a plugin to force the WP language
192 // as done by xili_language and here: load text domains and reinitialize wp_locale with the action 'wp'
193 // as done by qtranslate: define the locale with the action 'plugins_loaded', but in this case, the language must be specified in the url.
194 function load_textdomains() {
195
196 // our override_load_textdomain filter has done its job. let's remove it before calling load_textdomain
197 remove_filter('override_load_textdomain', array(&$this, 'mofile'));
198
199 // check there is at least one language defined and sets the current language
200 if ($this->get_languages_list() && $this->curlang = $this->get_current_language()) {
201
202 // set a cookie to remember the language. check headers have not been sent to avoid ugly error
203 if (!headers_sent())
204 setcookie('wordpress_polylang', $this->curlang->slug, time() + 31536000 /* 1 year */, COOKIEPATH, COOKIE_DOMAIN);
205
206 // set all our language filters and actions
207 $this->add_language_filters();
208
209 // now we can load text domains with the right language
210 $new_locale = get_locale();
211 foreach ($this->list_textdomains as $textdomain)
212 load_textdomain( $textdomain['domain'], str_replace($this->default_locale, $new_locale, $textdomain['mo']));
213
214 // and finally load user defined strings
215 global $l10n;
216 $l10n['pll_string'] = $this->mo_import($this->curlang);
217
218 // reinitializes wp_locale for weekdays and months, as well as for text direction
219 global $wp_locale;
220 $wp_locale->init();
221 $wp_locale->text_direction = get_metadata('term', $this->curlang->term_id, '_rtl', true) ? 'rtl' : 'ltr';
222 }
223
224 else {
225 // cant't work so load the text domains with WordPress default language
226 foreach ($this->list_textdomains as $textdomain)
227 load_textdomain($textdomain['domain'], $textdomain['mo']);
228 }
229 }
230
231 // filters posts according to the language
232 function pre_get_posts($query) {
233 // don't make anything if no language has been defined yet
234 if(!$this->get_languages_list())
235 return;
236
237 $qvars = $query->query_vars;
238
239 // users may want to display content in a different langage than the current one by setting it explicitely in the query
240 if ($this->curlang && isset($qvars['lang']) && $qvars['lang'])
241 return;
242
243 // detect our exclude pages query and returns to avoid conflicts
244 // this test should be sufficient
245 if (isset($qvars['tax_query'][0]['taxonomy']) && $qvars['tax_query'][0]['taxonomy'] == 'language' &&
246 isset($qvars['tax_query'][0]['operator']) && $qvars['tax_query'][0]['operator'] == 'NOT IN')
247 return;
248
249 // homepage is requested, let's set the language
250 // second check not to break wp-signup & wp-activate
251 // some PHP setups turn requests for / into /index.php in REQUEST_URI
252 // thanks to Gonçalo Peres for pointing out the issue with queries unknown to WP
253 // http://wordpress.org/support/topic/plugin-polylang-language-homepage-redirection-problem-and-solution-but-incomplete?replies=4#post-2729566
254 if (empty($query->query) && home_url('/') == trailingslashit((is_ssl() ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].str_replace(array('index.php', '?'.$_SERVER['QUERY_STRING']), array('', ''), $_SERVER['REQUEST_URI']))) {
255 // find out the language
256 if ($this->options['hide_default'] && isset($_COOKIE['wordpress_polylang']))
257 $this->curlang = $this->get_language($this->options['default_lang']);
258 else
259 $this->curlang = $this->get_preferred_language(); // sets the language according to browser preference or default language
260
261 // we are already on the right page
262 if ($this->options['default_lang'] == $this->curlang->slug && $this->options['hide_default']) {
263 if ($this->page_on_front && $link_id = $this->get_post($this->page_on_front, $this->curlang))
264 $query->set('page_id', $link_id);
265 else
266 $query->set('lang', $this->curlang->slug);
267 }
268 // redirect to the home page in the right language
269 else {
270 wp_redirect($this->get_home_url($this->curlang));
271 exit;
272 }
273 }
274
275 // redirect the language page to the homepage
276 if ($this->options['redirect_lang'] && is_tax('language') && count($query->query) == 1 && $this->page_on_front) {
277 $this->curlang = $this->get_language(get_query_var('lang'));
278 $query->parse_query('page_id='.$this->get_post($this->page_on_front, $this->curlang));
279 return;
280 }
281
282 // sets is_home on translated home page when it displays posts
283 // is_home must be true on page 2, 3... too
284 if (!$this->page_on_front && is_tax('language') && (count($query->query) == 1 || (is_paged() && count($query->query) == 2))) {
285 $query->is_home = true;
286 $query->is_tax = false;
287 $this->curlang = $this->get_language(get_query_var('lang')); // sets the language now otherwise it will be too late to filter sticky posts !
288 }
289
290 // sets the language for posts page in case the front page displays a static page
291 if ($this->page_for_posts) {
292 // If permalinks are used, WordPress does set and use $query->queried_object_id and sets $query->query_vars['page_id'] to 0
293 // and does set and use $query->query_vars['page_id'] if permalinks are not used :(
294 if (isset($qvars['pagename']) && $qvars['pagename'] && isset($query->queried_object_id))
295 $page_id = $query->queried_object_id;
296 elseif (isset($qvars['page_id']))
297 $page_id = $qvars['page_id'];
298
299 if (isset($page_id) && $page_id && $this->get_post($page_id, $this->get_post_language($this->page_for_posts)) == $this->page_for_posts) {
300 $this->page_for_posts = $page_id;
301 $this->curlang = $this->get_post_language($page_id);
302 $query->set('lang', $this->curlang->slug);
303 $query->is_page = false;
304 $query->is_home = true;
305 $query->is_posts_page = true;
306 $query->is_singular = false;
307 }
308 }
309
310 // FIXME to generalize as I probably forget things
311 $is_archive = (count($query->query) == 1 && isset($qvars['paged']) && $qvars['paged']) ||
312 (isset($qvars['m']) && $qvars['m']) ||
313 (isset($qvars['author']) && $qvars['author']) ||
314 (isset($qvars['post_type']) && is_post_type_archive() && in_array($qvars['post_type'], $this->post_types));
315
316 // sets 404 when the language is not set for archives needing the language in the url
317 if (!$this->options['hide_default'] && !isset($qvars['lang']) && !$GLOBALS['wp_rewrite']->using_permalinks() && $is_archive)
318 $query->set_404();
319
320 // sets the language in case we hide the default language
321 if ($this->options['hide_default'] && !isset($qvars['lang']) && ($is_archive || (count($query->query) == 1 && isset($qvars['feed']) && $qvars['feed']) ))
322 $query->set('lang', $this->options['default_lang']);
323
324 // allow filtering recent posts and secondary queries by the current language
325 // take care not to break queries for non visible post types such as nav_menu_items, attachments...
326 if (/*$query->is_home && */$this->curlang && (!isset($qvars['post_type']) || in_array($qvars['post_type'], $this->post_types) ||
327 (is_array($qvars['post_type']) && array_intersect($qvars['post_type'], $this->post_types)) ))
328 $query->set('lang', $this->curlang->slug);
329
330 // remove pages query when the language is set unless we do a search
331 // FIXME is only search broken by this ?
332 if (isset($qvars['lang']) && $qvars['lang'] && !isset($qvars['post_type']) && !is_search())
333 $query->set('post_type', 'post');
334
335 // unset the is_archive flag for language pages to prevent loading the archive template
336 // keep archive flag for comment feed otherwise the language filter does not work
337 if (isset($qvars['lang']) && $qvars['lang'] && !is_comment_feed() &&
338 !is_post_type_archive() && !is_date() && !is_author() && !is_category() && !is_tag() && !is_tax('post_format'))
339 $query->is_archive = false;
340
341 // unset the is_tax flag for authors pages and post types archives
342 // FIXME Probably I should do this for other cases
343 if (isset($qvars['lang']) && $qvars['lang'] && (is_author() || is_post_type_archive())) {
344 $query->is_tax = false;
345 unset($query->queried_object);
346 }
347
348 // sets a language for theme preview
349 if (isset($_GET['preview']))
350 $query->set('lang', $this->options['default_lang']);
351
352 if (PLL_DISPLAY_ALL) {
353 // add posts with no language set
354 $query->query_vars['tax_query'] = array(
355 'relation' => 'OR', array(
356 'taxonomy'=> 'language',
357 'terms'=> get_terms('language', array('fields'=>'ids')),
358 'operator'=>'NOT IN'));
359 }
360 }
361
362 // filter sticky posts by current language
363 function option_sticky_posts($posts) {
364 if ($this->curlang && !empty($posts)) {
365 foreach ($posts as $key=>$post_id) {
366 if ($this->get_post_language($post_id)->term_id != $this->curlang->term_id)
367 unset($posts[$key]);
368 }
369 }
370 return $posts;
371 }
372
373 // filters categories and post tags by language when needed
374 function terms_clauses($clauses, $taxonomies, $args) {
375 // does nothing except on taxonomies which are filterable
376 foreach ($taxonomies as $tax) {
377 if (!in_array($tax, $this->taxonomies))
378 return $clauses;
379 }
380
381 // adds our clauses to filter by current language
382 return $this->_terms_clauses($clauses, $this->curlang, PLL_DISPLAY_ALL);
383 }
384
385 // meta in the html head section
386 function wp_head() {
387 // outputs references to translated pages (if exists) in the html head section
388 foreach ($this->get_languages_list() as $language) {
389 if ($language->slug != $this->curlang->slug && $url = $this->get_translation_url($language))
390 printf("<link hreflang='%s' href='%s' rel='alternate' />\n", esc_attr($language->slug), esc_url($url));
391 }
392 }
393
394 // modifies the page link in case the front page is not in the default language
395 function page_link($link, $id) {
396 if ($this->options['redirect_lang'] && $this->page_on_front && $id == $this->get_post($this->page_on_front, $lang = $this->get_post_language($id)))
397 return $this->options['hide_default'] && $lang->slug == $this->options['default_lang'] ? trailingslashit($this->home) : get_term_link($lang, 'language');
398
399 if ($this->page_on_front && $this->options['hide_default'] && $id == $this->get_post($this->page_on_front, $this->options['default_lang']))
400 return trailingslashit($this->home);
401
402 return _get_page_link($id);
403 }
404
405 // prevents redirection of the homepage when using page on front
406 function redirect_canonical($redirect_url, $requested_url) {
407 return $requested_url == home_url('/') || $requested_url == $this->page_link('', get_option('page_on_front')) ? false : $redirect_url;
408 }
409
410 // adds some javascript workaround knowing it's not perfect...
411 function wp_print_footer_scripts() {
412 // modifies the search form since filtering get_search_form won't work if the template uses searchform.php or the search form is hardcoded
413 // don't use directly e[0] just in case there is somewhere else an element named 's'
414 // check before if the hidden input has not already been introduced by get_search_form
415 // thanks to AndyDeGroo for improving the code for compatility with old browsers
416 // http://wordpress.org/support/topic/development-of-polylang-version-08?replies=6#post-2645559
417 if (!$this->search_form_filter) {
418 $lang = esc_js($this->curlang->slug);
419 $js = "e = document.getElementsByName('s');
420 for (i = 0; i < e.length; i++) {
421 if (e[i].tagName.toUpperCase() == 'INPUT') {
422 var ih = document.createElement('input');
423 ih.type = 'hidden';
424 ih.name = 'lang';
425 ih.value = '$lang';
426 e[i].parentNode.appendChild(ih);
427 }
428 }";
429 echo "<script type='text/javascript'>" .$js. "</script>";
430 }
431 }
432
433 // adds the language information in the search form
434 // does not work if searchform.php is used or if the search form is hardcoded in another template file
435 function get_search_form($form) {
436 if ($form) {
437 $this->search_form_filter = true;
438 $form = str_replace('</form>', '<input type="hidden" name="lang" value="'.esc_attr($this->curlang->slug).'" /></form>', $form);
439 }
440 return $form;
441 }
442
443 // excludes pages which are not in the current language for wp_list_pages
444 // useful for the pages widget
445 function wp_list_pages_excludes($pages) {
446 return array_merge($pages, $this->exclude_pages($this->curlang->term_id));
447 }
448
449 // filters the comments according to the current language mainly for the recent comments widget
450 function comments_clauses($clauses, $comment_query) {
451 // first test if wp_posts.ID already available in the query
452 if (strpos($clauses['join'], '.ID')) {
453 global $wpdb;
454 $clauses['join'] .= $wpdb->prepare(" INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = ID");
455 $clauses['where'] .= $wpdb->prepare(" AND pll_tr.term_taxonomy_id = %d", $this->curlang->term_taxonomy_id);
456 }
457 return $clauses;
458 }
459
460 // modifies the sql request for wp_get_archives an get_adjacent_post to filter by the current language
461 function posts_join($sql) {
462 global $wpdb;
463 return $sql . $wpdb->prepare(" INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = ID");
464 }
465
466 // modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language
467 function posts_where($sql) {
468 global $wpdb;
469 return $sql . $wpdb->prepare(" AND pll_tr.term_taxonomy_id = %d", $this->curlang->term_taxonomy_id);
470 }
471
472 // modifies the author and date links to add the language parameter (as well as feed link)
473 function archive_link($link) {
474 return $this->add_language_to_link($link, $this->curlang);
475 }
476
477 // returns the url of the translation (if exists) of the current page
478 function get_translation_url($language) {
479 global $wp_query, $wp_rewrite;
480 $qvars = $wp_query->query;
481 $hide = $this->options['default_lang'] == $language->slug && $this->options['hide_default'];
482
483 // is_single is set to 1 for attachment but no language is set
484 if (is_single() && !is_attachment() && $id = $this->get_post($wp_query->queried_object_id, $language))
485 $url = get_permalink($id);
486
487 // page for posts
488 elseif (get_option('show_on_front') == 'page' && isset($wp_query->queried_object_id) && $wp_query->queried_object_id &&
489 $wp_query->queried_object_id == $this->page_for_posts && ($id = $this->get_post($this->page_for_posts, $language)))
490 $url = get_permalink($id);
491
492 elseif (is_page() && $id = $this->get_post($wp_query->queried_object_id, $language))
493 $url = $hide && $id == $this->get_post($this->page_on_front, $language) ? $this->home : get_page_link($id);
494
495 elseif (!is_tax('post_format') && !is_tax('language') && (is_category() || is_tag() || is_tax()) ) {
496 $term = get_queried_object();
497 $lang = $this->get_term_language($term->term_id);
498 $taxonomy = $term->taxonomy;
499
500 if ($language->slug == $lang->slug)
501 $url = get_term_link($term, $taxonomy); // self link
502 elseif ($link_id = $this->get_translation('term', $term->term_id, $language))
503 $url = get_term_link(get_term($link_id, $taxonomy), $taxonomy);
504 }
505
506 // don't test if there are existing translations before creating the url as it would be very expensive in sql queries
507 elseif (is_archive()) {
508 if ($wp_rewrite->using_permalinks()) {
509 $filters = array('author_link', 'post_type_archive_link', 'year_link', 'month_link', 'day_link');
510
511 // prevents filtering links by current language
512 remove_filter('term_link', array(&$this, 'term_link')); // for post format
513 foreach ($filters as $filter)
514 remove_filter($filter, array(&$this, 'archive_link'));
515
516 if (is_author())
517 $url = $this->add_language_to_link(get_author_posts_url(0, $qvars['author_name']), $language);
518
519 if (is_year())
520 $url = $this->add_language_to_link(get_year_link($qvars['year']), $language);
521
522 if (is_month())
523 $url = $this->add_language_to_link(get_month_link($qvars['year'], $qvars['monthnum']), $language);
524
525 if (is_day())
526 $url = $this->add_language_to_link(get_day_link($qvars['year'], $qvars['monthnum'], $qvars['day']), $language);
527
528 if (is_post_type_archive())
529 $url = $this->add_language_to_link(get_post_type_archive_link($qvars['post_type']), $language);
530
531 if (is_tax('post_format'))
532 $url = $this->add_language_to_link(get_post_format_link($qvars['post_format']), $language);
533
534 // put our language filters again
535 add_filter('term_link', array(&$this, 'term_link'), 10, 3);
536 foreach ($filters as $filter)
537 add_filter($filter, array(&$this, 'archive_link'));
538 }
539 else
540 $url = $hide ? remove_query_arg('lang') : add_query_arg('lang', $language->slug);
541 }
542
543 elseif (is_home() || is_tax('language') )
544 $url = $this->get_home_url($language);
545
546 return isset($url) ? $url : null;
547 }
548
549 // filters the nav menus according to the current language when called from get_nav_menu_locations()
550 // mainly for Artisteer generated themes
551 function nav_menu_locations($menus) {
552 if (is_array($menus)) {
553 foreach($menus as $location => $menu) {
554 $menu_lang = get_option('polylang_nav_menus');
555 if (isset($menu_lang[$location][$this->curlang->slug]))
556 $menus[$location] = $menu_lang[$location][$this->curlang->slug];
557 }
558 }
559 return $menus;
560 }
561
562 // filters the nav menus according to the current language when called from wp_nav_menus
563 function wp_nav_menu_args($args) {
564 if (!$args['menu'] && $args['theme_location']) {
565 $menu_lang = get_option('polylang_nav_menus');
566 if (isset($menu_lang[$args['theme_location']][$this->curlang->slug]))
567 $args['menu'] = $menu_lang[$args['theme_location']][$this->curlang->slug];
568 }
569 return $args;
570 }
571
572 // adds the language switcher at the end of the menu
573 function wp_nav_menu_items($items, $args) {
574 $menu_lang = get_option('polylang_nav_menus');
575 return isset($args->theme_location) && isset($menu_lang[$args->theme_location]['switcher']) && $menu_lang[$args->theme_location]['switcher'] ?
576 $items . $this->the_languages(array_merge($menu_lang[$args->theme_location], array('menu' => 1, 'echo' => 0))) : $items;
577 }
578
579 // filters the widgets according to the current language
580 function widget_display_callback($instance, $widget, $args) {
581 $widget_lang = get_option('polylang_widgets');
582 // don't display if a language filter is set and this is not the current one
583 return isset($widget_lang[$widget->id]) && $widget_lang[$widget->id] && $widget_lang[$widget->id] != $this->curlang->slug ? false : $instance;
584 }
585
586 // translates widget titles
587 function widget_title($title) {
588 return __($title, 'pll_string');
589 }
590
591 // translates site title and tagline
592 function bloginfo($output, $show) {
593 return in_array($show, array('', 'name', 'description')) ? __($output, 'pll_string') : $output;
594 }
595
596 // translates page for posts and page on front
597 function translate_page($value) {
598 return isset($this->curlang) && $value ? $this->get_post($value, $this->curlang) : $value;
599 }
600
601 // filters the home url to get the right language
602 function home_url($url) {
603 if (!did_action('template_redirect') || rtrim($url,'/') != $this->home)
604 return $url;
605
606 $theme = get_theme_root();
607 foreach (debug_backtrace() as $trace) {
608 $ok = $trace['function'] == 'wp_nav_menu' ||
609 // direct call from the theme
610 (isset($trace['file']) && !strpos($trace['file'], 'searchform.php') && strpos($trace['file'], $theme) !== false &&
611 in_array($trace['function'], array('home_url', 'bloginfo', 'get_bloginfo')) );
612
613 if ($ok)
614 return $this->get_home_url($this->curlang);
615 }
616
617 return $url;
618 }
619
620 // returns the home url in the right language
621 function get_home_url($language = '') {
622 if ($language == '')
623 $language = $this->curlang;
624
625 if ($this->options['default_lang'] == $language->slug && $this->options['hide_default'])
626 return trailingslashit($this->home);
627
628 // a static page is used as front page : /!\ don't use get_page_link to avoid infinite loop
629 if ($this->page_on_front && $id = $this->get_post($this->page_on_front, $language))
630 return $this->page_link('', $id);
631
632 return get_term_link($language, 'language');
633 }
634
635 // displays (or returns) the language switcher
636 function the_languages($args = '') {
637 $defaults = array(
638 'dropdown' => 0, // display as list and not as dropdown
639 'echo' => 1, // echoes the list
640 'hide_if_empty' => 1, // hides languages with no posts (or pages)
641 'menu' => '0', // not for nav menu
642 'show_flags' => 0, // don't show flags
643 'show_names' => 1, // show language names
644 'display_names_as' => 'name', // valid options are slug and name
645 'force_home' => 0, // tries to find a translation (available only if display != dropdown)
646 'hide_if_no_translation' => 0, // don't hide the link if there is no translation
647 'hide_current' => 0, // don't hide current language
648 );
649 extract(wp_parse_args($args, $defaults));
650
651 if ($dropdown)
652 $output = $this->dropdown_languages(array('hide_empty' => $hide_if_empty, 'selected' => $this->curlang->slug));
653
654 else {
655 $output = '';
656
657 foreach ($this->get_languages_list($hide_if_empty) as $language) {
658 // hide current language
659 if ($this->curlang->term_id == $language->term_id && $hide_current)
660 continue;
661
662 $url = $force_home ? null : $this->get_translation_url($language);
663 $url = apply_filters('pll_the_language_link', $url, $language->slug, $language->description);
664
665 // hide if no translation exists
666 if (!isset($url) && $hide_if_no_translation)
667 continue;
668
669 $url = isset($url) ? $url : $this->get_home_url($language); // if the page is not translated, link to the home page
670
671 $class = 'lang-item lang-item-'.esc_attr($language->term_id);
672 $class .= $language->term_id == $this->curlang->term_id ? ' current-lang' : '';
673 $class .= $menu ? ' menu-item' : '';
674
675 $flag = $show_flags ? $this->get_flag($language) : '';
676 $name = $show_names || !$show_flags ? esc_html($display_names_as == 'slug' ? $language->slug : $language->name) : '';
677
678 $output .= sprintf("<li class='%s'><a hreflang='%s' href='%s'>%s</a></li>\n",
679 $class, esc_attr($language->slug), esc_url($url), $show_flags && $show_names ? $flag.'&nbsp;'.$name : $flag.$name);
680 }
681 }
682
683 $output = apply_filters('pll_the_languages', $output, $args);
684
685 if(!$echo)
686 return $output;
687 echo $output;
688 }
689
690 // just returns the current language for API
691 function current_language($args) {
692 return !isset($this->curlang) ? false :
693 $args == 'name' ? $this->curlang->name :
694 $args == 'locale' ? $this->curlang->description :
695 $this->curlang->slug;
696 }
697 }
698 ?>
699