PluginProbe
Polylang / 3.8.9
Polylang v3.8.9
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 / src / switcher.php

switcher.php in Polylang 3.8.9, at src/switcher.php

296 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Polylang
4 */
5
6 /**
7 * A class to display a language switcher on frontend
8 *
9 * @since 1.2
10 */
11 class PLL_Switcher {
12 public const DEFAULTS = array(
13 'dropdown' => 0, // Display as list and not as dropdown.
14 'echo' => 1, // Echoes the list.
15 'hide_if_empty' => 1, // Hides languages with no posts (or pages).
16 'show_flags' => 0, // Don't show flags.
17 'show_names' => 1, // Show language names.
18 'display_names_as' => 'name', // Display the language name.
19 'force_home' => 0, // Tries to find a translation.
20 'hide_if_no_translation' => 0, // Don't hide the link if there is no translation.
21 'hide_current' => 0, // Don't hide the current language.
22 'post_id' => null, // Link to the translations of the current page.
23 'raw' => 0, // Build the language switcher.
24 'item_spacing' => 'preserve', // Preserve whitespace between list items.
25 'admin_render' => 0, // Make the switcher in a frontend context.
26 'admin_current_lang' => null, // Use the global current language.
27 );
28
29 /**
30 * @var PLL_Links|null
31 */
32 protected $links;
33
34 /**
35 * Returns options available for the language switcher - menu or widget
36 * either strings to display the options or default values
37 *
38 * @since 0.7
39 *
40 * @param string $type optional either 'menu', 'widget' or 'block', defaults to 'widget'
41 * @param string $key optional either 'string' or 'default', defaults to 'string'
42 * @return array list of switcher options strings or default values
43 */
44 public static function get_switcher_options( $type = 'widget', $key = 'string' ) {
45 $options = array(
46 'dropdown' => array( 'string' => __( 'Displays as a dropdown', 'polylang' ), 'default' => 0 ),
47 'show_names' => array( 'string' => __( 'Displays language names', 'polylang' ), 'default' => 1 ),
48 'show_flags' => array( 'string' => __( 'Displays flags', 'polylang' ), 'default' => 0 ),
49 'force_home' => array( 'string' => __( 'Forces link to front page', 'polylang' ), 'default' => 0 ),
50 'hide_current' => array( 'string' => __( 'Hides the current language', 'polylang' ), 'default' => 0 ),
51 'hide_if_no_translation' => array( 'string' => __( 'Hides languages with no translation', 'polylang' ), 'default' => 0 ),
52 );
53 return wp_list_pluck( $options, $key );
54 }
55
56 /**
57 * Returns the current language code.
58 *
59 * @since 3.0
60 *
61 * @param array $args Arguments passed to {@see PLL_Switcher::the_languages()}.
62 * @return string
63 */
64 protected function get_current_language( $args ) {
65 if ( $args['admin_current_lang'] ) {
66 return $args['admin_current_lang'];
67 }
68
69 if ( isset( $this->links->curlang ) ) {
70 return $this->links->curlang->slug;
71 }
72
73 return $this->links->options['default_lang'];
74 }
75
76 /**
77 * Returns the link for a given language.
78 *
79 * @since 3.0
80 *
81 * @param PLL_Language $language Language.
82 * @param array $args Arguments passed to {@see PLL_Switcher::the_languages()}.
83 * @return string|null
84 */
85 protected function get_link( $language, $args ) {
86 global $post;
87
88 // Priority to the post passed in parameters.
89 if ( null !== $args['post_id'] ) {
90 $tr_id = $this->links->model->post->get( $args['post_id'], $language );
91 if ( $tr_id && $this->links->model->post->current_user_can_read( $tr_id ) ) {
92 return get_permalink( $tr_id );
93 }
94 }
95
96 // If we are on frontend.
97 if ( $this->links instanceof PLL_Frontend_Links ) {
98 return $this->links->get_translation_url( $language );
99 }
100
101 // For blocks in posts in REST requests.
102 if ( $post instanceof WP_Post ) {
103 $tr_id = $this->links->model->post->get( $post->ID, $language );
104 if ( $tr_id && $this->links->model->post->current_user_can_read( $tr_id ) ) {
105 return get_permalink( $tr_id );
106 }
107 }
108
109 return null;
110 }
111
112 /**
113 * Get the language elements for use in a walker
114 *
115 * @since 1.2
116 *
117 * @param array $args Arguments passed to {@see PLL_Switcher::the_languages()}.
118 * @return array Language switcher elements.
119 */
120 protected function get_elements( $args ) {
121 $first = true;
122 $out = array();
123
124 $filter = $args['hide_if_empty'] ? 'hide_empty' : '';
125 foreach ( $this->links->model->languages->filter( $filter )->get_list() as $language ) {
126 $id = (int) $language->term_id;
127 $order = (int) $language->term_group;
128 $slug = $language->slug;
129 $locale = $language->get_locale( 'display' );
130 $is_rtl = $language->is_rtl;
131 $item_classes = array( 'lang-item', 'lang-item-' . $id, 'lang-item-' . esc_attr( $slug ) );
132 $classes = isset( $args['classes'] ) && is_array( $args['classes'] ) ?
133 array_merge(
134 $item_classes,
135 $args['classes']
136 ) :
137 $item_classes;
138 $link_classes = $args['link_classes'] ?? array();
139 $current_lang = $this->get_current_language( $args ) === $slug;
140
141 if ( $current_lang ) {
142 if ( $args['hide_current'] && ! ( $args['dropdown'] && ! $args['raw'] ) ) {
143 continue; // Hide current language except for dropdown
144 } else {
145 $classes[] = 'current-lang';
146 }
147 }
148
149 $url = $this->get_link( $language, $args );
150
151 if ( $no_translation = empty( $url ) ) {
152 $classes[] = 'no-translation';
153 }
154
155 /**
156 * Filter the link in the language switcher
157 *
158 * @since 0.7
159 *
160 * @param string|null $url The link, null if no translation was found.
161 * @param string $slug The language code.
162 * @param string $locale The language locale
163 */
164 $url = apply_filters( 'pll_the_language_link', $url, $slug, $language->locale );
165
166 // Hide if no translation exists
167 if ( empty( $url ) && $args['hide_if_no_translation'] ) {
168 continue;
169 }
170
171 $url = empty( $url ) || $args['force_home'] ? $this->links->get_home_url( $language ) : $url; // If the page is not translated, link to the home page
172
173 $name = $args['show_names'] || ! $args['show_flags'] || $args['raw'] ? ( 'slug' == $args['display_names_as'] ? $slug : $language->name ) : '';
174
175 if ( $args['raw'] && ! $args['show_flags'] ) {
176 $flag = $language->get_display_flag_url();
177 } elseif ( $args['show_flags'] ) {
178 $flag = $language->get_display_flag( empty( $args['show_names'] ) ? 'alt' : 'no-alt' );
179 } else {
180 $flag = '';
181 }
182
183 if ( $first ) {
184 $classes[] = 'lang-item-first';
185 $first = false;
186 }
187
188 $out[ $slug ] = compact( 'id', 'order', 'slug', 'locale', 'is_rtl', 'name', 'url', 'flag', 'current_lang', 'no_translation', 'classes', 'link_classes' );
189 }
190
191 return $out;
192 }
193
194 /**
195 * Displays a language switcher
196 * or returns the raw elements to build a custom language switcher.
197 *
198 * @since 0.1
199 *
200 * @param PLL_Links $links Instance of PLL_Links.
201 * @param array $args {
202 * Optional array of arguments.
203 *
204 * @type int $dropdown The list is displayed as dropdown if set, defaults to 0.
205 * @type int $echo Echoes the list if set to 1, defaults to 1.
206 * @type int $hide_if_empty Hides languages with no posts ( or pages ) if set to 1, defaults to 1.
207 * @type int $show_flags Displays flags if set to 1, defaults to 0.
208 * @type int $show_names Shows language names if set to 1, defaults to 1.
209 * @type string $display_names_as Whether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name.
210 * @type int $force_home Will always link to home in translated language if set to 1, defaults to 0.
211 * @type int $hide_if_no_translation Hides the link if there is no translation if set to 1, defaults to 0.
212 * @type int $hide_current Hides the current language if set to 1, defaults to 0.
213 * @type int $post_id Returns links to the translations of the post defined by post_id if set, defaults not set.
214 * @type int $raw Return a raw array instead of html markup if set to 1, defaults to 0.
215 * @type string $item_spacing Whether to preserve or discard whitespace between list items, valid options are 'preserve' and 'discard', defaults to 'preserve'.
216 * @type int $admin_render Allows to force the current language code in an admin context if set, default to 0. Need to set the admin_current_lang argument below.
217 * @type string $admin_current_lang The current language code in an admin context. Need to set the admin_render to 1, defaults not set.
218 * @type string[] $classes A list of CSS classes to set to each elements outputted.
219 * @type string[] $link_classes A list of CSS classes to set to each link outputted.
220 * }
221 * @return string|array either the html markup of the switcher or the raw elements to build a custom language switcher
222 */
223 public function the_languages( $links, $args = array() ) {
224
225 $this->links = $links;
226 $args = wp_parse_args( $args, self::DEFAULTS );
227
228 /**
229 * Filter the arguments of the 'pll_the_languages' template tag
230 *
231 * @since 1.5
232 *
233 * @param array $args
234 */
235 $args = apply_filters( 'pll_the_languages_args', $args );
236
237 // Force not to hide the language for the widget preview even if the option is checked.
238 if ( $this->links instanceof PLL_Admin_Links ) {
239 $args['hide_if_no_translation'] = 0;
240 }
241
242 // Prevents showing empty options in `<select>`.
243 if ( $args['dropdown'] && ! $args['raw'] ) {
244 $args['show_names'] = 1;
245 }
246
247 $elements = $this->get_elements( $args );
248
249 if ( $args['raw'] ) {
250 return $elements;
251 }
252
253 if ( $args['dropdown'] ) {
254 $args['name'] = 'lang_choice_' . $args['dropdown'];
255 $args['class'] = 'pll-switcher-select';
256 $args['value'] = 'url';
257 $args['selected'] = $this->get_link( $this->links->model->get_language( $this->get_current_language( $args ) ), $args );
258 $walker = new PLL_Walker_Dropdown();
259 } else {
260 $walker = new PLL_Walker_List();
261 }
262
263 // Cast each element to stdClass because $walker::walk() expects an array of objects.
264 foreach ( $elements as $i => $element ) {
265 $elements[ $i ] = (object) $element;
266 }
267
268 /**
269 * Filter the whole html markup returned by the 'pll_the_languages' template tag
270 *
271 * @since 0.8
272 *
273 * @param string $html html returned/outputted by the template tag
274 * @param array $args arguments passed to the template tag
275 */
276 $out = apply_filters( 'pll_the_languages', $walker->walk( $elements, -1, $args ), $args );
277
278 // Javascript to switch the language when using a dropdown list.
279 if ( $args['dropdown'] && 0 === $args['admin_render'] ) {
280 // Accept only few valid characters for the urls_x variable name (as the widget id includes '-' which is invalid).
281 $out .= sprintf(
282 '<script%1$s>
283 document.getElementById( "%2$s" ).addEventListener( "change", function ( event ) { location.href = event.currentTarget.value; } )
284 </script>',
285 current_theme_supports( 'html5', 'script' ) ? '' : ' type="text/javascript"',
286 esc_js( $args['name'] )
287 );
288 }
289
290 if ( $args['echo'] ) {
291 echo $out; // phpcs:ignore WordPress.Security.EscapeOutput
292 }
293 return $out;
294 }
295 }
296