PluginProbe
Polylang / 0.4.2
Polylang v0.4.2
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 / polylang.php

polylang.php in Polylang 0.4.2, at polylang.php

801 lines 31.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Polylang
4 Plugin URI: http://wordpress.org/extend/plugins/polylang/
5 Version: 0.4.2
6 Author: F. Demarle
7 Description: Adds multilingual capability to Wordpress
8 */
9
10 /* Copyright 2011 F. Demarle
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License, version 2, as
14 published by the Free Software Foundation.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26 define('POLYLANG_VERSION', '0.4.1');
27
28 define('POLYLANG_DIR', dirname(__FILE__));
29 define('INC_DIR', POLYLANG_DIR.'/include');
30
31 require_once(ABSPATH . 'wp-admin/includes/template.php'); // to ensure that 'get_current_screen' is defined
32
33 require_once(INC_DIR.'/base.php');
34 require_once(INC_DIR.'/admin.php');
35 require_once(INC_DIR.'/widget.php');
36
37 class Polylang extends Polylang_Base {
38 var $curlang; // current language
39 var $default_locale;
40 var $list_textdomains = array(); // all text domains
41 var $search_form_filter = false; // did we pass our get_search_form filter ?
42
43 function __construct() {
44 // manages plugin activation and deactivation
45 register_activation_hook( __FILE__, array(&$this, 'activate') );
46 register_deactivation_hook( __FILE__, array(&$this, 'deactivate') );
47
48 // plugin and widget initialization
49 add_action('init', array(&$this, 'init'));
50 add_action('widgets_init', array(&$this, 'widgets_init'));
51
52 // rewrite rules
53 add_filter('rewrite_rules_array', array(&$this, 'rewrite_rules_array' ));
54
55 // filters categories and post tags by language
56 add_filter('terms_clauses', array(&$this, 'terms_clauses'), 10, 3);
57
58 if (is_admin())
59 new Polylang_Admin();
60 else {
61 // text domain management
62 add_filter('locale', array(&$this, 'get_locale'));
63 add_filter('override_load_textdomain', array(&$this, 'mofile'), 10, 3);
64 add_action('wp', array(&$this, 'load_textdomains'));
65
66 // filters posts according to the language
67 add_filter('pre_get_posts', array(&$this, 'pre_get_posts'));
68
69 // meta in the html head section
70 remove_action('wp_head', 'rel_canonical');
71 add_action('wp_head', array(&$this, 'wp_head'));
72
73 // prevents redirection of the homepage
74 add_filter('redirect_canonical', array(&$this, 'redirect_canonical'), 10, 2);
75
76 // adds javascript at the end of the document
77 add_action('wp_print_footer_scripts', array(&$this, 'wp_print_footer_scripts'));
78
79 // adds the language information in the search form
80 add_filter('get_search_form', array(&$this, 'get_search_form'));
81
82 // filters the pages according to the current language in wp_list_pages
83 add_filter('wp_list_pages_excludes', array(&$this, 'wp_list_pages_excludes'));
84
85 // filters the comments according to the current language
86 add_filter('comments_clauses', array(&$this, 'comments_clauses'), 10, 2);
87
88 // rewrites feed links to filter them by language
89 add_filter('feed_link', array(&$this, 'feed_link'), 10, 2);
90
91 // rewrites archives links to filter them by language
92 add_filter('getarchives_join', array(&$this, 'posts_join'));
93 add_filter('getarchives_where', array(&$this, 'posts_where'));
94
95 // rewrites author and date links to filter them by language
96 add_filter('author_link', array(&$this, 'archive_link'));
97 add_filter('year_link', array(&$this, 'archive_link'));
98 add_filter('month_link', array(&$this, 'archive_link'));
99 add_filter('day_link', array(&$this, 'archive_link'));
100
101 // modifies the calendar to filter posts by language
102 add_filter('get_calendar', array(&$this, 'get_calendar'));
103
104 // rewrites next and previous post links to filter them by language
105 add_filter('get_previous_post_join', array(&$this, 'posts_join'));
106 add_filter('get_next_post_join', array(&$this, 'posts_join'));
107 add_filter('get_previous_post_where', array(&$this, 'posts_where'));
108 add_filter('get_next_post_where', array(&$this, 'posts_where'));
109
110 // filters the nav menus according to the current language
111 add_filter('wp_nav_menu_args', array(&$this, 'wp_nav_menu_args'));
112
113 // filters the widgets according to the current language
114 add_filter('widget_display_callback', array(&$this, 'widget_display_callback'), 10, 3);
115
116 // modifies the home url
117 add_filter('home_url', array(&$this, 'home_url'));
118
119 // allows a new value for the 'show' parameter to display the homepage url according to the current language
120 add_filter('bloginfo_url', array(&$this, 'bloginfo_url'), 10, 2);
121
122 // Template tags
123 add_action('the_languages', array(&$this, 'the_languages'));
124 }
125 }
126
127 // plugin activation
128 function activate() {
129 // create the termmeta table - not provided by WP by default - if it does not already exists
130 // uses exactly the same model as other meta tables to be able to use access functions provided by WP
131 global $wpdb;
132 $charset_collate = '';
133 if ( ! empty($wpdb->charset) )
134 $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
135 if ( ! empty($wpdb->collate) )
136 $charset_collate .= " COLLATE $wpdb->collate";
137
138 $table = $wpdb->prefix . 'termmeta';
139
140 $tables = $wpdb->get_results("show tables like '$table'");
141 if (!count($tables))
142 $wpdb->query("CREATE TABLE $table (
143 meta_id bigint(20) unsigned NOT NULL auto_increment,
144 term_id bigint(20) unsigned NOT NULL default '0',
145 meta_key varchar(255) default NULL,
146 meta_value longtext,
147 PRIMARY KEY (meta_id),
148 KEY term_id (term_id),
149 KEY meta_key (meta_key)
150 ) $charset_collate;");
151
152 // codex tells to use the init action to call register_taxonomy but I need it now for my rewrite rules
153 register_taxonomy('language', get_post_types(array('show_ui' => true)), array('label' => false, 'query_var'=>'lang'));
154
155 // defines default values for options in case this is the first installation
156 $options = get_option('polylang');
157 if (!$options) {
158 $options['browser'] = 1; // default language for the front page is set by browser preference
159 $options['rewrite'] = 0; // do not remove /language/ in permalinks
160 $options['hide_default'] = 0; // do not remove URL language information for default language
161 }
162 $options['version'] = POLYLANG_VERSION;
163 update_option('polylang', $options);
164
165 // add our rewrite rules
166 global $wp_rewrite;
167 $wp_rewrite->flush_rules();
168 }
169
170 // plugin deactivation
171 function deactivate() {
172 // delete our rewrite rules
173 remove_filter('rewrite_rules_array', array(&$this,'rewrite_rules_array' ));
174 global $wp_rewrite;
175 $wp_rewrite->flush_rules();
176 }
177
178 // some initialization
179 function init() {
180 global $wpdb;
181 $wpdb->termmeta = $wpdb->prefix . 'termmeta'; // registers the termmeta table in wpdb
182
183 // registers the language taxonomy
184 // codex: use the init action to call this function
185 register_taxonomy('language', get_post_types(array('show_ui' => true)), array(
186 'label' => false,
187 'public' => false, // avoid displaying the 'like post tags text box' in the quick edit
188 'query_var'=>'lang',
189 'update_count_callback' => '_update_post_term_count'));
190
191 // optionaly removes 'language' in permalinks so that we get http://www.myblog/en/ instead of http://www.myblog/language/en/
192 // the simple line of code is inspired by the WP No Category Base plugin: http://wordpresssupplies.com/wordpress-plugins/no-category-base/
193 global $wp_rewrite;
194 $options = get_option('polylang');
195 if ($options['rewrite'] && $wp_rewrite->extra_permastructs)
196 $wp_rewrite->extra_permastructs['language'][0] = '%language%';
197
198 $this->default_locale = get_locale(); // save the default locale before we start any language manipulation
199 load_plugin_textdomain('polylang', false, basename(POLYLANG_DIR).'/languages'); // plugin i18n
200 }
201
202 // registers our widget
203 function widgets_init() {
204 register_widget('Polylang_Widget');
205 }
206
207 // rewrites rules if pretty permalinks are used
208 function rewrite_rules_array($rules) {
209 $options = get_option('polylang');
210 $newrules = array();
211 $listlanguages = $this->get_languages_list();
212
213 // modifies the rules created by WordPress when '/language/' is removed in permalinks
214 if ($options['rewrite']) {
215 foreach ($listlanguages as $language) {
216 $slug = $options['default_lang'] == $language->slug && $options['hide_default'] ? '' : $language->slug . '/';
217 $newrules[$slug.'feed/(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]';
218 $newrules[$slug.'(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]';
219 $newrules[$slug.'page/?([0-9]{1,})/?$'] = 'index.php?lang='.$language->slug.'&paged=$matches[1]';
220 if ($slug)
221 $newrules[$slug.'?$'] = 'index.php?lang='.$language->slug;
222 }
223 unset($rules['([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?lang=$matches[1]&feed=$matches[2]
224 unset($rules['([^/]+)/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?lang=$matches[1]&feed=$matches[2]
225 unset($rules['([^/]+)/page/?([0-9]{1,})/?$']); // => index.php?lang=$matches[1]&paged=$matches[2]
226 unset($rules['([^/]+)/?$']); // => index.php?lang=$matches[1]
227 }
228
229 $base = $options['rewrite'] ? '' : 'language/';
230
231 // rewrite rules for comments feed filtered by language
232 foreach ($listlanguages as $language) {
233 $slug = $options['default_lang'] == $language->slug && $options['hide_default'] ? '' : $base.$language->slug . '/';
234 $newrules[$slug.'comments/feed/(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]&withcomments=1';
235 $newrules[$slug.'comments/(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?lang='.$language->slug.'&feed=$matches[1]&withcomments=1';
236 }
237 unset($rules['comments/feed/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?&feed=$matches[1]&withcomments=1
238 unset($rules['comments/(feed|rdf|rss|rss2|atom)/?$']); // => index.php?&feed=$matches[1]&withcomments=1
239
240 // rewrite rules for archives filtered by language
241 foreach ($rules as $key => $rule) {
242 $is_archive = strpos($rule, 'author_name=') || strpos($rule, 'year=') && !(
243 strpos($rule, 'p=') ||
244 strpos($rule, 'name=') ||
245 strpos($rule, 'page=') ||
246 strpos($rule, 'cpage=') );
247
248 if ($is_archive) {
249 foreach ($listlanguages as $language) {
250 $slug = $options['default_lang'] == $language->slug && $options['hide_default'] ? '' : $base.$language->slug . '/';
251 $newrules[$slug.$key] = str_replace('?', '?lang='.$language->slug.'&', $rule);
252 }
253 unset($rules[$key]); // now useless
254 }
255 }
256 return $newrules + $rules;
257 }
258
259 // filters categories and post tags by language when needed (both in admin panels and frontend)
260 function terms_clauses($clauses, $taxonomies, $args) {
261 // does nothing except on taxonomies which have show_ui set to 1 (includes category and post_tags)
262 foreach ($taxonomies as $tax) {
263 if(!get_taxonomy($tax)->show_ui)
264 return $clauses;
265 }
266
267 if (is_admin()) {
268 $screen = get_current_screen(); // since WP 3.1
269
270 // NOTE: $screen is not defined in the tag cloud of the Edit Post panel ($pagenow set to admin-ajax.php)
271 if (isset($screen))
272 // does nothing in the Categories, Post tags, Languages and Posts* admin panels
273 if ($screen->base == 'edit-tags' || $screen->base == 'toplevel_page_mlang' || $screen->base == 'edit')
274 return $clauses;
275
276 // *FIXME I want all categories in the dropdown list and only the ones in the right language in the inline edit
277 // It seems that I need javascript to get the post_id as inline edit data are manipulated in inline-edit-post.js
278
279 $this->curlang = $this->get_current_language();
280 }
281
282 // adds our clauses to filter by current language
283 if ($this->curlang) {
284 global $wpdb;
285 $clauses['join'] .= " INNER JOIN $wpdb->termmeta AS tm ON t.term_id = tm.term_id";
286 $clauses['where'] .= $wpdb->prepare(" AND tm.meta_key = '_language' AND tm.meta_value = %d", $this->curlang->term_id);
287 }
288 return $clauses;
289 }
290
291 // returns the language according to browser preference or the default language
292 function get_preferred_language() {
293 // check first is the user was already browsing this site
294 if (isset($_COOKIE['wordpress_polylang']))
295 return $this->get_language($_COOKIE['wordpress_polylang']);
296
297 // sets the browsing language according to the browser preferences
298 // code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
299 $options = get_option('polylang');
300 if ($options['browser']) {
301 $accept_langs = array();
302
303 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
304 // break up string into pieces (languages and q factors)
305 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);
306
307 if (count($lang_parse[1])) {
308 // NOTE: array_combine => PHP5
309 $accept_langs = array_combine($lang_parse[1], $lang_parse[4]); // create a list like "en" => 0.8
310 // set default to 1 for any without q factor
311 foreach ($accept_langs as $accept_lang => $val) {
312 if ($val === '') $accept_langs[$accept_lang] = 1;
313 }
314 arsort($accept_langs, SORT_NUMERIC); // sort list based on value
315 }
316 }
317
318 // looks through sorted list and use first one that matches our language list
319 $listlanguages = $this->get_languages_list(true); // hides languages with no post
320 foreach ($accept_langs as $accept_lang => $val) {
321 foreach ($listlanguages as $language) {
322 if (strpos($accept_lang, $language->slug) === 0 && !isset($pref_lang)) {
323 $pref_lang = $language;
324 }
325 }
326 }
327 } // options['browser']
328
329 // either there is no preference in the browser or preferences does not match our language list or it is requested not to use the browser preference
330 if (!isset($pref_lang))
331 $pref_lang = $this->get_language($options['default_lang']);
332
333 return $pref_lang;
334 }
335
336 // returns the current language
337 function get_current_language() {
338 global $post_ID;
339
340 if($this->curlang)
341 return $this->curlang;
342
343 // no language set for 404 and attachment
344 if (is_404() || is_attachment())
345 return $this->get_preferred_language();
346
347 if (is_admin()) {
348 if (isset($post_ID))
349 $lang = $this->get_post_language($post_ID);
350
351 if (isset($_POST['action']) && $_POST['action'] == 'get-tagcloud') {
352 // to get the language in the Post Tags metabox in the Edit Post screen (as $post_ID not defined)
353 $qvars = $this->get_referer_vars();
354 if (isset($qvars['new_lang']))
355 $lang= $this->get_language($qvars['new_lang']); // post has been created with 'add new' (translation)
356 elseif (isset($qvars['post']))
357 $lang = $this->get_post_language($qvars['post']); // edit post
358 }
359
360 // the post is created with the 'add new' (translation) link
361 if (isset($_GET['new_lang']))
362 $lang = $this->get_language($_GET['new_lang']);
363 }
364 elseif ($var = get_query_var('lang'))
365 $lang = $this->get_language($var);
366
367 elseif (is_single() || is_page() && $var = get_queried_object_id())
368 $lang = $this->get_post_language($var);
369
370 else {
371 foreach (get_taxonomies(array('show_ui'=>true)) as $taxonomy) {
372 if ($var = get_query_var(get_taxonomy($taxonomy)->query_var))
373 $lang = $this->get_term_language($var, $taxonomy);
374 }
375 }
376
377 return (isset($lang)) ? $lang : NULL;
378 }
379
380 // returns the locale based on current language
381 function get_locale($locale) {
382 if ($this->curlang)
383 $locale = $this->curlang->description;
384 return $locale;
385 }
386
387 // saves all text domains in a table for later usage
388 function mofile($bool, $domain, $mofile) {
389 $this->list_textdomains[] = array ('mo' => $mofile, 'domain' => $domain);
390 return true; // prevents WP loading text domains as we will load them all later
391 }
392
393 // NOTE: I believe there are two ways for a plugin to force the WP language
394 // as done by xili_language and here: load text domains and reinitialize wp_locale with the action 'wp'
395 // as done by qtranslate: define the locale with the action 'plugins_loaded', but in this case, the language must be specified in the url.
396 function load_textdomains() {
397 // sets the current language and set a cookie to remember it
398 if ($this->curlang = $this->get_current_language())
399 setcookie('wordpress_polylang', $this->curlang->slug, time() + 31536000 /* 1 year */, COOKIEPATH, COOKIE_DOMAIN);
400
401 // our override_load_textdomain filter has done its job. let's remove it before calling load_textdomain
402 remove_filter('override_load_textdomain', array(&$this, 'mofile'));
403
404 // now we can load text domains with the right language
405 $new_locale = get_locale();
406 foreach ($this->list_textdomains as $textdomain)
407 load_textdomain( $textdomain['domain'], str_replace($this->default_locale, $new_locale, $textdomain['mo']));
408
409 global $wp_locale;
410 $wp_locale->init(); // reinitializes wp_locale for weekdays and months
411 }
412
413 // filters posts according to the language
414 function pre_get_posts($query) {
415 $options = get_option('polylang');
416 $qvars = $query->query_vars;
417
418 // detect our exclude pages query and returns to avoid conflicts
419 // this test should be sufficient
420 if (isset($qvars['tax_query'][0]) && isset($qvars['tax_query'][0]['taxonomy']) && isset($qvars['tax_query'][0]['operator']))
421 return;
422
423 if (empty($query->query)) {
424 if ( $options['hide_default'] && isset($_COOKIE['wordpress_polylang']) )
425 $this->curlang = $this->get_language($options['default_lang']);
426 else
427 $this->curlang = $this->get_preferred_language(); // sets the language according to browser preference or default language
428
429 if ($options['default_lang'] == $this->curlang->slug && $options['hide_default']) {
430 if (($post_id = get_option('page_on_front')) && $link_id = $this->get_post($post_id, $this->curlang))
431 $query->set('page_id', $link_id);
432 else
433 $query->set('lang', $this->curlang->slug);
434 }
435 else {
436 if (($post_id = get_option('page_on_front')) && $link_id = $this->get_post($post_id, $this->curlang))
437 $url = _get_page_link($link_id);
438 else
439 $url = home_url('?lang='.$this->curlang->slug);
440
441 wp_redirect($url);
442 exit;
443 }
444 }
445
446 // FIXME to generalize as I probably forget things
447 // sets the language in case we hide the default language
448 if ( $options['hide_default'] && !isset($qvars['lang']) && (
449 (count($query->query) == 1 && isset($qvars['paged']) && $qvars['paged']) ||
450 isset($qvars['m']) && $qvars['m'] ||
451 isset($qvars['feed']) && $qvars['feed'] ||
452 isset($qvars['author']) && $qvars['author']))
453 $query->set('lang', $options['default_lang']);
454
455 // filters recent posts to the current language
456 // FIXME if $qvars['post_type'] == 'nav_menu_item', setting lang breaks custom menus.
457 // since to get nav_menu_items, get_post is used and no language is linked to nav_menu_items
458 if ($query->is_home && $this->curlang && !isset($qvars['post_type']))
459 $query->set('lang', $this->curlang->slug);
460
461 // remove pages query when the language is set unless we do a search
462 // FIXME is only search broken by this ?
463 if (isset($qvars['lang']) && $qvars['lang'] && !isset($qvars['post_type']) && !is_search())
464 $query->set('post_type', 'post');
465
466 // unset the is_archive flag for language pages to prevent loading the archive template
467 // keep archive flag for comment feed otherwise the language filter does not work
468 if (isset($qvars['lang']) && $qvars['lang'] && !is_post_type_archive() && !is_date() && !is_author() && !is_category() && !is_tag() && !is_comment_feed())
469 $query->is_archive = false;
470
471 // unset the is_tax flag for authors pages
472 // FIXME Probably I should do this for other cases
473 if (isset($qvars['lang']) && $qvars['lang'] && is_author())
474 $query->is_tax = false;
475 }
476
477 function wp_head() {
478 // modifies the canonical link to the homepage
479 if (is_singular()) {
480 global $wp_the_query;
481 if ($id = $wp_the_query->get_queried_object_id()) {
482 if (is_page())
483 $link = _get_page_link($id); // ignores page_on_front unlike get_permalink
484 else
485 $link = get_permalink ($id);
486 echo "<link rel='canonical' href='$link' />\n";
487 }
488 }
489
490 // outputs references to translated pages (if exists) in the html head section
491 foreach ($this->get_languages_list() as $language) {
492 if ($language->slug != $this->curlang->slug && $url = $this->get_translation_url($language))
493 printf("<link hreflang='%s' href='%s' rel='alternate' />\n", esc_attr($language->slug), esc_url($url));
494 }
495 }
496
497 // prevents redirection of the homepage
498 function redirect_canonical($redirect_url, $requested_url) {
499 if($requested_url == _get_page_link(get_option('page_on_front')))
500 return false;
501 return $redirect_url;
502 }
503
504 // adds some javascript workaround knowing it's not perfect...
505 function wp_print_footer_scripts() {
506 if ($this->curlang) {
507 $js = '';
508
509 // modifies the search form since filtering get_search_form won't work if the template uses searchform.php
510 // don't use directly e[0] just in case there is somewhere else an element named 's'
511 // check before if the hidden input has not already been introduced by get_search_form
512 if (!$this->search_form_filter) {
513 $lang = esc_js($this->curlang->slug);
514 $js .= "e = document.getElementsByName('s');
515 for (i = 0; i < e.length; i++) {
516 if (e[i] == '[object HTMLInputElement]') {
517 var ih = document.createElement('input');
518 ih.type = 'hidden';
519 ih.name = 'lang';
520 ih.value = '$lang';
521 e[i].parentNode.appendChild(ih);
522 }
523 }";
524 }
525
526 if ($js)
527 echo "<script type='text/javascript'>" .$js. "</script>";
528 }
529 }
530
531 // adds the language information in the search form
532 // does not work if searchform.php is used
533 function get_search_form($form) {
534 if ($form) {
535 $this->search_form_filter = true;
536 $form = str_replace('</form>', '<input type="hidden" name="lang" value="'.esc_attr($this->curlang->slug).'" /></form>', $form);
537 }
538 return $form;
539 }
540
541 // excludes pages which are not in the current language for wp_list_pages
542 // useful for the pages widget
543 function wp_list_pages_excludes($pages) {
544 if (isset($this->curlang)) {
545 $q = array(
546 'numberposts'=>-1,
547 'post_type' => 'page',
548 'fields' => 'ids',
549 'tax_query' => array(array(
550 'taxonomy'=>'language',
551 'fields' => 'id',
552 'terms'=>$this->curlang->term_id,
553 'operator'=>'NOT IN'
554 ))
555 );
556 $pages = array_merge($pages, get_posts($q));
557 }
558 return $pages;
559 }
560
561 // filters the comments according to the current language mainly for the recent comments widget
562 function comments_clauses($clauses, $comment_query) {
563 // first test if wp_posts.ID already available in the query
564 if ($this->curlang && strpos($clauses['join'], '.ID')) {
565 global $wpdb;
566 $clauses['join'] .= " INNER JOIN $wpdb->term_relationships AS tr ON tr.object_id = ID";
567 $clauses['join'] .= " INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";
568 $clauses['where'] .= $wpdb->prepare(" AND tt.term_id = %d", $this->curlang->term_id);
569 }
570 return $clauses;
571 }
572
573 // Modifies the feed link to add the language parameter
574 function feed_link($url, $feed) {
575 global $wp_rewrite;
576 $options = get_option('polylang');
577
578 if ($this->curlang) {
579 if ($wp_rewrite->using_permalinks()) {
580 $home = get_option('home');
581 $base = $options['rewrite'] ? '/' : '/language/';
582 $slug = $options['default_lang'] == $this->curlang->slug && $options['hide_default'] ? '' : $base.$this->curlang->slug;
583 $url = esc_url(str_replace($home, $home.$slug, $url));
584 }
585 elseif ($feed)
586 $url = esc_url(home_url('?lang='.$this->curlang->slug.'&feed='.$feed));
587
588 }
589 return $url;
590 }
591
592 // modifies the sql request for wp_get_archives an get_adjacent_post to filter by the current language
593 function posts_join($sql) {
594 if ($this->curlang) {
595 global $wpdb;
596 $sql .= " INNER JOIN $wpdb->term_relationships ON object_id = ID";
597 }
598 return $sql;
599 }
600
601 // modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language
602 function posts_where($sql) {
603 if ($this->curlang) {
604 global $wpdb;
605 $sql .= $wpdb->prepare(" AND term_taxonomy_id = %d", $this->curlang->term_id);
606 }
607 return $sql;
608 }
609
610 // modifies the author and date links to add the language parameter
611 function archive_link($link) {
612 if ($this->curlang) {
613 global $wp_rewrite;
614 $options = get_option('polylang');
615 $home = get_option('home');
616
617 if ($wp_rewrite->using_permalinks()) {
618 $base = $options['rewrite'] ? '/' : '/language/';
619 $slug = $options['default_lang'] == $this->curlang->slug && $options['hide_default'] ? '' : $base.$this->curlang->slug;
620 $link = esc_url(str_replace($home, $home.$slug, $link));
621 }
622 else
623 $link = esc_url(str_replace($home.'/?', $home.'/?lang='.$this->curlang->slug.'&amp;', $link));
624 }
625 return $link;
626 }
627
628 // modifies the calendar to filter posts by language
629 function get_calendar() {
630 require_once(INC_DIR.'/calendar.php');
631 return $calendar_output;
632 }
633
634 // returns the url of the translation (if exists) of the current page
635 function get_translation_url($language) {
636 global $wp_query, $wp_rewrite;
637 $qvars = $wp_query->query;
638 $options = get_option('polylang');
639 $hide = $options['default_lang'] == $language->slug && $options['hide_default'];
640
641 // is_single is set to 1 for attachment but no language is set
642 if (is_single() && !is_attachment() && $id = $this->get_post(get_the_ID(), $language))
643 $url = get_permalink($id);
644
645 elseif (is_page() && $id = $this->get_post(get_the_ID(), $language))
646 $url = $hide && $id == $this->get_post(get_option('page_on_front'), $language) ?
647 get_option('home') :
648 _get_page_link($id);
649
650 elseif ( !is_tax ('language') && (is_category() || is_tag() || is_tax () ) ) {
651 $term = get_queried_object();
652 $lang = $this->get_term_language($term->term_id);
653 $taxonomy = $term->taxonomy;
654
655 if ($language->slug == $lang->slug)
656 $url = get_term_link($term, $taxonomy); // self link
657 elseif ($link_id = $this->get_translated_term($term->term_id, $language))
658 $url = get_term_link(get_term($link_id, $taxonomy), $taxonomy);
659 }
660
661 // don't test if there are existing translations before creating the url as it would be very expensive in sql queries
662 elseif(is_archive()) {
663 if ($wp_rewrite->using_permalinks()) {
664 $base = $options['rewrite'] ? '/' : '/language/';
665 $base = $hide ? '' : $base.$language->slug;
666 $base = get_option('home').$base.'/';
667
668 if (is_author())
669 $url = esc_url($base.'author/'.$qvars['author_name'].'/');
670
671 if (is_year())
672 $url = esc_url($base.$qvars['year'].'/');
673
674 if (is_month())
675 $url = esc_url($base.$qvars['year'].'/'.$qvars['monthnum'].'/');
676
677 if (is_day())
678 $url = esc_url($base.$qvars['year'].'/'.$qvars['monthnum'].'/'.$qvars['day'].'/');
679 }
680 else
681 $url = $hide ? remove_query_arg('lang') : add_query_arg('lang', $language->slug);
682 }
683
684 elseif (is_home() || is_tax('language') )
685 $url = $hide ? get_option('home') : get_term_link($language, 'language');
686
687 return isset($url) ? $url : null;
688 }
689
690 // filters the nav menus according to the current language
691 function wp_nav_menu_args($args) {
692 if (!$args['menu'] && $args['theme_location'] && $this->curlang) {
693 $menu_lang = get_option('polylang_nav_menus');
694 $args['menu'] = $menu_lang[$args['theme_location']][$this->curlang->slug];
695 }
696 return $args;
697 }
698
699 // filters the widgets according to the current language
700 function widget_display_callback($instance, $widget, $args) {
701 $widget_lang = get_option('polylang_widgets');
702 // don't display if a language filter is set and this is not the current one
703 if (isset($this->curlang) && isset($widget_lang[$widget->id]) && $widget_lang[$widget->id] && $widget_lang[$widget->id] != $this->curlang->slug)
704 return false;
705
706 return $instance;
707 }
708
709 // filters the home url to get the right language
710 function home_url($url) {
711 if ( !(did_action('template_redirect') && rtrim($url,'/') == rtrim(get_option('home'),'/') && $this->curlang) )
712 return $url;
713
714 // don't like this but at least for WP_Widget_Categories::widget, it seems to be the only solution
715 // FIXME are there other exceptions ?
716 foreach (debug_backtrace() as $trace) {
717 $exceptions = $trace['function'] == 'get_pagenum_link' ||
718 $trace['function'] == 'get_author_posts_url' ||
719 ($trace['function'] == 'widget' && $trace['class'] == 'WP_Widget_Categories');
720 if ($exceptions)
721 return $url;
722 }
723
724 return $this->get_home_url($this->curlang);
725 }
726
727 // returns the home url in the right language
728 function get_home_url($language) {
729 $options = get_option('polylang');
730 if ($options['default_lang'] == $language->slug && $options['hide_default'])
731 return trailingslashit(get_option('home'));
732
733 // a static page is used as front page
734 if (($post_id = get_option('page_on_front')) && $id = $this->get_post($post_id, $language))
735 $url = _get_page_link($id);
736
737 return isset($url) ? $url : get_term_link($language, 'language');
738 }
739
740 // adds 'lang_url' as possible value to the 'show' parameter of bloginfo to display the home url in the correct language
741 // FIXME not tested
742 function bloginfo_url($output, $show) {
743 if ($show == 'lang_url' && $this->curlang) {
744 $url = $this->get_home_url($this->curlang);
745 $output = isset($url) ? $url : get_option('home');
746 }
747 return $output;
748 }
749
750 // Template tag: Displays links to the current page in other languages
751 // Usage: do_action('the_languages');
752 function the_languages($args = '') {
753 $defaults = array(
754 'dropdown' => 0, // display as list and not as dropdown
755 'show_names' => 1, // show language names
756 'show_flags' => 0, // don't show flags
757 'hide_if_empty' => 1 // hides languages with no posts (or pages)
758 );
759 extract(wp_parse_args($args, $defaults));
760
761 $listlanguages = $this->get_languages_list($hide_if_empty);
762 $output = $dropdown ? '<select name="lang_choice" id="lang_choice">' : "<ul>\n";
763
764 foreach ($listlanguages as $language) {
765 if ($dropdown) {
766 $output .= sprintf(
767 "<option value='%s'%s>%s</option>\n",
768 esc_attr($language->slug),
769 $language->slug == $this->curlang->slug ? ' selected="selected"' : '',
770 esc_attr($language->name) // FIXME flag does not work for the dropdown list
771 );
772 }
773 else {
774 $url = $this->get_translation_url($language);
775 $url = isset($url) ? $url : $this->get_home_url($language); // if the page is not translated, link to the home page
776
777 $class = 'lang-item lang-item-'.esc_attr($language->term_id);
778 $class .= $language->slug == $this->curlang->slug ? ' current-lang' : '';
779
780 $flag = $show_flags && (
781 file_exists(POLYLANG_DIR.($file = '/local_flags/'.$language->description.'.png')) ||
782 file_exists(POLYLANG_DIR.($file = '/flags/'.$language->description.'.png')) ) ?
783 '<img src="'.esc_url(WP_PLUGIN_URL.'/polylang'.$file).'" alt="'.esc_attr($language->name).'" />' : '';
784
785 $name = $show_names || !$show_flags ? esc_attr($language->name) : '';
786
787 $output .= '<li class="'.$class.'"><a href="'.esc_url($url).'">'.($show_flags && $show_names ? $flag.'&nbsp;'.$name : $flag.$name)."</a></li>\n";
788 }
789 }
790
791 $output .= $dropdown ? '</select>' : "</ul>\n";
792 echo $output;
793 }
794
795 } // class Polylang
796
797 if (class_exists("Polylang"))
798 new Polylang();
799
800 ?>
801