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

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

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