PluginProbe
Polylang / 2.0.12
Polylang v2.0.12
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 / frontend / choose-lang.php

choose-lang.php in Polylang 2.0.12, at frontend/choose-lang.php

333 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * base class to choose the language
5 *
6 * @since 1.2
7 */
8 abstract class PLL_Choose_Lang {
9 public $links_model, $model, $options;
10 public $curlang;
11
12 /**
13 * constructor
14 *
15 * @since 1.2
16 *
17 * @param object $polylang
18 */
19 public function __construct( &$polylang ) {
20 $this->links_model = &$polylang->links_model;
21 $this->model = &$polylang->model;
22 $this->options = &$polylang->options;
23
24 $this->curlang = &$polylang->curlang;
25 }
26
27 /**
28 * sets the language for ajax requests
29 * and setup actions
30 * any child class must call this method if it overrides it
31 *
32 * @since 1.8
33 */
34 public function init() {
35 if ( PLL_AJAX_ON_FRONT || false === stripos( $_SERVER['SCRIPT_FILENAME'], 'index.php' ) ) {
36 $this->set_language( empty( $_REQUEST['lang'] ) ? $this->get_preferred_language() : $this->model->get_language( $_REQUEST['lang'] ) );
37 }
38
39 add_action( 'pre_comment_on_post', array( $this, 'pre_comment_on_post' ) ); // sets the language of comment
40 add_action( 'parse_query', array( $this, 'parse_main_query' ), 2 ); // sets the language in special cases
41 }
42
43 /**
44 * writes language cookie
45 * loads user defined translations
46 * fires the action 'pll_language_defined'
47 *
48 * @since 1.2
49 *
50 * @param object $curlang current language
51 */
52 protected function set_language( $curlang ) {
53 // don't set the language a second time
54 if ( isset( $this->curlang ) ) {
55 return;
56 }
57
58 // final check in case $curlang has an unexpected value
59 // see https://wordpress.org/support/topic/detect-browser-language-sometimes-setting-null-language
60 $this->curlang = ( $curlang instanceof PLL_Language ) ? $curlang : $this->model->get_language( $this->options['default_lang'] );
61
62 $this->maybe_setcookie();
63
64 $GLOBALS['text_direction'] = $this->curlang->is_rtl ? 'rtl' : 'ltr';
65
66 /**
67 * Fires when the current language is defined
68 *
69 * @since 0.9.5
70 *
71 * @param string $slug current language code
72 * @param object $curlang current language object
73 */
74 do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
75 }
76
77 /**
78 * set a cookie to remember the language.
79 * possibility to set PLL_COOKIE to false will disable cookie although it will break some functionalities
80 *
81 * @since 1.5
82 */
83 protected function maybe_setcookie() {
84 // check headers have not been sent to avoid ugly error
85 // cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris.
86 if ( ! headers_sent() && PLL_COOKIE !== false && ( ! isset( $_COOKIE[ PLL_COOKIE ] ) || $_COOKIE[ PLL_COOKIE ] != $this->curlang->slug ) ) {
87
88 /**
89 * Filter the Polylang cookie duration
90 *
91 * @since 1.8
92 *
93 * @param int $duration cookie duration in seconds
94 */
95 $expiration = apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS );
96
97 setcookie(
98 PLL_COOKIE,
99 $this->curlang->slug,
100 time() + $expiration,
101 COOKIEPATH,
102 2 == $this->options['force_lang'] ? parse_url( $this->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN
103 );
104 }
105 }
106
107 /**
108 * get the preferred language according to the browser preferences
109 * code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
110 *
111 * @since 1.8
112 *
113 * @return string|bool the preferred language slug or false
114 */
115 public function get_preferred_browser_language() {
116 $accept_langs = array();
117
118 if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
119 // break up string into pieces ( languages and q factors )
120 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 );
121
122 $k = $lang_parse[1];
123 $v = $lang_parse[4];
124
125 if ( $n = count( $k ) ) {
126 // set default to 1 for any without q factor
127 foreach ( $v as $key => $val ) {
128 if ( '' === $val ) {
129 $v[ $key ] = 1;
130 }
131 }
132
133 // bubble sort ( need a stable sort for Android, so can't use a PHP sort function )
134 if ( $n > 1 ) {
135 for ( $i = 2; $i <= $n; $i++ ) {
136 for ( $j = 0; $j <= $n - 2; $j++ ) {
137 if ( $v[ $j ] < $v[ $j + 1 ] ) {
138 // swap values
139 $temp = $v[ $j ];
140 $v[ $j ] = $v[ $j + 1 ];
141 $v[ $j + 1 ] = $temp;
142 //swap keys
143 $temp = $k[ $j ];
144 $k[ $j ] = $k[ $j + 1 ];
145 $k[ $j + 1 ] = $temp;
146 }
147 }
148 }
149 }
150 $accept_langs = array_combine( $k,$v );
151 }
152 }
153
154 $languages = $this->model->get_languages_list( array( 'hide_empty' => true ) ); // hides languages with no post
155
156 /**
157 * Filter the list of languages to use to match the browser preferences
158 *
159 * @since 1.9.3
160 *
161 * @param array $languages array of PLL_Language objects
162 */
163 $languages = apply_filters( 'pll_languages_for_browser_preferences', $languages );
164
165 // looks through sorted list and use first one that matches our language list
166 foreach ( array_keys( $accept_langs ) as $accept_lang ) {
167 // first loop to match the exact locale
168 foreach ( $languages as $language ) {
169 if ( 0 === strcasecmp( $accept_lang, $language->get_locale( 'display' ) ) ) {
170 return $language->slug;
171 }
172 }
173
174 // second loop to match the language set
175 foreach ( $languages as $language ) {
176 if ( 0 === stripos( $accept_lang, $language->slug ) || 0 === stripos( $language->get_locale( 'display' ), $accept_lang ) ) {
177 return $language->slug;
178 }
179 }
180 }
181 return false;
182 }
183
184 /**
185 * returns the language according to browser preference or the default language
186 *
187 * @since 0.1
188 *
189 * @return object browser preferred language or default language
190 */
191 public function get_preferred_language() {
192 // check first if the user was already browsing this site
193 if ( isset( $_COOKIE[ PLL_COOKIE ] ) ) {
194 return $this->model->get_language( $_COOKIE[ PLL_COOKIE ] );
195 }
196
197 /**
198 * Filter the visitor's preferred language (normally set first by cookie
199 * if this is not the first visit, then by the browser preferences).
200 * If no preferred language has been found or set by this filter,
201 * Polylang fallbacks to the default language
202 *
203 * @since 1.0
204 *
205 * @param string $language preferred language code
206 */
207 $slug = apply_filters( 'pll_preferred_language', $this->options['browser'] ? $this->get_preferred_browser_language() : false );
208
209 // 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
210 return ( $lang = $this->model->get_language( $slug ) ) ? $lang : $this->model->get_language( $this->options['default_lang'] );
211 }
212
213 /**
214 * sets the language when home page is resquested
215 *
216 * @since 1.2
217 */
218 protected function home_language() {
219 // test referer in case PLL_COOKIE is set to false
220 // thanks to Ov3rfly http://wordpress.org/support/topic/enhance-feature-when-front-page-is-visited-set-language-according-to-browser
221 $language = $this->options['hide_default'] && ( ( isset( $_SERVER['HTTP_REFERER'] ) && in_array( parse_url( $_SERVER['HTTP_REFERER'], PHP_URL_HOST ), $this->links_model->get_hosts() ) ) || ! $this->options['browser'] ) ?
222 $this->model->get_language( $this->options['default_lang'] ) :
223 $this->get_preferred_language(); // sets the language according to browser preference or default language
224 $this->set_language( $language );
225 }
226
227 /**
228 * to call when the home page has been requested
229 * make sure to call this after 'setup_theme' has been fired as we need $wp_query
230 * performs a redirection to the home page in the current language if needed
231 *
232 * @since 0.9
233 */
234 public function home_requested() {
235 // we are already on the right page
236 if ( $this->options['default_lang'] == $this->curlang->slug && $this->options['hide_default'] ) {
237 $this->set_lang_query_var( $GLOBALS['wp_query'], $this->curlang );
238
239 /**
240 * Fires when the site root page is requested
241 *
242 * @since 1.8
243 */
244 do_action( 'pll_home_requested' );
245 }
246 // redirect to the home page in the right language
247 // test to avoid crash if get_home_url returns something wrong
248 // FIXME why this happens? http://wordpress.org/support/topic/polylang-crashes-1
249 // don't redirect if $_POST is not empty as it could break other plugins
250 // don't forget the query string which may be added by plugins
251 elseif ( is_string( $redirect = $this->curlang->home_url ) && empty( $_POST ) ) {
252 $redirect = empty( $_SERVER['QUERY_STRING'] ) ? $redirect : $redirect . ( $this->links_model->using_permalinks ? '?' : '&' ) . $_SERVER['QUERY_STRING'];
253
254 /**
255 * When a visitor reaches the site home, Polylang redirects to the home page in the correct language.
256 * This filter allows plugins to modify the redirected url or prevent this redirection
257 *
258 * @since 1.1.1
259 *
260 * @param string $redirect the url the visitor will be redirected to
261 */
262 if ( $redirect = apply_filters( 'pll_redirect_home', $redirect ) ) {
263 wp_redirect( $redirect );
264 exit;
265 }
266 }
267 }
268
269 /**
270 * set the language when posting a comment
271 *
272 * @since 0.8.4
273 *
274 * @param int $post_id the post beeing commented
275 */
276 public function pre_comment_on_post( $post_id ) {
277 $this->set_language( $this->model->post->get_language( $post_id ) );
278 }
279
280 /**
281 * modifies some main query vars for home page and page for posts
282 * to enable one home page ( and one page for posts ) per language
283 *
284 * @since 1.2
285 *
286 * @param object $query instance of WP_Query
287 */
288 public function parse_main_query( $query ) {
289 if ( ! $query->is_main_query() ) {
290 return;
291 }
292
293 /**
294 * This filter allows to set the language based on information contained in the main query
295 *
296 * @since 1.8
297 *
298 * @param bool|object $lang false or language object
299 * @param object $query WP_Query object
300 */
301 if ( $lang = apply_filters( 'pll_set_language_from_query', false, $query ) ) {
302 $this->set_language( $lang );
303 $this->set_lang_query_var( $query, $this->curlang );
304 }
305
306 // sets is_home on translated home page when it displays posts
307 // is_home must be true on page 2, 3... too
308 // as well as when searching an empty string: http://wordpress.org/support/topic/plugin-polylang-polylang-breaks-search-in-spun-theme
309 if ( 'posts' == get_option( 'show_on_front' ) && ( count( $query->query ) == 1 || ( is_paged() && count( $query->query ) == 2 ) || ( isset( $query->query['s'] ) && ! $query->query['s'] ) ) && $lang = get_query_var( 'lang' ) ) {
310 $lang = $this->model->get_language( $lang );
311 $this->set_language( $lang ); // sets the language now otherwise it will be too late to filter sticky posts !
312 $query->is_home = true;
313 $query->is_archive = $query->is_tax = false;
314 }
315 }
316
317 /**
318 * sets the language in query
319 * optimized for ( needs ) WP 3.5+
320 *
321 * @since 1.3
322 */
323 public function set_lang_query_var( &$query, $lang ) {
324 // defining directly the tax_query ( rather than setting 'lang' avoids transforming the query by WP )
325 $query->query_vars['tax_query'][] = array(
326 'taxonomy' => 'language',
327 'field' => 'term_taxonomy_id', // since WP 3.5
328 'terms' => $lang->term_taxonomy_id,
329 'operator' => 'IN',
330 );
331 }
332 }
333