Polylang - Version 2.1.2

Version Description

(2017-03-09) =

  • Pro: Add filter 'pll_xdata_nonce_life'
  • Pro: Fix translation of WooCommerce product attribute slug
  • Pro: Fix product synchronization in WooCommerce 2.7
  • Pro: Fix error message when bulk trashing synchronized posts
  • Add option to discard item spacing in the output of pll_the_languages() ( Props Ceslav Przywara ) #93 #95
  • Add as, dzo, kab, km, ml_IN, nl_BE, pa_IN, rhg, sah, ta_IN, tah, te, tt_RU to the predefined list of languages
  • Update plugin updater class to 1.6.10
  • Fix: Remove the dependency to is_ssl() to detect the language in the url ( language set from the directory name )
  • Fix issue with secondary level domains
  • Fix strings not translated in emails
  • Fix incorrect usage of add_action() ( Props Peter J. Herrel ) #103
  • Fix wrong redirect in customizer in WP 4.7
Download this release

Release Info

Developer Chouby
Plugin Icon 128x128 Polylang
Version 2.1.2
Comparing to
See all releases

Code changes from version 2.1.1 to 2.1.2

admin/admin-base.php CHANGED
@@ -261,7 +261,8 @@ class PLL_Admin_Base extends PLL_Base {
261
 
262
  // Inform that the admin language has been set
263
  // Only if the admin language is one of the Polylang defined language
264
- if ( $curlang = $this->model->get_language( get_locale() ) ) {
 
265
  // FIXME: Backward compatibility with WP < 4.7
266
  if ( version_compare( $GLOBALS['wp_version'], '4.7alpha', '<' ) ) {
267
  $GLOBALS['text_direction'] = $curlang->is_rtl ? 'rtl' : 'ltr'; // force text direction according to language setting
261
 
262
  // Inform that the admin language has been set
263
  // Only if the admin language is one of the Polylang defined language
264
+ // FIXME test get_user_locale for backward compatibility with WP 4.7
265
+ if ( $curlang = $this->model->get_language( function_exists( 'get_user_locale' ) ? get_user_locale() : get_locale() ) ) {
266
  // FIXME: Backward compatibility with WP < 4.7
267
  if ( version_compare( $GLOBALS['wp_version'], '4.7alpha', '<' ) ) {
268
  $GLOBALS['text_direction'] = $curlang->is_rtl ? 'rtl' : 'ltr'; // force text direction according to language setting
admin/admin-nav-menu.php CHANGED
@@ -65,7 +65,7 @@ class PLL_Admin_Nav_Menu extends PLL_Nav_Menu {
65
 
66
  <div id="posttype-lang-switch" class="posttypediv">
67
  <div id="tabs-panel-lang-switch" class="tabs-panel tabs-panel-active">
68
- <ul id ="lang-switch-checklist" class="categorychecklist form-no-clear">
69
  <li>
70
  <label class="menu-item-title">
71
  <input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-object-id]" value="-1"> <?php esc_html_e( 'Language switcher', 'polylang' ); ?>
65
 
66
  <div id="posttype-lang-switch" class="posttypediv">
67
  <div id="tabs-panel-lang-switch" class="tabs-panel tabs-panel-active">
68
+ <ul id="lang-switch-checklist" class="categorychecklist form-no-clear">
69
  <li>
70
  <label class="menu-item-title">
71
  <input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo $_nav_menu_placeholder; ?>][menu-item-object-id]" value="-1"> <?php esc_html_e( 'Language switcher', 'polylang' ); ?>
frontend/frontend-filters-links.php CHANGED
@@ -360,7 +360,7 @@ class PLL_Frontend_Filters_Links extends PLL_Filters_Links {
360
 
361
  // If the default language code is not hidden and the static front page url contains the page name
362
  // the customizer lands here and the code below would redirect to the list of posts
363
- if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) {
364
  return;
365
  }
366
 
360
 
361
  // If the default language code is not hidden and the static front page url contains the page name
362
  // the customizer lands here and the code below would redirect to the list of posts
363
+ if ( is_customize_preview() ) {
364
  return;
365
  }
366
 
include/api.php CHANGED
@@ -6,16 +6,17 @@
6
  * List of parameters accepted in $args:
7
  *
8
  * dropdown => displays a dropdown if set to 1, defaults to 0
9
- * echo => echoes the the switcher if set to 1 ( default )
10
  * hide_if_empty => hides languages with no posts ( or pages ) if set to 1 ( default )
11
  * show_flags => shows flags if set to 1, defaults to 0
12
  * show_names => shows languages names if set to 1 ( default )
13
- * display_names_as => whether to display the language name or code. valid options are 'slug' and 'name'
14
  * force_home => forces linking to the home page is set to 1, defaults to 0
15
  * hide_if_no_translation => hides the link if there is no translation if set to 1, defaults to 0
16
  * hide_current => hides the current language if set to 1, defaults to 0
17
  * post_id => if not null, link to translations of post defined by post_id, defaults to null
18
  * raw => set this to true to build your own custom language switcher, defaults to 0
 
19
  *
20
  * @since 0.5
21
  *
@@ -122,11 +123,7 @@ function pll_register_string( $name, $string, $context = 'polylang', $multiline
122
  * @return string the string translation in the current language
123
  */
124
  function pll__( $string ) {
125
- if ( ! did_action( 'pll_language_defined' ) || ! is_scalar( $string ) ) { // No need for translation
126
- return $string;
127
- }
128
-
129
- return __( $string, 'pll_string' );
130
  }
131
 
132
  /**
6
  * List of parameters accepted in $args:
7
  *
8
  * dropdown => displays a dropdown if set to 1, defaults to 0
9
+ * echo => echoes the switcher if set to 1 ( default )
10
  * hide_if_empty => hides languages with no posts ( or pages ) if set to 1 ( default )
11
  * show_flags => shows flags if set to 1, defaults to 0
12
  * show_names => shows languages names if set to 1 ( default )
13
+ * display_names_as => whether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name
14
  * force_home => forces linking to the home page is set to 1, defaults to 0
15
  * hide_if_no_translation => hides the link if there is no translation if set to 1, defaults to 0
16
  * hide_current => hides the current language if set to 1, defaults to 0
17
  * post_id => if not null, link to translations of post defined by post_id, defaults to null
18
  * raw => set this to true to build your own custom language switcher, defaults to 0
19
+ * item_spacing => whether to preserve or discard whitespace between list items, valid options are 'preserve' and 'discard', defaults to preserve
20
  *
21
  * @since 0.5
22
  *
123
  * @return string the string translation in the current language
124
  */
125
  function pll__( $string ) {
126
+ return is_scalar( $string ) ? __( $string, 'pll_string' ) : $string;
 
 
 
 
127
  }
128
 
129
  /**
include/links-directory.php CHANGED
@@ -99,12 +99,15 @@ class PLL_Links_Directory extends PLL_Links_Permalinks {
99
  */
100
  public function get_language_from_url( $url = '' ) {
101
  if ( empty( $url ) ) {
102
- $url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
 
 
103
  }
104
 
105
- $pattern = str_replace( '/', '\/', $this->home . '/' . $this->root . ( $this->options['rewrite'] ? '' : 'language/' ) );
 
106
  $pattern = '#' . $pattern . '('. implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(\/|$)#';
107
- return preg_match( $pattern, trailingslashit( $url ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
108
  }
109
 
110
  /**
99
  */
100
  public function get_language_from_url( $url = '' ) {
101
  if ( empty( $url ) ) {
102
+ $path = $_SERVER['REQUEST_URI'];
103
+ } else {
104
+ $path = parse_url( $url, PHP_URL_PATH );
105
  }
106
 
107
+ $pattern = parse_url( $this->home . '/' . $this->root . ( $this->options['rewrite'] ? '' : 'language/' ), PHP_URL_PATH );
108
+ $pattern = str_replace( '/', '\/', $pattern );
109
  $pattern = '#' . $pattern . '('. implode( '|', $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) . ')(\/|$)#';
110
+ return preg_match( $pattern, trailingslashit( $path ), $matches ) ? $matches[1] : ''; // $matches[1] is the slug of the requested language
111
  }
112
 
113
  /**
include/links-domain.php CHANGED
@@ -38,7 +38,7 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
38
  */
39
  public function add_language_to_link( $url, $lang ) {
40
  if ( ! empty( $lang ) && ! empty( $this->hosts[ $lang->slug ] ) ) {
41
- $url = str_replace( '://' . parse_url( $this->home, PHP_URL_HOST ), '://' . $this->hosts[ $lang->slug ], $url );
42
  }
43
  return $url;
44
  }
@@ -54,7 +54,7 @@ class PLL_Links_Domain extends PLL_Links_Abstract_Domain {
54
  */
55
  public function remove_language_from_link( $url ) {
56
  if ( ! empty( $this->hosts ) ) {
57
- $url = preg_replace( '#:\/\/(' . implode( '|', $this->hosts ) . ')#', '://' . parse_url( $this->home, PHP_URL_HOST ), $url );
58
  }
59
  return $url;
60
  }
38
  */
39
  public function add_language_to_link( $url, $lang ) {
40
  if ( ! empty( $lang ) && ! empty( $this->hosts[ $lang->slug ] ) ) {
41
+ $url = preg_replace( '#:\/\/(' . parse_url( $this->home, PHP_URL_HOST ) . ')($|\/.*)#', '://' . $this->hosts[ $lang->slug ] . "$2", $url );
42
  }
43
  return $url;
44
  }
54
  */
55
  public function remove_language_from_link( $url ) {
56
  if ( ! empty( $this->hosts ) ) {
57
+ $url = preg_replace( '#:\/\/(' . implode( '|', $this->hosts ) . ')($|\/.*)#', '://' . parse_url( $this->home, PHP_URL_HOST ) . "$2", $url );
58
  }
59
  return $url;
60
  }
include/switcher.php CHANGED
@@ -116,12 +116,13 @@ class PLL_Switcher {
116
  * hide_if_empty => hides languages with no posts ( or pages ) if set to 1, defaults to 1
117
  * show_flags => displays flags if set to 1, defaults to 0
118
  * show_names => show language names if set to 1, defaults to 1
119
- * display_names_as => wether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name
120
  * force_home => will always link to home in translated language if set to 1, defaults to 0
121
  * hide_if_no_translation => hide the link if there is no translation if set to 1, defaults to 0
122
  * hide_current => hide the current language if set to 1, defaults to 0
123
  * post_id => returns links to translations of post defined by post_id if set, defaults not set
124
  * raw => return a raw array instead of html markup if set to 1, defaults to 0
 
125
  *
126
  * @since 0.1
127
  *
@@ -143,6 +144,7 @@ class PLL_Switcher {
143
  'hide_current' => 0, // don't hide current language
144
  'post_id' => null, // if not null, link to translations of post defined by post_id
145
  'raw' => 0, // set this to true to build your own custom language switcher
 
146
  );
147
  $args = wp_parse_args( $args, $defaults );
148
 
116
  * hide_if_empty => hides languages with no posts ( or pages ) if set to 1, defaults to 1
117
  * show_flags => displays flags if set to 1, defaults to 0
118
  * show_names => show language names if set to 1, defaults to 1
119
+ * display_names_as => whether to display the language name or its slug, valid options are 'slug' and 'name', defaults to name
120
  * force_home => will always link to home in translated language if set to 1, defaults to 0
121
  * hide_if_no_translation => hide the link if there is no translation if set to 1, defaults to 0
122
  * hide_current => hide the current language if set to 1, defaults to 0
123
  * post_id => returns links to translations of post defined by post_id if set, defaults not set
124
  * raw => return a raw array instead of html markup if set to 1, defaults to 0
125
+ * item_spacing => whether to preserve or discard whitespace between list items, valid options are 'preserve' and 'discard', defaults to preserve
126
  *
127
  * @since 0.1
128
  *
144
  'hide_current' => 0, // don't hide current language
145
  'post_id' => null, // if not null, link to translations of post defined by post_id
146
  'raw' => 0, // set this to true to build your own custom language switcher
147
+ 'item_spacing' => 'preserve', // 'preserve' or 'discard' whitespace between list items
148
  );
149
  $args = wp_parse_args( $args, $defaults );
150
 
include/walker-list.php CHANGED
@@ -17,12 +17,14 @@ class PLL_Walker_List extends Walker {
17
  */
18
  function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
19
  $output .= sprintf(
20
- "\t".'<li class="%1$s"><a lang="%2$s" hreflang="%2$s" href="%3$s">%4$s%5$s</a></li>'."\n",
21
  esc_attr( implode( ' ', $element->classes ) ),
22
  esc_attr( $element->locale ),
23
  esc_url( $element->url ),
24
  $element->flag,
25
- $args['show_flags'] && $args['show_names'] ? '<span style="margin-left:0.3em;">' . esc_html( $element->name ) . '</span>' : esc_html( $element->name )
 
 
26
  );
27
  }
28
 
17
  */
18
  function start_el( &$output, $element, $depth = 0, $args = array(), $current_object_id = 0 ) {
19
  $output .= sprintf(
20
+ '%6$s<li class="%1$s"><a lang="%2$s" hreflang="%2$s" href="%3$s">%4$s%5$s</a></li>%7$s',
21
  esc_attr( implode( ' ', $element->classes ) ),
22
  esc_attr( $element->locale ),
23
  esc_url( $element->url ),
24
  $element->flag,
25
+ $args['show_flags'] && $args['show_names'] ? '<span style="margin-left:0.3em;">' . esc_html( $element->name ) . '</span>' : esc_html( $element->name ),
26
+ $args['item_spacing'] === 'discard' ? '' : "\t",
27
+ $args['item_spacing'] === 'discard' ? '' : "\n"
28
  );
29
  }
30
 
include/widget-recent-comments.php DELETED
@@ -1,102 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'WP_Widget_Recent_Comments' ) ) {
4
- require_once( ABSPATH . '/wp-includes/default-widgets.php' );
5
- }
6
-
7
- /*
8
- * backward compatibility with WP < 4.4
9
- * obliged to rewrite the whole functionnality to have a language dependant cache key
10
- * code base is WP 4.2
11
- *
12
- * @since 1.5
13
- */
14
- class PLL_Widget_Recent_Comments extends WP_Widget_Recent_Comments {
15
-
16
- /*
17
- * displays the widget
18
- * modified version of the parent function to call to use a language dependant cache key
19
- *
20
- * @since 1.5
21
- *
22
- * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
23
- * @param array $instance The settings for the particular instance of the widget
24
- */
25
- public function widget( $args, $instance ) {
26
- global $comments, $comment;
27
-
28
- $cache = array();
29
- if ( ! $this->is_preview() ) {
30
- $cache = wp_cache_get( 'widget_recent_comments', 'widget' );
31
- }
32
- if ( ! is_array( $cache ) ) {
33
- $cache = array();
34
- }
35
-
36
- if ( ! isset( $args['widget_id'] ) )
37
- $args['widget_id'] = $this->id;
38
-
39
- $lang = pll_current_language(); #added
40
- if ( isset( $cache[ $args['widget_id'] ][ $lang ] ) ) { #modified#
41
- echo $cache[ $args['widget_id'] ][ $lang ]; #modified#
42
- return;
43
- }
44
-
45
- $output = '';
46
-
47
- $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Comments' );
48
-
49
- /** This filter is documented in wp-includes/default-widgets.php */
50
- $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
51
-
52
- $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5;
53
- if ( ! $number )
54
- $number = 5;
55
-
56
- /**
57
- * Filter the arguments for the Recent Comments widget.
58
- *
59
- * @since 3.4.0
60
- *
61
- * @see WP_Comment_Query::query() for information on accepted arguments.
62
- *
63
- * @param array $comment_args An array of arguments used to retrieve the recent comments.
64
- */
65
- $comments = get_comments( apply_filters( 'widget_comments_args', array(
66
- 'number' => $number,
67
- 'status' => 'approve',
68
- 'post_status' => 'publish'
69
- ) ) );
70
-
71
- $output .= $args['before_widget'];
72
- if ( $title ) {
73
- $output .= $args['before_title'] . $title . $args['after_title'];
74
- }
75
-
76
- $output .= '<ul id="recentcomments">';
77
- if ( $comments ) {
78
- // Prime cache for associated posts. (Prime post term cache if we need it for permalinks.)
79
- $post_ids = array_unique( wp_list_pluck( $comments, 'comment_post_ID' ) );
80
- _prime_post_caches( $post_ids, strpos( get_option( 'permalink_structure' ), '%category%' ), false );
81
-
82
- foreach ( (array) $comments as $comment ) {
83
- $output .= '<li class="recentcomments">';
84
- /* translators: comments widget: 1: comment author, 2: post link */
85
- $output .= sprintf( _x( '%1$s on %2$s', 'widgets' ),
86
- '<span class="comment-author-link">' . get_comment_author_link() . '</span>',
87
- '<a href="' . esc_url( get_comment_link( $comment->comment_ID ) ) . '">' . get_the_title( $comment->comment_post_ID ) . '</a>'
88
- );
89
- $output .= '</li>';
90
- }
91
- }
92
- $output .= '</ul>';
93
- $output .= $args['after_widget'];
94
-
95
- echo $output;
96
-
97
- if ( ! $this->is_preview() ) {
98
- $cache[ $args['widget_id'] ][ $lang ] = $output; #modified#
99
- wp_cache_set( 'widget_recent_comments', $cache, 'widget' );
100
- }
101
- }
102
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
include/widget-recent-posts.php DELETED
@@ -1,103 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'WP_Widget_Recent_Posts' ) ) {
4
- require_once( ABSPATH . '/wp-includes/default-widgets.php' );
5
- }
6
-
7
- /*
8
- * backward compatibility with WP < 4.4
9
- * obliged to rewrite the whole functionnality to have a language dependant cache key
10
- * code base is WP 4.2
11
- *
12
- * @since 1.5
13
- */
14
- class PLL_Widget_Recent_Posts extends WP_Widget_Recent_Posts {
15
-
16
- /*
17
- * displays the widget
18
- * modified version of the parent function to call to use a language dependant cache key
19
- *
20
- * @since 1.5
21
- *
22
- * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
23
- * @param array $instance The settings for the particular instance of the widget
24
- */
25
- public function widget( $args, $instance ) {
26
- $cache = array();
27
- if ( ! $this->is_preview() ) {
28
- $cache = wp_cache_get( 'widget_recent_posts', 'widget' );
29
- }
30
-
31
- if ( ! is_array( $cache ) ) {
32
- $cache = array();
33
- }
34
-
35
- if ( ! isset( $args['widget_id'] ) ) {
36
- $args['widget_id'] = $this->id;
37
- }
38
-
39
- $lang = pll_current_language(); #added
40
- if ( isset( $cache[ $args['widget_id'] ][ $lang ] ) ) { #modified#
41
- echo $cache[ $args['widget_id'] ][ $lang ]; #modified#
42
- return;
43
- }
44
-
45
- ob_start();
46
-
47
- $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Posts' );
48
-
49
- /** This filter is documented in wp-includes/default-widgets.php */
50
- $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
51
-
52
- $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5;
53
- if ( ! $number )
54
- $number = 5;
55
- $show_date = isset( $instance['show_date'] ) ? $instance['show_date'] : false;
56
-
57
- /**
58
- * Filter the arguments for the Recent Posts widget.
59
- *
60
- * @since 3.4.0
61
- *
62
- * @see WP_Query::get_posts()
63
- *
64
- * @param array $args An array of arguments used to retrieve the recent posts.
65
- */
66
- $r = new WP_Query( apply_filters( 'widget_posts_args', array(
67
- 'posts_per_page' => $number,
68
- 'no_found_rows' => true,
69
- 'post_status' => 'publish',
70
- 'ignore_sticky_posts' => true
71
- ) ) );
72
-
73
- if ( $r->have_posts() ) :
74
- ?>
75
- <?php echo $args['before_widget']; ?>
76
- <?php if ( $title ) {
77
- echo $args['before_title'] . $title . $args['after_title'];
78
- } ?>
79
- <ul>
80
- <?php while ( $r->have_posts() ) : $r->the_post(); ?>
81
- <li>
82
- <a href="<?php the_permalink(); ?>"><?php get_the_title() ? the_title() : the_ID(); ?></a>
83
- <?php if ( $show_date ) : ?>
84
- <span class="post-date"><?php echo get_the_date(); ?></span>
85
- <?php endif; ?>
86
- </li>
87
- <?php endwhile; ?>
88
- </ul>
89
- <?php echo $args['after_widget']; ?>
90
- <?php
91
- // Reset the global $the_post as this query will have stomped on it
92
- wp_reset_postdata();
93
-
94
- endif;
95
-
96
- if ( ! $this->is_preview() ) {
97
- $cache[ $args['widget_id'] ][ $lang ] = ob_get_flush(); #modified#
98
- wp_cache_set( 'widget_recent_posts', $cache, 'widget' );
99
- } else {
100
- ob_end_flush();
101
- }
102
- }
103
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
install/plugin-updater.php CHANGED
@@ -1,8 +1,5 @@
1
  <?php
2
 
3
- // uncomment this line for testing
4
- //set_site_transient( 'update_plugins', null );
5
-
6
  // Exit if accessed directly
7
  if ( ! defined( 'ABSPATH' ) ) {
8
  exit;
@@ -12,15 +9,18 @@ if ( ! defined( 'ABSPATH' ) ) {
12
  * Allows plugins to use their own update API.
13
  * Modified version with 'polylang' text domain and comments for translators
14
  *
15
- * @author Pippin Williamson
16
- * @version 1.6.3
17
  */
18
  class PLL_Plugin_Updater {
19
- private $api_url = '';
20
- private $api_data = array();
21
- private $name = '';
22
- private $slug = '';
23
- private $version = '';
 
 
 
24
 
25
  /**
26
  * Class constructor.
@@ -36,11 +36,14 @@ class PLL_Plugin_Updater {
36
 
37
  global $edd_plugin_data;
38
 
39
- $this->api_url = trailingslashit( $_api_url );
40
- $this->api_data = $_api_data;
41
- $this->name = plugin_basename( $_plugin_file );
42
- $this->slug = basename( $_plugin_file, '.php' );
43
- $this->version = $_api_data['version'];
 
 
 
44
 
45
  $edd_plugin_data[ $this->slug ] = $this->api_data;
46
 
@@ -60,7 +63,7 @@ class PLL_Plugin_Updater {
60
 
61
  add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
62
  add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
63
- remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10, 2 );
64
  add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
65
  add_action( 'admin_init', array( $this, 'show_changelog' ) );
66
 
@@ -91,23 +94,30 @@ class PLL_Plugin_Updater {
91
  return $_transient_data;
92
  }
93
 
94
- if ( empty( $_transient_data->response ) || empty( $_transient_data->response[ $this->name ] ) ) {
 
 
95
 
96
- $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
97
 
98
- if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
 
99
 
100
- if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
101
 
102
- $_transient_data->response[ $this->name ] = $version_info;
103
 
104
- }
105
 
106
- $_transient_data->last_checked = time();
107
- $_transient_data->checked[ $this->name ] = $this->version;
 
108
 
109
  }
110
 
 
 
 
111
  }
112
 
113
  return $_transient_data;
@@ -121,11 +131,15 @@ class PLL_Plugin_Updater {
121
  */
122
  public function show_update_notification( $file, $plugin ) {
123
 
124
- if ( ! current_user_can( 'update_plugins' ) ) {
125
  return;
126
  }
127
 
128
- if ( ! is_multisite() ) {
 
 
 
 
129
  return;
130
  }
131
 
@@ -142,14 +156,12 @@ class PLL_Plugin_Updater {
142
 
143
  if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
144
 
145
- $cache_key = md5( 'edd_plugin_' . sanitize_key( $this->name ) . '_version_info' );
146
- $version_info = get_transient( $cache_key );
147
 
148
  if ( false === $version_info ) {
 
149
 
150
- $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
151
-
152
- set_transient( $cache_key, $version_info, 3600 );
153
  }
154
 
155
  if ( ! is_object( $version_info ) ) {
@@ -162,7 +174,7 @@ class PLL_Plugin_Updater {
162
 
163
  }
164
 
165
- $update_cache->last_checked = time();
166
  $update_cache->checked[ $this->name ] = $this->version;
167
 
168
  set_site_transient( 'update_plugins', $update_cache );
@@ -180,7 +192,10 @@ class PLL_Plugin_Updater {
180
 
181
  // build a plugin list row, with update notification
182
  $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
183
- echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
 
 
 
184
 
185
  $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
186
 
@@ -212,7 +227,6 @@ class PLL_Plugin_Updater {
212
  }
213
  }
214
 
215
-
216
  /**
217
  * Updates information on the "View version x.x details" page with custom data.
218
  *
@@ -225,7 +239,6 @@ class PLL_Plugin_Updater {
225
  */
226
  public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
227
 
228
-
229
  if ( $_action != 'plugin_information' ) {
230
 
231
  return $_data;
@@ -242,21 +255,55 @@ class PLL_Plugin_Updater {
242
  'slug' => $this->slug,
243
  'is_ssl' => is_ssl(),
244
  'fields' => array(
245
- 'banners' => false, // These will be supported soon hopefully
246
  'reviews' => false
247
  )
248
  );
249
 
250
- $api_response = $this->api_request( 'plugin_information', $to_send );
 
 
 
 
 
 
 
 
251
 
252
- if ( false !== $api_response ) {
253
- $_data = $api_response;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  }
255
 
256
  return $_data;
257
  }
258
 
259
-
260
  /**
261
  * Disable SSL verification in order to prevent download update failures
262
  *
@@ -293,7 +340,7 @@ class PLL_Plugin_Updater {
293
  return;
294
  }
295
 
296
- if ( $this->api_url == home_url() ) {
297
  return false; // Don't allow a plugin to ping itself
298
  }
299
 
@@ -302,9 +349,11 @@ class PLL_Plugin_Updater {
302
  'license' => ! empty( $data['license'] ) ? $data['license'] : '',
303
  'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
304
  'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
 
305
  'slug' => $data['slug'],
306
  'author' => $data['author'],
307
- 'url' => home_url()
 
308
  );
309
 
310
  $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
@@ -319,6 +368,16 @@ class PLL_Plugin_Updater {
319
  $request = false;
320
  }
321
 
 
 
 
 
 
 
 
 
 
 
322
  return $request;
323
  }
324
 
@@ -326,27 +385,28 @@ class PLL_Plugin_Updater {
326
 
327
  global $edd_plugin_data;
328
 
329
- if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
330
  return;
331
  }
332
 
333
- if ( empty( $_REQUEST['plugin'] ) ) {
334
  return;
335
  }
336
 
337
- if ( empty( $_REQUEST['slug'] ) ) {
338
  return;
339
  }
340
 
341
- if ( ! current_user_can( 'update_plugins' ) ) {
342
  wp_die( __( 'You do not have permission to install plugin updates', 'polylang' ), __( 'Error', 'polylang' ), array( 'response' => 403 ) );
343
  }
344
 
345
  $data = $edd_plugin_data[ $_REQUEST['slug'] ];
346
- $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_version_info' );
347
- $version_info = get_transient( $cache_key );
 
348
 
349
- if ( false === $version_info ) {
350
 
351
  $api_params = array(
352
  'edd_action' => 'get_version',
@@ -354,7 +414,8 @@ class PLL_Plugin_Updater {
354
  'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
355
  'slug' => $_REQUEST['slug'],
356
  'author' => $data['author'],
357
- 'url' => home_url()
 
358
  );
359
 
360
  $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
@@ -363,21 +424,59 @@ class PLL_Plugin_Updater {
363
  $version_info = json_decode( wp_remote_retrieve_body( $request ) );
364
  }
365
 
 
366
  if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
367
  $version_info->sections = maybe_unserialize( $version_info->sections );
368
  } else {
369
  $version_info = false;
370
  }
371
 
372
- set_transient( $cache_key, $version_info, 3600 );
 
 
 
 
 
 
373
 
374
  }
375
 
376
- if ( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
377
  echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
378
  }
379
 
380
  exit;
381
  }
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  }
1
  <?php
2
 
 
 
 
3
  // Exit if accessed directly
4
  if ( ! defined( 'ABSPATH' ) ) {
5
  exit;
9
  * Allows plugins to use their own update API.
10
  * Modified version with 'polylang' text domain and comments for translators
11
  *
12
+ * @author Easy Digital Downloads
13
+ * @version 1.6.10
14
  */
15
  class PLL_Plugin_Updater {
16
+
17
+ private $api_url = '';
18
+ private $api_data = array();
19
+ private $name = '';
20
+ private $slug = '';
21
+ private $version = '';
22
+ private $wp_override = false;
23
+ private $cache_key = '';
24
 
25
  /**
26
  * Class constructor.
36
 
37
  global $edd_plugin_data;
38
 
39
+ $this->api_url = trailingslashit( $_api_url );
40
+ $this->api_data = $_api_data;
41
+ $this->name = plugin_basename( $_plugin_file );
42
+ $this->slug = basename( $_plugin_file, '.php' );
43
+ $this->version = $_api_data['version'];
44
+ $this->wp_override = isset( $_api_data['wp_override'] ) ? (bool) $_api_data['wp_override'] : false;
45
+ $this->beta = ! empty( $this->api_data['beta'] ) ? true : false;
46
+ $this->cache_key = md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
47
 
48
  $edd_plugin_data[ $this->slug ] = $this->api_data;
49
 
63
 
64
  add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
65
  add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
66
+ remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
67
  add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
68
  add_action( 'admin_init', array( $this, 'show_changelog' ) );
69
 
94
  return $_transient_data;
95
  }
96
 
97
+ if ( ! empty( $_transient_data->response ) && ! empty( $_transient_data->response[ $this->name ] ) && false === $this->wp_override ) {
98
+ return $_transient_data;
99
+ }
100
 
101
+ $version_info = $this->get_cached_version_info();
102
 
103
+ if ( false === $version_info ) {
104
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
105
 
106
+ $this->set_version_info_cache( $version_info );
107
 
108
+ }
109
 
110
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
111
 
112
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
113
+
114
+ $_transient_data->response[ $this->name ] = $version_info;
115
 
116
  }
117
 
118
+ $_transient_data->last_checked = current_time( 'timestamp' );
119
+ $_transient_data->checked[ $this->name ] = $this->version;
120
+
121
  }
122
 
123
  return $_transient_data;
131
  */
132
  public function show_update_notification( $file, $plugin ) {
133
 
134
+ if ( is_network_admin() ) {
135
  return;
136
  }
137
 
138
+ if( ! current_user_can( 'update_plugins' ) ) {
139
+ return;
140
+ }
141
+
142
+ if( ! is_multisite() ) {
143
  return;
144
  }
145
 
156
 
157
  if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
158
 
159
+ $version_info = $this->get_cached_version_info();
 
160
 
161
  if ( false === $version_info ) {
162
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug, 'beta' => $this->beta ) );
163
 
164
+ $this->set_version_info_cache( $version_info );
 
 
165
  }
166
 
167
  if ( ! is_object( $version_info ) ) {
174
 
175
  }
176
 
177
+ $update_cache->last_checked = current_time( 'timestamp' );
178
  $update_cache->checked[ $this->name ] = $this->version;
179
 
180
  set_site_transient( 'update_plugins', $update_cache );
192
 
193
  // build a plugin list row, with update notification
194
  $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
195
+ # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange">
196
+ echo '<tr class="plugin-update-tr" id="' . $this->slug . '-update" data-slug="' . $this->slug . '" data-plugin="' . $this->slug . '/' . $file . '">';
197
+ echo '<td colspan="3" class="plugin-update colspanchange">';
198
+ echo '<div class="update-message notice inline notice-warning notice-alt">';
199
 
200
  $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
201
 
227
  }
228
  }
229
 
 
230
  /**
231
  * Updates information on the "View version x.x details" page with custom data.
232
  *
239
  */
240
  public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
241
 
 
242
  if ( $_action != 'plugin_information' ) {
243
 
244
  return $_data;
255
  'slug' => $this->slug,
256
  'is_ssl' => is_ssl(),
257
  'fields' => array(
258
+ 'banners' => array(),
259
  'reviews' => false
260
  )
261
  );
262
 
263
+ $cache_key = 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) );
264
+
265
+ // Get the transient where we store the api request for this plugin for 24 hours
266
+ $edd_api_request_transient = $this->get_cached_version_info( $cache_key );
267
+
268
+ //If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
269
+ if ( empty( $edd_api_request_transient ) ) {
270
+
271
+ $api_response = $this->api_request( 'plugin_information', $to_send );
272
 
273
+ // Expires in 3 hours
274
+ $this->set_version_info_cache( $api_response, $cache_key );
275
+
276
+ if ( false !== $api_response ) {
277
+ $_data = $api_response;
278
+ }
279
+
280
+ } else {
281
+ $_data = $edd_api_request_transient;
282
+ }
283
+
284
+ // Convert sections into an associative array, since we're getting an object, but Core expects an array.
285
+ if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
286
+ $new_sections = array();
287
+ foreach ( $_data->sections as $key => $key ) {
288
+ $new_sections[ $key ] = $key;
289
+ }
290
+
291
+ $_data->sections = $new_sections;
292
+ }
293
+
294
+ // Convert banners into an associative array, since we're getting an object, but Core expects an array.
295
+ if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
296
+ $new_banners = array();
297
+ foreach ( $_data->banners as $key => $key ) {
298
+ $new_banners[ $key ] = $key;
299
+ }
300
+
301
+ $_data->banners = $new_banners;
302
  }
303
 
304
  return $_data;
305
  }
306
 
 
307
  /**
308
  * Disable SSL verification in order to prevent download update failures
309
  *
340
  return;
341
  }
342
 
343
+ if( $this->api_url == trailingslashit (home_url() ) ) {
344
  return false; // Don't allow a plugin to ping itself
345
  }
346
 
349
  'license' => ! empty( $data['license'] ) ? $data['license'] : '',
350
  'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
351
  'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
352
+ 'version' => isset( $data['version'] ) ? $data['version'] : false,
353
  'slug' => $data['slug'],
354
  'author' => $data['author'],
355
+ 'url' => home_url(),
356
+ 'beta' => ! empty( $data['beta'] ),
357
  );
358
 
359
  $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
368
  $request = false;
369
  }
370
 
371
+ if ( $request && isset( $request->banners ) ) {
372
+ $request->banners = maybe_unserialize( $request->banners );
373
+ }
374
+
375
+ if( ! empty( $request ) ) {
376
+ foreach( $request->sections as $key => $section ) {
377
+ $request->$key = (array) $section;
378
+ }
379
+ }
380
+
381
  return $request;
382
  }
383
 
385
 
386
  global $edd_plugin_data;
387
 
388
+ if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
389
  return;
390
  }
391
 
392
+ if( empty( $_REQUEST['plugin'] ) ) {
393
  return;
394
  }
395
 
396
+ if( empty( $_REQUEST['slug'] ) ) {
397
  return;
398
  }
399
 
400
+ if( ! current_user_can( 'update_plugins' ) ) {
401
  wp_die( __( 'You do not have permission to install plugin updates', 'polylang' ), __( 'Error', 'polylang' ), array( 'response' => 403 ) );
402
  }
403
 
404
  $data = $edd_plugin_data[ $_REQUEST['slug'] ];
405
+ $beta = ! empty( $data['beta'] ) ? true : false;
406
+ $cache_key = md5( 'edd_plugin_' . sanitize_key( $_REQUEST['plugin'] ) . '_' . $beta . '_version_info' );
407
+ $version_info = $this->get_cached_version_info( $cache_key );
408
 
409
+ if( false === $version_info ) {
410
 
411
  $api_params = array(
412
  'edd_action' => 'get_version',
414
  'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
415
  'slug' => $_REQUEST['slug'],
416
  'author' => $data['author'],
417
+ 'url' => home_url(),
418
+ 'beta' => ! empty( $data['beta'] )
419
  );
420
 
421
  $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
424
  $version_info = json_decode( wp_remote_retrieve_body( $request ) );
425
  }
426
 
427
+
428
  if ( ! empty( $version_info ) && isset( $version_info->sections ) ) {
429
  $version_info->sections = maybe_unserialize( $version_info->sections );
430
  } else {
431
  $version_info = false;
432
  }
433
 
434
+ if( ! empty( $version_info ) ) {
435
+ foreach( $version_info->sections as $key => $section ) {
436
+ $version_info->$key = (array) $section;
437
+ }
438
+ }
439
+
440
+ $this->set_version_info_cache( $version_info, $cache_key );
441
 
442
  }
443
 
444
+ if( ! empty( $version_info ) && isset( $version_info->sections['changelog'] ) ) {
445
  echo '<div style="background:#fff;padding:10px;">' . $version_info->sections['changelog'] . '</div>';
446
  }
447
 
448
  exit;
449
  }
450
 
451
+ public function get_cached_version_info( $cache_key = '' ) {
452
+
453
+ if( empty( $cache_key ) ) {
454
+ $cache_key = $this->cache_key;
455
+ }
456
+
457
+ $cache = get_option( $cache_key );
458
+
459
+ if( empty( $cache['timeout'] ) || current_time( 'timestamp' ) > $cache['timeout'] ) {
460
+ return false; // Cache is expired
461
+ }
462
+
463
+ return json_decode( $cache['value'] );
464
+
465
+ }
466
+
467
+ public function set_version_info_cache( $value = '', $cache_key = '' ) {
468
+
469
+ if( empty( $cache_key ) ) {
470
+ $cache_key = $this->cache_key;
471
+ }
472
+
473
+ $data = array(
474
+ 'timeout' => strtotime( '+3 hours', current_time( 'timestamp' ) ),
475
+ 'value' => json_encode( $value )
476
+ );
477
+
478
+ update_option( $cache_key, $data );
479
+
480
+ }
481
+
482
  }
modules/wpml/wpml-compat.php CHANGED
@@ -25,7 +25,7 @@ class PLL_WPML_Compat {
25
  self::$strings = get_option( 'polylang_wpml_strings', array() );
26
 
27
  add_action( 'pll_language_defined', array( $this, 'define_constants' ) );
28
- add_action( 'pll_get_strings', array( $this, 'get_strings' ) );
29
  }
30
 
31
  /**
25
  self::$strings = get_option( 'polylang_wpml_strings', array() );
26
 
27
  add_action( 'pll_language_defined', array( $this, 'define_constants' ) );
28
+ add_filter( 'pll_get_strings', array( $this, 'get_strings' ) );
29
  }
30
 
31
  /**
polylang.php CHANGED
@@ -3,7 +3,7 @@
3
  /*
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
- Version: 2.1.1
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
@@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) {
35
  exit; // don't access directly
36
  };
37
 
38
- define( 'POLYLANG_VERSION', '2.1.1' );
39
  define( 'PLL_MIN_WP_VERSION', '4.4' );
40
 
41
  define( 'POLYLANG_FILE', __FILE__ ); // this file
3
  /*
4
  Plugin Name: Polylang
5
  Plugin URI: https://polylang.pro
6
+ Version: 2.1.2
7
  Author: Frédéric Demarle
8
  Author uri: https://polylang.pro
9
  Description: Adds multilingual capability to WordPress
35
  exit; // don't access directly
36
  };
37
 
38
+ define( 'POLYLANG_VERSION', '2.1.2' );
39
  define( 'PLL_MIN_WP_VERSION', '4.4' );
40
 
41
  define( 'POLYLANG_FILE', __FILE__ ); // this file
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://polylang.pro
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
  Requires at least: 4.4
6
  Tested up to: 4.7
7
- Stable tag: 2.1.1
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
@@ -76,6 +76,21 @@ Don't hesitate to [give your feedback](http://wordpress.org/support/view/plugin-
76
 
77
  == Changelog ==
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  = 2.1.1 (2017-02-15) =
80
 
81
  * Pro: Add filter 'pll_enable_duplicate_media' for a fine control of automatic media duplication
4
  Tags: multilingual, bilingual, translate, translation, language, multilanguage, international, localization
5
  Requires at least: 4.4
6
  Tested up to: 4.7
7
+ Stable tag: 2.1.2
8
  License: GPLv2 or later
9
 
10
  Making WordPress multilingual
76
 
77
  == Changelog ==
78
 
79
+ = 2.1.2 (2017-03-09) =
80
+
81
+ * Pro: Add filter 'pll_xdata_nonce_life'
82
+ * Pro: Fix translation of WooCommerce product attribute slug
83
+ * Pro: Fix product synchronization in WooCommerce 2.7
84
+ * Pro: Fix error message when bulk trashing synchronized posts
85
+ * Add option to discard item spacing in the output of pll_the_languages() ( Props Ceslav Przywara ) #93 #95
86
+ * Add as, dzo, kab, km, ml_IN, nl_BE, pa_IN, rhg, sah, ta_IN, tah, te, tt_RU to the predefined list of languages
87
+ * Update plugin updater class to 1.6.10
88
+ * Fix: Remove the dependency to is_ssl() to detect the language in the url ( language set from the directory name )
89
+ * Fix issue with secondary level domains
90
+ * Fix strings not translated in emails
91
+ * Fix incorrect usage of add_action() ( Props Peter J. Herrel ) #103
92
+ * Fix wrong redirect in customizer in WP 4.7
93
+
94
  = 2.1.1 (2017-02-15) =
95
 
96
  * Pro: Add filter 'pll_enable_duplicate_media' for a fine control of automatic media duplication
settings/languages.php CHANGED
@@ -18,6 +18,7 @@ $languages = array(
18
  'af' => array( 'af', 'af', 'Afrikaans', 'ltr', 'za' ),
19
  'ar' => array( 'ar', 'ar', 'العربية', 'rtl', 'arab' ),
20
  'ary' => array( 'ar', 'ary', 'العربية المغربية', 'rtl', 'ma' ),
 
21
  'az' => array( 'az', 'az', 'Azərbaycan', 'ltr', 'az' ),
22
  'azb' => array( 'az', 'azb', 'گؤنئی آذربایجان', 'rtl', 'az' ),
23
  'bel' => array( 'be', 'bel', 'Беларуская мова', 'ltr', 'by' ),
@@ -34,6 +35,7 @@ $languages = array(
34
  'de_CH_informal' => array( 'de', 'de_CH_informal', 'Deutsch', 'ltr', 'ch' ),
35
  'de_DE' => array( 'de', 'de_DE', 'Deutsch', 'ltr', 'de' ),
36
  'de_DE_formal' => array( 'de', 'de_DE_formal', 'Deutsch', 'ltr', 'de' ),
 
37
  'el' => array( 'el', 'el', 'Ελληνικά', 'ltr', 'gr' ),
38
  'en_AU' => array( 'en', 'en_AU', 'English', 'ltr', 'au' ),
39
  'en_CA' => array( 'en', 'en_CA', 'English', 'ltr', 'ca' ),
@@ -75,29 +77,36 @@ $languages = array(
75
  'ja' => array( 'ja', 'ja', '日本語', 'ltr', 'jp' ),
76
  'jv_ID' => array( 'jv', 'jv_ID', 'Basa Jawa', 'ltr', 'id' ),
77
  'ka_GE' => array( 'ka', 'ka_GE', 'ქართული', 'ltr', 'ge' ),
 
78
  'kk' => array( 'kk', 'kk', 'Қазақ тілі', 'ltr', 'kz' ),
 
79
  'ko_KR' => array( 'ko', 'ko_KR', '한국어', 'ltr', 'kr' ),
80
  'ckb' => array( 'ku', 'ckb', 'کوردی', 'rtl', 'kurdistan' ),
81
  'lo' => array( 'lo', 'lo', 'ພາສາລາວ', 'ltr', 'la' ),
82
  'lt_LT' => array( 'lt', 'lt_LT', 'Lietuviškai', 'ltr', 'lt' ),
83
  'lv' => array( 'lv', 'lv', 'Latviešu valoda', 'ltr', 'lv' ),
84
  'mk_MK' => array( 'mk', 'mk_MK', 'македонски јазик', 'ltr', 'mk' ),
 
85
  'mn' => array( 'mn', 'mn', 'Монгол хэл', 'ltr', 'mn' ),
86
  'mr' => array( 'mr', 'mr', 'मराठी', 'ltr', 'in' ),
87
  'ms_MY' => array( 'ms', 'ms_MY', 'Bahasa Melayu', 'ltr', 'my' ),
88
  'my_MM' => array( 'my', 'my_MM', 'ဗမာစာ', 'ltr', 'mm' ),
89
  'nb_NO' => array( 'nb', 'nb_NO', 'Norsk Bokmål', 'ltr', 'no' ),
90
  'ne_NP' => array( 'ne', 'ne_NP', 'नेपाली', 'ltr', 'np' ),
 
91
  'nl_NL' => array( 'nl', 'nl_NL', 'Nederlands', 'ltr', 'nl' ),
92
  'nl_NL_formal' => array( 'nl', 'nl_NL_formal', 'Nederlands', 'ltr', 'nl' ),
93
  'nn_NO' => array( 'nn', 'nn_NO', 'Norsk Nynorsk', 'ltr', 'no' ),
94
  'oci' => array( 'oc', 'oci', 'Occitan', 'ltr', 'occitania' ),
 
95
  'pl_PL' => array( 'pl', 'pl_PL', 'Polski', 'ltr', 'pl' ),
96
  'ps' => array( 'ps', 'ps', 'پښتو', 'rtl', 'af' ),
97
  'pt_BR' => array( 'pt', 'pt_BR', 'Português', 'ltr', 'br' ),
98
  'pt_PT' => array( 'pt', 'pt_PT', 'Português', 'ltr', 'pt' ),
 
99
  'ro_RO' => array( 'ro', 'ro_RO', 'Română', 'ltr', 'ro' ),
100
  'ru_RU' => array( 'ru', 'ru_RU', 'Русский', 'ltr', 'ru' ),
 
101
  'si_LK' => array( 'si', 'si_LK', 'සිංහල', 'ltr', 'lk' ),
102
  'sk_SK' => array( 'sk', 'sk_SK', 'Slovenčina', 'ltr', 'sk' ),
103
  'sl_SI' => array( 'sl', 'sl_SI', 'Slovenščina', 'ltr', 'si' ),
@@ -107,10 +116,14 @@ $languages = array(
107
  'su_ID' => array( 'su', 'su_ID', 'Basa Sunda', 'ltr', 'id' ),
108
  'sv_SE' => array( 'sv', 'sv_SE', 'Svenska', 'ltr', 'se' ),
109
  'szl' => array( 'szl', 'szl', 'Ślōnskŏ gŏdka', 'ltr', 'pl' ),
 
110
  'ta_LK' => array( 'ta', 'ta_LK', 'தமிழ்', 'ltr', 'lk' ),
 
 
111
  'th' => array( 'th', 'th', 'ไทย', 'ltr', 'th' ),
112
  'tl' => array( 'tl', 'tl', 'Tagalog', 'ltr', 'ph' ),
113
  'tr_TR' => array( 'tr', 'tr_TR', 'Türkçe', 'ltr', 'tr' ),
 
114
  'ug_CN' => array( 'ug', 'ug_CN', 'Uyƣurqə', 'ltr', 'cn' ),
115
  'uk' => array( 'uk', 'uk', 'Українська', 'ltr', 'ua' ),
116
  'ur' => array( 'ur', 'ur', 'اردو', 'rtl', 'pk' ),
18
  'af' => array( 'af', 'af', 'Afrikaans', 'ltr', 'za' ),
19
  'ar' => array( 'ar', 'ar', 'العربية', 'rtl', 'arab' ),
20
  'ary' => array( 'ar', 'ary', 'العربية المغربية', 'rtl', 'ma' ),
21
+ 'as' => array( 'as', 'as', 'অসমীয়া', 'ltr', 'in' ),
22
  'az' => array( 'az', 'az', 'Azərbaycan', 'ltr', 'az' ),
23
  'azb' => array( 'az', 'azb', 'گؤنئی آذربایجان', 'rtl', 'az' ),
24
  'bel' => array( 'be', 'bel', 'Беларуская мова', 'ltr', 'by' ),
35
  'de_CH_informal' => array( 'de', 'de_CH_informal', 'Deutsch', 'ltr', 'ch' ),
36
  'de_DE' => array( 'de', 'de_DE', 'Deutsch', 'ltr', 'de' ),
37
  'de_DE_formal' => array( 'de', 'de_DE_formal', 'Deutsch', 'ltr', 'de' ),
38
+ 'dzo' => array( 'dz', 'dzo', 'རྫོང་ཁ', 'ltr', 'bt' ),
39
  'el' => array( 'el', 'el', 'Ελληνικά', 'ltr', 'gr' ),
40
  'en_AU' => array( 'en', 'en_AU', 'English', 'ltr', 'au' ),
41
  'en_CA' => array( 'en', 'en_CA', 'English', 'ltr', 'ca' ),
77
  'ja' => array( 'ja', 'ja', '日本語', 'ltr', 'jp' ),
78
  'jv_ID' => array( 'jv', 'jv_ID', 'Basa Jawa', 'ltr', 'id' ),
79
  'ka_GE' => array( 'ka', 'ka_GE', 'ქართული', 'ltr', 'ge' ),
80
+ 'kab' => array( 'kab', 'kab', 'Taqbaylit', 'ltr', 'dz' ),
81
  'kk' => array( 'kk', 'kk', 'Қазақ тілі', 'ltr', 'kz' ),
82
+ 'km' => array( 'km', 'km', 'ភាសាខ្មែរ', 'ltr', 'kh' ),
83
  'ko_KR' => array( 'ko', 'ko_KR', '한국어', 'ltr', 'kr' ),
84
  'ckb' => array( 'ku', 'ckb', 'کوردی', 'rtl', 'kurdistan' ),
85
  'lo' => array( 'lo', 'lo', 'ພາສາລາວ', 'ltr', 'la' ),
86
  'lt_LT' => array( 'lt', 'lt_LT', 'Lietuviškai', 'ltr', 'lt' ),
87
  'lv' => array( 'lv', 'lv', 'Latviešu valoda', 'ltr', 'lv' ),
88
  'mk_MK' => array( 'mk', 'mk_MK', 'македонски јазик', 'ltr', 'mk' ),
89
+ 'ml_IN' => array( 'ml', 'ml_IN', 'മലയാളം', 'ltr', 'in' ),
90
  'mn' => array( 'mn', 'mn', 'Монгол хэл', 'ltr', 'mn' ),
91
  'mr' => array( 'mr', 'mr', 'मराठी', 'ltr', 'in' ),
92
  'ms_MY' => array( 'ms', 'ms_MY', 'Bahasa Melayu', 'ltr', 'my' ),
93
  'my_MM' => array( 'my', 'my_MM', 'ဗမာစာ', 'ltr', 'mm' ),
94
  'nb_NO' => array( 'nb', 'nb_NO', 'Norsk Bokmål', 'ltr', 'no' ),
95
  'ne_NP' => array( 'ne', 'ne_NP', 'नेपाली', 'ltr', 'np' ),
96
+ 'nl_BE' => array( 'nl', 'nl_BE', 'Nederlands', 'ltr', 'be' ),
97
  'nl_NL' => array( 'nl', 'nl_NL', 'Nederlands', 'ltr', 'nl' ),
98
  'nl_NL_formal' => array( 'nl', 'nl_NL_formal', 'Nederlands', 'ltr', 'nl' ),
99
  'nn_NO' => array( 'nn', 'nn_NO', 'Norsk Nynorsk', 'ltr', 'no' ),
100
  'oci' => array( 'oc', 'oci', 'Occitan', 'ltr', 'occitania' ),
101
+ 'pa_IN' => array( 'pa', 'pa_IN', 'ਪੰਜਾਬੀ', 'ltr', 'in' ),
102
  'pl_PL' => array( 'pl', 'pl_PL', 'Polski', 'ltr', 'pl' ),
103
  'ps' => array( 'ps', 'ps', 'پښتو', 'rtl', 'af' ),
104
  'pt_BR' => array( 'pt', 'pt_BR', 'Português', 'ltr', 'br' ),
105
  'pt_PT' => array( 'pt', 'pt_PT', 'Português', 'ltr', 'pt' ),
106
+ 'rhg' => array( 'rhg', 'rhg', 'Ruáinga', 'ltr', 'mm' ),
107
  'ro_RO' => array( 'ro', 'ro_RO', 'Română', 'ltr', 'ro' ),
108
  'ru_RU' => array( 'ru', 'ru_RU', 'Русский', 'ltr', 'ru' ),
109
+ 'sah' => array( 'sah', 'sah', 'Сахалыы', 'ltr', 'ru' ),
110
  'si_LK' => array( 'si', 'si_LK', 'සිංහල', 'ltr', 'lk' ),
111
  'sk_SK' => array( 'sk', 'sk_SK', 'Slovenčina', 'ltr', 'sk' ),
112
  'sl_SI' => array( 'sl', 'sl_SI', 'Slovenščina', 'ltr', 'si' ),
116
  'su_ID' => array( 'su', 'su_ID', 'Basa Sunda', 'ltr', 'id' ),
117
  'sv_SE' => array( 'sv', 'sv_SE', 'Svenska', 'ltr', 'se' ),
118
  'szl' => array( 'szl', 'szl', 'Ślōnskŏ gŏdka', 'ltr', 'pl' ),
119
+ 'ta_IN' => array( 'ta', 'ta_IN', 'தமிழ்', 'ltr', 'in' ),
120
  'ta_LK' => array( 'ta', 'ta_LK', 'தமிழ்', 'ltr', 'lk' ),
121
+ 'tah' => array( 'ty', 'tah', 'Reo Tahiti', 'ltr', 'pf' ),
122
+ 'te' => array( 'te', 'te', 'తెలుగు', 'ltr', 'in' ),
123
  'th' => array( 'th', 'th', 'ไทย', 'ltr', 'th' ),
124
  'tl' => array( 'tl', 'tl', 'Tagalog', 'ltr', 'ph' ),
125
  'tr_TR' => array( 'tr', 'tr_TR', 'Türkçe', 'ltr', 'tr' ),
126
+ 'tt_RU' => array( 'tt', 'tt_RU', 'Татар теле', 'ltr', 'ru' ),
127
  'ug_CN' => array( 'ug', 'ug_CN', 'Uyƣurqə', 'ltr', 'cn' ),
128
  'uk' => array( 'uk', 'uk', 'Українська', 'ltr', 'ua' ),
129
  'ur' => array( 'ur', 'ur', 'اردو', 'rtl', 'pk' ),