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

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

343 lines 11.3 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 ( Polylang::is_ajax_on_front() || ! wp_using_themes() ) {
36 $this->set_language( empty( $_REQUEST['lang'] ) ? $this->get_preferred_language() : $this->model->get_language( sanitize_key( $_REQUEST['lang'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
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 add_action( 'wp', array( $this, 'maybe_setcookie' ), 7 );
42 }
43
44 /**
45 * Writes language cookie
46 * Loads user defined translations
47 * Fires the action 'pll_language_defined'
48 *
49 * @since 1.2
50 *
51 * @param object $curlang current language
52 */
53 protected function set_language( $curlang ) {
54 // Don't set the language a second time
55 if ( isset( $this->curlang ) ) {
56 return;
57 }
58
59 // Final check in case $curlang has an unexpected value
60 // See https://wordpress.org/support/topic/detect-browser-language-sometimes-setting-null-language
61 $this->curlang = ( $curlang instanceof PLL_Language ) ? $curlang : $this->model->get_language( $this->options['default_lang'] );
62
63 $GLOBALS['text_direction'] = $this->curlang->is_rtl ? 'rtl' : 'ltr';
64 wp_styles()->text_direction = $GLOBALS['text_direction'];
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 * Setting PLL_COOKIE to false will disable cookie although it will break some functionalities
80 *
81 * @since 1.5
82 */
83 public function maybe_setcookie() {
84 // Don't set cookie in javascript when a cache plugin is active
85 // Check headers have not been sent to avoid ugly error
86 // Cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris.
87 if ( ! pll_is_cache_active() && ! headers_sent() && PLL_COOKIE !== false && ! empty( $this->curlang ) && ( ! isset( $_COOKIE[ PLL_COOKIE ] ) || $_COOKIE[ PLL_COOKIE ] != $this->curlang->slug ) && ! is_404() ) {
88
89 /**
90 * Filter the Polylang cookie duration
91 * /!\ this filter may be fired *before* the theme is loaded
92 *
93 * @since 1.8
94 *
95 * @param int $duration cookie duration in seconds
96 */
97 $expiration = apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS );
98
99 setcookie(
100 PLL_COOKIE,
101 $this->curlang->slug,
102 time() + $expiration,
103 COOKIEPATH,
104 2 == $this->options['force_lang'] ? wp_parse_url( $this->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN,
105 is_ssl()
106 );
107 }
108 }
109
110 /**
111 * Get the preferred language according to the browser preferences
112 * Code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header
113 *
114 * @since 1.8
115 *
116 * @return string|bool the preferred language slug or false
117 */
118 public function get_preferred_browser_language() {
119 $accept_langs = array();
120
121 if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) {
122 // Break up string into pieces ( languages and q factors )
123 preg_match_all(
124 '/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*((?>1|0)(?>\.[0-9]+)?))?/i',
125 sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ),
126 $lang_parse
127 );
128
129 $k = $lang_parse[1];
130 $v = $lang_parse[4];
131
132 if ( $n = count( $k ) ) {
133 // Set default to 1 for any without q factor
134 foreach ( $v as $key => $val ) {
135 if ( '' === $val || (float) $val > 1 ) {
136 $v[ $key ] = 1;
137 }
138 }
139
140 // Bubble sort ( need a stable sort for Android, so can't use a PHP sort function )
141 if ( $n > 1 ) {
142 for ( $i = 2; $i <= $n; $i++ ) {
143 for ( $j = 0; $j <= $n - 2; $j++ ) {
144 if ( $v[ $j ] < $v[ $j + 1 ] ) {
145 // Swap values
146 $temp = $v[ $j ];
147 $v[ $j ] = $v[ $j + 1 ];
148 $v[ $j + 1 ] = $temp;
149 // Swap keys
150 $temp = $k[ $j ];
151 $k[ $j ] = $k[ $j + 1 ];
152 $k[ $j + 1 ] = $temp;
153 }
154 }
155 }
156 }
157 $accept_langs = array_combine( $k, $v );
158 }
159 }
160
161 $accept_langs = wp_list_filter( $accept_langs, array( '0' ), 'NOT' ); // Remove languages markes as unacceptable (q=0).
162
163 $languages = $this->model->get_languages_list( array( 'hide_empty' => true ) ); // Hides languages with no post
164
165 /**
166 * Filter the list of languages to use to match the browser preferences
167 *
168 * @since 1.9.3
169 *
170 * @param array $languages array of PLL_Language objects
171 */
172 $languages = apply_filters( 'pll_languages_for_browser_preferences', $languages );
173
174 // Looks through sorted list and use first one that matches our language list
175 foreach ( array_keys( $accept_langs ) as $accept_lang ) {
176 // First loop to match the exact locale
177 foreach ( $languages as $language ) {
178 if ( 0 === strcasecmp( $accept_lang, $language->get_locale( 'display' ) ) ) {
179 return $language->slug;
180 }
181 }
182
183 // Second loop to match the language set
184 foreach ( $languages as $language ) {
185 if ( 0 === stripos( $accept_lang, $language->slug ) || 0 === stripos( $language->get_locale( 'display' ), $accept_lang ) ) {
186 return $language->slug;
187 }
188 }
189 }
190 return false;
191 }
192
193 /**
194 * Returns the language according to browser preference or the default language
195 *
196 * @since 0.1
197 *
198 * @return object browser preferred language or default language
199 */
200 public function get_preferred_language() {
201 // check first if the user was already browsing this site
202 if ( isset( $_COOKIE[ PLL_COOKIE ] ) ) {
203 return $this->model->get_language( sanitize_key( $_COOKIE[ PLL_COOKIE ] ) );
204 }
205
206 /**
207 * Filter the visitor's preferred language (normally set first by cookie
208 * if this is not the first visit, then by the browser preferences).
209 * If no preferred language has been found or set by this filter,
210 * Polylang fallbacks to the default language
211 *
212 * @since 1.0
213 *
214 * @param string $language preferred language code
215 */
216 $slug = apply_filters( 'pll_preferred_language', $this->options['browser'] ? $this->get_preferred_browser_language() : false );
217
218 // 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
219 return ( $lang = $this->model->get_language( $slug ) ) ? $lang : $this->model->get_language( $this->options['default_lang'] );
220 }
221
222 /**
223 * Sets the language when home page is requested
224 *
225 * @since 1.2
226 */
227 protected function home_language() {
228 // Test referer in case PLL_COOKIE is set to false. Since WP 3.6.1, wp_get_referer() validates the host which is exactly what we want
229 // Thanks to Ov3rfly http://wordpress.org/support/topic/enhance-feature-when-front-page-is-visited-set-language-according-to-browser
230 $language = $this->options['hide_default'] && ( wp_get_referer() || ! $this->options['browser'] ) ?
231 $this->model->get_language( $this->options['default_lang'] ) :
232 $this->get_preferred_language(); // Sets the language according to browser preference or default language
233 $this->set_language( $language );
234 }
235
236 /**
237 * To call when the home page has been requested
238 * Make sure to call this after 'setup_theme' has been fired as we need $wp_query
239 * Performs a redirection to the home page in the current language if needed
240 *
241 * @since 0.9
242 */
243 public function home_requested() {
244 // We are already on the right page
245 if ( $this->options['default_lang'] == $this->curlang->slug && $this->options['hide_default'] ) {
246 $this->set_curlang_in_query( $GLOBALS['wp_query'] );
247
248 /**
249 * Fires when the site root page is requested
250 *
251 * @since 1.8
252 */
253 do_action( 'pll_home_requested' );
254 }
255 // Redirect to the home page in the right language
256 // Test to avoid crash if get_home_url returns something wrong
257 // FIXME why this happens? http://wordpress.org/support/topic/polylang-crashes-1
258 // Don't redirect if $_POST is not empty as it could break other plugins
259 elseif ( is_string( $redirect = $this->curlang->home_url ) && empty( $_POST ) ) { // phpcs:ignore WordPress.Security.NonceVerification
260 // Don't forget the query string which may be added by plugins
261 $query_string = wp_parse_url( pll_get_requested_url(), PHP_URL_QUERY );
262 if ( ! empty( $query_string ) ) {
263 $redirect .= ( $this->links_model->using_permalinks ? '?' : '&' ) . $query_string;
264 }
265
266 /**
267 * When a visitor reaches the site home, Polylang redirects to the home page in the correct language.
268 * This filter allows plugins to modify the redirected url or prevent this redirection
269 * /!\ this filter may be fired *before* the theme is loaded
270 *
271 * @since 1.1.1
272 *
273 * @param string $redirect the url the visitor will be redirected to
274 */
275 if ( $redirect = apply_filters( 'pll_redirect_home', $redirect ) ) {
276 $this->maybe_setcookie();
277 wp_safe_redirect( $redirect, 302, POLYLANG );
278 exit;
279 }
280 }
281 }
282
283 /**
284 * Set the language when posting a comment
285 *
286 * @since 0.8.4
287 *
288 * @param int $post_id the post being commented
289 */
290 public function pre_comment_on_post( $post_id ) {
291 $this->set_language( $this->model->post->get_language( $post_id ) );
292 }
293
294 /**
295 * Modifies some main query vars for home page and page for posts
296 * to enable one home page ( and one page for posts ) per language
297 *
298 * @since 1.2
299 *
300 * @param object $query instance of WP_Query
301 */
302 public function parse_main_query( $query ) {
303 if ( ! $query->is_main_query() ) {
304 return;
305 }
306
307 /**
308 * This filter allows to set the language based on information contained in the main query
309 *
310 * @since 1.8
311 *
312 * @param bool|object $lang false or language object
313 * @param object $query WP_Query object
314 */
315 if ( $lang = apply_filters( 'pll_set_language_from_query', false, $query ) ) {
316 $this->set_language( $lang );
317 $this->set_curlang_in_query( $query );
318 }
319
320 // sets is_home on translated home page when it displays posts
321 // is_home must be true on page 2, 3... too
322 // as well as when searching an empty string: http://wordpress.org/support/topic/plugin-polylang-polylang-breaks-search-in-spun-theme
323 elseif ( ( count( $query->query ) == 1 || ( is_paged() && count( $query->query ) == 2 ) || ( isset( $query->query['s'] ) && ! $query->query['s'] ) ) && $lang = get_query_var( 'lang' ) ) {
324 $lang = $this->model->get_language( $lang );
325 $this->set_language( $lang ); // sets the language now otherwise it will be too late to filter sticky posts !
326 $query->is_home = true;
327 $query->is_archive = $query->is_tax = false;
328 }
329 }
330
331 /**
332 * Sets the current language in the query
333 *
334 * @since 2.2
335 *
336 * @param object $query
337 */
338 protected function set_curlang_in_query( &$query ) {
339 $pll_query = new PLL_Query( $query, $this->model );
340 $pll_query->set_language( $this->curlang );
341 }
342 }
343