PluginProbe
Polylang / 0.9.3
Polylang v0.9.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.9.3, at include/core.php

893 lines 37.6 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 $labels; // post types and taxonomies labels to translate
9 private $first_query = true;
10
11 // options often needed
12 private $page_for_posts;
13 private $page_on_front;
14
15 // used to cache results
16 private $posts = array();
17 private $translation_url = array();
18 private $home_urls = array();
19
20 function __construct() {
21 parent::__construct();
22
23 // init options often needed
24 $this->page_for_posts = get_option('page_for_posts');
25 $this->page_on_front = get_option('page_on_front');
26
27 // if no language found, choose the preferred one
28 add_filter('pll_get_current_language', array(&$this, 'pll_get_current_language'));
29
30 // sets the language of comment
31 add_action('pre_comment_on_post', array(&$this, 'pre_comment_on_post'));
32
33 // text domain management
34 if ($this->options['force_lang'] && get_option('permalink_structure') && PLL_LANG_EARLY)
35 add_action('setup_theme', array(&$this, 'setup_theme'), 20); // after Polylang::setup_theme
36 else {
37 add_filter('override_load_textdomain', array(&$this, 'mofile'), 10, 3);
38 add_filter('gettext', array(&$this, 'gettext'), 10, 3);
39 add_filter('gettext_with_context', array(&$this, 'gettext_with_context'), 10, 4);
40 }
41
42 add_action('init', array(&$this, 'init'));
43 foreach (array('wp', 'login_init', 'admin_init') as $filter) // admin_init for ajax thanks to g100g
44 add_action($filter, array(&$this, 'load_textdomains'), 5); // priority 5 for post types and taxonomies with registered with in wp hook with default priority
45
46 // filters the WordPress locale
47 add_filter('locale', array(&$this, 'get_locale'));
48
49 // filters posts according to the language
50 add_filter('pre_get_posts', array(&$this, 'pre_get_posts'));
51
52 // filter sticky posts by current language
53 add_filter('option_sticky_posts', array(&$this, 'option_sticky_posts'));
54
55 // translates page for posts and page on front
56 add_filter('option_page_for_posts', array(&$this, 'translate_page'));
57 add_filter('option_page_on_front', array(&$this, 'translate_page'));
58 }
59
60 // set these filters and actions only once the current language has been defined
61 function add_language_filters() {
62 // modifies the language information in rss feed
63 // useful if WP < 3.4
64 add_filter('option_rss_language', array(&$this, 'option_rss_language'));
65
66 // filters categories and post tags by language
67 add_filter('terms_clauses', array(&$this, 'terms_clauses'), 10, 3);
68
69 // meta in the html head section
70 add_action('wp_head', array(&$this, 'wp_head'));
71
72 // modifies the page link in case the front page is not in the default language
73 add_filter('page_link', array(&$this, 'page_link'), 10, 2);
74
75 // prevents redirection of the homepage
76 add_filter('redirect_canonical', array(&$this, 'redirect_canonical'), 10, 2);
77
78 // adds javascript at the end of the document
79 if (!$GLOBALS['wp_rewrite']->using_permalinks() && PLL_SEARCH_FORM_JS)
80 add_action('wp_footer', array(&$this, 'wp_print_footer_scripts'));
81
82 // adds the language information in the search form
83 // 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
84 add_filter('get_search_form', array(&$this, 'get_search_form'), 99);
85
86 // adds the language information in admin bar search form
87 remove_action('admin_bar_menu', 'wp_admin_bar_search_menu', 4);
88 add_action('admin_bar_menu', array(&$this, 'admin_bar_search_menu'), 4);
89
90 // filters the pages according to the current language in wp_list_pages
91 add_filter('wp_list_pages_excludes', array(&$this, 'wp_list_pages_excludes'));
92
93 // filters the comments according to the current language
94 add_filter('comments_clauses', array(&$this, 'comments_clauses'), 10, 2);
95
96 // rewrites archives, next and previous post links to filter them by language
97 foreach (array('getarchives', 'get_previous_post', 'get_next_post') as $filter)
98 foreach (array('_join', '_where') as $clause)
99 add_filter($filter.$clause, array(&$this, 'posts'.$clause));
100
101 // rewrites author and date links to filter them by language
102 foreach (array('feed_link', 'author_link', 'post_type_archive_link', 'year_link', 'month_link', 'day_link') as $filter)
103 add_filter($filter, array(&$this, 'archive_link'));
104
105 $this->add_post_term_link_filters(); // these filters are in base as they may be used on admin side too
106
107 // filters the nav menus according to the current language
108 add_filter('theme_mod_nav_menu_locations', array(&$this, 'nav_menu_locations'));
109 add_filter('wp_nav_menu_args', array(&$this, 'wp_nav_menu_args'));
110 add_filter('wp_nav_menu_items', array(&$this, 'wp_nav_menu_items'), 10, 2);
111
112 // filters the widgets according to the current language
113 add_filter('widget_display_callback', array(&$this, 'widget_display_callback'), 10, 3);
114
115 // strings translation (must be applied before WordPress applies its default formatting filters)
116 add_filter('widget_title', 'pll__', 1);
117 add_filter('option_blogname', 'pll__', 1);
118 add_filter('option_blogdescription', 'pll__', 1);
119
120 // translates biography
121 add_filter('get_user_metadata', array(&$this,'get_user_metadata'), 10, 4);
122
123 // modifies the home url
124 if (PLL_FILTER_HOME_URL)
125 add_filter('home_url', array(&$this, 'home_url'));
126 }
127
128 // returns the language according to browser preference or the default language
129 function get_preferred_language() {
130 // check first is the user was already browsing this site
131 if (isset($_COOKIE['wordpress_polylang']))
132 return $this->get_language($_COOKIE['wordpress_polylang']);
133
134 // sets the browsing language according to the browser preferences
135 // code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
136 if ($this->options['browser']) {
137 $accept_langs = array();
138
139 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
140 // break up string into pieces (languages and q factors)
141 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);
142
143 $k = $lang_parse[1];
144 $v = $lang_parse[4];
145
146 if ($n = count($k)) {
147 // set default to 1 for any without q factor
148 foreach ($v as $key => $val)
149 if ($val === '') $v[$key] = 1;
150
151 // bubble sort (need a stable sort for Android, so can't use a PHP sort function)
152 if ($n > 1) {
153 for ($i = 2; $i <= $n; $i++)
154 for ($j = 0; $j <= $n-2; $j++)
155 if ( $v[$j] < $v[$j + 1]) {
156 // swap values
157 $temp = $v[$j];
158 $v[$j] = $v[$j + 1];
159 $v[$j + 1] = $temp;
160 //swap keys
161 $temp = $k[$j];
162 $k[$j] = $k[$j + 1];
163 $k[$j + 1] = $temp;
164 }
165 }
166 // NOTE: array_combine => PHP5
167 $accept_langs = array_combine($k,$v);
168 }
169 }
170
171 // looks through sorted list and use first one that matches our language list
172 $listlanguages = $this->get_languages_list(array('hide_empty' => true)); // hides languages with no post
173 foreach ($accept_langs as $accept_lang => $val) {
174 foreach ($listlanguages as $language) {
175 if (strpos($accept_lang, $language->slug) === 0 && !isset($pref_lang)) {
176 $pref_lang = $language;
177 }
178 }
179 }
180 } // options['browser']
181
182 // 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
183 return isset($pref_lang) ? $pref_lang : $this->get_language($this->options['default_lang']);
184 }
185
186 // returns the current language
187 function get_current_language() {
188 if ($this->curlang)
189 return $this->curlang;
190
191 // no language set for 404
192 if (is_404() || current_filter() == 'login_init')
193 return $this->get_preferred_language();
194
195 if ($var = get_query_var('lang'))
196 $lang = $this->get_language(reset(explode(',',$var))); // choose the first queried language
197
198 // Ajax thanks to g100g
199 elseif (isset($_REQUEST['pll_load_front']))
200 $lang = isset($_REQUEST['lang']) && $_REQUEST['lang'] ? $this->get_language($_REQUEST['lang']) : $this->get_preferred_language();
201
202 elseif ((is_single() || is_page() || (is_attachment() && PLL_MEDIA_SUPPORT)) && ( ($var = get_queried_object_id()) || ($var = get_query_var('p')) || ($var = get_query_var('page_id')) || ($var = get_query_var('attachment_id')) ))
203 $lang = $this->get_post_language($var);
204
205 else {
206 foreach ($this->taxonomies as $taxonomy) {
207 if ($var = get_query_var(get_taxonomy($taxonomy)->query_var))
208 $lang = $this->get_term_language($var, $taxonomy);
209 }
210 }
211 // allows plugins to set the language
212 return apply_filters('pll_get_current_language', isset($lang) ? $lang : false);
213 }
214
215 // if no language found, return the preferred one
216 function pll_get_current_language($lang) {
217 return !$lang ? $this->get_preferred_language() : $lang;
218 }
219
220 // sets the language of comment
221 // useful to redirect to correct post comment url when adding the language to all url
222 function pre_comment_on_post($post_id) {
223 $this->curlang = $this->get_post_language($post_id);
224 $this->add_post_term_link_filters();
225 }
226
227 // sets the language when it is always included in the url
228 function setup_theme() {
229 $root = $this->options['rewrite']? '' : 'language/';
230
231 foreach ($this->get_languages_list() as $language)
232 $languages[] = $language->slug;
233
234 $languages = $GLOBALS['wp_rewrite']->using_permalinks() ?
235 '#\/'.$root.'('.implode('|', $languages).')\/#' :
236 '#lang=('.implode('|', $languages).')#';
237
238 preg_match($languages, trailingslashit($_SERVER['REQUEST_URI']), $matches);
239
240 // home is resquested
241 // some PHP setups turn requests for / into /index.php in REQUEST_URI
242 // thanks to Gonçalo Peres for pointing out the issue with queries unknown to WP
243 // http://wordpress.org/support/topic/plugin-polylang-language-homepage-redirection-problem-and-solution-but-incomplete?replies=4#post-2729566
244 if (str_replace('www.', '', home_url('/')) == trailingslashit((is_ssl() ? 'https://' : 'http://').str_replace('www.', '', $_SERVER['HTTP_HOST']).str_replace(array('index.php', '?'.$_SERVER['QUERY_STRING']), array('', ''), $_SERVER['REQUEST_URI'])))
245 $this->home_requested();
246
247 // $matches[1] is the slug of the requested language
248 elseif ($matches)
249 $this->curlang = $this->get_language($matches[1]);
250 elseif (false === strpos($_SERVER['SCRIPT_NAME'], 'index.php')) // wp-login, wp-signup, wp-activate
251 $this->curlang = $this->get_preferred_language();
252 else
253 $this->curlang = $this->get_language($this->options['default_lang']);
254
255 $GLOBALS['l10n']['pll_string'] = $this->mo_import($this->curlang);
256 }
257
258 // save the default locale before we start any language manipulation
259 function init() {
260 $this->default_locale = get_locale();
261 }
262
263 // returns the locale based on current language
264 function get_locale($locale) {
265 return $this->curlang ? $this->curlang->description : $locale;
266 }
267
268 // modifies the language information in rss feed
269 function option_rss_language($value) {
270 return get_bloginfo_rss('language');
271 }
272
273 // saves all text domains in a table for later usage
274 function mofile($bool, $domain, $mofile) {
275 $this->list_textdomains[] = array ('mo' => $mofile, 'domain' => $domain);
276 return true; // prevents WP loading text domains as we will load them all later
277 }
278
279 // saves post types and taxonomies labels for a later usage
280 function gettext($translation, $text, $domain) {
281 $this->labels[$text] = array('domain' => $domain);
282 return $translation;
283 }
284
285 // saves post types and taxonomies labels for a later usage
286 function gettext_with_context($translation, $text, $context, $domain) {
287 $this->labels[$text] = array('domain' => $domain, 'context' => $context);
288 return $translation;
289 }
290
291 // translates post types and taxonomies labels once the language is known
292 function translate_labels($type) {
293 foreach($type->labels as $key=>$label)
294 if (isset($this->labels[$label]))
295 $type->labels->$key = isset($this->labels[$label]['context']) ?
296 _x($label, $this->labels[$label]['context'], $this->labels[$label]['domain']) :
297 __($label, $this->labels[$label]['domain']);
298 }
299
300 // NOTE: I believe there are two ways for a plugin to force the WP language
301 // as done by xili_language: load text domains and reinitialize wp_locale with the action 'wp'
302 // as done by qtranslate: define the locale with the action 'plugins_loaded', but in this case, the language must be specified in the url.
303 function load_textdomains() {
304 global $wp_rewrite, $l10n;
305
306 // our override_load_textdomain filter has done its job. let's remove it before calling load_textdomain
307 remove_filter('override_load_textdomain', array(&$this, 'mofile'));
308 remove_filter('gettext', array(&$this, 'gettext'), 10, 3);
309 remove_filter('gettext_with_context', array(&$this, 'gettext_with_context'), 10, 4);
310
311 // check there is at least one language defined and sets the current language
312 if ($this->get_languages_list() && $this->curlang = $this->get_current_language()) {
313
314 // set a cookie to remember the language. check headers have not been sent to avoid ugly error
315 if (!headers_sent() && (!isset($_COOKIE['wordpress_polylang']) || $_COOKIE['wordpress_polylang'] != $this->curlang->slug))
316 setcookie('wordpress_polylang', $this->curlang->slug, time() + 31536000 /* 1 year */, COOKIEPATH, COOKIE_DOMAIN);
317
318 // set all our language filters and actions
319 $this->add_language_filters();
320
321 if (!($this->options['force_lang'] && $wp_rewrite->using_permalinks() && PLL_LANG_EARLY) && $this->curlang->description != 'en_US') {
322 // now we can load text domains with the right language
323 $new_locale = get_locale();
324 foreach ($this->list_textdomains as $textdomain)
325 load_textdomain( $textdomain['domain'], str_replace($this->default_locale, $new_locale, $textdomain['mo']));
326
327 // reinitializes wp_locale for weekdays and months, as well as for text direction
328 unset($GLOBALS['wp_locale']);
329 $GLOBALS['wp_locale'] = new WP_Locale();
330 $GLOBALS['wp_locale']->text_direction = get_metadata('term', $this->curlang->term_id, '_rtl', true) ? 'rtl' : 'ltr';
331
332 // translate labels of post types and taxonomies
333 foreach ($GLOBALS['wp_taxonomies'] as $tax)
334 $this->translate_labels($tax);
335 foreach ($GLOBALS['wp_post_types'] as $pt)
336 $this->translate_labels($pt);
337
338 // and finally load user defined strings
339 $l10n['pll_string'] = $this->mo_import($this->curlang);
340 }
341
342 }
343
344 else {
345 // can't work so load the text domains with WordPress default language
346 foreach ($this->list_textdomains as $textdomain)
347 load_textdomain($textdomain['domain'], $textdomain['mo']);
348 }
349 }
350
351 // special actions when home page is requested
352 // optionally redirects to the home page in the preferred language
353 function home_requested($query = false) {
354 // need this filter to get the right url when adding language code to all urls
355 if ($this->options['force_lang'] && $GLOBALS['wp_rewrite']->using_permalinks())
356 add_filter('_get_page_link', array(&$this, 'post_link'), 10, 2);
357
358 if ($this->options['hide_default'] && isset($_COOKIE['wordpress_polylang']))
359 $this->curlang = $this->get_language($this->options['default_lang']);
360 else
361 $this->curlang = $this->get_preferred_language(); // sets the language according to browser preference or default language
362
363 // we are already on the right page
364 if ($this->options['default_lang'] == $this->curlang->slug && $this->options['hide_default']) {
365 if ($this->page_on_front && $link_id = $this->get_post($this->page_on_front, $this->curlang))
366 $query ? $query->set('page_id', $link_id) : set_query_var('page_id', $link_id);
367 else
368 $query ? $query->set('lang', $this->curlang->slug) : set_query_var('lang', $this->curlang->slug);
369 }
370 // redirect to the home page in the right language
371 // test to avoid crash if get_home_url returns something wrong
372 // FIXME why this happens? http://wordpress.org/support/topic/polylang-crashes-1
373 elseif (is_string($redirect = $this->get_home_url($this->curlang))) {
374 wp_redirect($redirect);
375 exit;
376 }
377 }
378
379 // filters posts according to the language
380 function pre_get_posts($query) {
381 // don't make anything if no language has been defined yet
382 // $this->post_types & $this->taxonomies are defined only once the action 'wp_loaded' has been fired
383 // honor suppress_filters
384 if (!$this->get_languages_list() || !did_action('wp_loaded') || $query->get('suppress_filters'))
385 return $query;
386
387 $qvars = $query->query_vars;
388
389 // users may want to display content in a different language than the current one by setting it explicitely in the query
390 if (!$this->first_query && $this->curlang && isset($qvars['lang']) && $qvars['lang'])
391 return $query;
392
393 $this->first_query = false;
394
395 // detect our exclude pages query and returns to avoid conflicts
396 // this test should be sufficient
397 if (isset($qvars['tax_query'][0]['taxonomy']) && $qvars['tax_query'][0]['taxonomy'] == 'language' &&
398 isset($qvars['tax_query'][0]['operator']) && $qvars['tax_query'][0]['operator'] == 'NOT IN')
399 return $query;
400
401 // special case for wp-signup.php & wp-activate.php
402 if (is_home() && false === strpos($_SERVER['SCRIPT_NAME'], 'index.php')) {
403 $this->curlang = $this->get_preferred_language();
404 return $query;
405 }
406
407 // homepage is requested, let's set the language
408 // take care to avoid posts page for which is_home = 1
409 if (!$this->curlang && empty($query->query) && (is_home() || (is_page() && $qvars['page_id'] == $this->page_on_front)))
410 $this->home_requested($query);
411
412 // redirect the language page to the homepage
413 if ($this->options['redirect_lang'] && is_tax('language') && $this->page_on_front && (count($query->query) == 1 || (is_paged() && count($query->query) == 2))) {
414 $qvars['page_id'] = $this->get_post($this->page_on_front, $this->get_language(get_query_var('lang')));
415 $query->parse_query($qvars);
416 return $query;
417 }
418
419 // sets is_home on translated home page when it displays posts
420 // is_home must be true on page 2, 3... too
421 if (!$this->page_on_front && is_tax('language') && (count($query->query) == 1 || (is_paged() && count($query->query) == 2))) {
422 $query->is_home = true;
423 $query->is_tax = false;
424 $this->curlang = $this->get_language(get_query_var('lang')); // sets the language now otherwise it will be too late to filter sticky posts !
425 }
426
427 // sets the language for posts page in case the front page displays a static page
428 if ($this->page_for_posts) {
429 // If permalinks are used, WordPress does set and use $query->queried_object_id and sets $query->query_vars['page_id'] to 0
430 // and does set and use $query->query_vars['page_id'] if permalinks are not used :(
431 if (isset($qvars['pagename']) && $qvars['pagename'] && isset($query->queried_object_id))
432 $page_id = $query->queried_object_id;
433 elseif (isset($qvars['page_id']))
434 $page_id = $qvars['page_id'];
435
436 if (isset($page_id) && $page_id && $this->get_post($page_id, $this->get_post_language($this->page_for_posts)) == $this->page_for_posts) {
437 $this->page_for_posts = $page_id;
438 $this->curlang = $this->get_post_language($page_id);
439 $query->set('lang', $this->curlang->slug);
440 $query->is_page = false;
441 $query->is_singular = false;
442 $query->is_home = true;
443 $query->is_posts_page = true;
444 }
445 }
446
447 $is_post_type = isset($qvars['post_type']) && (in_array($qvars['post_type'], $this->post_types) ||
448 (is_array($qvars['post_type']) && array_intersect($qvars['post_type'], $this->post_types)) );
449
450 // FIXME to generalize as I probably forget things
451 $is_archive = (count($query->query) == 1 && isset($qvars['paged']) && $qvars['paged']) ||
452 (isset($qvars['m']) && $qvars['m']) ||
453 (isset($qvars['author']) && $qvars['author']) ||
454 (isset($qvars['post_type']) && is_post_type_archive() && $is_post_type);
455
456 // sets 404 when the language is not set for archives needing the language in the url
457 if (!$this->options['hide_default'] && !isset($qvars['lang']) && !$GLOBALS['wp_rewrite']->using_permalinks() && $is_archive)
458 $query->set_404();
459
460 // sets the language in case we hide the default language
461 if ($this->options['hide_default'] && !isset($qvars['lang']) && ($is_archive || (count($query->query) == 1 && isset($qvars['feed']) && $qvars['feed']) ))
462 $query->set('lang', $this->options['default_lang']);
463
464 // allow filtering recent posts and secondary queries by the current language
465 // take care not to break queries for non visible post types such as nav_menu_items, attachments...
466 if (/*$query->is_home && */$this->curlang && (!isset($qvars['post_type']) || $is_post_type ))
467 $query->set('lang', $this->curlang->slug);
468
469 // remove pages query when the language is set unless we do a search
470 // FIXME is only search broken by this ?
471 if (isset($qvars['lang']) && $qvars['lang'] && !isset($qvars['post_type']) && !is_search())
472 $query->set('post_type', 'post');
473
474 // unset the is_archive flag for language pages to prevent loading the archive template
475 // keep archive flag for comment feed otherwise the language filter does not work
476 if (isset($qvars['lang']) && $qvars['lang'] && !is_comment_feed() &&
477 !is_post_type_archive() && !is_date() && !is_author() && !is_category() && !is_tag() && !is_tax('post_format'))
478 $query->is_archive = false;
479
480 // unset the is_tax flag for authors pages and post types archives
481 // FIXME Probably I should do this for other cases
482 if (isset($qvars['lang']) && $qvars['lang'] && (is_author() || is_post_type_archive() || is_date())) {
483 $query->is_tax = false;
484 unset($query->queried_object);
485 }
486
487 // sets a language for theme preview
488 if ($qvars['preview'])
489 $query->set('lang', $this->curlang->slug);
490
491 // to avoid conflict beetwen taxonomies
492 if (isset($query->tax_query->queries))
493 foreach ($query->tax_query->queries as $tax)
494 if (in_array($tax['taxonomy'], $this->taxonomies))
495 unset ($query->query_vars['lang']);
496
497 return $query;
498 }
499
500 // filter sticky posts by current language
501 function option_sticky_posts($posts) {
502 if ($this->curlang && !empty($posts)) {
503 foreach ($posts as $key=>$post_id) {
504 if ($this->get_post_language($post_id)->term_id != $this->curlang->term_id)
505 unset($posts[$key]);
506 }
507 }
508 return $posts;
509 }
510
511 // filters categories and post tags by language when needed
512 function terms_clauses($clauses, $taxonomies, $args) {
513 // does nothing except on taxonomies which are filterable
514 foreach ($taxonomies as $tax) {
515 if (!in_array($tax, $this->taxonomies))
516 return $clauses;
517 }
518
519 // adds our clauses to filter by current language
520 return $this->_terms_clauses($clauses, $this->curlang);
521 }
522
523 // meta in the html head section
524 function wp_head() {
525 // outputs references to translated pages (if exists) in the html head section
526 foreach ($this->get_languages_list() as $language) {
527 if ($language->slug != $this->curlang->slug && $url = $this->get_translation_url($language))
528 printf("<link hreflang='%s' href='%s' rel='alternate' />\n", esc_attr($language->slug), esc_url($url));
529 }
530 }
531
532 // modifies the page link in case the front page is not in the default language
533 function page_link($link, $id) {
534 if ($this->options['redirect_lang'] && $this->page_on_front) {
535 $lang = $lang = $this->get_post_language($id);
536 if (!isset($this->posts[$lang->slug][$this->page_on_front]))
537 $this->posts[$lang->slug][$this->page_on_front] = $this->get_post($this->page_on_front, $lang);
538 if ($id == $this->posts[$lang->slug][$this->page_on_front])
539 return $this->options['hide_default'] && $lang->slug == $this->options['default_lang'] ? trailingslashit($this->home) : get_term_link($lang, 'language');
540 }
541
542 if ($this->page_on_front && $this->options['hide_default']) {
543 if (!isset($this->posts[$this->options['default_lang']][$this->page_on_front]))
544 $this->posts[$this->options['default_lang']][$this->page_on_front] = $this->get_post($this->page_on_front, $this->options['default_lang']);
545 if ($id == $this->posts[$this->options['default_lang']][$this->page_on_front])
546 return trailingslashit($this->home);
547 }
548
549 return _get_page_link($id);
550 }
551
552 // prevents redirection of the homepage when using page on front
553 function redirect_canonical($redirect_url, $requested_url) {
554 return $requested_url == home_url('/') || strpos($requested_url, $this->page_link('', get_option('page_on_front'))) !== false ? false : $redirect_url;
555 }
556
557 // adds some javascript workaround knowing it's not perfect...
558 function wp_print_footer_scripts() {
559 // modifies the search form since filtering get_search_form won't work if the template uses searchform.php or the search form is hardcoded
560 // don't use directly e[0] just in case there is somewhere else an element named 's'
561 // check before if the hidden input has not already been introduced by get_search_form (FIXME: is there a way to improve this ?
562 // thanks to AndyDeGroo for improving the code for compatility with old browsers
563 // http://wordpress.org/support/topic/development-of-polylang-version-08?replies=6#post-2645559
564
565 $lang = esc_js($this->curlang->slug);
566 $js = "//<![CDATA[
567 e = document.getElementsByName('s');
568 for (i = 0; i < e.length; i++) {
569 if (e[i].tagName.toUpperCase() == 'INPUT') {
570 s = e[i].parentNode.parentNode.children;
571 l = 0;
572 for (j = 0; j < s.length; j++) {
573 if (s[j].name == 'lang') {
574 l = 1;
575 }
576 }
577 if ( l == 0) {
578 var ih = document.createElement('input');
579 ih.type = 'hidden';
580 ih.name = 'lang';
581 ih.value = '$lang';
582 e[i].parentNode.appendChild(ih);
583 }
584 }
585 }
586 //]]>";
587 echo "<script type='text/javascript'>" .$js. "</script>";
588 }
589
590 // adds the language information in the search form
591 // does not work if searchform.php is used or if the search form is hardcoded in another template file
592 function get_search_form($form) {
593 if ($form)
594 $form = $GLOBALS['wp_rewrite']->using_permalinks() ?
595 str_replace(trailingslashit($this->home), $this->get_home_url($this->curlang, true), $form) :
596 str_replace('</form>', '<input type="hidden" name="lang" value="'.esc_attr($this->curlang->slug).'" /></form>', $form);
597
598 return $form;
599 }
600
601 // rewrites the admin bar search form to include the language
602 function admin_bar_search_menu($wp_admin_bar) {
603 global $wp_rewrite;
604
605 $title = sprintf('<form action="%s" method="get" id="adminbarsearch">
606 <input class="adminbar-input" name="s" id="adminbar-search" tabindex="10" type="text" value="" maxlength="150" />
607 <input type="submit" class="adminbar-button" value="%s"/>%s</form>',
608 $wp_rewrite->using_permalinks() ? $this->get_home_url($this->curlang, true) : esc_url($this->home),
609 __('Search'),
610 $wp_rewrite->using_permalinks() ? '' : sprintf('<input type="hidden" name="lang" value="%s" />', esc_attr($this->curlang->slug))
611 );
612
613 $wp_admin_bar->add_menu(array(
614 'parent' => 'top-secondary',
615 'id' => 'search',
616 'title' => $title,
617 'meta' => array('class' => 'admin-bar-search', 'tabindex' => -1)
618 ));
619 }
620
621 // excludes pages which are not in the current language for wp_list_pages
622 // useful for the pages widget
623 function wp_list_pages_excludes($pages) {
624 return array_merge($pages, $this->exclude_pages($this->curlang->term_id));
625 }
626
627 // filters the comments according to the current language mainly for the recent comments widget
628 function comments_clauses($clauses, $comment_query) {
629 return $this->_comments_clauses($clauses, $this->curlang);
630 }
631
632 // modifies the sql request for wp_get_archives an get_adjacent_post to filter by the current language
633 function posts_join($sql) {
634 global $wpdb;
635 return $sql . $wpdb->prepare(" INNER JOIN $wpdb->term_relationships AS pll_tr ON pll_tr.object_id = ID");
636 }
637
638 // modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language
639 function posts_where($sql) {
640 global $wpdb;
641 $id = $this->curlang->term_taxonomy_id;
642 return $sql . $wpdb->prepare(" AND pll_tr.term_taxonomy_id IN ($id)");
643 }
644
645 // modifies the author and date links to add the language parameter (as well as feed link)
646 function archive_link($link) {
647 return $this->add_language_to_link($link, $this->curlang);
648 }
649
650 // returns the url of the translation (if exists) of the current page
651 function get_translation_url($language) {
652 if (isset($this->translation_url[$language->slug]))
653 return $this->translation_url[$language->slug];
654
655 global $wp_query, $wp_rewrite;
656 $qvars = $wp_query->query;
657 $hide = $this->options['default_lang'] == $language->slug && $this->options['hide_default'];
658
659 // post and attachment
660 if (is_single() && (PLL_MEDIA_SUPPORT || !is_attachment()) && $id = $this->get_post($wp_query->queried_object_id, $language))
661 $url = get_permalink($id);
662
663 // page for posts
664 elseif (get_option('show_on_front') == 'page' && isset($wp_query->queried_object_id) && $wp_query->queried_object_id &&
665 $wp_query->queried_object_id == $this->page_for_posts && ($id = $this->get_post($this->page_for_posts, $language)))
666 $url = get_permalink($id);
667
668 elseif (is_page() && $id = $this->get_post($wp_query->queried_object_id, $language))
669 $url = $hide && $id == $this->get_post($this->page_on_front, $language) ? $this->home : get_page_link($id);
670
671 elseif (!is_tax('post_format') && !is_tax('language') && (is_category() || is_tag() || is_tax()) ) {
672 $term = get_queried_object();
673 $lang = $this->get_term_language($term->term_id);
674
675 if (!$lang || $language->slug == $lang->slug)
676 $url = get_term_link($term, $term->taxonomy); // self link
677 elseif ($link_id = $this->get_translation('term', $term->term_id, $language))
678 $url = get_term_link(get_term($link_id, $term->taxonomy), $term->taxonomy);
679 }
680
681 // don't test if there are existing translations before creating the url as it would be very expensive in sql queries
682 elseif (is_archive()) {
683 if ($wp_rewrite->using_permalinks()) {
684 $filters = array('author_link', 'post_type_archive_link', 'year_link', 'month_link', 'day_link');
685
686 // prevents filtering links by current language
687 remove_filter('term_link', array(&$this, 'term_link')); // for post format
688 foreach ($filters as $filter)
689 remove_filter($filter, array(&$this, 'archive_link'));
690
691 if (is_author())
692 $url = $this->add_language_to_link(get_author_posts_url(0, $qvars['author_name']), $language);
693
694 if (is_year())
695 $url = $this->add_language_to_link(get_year_link($qvars['year']), $language);
696
697 if (is_month())
698 $url = $this->add_language_to_link(get_month_link($qvars['year'], $qvars['monthnum']), $language);
699
700 if (is_day())
701 $url = $this->add_language_to_link(get_day_link($qvars['year'], $qvars['monthnum'], $qvars['day']), $language);
702
703 if (is_post_type_archive())
704 $url = $this->add_language_to_link(get_post_type_archive_link($qvars['post_type']), $language);
705
706 if (is_tax('post_format'))
707 $url = $this->add_language_to_link(get_post_format_link($qvars['post_format']), $language);
708
709 // put our language filters again
710 add_filter('term_link', array(&$this, 'term_link'), 10, 3);
711 foreach ($filters as $filter)
712 add_filter($filter, array(&$this, 'archive_link'));
713 }
714 else {
715 $url = $hide ? remove_query_arg('lang') : add_query_arg('lang', $language->slug);
716 $url = remove_query_arg('paged', $url);
717 }
718 }
719
720 elseif (is_search()) {
721 if ($wp_rewrite->using_permalinks())
722 $url = $this->add_language_to_link($this->home.'/?'.$_SERVER['QUERY_STRING'], $language);
723 else {
724 $url = add_query_arg('lang', $language->slug);
725 $url = remove_query_arg('paged', $url);
726 }
727 }
728
729 elseif (is_home() || is_tax('language') )
730 $url = $this->get_home_url($language);
731
732 return $this->translation_url[$language->slug] = (isset($url) ? $url : null);
733 }
734
735 // filters the nav menus according to the current language when called from get_nav_menu_locations()
736 // mainly for Artisteer generated themes
737 function nav_menu_locations($menus) {
738 if (is_array($menus)) {
739 foreach($menus as $location => $menu) {
740 $menu_lang = get_option('polylang_nav_menus');
741 if (isset($menu_lang[$location][$this->curlang->slug]))
742 $menus[$location] = $menu_lang[$location][$this->curlang->slug];
743 }
744 }
745 return $menus;
746 }
747
748 // filters the nav menus according to the current language when called from wp_nav_menus
749 function wp_nav_menu_args($args) {
750 if (/*!$args['menu'] &&*/ $args['theme_location']) {
751 $menu_lang = get_option('polylang_nav_menus');
752 if (isset($menu_lang[$args['theme_location']][$this->curlang->slug]))
753 $args['menu'] = $menu_lang[$args['theme_location']][$this->curlang->slug];
754 }
755 return $args;
756 }
757
758 // adds the language switcher at the end of the menu
759 function wp_nav_menu_items($items, $args) {
760 $menu_lang = get_option('polylang_nav_menus');
761 return isset($args->theme_location) && isset($menu_lang[$args->theme_location]['switcher']) && $menu_lang[$args->theme_location]['switcher'] ?
762 $items . $this->the_languages(array_merge($menu_lang[$args->theme_location], array('menu' => 1, 'echo' => 0))) : $items;
763 }
764
765 // filters the widgets according to the current language
766 function widget_display_callback($instance, $widget, $args) {
767 $widget_lang = get_option('polylang_widgets');
768 // don't display if a language filter is set and this is not the current one
769 return isset($widget_lang[$widget->id]) && $widget_lang[$widget->id] && $widget_lang[$widget->id] != $this->curlang->slug ? false : $instance;
770 }
771
772 // translates biography
773 function get_user_metadata($null, $id, $meta_key, $single) {
774 return $meta_key == 'description' ? get_user_meta($id, 'description_'.$this->curlang->slug, true) : $null;
775 }
776
777 // translates page for posts and page on front
778 function translate_page($val) {
779 // returns the current page if there is no translation to avoid ugly notices
780 // the fonction is often called so let's store the result
781 return isset($this->curlang) && $val && (isset($this->posts[$val]) || $this->posts[$val] = $this->get_post($val, $this->curlang)) ? $this->posts[$val] : $val;
782 }
783
784 // filters the home url to get the right language
785 function home_url($url) {
786 if (!did_action('template_redirect') || rtrim($url,'/') != $this->home)
787 return $url;
788
789 $theme = get_theme_root();
790 foreach (debug_backtrace(/*!DEBUG_BACKTRACE_PROVIDE_OBJECT|DEBUG_BACKTRACE_IGNORE_ARGS*/) as $trace) {
791 // search form
792 if (isset($trace['file']) && strpos($trace['file'], 'searchform.php'))
793 return $GLOBALS['wp_rewrite']->using_permalinks() ? $this->get_home_url($this->curlang, true) : $url;
794
795 $ok = $trace['function'] == 'wp_nav_menu' ||
796 // direct call from the theme
797 (isset($trace['file']) && strpos($trace['file'], $theme) !== false &&
798 in_array($trace['function'], array('home_url', 'bloginfo', 'get_bloginfo')) );
799
800 if ($ok)
801 return $this->get_home_url($this->curlang);
802 }
803
804 return $url;
805 }
806
807 // returns the home url in the right language
808 function get_home_url($language = '', $is_search = false) {
809 if ($language == '')
810 $language = $this->curlang;
811
812 if (isset($this->home_urls[$language->slug]))
813 return $this->home_urls[$language->slug];
814
815 if ($this->options['default_lang'] == $language->slug && $this->options['hide_default'])
816 return $this->home_urls[$language->slug] = trailingslashit($this->home);
817
818 // a static page is used as front page : /!\ don't use get_page_link to avoid infinite loop
819 // don't use this for search form
820 if (!$is_search && $this->page_on_front && $id = $this->get_post($this->page_on_front, $language))
821 return $this->home_urls[$language->slug] = $this->page_link('', $id);
822
823 return $this->home_urls[$language->slug] = get_term_link($language, 'language');
824 }
825
826 // displays (or returns) the language switcher
827 function the_languages($args = '') {
828 $defaults = array(
829 'dropdown' => 0, // display as list and not as dropdown
830 'echo' => 1, // echoes the list
831 'hide_if_empty' => 1, // hides languages with no posts (or pages)
832 'menu' => '0', // not for nav menu
833 'show_flags' => 0, // don't show flags
834 'show_names' => 1, // show language names
835 'display_names_as' => 'name', // valid options are slug and name
836 'force_home' => 0, // tries to find a translation
837 'hide_if_no_translation' => 0, // don't hide the link if there is no translation
838 'hide_current' => 0, // don't hide current language
839 'post_id' => null, // if not null, link to translations of post defined by post_id
840 );
841 extract(wp_parse_args($args, $defaults));
842
843 if ($dropdown)
844 $output = $this->dropdown_languages(array('hide_empty' => $hide_if_empty, 'selected' => $this->curlang->slug));
845
846 else {
847 $output = '';
848
849 foreach ($this->get_languages_list(array('hide_empty' => $hide_if_empty)) as $language) {
850 // hide current language
851 if ($this->curlang->term_id == $language->term_id && $hide_current)
852 continue;
853
854 $url = $post_id !== null && ($tr_id = $this->get_post($post_id, $language)) ? get_permalink($tr_id) :
855 $post_id === null && !$force_home ? $this->get_translation_url($language) : null;
856
857 $url = apply_filters('pll_the_language_link', $url, $language->slug, $language->description);
858
859 // hide if no translation exists
860 if (!isset($url) && $hide_if_no_translation)
861 continue;
862
863 $url = isset($url) ? $url : $this->get_home_url($language); // if the page is not translated, link to the home page
864
865 $class = 'lang-item lang-item-'.esc_attr($language->term_id);
866 $class .= $language->term_id == $this->curlang->term_id ? ' current-lang' : '';
867 $class .= $menu ? ' menu-item' : '';
868
869 $flag = $show_flags ? $this->get_flag($language) : '';
870 $name = $show_names || !$show_flags ? esc_html($display_names_as == 'slug' ? $language->slug : $language->name) : '';
871
872 $output .= sprintf("<li class='%s'><a hreflang='%s' href='%s'>%s</a></li>\n",
873 $class, esc_attr($language->slug), esc_url($url), $show_flags && $show_names ? $flag.'&nbsp;'.$name : $flag.$name);
874 }
875 }
876
877 $output = apply_filters('pll_the_languages', $output, $args);
878
879 if(!$echo)
880 return $output;
881 echo $output;
882 }
883
884 // just returns the current language for API
885 function current_language($args) {
886 return !isset($this->curlang) ? false :
887 ($args == 'name' ? $this->curlang->name :
888 ($args == 'locale' ? $this->curlang->description :
889 $this->curlang->slug));
890 }
891 }
892 ?>
893