PluginProbe
Translate and Go multilingual – Automatic AI translation – wpLingua / 2.16.7
Translate and Go multilingual – Automatic AI translation – wpLingua v2.16.7
2.16.7 2.16.6 2.16.5 2.16.4 2.16.3 2.16.2 2.16.1 2.16.0 2.15.2 2.15.1 2.15.0 2.14.3 2.14.2 2.14.1 2.14.0 2.13.1 2.13.0 2.12.3 2.12.2 2.12.1 trunk 1.0.3 1.0.4 1.0.5 1.1.0 All 112 releases
← All changes | wplingua.php +303 -30 1.0.32.16.7 View file →
@@ -1,14 +1,14 @@
1 1 <?php
2 2 /**
3 3 * Plugin Name: wpLingua
4 - * Plugin URI: https://github.com/julien-jacob/wplingua
4 + * Plugin URI: https://wplingua.com/
5 5 * Description: An all-in-one solution that makes your websites multilingual and translates them automatically, without word or page limits. The highlights: a free first language, an on-page visual editor for editing translations, a customizable language switcher, search engine optimization (SEO), self-hosted data and more!
6 - * Author: wpLingua Team
6 + * Author: wpLingua Translation Service
7 7 * Author URI: https://wplingua.com/
8 8 * Text Domain: wplingua
9 9 * Domain Path: /languages/
10 - * Version: 1.0.3
10 + * Version: 2.16.7
11 11 * Requires PHP: 7.4
12 12 * License: GPL v2 or later
13 13 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
14 14 */
@@ -21,30 +21,47 @@
21 21
22 22
23 23 // Define wpLingua constants
24 24 define( 'WPLNG_API_URL', 'https://api.wplingua.com' );
25 -define( 'WPLNG_API_VERSION', '1.0' );
25 +define( 'WPLNG_API_VERSION', '3.0' );
26 26 define( 'WPLNG_API_SSLVERIFY', true );
27 -define( 'WPLNG_PLUGIN_VERSION', '1.0.3' );
28 -define( 'WPLNG_PLUGIN_PATH', dirname( __FILE__ ) );
29 -define( 'WPLNG_MAX_TRANSLATIONS', 256 );
30 -define( 'WPLNG_MAX_FILE_SIZE', 1000000 );
31 -define( 'WPLNG_LOG_JSON_DEBUG', false );
27 +define( 'WPLNG_PLUGIN_VERSION', '2.16.7' );
28 +define( 'WPLNG_PLUGIN_FILE', plugin_basename( __FILE__ ) );
29 +define( 'WPLNG_PLUGIN_DIR', __DIR__ );
30 +define( 'WPLNG_CACHE_DIR', WP_CONTENT_DIR . '/wplingua-cache' );
31 +define( 'WPLNG_PHP_MIN_VERSION', '7.4' );
32 +define( 'WPLNG_MAX_TRANSLATIONS_STR', 220 );
33 +define( 'WPLNG_MAX_TRANSLATIONS_CHAR', 4200 );
34 +define( 'WPLNG_MAX_FILE_SIZE', 5000000 );
32 35
33 36
34 -// Load plugin text domain
35 -load_plugin_textdomain(
36 - 'wplingua',
37 - false,
38 - basename( dirname( __FILE__ ) ) . '/languages'
39 -);
37 +// Define debug constants
38 +defined( 'WPLNG_DEBUG_JSON' ) || define( 'WPLNG_DEBUG_JSON', false );
39 +defined( 'WPLNG_DEBUG_AJAX' ) || define( 'WPLNG_DEBUG_AJAX', false );
40 +defined( 'WPLNG_DEBUG_REST' ) || define( 'WPLNG_DEBUG_REST', false );
41 +defined( 'WPLNG_DEBUG_BEAT' ) || define( 'WPLNG_DEBUG_BEAT', false );
42 +defined( 'WPLNG_DEBUG_XML' ) || define( 'WPLNG_DEBUG_XML', false );
40 43
41 44
42 45 // Load all needed PHP files
43 -require_once WPLNG_PLUGIN_PATH . '/loader.php';
46 +require_once WPLNG_PLUGIN_DIR . '/loader.php';
44 47
45 48
46 49 /**
50 + * Loads wpLingua plugin's translated strings.
51 + *
52 + * @return void
53 + */
54 +function wplng_load_plugin_textdomain() {
55 + load_plugin_textdomain(
56 + 'wplingua',
57 + false,
58 + 'wplingua/languages'
59 + );
60 +}
61 +
62 +
63 +/**
47 64 * Register all wpLingua Hook
48 65 *
49 66 * @return void
50 67 */
@@ -49,28 +66,96 @@
49 66 * @return void
50 67 */
51 68 function wplng_start() {
52 69
70 + // Setup $wplng_class_reload
71 + global $wplng_class_reload;
72 + $wplng_class_reload = false;
73 +
74 + // Setup $wplng_request_uri
53 75 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
54 76
55 - $request_uri = sanitize_url( $_SERVER['REQUEST_URI'] );
77 + $request_uri = wp_unslash( (string) $_SERVER['REQUEST_URI'] );
78 + $request_uri = wplng_normalize_request_path( $request_uri );
79 + $decoded_uri = urldecode( $request_uri );
56 80
57 - // Check if the referer is clean
58 - if ( strtolower( esc_url_raw( $request_uri ) ) !== strtolower( $request_uri ) ) {
81 + // Check if the request URI is clean
82 + if ( strtolower( esc_url_raw( $request_uri ) ) !== strtolower( $request_uri )
83 + || wplng_str_is_malicious( $request_uri )
84 + || wplng_str_is_malicious( $decoded_uri )
85 + ) {
59 86 return;
60 87 }
61 88
89 + // Also check query string for double-encoded attacks
90 + if ( isset( $_SERVER['QUERY_STRING'] ) ) {
91 + $query_string = sanitize_text_field( $_SERVER['QUERY_STRING'] );
92 + $decoded_query = urldecode( urldecode( $query_string ) );
93 +
94 + if ( wplng_str_is_malicious( $decoded_query ) ) {
95 + return;
96 + }
97 + }
98 +
62 99 global $wplng_request_uri;
63 100 $wplng_request_uri = $request_uri;
101 + }
64 102
103 + // The plugin version has changed
104 + if ( get_option( 'wplng_version' ) !== WPLNG_PLUGIN_VERSION ) {
105 + // Update version number in DB
106 + update_option( 'wplng_version', WPLNG_PLUGIN_VERSION, true );
107 +
108 + // Clear all wpLingua cache
109 + delete_option( 'wplng_cached_translations' );
110 + delete_option( 'wplng_cached_slugs' );
111 + delete_option( 'wplng_api_key_data' );
112 + wplng_clear_folder_cache();
113 + wplng_clear_website_cache();
114 +
115 + // Clear old cache method (version <= 2.12.3)
116 + delete_transient( 'wplng_cached_translations' );
117 + delete_transient( 'wplng_cached_slugs' );
65 118 }
66 119
120 + // Load plugin text domain /languages/
121 + add_action( 'init', 'wplng_load_plugin_textdomain' );
122 +
123 + // Do not cache page for connected editor
124 + add_action( 'init', 'wplng_init_manage_cache', 1 );
125 +
126 + // Display a notice if incompatibility is detected
127 + add_action( 'admin_notices', 'wplng_admin_notice_incompatible_plugin', 1 );
128 + add_action( 'admin_notices', 'wplng_admin_notice_incompatible_multisite', 1 );
129 + add_action( 'admin_notices', 'wplng_admin_notice_incompatible_php_version', 1 );
130 + add_action( 'admin_notices', 'wplng_admin_notice_incompatible_htaccess', 1 );
131 + add_action( 'admin_notices', 'wplng_admin_notice_incompatible_plain_permalink', 1 );
132 +
133 + // Return if incompatibility is detected
134 + if ( ! empty( wplng_get_incompatible_plugins() )
135 + || ( is_multisite()
136 + && ! apply_filters( 'wplng_bypass_multisite_incompatibility', false )
137 + )
138 + || ( version_compare( PHP_VERSION, WPLNG_PHP_MIN_VERSION ) < 0 )
139 + || ! wplng_htaccess_is_valid()
140 + || empty( get_option( 'permalink_structure' ) )
141 + ) {
142 + return;
143 + }
144 +
67 145 // Register plugin settings
68 146 add_action( 'admin_init', 'wplng_register_settings' );
69 147
70 148 // Add settings link in plugin list
71 - add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'wplng_settings_link' );
149 + add_filter( 'plugin_action_links_' . WPLNG_PLUGIN_FILE, 'wplng_settings_link' );
72 150
151 + // Redirect to the settings page on plugin activation
152 + add_action( 'activated_plugin', 'wplng_plugin_activation_redirect' );
153 +
154 + // Set footer text for options pages
155 + add_filter( 'admin_footer_text', 'wplng_admin_footer_text', 11 );
156 + add_filter( 'update_footer', 'wplng_update_footer', 11 );
157 +
73 158 // Print head script (JSON with all languages informations)
74 159 add_action( 'toplevel_page_wplingua-settings', 'wplng_inline_script_languages' );
75 160
76 161 if ( empty( wplng_get_api_data() ) ) {
@@ -80,8 +165,11 @@
80 165
81 166 // Enqueue CSS and JS files for register option pages
82 167 add_action( 'admin_enqueue_scripts', 'wplng_option_page_register_assets' );
83 168
169 + // Display a notice if the plugin is activate but not configured
170 + add_action( 'admin_notices', 'wplng_admin_notice_no_key_set', 1 );
171 +
84 172 } else {
85 173
86 174 /**
87 175 * Back office
@@ -88,23 +176,45 @@
88 176 */
89 177
90 178 // Add menu in back office
91 179 add_action( 'admin_menu', 'wplng_create_menu' );
180 + add_filter( 'parent_file', 'wplng_option_page_parent_file' );
181 + add_filter( 'submenu_file', 'wplng_option_page_submenu_file' );
182 + add_action( 'admin_head', 'wplng_option_page_hide_submenu' );
92 183
93 184 // Add admin Bar menu
94 185 add_action( 'admin_bar_menu', 'wplng_admin_bar_menu', 81 );
186 + add_action( 'admin_bar_menu', 'wplng_admin_bar_edit', 81 );
95 187
188 + // Switcher in nav menu options
189 + add_action( 'admin_enqueue_scripts', 'wplng_switcher_nav_menu_inline_scripts' );
190 + add_action( 'admin_head-nav-menus.php', 'wp_nav_menu_switcher_box_add_register' );
191 + add_action( 'wp_nav_menu_item_custom_fields', 'wp_nav_menu_switcher_box_edit', 10, 2 );
192 +
96 193 // Enqueue CSS and JS files for option pages
97 194 add_action( 'admin_enqueue_scripts', 'wplng_option_page_settings_assets' );
195 + add_action( 'admin_enqueue_scripts', 'wplng_option_page_switcher_assets' );
98 196 add_action( 'admin_enqueue_scripts', 'wplng_option_page_exclusions_assets' );
99 - add_action( 'admin_enqueue_scripts', 'wplng_option_page_switcher_assets' );
197 + add_action( 'admin_enqueue_scripts', 'wplng_option_page_dictionary_assets' );
198 + add_action( 'admin_enqueue_scripts', 'wplng_option_page_link_media_assets' );
100 199
101 200 // Update flags URL
102 201 add_action( 'update_option_wplng_flags_style', 'wplng_options_switcher_update_flags_style', 10, 2 );
103 202
203 + // Update translations on dictionary changes
204 + add_action( 'update_option_wplng_dictionary_entries', 'wplng_on_dictionary_entries_updated', 10, 2 );
205 + add_action( 'wp_ajax_wplng_dictionary_update_translations', 'wplng_ajax_dictionary_update_translations' );
206 +
104 207 // Reset API data on API key changing
105 208 add_action( 'update_option_wplng_api_key', 'wplng_on_update_option_wplng_api_key', 10, 2 );
106 209
210 + // Add edit link on page and post list
211 + add_filter( 'page_row_actions', 'wplng_row_edit_translation_link', 10, 2 );
212 + add_filter( 'post_row_actions', 'wplng_row_edit_translation_link', 10, 2 );
213 +
214 + // Notice for obtaining the PRO version
215 + // add_action( 'admin_notices', 'wplng_admin_notice_get_pro_version', 1 );
216 +
107 217 /**
108 218 * wplng_translation : CPT, taxo, meta
109 219 */
110 220
@@ -121,12 +231,17 @@
121 231 add_action( 'trashed_post', 'wplng_clear_translations_cache_trash_untrash' );
122 232 add_action( 'untrash_post', 'wplng_clear_translations_cache_trash_untrash' );
123 233 add_action( 'delete_post', 'wplng_clear_translations_cache_trash_untrash' );
124 234
125 - // Enqueue Script for wplng_translation admin
126 - add_action( 'admin_print_scripts-post-new.php', 'wplng_translation_assets' );
127 - add_action( 'admin_print_scripts-post.php', 'wplng_translation_assets' );
235 + // Enqueue Script for wplng_translation admin: Edit
236 + add_action( 'admin_print_scripts-post.php', 'wplng_translation_edit_assets' );
128 237
238 + // Enqueue Script for wplng_translation admin: List
239 + add_action( 'admin_print_scripts-edit.php', 'wplng_translation_list_assets' );
240 +
241 + // Only show translations for the website languages
242 + add_action( 'pre_get_posts', 'wplng_filter_wplng_translation_posts' );
243 +
129 244 // Remove Quick edit from translations list
130 245 add_filter( 'post_row_actions', 'wplng_translation_remove_quick_edit', 10, 2 );
131 246
132 247 // Ajax function for regenerate translation on edit page
@@ -131,12 +246,78 @@
131 246
132 247 // Ajax function for regenerate translation on edit page
133 248 add_action( 'wp_ajax_wplng_ajax_translation', 'wplng_ajax_generate_translation' );
134 249
250 + // Ajax function for edit modal: Get HTML modal
251 + add_action( 'wp_ajax_wplng_ajax_edit_modal', 'wplng_ajax_edit_modal' );
252 +
253 + // Ajax function for edit modal: Save modal
254 + add_action( 'wp_ajax_wplng_ajax_save_modal', 'wplng_ajax_save_modal' );
255 +
256 + // Ajax function for edit modal: HeartBeat
257 + add_action( 'wp_ajax_wplng_ajax_heartbeat', 'wplng_ajax_heartbeat' );
258 + add_action( 'wp_ajax_nopriv_wplng_ajax_heartbeat', 'wplng_ajax_heartbeat' );
259 +
135 260 // Display 100 translation in admin area by default
136 261 add_filter( 'get_user_option_edit_wplng_translation_per_page', 'wplng_translation_per_page' );
137 262
263 + // Filter translations by status
264 + add_action( 'restrict_manage_posts', 'wplng_restrict_manage_posts_translation_status' );
265 + add_filter( 'parse_query', 'wplng_posts_filter_translation_status' );
266 +
267 + // Translation status on website translations list
268 + add_filter( 'post_class', 'wplng_post_class_translation_status', 10, 3 );
269 + add_filter( 'post_row_actions', 'wplng_post_row_actions_translation_status', 10, 2 );
270 + add_filter( 'manage_wplng_translation_posts_columns', 'wplng_translation_status_columns' );
271 + add_action( 'manage_wplng_translation_posts_custom_column', 'wplng_translation_status_item', 10, 2 );
272 +
138 273 /**
274 + * wplng_slug : CPT, taxo, meta
275 + */
276 +
277 + // Register wplng_translation CPT
278 + add_action( 'init', 'wplng_register_post_type_slug' );
279 +
280 + // Add metabox for wplng_slug
281 + add_action( 'add_meta_boxes_wplng_slug', 'wplng_slug_add_meta_box' );
282 +
283 + // Save metabox on posts saving
284 + add_action( 'save_post_wplng_slug', 'wplng_slug_save_meta_boxes_data', 10, 2 );
285 +
286 + // Clear slugs cache on trash / untrash
287 + add_action( 'trashed_post', 'wplng_clear_slugs_cache_trash_untrash' );
288 + add_action( 'untrash_post', 'wplng_clear_slugs_cache_trash_untrash' );
289 + add_action( 'delete_post', 'wplng_clear_slugs_cache_trash_untrash' );
290 +
291 + // Enqueue Script for wplng_slug admin: Edit
292 + add_action( 'admin_print_scripts-post.php', 'wplng_slug_edit_assets' );
293 +
294 + // Enqueue Script for wplng_slug admin: List
295 + add_action( 'admin_print_scripts-edit.php', 'wplng_slug_list_assets' );
296 +
297 + // Only show slugs for the website languages
298 + add_action( 'pre_get_posts', 'wplng_filter_wplng_slug_posts' );
299 +
300 + // Remove Quick edit from slugs list
301 + add_filter( 'post_row_actions', 'wplng_slug_remove_quick_edit', 10, 2 );
302 +
303 + // Ajax function for regenerate slug on edit page
304 + add_action( 'wp_ajax_wplng_ajax_slug', 'wplng_ajax_generate_slug' );
305 +
306 + // Display 100 translation in admin area by default
307 + add_filter( 'get_user_option_edit_wplng_slug_per_page', 'wplng_slug_per_page' );
308 +
309 + // Filter slugs by status
310 + add_action( 'restrict_manage_posts', 'wplng_restrict_manage_posts_slug_status' );
311 + add_filter( 'parse_query', 'wplng_posts_filter_slug_status' );
312 +
313 + // Translation status on website slugs list
314 + add_filter( 'post_class', 'wplng_post_class_slug_status', 10, 3 );
315 + add_filter( 'post_row_actions', 'wplng_post_row_actions_slug_status', 10, 2 );
316 + add_filter( 'manage_wplng_slug_posts_columns', 'wplng_slug_status_columns' );
317 + add_action( 'manage_wplng_slug_posts_custom_column', 'wplng_slug_status_item', 10, 2 );
318 +
319 + /**
139 320 * Front
140 321 */
141 322
142 323 // Enqueue CSS and JS files
@@ -141,45 +322,137 @@
141 322
142 323 // Enqueue CSS and JS files
143 324 add_action( 'wp_enqueue_scripts', 'wplng_register_assets' );
144 325
326 + // Add custom inline styles (option page: switcher)
327 + add_action( 'wp_head', 'wplng_add_custom_inline_styles', 50 );
328 +
329 + // Script JS in page
330 + add_action( 'wp_footer', 'wplng_on_page_script' );
331 +
145 332 // Add languages switcher before </body>
146 333 add_action( 'wp_footer', 'wplng_switcher_wp_footer' );
147 334
335 + // Add languages switcher in nav menu
336 + add_filter( 'wp_nav_menu_objects', 'wplng_switcher_nav_menu_replace_items' );
337 + add_filter( 'nav_menu_link_attributes', 'wplng_add_nav_menu_link_attributes_atts', 10, 2 );
338 +
148 339 // Set alternate links with hreflang parametters
149 - add_action( 'wp_head', 'wplng_link_alternate_hreflang' );
340 + add_action( 'wp_head', 'wplng_link_alternate_hreflang', 1 );
150 341
342 + // Disable web browser automatic translation
343 + add_action( 'language_attributes', 'wplng_disable_web_browser_auto_translate' );
344 +
345 + // Ajax function for Load in progress
346 + add_action( 'wp_ajax_wplng_load_in_progress', 'wplng_ajax_load_in_progress' );
347 +
151 348 /**
152 349 * OB and REQUEST_URI
153 350 */
154 351
155 - // Manage URL with REQUEST_URI and start OB
352 + // Manage URL with REQUEST_URI and start OB
156 353 add_action( 'init', 'wplng_ob_start', 1 );
157 354
355 + // Redirect page if is called with an untranslate slug
356 + add_action( 'template_redirect', 'wplng_redirect_translated_slug' );
357 +
358 + // Translate WooCommerce cart URL
359 + add_filter( 'woocommerce_get_cart_url', 'wplng_url_translate' );
360 +
158 361 /**
362 + * Generate JSON translation on the fly for wp-i18n.js
363 + */
364 +
365 + // Generate JSON translation
366 + add_filter( 'load_script_translation_file', 'wplng_load_script_translation_file', 20, 3 );
367 +
368 + /**
369 + * Clear wpLingua cache on updates
370 + */
371 +
372 + // Clear wpLingua cache on WordPress core, plugins, themes or translations update
373 + add_action( 'upgrader_process_complete', 'wplng_clear_cache_on_update', 10, 2 );
374 +
375 + // Clear wpLingua cache on plugin activation/deactivation
376 + add_action( 'activated_plugin', 'wplng_clear_folder_cache' );
377 + add_action( 'deactivated_plugin', 'wplng_clear_folder_cache' );
378 +
379 + // Clear wpLingua cache on theme switch
380 + add_action( 'switch_theme', 'wplng_clear_folder_cache' );
381 +
382 + // Clear wpLingua cache on site language change
383 + add_action( 'update_option_WPLANG', 'wplng_clear_folder_cache' );
384 +
385 + // Clear wpLingua cache on wpLingua settings change
386 + add_action( 'update_option_wplng_website_language', 'wplng_clear_folder_cache' );
387 + add_action( 'update_option_wplng_target_languages', 'wplng_clear_folder_cache' );
388 +
389 + /**
159 390 * Features
160 391 */
161 392
393 + // Make multilingua Sitemap XML for All In One SEO plugin
394 + // Il other case, multilingual Sitemap is make by output buffering
395 + if ( get_option( 'wplng_sitemap_xml', true )
396 + && function_exists( 'aioseo' )
397 + ) {
398 + add_filter( 'aioseo_sitemap_post', 'wplng_aioseo_filter_sitemap_post', 10, 2 );
399 + add_filter( 'aioseo_sitemap_term', 'wplng_aioseo_filter_sitemap_term', 10, 2 );
400 + }
401 +
162 402 // Search from translated languages
163 403 if ( ! empty( get_option( 'wplng_translate_search' ) )
164 404 && wplng_api_feature_is_allow( 'search' )
165 405 ) {
166 406 add_action( 'parse_query', 'wplng_translate_search_query' );
407 + add_filter( 'get_search_query', 'wplng_search_put_tag' );
408 + add_filter( 'wplng_translated_html', 'wplng_search_replace_tag', 5 );
167 409 } else {
168 410 add_filter( 'wplng_url_is_translatable', 'wplng_exclude_search', 20 );
169 411 }
170 412
171 - // Woocommerce
172 - if ( empty( get_option( 'wplng_translate_woocommerce' ) ) ) {
173 - add_filter( 'wplng_url_exclude_regex', 'wplng_exclude_woocommerce_url', 20 );
413 + // Redirect by browser language
414 + $browser_language_redirect = get_option( 'wplng_browser_language_redirect' );
415 + if ( $browser_language_redirect === 'js_only' ) {
416 + add_action( 'wp_head', 'wplng_browser_language_redirect_js_only', 2 );
417 + } elseif ( $browser_language_redirect === 'php_js' ) {
418 + add_action( 'template_redirect', 'wplng_browser_language_redirect_php_js' );
174 419 }
175 420
176 421 /**
177 422 * Shortcode
178 423 */
424 +
179 425 add_shortcode( 'wplng_switcher', 'wplng_shortcode_switcher' );
180 426 add_shortcode( 'wplng_notranslate', 'wplng_shortcode_notranslate' );
427 + add_shortcode( 'wplng_only', 'wplng_shortcode_only' );
181 428
429 + /**
430 + * Gutenberg
431 + */
432 +
433 + add_filter( 'block_categories_all', 'wplng_block_category' );
434 + add_action( 'init', 'wplng_register_block' );
435 + add_action( 'enqueue_block_editor_assets', 'wplng_register_block_assets' );
436 +
437 + /**
438 + * Do action after wpLingua fully initialized
439 + */
440 +
441 + do_action( 'wplng_initialized' );
442 +
182 443 }
444 +}
445 +wplng_start();
183 446
447 +
448 +/**
449 + * Delete translation and slug cache options on plugin deactivation.
450 + *
451 + * @return void
452 + */
453 +function wplng_deactivate() {
454 + delete_option( 'wplng_cached_translations' );
455 + delete_option( 'wplng_cached_slugs' );
456 + delete_option( 'wplng_api_key_data' );
184 457 }
185 -wplng_start();
458 +register_deactivation_hook( __FILE__, 'wplng_deactivate' );