PluginProbe
Polylang / 2.3
Polylang v2.3
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / frontend / choose-lang.php

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

329 lines 10.7 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() || 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 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
65 /**
66 * Fires when the current language is defined
67 *
68 * @since 0.9.5
69 *
70 * @param string $slug current language code
71 * @param object $curlang current language object
72 */
73 do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang );
74 }
75
76 /**
77 * set a cookie to remember the language.
78 * possibility to set PLL_COOKIE to false will disable cookie although it will break some functionalities
79 *
80 * @since 1.5
81 */
82 public function maybe_setcookie() {
83 // check headers have not been sent to avoid ugly error
84 // cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris.
85 if ( ! headers_sent() && PLL_COOKIE !== false && ! empty( $this->curlang ) && ( ! isset( $_COOKIE[ PLL_COOKIE ] ) || $_COOKIE[ PLL_COOKIE ] != $this->curlang->slug ) && ! is_404() ) {
86
87 /**
88 * Filter the Polylang cookie duration
89 *
90 * @since 1.8
91 *
92 * @param int $duration cookie duration in seconds
93 */
94 $expiration = apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS );
95
96 setcookie(
97 PLL_COOKIE,
98 $this->curlang->slug,
99 time() + $expiration,
100 COOKIEPATH,
101 2 == $this->options['force_lang'] ? parse_url( $this->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN,
102 is_ssl()
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 requested
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_curlang_in_query( $GLOBALS['wp_query'] );
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 being 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_curlang_in_query( $query );
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 current language in the query
319 *
320 * @since 2.2
321 *
322 * @param object $query
323 */
324 protected function set_curlang_in_query( &$query ) {
325 $pll_query = new PLL_Query( $query, $this->model );
326 $pll_query->set_language( $this->curlang );
327 }
328 }
329