Файловый менеджер - Редактировать - /home/tuudkjt/globeasy/wp-includes/ID3/translatepress-multilingual.tar
Назад
class-translate-press.php 0000777 00000105367 15251156640 0011537 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Translate_Press * * Singleton. Loads required files, initializes components and hooks methods. * */ class TRP_Translate_Press{ protected $loader; protected $settings; protected $translation_render; protected $machine_translator; protected $machine_translator_logger; protected $query; protected $language_switcher; protected $translation_manager; protected $editor_api_regular_strings; protected $editor_api_gettext_strings; protected $url_converter; protected $languages; protected $slug_manager; protected $upgrade; protected $plugin_updater; protected $plugin_optin; protected $license_page; protected $advanced_tab; protected $translation_memory; protected $machine_translation_tab; protected $error_manager; protected $string_translation; protected $string_translation_api_regular; protected $notifications; protected $search; protected $install_plugins; protected $reviews; protected $gettext_manager; protected $gettext_scan; protected $rewrite_rules; protected $check_invalid_text; protected $woocommerce_emails; protected $preferred_user_language; protected $gutenberg_blocks; protected $onboarding_setup; protected $language_switcher_tab; public $tp_product_name = array(); public static $translate_press = null; /** * Get singleton object. * * @return TRP_Translate_Press Singleton object. */ public static function get_trp_instance(){ if ( self::$translate_press == null ){ self::$translate_press = new TRP_Translate_Press(); } return self::$translate_press; } /** * TRP_Translate_Press constructor. */ public function __construct() { // Early bind to break recursion loop caused by calling get_trp_instance during construction if ( self::$translate_press === null ) { self::$translate_press = $this; } define( 'TRP_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'TRP_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'TRP_PLUGIN_BASE', plugin_basename( __DIR__ . '/index.php' ) ); define( 'TRP_PLUGIN_SLUG', 'translatepress-multilingual' ); define( 'TRP_PLUGIN_VERSION', '3.1' ); wp_cache_add_non_persistent_groups(array('trp')); $this->load_dependencies(); $this->initialize_components(); $this->set_tp_product_name(); $this->define_admin_hooks(); $this->define_frontend_hooks(); } /** * Returns particular component by name. * * @param string $component 'loader' | 'settings' | 'translation_render' | * 'machine_translator' | 'query' | 'language_switcher' | * 'translation_manager' | 'url_converter' | 'languages' * @return mixed */ public function get_component( $component ){ return $this->$component; } /** * Includes necessary files. */ protected function load_dependencies() { require_once TRP_PLUGIN_DIR . 'includes/class-settings.php'; require_once TRP_PLUGIN_DIR . 'includes/class-translation-manager.php'; require_once TRP_PLUGIN_DIR . 'includes/class-editor-api-regular-strings.php'; require_once TRP_PLUGIN_DIR . 'includes/class-editor-api-gettext-strings.php'; require_once TRP_PLUGIN_DIR . 'includes/class-hooks-loader.php'; require_once TRP_PLUGIN_DIR . 'includes/class-languages.php'; require_once TRP_PLUGIN_DIR . 'includes/class-translation-render.php'; require_once TRP_PLUGIN_DIR . 'includes/class-language-switcher.php'; require_once TRP_PLUGIN_DIR . 'includes/class-language-switcher-v2.php'; require_once TRP_PLUGIN_DIR . 'includes/class-machine-translator.php'; require_once TRP_PLUGIN_DIR . 'includes/class-machine-translator-logger.php'; require_once TRP_PLUGIN_DIR . 'includes/queries/class-query.php'; require_once TRP_PLUGIN_DIR . 'includes/queries/class-gettext-normalization.php'; require_once TRP_PLUGIN_DIR . 'includes/queries/class-gettext-table-creation.php'; require_once TRP_PLUGIN_DIR . 'includes/queries/class-gettext-insert-update.php'; require_once TRP_PLUGIN_DIR . 'includes/queries/class-regular-delete.php'; require_once TRP_PLUGIN_DIR . 'includes/queries/class-gettext-delete.php'; require_once TRP_PLUGIN_DIR . 'includes/class-url-converter.php'; require_once TRP_PLUGIN_DIR . 'includes/class-uri.php'; require_once TRP_PLUGIN_DIR . 'includes/class-upgrade.php'; require_once TRP_PLUGIN_DIR . 'includes/class-plugin-notices.php'; require_once TRP_PLUGIN_DIR . 'includes/class-advanced-tab.php'; require_once TRP_PLUGIN_DIR . 'includes/class-translation-memory.php'; require_once TRP_PLUGIN_DIR . 'includes/class-error-manager.php'; require_once TRP_PLUGIN_DIR . 'includes/external-functions.php'; require_once TRP_PLUGIN_DIR . 'includes/compatibility-functions.php'; require_once TRP_PLUGIN_DIR . 'includes/functions.php'; require_once TRP_PLUGIN_DIR . 'includes/custom-language.php'; require_once TRP_PLUGIN_DIR . 'assets/lib/simplehtmldom/simple_html_dom.php'; require_once TRP_PLUGIN_DIR . 'includes/shortcodes.php'; require_once TRP_PLUGIN_DIR . 'includes/class-machine-translation-tab.php'; require_once TRP_PLUGIN_DIR . 'includes/string-translation/class-string-translation.php'; require_once TRP_PLUGIN_DIR . 'includes/string-translation/class-string-translation-helper.php'; require_once TRP_PLUGIN_DIR . 'includes/string-translation/class-gettext-scan.php'; require_once TRP_PLUGIN_DIR . 'includes/class-search.php'; require_once TRP_PLUGIN_DIR . 'includes/class-install-plugins.php'; require_once TRP_PLUGIN_DIR . 'includes/class-reviews.php'; require_once TRP_PLUGIN_DIR . 'includes/gettext/class-gettext-manager.php'; require_once TRP_PLUGIN_DIR . 'includes/gettext/class-process-gettext.php'; require_once TRP_PLUGIN_DIR . 'includes/gettext/class-plural-forms.php'; require_once TRP_PLUGIN_DIR . 'includes/class-rewrite-rules.php'; require_once TRP_PLUGIN_DIR . 'includes/class-check-invalid-text.php'; require_once TRP_PLUGIN_DIR . 'includes/class-woocommerce-emails.php'; require_once TRP_PLUGIN_DIR . 'includes/string-translation/class-string-translation-api-gettext.php'; require_once TRP_PLUGIN_DIR . 'includes/string-translation/class-string-translation-api-regular.php'; require_once TRP_PLUGIN_DIR . 'assets/lib/tp-add-ons-listing/tp-add-ons-listing.php'; require_once TRP_PLUGIN_DIR . 'includes/class-plugin-optin.php'; require_once TRP_PLUGIN_DIR . 'includes/class-preferred-user-language.php'; require_once TRP_PLUGIN_DIR . 'includes/gutenberg-blocks/class-gutenberg-blocks.php'; require_once TRP_PLUGIN_DIR . 'includes/class-onboarding.php'; require_once TRP_PLUGIN_DIR . 'includes/class-language-switcher-tab.php'; require_once TRP_PLUGIN_DIR . 'includes/class-support-chat.php'; if ( did_action( 'elementor/loaded' ) ) require_once TRP_PLUGIN_DIR . 'includes/class-elementor-language-for-blocks.php'; if ( defined( 'WPB_VC_VERSION' ) ) { require_once TRP_PLUGIN_DIR . 'includes/class-wp-bakery-language-for-blocks.php'; } } /** * Instantiates components. */ protected function initialize_components() { $this->loader = new TRP_Hooks_Loader(); $this->languages = new TRP_Languages(); $this->settings = new TRP_Settings(); $this->plugin_optin = new TRP_Plugin_Optin(); $this->advanced_tab = new TRP_Advanced_Tab($this->settings->get_settings()); $this->advanced_tab->include_custom_codes(); $this->machine_translation_tab = new TRP_Machine_Translation_Tab( $this->settings->get_settings() ); $this->machine_translation_tab->load_engines(); $this->language_switcher_tab = new TRP_Language_Switcher_Tab( $this->settings->get_settings() ); $this->translation_render = new TRP_Translation_Render( $this->settings->get_settings() ); $this->url_converter = new TRP_Url_Converter( $this->settings->get_settings() ); $this->query = new TRP_Query( $this->settings->get_settings() ); $this->machine_translator_logger = new TRP_Machine_Translator_Logger( $this->settings->get_settings() ); $this->machine_translator = new TRP_Machine_Translator( $this->settings->get_settings() ); // Will be overwritten in init_machine_translation with the actual machine translator class. Use this as replacement until then. $this->translation_manager = new TRP_Translation_Manager( $this->settings->get_settings() ); $this->editor_api_regular_strings = new TRP_Editor_Api_Regular_Strings( $this->settings->get_settings() ); $this->editor_api_gettext_strings = new TRP_Editor_Api_Gettext_Strings( $this->settings->get_settings() ); $this->notifications = new TRP_Trigger_Plugin_Notifications( $this->settings->get_settings() ); $this->upgrade = new TRP_Upgrade( $this->settings->get_settings() ); $this->plugin_updater = new TRP_Plugin_Updater(); $this->license_page = new TRP_LICENSE_PAGE(); $this->translation_memory = new TRP_Translation_Memory( $this->settings->get_settings() ); $this->error_manager = new TRP_Error_Manager( $this->settings->get_settings() ); $this->string_translation = new TRP_String_Translation( $this->settings->get_settings(), $this->loader ); $this->gettext_scan = new TRP_Gettext_Scan( $this->settings->get_settings() ); $this->search = new TRP_Search( $this->settings->get_settings() ); $this->install_plugins = new TRP_Install_Plugins(); $this->reviews = new TRP_Reviews( $this->settings->get_settings() ); $this->gettext_manager = new TRP_Gettext_Manager( $this->settings->get_settings() ); $this->rewrite_rules = new TRP_Rewrite_Rules( $this->settings->get_settings() ); $this->check_invalid_text = new TRP_Check_Invalid_Text( ); $this->woocommerce_emails = new TRP_Woocommerce_Emails(); $this->preferred_user_language = new TRP_Preferred_User_Language(); $this->onboarding_setup = new TRP_Onboarding( $this->settings->get_settings() ); //Gutenberg Blocks global $wp_version; if ( version_compare( $wp_version, "5.0.0", ">=" ) && apply_filters( 'trp_initialize_gutenberg_blocks', true ) ) { $this->gutenberg_blocks = new TRP_Gutenberg_Blocks( $this->settings->get_settings() ); } } /**Made this function static so it can be called without initializing this class * * We use this function to detect if we have any addons that require a license * Used throughout the plugin to detect the version for notifications, license checks, different messages. * Sets $this->tp_product_name that is different from TRANSLATE_PRESS constant. */ public static function set_tp_product_name_static(){ // by default, set tp_product_name is not set. // will be overwritten by active plugin names OR based on TRANSLATE_PRESS constant $tp_product_name = array(); // the names of your product should match the download names in EDD exactly // The order is important because we only match the last one. $trp_all_tp_product_names = array( "translatepress-multilingual" => "TranslatePress", "translatepress-business" => "TranslatePress Business", "translatepress-developer" => "TranslatePress Developer", "translatepress-personal" => "TranslatePress Personal", ); $active_plugins = get_option('active_plugins'); $last_found_product_name = array(); foreach ( $trp_all_tp_product_names as $trp_tp_product_folder => $trp_tp_product_name ){ foreach( $active_plugins as $active_plugin ){ if( strpos( $active_plugin, $trp_tp_product_folder.'/' ) === 0 ){ $last_found_product_name = array($trp_tp_product_folder => $trp_tp_product_name); break; } } } $tp_product_name = $last_found_product_name; /* * For the dev version simulate the business version * * TRANSLATE_PRESS possible values: * TranslatePress * TranslatePress - Dev * TranslatePress - Personal * TranslatePress - Business * TranslatePress - Developer */ if (defined('TRANSLATE_PRESS') && TRANSLATE_PRESS === 'TranslatePress - Dev') { $tp_product_name = array("translatepress-business" => "TranslatePress Business"); } elseif (defined('TRANSLATE_PRESS') && TRANSLATE_PRESS === 'TranslatePress') { $tp_product_name = array("translatepress-multilingual" => "TranslatePress"); } elseif (defined('TRANSLATE_PRESS') && TRANSLATE_PRESS === 'TranslatePress - Personal') { $tp_product_name = array("translatepress-personal" => "TranslatePress Personal"); } elseif (defined('TRANSLATE_PRESS') && TRANSLATE_PRESS === 'TranslatePress - Business') { $tp_product_name = array("translatepress-business" => "TranslatePress Business"); } elseif (defined('TRANSLATE_PRESS') && TRANSLATE_PRESS === 'TranslatePress - Developer') { $tp_product_name = array("translatepress-developer" => "TranslatePress Developer"); } // default fallback, if tp_product_name not yet set, force set it to TranslatePress business if (!$tp_product_name) { $tp_product_name = array("translatepress-business" => "TranslatePress Business"); } return $tp_product_name; } // duplicate wrapper for the static function set_tp_product_name_static public function set_tp_product_name(){ $this->tp_product_name = self::set_tp_product_name_static(); } /** * Hooks methods used in admin area. */ protected function define_admin_hooks() { $this->loader->add_action( 'admin_menu', $this->settings, 'register_menu_page' ); $this->loader->add_action( 'admin_init', $this->settings, 'register_setting' ); $this->loader->add_action( 'admin_notices', $this->settings, 'admin_notices' ); $this->loader->add_action( 'admin_enqueue_scripts', $this->settings, 'enqueue_scripts_and_styles', 10, 1 ); $this->loader->add_filter( 'plugin_action_links_' . TRP_PLUGIN_BASE , $this->settings, 'plugin_action_links', 10, 1 ); $this->loader->add_action( 'trp_settings_navigation_tabs', $this->settings, 'add_navigation_tabs' ); $this->loader->add_action( 'trp_settings_navigation_tabs', $this->settings, 'add_svg_icons' ); $this->loader->add_action( 'trp_language_selector', $this->settings, 'languages_selector', 10, 1 ); $this->loader->add_action( 'plugins_loaded', $this->settings, 'disable_languages_selector', 10, 1 ); $this->loader->add_action( 'trp_settings_tabs', $this->advanced_tab, 'add_advanced_tab_to_settings', 10, 1 ); $this->loader->add_action( 'admin_menu', $this->advanced_tab, 'add_submenu_page_advanced' ); $this->loader->add_action( 'trp_output_advanced_settings_options', $this->advanced_tab, 'output_advanced_options' ); $this->loader->add_action( 'trp_before_output_advanced_settings_options', $this->advanced_tab, 'trp_advanced_settings_content_table' ); $this->loader->add_action( 'admin_init', $this->advanced_tab, 'register_setting' ); $this->loader->add_action( 'admin_notices', $this->advanced_tab, 'admin_notices' ); //Machine Translation tab $this->loader->add_action( 'trp_settings_tabs', $this->machine_translation_tab, 'add_tab_to_navigation', 10, 1 ); $this->loader->add_action( 'admin_menu', $this->machine_translation_tab, 'add_submenu_page' ); $this->loader->add_action( 'admin_init', $this->machine_translation_tab, 'register_setting' ); $this->loader->add_action( 'admin_notices', $this->machine_translation_tab, 'admin_notices' ); $this->loader->add_action( 'trp_machine_translation_extra_settings_bottom', $this->machine_translation_tab, 'display_unsupported_languages' ); //Machine Translation Logger defaults $this->loader->add_action( 'trp_machine_translation_sanitize_settings', $this->machine_translator_logger, 'sanitize_settings', 10, 1 ); //Error manager hooks $this->loader->add_action( 'admin_init', $this->error_manager, 'show_notification_about_errors', 10 ); $this->loader->add_action( 'admin_menu', $this->error_manager, 'register_submenu_errors_page', 10 ); $this->loader->add_action( 'trp_dismiss_notification', $this->error_manager, 'clear_notification_from_db', 10, 2 ); $this->loader->add_filter( 'trp_machine_translation_sanitize_settings', $this->error_manager, 'clear_disable_machine_translation_notification_from_db', 10, 1 ); $this->loader->add_filter( 'trp_error_manager_page_output', $this->error_manager, 'show_instructions_on_how_to_fix', 7, 1 ); $this->loader->add_filter( 'trp_error_manager_page_output', $this->error_manager, 'output_db_errors', 10, 1 ); $this->loader->add_action('load-admin_page_trp_error_manager', $this->error_manager, 'disable_error_after_click_link', 10); $this->loader->add_action( 'wp_ajax_nopriv_trp_get_translations_regular', $this->editor_api_regular_strings, 'get_translations' ); $this->loader->add_action( 'wp_ajax_trp_get_translations_regular', $this->editor_api_regular_strings, 'get_translations' ); $this->loader->add_action( 'wp_ajax_trp_save_translations_regular', $this->editor_api_regular_strings, 'save_translations' ); $this->loader->add_action( 'wp_ajax_trp_split_translation_block', $this->editor_api_regular_strings, 'split_translation_block' ); $this->loader->add_action( 'wp_ajax_trp_create_translation_block', $this->editor_api_regular_strings, 'create_translation_block' ); $this->loader->add_action( 'wp_ajax_trp_get_translations_gettext', $this->editor_api_gettext_strings, 'gettext_get_translations' ); $this->loader->add_action( 'wp_ajax_trp_save_translations_gettext', $this->editor_api_gettext_strings, 'gettext_save_translations' ); $this->loader->add_action( 'wp_ajax_trp_get_similar_string_translation', $this->translation_memory, 'ajax_get_similar_string_translation' ); $this->loader->add_action( 'wp_ajax_trp_scan_gettext', $this->gettext_scan, 'scan_gettext' ); $this->loader->add_filter( 'trp_get_existing_translations', $this->translation_manager, 'display_possible_db_errors', 20, 3 ); $this->loader->add_action( 'wp_ajax_trp_save_editor_user_meta', $this->translation_manager, 'save_editor_user_meta', 10 ); $this->loader->add_action( 'trp_editor_notices', $this->translation_manager, 'display_notice_to_upgrade_gettext_in_editor', 10, 1 ); $this->loader->add_action( 'trp_editor_notices', $this->translation_manager, 'display_notice_to_upgrade_slugs_in_editor', 10, 1 ); $this->loader->add_action( 'wp_ajax_trp_process_js_strings_in_translation_editor', $this->translation_render, 'process_js_strings_in_translation_editor' ); $this->loader->add_filter( 'trp_skip_selectors_from_dynamic_translation', $this->translation_render, 'skip_base_attributes_from_dynamic_translation', 10, 1 ); $this->loader->add_action( 'admin_menu', $this->upgrade, 'register_menu_page' ); $this->loader->add_action( 'admin_init', $this->upgrade, 'show_admin_error_message' ); $this->loader->add_action( 'admin_init', $this->upgrade, 'show_admin_notice' ); $this->loader->add_action( 'admin_init', $this->upgrade, 'show_notification_about_add_ons_removal' ); $this->loader->add_action( 'admin_init', $this->upgrade, 'trp_prepare_options_for_database_optimization' ); $this->loader->add_action( 'admin_init', $this->upgrade, 'show_language_switcher_v2_intro_notice' ); $this->loader->add_action( 'admin_enqueue_scripts', $this->upgrade, 'enqueue_update_script', 10, 1 ); $this->loader->add_action( 'wp_ajax_trp_update_database', $this->upgrade, 'trp_update_database' ); $this->loader->add_action( 'wp_ajax_trp_install_plugins', $this->install_plugins, 'install_plugins_request' ); /* add hooks for license operations */ if( !empty( $this->tp_product_name ) ) { $this->loader->add_action('admin_init', $this->plugin_updater, 'activate_license'); if(!array_key_exists('translatepress-multilingual', $this->tp_product_name)){ // check for license updates for paid licenses only. Accessing the License tab directly does the same thing. $this->loader->add_filter('pre_set_site_transient_update_plugins', $this->plugin_updater, 'check_license'); } $this->loader->add_action('admin_init', $this->plugin_updater, 'deactivate_license'); } /* add license page */ global $trp_license_page;//this global was used in the addons, so we need to use it here also so we don't initialize the license page multiple times (backward compatibility) if( !isset( $trp_license_page ) ) { $trp_license_page = $this->license_page; $this->loader->add_action('admin_menu', $this->license_page, 'license_menu'); $this->loader->add_action('admin_init', $this->license_page, 'register_license_setting'); } $this->loader->add_action( 'admin_init', $this->reviews, 'display_review_notice' ); $this->loader->add_action( 'trp_dismiss_notification', $this->reviews, 'dismiss_notification', 10, 2 ); // Filter rewrite rules for .htaccess $this->loader->add_filter( 'mod_rewrite_rules', $this->rewrite_rules, 'trp_remove_language_param', 100 ); // Add hooks for translating WooCommerce emails $this->loader->add_action( 'init', $this->woocommerce_emails, 'initialize_hooks' ); // Plugin optin $this->loader->add_action( 'admin_init', $this->plugin_optin, 'redirect_to_plugin_optin_page', 1 ); $this->loader->add_action( 'admin_menu', $this->plugin_optin, 'add_submenu_page_optin' ); $this->loader->add_action( 'admin_init', $this->plugin_optin, 'process_optin_actions', 10 ); $this->loader->add_action( 'activate_plugin', $this->plugin_optin, 'process_paid_plugin_activation', 10, 1 ); $this->loader->add_action( 'deactivated_plugin', $this->plugin_optin, 'process_paid_plugin_deactivation', 10, 1 ); $this->loader->add_action( 'trp_register_advanced_settings', $this->plugin_optin, 'setup_plugin_optin_advanced_setting', 1360, 1 ); $this->loader->add_action( 'trp_extra_sanitize_advanced_settings', $this->plugin_optin, 'process_plugin_optin_advanced_setting', 20, 3 ); $this->loader->add_action( 'show_user_profile', $this->preferred_user_language, 'always_use_this_language', 99, 1 ); $this->loader->add_action( 'edit_user_profile', $this->preferred_user_language, 'always_use_this_language', 99, 1 ); $this->loader->add_action( 'personal_options_update', $this->preferred_user_language, 'update_profile_fields', 99, 1 ); $this->loader->add_action( 'edit_user_profile_update', $this->preferred_user_language, 'update_profile_fields', 99, 1 ); $this->loader->add_filter( 'trp_wp_languages', $this->languages, 'add_extra_languages', 10, 1 ); } /** * Hooks methods used in front-end */ protected function define_frontend_hooks(){ //we do not need the plugin in cron requests ? if( wp_doing_cron() ) return; $this->loader->add_action( 'init', $this->translation_render, 'start_output_buffer', apply_filters( 'trp_start_output_buffer_priority', 0 ) ); $this->loader->add_action( 'wp_enqueue_scripts', $this->translation_render, 'enqueue_scripts', 10 ); $this->loader->add_action( 'wp_enqueue_scripts', $this->translation_render, 'enqueue_dynamic_translation', 1 ); $this->loader->add_filter( 'wp_redirect', $this->translation_render, 'force_preview_on_url_redirect', 99, 2 ); $this->loader->add_filter( 'wp_redirect', $this->translation_render, 'force_language_on_form_url_redirect', 99, 2 ); $this->loader->add_filter( 'trp_before_translate_content', $this->translation_render, 'force_preview_on_url_in_ajax', 10 ); $this->loader->add_filter( 'trp_before_translate_content', $this->translation_render, 'force_form_language_on_url_in_ajax', 20 ); /* handle CDATA str replacement from the content as it is messing up the renderer */ $this->loader->add_filter( "trp_before_translate_content", $this->translation_render, 'handle_cdata', 1000 ); $this->loader->add_action( "trp_set_translation_for_attribute", $this->translation_render, 'translate_image_srcset_attributes', 10, 3 ); $this->loader->add_filter( "trp_translateable_strings", $this->translation_render, 'antispambot_infinite_detection_fix', 10, 6 ); $this->loader->add_filter( "trp_allow_machine_translation_for_string", $this->translation_render, 'allow_machine_translation_for_string', 10, 4 ); $this->loader->add_filter( "trp_allow_machine_translation_for_string", $this->translation_render, 'skip_automatic_translation_for_no_auto_translation_selector', 10, 5 ); $this->loader->add_filter( "trp_allow_machine_translation_for_string", $this->translation_render, 'skip_strings_that_cannot_be_auto_translated', 10, 5 ); $this->loader->add_filter( "rest_pre_echo_response", $this->translation_render, 'handle_generic_rest_api_translations', 10, 3 ); $this->loader->add_filter( "oembed_response_data", $this->translation_render, 'oembed_response_data', 10, 4 ); /* add custom containers for post content and pots title so we can identify string that are part of them */ $this->loader->add_filter( "the_content", $this->translation_render, 'wrap_with_post_id', 1000 ); $this->loader->add_filter( "the_title", $this->translation_render, 'wrap_with_post_id', 1000, 2 ); // Load the appropriate language switcher conditionally if ( $this->language_switcher_tab->is_legacy_enabled() ) { $this->language_switcher = new TRP_Language_Switcher( $this->settings->get_settings(), $this ); $this->loader->add_action( 'wp_enqueue_scripts', $this->language_switcher, 'enqueue_language_switcher_scripts' ); $this->loader->add_action( 'wp_footer', $this->language_switcher, 'add_floater_language_switcher' ); $this->loader->add_filter( 'init', $this->language_switcher, 'register_ls_menu_switcher' ); $this->loader->add_action( 'wp_get_nav_menu_items', $this->language_switcher, 'ls_menu_permalinks', 10, 3 ); } else { $this->language_switcher = TRP_Language_Switcher_V2::instance( $this->settings->get_settings(), $this ); $this->loader->add_action( 'init', $this->language_switcher, 'init', 1 ); } $this->loader->add_action( 'trp_translation_manager_footer', $this->translation_manager, 'enqueue_scripts_and_styles' ); $this->loader->add_filter( 'template_include', $this->translation_manager, 'translation_editor', 99999 ); $this->loader->add_filter( 'option_date_format', $this->translation_manager, 'filter_the_date' ); $this->loader->add_action( 'wp_enqueue_scripts', $this->translation_manager, 'enqueue_preview_scripts_and_styles' ); $this->loader->add_action( 'admin_init', $this->translation_manager, 'maybe_dismiss_admin_bar_notification' ); $this->loader->add_action( 'admin_bar_menu', $this->translation_manager, 'add_shortcut_to_translation_editor', 90, 1 ); $this->loader->add_action( 'admin_head', $this->translation_manager, 'add_styling_to_admin_bar_button', 10 ); $this->loader->add_action( 'wp_head', $this->translation_manager, 'add_styling_to_admin_bar_button', 10 ); $this->loader->add_filter( 'show_admin_bar', $this->translation_manager, 'hide_admin_bar_when_in_editor', 90 ); $this->loader->add_action( 'enqueue_block_editor_assets', $this->translation_manager, 'trp_add_shortcut_to_trp_editor_gutenberg', 90); $this->loader->add_filter( 'template_include', $this->string_translation, 'string_translation_editor', 99999 ); $this->loader->add_filter( 'trp_string_types', $this->string_translation, 'register_string_types', 10, 1 ); $this->loader->add_filter( 'trp_editor_nonces', $this->string_translation, 'add_nonces_for_saving_translation', 10, 1 ); $this->loader->add_action( 'trp_string_translation_editor_footer', $this->string_translation, 'enqueue_scripts_and_styles' ); $this->loader->add_action( 'init', $this->string_translation, 'register_ajax_hooks' ); $this->loader->add_filter( 'home_url', $this->url_converter, 'add_language_to_home_url', 1, 4 ); $this->loader->add_action( 'wp_head', $this->url_converter, 'add_hreflang_to_head' ); $this->loader->add_filter( 'language_attributes', $this->url_converter, 'change_lang_attr_in_html_tag', 10, 1 ); $this->loader->add_filter('trp_is_file', $this->url_converter, 'does_url_contains_array', 10, 2); $this->loader->add_filter('trp_hreflang', $this->url_converter, 'replace_iso_2_with_iso_3_for_hreflang', 10, 2); $this->loader->add_filter('wp_footer', $this->url_converter, 'add_tp_language_lang_attribute', 1); $this->loader->add_filter( 'widget_text', null, 'do_shortcode', 11 ); $this->loader->add_filter( 'widget_text', null, 'shortcode_unautop', 11 ); /* handle dynamic texts with gettext */ $this->loader->add_filter( 'locale', $this->languages, 'change_locale', 99999 ); $this->loader->add_filter( 'plugin_locale', $this->languages, 'change_locale', 99999 ); $this->loader->add_action( 'init', $this->gettext_manager, 'create_gettext_translated_global' ); $this->loader->add_action( 'init', $this->gettext_manager, 'initialize_gettext_processing' ); $this->loader->add_action( 'trp_call_gettext_filters', $this->gettext_manager, 'verify_locale_of_loaded_textdomain' ); $this->loader->add_action( 'shutdown', $this->gettext_manager, 'machine_translate_gettext', 100 ); /* we need to treat the date_i18n function differently so we remove the gettext wraps */ $this->loader->add_filter( 'date_i18n', $this->gettext_manager, 'handle_date_i18n_function_for_gettext', 1, 4 ); /* strip esc_url() from gettext wraps */ $this->loader->add_filter( 'clean_url', $this->gettext_manager, 'trp_strip_gettext_tags_from_esc_url', 1, 3 ); /* strip sanitize_title() from gettext wraps and apply custom trp_remove_accents */ $this->loader->add_filter( 'sanitize_title', $this->gettext_manager, 'trp_sanitize_title', 1, 3 ); /* define an update hook here */ $this->loader->add_action( 'plugins_loaded', $this->upgrade, 'check_for_necessary_updates', 10 ); $this->loader->add_filter( 'trp_language_name', $this->languages, 'beautify_language_name', 10, 4 ); $this->loader->add_filter( 'trp_languages', $this->languages, 'reorder_languages', 10, 2 ); /* set up wp_mail hooks */ $this->loader->add_filter( 'wp_mail', $this->translation_render, 'wp_mail_filter', 1 ); /* hide php ors and notice when we are storing strings in db */ $this->loader->add_action( 'init', $this->translation_render, 'trp_debug_mode_off', 0 ); /* fix wptexturize to always replace with the default translated strings */ $this->loader->add_action( 'gettext_with_context', $this->translation_render, 'fix_wptexturize_characters', 999, 4 ); /* ?or init ? hook here where you can change the $current_user global */ $this->loader->add_action( 'init', $this->translation_manager, 'trp_view_as_user' ); /** * we need to modify the permalinks structure for woocommerce when we switch languages * when woo registers post_types and taxonomies in the rewrite parameter of the function they change the slugs of the items (they are localized with _x ) * we can't flush the permalinks on every page load so we filter the rewrite_rules option */ $this->loader->add_filter( "option_rewrite_rules", $this->url_converter, 'woocommerce_filter_permalinks_on_other_languages' ); $this->loader->add_filter( "option_woocommerce_permalinks", $this->url_converter, 'woocommerce_filter_permalink_option' ); $this->loader->add_filter( "pre_update_option_woocommerce_permalinks", $this->url_converter, 'prevent_permalink_update_on_other_languages', 10, 2 ); $this->loader->add_filter( "pre_update_option_rewrite_rules", $this->url_converter, 'delete_woocommerce_transient_permalink' ); $this->loader->add_filter( "pre_update_option_rewrite_rules", $this->url_converter, 'prevent_permalink_update_on_other_languages', 10, 2 ); /* add to the body class the current language */ $this->loader->add_filter( "body_class", $this->translation_manager, 'add_language_to_body_class' ); /* load textdomain */ $this->loader->add_action( "init", $this, 'init_translation', 8 ); // machine translation $this->loader->add_action( 'plugins_loaded', $this, 'init_machine_translation', 2 ); //search $this->loader->add_filter( 'pre_get_posts', $this->search, 'trp_search_filter', 99999999 ); $this->loader->add_filter( 'get_search_query', $this->search, 'trp_search_query', 10 ); /* prevent indexing edit translation preview pages */ $this->loader->add_action( 'trp_head', $this->translation_manager, 'output_noindex_tag', 100 ); $this->loader->add_action( 'wp_head', $this->translation_manager, 'output_noindex_tag', 100 ); } /** * Register hooks to WP. */ public function run() { /* * Hook that prevents running the hooks. Caution: some TP code like constructors of classes still run! */ $run_tp = apply_filters( 'trp_allow_tp_to_run', true, $this->loader ); if ( $run_tp ) { $this->loader->run(); } } /** * Load plugin textdomain */ public function init_translation(){ load_plugin_textdomain( 'translatepress-multilingual', false, basename(dirname(__FILE__)) . '/languages/' ); } public function init_machine_translation(){ $this->machine_translator = $this->machine_translation_tab->get_active_engine(); } } index.php 0000777 00000010521 15251156640 0006377 0 ustar 00 <?php /* Plugin Name: TranslatePress - Multilingual Plugin URI: https://translatepress.com/ Description: Experience a better way of translating your WordPress site using a visual front-end translation editor, with full support for WooCommerce and site builders. Version: 3.1 Author: Cozmoslabs, Razvan Mocanu, Madalin Ungureanu, Cristophor Hurduban Author URI: https://cozmoslabs.com/ Text Domain: translatepress-multilingual Domain Path: /languages License: GPL2 WC requires at least: 2.5.0 WC tested up to: 10.5.1 == Copyright == Copyright 2017 Cozmoslabs (www.cozmoslabs.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // Exit if accessed directly if ( !defined('ABSPATH' ) ) exit(); function trp_enable_translatepress(){ $enable_translatepress = true; $current_php_version = apply_filters( 'trp_php_version', phpversion() ); // 5.6.20 is the minimum version supported by WordPress if ( $current_php_version !== false && version_compare( $current_php_version, '5.6.20', '<' ) ){ $enable_translatepress = false; add_action( 'admin_menu', 'trp_translatepress_disabled_notice' ); } return apply_filters( 'trp_enable_translatepress', $enable_translatepress ); } if ( trp_enable_translatepress() ) { require_once plugin_dir_path( __FILE__ ) . 'class-translate-press.php'; /** License classes includes heresettings * Since version 1.4.6 * It need to be outside of a hook so it load before the classes that are in the addons, that we are trying to phase out */ require_once plugin_dir_path( __FILE__ ) . 'includes/class-edd-sl-plugin-updater.php'; /* make sure we execute our plugin before other plugins so the changes we make apply across the board */ add_action( 'plugins_loaded', 'trp_run_translatepress_hooks', 1 ); } function trp_run_translatepress_hooks(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp->run(); } function trp_translatepress_disabled_notice(){ echo '<div class="notice notice-error"><p>' . wp_kses( sprintf( __( '<strong>TranslatePress</strong> requires at least PHP version 5.6.20+ to run. It is the <a href="%s">minimum requirement of the latest WordPress version</a>. Please contact your server administrator to update your PHP version.','translatepress-multilingual' ), 'https://wordpress.org/about/requirements/' ), array( 'a' => array( 'href' => array() ), 'strong' => array() ) ) . '</p></div>'; } /** * Redirect users to the settings page on plugin activation */ add_action( 'activated_plugin', 'trp_plugin_activation_redirect' ); function trp_plugin_activation_redirect( $plugin ){ $trp_instance_for_tp_product_name = TRP_Translate_Press::get_trp_instance(); // redirect on free plugin activation - keep simple for now, more conditions to be added if ( !wp_doing_ajax() && $plugin == plugin_basename( __FILE__ ) ) { if (get_option('trp_onboarding_started') === false ){ // this could be used after we make the save settings function in onboarding to check onboarding completion // || get_option('trp_onboarding_completed') === 'false' || get_option('trp_onboarding_completed') == 'no' ) { add_option('trp_onboarding_started', 'yes'); wp_safe_redirect(admin_url('admin.php?page=trp-onboarding&step=welcome') ); exit(); } else { wp_safe_redirect(admin_url('options-general.php?page=translate-press')); exit(); } } } //This is for the DEV version if( file_exists(plugin_dir_path( __FILE__ ) . '/index-dev.php') ){ if(!array_key_exists('translatepress-multilingual', TRP_Translate_Press::set_tp_product_name_static() )){ // we only include this in instances where we simulate the business/developer version include_once( plugin_dir_path( __FILE__ ) . '/index-dev.php'); } } readme.txt 0000777 00000027544 15251156640 0006572 0 ustar 00 === Translate Multilingual sites - TranslatePress === Contributors: cozmoslabs, razvan.mo, madalin.ungureanu, sareiodata, cristophor Donate link: https://www.translatepress.com/ Tags: translate, translation, multilingual, automatic translation, bilingual, front-end translation, google translate, language Requires at least: 3.1.0 Tested up to: 6.9.1 Requires PHP: 7.4 Stable tag: 3.1 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Translate your entire site directly from the front-end and go multilingual. Full support for WooCommerce, page builders + Google Translate integration == Description == **Experience a better way to translate your WordPress site and go multilingual, directly from the front-end using a visual translation interface.** TranslatePress is a [WordPress translation plugin](https://translatepress.com/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) that anyone can use. The interface allows you to easily translate the entire page at once, including output from shortcodes, forms and page builders. It also works out of the box with WooCommerce. Built the WordPress way, TranslatePress - Multilingual is a GPL and self hosted translation plugin, meaning you'll own all your translations, forever. It's the fastest way to create a bilingual or [multilingual site](https://translatepress.com/how-to-create-a-multilingual-wordpress-site/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree). https://www.youtube.com/watch?v=EMBdXuyrZUA == Multilingual & Translation Features == * Translate all your website content directly from the front-end, in a friendly user interface (translations are displayed in real-time). * Fully compatible with all themes and plugins * Live preview of your translated pages, as you edit them. * Automatic translation support through [TranslatePress AI Free](https://translatepress.com/ai-free/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree), with a 2.000 AI words limit. * Integrates with Google Translate, allowing you to set up unlimited automatic translations using your own Google API key. * Fully customizable language switcher that you can display as a menu item, a floating dropdown, or place anywhere else using the Language Switcher block, or the **[language-switcher]** shortcode. * [Image translation](https://translatepress.com/docs/image-translation/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) support, for [translating images, sliders and other media](https://translatepress.com/translate-images-in-wordpress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree). * Support for both manual and automatic translation * Ability to [translate dynamic strings](https://translatepress.com/translate-dynamic-strings-wordpress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) (gettext) added by WordPress, plugins and themes. * Translate larger html blocks by merging strings into translation blocks. * Editorial control allowing you to publish your language only when all your translations are done * Conditional display content shortcode based on language [trp_language language="en_US"] English content only [/trp_language] * Possibility to [edit gettext strings](https://translatepress.com/edit-plugin-strings/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) from themes and plugins from English to English, without adding another language. Basically a string-replace functionality. * Translate only certain paths and [exclude content from being translated](https://translatepress.com/partially-translate-wordpress-exclude-posts-pages-products/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) * Translation Block feature in which you can translate multiple html elements together * Native **Gutenberg** support, so you can easily [translate Gutenberg blocks](https://translatepress.com/translate-gutenberg-blocks-in-wordpress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) * Out of the box [WooCommerce](https://translatepress.com/translate-woocommerce-products-translatepress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) compatibility * Use our [FREE Website Translation](https://translatepress.com/free-website-translation-tool-widget/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) tool/widget to browse any website into your own language. Note: this plugin uses the Google Translation API to translate the strings on your site. This feature can be enabled or disabled according to your preferences. Users with administrator rights have access to the following settings: * select default language of the website and one translation language, for bilingual sites * choose whether language switcher should display languages in their native names or English name * force custom links to open in current language * enable or disable url subdirectory for the default language * enable automatic translation via Google Translate == Powerful Translation Add-ons == TranslatePress - Multilingual has a range of [premium Add-ons](https://translatepress.com/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) that allow you to extend the power of this WordPress translation plugin: **Pro Add-ons** (available in the [premium versions](https://translatepress.com/pricing/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) only) * Full [TranslatePress AI](https://translatepress.com/ai/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) access - automatically translate your entire website without extra platforms to sign up for, API keys, and additional translation costs. Each premium version includes a set number of AI translated words you can use to instantly translate your site, saving you both time and money. * [Extra Languages](https://translatepress.com/docs/addons/multiple-languages/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) - allows you to add an unlimited number of translation languages, with the possibility to publish languages later after you complete the translation * [SEO Pack](https://translatepress.com/docs/addons/seo-pack/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) - lets you translate meta information (like page title, description, url slug, image alt tag, Twitter and Facebook Social Graph tags & more) for boosting your multilingual SEO and increase traffic. Works with all popular SEO plugins. * [Translator Accounts](https://translatepress.com/docs/addons/translator-accounts/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) - create or allow existing users to translate the site without admin rights * [Browse as User Role](https://translatepress.com/docs/addons/browse-as-role/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) - view and translate content that is visible only to a particular user role * [Navigation Based on Language](https://translatepress.com/docs/addons/navigate-based-language/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) - configure and display different menu items for different languages * [Automatic User Language Detection](https://translatepress.com/docs/addons/automatic-user-language-detection/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) - redirect first time visitors to their preferred language based on their browser settings or IP address * [DeepL Automatic Translation](https://translatepress.com/docs/addons/deepl-automatic-translation/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) - The DeepL Automatic Translation add-on lets you automatically translate your website through the DeepL API **Keyboard Shortcuts** * **CTRL ( ⌘ ) + S** – Save translation for the currently editing strings * **CTRL ( ⌘ ) + ALT + Z** – Discard all changes for the currently editing strings * **CTRL ( ⌘ ) + ALT + →** (Right Arrow) – Navigate to next string * **CTRL ( ⌘ ) + ALT + ←** (Left Arrow) – Navigate to previous string = Website = [translatepress.com](https://translatepress.com/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) = Documentation = [Visit TranslatePress WordPress Translation plugin documentation page](https://translatepress.com/docs/translatepress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) = Add-ons = [Add-ons](https://translatepress.com/docs/translatepress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) = Demo Site = You can test out TranslatePress - Multilingual plugin by [visiting our demo site](https://demo.translatepress.com/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) == Installation == 1. Upload the translatepress folder to the '/wp-content/plugins/' directory 2. Activate the plugin through the 'Plugins' menu in WordPress 3. Go to Settings -> TranslatePress and choose a translation language. 4. Open the front-end translation editor from the admin bar to translate your site. == Frequently Asked Questions == = Where are my translations stored? = All the translation are stored locally in your server's database. = What types of content can I translate? = TranslatePress - Multilingual plugin works out of the box with WooCommerce, custom post types, complex themes and site builders, so you'll be able to translate any type of content. = How is it different from other multilingual & translation plugins like WPML or Polylang? = TranslatePress is easier to use and more intuitive altogether. No more switching between the editor, string translation interfaces or badly translated plugins. You can now translate the full page content directly from the front-end. This makes TranslatePress a great alternative to plugins like Polylang and WPML. For more details check out this [WordPress Translation Plugin Comparison: TranslatePress vs WPML vs Polylang vs Gtranslate](https://translatepress.com/wordpress-translation-plugin-comparison-translatepress-vs-free-and-paid-alternatives/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree). = How do I start to translate my WordPress site? = After installing the plugin, select your secondary language and click "Translate Site" to start translating your entire site exactly as it looks in the front-end. = Will it slow down my website? = TranslatePress will have little impact on your site speed. For more details see [Top WordPress Translation Plugins Compared Based on Page Load Time](https://translatepress.com/top-wordpress-translation-plugins-compared-based-on-page-load-time/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) = Can you translate your WooCommerce store? = Yes, TranslatePress works out of the box with WooCommerce. You can use to build a [multilingual WooCommerce store](https://translatepress.com/translate-woocommerce-products-translatepress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree). = Where can I find out more information? = For more information please check out our [documentation](https://translatepress.com/docs/translatepress/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree). == Screenshots == 1. TranslatePress front-end visual translation editor in action 2. Front-end translation editor used to translate the entire page content 3. How to translate a dynamic string (gettext) 4. Translating WooCommerce Products for your multilingual store 5. Translate Images and Image Sliders 6. Settings Page 7. Floating Language Switcher 8. Menu Language Switcher == Changelog == = 3.1 = * Add a new add-on: Different Domain for Language * Fixed a few incorrect entries in the .pot file = Older versions = [Click Here](https://translatepress.com/docs/translatepress-free-changelog/?utm_source=wp.org&utm_medium=tp-description-page&utm_campaign=TPFree) to view the full changelog, or you can find it in the changelog.txt file in the plugin folder. languages/translatepress-multilingual.pot 0000777 00000371517 15251156640 0015046 0 ustar 00 # Copyright (C) 2026 TranslatePress Multilingual # This file is distributed under the same license as the TranslatePress Multilingual package. msgid "" msgstr "" "Project-Id-Version: TranslatePress Multilingual\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "POT-Creation-Date: 2026-02-16 07:17+0000\n" "X-Poedit-Basepath: ..\n" "X-Poedit-KeywordsList: __;_e;_ex:1,2c;_n:1,2;_n_noop:1,2;_nx:1,2,4c;_nx_noop:1,2,3c;_x:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" "X-Poedit-SearchPath-0: .\n" "X-Poedit-SearchPathExcluded-0: *.js\n" "X-Poedit-SourceCharset: UTF-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: index-dev.php:101 msgid "Please update the TranslatePress - Multilingual plugin to version %1$s or higher to ensure %2$s functions correctly." msgstr "" #: index-dev.php:90 msgid "Please install and activate the TranslatePress - Multilingual plugin" msgstr "" #: index-dev.php:92, includes/class-settings.php:170 msgid "Install & Activate" msgstr "" #: index-dev.php:137 msgid "Please update TranslatePress - Multilingual to version %2$s or newer. Your currently installed version of TranslatePress - Multilingual is no longer compatible with the current version of %1$s." msgstr "" #: index-dev.php:142 msgid "All TranslatePress functionalities are disabled until then." msgstr "" #: index-dev.php:143 msgid "Update Now" msgstr "" #: index-dev.php:326 msgid "This TranslatePress add-on has been migrated to the main plugin and is no longer used. You can delete it." msgstr "" #: index.php:68 msgid "<strong>TranslatePress</strong> requires at least PHP version 5.6.20+ to run. It is the <a href=\"%s\">minimum requirement of the latest WordPress version</a>. Please contact your server administrator to update your PHP version." msgstr "" #: includes/class-advanced-tab.php:23 msgid "Advanced" msgstr "" #: includes/class-advanced-tab.php:231, includes/class-language-switcher-tab.php:197 msgid "Settings saved." msgstr "" #: includes/class-advanced-tab.php:643, includes/class-advanced-tab.php:705, includes/class-advanced-tab.php:779 msgid "Are you sure you want to remove this item?" msgstr "" #: includes/class-advanced-tab.php:643, includes/class-advanced-tab.php:705, includes/class-advanced-tab.php:779, partials/main-settings-language-selector.php:127, includes/onboarding/class-languages.php:119, includes/onboarding/class-languages.php:146 msgid "Remove" msgstr "" #: includes/class-advanced-tab.php:684, includes/class-advanced-tab.php:749, includes/class-advanced-tab.php:894, partials/main-settings-language-selector.php:182 msgid "Add" msgstr "" #: includes/class-advanced-tab.php:833, includes/class-advanced-tab.php:877 msgid "Select..." msgstr "" #: includes/class-edd-sl-plugin-updater.php:260 msgid "There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s." msgstr "" #: includes/class-edd-sl-plugin-updater.php:216 msgid "There is a new version of %1$s available. %2$sView version %3$s details%4$s." msgstr "" #: includes/class-edd-sl-plugin-updater.php:247 msgid "To enable updates, please %1$senter your license key%2$s. Need a license key? %3$sPurchase one now%4$s." msgstr "" #: includes/class-edd-sl-plugin-updater.php:237 msgid "To enable updates, please go to the %1$slicense page%2$s and check that you have a valid license." msgstr "" #: includes/class-edd-sl-plugin-updater.php:230 msgid "To enable updates, your licence needs to be renewed. Please go to the %1$sTranslatePress Account%2$s page and login to renew." msgstr "" #: includes/class-edd-sl-plugin-updater.php:454 msgid "You do not have permission to install plugin updates" msgstr "" #: includes/class-edd-sl-plugin-updater.php:454 msgid "Error" msgstr "" #: includes/class-edd-sl-plugin-updater.php:817, includes/onboarding/class-autotranslation.php:90, includes/onboarding/class-license.php:61 msgid "Your license key expired on %s." msgstr "" #: includes/class-edd-sl-plugin-updater.php:822, includes/onboarding/class-autotranslation.php:95, includes/onboarding/class-license.php:66 msgid "Your license key has been disabled." msgstr "" #: includes/class-edd-sl-plugin-updater.php:825, includes/onboarding/class-autotranslation.php:98, includes/onboarding/class-license.php:33, includes/onboarding/class-license.php:69 msgid "Your TranslatePress license key is invalid or missing." msgstr "" #: includes/class-edd-sl-plugin-updater.php:830 msgid "Your license key is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-deactivated\">https://translatepress.com/account</a> -> Manage Sites." msgstr "" #: includes/class-edd-sl-plugin-updater.php:833 msgid "<p><strong>License key mismatch.</strong> The license you entered doesn’t match the TranslatePress version you have installed.</p><p>Please check that you’ve installed the correct version for your license from your TranslatePress account.</p>" msgstr "" #: includes/class-edd-sl-plugin-updater.php:836 msgid "If you have only the free plugin installed but added a paid license, please install the paid plugin from your TranslatePress account." msgstr "" #: includes/class-edd-sl-plugin-updater.php:842, includes/onboarding/class-autotranslation.php:108, includes/onboarding/class-license.php:79 msgid "Your license key has reached its activation limit." msgstr "" #: includes/class-edd-sl-plugin-updater.php:845 msgid "Upgrade your plan to add more sites. %1$sUpgrade now%2$s" msgstr "" #: includes/class-edd-sl-plugin-updater.php:848, includes/onboarding/class-autotranslation.php:111, includes/onboarding/class-license.php:82 msgid "This website is already activated under a free license. Each website can only use one free license." msgstr "" #: includes/class-edd-sl-plugin-updater.php:851, includes/class-edd-sl-plugin-updater.php:806, includes/class-edd-sl-plugin-updater.php:925, includes/onboarding/class-autotranslation.php:114, includes/onboarding/class-license.php:85 msgid "An error occurred, please try again." msgstr "" #: includes/class-edd-sl-plugin-updater.php:884 msgid "You have successfully activated your license" msgstr "" #: includes/class-editor-api-regular-strings.php:55 msgid "Others" msgstr "" #: includes/class-elementor-language-for-blocks.php:101 msgid "Restrict by Language" msgstr "" #: includes/class-elementor-language-for-blocks.php:119, includes/class-wp-bakery-language-for-blocks.php:138 msgid "Exclude from Language" msgstr "" #: includes/class-elementor-language-for-blocks.php:134, includes/class-wp-bakery-language-for-blocks.php:98 msgid "Restrict element to language" msgstr "" #: includes/class-elementor-language-for-blocks.php:136, includes/class-wp-bakery-language-for-blocks.php:101 msgid "Show this element only in one language." msgstr "" #: includes/class-elementor-language-for-blocks.php:142 msgid "Enable translation" msgstr "" #: includes/class-elementor-language-for-blocks.php:144 msgid "Allow translation to the corresponding language only if the content is written in the default language." msgstr "" #: includes/class-elementor-language-for-blocks.php:150, includes/class-wp-bakery-language-for-blocks.php:106, partials/main-settings-language-selector.php:154 msgid "Select language" msgstr "" #: includes/class-elementor-language-for-blocks.php:167, includes/class-wp-bakery-language-for-blocks.php:110 msgid "Choose in which language to show this element." msgstr "" #: includes/class-elementor-language-for-blocks.php:179 msgid "Exclude element from language" msgstr "" #: includes/class-elementor-language-for-blocks.php:181, includes/class-wp-bakery-language-for-blocks.php:141 msgid "Exclude this element from specific languages." msgstr "" #: includes/class-elementor-language-for-blocks.php:187, includes/class-wp-bakery-language-for-blocks.php:158 msgid "Select languages" msgstr "" #: includes/class-elementor-language-for-blocks.php:205, includes/class-wp-bakery-language-for-blocks.php:162 msgid "Choose from which languages to exclude this element." msgstr "" #: includes/class-elementor-language-for-blocks.php:209, includes/class-wp-bakery-language-for-blocks.php:146 msgid "This element will still be visible when you are translating your website through the Translation Editor." msgstr "" #: includes/class-elementor-language-for-blocks.php:210, includes/class-wp-bakery-language-for-blocks.php:153 msgid "The content of this element should be written in the default language." msgstr "" #: includes/class-error-manager.php:57 msgid "<strong>TranslatePress</strong> encountered SQL errors. <a href=\"%s\" title=\"View TranslatePress SQL Errors\">Check out the errors</a>." msgstr "" #: includes/class-error-manager.php:75 msgid "Automatic translation has been disabled." msgstr "" #: includes/class-error-manager.php:170, includes/class-plugin-notices.php:347, includes/class-plugin-notices.php:425, includes/class-plugin-notices.php:400, includes/class-plugin-notices.php:485, includes/class-plugin-notices.php:463, includes/class-plugin-notices.php:514, includes/class-plugin-notices.php:570, includes/class-plugin-notices.php:626, includes/class-plugin-notices.php:678, includes/class-plugin-notices.php:661, includes/class-reviews.php:123, includes/class-reviews.php:126, includes/class-upgrade.php:1040, includes/class-upgrade.php:1749, add-ons-advanced/extra-languages/class-extra-languages.php:167, add-ons-advanced/seo-pack/class-seo-pack.php:837, add-ons-pro/multiple-domains/class-multiple-domains.php:527 msgid "Dismiss this notice." msgstr "" #: includes/class-error-manager.php:188 msgid "Logged errors" msgstr "" #: includes/class-error-manager.php:189 msgid "These are the most recent 5 errors logged by TranslatePress:" msgstr "" #: includes/class-error-manager.php:196 msgid "Yes" msgstr "" #: includes/class-error-manager.php:213 msgid "Why are these errors occuring" msgstr "" #: includes/class-error-manager.php:214 msgid "If TranslatePress detects something wrong when executing queries on your database, it may disable the Automatic Translation feature in order to avoid any extra charging by Google/DeepL. Automatic Translation needs to be manually turned on, after you solve the issues." msgstr "" #: includes/class-error-manager.php:215 msgid "The SQL errors detected can occur for various reasons including missing tables, missing permissions for the SQL user to create tables or perform other operations, problems after site migration or changes to SQL server configuration." msgstr "" #: includes/class-error-manager.php:217 msgid "What you can do in this situation" msgstr "" #: includes/class-error-manager.php:219 msgid "Plan A." msgstr "" #: includes/class-error-manager.php:220 msgid "Go to Settings -> TranslatePress -> General tab and Save Settings. This will regenerate the tables using your current SQL settings. Check if no more errors occur while browsing your website in a translated language. Look at the timestamps of the errors to make sure you are not seeing the old errors. Only the most recent 5 errors are displayed." msgstr "" #: includes/class-error-manager.php:222 msgid "Plan B." msgstr "" #: includes/class-error-manager.php:223 msgid "If your problem isn't solved, try the following steps:" msgstr "" #: includes/class-error-manager.php:225 msgid "Create a backup of your database" msgstr "" #: includes/class-error-manager.php:226 msgid "Create a copy of each translation table where you encounter errors. You can copy the table within the same database (trp_dictionary_en_us_es_es_COPY for example) -- perform this step only if you want to keep the current translations" msgstr "" #: includes/class-error-manager.php:227 msgid "Remove the trouble tables by executing the DROP function on them" msgstr "" #: includes/class-error-manager.php:228 msgid "Go to Settings -> TranslatePress -> General tab and Save Settings. This will regenerate the tables using your current SQL server." msgstr "" #: includes/class-error-manager.php:229 msgid "Copy the relevant content from the duplicated tables (trp_dictionary_en_us_es_es_COPY for example) in the newly generated table (trp_dictionary_en_us_es_es) -- perform this step only if you want to keep the current translations" msgstr "" #: includes/class-error-manager.php:230 msgid "Test it to see if everything is working. If something went wrong, you can restore the backup that you've made at the first step. Check if no more errors occur while browsing your website in a translated language. Look at the timestamps of the errors to make sure you are not seeing the old errors. Only the most recent 5 errors are displayed." msgstr "" #: includes/class-error-manager.php:233 msgid "Plan C." msgstr "" #: includes/class-error-manager.php:234 msgid "If your problem still isn't solved, try asking your hosting about your errors. The most common issue is missing permissions for the SQL user, such as the Create Tables permission." msgstr "" #: includes/class-install-plugins.php:49 msgid "Could not install. Try again from <a href=\"%s\" >Plugins Dashboard.</a>" msgstr "" #: includes/class-install-plugins.php:47, add-ons-advanced/extra-languages/class-extra-languages.php:89, add-ons-advanced/extra-languages/class-extra-languages.php:110 msgid "Active" msgstr "" #: includes/class-language-switcher-tab.php:169, includes/class-language-switcher-tab.php:213, add-ons-pro/multiple-domains/class-multiple-domains.php:718 msgid "Permission denied." msgstr "" #: includes/class-language-switcher-tab.php:176, includes/class-language-switcher-tab.php:221 msgid "Invalid nonce." msgstr "" #: includes/class-language-switcher-tab.php:187 msgid "Settings scope unknown." msgstr "" #: includes/class-language-switcher-tab.php:235 msgid "Legacy disabled." msgstr "" #: includes/class-language-switcher-tab.php:396, includes/class-language-switcher-tab.php:414, includes/class-onboarding.php:118, includes/class-onboarding.php:118, partials/main-settings-page.php:121, includes/advanced-settings/separators.php:95 msgid "Language Switcher" msgstr "" #: includes/class-language-switcher-v2.php:730 msgid "Change language to %s" msgstr "" #: includes/class-machine-translation-tab.php:26, includes/class-onboarding.php:119, includes/class-onboarding.php:119, partials/machine-translation-settings-page.php:24 msgid "Automatic Translation" msgstr "" #: includes/class-machine-translation-tab.php:186, add-ons-pro/deepl/includes/class-deepl.php:33 msgid "DeepL" msgstr "" #: includes/class-machine-translation-tab.php:219 msgid "Unsupported languages" msgstr "" #: includes/class-machine-translation-tab.php:233 msgid "The selected automatic translation engine does not provide support for these languages.<br>You can still manually translate pages in these languages using the Translation Editor." msgstr "" #: includes/class-machine-translation-tab.php:255 msgid "API key validation failed." msgstr "" #: includes/class-machine-translation-tab.php:265 msgid "API key verification was successful." msgstr "" #: includes/class-machine-translator.php:157, includes/google-translate/class-google-translate-v2-machine-translator.php:200 msgid "Please enter your Google Translate key." msgstr "" #: includes/class-machine-translator.php:172, add-ons-pro/deepl/includes/class-deepl-machine-translator.php:365 msgid "Please enter your DeepL API key." msgstr "" #: includes/class-onboarding.php:53 msgid "Not TranslatePress onboarding page." msgstr "" #: includes/class-onboarding.php:70 msgid "Step %s does not exist" msgstr "" #: includes/class-onboarding.php:116, includes/class-onboarding.php:116 msgid "Welcome" msgstr "" #: includes/class-onboarding.php:117, includes/class-onboarding.php:117 msgid "Add Languages" msgstr "" #: includes/class-onboarding.php:120, includes/class-onboarding.php:120 msgid "Enable Addons" msgstr "" #: includes/class-onboarding.php:121, includes/class-onboarding.php:121 msgid "Finalize" msgstr "" #: includes/class-onboarding.php:126 msgid "Exit Setup" msgstr "" #: includes/class-onboarding.php:127 msgid "Upgrade" msgstr "" #: includes/class-onboarding.php:154 msgid "Nothing here" msgstr "" #: includes/class-plugin-notices.php:344, includes/class-plugin-notices.php:374 msgid "Your <strong>TranslatePress</strong> license is missing or invalid. <br/>Please %1$sregister your copy%2$s to enable automatic website translation via TranslatePress AI, premium addons, automatic updates and support. Need a license key? %3$sPurchase one now%4$s" msgstr "" #: includes/class-plugin-notices.php:421 msgid "Your <strong>TranslatePress</strong> license will expire on %1$s. Please %2$sRenew Your Licence%3$s to continue receiving access to automatic translations via TP AI, premium addons, product downloads and automatic updates. %4$sRenew Now%5$s" msgstr "" #: includes/class-plugin-notices.php:392 msgid "Error: " msgstr "" #: includes/class-plugin-notices.php:396, includes/class-plugin-notices.php:566 msgid "Something went wrong, please try again." msgstr "" #: includes/class-plugin-notices.php:390, includes/class-plugin-notices.php:560 msgid "Your <strong>TranslatePress</strong> license has expired. <br/>Please %1$sRenew Your Licence%2$s to continue receiving access to automatic translations via TranslatePress AI, premium addons, product downloads, and automatic updates. %3$sRenew now %4$s" msgstr "" #: includes/class-plugin-notices.php:383, includes/class-plugin-notices.php:550 msgid "License key mismatch. The license you entered doesn’t match the <strong>%1$s</strong> version you have installed. <br/>Please check that you’ve installed the correct version for your license from your %2$sTranslatePress account%3$s." msgstr "" #: includes/class-plugin-notices.php:385, includes/class-plugin-notices.php:552 msgid "<br/>If you have only the free plugin installed but added a paid license, please install the paid plugin from your TranslatePress account." msgstr "" #: includes/class-plugin-notices.php:380, includes/class-plugin-notices.php:547 msgid "You have reached the activation limit for your <strong>%1$s</strong> license. <br/>Manage your active sites from %2$s your account %3$s." msgstr "" #: includes/class-plugin-notices.php:377, includes/class-plugin-notices.php:544 msgid "Your license is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-deactivated\">https://translatepress.com/account</a> -> Manage Sites." msgstr "" #: includes/class-plugin-notices.php:475 msgid "%1$s automatic translation requires an active license. Please %2$srenew%3$s your license or purchase a new one %4$shere%5$s." msgstr "" #: includes/class-plugin-notices.php:455 msgid "Please %1$senter%2$s your license key to enable %3$s automatic translation." msgstr "" #: includes/class-plugin-notices.php:510 msgid "You have less than 5,000 TranslatePress AI words remaining. To continue automatically translating your website, please %spurchase additional AI words at a discount from your account%s." msgstr "" #: includes/class-plugin-notices.php:562 msgid " Error: " msgstr "" #: includes/class-plugin-notices.php:557 msgid "This website is already activated under a free license. Each website can only use one free license. Please upgrade to a premium plan for more TranslatePress AI words from %1$s your account %2$s." msgstr "" #: includes/class-plugin-notices.php:541 msgid "You do not have a valid license for <strong>TranslatePress</strong>. %1$sGet one for free%2$s to get access to TranslatePress AI." msgstr "" #: includes/class-plugin-notices.php:623 msgid "The daily quota for machine translation characters exceeded. Please check the <strong>TranslatePress -> <a href=\"%s\">Automatic Translation</a></strong> page for more information." msgstr "" #: includes/class-plugin-optin.php:205 msgid "Marketing optin" msgstr "" #: includes/class-plugin-optin.php:206 msgid "Opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking." msgstr "" #: includes/class-preferred-user-language.php:57 msgid "TranslatePress Preferred User Language" msgstr "" #: includes/class-preferred-user-language.php:61 msgid "Preferred language to navigate the site" msgstr "" #: includes/class-preferred-user-language.php:73 msgid "The language is automatically set based by the last visited language by the user." msgstr "" #: includes/class-preferred-user-language.php:89 msgid "Always use this language" msgstr "" #: includes/class-preferred-user-language.php:92 msgid "By checking this setting the preferred language will remain the one selected above, without the possibility of being changed in the frontend.<br>This language will be used in different operations such as sending email to the user." msgstr "" #: includes/class-reviews.php:109 msgid "Hello! Seems like you've been using <strong>TranslatePress</strong> for a while now to translate your website. That's awesome! " msgstr "" #: includes/class-reviews.php:113 msgid "If you can spare a few moments to rate it on WordPress.org it would help us a lot (and boost my motivation)." msgstr "" #: includes/class-reviews.php:117 msgid "~ Razvan, developer of TranslatePress" msgstr "" #: includes/class-reviews.php:122 msgid "Rate TranslatePress on WordPress.org plugin page" msgstr "" #: includes/class-reviews.php:122 msgid "Ok, I will gladly help!" msgstr "" #: includes/class-reviews.php:123 msgid "No, thanks." msgstr "" #: includes/class-settings.php:31 msgid "Full Language Names" msgstr "" #: includes/class-settings.php:32 msgid "Short Language Names" msgstr "" #: includes/class-settings.php:33 msgid "Flags with Full Language Names" msgstr "" #: includes/class-settings.php:34 msgid "Flags with Short Language Names" msgstr "" #: includes/class-settings.php:35 msgid "Only Flags" msgstr "" #: includes/class-settings.php:36 msgid "Full Language Names No HTML" msgstr "" #: includes/class-settings.php:71, includes/onboarding/class-switcher.php:259 msgid "Bottom Right" msgstr "" #: includes/class-settings.php:72, includes/onboarding/class-switcher.php:260 msgid "Bottom Left" msgstr "" #: includes/class-settings.php:73, includes/onboarding/class-switcher.php:261 msgid "Top Right" msgstr "" #: includes/class-settings.php:74, includes/onboarding/class-switcher.php:262 msgid "Top Left" msgstr "" #: includes/class-settings.php:95, includes/onboarding/class-switcher.php:290 msgid "Dark" msgstr "" #: includes/class-settings.php:96 msgid "Light" msgstr "" #: includes/class-settings.php:169 msgid "Deactivate" msgstr "" #: includes/class-settings.php:171 msgid "Activate" msgstr "" #: includes/class-settings.php:247 msgid "Invalid language code. Please try again." msgstr "" #: includes/class-settings.php:486 msgid "Language codes can contain only A-Z a-z 0-9 - _ characters. Check your language codes in TranslatePress General Settings." msgstr "" #: includes/class-settings.php:551 msgid "Error! Duplicate URL slug values." msgstr "" #: includes/class-settings.php:552 msgid "You cannot select two languages that have the same <a href=\"https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes\" target=\"_blank\">iso code</a> but different formalities because doing so will lead to duplicate <a href=\"https://developers.google.com/search/docs/specialty/international/localized-versions\" target=\"_blank\">hreflang tags</a>." msgstr "" #: includes/class-settings.php:553 msgid "Duplicate language detected.<br>Each language can only be added once to ensure accurate translation management.<br> Please change the duplicate language entry and try again. " msgstr "" #: includes/class-settings.php:610 msgid "Current Language" msgstr "" #: includes/class-settings.php:616 msgid "Opposite Language" msgstr "" #: includes/class-settings.php:656 msgid "General" msgstr "" #: includes/class-settings.php:661, includes/class-translation-manager.php:544, add-ons-pro/translator-accounts/includes/class-translator-accounts.php:156 msgid "Translate Site" msgstr "" #: includes/class-settings.php:666 msgid "Addons" msgstr "" #: includes/class-settings.php:674 msgid "License" msgstr "" #: includes/class-settings.php:718, includes/class-translation-manager.php:700 msgid "Settings" msgstr "" #: includes/class-settings.php:732, partials/license-settings-page.php:29, includes/onboarding/class-autotranslation.php:169, includes/onboarding/class-license.php:131 msgid "Activate License" msgstr "" #: includes/class-settings.php:724 msgid "Pro Features" msgstr "" #: includes/class-support-chat.php:190, includes/class-support-chat.php:231 msgid "Need Help?" msgstr "" #: includes/class-support-chat.php:191 msgid "Recent community discussions" msgstr "" #: includes/class-support-chat.php:192 msgid "Loading..." msgstr "" #: includes/class-support-chat.php:193 msgid "Unable to load forum posts" msgstr "" #: includes/class-support-chat.php:194 msgid "Ask a Question" msgstr "" #: includes/class-support-chat.php:195 msgid "View All Topics" msgstr "" #: includes/class-support-chat.php:196 msgid "by" msgstr "" #: includes/class-support-chat.php:197 msgid "Have a question?" msgstr "" #: includes/class-support-chat.php:198 msgid "Get help directly from the plugin developers, suggest improvements, or share your feedback!" msgstr "" #: includes/class-support-chat.php:199 msgid "Tip for faster help:" msgstr "" #: includes/class-support-chat.php:200 msgid "Include what you tried, what you expected, and what happened. Screenshots help!" msgstr "" #: includes/class-support-chat.php:215 msgid "Toggle support chat" msgstr "" #: includes/class-support-chat.php:232 msgid "Ask the community" msgstr "" #: includes/class-support-chat.php:251 msgid "Close" msgstr "" #: includes/class-support-chat.php:365 msgid "Empty feed response" msgstr "" #: includes/class-support-chat.php:373 msgid "Unable to parse feed" msgstr "" #: includes/class-support-chat.php:431 msgid "Just now" msgstr "" #. translators: %d: number of hours #: includes/class-support-chat.php:434 msgid "%d hour ago" msgid_plural "%d hours ago" msgstr[0] "" msgstr[1] "" #. translators: %d: number of days #: includes/class-support-chat.php:441 msgid "%d day ago" msgid_plural "%d days ago" msgstr[0] "" msgstr[1] "" #: includes/class-translation-manager.php:102 msgid "Source" msgstr "" #: includes/class-translation-manager.php:103 msgid "Srcset" msgstr "" #: includes/class-translation-manager.php:104 msgid "Alt attribute" msgstr "" #: includes/class-translation-manager.php:105 msgid "Title attribute" msgstr "" #: includes/class-translation-manager.php:106 msgid "Anchor link" msgstr "" #: includes/class-translation-manager.php:107 msgid "Placeholder attribute" msgstr "" #: includes/class-translation-manager.php:108 msgid "Submit attribute" msgstr "" #: includes/class-translation-manager.php:109 msgid "Text" msgstr "" #: includes/class-translation-manager.php:110 msgid "Video Poster" msgstr "" #: includes/class-translation-manager.php:113 msgid "plural form" msgstr "" #: includes/class-translation-manager.php:114 msgid "one" msgstr "" #: includes/class-translation-manager.php:115 msgid "few" msgstr "" #: includes/class-translation-manager.php:116 msgid "many" msgstr "" #: includes/class-translation-manager.php:117 msgid "other" msgstr "" #: includes/class-translation-manager.php:119 msgid "Saved" msgstr "" #: includes/class-translation-manager.php:120 msgid "Save" msgstr "" #: includes/class-translation-manager.php:121 msgid "Saving translation..." msgstr "" #: includes/class-translation-manager.php:122 msgid "You have unsaved changes!" msgstr "" #: includes/class-translation-manager.php:123 msgid "Discard changes" msgstr "" #: includes/class-translation-manager.php:124 msgid "Discard All" msgstr "" #: includes/class-translation-manager.php:125 msgid "Loading Strings..." msgstr "" #: includes/class-translation-manager.php:126 msgid "Select string to translate..." msgstr "" #: includes/class-translation-manager.php:127 msgid "Close Editor" msgstr "" #: includes/class-translation-manager.php:128 msgid "From" msgstr "" #: includes/class-translation-manager.php:129 msgid "To" msgstr "" #: includes/class-translation-manager.php:130 msgid "Add Media" msgstr "" #: includes/class-translation-manager.php:131 msgid "Other languages" msgstr "" #: includes/class-translation-manager.php:132 msgid "Context" msgstr "" #: includes/class-translation-manager.php:133 msgid "View Website As" msgstr "" #: includes/class-translation-manager.php:134 msgid "Available in our Pro Versions" msgstr "" #: includes/class-translation-manager.php:137 msgid "Select or Upload Media" msgstr "" #: includes/class-translation-manager.php:138 msgid "Use this media" msgstr "" #: includes/class-translation-manager.php:141 msgid "Translate" msgstr "" #: includes/class-translation-manager.php:142 msgid "Translate entire block element" msgstr "" #: includes/class-translation-manager.php:143 msgid "Split block to translate strings individually" msgstr "" #: includes/class-translation-manager.php:144 msgid "Save changes to translation. Shortcut: CTRL(⌘) + S" msgstr "" #: includes/class-translation-manager.php:145 msgid "Navigate to next string in dropdown list. Shortcut: CTRL(⌘) + ALT + Right Arrow" msgstr "" #: includes/class-translation-manager.php:146 msgid "Navigate to previous string in dropdown list. Shortcut: CTRL(⌘) + ALT + Left Arrow" msgstr "" #: includes/class-translation-manager.php:147 msgid "Discard all changes. Shortcut: CTRL(⌘) + ALT + Z" msgstr "" #: includes/class-translation-manager.php:148 msgid "Discard changes to this text box. To discard changes to all text boxes use shortcut: CTRL(⌘) + ALT + Z" msgstr "" #: includes/class-translation-manager.php:149 msgid "Dismiss tooltip" msgstr "" #: includes/class-translation-manager.php:150, includes/class-translation-manager.php:198, includes/class-translation-manager.php:204, includes/class-translation-manager.php:209, includes/class-translation-manager.php:214 msgid "Quick Intro" msgstr "" #: includes/class-translation-manager.php:152 msgid "Are you sure you want to split this phrase into smaller parts?" msgstr "" #: includes/class-translation-manager.php:153 msgid "This string is not ready for translation yet. <br>Try again in a moment..." msgstr "" #: includes/class-translation-manager.php:155 msgid "For this option to work, please update the Browse as other role add-on to the latest version." msgstr "" #: includes/class-translation-manager.php:156 msgid "To translate slugs, please update the SEO Pack add-on to the latest version." msgstr "" #: includes/class-translation-manager.php:159 msgid "You can add a new language from <a href=\"%s\">Settings->TranslatePress</a>" msgstr "" #: includes/class-translation-manager.php:160 msgid "However, you can still use TranslatePress to <strong style=\"background: #f5fb9d;\">modify gettext strings</strong> available in your page." msgstr "" #: includes/class-translation-manager.php:161 msgid "Strings that are user-created cannot be modified, only those from themes and plugins." msgstr "" #: includes/class-translation-manager.php:163 msgid "Extra Translation Features" msgstr "" #: includes/class-translation-manager.php:164 msgid "Support for 130+ Extra Languages" msgstr "" #: includes/class-translation-manager.php:165 msgid "Access to TranslatePress AI" msgstr "" #: includes/class-translation-manager.php:166 msgid "Translate SEO Title, Description, Slug" msgstr "" #: includes/class-translation-manager.php:167 msgid "Publish only when translation is complete" msgstr "" #: includes/class-translation-manager.php:168 msgid "Translate by Browsing as User Role" msgstr "" #: includes/class-translation-manager.php:169 msgid "Different Menu Items for each Language" msgstr "" #: includes/class-translation-manager.php:170, partials/addons-settings-page.php:81, add-ons-pro/automatic-language-detection/class-automatic-language-detection.php:66, includes/onboarding/class-addons.php:77, add-ons-pro/automatic-language-detection/partials/general-settings.php:6 msgid "Automatic User Language Detection" msgstr "" #: includes/class-translation-manager.php:172, includes/class-translation-manager.php:177 msgid "Upgrade to PRO" msgstr "" #: includes/class-translation-manager.php:174 msgid "Upgrade to PRO with our biggest discount of the year!" msgstr "" #: includes/class-translation-manager.php:175 msgid "This Black Friday, get access to these features and more at a fraction of the costs:" msgstr "" #: includes/class-translation-manager.php:179 msgid "No available suggestions" msgstr "" #: includes/class-translation-manager.php:180 msgid "Suggestions from translation memory" msgstr "" #: includes/class-translation-manager.php:181 msgid "Click to Copy" msgstr "" #: includes/class-translation-manager.php:183 msgid "Human Translation" msgstr "" #: includes/class-translation-manager.php:184 msgid "Machine Translation" msgstr "" #: includes/class-translation-manager.php:186 msgid "Text on this page is %s% translated into all languages." msgstr "" #: includes/class-translation-manager.php:187 msgid "%1$s% of text on this page is translated into %2$s." msgstr "" #: includes/class-translation-manager.php:188 msgid "This page is %1$s% translated into %2$s." msgstr "" #: includes/class-translation-manager.php:190 msgid "The slug that you are trying to edit is present in other slug types:%s%. Editing it will replace each occurrence, regardless of the current type." msgstr "" #: includes/class-translation-manager.php:199 msgid "Hover any text on the page, click %s,<br> then modify the translation in the sidebar." msgstr "" #: includes/class-translation-manager.php:205 msgid "Don't forget to Save Translation. Use keyboard shortcut CTRL(⌘) + S" msgstr "" #: includes/class-translation-manager.php:210 msgid "Switch language to see the translation changes directly on the page." msgstr "" #: includes/class-translation-manager.php:215 msgid "Search for any text in this page in the dropdown." msgstr "" #: includes/class-translation-manager.php:235 msgid "Your %s license has <span class=\"trp-license-status-emphasized\">expired</span>." msgstr "" #: includes/class-translation-manager.php:243 msgid "Please renew your license to continue receiving access to TranslatePress AI, premium addons, automatic updates and support." msgstr "" #: includes/class-translation-manager.php:244 msgid "Renew Now" msgstr "" #: includes/class-translation-manager.php:238 msgid "<strong>This Black Friday, renew your license at a special price</strong> to continue receiving access to product downloads, automatic updates, and support." msgstr "" #: includes/class-translation-manager.php:239 msgid "Get Deal" msgstr "" #: includes/class-translation-manager.php:253 msgid "Your %s license was <span class=\"trp-license-status-emphasized\">refunded</span>." msgstr "" #: includes/class-translation-manager.php:254 msgid "Please purchase a new license to continue receiving access to TranslatePress AI, premium addons, automatic updates and support." msgstr "" #: includes/class-translation-manager.php:255 msgid "Purchase a new license" msgstr "" #: includes/class-translation-manager.php:267 msgid "Your %s license is <span class=\"trp-license-status-emphasized\">missing or invalid</span>." msgstr "" #: includes/class-translation-manager.php:269 msgid "Please enter a valid license to get access to TranslatePress AI, premium addons, automatic updates and support. Need a license key? %1$sPurchase one now%2$s" msgstr "" #: includes/class-translation-manager.php:270 msgid "Enter a valid license" msgstr "" #: includes/class-translation-manager.php:326, includes/string-translation/class-string-translation.php:372, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:17, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:16 msgid "Slugs" msgstr "" #: includes/class-translation-manager.php:327 msgid "Meta Information" msgstr "" #: includes/class-translation-manager.php:328 msgid "String List" msgstr "" #: includes/class-translation-manager.php:329 msgid "Gettext Strings" msgstr "" #: includes/class-translation-manager.php:330 msgid "Images" msgstr "" #: includes/class-translation-manager.php:331 msgid "Videos" msgstr "" #: includes/class-translation-manager.php:332 msgid "Audios" msgstr "" #: includes/class-translation-manager.php:333 msgid "Dynamically Added Strings" msgstr "" #: includes/class-translation-manager.php:370 msgid "Translation Editor" msgstr "" #: includes/class-translation-manager.php:372 msgid "Edit translations by visually selecting them on each site page" msgstr "" #: includes/class-translation-manager.php:376 msgid "String Translation" msgstr "" #: includes/class-translation-manager.php:378 msgid "Edit url slug translations, plugins and theme translation (emails, forms etc.)" msgstr "" #: includes/class-translation-manager.php:454 msgid "Current User" msgstr "" #: includes/class-translation-manager.php:455 msgid "Logged Out" msgstr "" #: includes/class-translation-manager.php:557, includes/class-translation-manager.php:769 msgid "Translate Page" msgstr "" #: includes/class-translation-manager.php:638 msgid "Add a New Language" msgstr "" #: includes/class-translation-manager.php:654 msgid "Get a Free AI License" msgstr "" #: includes/class-translation-manager.php:669 msgid "Your License is Invalid" msgstr "" #. translators: %d is the number of AI words remaining #: includes/class-translation-manager.php:683 msgid "Get More AI Words (%d left)" msgstr "" #: includes/class-translation-manager.php:767 msgid "Opens post in the translation editor. Post must be saved as draft or published beforehand." msgstr "" #: includes/class-translation-manager.php:916 msgid "Security check" msgstr "" #: includes/class-translation-manager.php:988 msgid "<strong>Warning:</strong> Some strings have possibly incorrectly encoded characters. This may result in breaking the queries, rendering the page untranslated in live mode. Consider revising the following strings or their method of outputting." msgstr "" #: includes/class-translation-manager.php:1013, includes/class-translation-manager.php:1032, includes/class-upgrade.php:374 msgid "TranslatePress data update" msgstr "" #: includes/class-translation-manager.php:1013, includes/class-translation-manager.php:1032, includes/class-upgrade.php:374 msgid "We need to update your translations database to the latest version." msgstr "" #: includes/class-translation-manager.php:1014 msgid "Updating will allow editing translations of localized text from plugins and theme. Existing translation will still work as expected." msgstr "" #: includes/class-translation-manager.php:1016, includes/class-translation-manager.php:1035, includes/class-upgrade.php:375 msgid "IMPORTANT: It is strongly recommended to first backup the database!\nAre you sure you want to continue?" msgstr "" #: includes/class-translation-manager.php:1016, includes/class-translation-manager.php:1035, includes/class-upgrade.php:375 msgid "Run the updater" msgstr "" #: includes/class-translation-manager.php:1033 msgid "Updating will allow editing translations of slugs. Existing translation will still work as expected." msgstr "" #: includes/class-translation-manager.php:1088 msgid "Please activate the SEO Addon from <br/>WordPress -> Settings -> TranslatePress -> Addons section" msgstr "" #: includes/class-translation-manager.php:1091 msgid "Go to Addons" msgstr "" #: includes/class-translation-manager.php:1107 msgid "The SEO Pack add-on allows translation of all the URL slugs:" msgstr "" #: includes/class-translation-manager.php:1110 msgid "Taxonomy slugs" msgstr "" #: includes/class-translation-manager.php:1113 msgid "Term slugs" msgstr "" #: includes/class-translation-manager.php:1116 msgid "Post slugs (this includes pages and custom post types)" msgstr "" #: includes/class-translation-manager.php:1119 msgid "Post type base slugs" msgstr "" #: includes/class-translation-manager.php:1122 msgid "WooCommerce slugs" msgstr "" #: includes/class-translation-manager.php:1127 msgid "The SEO Pack add-on is available with ALL premium versions of the plugin." msgstr "" #: includes/class-translation-manager.php:1130 msgid "Upgrade to Pro" msgstr "" #: includes/class-translation-render.php:161 msgid "Description" msgstr "" #: includes/class-translation-render.php:167 msgid "Article Section" msgstr "" #: includes/class-translation-render.php:173 msgid "Article Tag" msgstr "" #: includes/class-translation-render.php:179 msgid "OG Title" msgstr "" #: includes/class-translation-render.php:185 msgid "OG Site Name" msgstr "" #: includes/class-translation-render.php:191 msgid "OG Description" msgstr "" #: includes/class-translation-render.php:197 msgid "OG Image Alt" msgstr "" #: includes/class-translation-render.php:203 msgid "Twitter Title" msgstr "" #: includes/class-translation-render.php:209 msgid "Twitter Description" msgstr "" #: includes/class-translation-render.php:215 msgid "Twitter Image Alt" msgstr "" #: includes/class-translation-render.php:219 msgid "Page Title" msgstr "" #: includes/class-translation-render.php:225 msgid "Dublin Core Title" msgstr "" #: includes/class-translation-render.php:231 msgid "Dublin Core Description" msgstr "" #: includes/class-translation-render.php:237 msgid "OG Image" msgstr "" #: includes/class-translation-render.php:243 msgid "OG Image Secure URL" msgstr "" #: includes/class-translation-render.php:249 msgid "Twitter Image" msgstr "" #: includes/class-upgrade.php:175 msgid "Removing cdata dictionary strings for language %s..." msgstr "" #: includes/class-upgrade.php:176 msgid "Removing untranslated dictionary links for language %s..." msgstr "" #: includes/class-upgrade.php:177 msgid "Removing duplicated gettext strings for language %s..." msgstr "" #: includes/class-upgrade.php:178 msgid "Removing duplicated dictionary strings for language %s..." msgstr "" #: includes/class-upgrade.php:179 msgid "Removing untranslated dictionary strings where translation is available for language %s..." msgstr "" #: includes/class-upgrade.php:180 msgid "Inserting original strings for language %s..." msgstr "" #: includes/class-upgrade.php:181 msgid "Cleaning original strings table for language %s..." msgstr "" #: includes/class-upgrade.php:182 msgid "Updating original string ids for language %s..." msgstr "" #: includes/class-upgrade.php:183 msgid "Regenerating original meta table for language %s..." msgstr "" #: includes/class-upgrade.php:184 msgid "Cleaning original meta table for language %s..." msgstr "" #: includes/class-upgrade.php:185 msgid "Replacing original id NULL with value for language %s..." msgstr "" #: includes/class-upgrade.php:186 msgid "Inserting gettext original strings for language %s..." msgstr "" #: includes/class-upgrade.php:187 msgid "Cleaning gettext original strings table for language %s..." msgstr "" #: includes/class-upgrade.php:188 msgid "Updating gettext original string ids for language %s..." msgstr "" #: includes/class-upgrade.php:189 msgid "Migrating taxonomy and post type base slugs to new table structure..." msgstr "" #: includes/class-upgrade.php:190 msgid "Migrating post slugs to new table structure for language %s..." msgstr "" #: includes/class-upgrade.php:191 msgid "Migrating term slugs to new table structure for language %s..." msgstr "" #: includes/class-upgrade.php:192 msgid "Finishing up..." msgstr "" #: includes/class-upgrade.php:402 msgid "Database optimization did not complete successfully. We recommend restoring the original database or <a href=\"%s\" >trying again.</a>" msgstr "" #: includes/class-upgrade.php:418 msgid "Update aborted! Your user account doesn't have the capability to perform database updates." msgstr "" #: includes/class-upgrade.php:423 msgid "Update aborted! Invalid nonce." msgstr "" #: includes/class-upgrade.php:468 msgid "Update aborted! Incorrect action." msgstr "" #: includes/class-upgrade.php:471 msgid "Update aborted! Incorrect language code." msgstr "" #: includes/class-upgrade.php:455 msgid "Updating database to version %s+" msgstr "" #: includes/class-upgrade.php:459, includes/class-upgrade.php:532 msgid "Processing table for language %s..." msgstr "" #: includes/class-upgrade.php:441, includes/class-upgrade.php:571 msgid "Back to TranslatePress Settings" msgstr "" #: includes/class-upgrade.php:445 msgid "Successfully updated database!" msgstr "" #: includes/class-upgrade.php:537, includes/class-upgrade.php:529 msgid " done." msgstr "" #: includes/class-upgrade.php:1038 msgid "All individual TranslatePress add-on plugins <a href=\"%1$s\" target=\"_blank\">have been discontinued</a> and are now included in the premium Personal, Business and Developer versions of TranslatePress. Please log into your <a href=\"%2$s\" target=\"_blank\">account page</a>, download the new premium version and install it. Your individual addons settings will be ported over." msgstr "" #: includes/class-upgrade.php:1775 msgid "Brand-new Language Switcher Settings are here!" msgstr "" #: includes/class-upgrade.php:1778 msgid "Explore pre-made templates, switch colors, flag styles, spacing, layouts & more. Use the live preview to perfect your switcher in seconds." msgstr "" #: includes/class-upgrade.php:1782 msgid "Start customizing" msgstr "" #: includes/class-upgrade.php:1783 msgid "Read documentation" msgstr "" #: includes/class-wp-bakery-language-for-blocks.php:179 msgid "TranslatePress" msgstr "" #: includes/compatibility-functions.php:32 msgid "<strong>TranslatePress</strong> requires <strong><a href=\"http://php.net/manual/en/book.mbstring.php\">Multibyte String PHP library</a></strong>. Please contact your server administrator to install it on your server." msgstr "" #: includes/compatibility-functions.php:1712 msgid "Detected long query limitation on WPEngine hosting. Some large pages may appear untranslated. You can remove limitation by adding the following to your site’s wp-config.php: define( 'WPE_GOVERNOR', false ); " msgstr "" #: includes/custom-language.php:13 msgid "Custom Language Flag" msgstr "" #: includes/custom-language.php:170 msgid "The Language code of the added custom language cannot be empty." msgstr "" #: includes/custom-language.php:163 msgid "The Language code of the added custom language is invalid." msgstr "" #: includes/custom-language.php:184 msgid "The Automatic Translation Code of the added custom language is invalid." msgstr "" #: partials/addons-settings-page.php:21 msgid "TranslatePress Add-ons" msgstr "" #: partials/addons-settings-page.php:23 msgid "You must first purchase this version to have access to the addon %1$shere%2$s" msgstr "" #: partials/addons-settings-page.php:33 msgid "Please %1$senter your license%2$s key first, to activate this addon." msgstr "" #: partials/addons-settings-page.php:31 msgid "You need an active license to have access to the addon. Renew or purchase a new one %1$shere%2$s." msgstr "" #: partials/addons-settings-page.php:38, includes/onboarding/class-addons.php:45 msgid "Advanced Add-ons" msgstr "" #: partials/addons-settings-page.php:38, includes/onboarding/class-addons.php:46 msgid "These addons extend your translation plugin and are available in the Developer, Business and Personal plans." msgstr "" #: partials/addons-settings-page.php:41, includes/onboarding/class-addons.php:51 msgid "SEO Pack" msgstr "" #: partials/addons-settings-page.php:44 msgid "SEO Pack (Legacy)" msgstr "" #: partials/addons-settings-page.php:51 msgid "SEO support for page slug, page title, description and facebook and twitter social graph information. The HTML lang attribute is properly set." msgstr "" #: partials/addons-settings-page.php:58, includes/onboarding/class-addons.php:57 msgid "Multiple Languages" msgstr "" #: partials/addons-settings-page.php:59, includes/onboarding/class-addons.php:58 msgid "Add as many languages as you need for your project to go global. Publish your language only when all your translations are done." msgstr "" #: partials/addons-settings-page.php:68, includes/onboarding/class-addons.php:65 msgid "Pro Add-ons" msgstr "" #: partials/addons-settings-page.php:68, includes/onboarding/class-addons.php:66 msgid "These addons extend your translation plugin and are available in the Business and Developer plans." msgstr "" #: partials/addons-settings-page.php:73, includes/onboarding/class-addons.php:71 msgid "DeepL Automatic Translation" msgstr "" #: partials/addons-settings-page.php:74, includes/onboarding/class-addons.php:72 msgid "Automatically translate your website through the DeepL API." msgstr "" #: partials/addons-settings-page.php:82, includes/onboarding/class-addons.php:78 msgid "Prompts visitors to switch to their preferred language based on their browser settings or IP address and remembers the last visited language." msgstr "" #: partials/addons-settings-page.php:89, includes/onboarding/class-addons.php:83 msgid "Translator Accounts" msgstr "" #: partials/addons-settings-page.php:90, includes/onboarding/class-addons.php:84 msgid "Create translator accounts for new users or allow existing users that are not administrators to translate your website." msgstr "" #: partials/addons-settings-page.php:97, includes/onboarding/class-addons.php:89 msgid "Browse As User Role" msgstr "" #: partials/addons-settings-page.php:98, includes/onboarding/class-addons.php:90 msgid "Navigate your website just like a particular user role would. Really useful for dynamic content or hidden content that appears for particular users." msgstr "" #: partials/addons-settings-page.php:105, includes/onboarding/class-addons.php:95 msgid "Navigation Based on Language" msgstr "" #: partials/addons-settings-page.php:106, includes/onboarding/class-addons.php:96 msgid "Configure different menu items for different languages." msgstr "" #: partials/addons-settings-page.php:113, add-ons-pro/multiple-domains/class-multiple-domains.php:326, includes/onboarding/class-addons.php:101 msgid "Different Domain per Language" msgstr "" #: partials/addons-settings-page.php:114, includes/onboarding/class-addons.php:102 msgid "Connect separate domains or subdomains to each of your translated versions. Strengthen your brand’s local identity and boost SEO performance for every language you support." msgstr "" #: partials/addons-settings-page.php:124 msgid "Recommended Plugins" msgstr "" #: partials/addons-settings-page.php:124 msgid "A short list of plugins you can use to extend your website." msgstr "" #: partials/addons-settings-page.php:130 msgid "Profile Builder" msgstr "" #: partials/addons-settings-page.php:131 msgid "Capture more user information on the registration form with the help of Profile Builder's custom user profile fields and/or add an Email Confirmation process to verify your customers accounts." msgstr "" #: partials/addons-settings-page.php:142 msgid "Paid Member Subscriptions" msgstr "" #: partials/addons-settings-page.php:143 msgid "Accept user payments, create subscription plans and restrict content on your membership site." msgstr "" #: partials/addons-settings-page.php:154 msgid "WP Webhooks Automator" msgstr "" #: partials/addons-settings-page.php:155 msgid "Create no-code automations and workflows on your WordPress site. Easily connect your plugins, sites and apps together." msgstr "" #: partials/advanced-settings-page.php:18, partials/machine-translation-settings-page.php:352, partials/main-settings-page.php:227 msgid "Save Changes" msgstr "" #: partials/error-manager-page.php:7 msgid "TranslatePress Errors" msgstr "" #: partials/error-manager-page.php:10 msgid "There are no logged errors." msgstr "" #: partials/floating-switcher.php:33, partials/shortcode-switcher.php:56 msgid "Change language" msgstr "" #: partials/floating-switcher.php:80, partials/shortcode-switcher.php:116, partials/shortcode-switcher.php:98 msgid "Website language selector" msgstr "" #: partials/floating-switcher.php:89 msgid "WordPress Translation Plugin" msgstr "" #: partials/floating-switcher.php:106, partials/shortcode-switcher.php:124 msgid "Available languages" msgstr "" #: partials/language-switcher-configurator-page.php:16 msgid "Language Switcher update notice" msgstr "" #: partials/language-switcher-configurator-page.php:18 msgid "Legacy language switcher is currently enabled" msgstr "" #. translators: Explain where to toggle legacy back on #: partials/language-switcher-configurator-page.php:23 msgid "We’ve upgraded the switcher for richer customization and a better user experience.<br>In order to use the new configurator, turn off <strong>Load legacy language switcher</strong>." msgstr "" #: partials/language-switcher-configurator-page.php:31 msgid "Note: You can switch back anytime from <strong>Advanced Settings → <a href=\"%s\">Troubleshooting</a></strong>." msgstr "" #: partials/language-switcher-configurator-page.php:53 msgid "Enable the new switcher" msgstr "" #: partials/license-settings-page.php:7 msgid "Your License Key is valid." msgstr "" #: partials/license-settings-page.php:8 msgid "Your License Key is invalid." msgstr "" #: partials/license-settings-page.php:9 msgid "Your License has expired." msgstr "" #: partials/license-settings-page.php:25 msgid "Deactivate License" msgstr "" #: partials/license-settings-page.php:47 msgid "Add a license key" msgstr "" #: partials/license-settings-page.php:53 msgid "License Key" msgstr "" #: partials/license-settings-page.php:86 msgid "Manage your license in your %1$s." msgstr "" #: partials/license-settings-page.php:88 msgid "Account Page" msgstr "" #: partials/license-settings-page.php:113 msgid "Don’t have a TranslatePress AI License Key?" msgstr "" #: partials/license-settings-page.php:119 msgid "You can get one for %1$sfree%2$s, by creating a free account. It includes:" msgstr "" #: partials/license-settings-page.php:125, includes/mtapi/functions.php:74, includes/onboarding/class-autotranslation.php:212 msgid "Access to TranslatePress AI for instant automatic translations" msgstr "" #: partials/license-settings-page.php:130, includes/mtapi/functions.php:79, includes/onboarding/class-autotranslation.php:217 msgid "2000 AI words to translate automatically" msgstr "" #: partials/license-settings-page.php:135 msgid "Get a free License Today" msgstr "" #: partials/license-settings-page.php:156 msgid "Debug Information" msgstr "" #: partials/license-settings-page.php:162 msgid "Debug Data for License Checking" msgstr "" #: partials/license-settings-page.php:190 msgid "Debug Data for License Activation" msgstr "" #: partials/license-settings-page.php:222 msgid "Get more AI words and unlock all features with TranslatePress Pro." msgstr "" #: partials/license-settings-page.php:223, includes/onboarding/class-addons.php:154, includes/onboarding/class-languages.php:153 msgid "Upgrade now ↗" msgstr "" #: partials/license-settings-page.php:227 msgid "Already purchased a Premium version?" msgstr "" #: partials/license-settings-page.php:236 msgid "Go to your %1$s" msgstr "" #: partials/license-settings-page.php:238 msgid "TranslatePress.com Account" msgstr "" #: partials/license-settings-page.php:246 msgid "Download & Install the Pro plugin" msgstr "" #: partials/license-settings-page.php:251, partials/machine-translation-settings-page.php:132 msgid "Learn More" msgstr "" #: partials/machine-translation-settings-page.php:29, includes/onboarding/class-autotranslation.php:126, includes/onboarding/class-autotranslation.php:157 msgid "Enable Automatic Translation" msgstr "" #: partials/machine-translation-settings-page.php:61 msgid "To use <strong>DeepL</strong> for automatic translation, activate this Pro add-on from the <a href=\"%1$s\" target=\"_self\" title=\"%2$s\">%2$s</a>." msgstr "" #: partials/machine-translation-settings-page.php:54 msgid "<strong>DeepL</strong> automatic translation is available as a <a href=\"%1$s\" target=\"_blank\" title=\"%2$s\">%2$s</a>." msgstr "" #: partials/machine-translation-settings-page.php:55 msgid "By upgrading you'll get access to all paid add-ons, premium support and help fund the future development of TranslatePress." msgstr "" #. translators: %1$s is the URL to the DeepL add-on. %2$s is the name of the Pro offerings. #: partials/machine-translation-settings-page.php:76 msgctxt "Verbiage for the DeepL Pro Add-on" msgid "TranslatePress Pro Add-on" msgstr "" #: partials/machine-translation-settings-page.php:70 msgctxt "Verbiage for the DeepL Pro Add-on" msgid "Addons tab" msgstr "" #: partials/machine-translation-settings-page.php:83 msgid "Please note that DeepL API usage is paid separately. See <a href=\"https://www.deepl.com/pro.html#developer\">DeepL pricing information</a>." msgstr "" #: partials/machine-translation-settings-page.php:88 msgid "TranslatePress Pro Add-ons" msgstr "" #: partials/machine-translation-settings-page.php:101 msgid "Test API credentials" msgstr "" #: partials/machine-translation-settings-page.php:105 msgid "Check if the selected translation engine is configured correctly." msgstr "" #: partials/machine-translation-settings-page.php:111 msgid "Alternative Engines" msgstr "" #: partials/machine-translation-settings-page.php:126 msgid "Switch to TranslatePress AI" msgstr "" #: partials/machine-translation-settings-page.php:129 msgid "Integrate machine translation directly with your WordPress website." msgstr "" #: partials/machine-translation-settings-page.php:119 msgid "More info" msgstr "" #. translators: The <br> ensures a line break after "order to" #: partials/machine-translation-settings-page.php:160 msgid "Choose which engine you want to use in order to %1$s automatically translate your website." msgstr "" #: partials/machine-translation-settings-page.php:176 msgid "Automatic Translation Settings" msgstr "" #: partials/machine-translation-settings-page.php:198 msgid "Automatically Translate Slugs" msgstr "" #: partials/machine-translation-settings-page.php:202 msgid "Generate automatic translations of slugs for posts, pages and Custom Post Types.<br/>The slugs will be automatically translated starting with the second refresh of each page." msgstr "" #: partials/machine-translation-settings-page.php:216 msgid "This feature is only available in the paid version. Upgrade TranslatePress and unlock more premium features." msgstr "" #: partials/machine-translation-settings-page.php:218 msgid "Requires <a href=\"%s\" title=\"TranslatePress Add-on SEO Pack documentation\" target=\"_blank\">SEO Pack Add-on</a> to be installed and activated." msgstr "" #: partials/machine-translation-settings-page.php:235, partials/main-settings-language-selector.php:204, partials/settings-header.php:34, includes/mtapi/functions.php:87, includes/mtapi/functions.php:188 msgid "Upgrade now" msgstr "" #: partials/machine-translation-settings-page.php:259 msgid "Block Crawlers" msgstr "" #: partials/machine-translation-settings-page.php:262 msgid "Block crawlers from triggering automatic translations on your website.<br>This will not prevent crawlers from accessing this site's pages." msgstr "" #: partials/machine-translation-settings-page.php:281 msgid "Limit machine translation / characters per day" msgstr "" #: partials/machine-translation-settings-page.php:284 msgid "Add a limit to the number of automatically translated characters so you can better budget your project." msgstr "" #: partials/machine-translation-settings-page.php:307 msgid "characters per day" msgstr "" #: partials/machine-translation-settings-page.php:310 msgid "Today's Character Count: " msgstr "" #: partials/machine-translation-settings-page.php:330 msgid "Log machine translation queries." msgstr "" #: partials/machine-translation-settings-page.php:334 msgid "Only enable for testing purposes. Can impact performance.<br>All records are stored in the wp_trp_machine_translation_log database table. Use a plugin like <a href=\"https://wordpress.org/plugins/wp-data-access/\" target=\"_blank\">WP Data Access</a> to browse the logs or directly from your database manager (PHPMyAdmin, etc.)" msgstr "" #: partials/machine-translation-test-api-popup.php:5 msgid "Test API Credentials" msgstr "" #: partials/machine-translation-test-api-popup.php:15 msgid "HTTP Referrer: " msgstr "" #: partials/machine-translation-test-api-popup.php:18 msgid "Use this HTTP Referrer if the API lets you restrict key usage from its Dashboard." msgstr "" #: partials/machine-translation-test-api-popup.php:22 msgid "Response" msgstr "" #: partials/machine-translation-test-api-popup.php:27 msgid "Response Body" msgstr "" #: partials/machine-translation-test-api-popup.php:32 msgid "Entire Response From wp_remote_get():" msgstr "" #: partials/main-settings-language-selector.php:26, add-ons-pro/navigation-based-on-language/class-navigation-based-on-language.php:86 msgid "All Languages" msgstr "" #: partials/main-settings-language-selector.php:27 msgid "Select the languages you wish to make your website available in." msgstr "" #: partials/main-settings-language-selector.php:32, partials/main-settings-language-selector.php:101 msgid "Formality" msgstr "" #: partials/main-settings-language-selector.php:36, partials/main-settings-language-selector.php:114 msgid "Code" msgstr "" #: partials/main-settings-language-selector.php:39, partials/main-settings-language-selector.php:118, includes/onboarding/class-languages.php:116, includes/onboarding/class-languages.php:143 msgid "Slug" msgstr "" #: partials/main-settings-language-selector.php:50, includes/onboarding/class-switcher.php:279 msgid "Default" msgstr "" #: partials/main-settings-language-selector.php:51 msgid "Formal" msgstr "" #: partials/main-settings-language-selector.php:52 msgid "Informal" msgstr "" #: partials/main-settings-language-selector.php:75, includes/string-translation/class-string-translation.php:225 msgid "Language" msgstr "" #: partials/main-settings-language-selector.php:100 msgid "This language does not support formality. " msgstr "" #: partials/main-settings-language-selector.php:127 msgid "Are you sure you want to remove this language?" msgstr "" #: partials/main-settings-language-selector.php:158 msgid "Custom Languages" msgstr "" #: partials/main-settings-language-selector.php:226 msgid "To Add more languages activate the Multiple Languages Addon" msgstr "" #: partials/main-settings-language-selector.php:223 msgid "You need an active license to add more languages. Verify in your %1$saccount%2$s that your license is valid" msgstr "" #: partials/main-settings-language-selector.php:219 msgid "Please %1$senter your license%2$s key first to add more languages." msgstr "" #: partials/main-settings-language-selector.php:200 msgid "Adding more than two languages is a paid feature. Upgrade TranslatePress and unlock more premium features." msgstr "" #: partials/main-settings-page.php:15 msgid "Website Languages" msgstr "" #: partials/main-settings-page.php:19, includes/onboarding/class-languages.php:88 msgid "Default Language" msgstr "" #: partials/main-settings-page.php:29, includes/onboarding/class-languages.php:99 msgid "Select the language your content is written in." msgstr "" #: partials/main-settings-page.php:34 msgid "WARNING. Changing the default language will invalidate existing translations." msgstr "" #: partials/main-settings-page.php:35 msgid "Even changing from en_US to en_GB, because they are treated as two different languages." msgstr "" #: partials/main-settings-page.php:36 msgid "In most cases changing the default flag is all it is needed: " msgstr "" #: partials/main-settings-page.php:37 msgid "replace the default flag" msgstr "" #: partials/main-settings-page.php:47, partials/main-settings-page.php:60 msgid "Re-run Setup Wizard" msgstr "" #: partials/main-settings-page.php:54 msgid "The Setup wizard allows you to quickly setup TranslatePress. You can initiate it at any time." msgstr "" #: partials/main-settings-page.php:67 msgid "Language Settings" msgstr "" #: partials/main-settings-page.php:77 msgid "Use Native language name" msgstr "" #: partials/main-settings-page.php:78 msgid "Check if you want to display languages in their native names. Otherwise, languages will be displayed in English." msgstr "" #: partials/main-settings-page.php:93 msgid "Use a subdirectory for the default language" msgstr "" #: partials/main-settings-page.php:95 msgid "Check if you want to add the subdirectory in the URL for the default language.</br>By checking this option, the default language seen by website visitors will become the first one in the \"All Languages\" list." msgstr "" #: partials/main-settings-page.php:110 msgid "Force language in custom links" msgstr "" #: partials/main-settings-page.php:112 msgid "Select Yes if you want to force custom links without language encoding to keep the currently selected language." msgstr "" #: partials/main-settings-page.php:130 msgid "Shortcode " msgstr "" #: partials/main-settings-page.php:134 msgid "Use shortcode on any page or widget." msgstr "" #: partials/main-settings-page.php:135 msgid "You can also add the <a href=\"%s\" title=\"Language Switcher Block Documentation\">Language Switcher Block</a> in the WP Gutenberg Editor." msgstr "" #: partials/main-settings-page.php:146 msgid "Menu item" msgstr "" #: partials/main-settings-page.php:152 msgid "Go to %1$s Appearance -> Menus%2$s to add languages to the Language Switcher in any menu." msgstr "" #: partials/main-settings-page.php:153 msgid "Learn more in our documentation." msgstr "" #: partials/main-settings-page.php:164 msgid "Floating language selection" msgstr "" #: partials/main-settings-page.php:169 msgid "Add a floating dropdown that follows the user on every page." msgstr "" #: partials/main-settings-page.php:180 msgid "Show \"Powered by TranslatePress\"" msgstr "" #: partials/main-settings-page.php:182 msgid "Show the small \"Powered by TranslatePress\" label in the floater language switcher." msgstr "" #: partials/main-settings-page.php:200 msgid "5 Days to Better Multilingual Websites" msgstr "" #: partials/main-settings-page.php:204 msgid "%sJoin our FREE & EXCLUSIVE onboarding course%s and learn how to grow your multilingual traffic, reach international markets, and save time & money while getting the most out of TranslatePress!" msgstr "" #: partials/main-settings-page.php:210 msgid "Invalid email address" msgstr "" #: partials/main-settings-page.php:212 msgid "Your email" msgstr "" #: partials/main-settings-page.php:215 msgid "Sign me up!" msgstr "" #: partials/main-settings-page.php:219 msgid "Sign up with your email address and receive a 5-part email guide to help you maximize the power of TranslatePress." msgstr "" #: partials/main-settings-page.php:222 msgid "Dismiss email course notification" msgstr "" #: partials/plugin-optin-page.php:20 msgid "Hey %s,<br>Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking." msgstr "" #: partials/plugin-optin-page.php:24 msgid "Allow & Continue" msgstr "" #: partials/plugin-optin-page.php:26 msgid "Skip" msgstr "" #: partials/plugin-optin-page.php:34 msgid "This will allow TranslatePress to:" msgstr "" #: partials/plugin-optin-page.php:41 msgid "Your profile overview" msgstr "" #: partials/plugin-optin-page.php:42 msgid "Name and email address" msgstr "" #: partials/plugin-optin-page.php:57 msgid "Admin Notices" msgstr "" #: partials/plugin-optin-page.php:58 msgid "Updates, announcements, marketing, no spam" msgstr "" #: partials/plugin-optin-page.php:65 msgid "Plugin status & settings" msgstr "" #: partials/plugin-optin-page.php:66 msgid "Active, Deactivated, installed version and settings" msgstr "" #: partials/plugin-optin-page.php:80 msgid "Privacy Policy" msgstr "" #: partials/plugin-optin-page.php:82 msgid "Terms of Service" msgstr "" #: partials/settings-header.php:26 msgid "Support" msgstr "" #: partials/settings-header.php:32 msgid "Documentation" msgstr "" #: partials/trp-remove-duplicate-rows.php:11, includes/advanced-settings/remove-duplicates-from-db.php:11 msgid "Optimize TranslatePress database tables" msgstr "" #: partials/trp-remove-duplicate-rows.php:15 msgid "<strong>IMPORTANT NOTE:</strong> Before performing this action it is strongly recommended to first backup the database." msgstr "" #: partials/trp-remove-duplicate-rows.php:17 msgid "IMPORTANT: It is strongly recommended to first backup the database!! Are you sure you want to continue?" msgstr "" #: partials/trp-remove-duplicate-rows.php:20 msgid "Operations to perform" msgstr "" #: partials/trp-remove-duplicate-rows.php:31 msgid "Remove CDATA for original and dictionary strings" msgstr "" #: partials/trp-remove-duplicate-rows.php:33 msgid "Removes CDATA from trp_original_strings and trp_dictionary_* tables.<br>This type of content should not be detected by TranslatePress. It might have been introduced in the database in older versions of the plugin." msgstr "" #: partials/trp-remove-duplicate-rows.php:43 msgid "Remove untranslated links from dictionary tables" msgstr "" #: partials/trp-remove-duplicate-rows.php:45 msgid "Removes untranslated links and images from all trp_dictionary_* tables. These tables contain translations for user-inputted strings such as post content, post title, menus etc." msgstr "" #: partials/trp-remove-duplicate-rows.php:55 msgid "Remove duplicate rows for gettext strings" msgstr "" #: partials/trp-remove-duplicate-rows.php:57 msgid "Cleans up all trp_gettext_* tables of duplicate rows. These tables contain translations for themes and plugin strings." msgstr "" #: partials/trp-remove-duplicate-rows.php:67 msgid "Remove duplicate rows for dictionary strings" msgstr "" #: partials/trp-remove-duplicate-rows.php:69 msgid "Cleans up all trp_dictionary_* tables of duplicate rows. These tables contain translations for user-inputted strings such as post content, post title, menus etc." msgstr "" #: partials/trp-remove-duplicate-rows.php:79 msgid "Remove duplicate rows for original dictionary strings" msgstr "" #: partials/trp-remove-duplicate-rows.php:81 msgid "Cleans up all trp_original_strings table of duplicate rows. This table contains strings in the default language, without any translation.<br>The trp_original_meta table, which contains meta information that refers to the post parent’s ID, is also regenerated.<br>Such duplicates can appear in exceptional situations of unexpected behavior." msgstr "" #: partials/trp-remove-duplicate-rows.php:91 msgid "Replace gettext strings that have original ID NULL with the correct original IDs" msgstr "" #: partials/trp-remove-duplicate-rows.php:93 msgid "Fixes an edge case issue where some gettext strings have the original ID incorrectly set to NULL, causing problems in the Translation Editor.<br>This operation corrects the original IDs in the trp_gettext_* tables.<br>Only check this option if you encountered an issue in the Translation Editor where clicking the green pencil did not bring up the gettext string for translation in the left sidebar.<br>Otherwise, please leave this option unchecked because it's an intensive operation." msgstr "" #: partials/trp-remove-duplicate-rows.php:103 msgid "Optimize Database" msgstr "" #: partials/trp-update-database.php:11 msgid "TranslatePress Database Updater" msgstr "" #: partials/trp-update-database.php:16 msgid "Updating TranslatePress tables. Please leave this window open." msgstr "" #: add-ons-advanced/extra-languages/class-extra-languages.php:90 msgid "The inactive languages will still be visible and active for the admin. For other users they won't be visible in the language switchers and won't be accessible either." msgstr "" #: add-ons-advanced/extra-languages/class-extra-languages.php:155, add-ons-pro/multiple-domains/class-multiple-domains.php:515 msgid "unknown" msgstr "" #: add-ons-advanced/extra-languages/class-extra-languages.php:159 msgid "<strong>Extra Languages add-on</strong> requires TranslatePress version %1$s or higher. You are currently using version %2$s. Please update TranslatePress to enable this feature." msgstr "" #: add-ons-advanced/seo-pack/class-seo-pack.php:832 msgid "Automatic and manual slug translation changes performed when <strong>TranslatePress - Multilingual</strong> 2.8.4 was active had to be removed because of some issues with that version. All slug translations from before that version are now in use. Thank you for understanding!" msgstr "" #: add-ons-advanced/seo-pack/class-seo-pack.php:833 msgid "If you absolutely need them, the removed translations can be found in tables trp_slug_original_obsolete and trp_slug_translation_obsolete." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:329 msgid "View Docs" msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:336 msgid "Assign different domains or subdomains to each language. When visitors access these domains, TranslatePress loads the appropriate language translation directly without redirecting." msgstr "" #. translators: %1$s, %2$s, %3$s are example domains #: add-ons-pro/multiple-domains/class-multiple-domains.php:342 msgid "Example: %1$s for English, %2$s for Spanish, %3$s for French." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:350 msgid "Before enabling:" msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:351 msgid "Ensure your domains are registered, pointed to your server, and have SSL certificates configured." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:364, add-ons-pro/multiple-domains/class-multiple-domains.php:384, includes/advanced-settings/exclude-gettext-strings.php:14, includes/string-translation/class-string-translation.php:294, includes/string-translation/class-string-translation.php:324 msgid "Domain" msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:365 msgid "Map this language to a different domain or sub-domain." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:419 msgid "https://example.com" msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:426 msgid "Prefill with current domain" msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:434 msgid "Check DNS" msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:499 msgid "Checking DNS..." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:500 msgid "DNS is correctly configured!" msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:501 msgid "DNS check failed. Please verify your domain configuration." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:502 msgid "This domain is already assigned to another language." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:519 msgid "<strong>Different Domain per Language add-on</strong> requires TranslatePress version %1$s or higher. You are currently using version %2$s. Please update TranslatePress to enable this feature." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:652 msgid "Note: This option is disabled when Different Domain for Language addon is active." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:677 msgid "Different Domain per Language: Domain is required when domain mapping is enabled. The toggle has been disabled for languages with empty domains." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:691 msgid "Different Domain per Language: The same domain cannot be assigned to multiple languages. The toggle has been disabled for duplicate domains." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:705 msgid "Different Domain per Language: A language domain cannot be the same as the main site URL. The toggle has been disabled for the matching domain." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:724 msgid "Please enter a domain." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:731 msgid "Invalid domain format." msgstr "" #. translators: %s is the error message #: add-ons-pro/multiple-domains/class-multiple-domains.php:745 msgid "Could not reach domain: %s" msgstr "" #. translators: %d is the HTTP status code #: add-ons-pro/multiple-domains/class-multiple-domains.php:761 msgid "Domain returned HTTP status %d." msgstr "" #: add-ons-pro/multiple-domains/class-multiple-domains.php:755 msgid "Domain is reachable!" msgstr "" #: add-ons-pro/navigation-based-on-language/class-navigation-based-on-language.php:80 msgid "Limit this menu item to the following languages" msgstr "" #: includes/advanced-settings/custom-date-format.php:20 msgid "Date format" msgstr "" #: includes/advanced-settings/custom-date-format.php:21 msgid "Customize the date formatting per each translated language.<br/>Leave empty for default WP setting or see more information <a href=\"https://wordpress.org/support/article/formatting-date-and-time/\" title=\"Formatting Date and Time\" target=\"_blank\">here</a>" msgstr "" #: includes/advanced-settings/custom-language.php:22 msgid "To edit an existing TranslatePress language, input the language code and fill in only the columns you want to overwrite (e.g. Language name, Flag).<br>You can also add new custom languages. They will be available under General settings, All Languages list, where the URL slug can be edited." msgstr "" #: includes/advanced-settings/custom-language.php:23 msgid "For custom flag, first upload the image in media library then paste the URL.<br>Changing or deleting a custom language will impact translations and site URL's.<br>The Language code and the ISO Code should contain only alphabetical values, numerical values, \"-\" and \"_\".<br>The ISO Codes can be found on <a href = \"https://cloud.google.com/translate/docs/languages\" target = \"_blank\">Google ISO Codes</a> and <a href = \"https://www.deepl.com/docs-api/translating-text/\" target = \"_blank\">DeepL Target Codes</a>." msgstr "" #: includes/advanced-settings/custom-language.php:28 msgid "Language code" msgstr "" #: includes/advanced-settings/custom-language.php:29 msgid "Language name" msgstr "" #: includes/advanced-settings/custom-language.php:30 msgid "Native name" msgstr "" #: includes/advanced-settings/custom-language.php:31 msgid "ISO Code" msgstr "" #: includes/advanced-settings/custom-language.php:32 msgid "Flag URL" msgstr "" #: includes/advanced-settings/custom-language.php:33 msgid "Text RTL" msgstr "" #: includes/advanced-settings/custom-language.php:36, includes/advanced-settings/separators.php:142 msgid "Custom language" msgstr "" #: includes/advanced-settings/disable-dynamic-translation.php:11 msgid "Disable dynamic translation" msgstr "" #: includes/advanced-settings/disable-dynamic-translation.php:12 msgid "It disables detection of strings displayed dynamically using JavaScript. <br/>Strings loaded via a server side AJAX call will still be translated." msgstr "" #: includes/advanced-settings/disable-gettext-strings.php:12 msgid "Disable translation for gettext strings" msgstr "" #: includes/advanced-settings/disable-gettext-strings.php:13 msgid "Gettext Strings are strings outputted by themes and plugins. <br> Translating these types of strings through TranslatePress can be unnecessary if they are already translated using the .po/.mo translation file system.<br>Enabling this option can improve the page load performance of your site in certain cases. The disadvantage is that you can no longer edit gettext translations using TranslatePress, nor benefit from automatic translation on these strings." msgstr "" #: includes/advanced-settings/disable-gettext-strings.php:60 msgid "Gettext Strings translation is disabled" msgstr "" #: includes/advanced-settings/disable-gettext-strings.php:61 msgid "To enable it go to " msgstr "" #: includes/advanced-settings/disable-gettext-strings.php:63 msgid "TranslatePress->Advanced Settings->Debug->Disable translation for gettext strings" msgstr "" #: includes/advanced-settings/disable-gettext-strings.php:64 msgid " and uncheck the Checkbox." msgstr "" #: includes/advanced-settings/disable-gettext-strings.php:67 msgid "Dismiss" msgstr "" #: includes/advanced-settings/disable-languages-sitemap.php:11 msgid "Exclude translated links from sitemap" msgstr "" #: includes/advanced-settings/disable-languages-sitemap.php:13 msgid "Do not include translated links in sitemaps generated by SEO plugins.<br/>Requires <a href=\"https://translatepress.com/docs/addons/seo-pack/?utm_source=tp-advanced&utm_medium=client-site&utm_campaign=miscellaneous\" title=\"TranslatePress Add-on SEO Pack documentation\" target=\"_blank\"> SEO Pack Add-on</a> to be installed and activated." msgstr "" #: includes/advanced-settings/disable-post-container-tags.php:13 msgid "Disable post container tags for post title" msgstr "" #: includes/advanced-settings/disable-post-container-tags.php:14 msgid "It disables search indexing the post title in translated languages.<br/>Useful when the title of the post doesn't allow HTML thus breaking the page." msgstr "" #: includes/advanced-settings/disable-post-container-tags.php:38 msgid "Disable post container tags for post content" msgstr "" #: includes/advanced-settings/disable-post-container-tags.php:39 msgid "It disables search indexing the post content in translated languages.<br/>Useful when the content of the post doesn't allow HTML thus breaking the page." msgstr "" #: includes/advanced-settings/do-not-translate-certain-paths.php:12, includes/advanced-settings/separators.php:60 msgid "Do not translate certain paths" msgstr "" #: includes/advanced-settings/do-not-translate-certain-paths.php:13 msgid "Choose what paths can be translated. Supports wildcard at the end of the path.<br>For example, to exclude https://example.com/some/path you can either use the rule /some/path/ or /some/*.<br>Enter each rule on it's own line. To exclude the home page use {{home}}." msgstr "" #: includes/advanced-settings/do-not-translate-certain-paths.php:34 msgid "Exclude Paths From Translation" msgstr "" #: includes/advanced-settings/do-not-translate-certain-paths.php:39 msgid "Translate Only Certain Paths" msgstr "" #: includes/advanced-settings/enable-hreflang-xdefault.php:12 msgid "Enable the hreflang x-default tag for language:" msgstr "" #: includes/advanced-settings/enable-hreflang-xdefault.php:13 msgid "Enables the hreflang=\"x-default\" for an entire language. See documentation for more details." msgstr "" #: includes/advanced-settings/enable-numerals-translation.php:13 msgid "Translate numbers and numerals" msgstr "" #: includes/advanced-settings/enable-numerals-translation.php:14 msgid "Enable translation of numbers ( e.g. phone numbers)" msgstr "" #: includes/advanced-settings/exclude-dynamic-selectors.php:13, includes/advanced-settings/exclude-selectors-automatic-translation.php:16, includes/advanced-settings/exclude-selectors.php:13 msgid "Selector" msgstr "" #: includes/advanced-settings/exclude-dynamic-selectors.php:15, includes/advanced-settings/separators.php:39 msgid "Exclude from dynamic translation" msgstr "" #: includes/advanced-settings/exclude-dynamic-selectors.php:16 msgid "Do not dynamically translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being translated using JavaScript.<br/>These strings will still be translated on the server side if possible." msgstr "" #: includes/advanced-settings/exclude-gettext-strings.php:13 msgid "Gettext String" msgstr "" #: includes/advanced-settings/exclude-gettext-strings.php:16 msgid "Exclude Gettext Strings" msgstr "" #: includes/advanced-settings/exclude-gettext-strings.php:17 msgid "Exclude these strings from being translated as Gettext strings by TranslatePress. Leave the domain empty to take into account any Gettext string.<br/>Can still be translated through po/mo files." msgstr "" #: includes/advanced-settings/exclude-selectors-automatic-translation.php:18, includes/advanced-settings/separators.php:53 msgid "Exclude selectors only from automatic translation" msgstr "" #: includes/advanced-settings/exclude-selectors-automatic-translation.php:19 msgid "Do not automatically translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being automatically translated.<br>Manual translation of these strings is still possible." msgstr "" #: includes/advanced-settings/exclude-selectors.php:15, includes/advanced-settings/separators.php:46 msgid "Exclude selectors from translation" msgstr "" #: includes/advanced-settings/exclude-selectors.php:16 msgid "Do not translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being translated.<br>These strings cannot be translated manually nor automatically." msgstr "" #: includes/advanced-settings/exclude-words-from-auto-translate.php:13 msgid "String" msgstr "" #: includes/advanced-settings/exclude-words-from-auto-translate.php:15, includes/advanced-settings/separators.php:32 msgid "Exclude strings from automatic translation" msgstr "" #: includes/advanced-settings/exclude-words-from-auto-translate.php:16 msgid "Do not automatically translate these strings (ex. names, technical words...)<br>Paragraphs containing these strings will still be translated except for the specified part." msgstr "" #: includes/advanced-settings/fix-broken-html.php:11 msgid "Fix broken HTML" msgstr "" #: includes/advanced-settings/fix-broken-html.php:12 msgid "General attempt to fix broken or missing HTML on translated pages.<br/>" msgstr "" #: includes/advanced-settings/force-slash-at-end-of-links.php:13 msgid "Force slash at end of home url:" msgstr "" #: includes/advanced-settings/force-slash-at-end-of-links.php:14 msgid "Ads a slash at the end of the home_url() function" msgstr "" #: includes/advanced-settings/hreflang-remove-locale.php:14 msgid "Show Both (recommended)" msgstr "" #: includes/advanced-settings/hreflang-remove-locale.php:14 msgid "Remove Country Locale" msgstr "" #: includes/advanced-settings/hreflang-remove-locale.php:14 msgid "Remove Region Independent Locale" msgstr "" #: includes/advanced-settings/hreflang-remove-locale.php:15 msgid "Remove duplicate hreflang" msgstr "" #: includes/advanced-settings/hreflang-remove-locale.php:16 msgid "Choose which hreflang tags will appear on your website.<br/>We recommend showing both types of hreflang tags as indicated by <a href=\"https://developers.google.com/search/docs/advanced/crawling/localized-versions\" title=\"Google Crawling\" target=\"_blank\">Google documentation</a>.<br/>Removing Country Locale when having multiple Country Locales of the same language (ex. English UK and English US) will result in showing one hreflang tag with link to just one of the region locales for that language." msgstr "" #: includes/advanced-settings/html-lang-remove-locale.php:14 msgid "Default (example: en-US, fr-CA, etc.)" msgstr "" #: includes/advanced-settings/html-lang-remove-locale.php:14 msgid "Regional (example: en, fr, es, etc.)" msgstr "" #: includes/advanced-settings/html-lang-remove-locale.php:15 msgid "HTML Lang Attribute Format" msgstr "" #: includes/advanced-settings/html-lang-remove-locale.php:16 msgid "Change lang attribute of the html tag to a format that includes country regional or not. <br>In HTML, the lang attribute (<html lang=\"en-US\">) should be used to specify the language of text content so that the browser can correctly display or process your content (eg. for hyphenation, styling, spell checking, etc)." msgstr "" #: includes/advanced-settings/load-legacy-language-switcher.php:12 msgid "Load legacy Language Switcher" msgstr "" #: includes/advanced-settings/load-legacy-language-switcher.php:13 msgid "Applies to all types of language switchers (floating, shortcode, and menu). When enabled, the site will revert to using the original Language Switcher configured in the General Settings tab, replacing the new customizable version. Your existing switcher settings will remain saved, but they will be ignored while this option is active." msgstr "" #: includes/advanced-settings/load-legacy-seo-pack.php:15 msgid "Load legacy SEO Pack Add-On" msgstr "" #: includes/advanced-settings/load-legacy-seo-pack.php:17 msgid "In case the recent migration to the new slug rewrite is causing trouble, set this to Yes to use the old method <br> Please <a href=\"https://translatepress.com/support/open-ticket/?utm_source=tp-advanced&utm_medium=client-site&utm_campaign=troubleshooting\" target=\"_blank\">open a support ticket</a> letting us know of the issues you are having." msgstr "" #: includes/advanced-settings/manual-translation-only.php:11 msgid "Manual Translation Only" msgstr "" #: includes/advanced-settings/manual-translation-only.php:12 msgid "TranslatePress pro-actively scans and saves strings in the database when users access translated pages. <br>This setting disables this functionality and only allows translation and string saving when inside the Translation Editor. <br>Also disables machine translation outside the Translation Editor, giving you better control over character spending, by translating only the pages you visit in the Translation Editor." msgstr "" #: includes/advanced-settings/open-language-switcher-shortcode-on-click.php:12 msgid "Open language switcher only on click" msgstr "" #: includes/advanced-settings/open-language-switcher-shortcode-on-click.php:13 msgid "Open the language switcher shortcode by clicking on it instead of hovering.<br> Close it by clicking on it, anywhere else on the screen or by pressing the escape key. This will affect only the shortcode language switcher." msgstr "" #: includes/advanced-settings/opposite-flag-shortcode.php:12 msgid "Show opposite language in the language switcher" msgstr "" #: includes/advanced-settings/opposite-flag-shortcode.php:13 msgid "Transforms the language switcher into a button showing the other available language, not the current one.<br> Only works when there are exactly two languages, the default one and a translation one.<br>This will affect the shortcode language switcher and floating language switcher as well.<br> To achieve this in menu language switcher go to Appearance->Menus->Language Switcher and select Opposite Language." msgstr "" #: includes/advanced-settings/remove-duplicates-from-db.php:12 msgid "<a href=\"%s\">Click here</a> to access the database optimization tool." msgstr "" #: includes/advanced-settings/remove-duplicates-from-db.php:12 msgid "It helps remove possible duplicate translations, clear unnecessary data and repair possible metadata issues." msgstr "" #: includes/advanced-settings/remove-duplicates-from-db.php:12 msgid "<a href=\"%s\" target=\"_blank\">Here</a> you can observe the last 5 SQL errors relevant to TranslatePress if they exist." msgstr "" #: includes/advanced-settings/separators.php:12, includes/advanced-settings/separators.php:67 msgid "Troubleshooting" msgstr "" #: includes/advanced-settings/separators.php:25 msgid "Exclude Gettext strings" msgstr "" #: includes/advanced-settings/separators.php:74, includes/advanced-settings/separators.php:120 msgid "Debug" msgstr "" #: includes/advanced-settings/separators.php:81 msgid "Custom languages" msgstr "" #: includes/advanced-settings/separators.php:88, includes/advanced-settings/separators.php:131 msgid "Miscellaneous options" msgstr "" #: includes/advanced-settings/separators.php:109 msgid "Exclude strings & pages" msgstr "" #: includes/advanced-settings/serve-similar-translation.php:13 msgid "Automatic Translation Memory" msgstr "" #: includes/advanced-settings/serve-similar-translation.php:14 msgid "Serve same translation for similar text. The strings need to have a percentage of 95% similarity.<br>Helps prevent losing existing translation when correcting typos or making minor adjustments to the original text. <br>If a translation already exists for a very similar original string, it will automatically be used for the current original string.<br>Does not work when making changes to a text that is part of a translation block unless the new text is manually merged again in a translation block.<br>Each string needs to have a minimum of 50 characters." msgstr "" #: includes/advanced-settings/serve-similar-translation.php:14 msgid "WARNING: This feature can negatively impact page loading times in secondary languages, particularly with large databases (for example websites with a lot of pages or products). If you experience slow loading times, disable this and try again." msgstr "" #: includes/advanced-settings/show-dynamic-content-before-translation.php:11 msgid "Fix missing dynamic content" msgstr "" #: includes/advanced-settings/show-dynamic-content-before-translation.php:12 msgid "May help fix missing content inserted using JavaScript. <br> It shows dynamically inserted content in original language for a moment before the translation request is finished." msgstr "" #: includes/advanced-settings/strip-gettext-post-content.php:11 msgid "Filter Gettext wrapping from post content and title" msgstr "" #: includes/advanced-settings/strip-gettext-post-content.php:12 msgid "Filters gettext wrapping such as #!trpst#trp-gettext from all updated post content and post title. Does not affect previous post content. <br/><strong>Database backup is recommended before switching on.</strong>" msgstr "" #: includes/advanced-settings/strip-gettext-post-meta.php:12 msgid "Filter Gettext wrapping from post meta" msgstr "" #: includes/advanced-settings/strip-gettext-post-meta.php:13 msgid "Filters gettext wrapping such as #!trpst#trp-gettext from all updated post meta. Does not affect previous post meta. <br/><strong>Database backup is recommended before switching on.</strong>" msgstr "" #: includes/google-translate/functions.php:9 msgid "Google Translate v2" msgstr "" #: includes/google-translate/functions.php:45 msgid "Google Translate API Key" msgstr "" #: includes/google-translate/functions.php:48 msgid "Add your API Key here..." msgstr "" #: includes/google-translate/functions.php:68 msgid "Visit <a href=\"https://cloud.google.com/docs/authentication/api-keys\" target=\"_blank\">this link</a> to see how you can set up an API key, <strong>control API costs</strong> and set HTTP referrer restrictions." msgstr "" #: includes/google-translate/functions.php:69 msgid "Your HTTP referrer is: %s" msgstr "" #: includes/google-translate/functions.php:106 msgid "There was an error on the server processing your Google Translate key." msgstr "" #: includes/google-translate/functions.php:103 msgid "There was an error with your Google Translate key." msgstr "" #: includes/mtapi/class-mtapi-machine-translator.php:239 msgid "Please check your TranslatePress license key." msgstr "" #: includes/mtapi/functions.php:8 msgid "TranslatePress AI" msgstr "" #: includes/mtapi/functions.php:40, includes/onboarding/class-autotranslation.php:181, add-ons-pro/deepl/includes/class-deepl.php:195 msgid "No Active License Detected for this website." msgstr "" #: includes/mtapi/functions.php:45 msgid "In order to enable Automatic Translation using TranslatePress AI, you need a license key by creating a free account." msgstr "" #: includes/mtapi/functions.php:51 msgid "Create your Free Account" msgstr "" #: includes/mtapi/functions.php:54 msgid " or " msgstr "" #: includes/mtapi/functions.php:57, add-ons-pro/deepl/includes/class-deepl.php:200 msgid "Enter your license key" msgstr "" #: includes/mtapi/functions.php:60, add-ons-pro/deepl/includes/class-deepl.php:202 msgid " Or %1$spurchase one here%2$s" msgstr "" #: includes/mtapi/functions.php:69 msgid "Your free account includes: " msgstr "" #: includes/mtapi/functions.php:84, includes/mtapi/functions.php:185 msgid "Get more AI Tokens and unlock all AI features with TranslatePress Pro." msgstr "" #: includes/mtapi/functions.php:143, includes/onboarding/class-autotranslation.php:289 msgid "You have a valid %s <strong>license</strong>." msgstr "" #: includes/mtapi/functions.php:150, includes/onboarding/class-autotranslation.php:296 msgid " words remaining. " msgstr "" #: includes/mtapi/functions.php:156 msgid "Recheck" msgstr "" #: includes/mtapi/functions.php:160 msgid "Rechecking..." msgstr "" #: includes/mtapi/functions.php:163 msgid "Done." msgstr "" #: includes/mtapi/functions.php:175, includes/onboarding/class-autotranslation.php:306 msgid "Manage your license & quota on the %s" msgstr "" #: includes/mtapi/functions.php:177, includes/onboarding/class-autotranslation.php:307 msgid "TranslatePress.com Account Page" msgstr "" #: includes/onboarding/class-addons.php:52 msgid "SEO support for page slug, page title, description and Facebook and Twitter social graph information. The HTML lang attribute is properly set." msgstr "" #: includes/onboarding/class-addons.php:147 msgid "Enable Modules" msgstr "" #: includes/onboarding/class-addons.php:148 msgid "Enable Add-on modules to extend TranslatePress and enhance the functionality of your translated site." msgstr "" #: includes/onboarding/class-addons.php:153 msgid "More functionality with TranslatePress Pro." msgstr "" #: includes/onboarding/class-addons.php:156, includes/onboarding/class-languages.php:155 msgid "Already a Pro User?" msgstr "" #: includes/onboarding/class-addons.php:156, includes/onboarding/class-languages.php:155 msgid "Activate License Key" msgstr "" #: includes/onboarding/class-addons.php:216 msgid "This add-on is not available on your current plan." msgstr "" #: includes/onboarding/class-addons.php:228, includes/onboarding/class-autotranslation.php:189, includes/onboarding/class-autotranslation.php:313, includes/onboarding/class-languages.php:125, includes/onboarding/class-switcher.php:322, includes/onboarding/class-welcome.php:43 msgid "Continue" msgstr "" #: includes/onboarding/class-autotranslation.php:16, includes/onboarding/class-finish.php:17, includes/onboarding/class-install.php:30, includes/onboarding/class-install.php:106, includes/onboarding/class-languages.php:38, includes/onboarding/class-license.php:31, includes/onboarding/class-switcher.php:27, includes/onboarding/class-welcome.php:13 msgid "The link you followed has expired. Please reload the page and try again." msgstr "" #: includes/onboarding/class-autotranslation.php:45 msgid "A valid license is required to enable Automatic Translation." msgstr "" #: includes/onboarding/class-autotranslation.php:102 msgid "Your license key is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=tp-ai\">https://translatepress.com/account</a> -> Manage Sites." msgstr "" #: includes/onboarding/class-autotranslation.php:105, includes/onboarding/class-license.php:76 msgid "<p><strong>License key mismatch.</strong> The license you entered doesn't match the TranslatePress version you have installed.</p><p>Please check that you've installed the correct version for your license from your TranslatePress account.</p>" msgstr "" #: includes/onboarding/class-autotranslation.php:127 msgid "Automatically translate your website using TranslatePress AI." msgstr "" #: includes/onboarding/class-autotranslation.php:164 msgid "In order to enable Automatic Translation using TranslatePress AI, please enter your license key from" msgstr "" #: includes/onboarding/class-autotranslation.php:164 msgid "your account." msgstr "" #: includes/onboarding/class-autotranslation.php:192, includes/onboarding/class-autotranslation.php:225 msgid "Skip and continue with manual translation »" msgstr "" #: includes/onboarding/class-autotranslation.php:200 msgid "Get Your Free TranslatePress AI License" msgstr "" #: includes/onboarding/class-autotranslation.php:204 msgid "Generate License" msgstr "" #: includes/onboarding/class-autotranslation.php:209 msgid "Creating a free account includes: " msgstr "" #: includes/onboarding/class-autotranslation.php:221 msgid "Are you a TranslatePress PRO user?" msgstr "" #: includes/onboarding/class-autotranslation.php:221 msgid "Install & Activate your pro plugin." msgstr "" #: includes/onboarding/class-autotranslation.php:316 msgid "Skip this step" msgstr "" #: includes/onboarding/class-finish.php:29 msgid "The email address you added is incorrect." msgstr "" #: includes/onboarding/class-finish.php:45 msgid "Setup Complete" msgstr "" #: includes/onboarding/class-finish.php:48 msgid "You're ready to start translating!" msgstr "" #: includes/onboarding/class-finish.php:49 msgid "You have successfully set up TranslatePress for your website." msgstr "" #: includes/onboarding/class-finish.php:54 msgid "Receive " msgstr "" #: includes/onboarding/class-finish.php:55 msgid "Sign me up to the Newsletter" msgstr "" #: includes/onboarding/class-finish.php:66 msgid "Start translating" msgstr "" #: includes/onboarding/class-finish.php:69 msgid "Sign Up and Start translating" msgstr "" #: includes/onboarding/class-install.php:33 msgid "Please upload a TranslatePress Pro plugin file." msgstr "" #: includes/onboarding/class-install.php:47 msgid "Upload error: " msgstr "" #: includes/onboarding/class-install.php:62 msgid "Install failed: " msgstr "" #: includes/onboarding/class-install.php:69 msgid "Plugin installed, but entry file not found. " msgstr "" #: includes/onboarding/class-install.php:76 msgid "Activation error: " msgstr "" #: includes/onboarding/class-install.php:95 msgid "Invalid plugin specified." msgstr "" #: includes/onboarding/class-install.php:125 msgid "Invalid action specified." msgstr "" #: includes/onboarding/class-install.php:121 msgid "Plugin deactivation failed." msgstr "" #: includes/onboarding/class-install.php:115 msgid "Plugin activation failed: " msgstr "" #: includes/onboarding/class-install.php:138 msgid "First, install and activate TranslatePress Pro" msgstr "" #: includes/onboarding/class-install.php:140 msgid "Please upload the TranslatePress PRO zip archive from your" msgstr "" #: includes/onboarding/class-install.php:141, includes/onboarding/class-license.php:103 msgid "TranslatePress Account" msgstr "" #: includes/onboarding/class-install.php:151 msgid "Install and Activate" msgstr "" #: includes/onboarding/class-install.php:158 msgid "Installed Pro versions" msgstr "" #: includes/onboarding/class-install.php:190 msgid "« Go back" msgstr "" #: includes/onboarding/class-install.php:193 msgid "Activate License »" msgstr "" #: includes/onboarding/class-languages.php:49 msgid "You are trying to add an invalid additional language. Please select a valid option." msgstr "" #: includes/onboarding/class-languages.php:44 msgid "Please add an additional language." msgstr "" #: includes/onboarding/class-languages.php:42 msgid "You are trying to add an invalid default language. Please select a valid option." msgstr "" #: includes/onboarding/class-languages.php:40 msgid "You need to select a default language." msgstr "" #: includes/onboarding/class-languages.php:83 msgid "Configure Site Languages" msgstr "" #: includes/onboarding/class-languages.php:84 msgid "Select the default and additional languages for your website." msgstr "" #: includes/onboarding/class-languages.php:85 msgid "You can edit your site languages at any point." msgstr "" #: includes/onboarding/class-languages.php:104, includes/onboarding/class-languages.php:131 msgid "Additional Language" msgstr "" #: includes/onboarding/class-languages.php:106 msgid "Choose a secondary language..." msgstr "" #: includes/onboarding/class-languages.php:123 msgid "Add Language" msgstr "" #: includes/onboarding/class-languages.php:133 msgid "Choose a language..." msgstr "" #: includes/onboarding/class-languages.php:152 msgid "Add more than two languages with TranslatePress Pro." msgstr "" #: includes/onboarding/class-license.php:73 msgid "Your license key is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=activate-license\">https://translatepress.com/account</a> -> Manage Sites." msgstr "" #: includes/onboarding/class-license.php:100 msgid "Add your License Key" msgstr "" #: includes/onboarding/class-license.php:102 msgid "Add your License Key to unlock all premium features. Find the License Key in your" msgstr "" #: includes/onboarding/class-license.php:117 msgid "Your license is valid and active." msgstr "" #: includes/onboarding/class-license.php:136 msgid "« Go Back" msgstr "" #: includes/onboarding/class-switcher.php:238 msgid "Set up Language Switcher" msgstr "" #: includes/onboarding/class-switcher.php:239 msgid "Select the style of the language switcher. You will find more ways to display it, in plugin settings." msgstr "" #: includes/onboarding/class-switcher.php:253 msgid "Displays a small language drop-down across your website, in a corner of your choosing." msgstr "" #: includes/onboarding/class-switcher.php:257 msgid "Switcher Location" msgstr "" #: includes/onboarding/class-switcher.php:268 msgid "Apply a Template" msgstr "" #: includes/onboarding/class-switcher.php:269 msgid "You can customize the design later" msgstr "" #: includes/onboarding/class-switcher.php:275 msgid "Default Template" msgstr "" #: includes/onboarding/class-switcher.php:286 msgid "Dark Template" msgstr "" #: includes/onboarding/class-switcher.php:299 msgid "Border Template" msgstr "" #: includes/onboarding/class-switcher.php:303 msgid "Border" msgstr "" #: includes/onboarding/class-switcher.php:310 msgid "Transparent Template" msgstr "" #: includes/onboarding/class-switcher.php:314 msgid "Transparent" msgstr "" #: includes/onboarding/class-welcome.php:39 msgid "Welcome to TranslatePress" msgstr "" #: includes/onboarding/class-welcome.php:40 msgid "Quick guided setup to configure TranslatePress in no time!" msgstr "" #: includes/onboarding/class-welcome.php:42 msgid "It takes less than a minute." msgstr "" #: includes/string-translation/class-gettext-scan.php:88 msgid "Scanning item %1$d of %2$d..." msgstr "" #: includes/string-translation/class-string-translation.php:176 msgid "Manually translated" msgstr "" #: includes/string-translation/class-string-translation.php:177 msgid "Automatically translated" msgstr "" #: includes/string-translation/class-string-translation.php:178 msgid "Not translated" msgstr "" #: includes/string-translation/class-string-translation.php:188 msgid "Bulk Actions" msgstr "" #: includes/string-translation/class-string-translation.php:190 msgid "Delete entries" msgstr "" #: includes/string-translation/class-string-translation.php:195 msgid "Edit" msgstr "" #: includes/string-translation/class-string-translation.php:196 msgid "Delete" msgstr "" #: includes/string-translation/class-string-translation.php:223 msgid "Filter" msgstr "" #: includes/string-translation/class-string-translation.php:224 msgid "Clear filters" msgstr "" #: includes/string-translation/class-string-translation.php:226 msgid "Add New" msgstr "" #: includes/string-translation/class-string-translation.php:227 msgid "Rescan plugins and theme for strings" msgstr "" #: includes/string-translation/class-string-translation.php:228 msgid "Scanning plugins and theme for strings..." msgstr "" #: includes/string-translation/class-string-translation.php:229 msgid "Plugins and theme scan is complete" msgstr "" #: includes/string-translation/class-string-translation.php:230 msgid "Plugins and theme scan did not finish due to an error" msgstr "" #: includes/string-translation/class-string-translation.php:231 msgid "Import / Export" msgstr "" #: includes/string-translation/class-string-translation.php:232 msgid "items" msgstr "" #: includes/string-translation/class-string-translation.php:233 msgctxt "page 1 of 3" msgid "of" msgstr "" #: includes/string-translation/class-string-translation.php:234 msgid "See More" msgstr "" #: includes/string-translation/class-string-translation.php:235 msgid "See Less" msgstr "" #: includes/string-translation/class-string-translation.php:236 msgid "Apply" msgstr "" #: includes/string-translation/class-string-translation.php:237 msgid "No strings match your query." msgstr "" #: includes/string-translation/class-string-translation.php:238 msgid "Try to rescan plugins and theme for strings." msgstr "" #: includes/string-translation/class-string-translation.php:239 msgid "An error occurred while loading results. Most likely you were logged out. Reload page?" msgstr "" #: includes/string-translation/class-string-translation.php:240 msgid "found in translation" msgstr "" #: includes/string-translation/class-string-translation.php:242 msgid "Select All" msgstr "" #: includes/string-translation/class-string-translation.php:243 msgid "Select Visible" msgstr "" #: includes/string-translation/class-string-translation.php:244 msgid "You are about to perform this action on all the strings matching your filter, not just the visibly checked. To perform the action only to the visible strings click \"Select Visible\" from the table header dropdown." msgstr "" #: includes/string-translation/class-string-translation.php:245 msgid "You are about to perform this action only on the visible strings. To perform the action on all the strings matching the filter click \"Select All\" from the table header dropdown." msgstr "" #: includes/string-translation/class-string-translation.php:246 msgid "To continue please type the word:" msgstr "" #: includes/string-translation/class-string-translation.php:247 msgid "The word typed was incorrect. Action was cancelled." msgstr "" #: includes/string-translation/class-string-translation.php:249 msgctxt "Untranslated in this language" msgid "in" msgstr "" #: includes/string-translation/class-string-translation.php:252 msgid "Warning: This action cannot be undone. Deleting a string will remove its current translation. The original string will appear again in this interface after TranslatePress detects it. This action is NOT equivalent to excluding the string from being translated again." msgstr "" #: includes/string-translation/class-string-translation.php:253 msgid "%d original entries and their translations were deleted." msgstr "" #: includes/string-translation/class-string-translation.php:256 msgid "Navigate to next page" msgstr "" #: includes/string-translation/class-string-translation.php:257 msgid "Navigate to previous page" msgstr "" #: includes/string-translation/class-string-translation.php:258 msgid "Navigate to first page" msgstr "" #: includes/string-translation/class-string-translation.php:259 msgid "Navigate to last page" msgstr "" #: includes/string-translation/class-string-translation.php:260 msgid "Type a page number to navigate to" msgstr "" #: includes/string-translation/class-string-translation.php:261 msgid "Incorrect page number. Type a page number between 1 and total number of pages" msgstr "" #: includes/string-translation/class-string-translation.php:262 msgid "Search original and translated strings containing typed keywords while also matching selected filters. Place string in quotes for exact match: \"string\"" msgstr "" #: includes/string-translation/class-string-translation.php:263 msgid "Filter strings according to selected translation status, filters and keywords and selected filters" msgstr "" #: includes/string-translation/class-string-translation.php:264 msgid "Removes selected filters" msgstr "" #: includes/string-translation/class-string-translation.php:265 msgid "See options for selecting all strings" msgstr "" #: includes/string-translation/class-string-translation.php:266 msgid "Click to sort strings by this column" msgstr "" #: includes/string-translation/class-string-translation.php:267 msgid "Language in which the translation status filter applies. Leave unselected for the translation status to apply to ANY language" msgstr "" #: includes/string-translation/class-string-translation.php:268 msgid "Search" msgstr "" #: includes/string-translation/class-string-translation.php:269 msgid "Slugs that are not found in either one of the other categories." msgstr "" #: includes/string-translation/class-string-translation.php:282 msgid "Plugins and Theme String Translation" msgstr "" #: includes/string-translation/class-string-translation.php:283 msgid "Gettext" msgstr "" #: includes/string-translation/class-string-translation.php:284 msgid "Search Gettext Strings" msgstr "" #: includes/string-translation/class-string-translation.php:291, includes/string-translation/class-string-translation.php:321, includes/string-translation/class-string-translation.php:346 msgid "ID" msgstr "" #: includes/string-translation/class-string-translation.php:292, includes/string-translation/class-string-translation.php:322, includes/string-translation/class-string-translation.php:347 msgid "Original String" msgstr "" #: includes/string-translation/class-string-translation.php:293, includes/string-translation/class-string-translation.php:323, includes/string-translation/class-string-translation.php:348, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:29, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:42, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:62, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:82, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:100, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:117, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:28, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:41, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:61, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:81, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:99 msgid "Translation" msgstr "" #: includes/string-translation/class-string-translation.php:300, includes/string-translation/class-string-translation.php:330 msgid "Filter by domain" msgstr "" #: includes/string-translation/class-string-translation.php:304 msgid "Filter by type" msgstr "" #: includes/string-translation/class-string-translation.php:305 msgid "Email text" msgstr "" #: includes/string-translation/class-string-translation.php:312 msgid "Emails String Translation" msgstr "" #: includes/string-translation/class-string-translation.php:313 msgid "Emails" msgstr "" #: includes/string-translation/class-string-translation.php:314 msgid "Search Email Strings" msgstr "" #: includes/string-translation/class-string-translation.php:338 msgid "User Inputted String Translation" msgstr "" #: includes/string-translation/class-string-translation.php:339 msgid "Regular" msgstr "" #: includes/string-translation/class-string-translation.php:340 msgid "Search Regular Strings" msgstr "" #: includes/string-translation/class-string-translation.php:354 msgid "Filter by Translation Block" msgstr "" #: includes/string-translation/class-string-translation.php:371, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:16, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:15 msgid "URL Slugs Translation" msgstr "" #: includes/string-translation/string-translation-editor.php:14 msgid "String Translation Editor" msgstr "" #: add-ons-advanced/seo-pack/includes/class-slug-manager.php:61, add-ons-advanced/seo-pack-legacy/includes/class-slug-manager.php:55, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:61, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:60 msgid "Post Slug" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-cookie-sync.php:114 msgid "Invalid language code" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:54 msgid "First by browser language, then IP address (recommended)" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:55 msgid "First by IP address, then by browser language" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:56 msgid "Only by browser language" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:57 msgid "Only by IP address" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:147 msgid "A popup appears asking the user if they want to be redirected" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:148 msgid "Redirect directly (*not recommended)" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:154 msgid "Pop-up window over the content" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:155 msgid "Hello bar before the content" msgstr "" #: add-ons-pro/automatic-language-detection/includes/class-ald-settings.php:188 msgid "WARNING. Cannot determine your language preference based on your current IP.<br>This is most likely because the website is on a local environment." msgstr "" #: add-ons-pro/automatic-language-detection/partials/general-settings.php:8 msgid "Go to <a href=\"%s\" target=\"_self\">Advanced</a> tab to change this feature's settings" msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:7 msgid "User Language Detection Method" msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:22 msgid "Select how the language should be detected for first time visitors.<br>The visitor's last displayed language will be remembered through cookies." msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:31 msgid "User Notification Popup" msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:36 msgid "A popup appears asking the user if they want to be redirected." msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:40 msgid "Popup Type" msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:51 msgid "Popup Text" msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:55 msgid "The same text is displayed in all languages. <br>A selecting language switcher will be appended to the pop-up. The detected language is pre-selected." msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:60 msgid "Button Text" msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:64 msgid "Write the text you wish to appear on the button.." msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:69 msgid "Close Button Text" msgstr "" #: add-ons-pro/automatic-language-detection/partials/settings-option.php:73 msgid "Write the text you wish to appear on the close button. Leave empty for just the close button." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:62 msgid "Bad request. There was an error accessing the DeepL API." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:65 msgid "The API key entered is invalid." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:68 msgid "The API resource could not be found." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:71 msgid "The request size is too large." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:74 msgid "The request is too long." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:77 msgid "Too many requests. Please try again later." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:80 msgid "Your translation quota has been reached." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:83 msgid "We could not process your request. Please try again later." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:86 msgid "There is an error on the DeepL service and your request could not be processed." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:132 msgid "DeepL API Type" msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:137 msgid "Pro" msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:140 msgid "Free" msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:146 msgid "Select the type of DeepL API you want to use." msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:151 msgid "DeepL API Key" msgstr "" #: add-ons-pro/deepl/includes/class-deepl.php:178 msgid "Visit <a href=\"%s\" target=\"_blank\">this link</a> to see how you can set up an API key and control API costs." msgstr "" #: add-ons-pro/translator-accounts/includes/class-translator-accounts-activator.php:34, add-ons-pro/translator-accounts/includes/class-translator-accounts.php:101, add-ons-pro/translator-accounts/includes/class-translator-accounts.php:102 msgid "Translator" msgstr "" #: add-ons-pro/translator-accounts/includes/class-translator-accounts.php:97 msgid " TranslatePress Settings" msgstr "" #: add-ons-pro/translator-accounts/includes/class-translator-accounts.php:106 msgid "Allow this user to translate the website." msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-option-based-strings.php:97, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-api-postslug.php:108, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-api-term.php:98 msgid "(inactive)" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:21, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:20 msgid "Taxonomy Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:22, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:21 msgid "Search Taxonomy Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:28, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:27 msgid "Taxonomy Slug" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:35, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:34 msgid "Term Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:36, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:35 msgid "Search Term Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:41, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:40 msgid "Term Slug" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:43, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:42 msgid "Taxonomy" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:48, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:47 msgid "Filter by Taxonomy" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:54, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:53 msgid "Post Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:55, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:54 msgid "Search Post Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:60, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:59 msgid "Post ID" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:63, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:62 msgid "Post Type" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:68, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:67 msgid "Filter by Post Type" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:72, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:71 msgid "Published" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:73, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:72 msgid "Any Post Status" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:79, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:78 msgid "Post Type Base Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:81, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:80 msgid "Post Type Base Slug" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:85, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:84 msgid "Search Post Type Base Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:97, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:96 msgid "WooCommerce Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:99, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:98 msgid "WooCommerce Slug" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:103, add-ons-advanced/seo-pack-legacy/includes/string-translation/class-string-translation-seo.php:102 msgid "Search WooCommerce Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:114, add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:116 msgid "Other Slugs" msgstr "" #: add-ons-advanced/seo-pack/includes/string-translation/class-string-translation-seo.php:120 msgid "Search Other Slugs" msgstr "" languages/translatepress-multilingual.catalog.php 0000777 00000265701 15251156640 0016441 0 ustar 00 <?php __("Please update the TranslatePress - Multilingual plugin to version %1$s or higher to ensure %2$s functions correctly.", "translatepress-multilingual"); ?> <?php __("Please install and activate the TranslatePress - Multilingual plugin", "translatepress-multilingual"); ?> <?php __("Install & Activate", "translatepress-multilingual"); ?> <?php __("Please update TranslatePress - Multilingual to version %2$s or newer. Your currently installed version of TranslatePress - Multilingual is no longer compatible with the current version of %1$s.", "translatepress-multilingual"); ?> <?php __("All TranslatePress functionalities are disabled until then.", "translatepress-multilingual"); ?> <?php __("Update Now", "translatepress-multilingual"); ?> <?php __("This TranslatePress add-on has been migrated to the main plugin and is no longer used. You can delete it.", "translatepress-multilingual"); ?> <?php __("<strong>TranslatePress</strong> requires at least PHP version 5.6.20+ to run. It is the <a href=\"%s\">minimum requirement of the latest WordPress version</a>. Please contact your server administrator to update your PHP version.", "translatepress-multilingual"); ?> <?php __("Advanced", "translatepress-multilingual"); ?> <?php __("Settings saved.", "translatepress-multilingual"); ?> <?php __("Are you sure you want to remove this item?", "translatepress-multilingual"); ?> <?php __("Remove", "translatepress-multilingual"); ?> <?php __("Add", "translatepress-multilingual"); ?> <?php __("Select...", "translatepress-multilingual"); ?> <?php __("There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.", "translatepress-multilingual"); ?> <?php __("There is a new version of %1$s available. %2$sView version %3$s details%4$s.", "translatepress-multilingual"); ?> <?php __("To enable updates, please %1$senter your license key%2$s. Need a license key? %3$sPurchase one now%4$s.", "translatepress-multilingual"); ?> <?php __("To enable updates, please go to the %1$slicense page%2$s and check that you have a valid license.", "translatepress-multilingual"); ?> <?php __("To enable updates, your licence needs to be renewed. Please go to the %1$sTranslatePress Account%2$s page and login to renew.", "translatepress-multilingual"); ?> <?php __("You do not have permission to install plugin updates", "translatepress-multilingual"); ?> <?php __("Error", "translatepress-multilingual"); ?> <?php __("Your license key expired on %s.", "translatepress-multilingual"); ?> <?php __("Your license key has been disabled.", "translatepress-multilingual"); ?> <?php __("Your TranslatePress license key is invalid or missing.", "translatepress-multilingual"); ?> <?php __("Your license key is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-deactivated\">https://translatepress.com/account</a> -> Manage Sites.", "translatepress-multilingual"); ?> <?php __("<p><strong>License key mismatch.</strong> The license you entered doesn’t match the TranslatePress version you have installed.</p><p>Please check that you’ve installed the correct version for your license from your TranslatePress account.</p>", "translatepress-multilingual"); ?> <?php __("If you have only the free plugin installed but added a paid license, please install the paid plugin from your TranslatePress account.", "translatepress-multilingual"); ?> <?php __("Your license key has reached its activation limit.", "translatepress-multilingual"); ?> <?php __("Upgrade your plan to add more sites. %1$sUpgrade now%2$s", "translatepress-multilingual"); ?> <?php __("This website is already activated under a free license. Each website can only use one free license.", "translatepress-multilingual"); ?> <?php __("An error occurred, please try again.", "translatepress-multilingual"); ?> <?php __("You have successfully activated your license", "translatepress-multilingual"); ?> <?php __("Others", "translatepress-multilingual"); ?> <?php __("Restrict by Language", "translatepress-multilingual"); ?> <?php __("Exclude from Language", "translatepress-multilingual"); ?> <?php __("Restrict element to language", "translatepress-multilingual"); ?> <?php __("Show this element only in one language.", "translatepress-multilingual"); ?> <?php __("Enable translation", "translatepress-multilingual"); ?> <?php __("Allow translation to the corresponding language only if the content is written in the default language.", "translatepress-multilingual"); ?> <?php __("Select language", "translatepress-multilingual"); ?> <?php __("Choose in which language to show this element.", "translatepress-multilingual"); ?> <?php __("Exclude element from language", "translatepress-multilingual"); ?> <?php __("Exclude this element from specific languages.", "translatepress-multilingual"); ?> <?php __("Select languages", "translatepress-multilingual"); ?> <?php __("Choose from which languages to exclude this element.", "translatepress-multilingual"); ?> <?php __("This element will still be visible when you are translating your website through the Translation Editor.", "translatepress-multilingual"); ?> <?php __("The content of this element should be written in the default language.", "translatepress-multilingual"); ?> <?php __("<strong>TranslatePress</strong> encountered SQL errors. <a href=\"%s\" title=\"View TranslatePress SQL Errors\">Check out the errors</a>.", "translatepress-multilingual"); ?> <?php __("Automatic translation has been disabled.", "translatepress-multilingual"); ?> <?php __("Dismiss this notice.", "translatepress-multilingual"); ?> <?php __("Logged errors", "translatepress-multilingual"); ?> <?php __("These are the most recent 5 errors logged by TranslatePress:", "translatepress-multilingual"); ?> <?php __("Yes", "translatepress-multilingual"); ?> <?php __("Why are these errors occuring", "translatepress-multilingual"); ?> <?php __("If TranslatePress detects something wrong when executing queries on your database, it may disable the Automatic Translation feature in order to avoid any extra charging by Google/DeepL. Automatic Translation needs to be manually turned on, after you solve the issues.", "translatepress-multilingual"); ?> <?php __("The SQL errors detected can occur for various reasons including missing tables, missing permissions for the SQL user to create tables or perform other operations, problems after site migration or changes to SQL server configuration.", "translatepress-multilingual"); ?> <?php __("What you can do in this situation", "translatepress-multilingual"); ?> <?php __("Plan A.", "translatepress-multilingual"); ?> <?php __("Go to Settings -> TranslatePress -> General tab and Save Settings. This will regenerate the tables using your current SQL settings. Check if no more errors occur while browsing your website in a translated language. Look at the timestamps of the errors to make sure you are not seeing the old errors. Only the most recent 5 errors are displayed.", "translatepress-multilingual"); ?> <?php __("Plan B.", "translatepress-multilingual"); ?> <?php __("If your problem isn't solved, try the following steps:", "translatepress-multilingual"); ?> <?php __("Create a backup of your database", "translatepress-multilingual"); ?> <?php __("Create a copy of each translation table where you encounter errors. You can copy the table within the same database (trp_dictionary_en_us_es_es_COPY for example) -- perform this step only if you want to keep the current translations", "translatepress-multilingual"); ?> <?php __("Remove the trouble tables by executing the DROP function on them", "translatepress-multilingual"); ?> <?php __("Go to Settings -> TranslatePress -> General tab and Save Settings. This will regenerate the tables using your current SQL server.", "translatepress-multilingual"); ?> <?php __("Copy the relevant content from the duplicated tables (trp_dictionary_en_us_es_es_COPY for example) in the newly generated table (trp_dictionary_en_us_es_es) -- perform this step only if you want to keep the current translations", "translatepress-multilingual"); ?> <?php __("Test it to see if everything is working. If something went wrong, you can restore the backup that you've made at the first step. Check if no more errors occur while browsing your website in a translated language. Look at the timestamps of the errors to make sure you are not seeing the old errors. Only the most recent 5 errors are displayed.", "translatepress-multilingual"); ?> <?php __("Plan C.", "translatepress-multilingual"); ?> <?php __("If your problem still isn't solved, try asking your hosting about your errors. The most common issue is missing permissions for the SQL user, such as the Create Tables permission.", "translatepress-multilingual"); ?> <?php __("Could not install. Try again from <a href=\"%s\" >Plugins Dashboard.</a>", "translatepress-multilingual"); ?> <?php __("Active", "translatepress-multilingual"); ?> <?php __("Permission denied.", "translatepress-multilingual"); ?> <?php __("Invalid nonce.", "translatepress-multilingual"); ?> <?php __("Settings scope unknown.", "translatepress-multilingual"); ?> <?php __("Legacy disabled.", "translatepress-multilingual"); ?> <?php __("Language Switcher", "translatepress-multilingual"); ?> <?php __("Change language to %s", "translatepress-multilingual"); ?> <?php __("Automatic Translation", "translatepress-multilingual"); ?> <?php __("DeepL", "translatepress-multilingual"); ?> <?php __("Unsupported languages", "translatepress-multilingual"); ?> <?php __("The selected automatic translation engine does not provide support for these languages.<br>You can still manually translate pages in these languages using the Translation Editor.", "translatepress-multilingual"); ?> <?php __("API key validation failed.", "translatepress-multilingual"); ?> <?php __("API key verification was successful.", "translatepress-multilingual"); ?> <?php __("Please enter your Google Translate key.", "translatepress-multilingual"); ?> <?php __("Please enter your DeepL API key.", "translatepress-multilingual"); ?> <?php __("Not TranslatePress onboarding page.", "translatepress-multilingual"); ?> <?php __("Step %s does not exist", "translatepress-multilingual"); ?> <?php __("Welcome", "translatepress-multilingual"); ?> <?php __("Add Languages", "translatepress-multilingual"); ?> <?php __("Enable Addons", "translatepress-multilingual"); ?> <?php __("Finalize", "translatepress-multilingual"); ?> <?php __("Exit Setup", "translatepress-multilingual"); ?> <?php __("Upgrade", "translatepress-multilingual"); ?> <?php __("Nothing here", "translatepress-multilingual"); ?> <?php __("Your <strong>TranslatePress</strong> license is missing or invalid. <br/>Please %1$sregister your copy%2$s to enable automatic website translation via TranslatePress AI, premium addons, automatic updates and support. Need a license key? %3$sPurchase one now%4$s", "translatepress-multilingual"); ?> <?php __("Your <strong>TranslatePress</strong> license will expire on %1$s. Please %2$sRenew Your Licence%3$s to continue receiving access to automatic translations via TP AI, premium addons, product downloads and automatic updates. %4$sRenew Now%5$s", "translatepress-multilingual"); ?> <?php __("Error: ", "translatepress-multilingual"); ?> <?php __("Something went wrong, please try again.", "translatepress-multilingual"); ?> <?php __("Your <strong>TranslatePress</strong> license has expired. <br/>Please %1$sRenew Your Licence%2$s to continue receiving access to automatic translations via TranslatePress AI, premium addons, product downloads, and automatic updates. %3$sRenew now %4$s", "translatepress-multilingual"); ?> <?php __("License key mismatch. The license you entered doesn’t match the <strong>%1$s</strong> version you have installed. <br/>Please check that you’ve installed the correct version for your license from your %2$sTranslatePress account%3$s.", "translatepress-multilingual"); ?> <?php __("<br/>If you have only the free plugin installed but added a paid license, please install the paid plugin from your TranslatePress account.", "translatepress-multilingual"); ?> <?php __("You have reached the activation limit for your <strong>%1$s</strong> license. <br/>Manage your active sites from %2$s your account %3$s.", "translatepress-multilingual"); ?> <?php __("Your license is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-deactivated\">https://translatepress.com/account</a> -> Manage Sites.", "translatepress-multilingual"); ?> <?php __("%1$s automatic translation requires an active license. Please %2$srenew%3$s your license or purchase a new one %4$shere%5$s.", "translatepress-multilingual"); ?> <?php __("Please %1$senter%2$s your license key to enable %3$s automatic translation.", "translatepress-multilingual"); ?> <?php __("You have less than 5,000 TranslatePress AI words remaining. To continue automatically translating your website, please %spurchase additional AI words at a discount from your account%s.", "translatepress-multilingual"); ?> <?php __(" Error: ", "translatepress-multilingual"); ?> <?php __("This website is already activated under a free license. Each website can only use one free license. Please upgrade to a premium plan for more TranslatePress AI words from %1$s your account %2$s.", "translatepress-multilingual"); ?> <?php __("You do not have a valid license for <strong>TranslatePress</strong>. %1$sGet one for free%2$s to get access to TranslatePress AI.", "translatepress-multilingual"); ?> <?php __("The daily quota for machine translation characters exceeded. Please check the <strong>TranslatePress -> <a href=\"%s\">Automatic Translation</a></strong> page for more information.", "translatepress-multilingual"); ?> <?php __("Marketing optin", "translatepress-multilingual"); ?> <?php __("Opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking.", "translatepress-multilingual"); ?> <?php __("TranslatePress Preferred User Language", "translatepress-multilingual"); ?> <?php __("Preferred language to navigate the site", "translatepress-multilingual"); ?> <?php __("The language is automatically set based by the last visited language by the user.", "translatepress-multilingual"); ?> <?php __("Always use this language", "translatepress-multilingual"); ?> <?php __("By checking this setting the preferred language will remain the one selected above, without the possibility of being changed in the frontend.<br>This language will be used in different operations such as sending email to the user.", "translatepress-multilingual"); ?> <?php __("Hello! Seems like you've been using <strong>TranslatePress</strong> for a while now to translate your website. That's awesome! ", "translatepress-multilingual"); ?> <?php __("If you can spare a few moments to rate it on WordPress.org it would help us a lot (and boost my motivation).", "translatepress-multilingual"); ?> <?php __("~ Razvan, developer of TranslatePress", "translatepress-multilingual"); ?> <?php __("Rate TranslatePress on WordPress.org plugin page", "translatepress-multilingual"); ?> <?php __("Ok, I will gladly help!", "translatepress-multilingual"); ?> <?php __("No, thanks.", "translatepress-multilingual"); ?> <?php __("Full Language Names", "translatepress-multilingual"); ?> <?php __("Short Language Names", "translatepress-multilingual"); ?> <?php __("Flags with Full Language Names", "translatepress-multilingual"); ?> <?php __("Flags with Short Language Names", "translatepress-multilingual"); ?> <?php __("Only Flags", "translatepress-multilingual"); ?> <?php __("Full Language Names No HTML", "translatepress-multilingual"); ?> <?php __("Bottom Right", "translatepress-multilingual"); ?> <?php __("Bottom Left", "translatepress-multilingual"); ?> <?php __("Top Right", "translatepress-multilingual"); ?> <?php __("Top Left", "translatepress-multilingual"); ?> <?php __("Dark", "translatepress-multilingual"); ?> <?php __("Light", "translatepress-multilingual"); ?> <?php __("Deactivate", "translatepress-multilingual"); ?> <?php __("Activate", "translatepress-multilingual"); ?> <?php __("Invalid language code. Please try again.", "translatepress-multilingual"); ?> <?php __("Language codes can contain only A-Z a-z 0-9 - _ characters. Check your language codes in TranslatePress General Settings.", "translatepress-multilingual"); ?> <?php __("Error! Duplicate URL slug values.", "translatepress-multilingual"); ?> <?php __("You cannot select two languages that have the same <a href=\"https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes\" target=\"_blank\">iso code</a> but different formalities because doing so will lead to duplicate <a href=\"https://developers.google.com/search/docs/specialty/international/localized-versions\" target=\"_blank\">hreflang tags</a>.", "translatepress-multilingual"); ?> <?php __("Duplicate language detected.<br>Each language can only be added once to ensure accurate translation management.<br> Please change the duplicate language entry and try again. ", "translatepress-multilingual"); ?> <?php __("Current Language", "translatepress-multilingual"); ?> <?php __("Opposite Language", "translatepress-multilingual"); ?> <?php __("General", "translatepress-multilingual"); ?> <?php __("Translate Site", "translatepress-multilingual"); ?> <?php __("Addons", "translatepress-multilingual"); ?> <?php __("License", "translatepress-multilingual"); ?> <?php __("Settings", "translatepress-multilingual"); ?> <?php __("Activate License", "translatepress-multilingual"); ?> <?php __("Pro Features", "translatepress-multilingual"); ?> <?php __("Need Help?", "translatepress-multilingual"); ?> <?php __("Recent community discussions", "translatepress-multilingual"); ?> <?php __("Loading...", "translatepress-multilingual"); ?> <?php __("Unable to load forum posts", "translatepress-multilingual"); ?> <?php __("Ask a Question", "translatepress-multilingual"); ?> <?php __("View All Topics", "translatepress-multilingual"); ?> <?php __("by", "translatepress-multilingual"); ?> <?php __("Have a question?", "translatepress-multilingual"); ?> <?php __("Get help directly from the plugin developers, suggest improvements, or share your feedback!", "translatepress-multilingual"); ?> <?php __("Tip for faster help:", "translatepress-multilingual"); ?> <?php __("Include what you tried, what you expected, and what happened. Screenshots help!", "translatepress-multilingual"); ?> <?php __("Toggle support chat", "translatepress-multilingual"); ?> <?php __("Ask the community", "translatepress-multilingual"); ?> <?php __("Close", "translatepress-multilingual"); ?> <?php __("Empty feed response", "translatepress-multilingual"); ?> <?php __("Unable to parse feed", "translatepress-multilingual"); ?> <?php __("Just now", "translatepress-multilingual"); ?> <?php _n("%d hour ago", "%d hours ago", 1, "translatepress-multilingual"); ?> <?php _n("%d day ago", "%d days ago", 1, "translatepress-multilingual"); ?> <?php __("Source", "translatepress-multilingual"); ?> <?php __("Srcset", "translatepress-multilingual"); ?> <?php __("Alt attribute", "translatepress-multilingual"); ?> <?php __("Title attribute", "translatepress-multilingual"); ?> <?php __("Anchor link", "translatepress-multilingual"); ?> <?php __("Placeholder attribute", "translatepress-multilingual"); ?> <?php __("Submit attribute", "translatepress-multilingual"); ?> <?php __("Text", "translatepress-multilingual"); ?> <?php __("Video Poster", "translatepress-multilingual"); ?> <?php __("plural form", "translatepress-multilingual"); ?> <?php __("one", "translatepress-multilingual"); ?> <?php __("few", "translatepress-multilingual"); ?> <?php __("many", "translatepress-multilingual"); ?> <?php __("other", "translatepress-multilingual"); ?> <?php __("Saved", "translatepress-multilingual"); ?> <?php __("Save", "translatepress-multilingual"); ?> <?php __("Saving translation...", "translatepress-multilingual"); ?> <?php __("You have unsaved changes!", "translatepress-multilingual"); ?> <?php __("Discard changes", "translatepress-multilingual"); ?> <?php __("Discard All", "translatepress-multilingual"); ?> <?php __("Loading Strings...", "translatepress-multilingual"); ?> <?php __("Select string to translate...", "translatepress-multilingual"); ?> <?php __("Close Editor", "translatepress-multilingual"); ?> <?php __("From", "translatepress-multilingual"); ?> <?php __("To", "translatepress-multilingual"); ?> <?php __("Add Media", "translatepress-multilingual"); ?> <?php __("Other languages", "translatepress-multilingual"); ?> <?php __("Context", "translatepress-multilingual"); ?> <?php __("View Website As", "translatepress-multilingual"); ?> <?php __("Available in our Pro Versions", "translatepress-multilingual"); ?> <?php __("Select or Upload Media", "translatepress-multilingual"); ?> <?php __("Use this media", "translatepress-multilingual"); ?> <?php __("Translate", "translatepress-multilingual"); ?> <?php __("Translate entire block element", "translatepress-multilingual"); ?> <?php __("Split block to translate strings individually", "translatepress-multilingual"); ?> <?php __("Save changes to translation. Shortcut: CTRL(⌘) + S", "translatepress-multilingual"); ?> <?php __("Navigate to next string in dropdown list. Shortcut: CTRL(⌘) + ALT + Right Arrow", "translatepress-multilingual"); ?> <?php __("Navigate to previous string in dropdown list. Shortcut: CTRL(⌘) + ALT + Left Arrow", "translatepress-multilingual"); ?> <?php __("Discard all changes. Shortcut: CTRL(⌘) + ALT + Z", "translatepress-multilingual"); ?> <?php __("Discard changes to this text box. To discard changes to all text boxes use shortcut: CTRL(⌘) + ALT + Z", "translatepress-multilingual"); ?> <?php __("Dismiss tooltip", "translatepress-multilingual"); ?> <?php __("Quick Intro", "translatepress-multilingual"); ?> <?php __("Are you sure you want to split this phrase into smaller parts?", "translatepress-multilingual"); ?> <?php __("This string is not ready for translation yet. <br>Try again in a moment...", "translatepress-multilingual"); ?> <?php __("For this option to work, please update the Browse as other role add-on to the latest version.", "translatepress-multilingual"); ?> <?php __("To translate slugs, please update the SEO Pack add-on to the latest version.", "translatepress-multilingual"); ?> <?php __("You can add a new language from <a href=\"%s\">Settings->TranslatePress</a>", "translatepress-multilingual"); ?> <?php __("However, you can still use TranslatePress to <strong style=\"background: #f5fb9d;\">modify gettext strings</strong> available in your page.", "translatepress-multilingual"); ?> <?php __("Strings that are user-created cannot be modified, only those from themes and plugins.", "translatepress-multilingual"); ?> <?php __("Extra Translation Features", "translatepress-multilingual"); ?> <?php __("Support for 130+ Extra Languages", "translatepress-multilingual"); ?> <?php __("Access to TranslatePress AI", "translatepress-multilingual"); ?> <?php __("Translate SEO Title, Description, Slug", "translatepress-multilingual"); ?> <?php __("Publish only when translation is complete", "translatepress-multilingual"); ?> <?php __("Translate by Browsing as User Role", "translatepress-multilingual"); ?> <?php __("Different Menu Items for each Language", "translatepress-multilingual"); ?> <?php __("Automatic User Language Detection", "translatepress-multilingual"); ?> <?php __("Upgrade to PRO", "translatepress-multilingual"); ?> <?php __("Upgrade to PRO with our biggest discount of the year!", "translatepress-multilingual"); ?> <?php __("This Black Friday, get access to these features and more at a fraction of the costs:", "translatepress-multilingual"); ?> <?php __("No available suggestions", "translatepress-multilingual"); ?> <?php __("Suggestions from translation memory", "translatepress-multilingual"); ?> <?php __("Click to Copy", "translatepress-multilingual"); ?> <?php __("Human Translation", "translatepress-multilingual"); ?> <?php __("Machine Translation", "translatepress-multilingual"); ?> <?php __("Text on this page is %s% translated into all languages.", "translatepress-multilingual"); ?> <?php __("%1$s% of text on this page is translated into %2$s.", "translatepress-multilingual"); ?> <?php __("This page is %1$s% translated into %2$s.", "translatepress-multilingual"); ?> <?php __("The slug that you are trying to edit is present in other slug types:%s%. Editing it will replace each occurrence, regardless of the current type.", "translatepress-multilingual"); ?> <?php __("Hover any text on the page, click %s,<br> then modify the translation in the sidebar.", "translatepress-multilingual"); ?> <?php __("Don't forget to Save Translation. Use keyboard shortcut CTRL(⌘) + S", "translatepress-multilingual"); ?> <?php __("Switch language to see the translation changes directly on the page.", "translatepress-multilingual"); ?> <?php __("Search for any text in this page in the dropdown.", "translatepress-multilingual"); ?> <?php __("Your %s license has <span class=\"trp-license-status-emphasized\">expired</span>.", "translatepress-multilingual"); ?> <?php __("Please renew your license to continue receiving access to TranslatePress AI, premium addons, automatic updates and support.", "translatepress-multilingual"); ?> <?php __("Renew Now", "translatepress-multilingual"); ?> <?php __("<strong>This Black Friday, renew your license at a special price</strong> to continue receiving access to product downloads, automatic updates, and support.", "translatepress-multilingual"); ?> <?php __("Get Deal", "translatepress-multilingual"); ?> <?php __("Your %s license was <span class=\"trp-license-status-emphasized\">refunded</span>.", "translatepress-multilingual"); ?> <?php __("Please purchase a new license to continue receiving access to TranslatePress AI, premium addons, automatic updates and support.", "translatepress-multilingual"); ?> <?php __("Purchase a new license", "translatepress-multilingual"); ?> <?php __("Your %s license is <span class=\"trp-license-status-emphasized\">missing or invalid</span>.", "translatepress-multilingual"); ?> <?php __("Please enter a valid license to get access to TranslatePress AI, premium addons, automatic updates and support. Need a license key? %1$sPurchase one now%2$s", "translatepress-multilingual"); ?> <?php __("Enter a valid license", "translatepress-multilingual"); ?> <?php __("Slugs", "translatepress-multilingual"); ?> <?php __("Meta Information", "translatepress-multilingual"); ?> <?php __("String List", "translatepress-multilingual"); ?> <?php __("Gettext Strings", "translatepress-multilingual"); ?> <?php __("Images", "translatepress-multilingual"); ?> <?php __("Videos", "translatepress-multilingual"); ?> <?php __("Audios", "translatepress-multilingual"); ?> <?php __("Dynamically Added Strings", "translatepress-multilingual"); ?> <?php __("Translation Editor", "translatepress-multilingual"); ?> <?php __("Edit translations by visually selecting them on each site page", "translatepress-multilingual"); ?> <?php __("String Translation", "translatepress-multilingual"); ?> <?php __("Edit url slug translations, plugins and theme translation (emails, forms etc.)", "translatepress-multilingual"); ?> <?php __("Current User", "translatepress-multilingual"); ?> <?php __("Logged Out", "translatepress-multilingual"); ?> <?php __("Translate Page", "translatepress-multilingual"); ?> <?php __("Add a New Language", "translatepress-multilingual"); ?> <?php __("Get a Free AI License", "translatepress-multilingual"); ?> <?php __("Your License is Invalid", "translatepress-multilingual"); ?> <?php __("Get More AI Words (%d left)", "translatepress-multilingual"); ?> <?php __("Opens post in the translation editor. Post must be saved as draft or published beforehand.", "translatepress-multilingual"); ?> <?php __("Security check", "translatepress-multilingual"); ?> <?php __("<strong>Warning:</strong> Some strings have possibly incorrectly encoded characters. This may result in breaking the queries, rendering the page untranslated in live mode. Consider revising the following strings or their method of outputting.", "translatepress-multilingual"); ?> <?php __("TranslatePress data update", "translatepress-multilingual"); ?> <?php __("We need to update your translations database to the latest version.", "translatepress-multilingual"); ?> <?php __("Updating will allow editing translations of localized text from plugins and theme. Existing translation will still work as expected.", "translatepress-multilingual"); ?> <?php __("IMPORTANT: It is strongly recommended to first backup the database!\nAre you sure you want to continue?", "translatepress-multilingual"); ?> <?php __("Run the updater", "translatepress-multilingual"); ?> <?php __("Updating will allow editing translations of slugs. Existing translation will still work as expected.", "translatepress-multilingual"); ?> <?php __("Please activate the SEO Addon from <br/>WordPress -> Settings -> TranslatePress -> Addons section", "translatepress-multilingual"); ?> <?php __("Go to Addons", "translatepress-multilingual"); ?> <?php __("The SEO Pack add-on allows translation of all the URL slugs:", "translatepress-multilingual"); ?> <?php __("Taxonomy slugs", "translatepress-multilingual"); ?> <?php __("Term slugs", "translatepress-multilingual"); ?> <?php __("Post slugs (this includes pages and custom post types)", "translatepress-multilingual"); ?> <?php __("Post type base slugs", "translatepress-multilingual"); ?> <?php __("WooCommerce slugs", "translatepress-multilingual"); ?> <?php __("The SEO Pack add-on is available with ALL premium versions of the plugin.", "translatepress-multilingual"); ?> <?php __("Upgrade to Pro", "translatepress-multilingual"); ?> <?php __("Description", "translatepress-multilingual"); ?> <?php __("Article Section", "translatepress-multilingual"); ?> <?php __("Article Tag", "translatepress-multilingual"); ?> <?php __("OG Title", "translatepress-multilingual"); ?> <?php __("OG Site Name", "translatepress-multilingual"); ?> <?php __("OG Description", "translatepress-multilingual"); ?> <?php __("OG Image Alt", "translatepress-multilingual"); ?> <?php __("Twitter Title", "translatepress-multilingual"); ?> <?php __("Twitter Description", "translatepress-multilingual"); ?> <?php __("Twitter Image Alt", "translatepress-multilingual"); ?> <?php __("Page Title", "translatepress-multilingual"); ?> <?php __("Dublin Core Title", "translatepress-multilingual"); ?> <?php __("Dublin Core Description", "translatepress-multilingual"); ?> <?php __("OG Image", "translatepress-multilingual"); ?> <?php __("OG Image Secure URL", "translatepress-multilingual"); ?> <?php __("Twitter Image", "translatepress-multilingual"); ?> <?php __("Removing cdata dictionary strings for language %s...", "translatepress-multilingual"); ?> <?php __("Removing untranslated dictionary links for language %s...", "translatepress-multilingual"); ?> <?php __("Removing duplicated gettext strings for language %s...", "translatepress-multilingual"); ?> <?php __("Removing duplicated dictionary strings for language %s...", "translatepress-multilingual"); ?> <?php __("Removing untranslated dictionary strings where translation is available for language %s...", "translatepress-multilingual"); ?> <?php __("Inserting original strings for language %s...", "translatepress-multilingual"); ?> <?php __("Cleaning original strings table for language %s...", "translatepress-multilingual"); ?> <?php __("Updating original string ids for language %s...", "translatepress-multilingual"); ?> <?php __("Regenerating original meta table for language %s...", "translatepress-multilingual"); ?> <?php __("Cleaning original meta table for language %s...", "translatepress-multilingual"); ?> <?php __("Replacing original id NULL with value for language %s...", "translatepress-multilingual"); ?> <?php __("Inserting gettext original strings for language %s...", "translatepress-multilingual"); ?> <?php __("Cleaning gettext original strings table for language %s...", "translatepress-multilingual"); ?> <?php __("Updating gettext original string ids for language %s...", "translatepress-multilingual"); ?> <?php __("Migrating taxonomy and post type base slugs to new table structure...", "translatepress-multilingual"); ?> <?php __("Migrating post slugs to new table structure for language %s...", "translatepress-multilingual"); ?> <?php __("Migrating term slugs to new table structure for language %s...", "translatepress-multilingual"); ?> <?php __("Finishing up...", "translatepress-multilingual"); ?> <?php __("Database optimization did not complete successfully. We recommend restoring the original database or <a href=\"%s\" >trying again.</a>", "translatepress-multilingual"); ?> <?php __("Update aborted! Your user account doesn't have the capability to perform database updates.", "translatepress-multilingual"); ?> <?php __("Update aborted! Invalid nonce.", "translatepress-multilingual"); ?> <?php __("Update aborted! Incorrect action.", "translatepress-multilingual"); ?> <?php __("Update aborted! Incorrect language code.", "translatepress-multilingual"); ?> <?php __("Updating database to version %s+", "translatepress-multilingual"); ?> <?php __("Processing table for language %s...", "translatepress-multilingual"); ?> <?php __("Back to TranslatePress Settings", "translatepress-multilingual"); ?> <?php __("Successfully updated database!", "translatepress-multilingual"); ?> <?php __(" done.", "translatepress-multilingual"); ?> <?php __("All individual TranslatePress add-on plugins <a href=\"%1$s\" target=\"_blank\">have been discontinued</a> and are now included in the premium Personal, Business and Developer versions of TranslatePress. Please log into your <a href=\"%2$s\" target=\"_blank\">account page</a>, download the new premium version and install it. Your individual addons settings will be ported over.", "translatepress-multilingual"); ?> <?php __("Brand-new Language Switcher Settings are here!", "translatepress-multilingual"); ?> <?php __("Explore pre-made templates, switch colors, flag styles, spacing, layouts & more. Use the live preview to perfect your switcher in seconds.", "translatepress-multilingual"); ?> <?php __("Start customizing", "translatepress-multilingual"); ?> <?php __("Read documentation", "translatepress-multilingual"); ?> <?php __("TranslatePress", "translatepress-multilingual"); ?> <?php __("<strong>TranslatePress</strong> requires <strong><a href=\"http://php.net/manual/en/book.mbstring.php\">Multibyte String PHP library</a></strong>. Please contact your server administrator to install it on your server.", "translatepress-multilingual"); ?> <?php __("Detected long query limitation on WPEngine hosting. Some large pages may appear untranslated. You can remove limitation by adding the following to your site’s wp-config.php: define( 'WPE_GOVERNOR', false ); ", "translatepress-multilingual"); ?> <?php __("Custom Language Flag", "translatepress-multilingual"); ?> <?php __("The Language code of the added custom language cannot be empty.", "translatepress-multilingual"); ?> <?php __("The Language code of the added custom language is invalid.", "translatepress-multilingual"); ?> <?php __("The Automatic Translation Code of the added custom language is invalid.", "translatepress-multilingual"); ?> <?php __("TranslatePress Add-ons", "translatepress-multilingual"); ?> <?php __("You must first purchase this version to have access to the addon %1$shere%2$s", "translatepress-multilingual"); ?> <?php __("Please %1$senter your license%2$s key first, to activate this addon.", "translatepress-multilingual"); ?> <?php __("You need an active license to have access to the addon. Renew or purchase a new one %1$shere%2$s.", "translatepress-multilingual"); ?> <?php __("Advanced Add-ons", "translatepress-multilingual"); ?> <?php __("These addons extend your translation plugin and are available in the Developer, Business and Personal plans.", "translatepress-multilingual"); ?> <?php __("SEO Pack", "translatepress-multilingual"); ?> <?php __("SEO Pack (Legacy)", "translatepress-multilingual"); ?> <?php __("SEO support for page slug, page title, description and facebook and twitter social graph information. The HTML lang attribute is properly set.", "translatepress-multilingual"); ?> <?php __("Multiple Languages", "translatepress-multilingual"); ?> <?php __("Add as many languages as you need for your project to go global. Publish your language only when all your translations are done.", "translatepress-multilingual"); ?> <?php __("Pro Add-ons", "translatepress-multilingual"); ?> <?php __("These addons extend your translation plugin and are available in the Business and Developer plans.", "translatepress-multilingual"); ?> <?php __("DeepL Automatic Translation", "translatepress-multilingual"); ?> <?php __("Automatically translate your website through the DeepL API.", "translatepress-multilingual"); ?> <?php __("Prompts visitors to switch to their preferred language based on their browser settings or IP address and remembers the last visited language.", "translatepress-multilingual"); ?> <?php __("Translator Accounts", "translatepress-multilingual"); ?> <?php __("Create translator accounts for new users or allow existing users that are not administrators to translate your website.", "translatepress-multilingual"); ?> <?php __("Browse As User Role", "translatepress-multilingual"); ?> <?php __("Navigate your website just like a particular user role would. Really useful for dynamic content or hidden content that appears for particular users.", "translatepress-multilingual"); ?> <?php __("Navigation Based on Language", "translatepress-multilingual"); ?> <?php __("Configure different menu items for different languages.", "translatepress-multilingual"); ?> <?php __("Different Domain per Language", "translatepress-multilingual"); ?> <?php __("Connect separate domains or subdomains to each of your translated versions. Strengthen your brand’s local identity and boost SEO performance for every language you support.", "translatepress-multilingual"); ?> <?php __("Recommended Plugins", "translatepress-multilingual"); ?> <?php __("A short list of plugins you can use to extend your website.", "translatepress-multilingual"); ?> <?php __("Profile Builder", "translatepress-multilingual"); ?> <?php __("Capture more user information on the registration form with the help of Profile Builder's custom user profile fields and/or add an Email Confirmation process to verify your customers accounts.", "translatepress-multilingual"); ?> <?php __("Paid Member Subscriptions", "translatepress-multilingual"); ?> <?php __("Accept user payments, create subscription plans and restrict content on your membership site.", "translatepress-multilingual"); ?> <?php __("WP Webhooks Automator", "translatepress-multilingual"); ?> <?php __("Create no-code automations and workflows on your WordPress site. Easily connect your plugins, sites and apps together.", "translatepress-multilingual"); ?> <?php __("Save Changes", "translatepress-multilingual"); ?> <?php __("TranslatePress Errors", "translatepress-multilingual"); ?> <?php __("There are no logged errors.", "translatepress-multilingual"); ?> <?php __("Change language", "translatepress-multilingual"); ?> <?php __("Website language selector", "translatepress-multilingual"); ?> <?php __("WordPress Translation Plugin", "translatepress-multilingual"); ?> <?php __("Available languages", "translatepress-multilingual"); ?> <?php __("Language Switcher update notice", "translatepress-multilingual"); ?> <?php __("Legacy language switcher is currently enabled", "translatepress-multilingual"); ?> <?php __("We’ve upgraded the switcher for richer customization and a better user experience.<br>In order to use the new configurator, turn off <strong>Load legacy language switcher</strong>.", "translatepress-multilingual"); ?> <?php __("Note: You can switch back anytime from <strong>Advanced Settings → <a href=\"%s\">Troubleshooting</a></strong>.", "translatepress-multilingual"); ?> <?php __("Enable the new switcher", "translatepress-multilingual"); ?> <?php __("Your License Key is valid.", "translatepress-multilingual"); ?> <?php __("Your License Key is invalid.", "translatepress-multilingual"); ?> <?php __("Your License has expired.", "translatepress-multilingual"); ?> <?php __("Deactivate License", "translatepress-multilingual"); ?> <?php __("Add a license key", "translatepress-multilingual"); ?> <?php __("License Key", "translatepress-multilingual"); ?> <?php __("Manage your license in your %1$s.", "translatepress-multilingual"); ?> <?php __("Account Page", "translatepress-multilingual"); ?> <?php __("Don’t have a TranslatePress AI License Key?", "translatepress-multilingual"); ?> <?php __("You can get one for %1$sfree%2$s, by creating a free account. It includes:", "translatepress-multilingual"); ?> <?php __("Access to TranslatePress AI for instant automatic translations", "translatepress-multilingual"); ?> <?php __("2000 AI words to translate automatically", "translatepress-multilingual"); ?> <?php __("Get a free License Today", "translatepress-multilingual"); ?> <?php __("Debug Information", "translatepress-multilingual"); ?> <?php __("Debug Data for License Checking", "translatepress-multilingual"); ?> <?php __("Debug Data for License Activation", "translatepress-multilingual"); ?> <?php __("Get more AI words and unlock all features with TranslatePress Pro.", "translatepress-multilingual"); ?> <?php __("Upgrade now ↗", "translatepress-multilingual"); ?> <?php __("Already purchased a Premium version?", "translatepress-multilingual"); ?> <?php __("Go to your %1$s", "translatepress-multilingual"); ?> <?php __("TranslatePress.com Account", "translatepress-multilingual"); ?> <?php __("Download & Install the Pro plugin", "translatepress-multilingual"); ?> <?php __("Learn More", "translatepress-multilingual"); ?> <?php __("Enable Automatic Translation", "translatepress-multilingual"); ?> <?php __("To use <strong>DeepL</strong> for automatic translation, activate this Pro add-on from the <a href=\"%1$s\" target=\"_self\" title=\"%2$s\">%2$s</a>.", "translatepress-multilingual"); ?> <?php __("<strong>DeepL</strong> automatic translation is available as a <a href=\"%1$s\" target=\"_blank\" title=\"%2$s\">%2$s</a>.", "translatepress-multilingual"); ?> <?php __("By upgrading you'll get access to all paid add-ons, premium support and help fund the future development of TranslatePress.", "translatepress-multilingual"); ?> <?php __("TranslatePress Pro Add-on", "translatepress-multilingual"); ?> <?php __("Addons tab", "translatepress-multilingual"); ?> <?php __("Please note that DeepL API usage is paid separately. See <a href=\"https://www.deepl.com/pro.html#developer\">DeepL pricing information</a>.", "translatepress-multilingual"); ?> <?php __("TranslatePress Pro Add-ons", "translatepress-multilingual"); ?> <?php __("Test API credentials", "translatepress-multilingual"); ?> <?php __("Check if the selected translation engine is configured correctly.", "translatepress-multilingual"); ?> <?php __("Alternative Engines", "translatepress-multilingual"); ?> <?php __("Switch to TranslatePress AI", "translatepress-multilingual"); ?> <?php __("Integrate machine translation directly with your WordPress website.", "translatepress-multilingual"); ?> <?php __("More info", "translatepress-multilingual"); ?> <?php __("Choose which engine you want to use in order to %1$s automatically translate your website.", "translatepress-multilingual"); ?> <?php __("Automatic Translation Settings", "translatepress-multilingual"); ?> <?php __("Automatically Translate Slugs", "translatepress-multilingual"); ?> <?php __("Generate automatic translations of slugs for posts, pages and Custom Post Types.<br/>The slugs will be automatically translated starting with the second refresh of each page.", "translatepress-multilingual"); ?> <?php __("This feature is only available in the paid version. Upgrade TranslatePress and unlock more premium features.", "translatepress-multilingual"); ?> <?php __("Requires <a href=\"%s\" title=\"TranslatePress Add-on SEO Pack documentation\" target=\"_blank\">SEO Pack Add-on</a> to be installed and activated.", "translatepress-multilingual"); ?> <?php __("Upgrade now", "translatepress-multilingual"); ?> <?php __("Block Crawlers", "translatepress-multilingual"); ?> <?php __("Block crawlers from triggering automatic translations on your website.<br>This will not prevent crawlers from accessing this site's pages.", "translatepress-multilingual"); ?> <?php __("Limit machine translation / characters per day", "translatepress-multilingual"); ?> <?php __("Add a limit to the number of automatically translated characters so you can better budget your project.", "translatepress-multilingual"); ?> <?php __("characters per day", "translatepress-multilingual"); ?> <?php __("Today's Character Count: ", "translatepress-multilingual"); ?> <?php __("Log machine translation queries.", "translatepress-multilingual"); ?> <?php __("Only enable for testing purposes. Can impact performance.<br>All records are stored in the wp_trp_machine_translation_log database table. Use a plugin like <a href=\"https://wordpress.org/plugins/wp-data-access/\" target=\"_blank\">WP Data Access</a> to browse the logs or directly from your database manager (PHPMyAdmin, etc.)", "translatepress-multilingual"); ?> <?php __("Test API Credentials", "translatepress-multilingual"); ?> <?php __("HTTP Referrer: ", "translatepress-multilingual"); ?> <?php __("Use this HTTP Referrer if the API lets you restrict key usage from its Dashboard.", "translatepress-multilingual"); ?> <?php __("Response", "translatepress-multilingual"); ?> <?php __("Response Body", "translatepress-multilingual"); ?> <?php __("Entire Response From wp_remote_get():", "translatepress-multilingual"); ?> <?php __("All Languages", "translatepress-multilingual"); ?> <?php __("Select the languages you wish to make your website available in.", "translatepress-multilingual"); ?> <?php __("Formality", "translatepress-multilingual"); ?> <?php __("Code", "translatepress-multilingual"); ?> <?php __("Slug", "translatepress-multilingual"); ?> <?php __("Default", "translatepress-multilingual"); ?> <?php __("Formal", "translatepress-multilingual"); ?> <?php __("Informal", "translatepress-multilingual"); ?> <?php __("Language", "translatepress-multilingual"); ?> <?php __("This language does not support formality. ", "translatepress-multilingual"); ?> <?php __("Are you sure you want to remove this language?", "translatepress-multilingual"); ?> <?php __("Custom Languages", "translatepress-multilingual"); ?> <?php __("To Add more languages activate the Multiple Languages Addon", "translatepress-multilingual"); ?> <?php __("You need an active license to add more languages. Verify in your %1$saccount%2$s that your license is valid", "translatepress-multilingual"); ?> <?php __("Please %1$senter your license%2$s key first to add more languages.", "translatepress-multilingual"); ?> <?php __("Adding more than two languages is a paid feature. Upgrade TranslatePress and unlock more premium features.", "translatepress-multilingual"); ?> <?php __("Website Languages", "translatepress-multilingual"); ?> <?php __("Default Language", "translatepress-multilingual"); ?> <?php __("Select the language your content is written in.", "translatepress-multilingual"); ?> <?php __("WARNING. Changing the default language will invalidate existing translations.", "translatepress-multilingual"); ?> <?php __("Even changing from en_US to en_GB, because they are treated as two different languages.", "translatepress-multilingual"); ?> <?php __("In most cases changing the default flag is all it is needed: ", "translatepress-multilingual"); ?> <?php __("replace the default flag", "translatepress-multilingual"); ?> <?php __("Re-run Setup Wizard", "translatepress-multilingual"); ?> <?php __("The Setup wizard allows you to quickly setup TranslatePress. You can initiate it at any time.", "translatepress-multilingual"); ?> <?php __("Language Settings", "translatepress-multilingual"); ?> <?php __("Use Native language name", "translatepress-multilingual"); ?> <?php __("Check if you want to display languages in their native names. Otherwise, languages will be displayed in English.", "translatepress-multilingual"); ?> <?php __("Use a subdirectory for the default language", "translatepress-multilingual"); ?> <?php __("Check if you want to add the subdirectory in the URL for the default language.</br>By checking this option, the default language seen by website visitors will become the first one in the \"All Languages\" list.", "translatepress-multilingual"); ?> <?php __("Force language in custom links", "translatepress-multilingual"); ?> <?php __("Select Yes if you want to force custom links without language encoding to keep the currently selected language.", "translatepress-multilingual"); ?> <?php __("Shortcode ", "translatepress-multilingual"); ?> <?php __("Use shortcode on any page or widget.", "translatepress-multilingual"); ?> <?php __("You can also add the <a href=\"%s\" title=\"Language Switcher Block Documentation\">Language Switcher Block</a> in the WP Gutenberg Editor.", "translatepress-multilingual"); ?> <?php __("Menu item", "translatepress-multilingual"); ?> <?php __("Go to %1$s Appearance -> Menus%2$s to add languages to the Language Switcher in any menu.", "translatepress-multilingual"); ?> <?php __("Learn more in our documentation.", "translatepress-multilingual"); ?> <?php __("Floating language selection", "translatepress-multilingual"); ?> <?php __("Add a floating dropdown that follows the user on every page.", "translatepress-multilingual"); ?> <?php __("Show \"Powered by TranslatePress\"", "translatepress-multilingual"); ?> <?php __("Show the small \"Powered by TranslatePress\" label in the floater language switcher.", "translatepress-multilingual"); ?> <?php __("5 Days to Better Multilingual Websites", "translatepress-multilingual"); ?> <?php __("%sJoin our FREE & EXCLUSIVE onboarding course%s and learn how to grow your multilingual traffic, reach international markets, and save time & money while getting the most out of TranslatePress!", "translatepress-multilingual"); ?> <?php __("Invalid email address", "translatepress-multilingual"); ?> <?php __("Your email", "translatepress-multilingual"); ?> <?php __("Sign me up!", "translatepress-multilingual"); ?> <?php __("Sign up with your email address and receive a 5-part email guide to help you maximize the power of TranslatePress.", "translatepress-multilingual"); ?> <?php __("Dismiss email course notification", "translatepress-multilingual"); ?> <?php __("Hey %s,<br>Never miss an important update - opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking.", "translatepress-multilingual"); ?> <?php __("Allow & Continue", "translatepress-multilingual"); ?> <?php __("Skip", "translatepress-multilingual"); ?> <?php __("This will allow TranslatePress to:", "translatepress-multilingual"); ?> <?php __("Your profile overview", "translatepress-multilingual"); ?> <?php __("Name and email address", "translatepress-multilingual"); ?> <?php __("Admin Notices", "translatepress-multilingual"); ?> <?php __("Updates, announcements, marketing, no spam", "translatepress-multilingual"); ?> <?php __("Plugin status & settings", "translatepress-multilingual"); ?> <?php __("Active, Deactivated, installed version and settings", "translatepress-multilingual"); ?> <?php __("Privacy Policy", "translatepress-multilingual"); ?> <?php __("Terms of Service", "translatepress-multilingual"); ?> <?php __("Support", "translatepress-multilingual"); ?> <?php __("Documentation", "translatepress-multilingual"); ?> <?php __("Optimize TranslatePress database tables", "translatepress-multilingual"); ?> <?php __("<strong>IMPORTANT NOTE:</strong> Before performing this action it is strongly recommended to first backup the database.", "translatepress-multilingual"); ?> <?php __("IMPORTANT: It is strongly recommended to first backup the database!! Are you sure you want to continue?", "translatepress-multilingual"); ?> <?php __("Operations to perform", "translatepress-multilingual"); ?> <?php __("Remove CDATA for original and dictionary strings", "translatepress-multilingual"); ?> <?php __("Removes CDATA from trp_original_strings and trp_dictionary_* tables.<br>This type of content should not be detected by TranslatePress. It might have been introduced in the database in older versions of the plugin.", "translatepress-multilingual"); ?> <?php __("Remove untranslated links from dictionary tables", "translatepress-multilingual"); ?> <?php __("Removes untranslated links and images from all trp_dictionary_* tables. These tables contain translations for user-inputted strings such as post content, post title, menus etc.", "translatepress-multilingual"); ?> <?php __("Remove duplicate rows for gettext strings", "translatepress-multilingual"); ?> <?php __("Cleans up all trp_gettext_* tables of duplicate rows. These tables contain translations for themes and plugin strings.", "translatepress-multilingual"); ?> <?php __("Remove duplicate rows for dictionary strings", "translatepress-multilingual"); ?> <?php __("Cleans up all trp_dictionary_* tables of duplicate rows. These tables contain translations for user-inputted strings such as post content, post title, menus etc.", "translatepress-multilingual"); ?> <?php __("Remove duplicate rows for original dictionary strings", "translatepress-multilingual"); ?> <?php __("Cleans up all trp_original_strings table of duplicate rows. This table contains strings in the default language, without any translation.<br>The trp_original_meta table, which contains meta information that refers to the post parent’s ID, is also regenerated.<br>Such duplicates can appear in exceptional situations of unexpected behavior.", "translatepress-multilingual"); ?> <?php __("Replace gettext strings that have original ID NULL with the correct original IDs", "translatepress-multilingual"); ?> <?php __("Fixes an edge case issue where some gettext strings have the original ID incorrectly set to NULL, causing problems in the Translation Editor.<br>This operation corrects the original IDs in the trp_gettext_* tables.<br>Only check this option if you encountered an issue in the Translation Editor where clicking the green pencil did not bring up the gettext string for translation in the left sidebar.<br>Otherwise, please leave this option unchecked because it's an intensive operation.", "translatepress-multilingual"); ?> <?php __("Optimize Database", "translatepress-multilingual"); ?> <?php __("TranslatePress Database Updater", "translatepress-multilingual"); ?> <?php __("Updating TranslatePress tables. Please leave this window open.", "translatepress-multilingual"); ?> <?php __("The inactive languages will still be visible and active for the admin. For other users they won't be visible in the language switchers and won't be accessible either.", "translatepress-multilingual"); ?> <?php __("unknown", "translatepress-multilingual"); ?> <?php __("<strong>Extra Languages add-on</strong> requires TranslatePress version %1$s or higher. You are currently using version %2$s. Please update TranslatePress to enable this feature.", "translatepress-multilingual"); ?> <?php __("Automatic and manual slug translation changes performed when <strong>TranslatePress - Multilingual</strong> 2.8.4 was active had to be removed because of some issues with that version. All slug translations from before that version are now in use. Thank you for understanding!", "translatepress-multilingual"); ?> <?php __("If you absolutely need them, the removed translations can be found in tables trp_slug_original_obsolete and trp_slug_translation_obsolete.", "translatepress-multilingual"); ?> <?php __("View Docs", "translatepress-multilingual"); ?> <?php __("Assign different domains or subdomains to each language. When visitors access these domains, TranslatePress loads the appropriate language translation directly without redirecting.", "translatepress-multilingual"); ?> <?php __("Example: %1$s for English, %2$s for Spanish, %3$s for French.", "translatepress-multilingual"); ?> <?php __("Before enabling:", "translatepress-multilingual"); ?> <?php __("Ensure your domains are registered, pointed to your server, and have SSL certificates configured.", "translatepress-multilingual"); ?> <?php __("Domain", "translatepress-multilingual"); ?> <?php __("Map this language to a different domain or sub-domain.", "translatepress-multilingual"); ?> <?php __("https://example.com", "translatepress-multilingual"); ?> <?php __("Prefill with current domain", "translatepress-multilingual"); ?> <?php __("Check DNS", "translatepress-multilingual"); ?> <?php __("Checking DNS...", "translatepress-multilingual"); ?> <?php __("DNS is correctly configured!", "translatepress-multilingual"); ?> <?php __("DNS check failed. Please verify your domain configuration.", "translatepress-multilingual"); ?> <?php __("This domain is already assigned to another language.", "translatepress-multilingual"); ?> <?php __("<strong>Different Domain per Language add-on</strong> requires TranslatePress version %1$s or higher. You are currently using version %2$s. Please update TranslatePress to enable this feature.", "translatepress-multilingual"); ?> <?php __("Note: This option is disabled when Different Domain for Language addon is active.", "translatepress-multilingual"); ?> <?php __("Different Domain per Language: Domain is required when domain mapping is enabled. The toggle has been disabled for languages with empty domains.", "translatepress-multilingual"); ?> <?php __("Different Domain per Language: The same domain cannot be assigned to multiple languages. The toggle has been disabled for duplicate domains.", "translatepress-multilingual"); ?> <?php __("Different Domain per Language: A language domain cannot be the same as the main site URL. The toggle has been disabled for the matching domain.", "translatepress-multilingual"); ?> <?php __("Please enter a domain.", "translatepress-multilingual"); ?> <?php __("Invalid domain format.", "translatepress-multilingual"); ?> <?php __("Could not reach domain: %s", "translatepress-multilingual"); ?> <?php __("Domain returned HTTP status %d.", "translatepress-multilingual"); ?> <?php __("Domain is reachable!", "translatepress-multilingual"); ?> <?php __("Limit this menu item to the following languages", "translatepress-multilingual"); ?> <?php __("Date format", "translatepress-multilingual"); ?> <?php __("Customize the date formatting per each translated language.<br/>Leave empty for default WP setting or see more information <a href=\"https://wordpress.org/support/article/formatting-date-and-time/\" title=\"Formatting Date and Time\" target=\"_blank\">here</a>", "translatepress-multilingual"); ?> <?php __("To edit an existing TranslatePress language, input the language code and fill in only the columns you want to overwrite (e.g. Language name, Flag).<br>You can also add new custom languages. They will be available under General settings, All Languages list, where the URL slug can be edited.", "translatepress-multilingual"); ?> <?php __("For custom flag, first upload the image in media library then paste the URL.<br>Changing or deleting a custom language will impact translations and site URL's.<br>The Language code and the ISO Code should contain only alphabetical values, numerical values, \"-\" and \"_\".<br>The ISO Codes can be found on <a href = \"https://cloud.google.com/translate/docs/languages\" target = \"_blank\">Google ISO Codes</a> and <a href = \"https://www.deepl.com/docs-api/translating-text/\" target = \"_blank\">DeepL Target Codes</a>.", "translatepress-multilingual"); ?> <?php __("Language code", "translatepress-multilingual"); ?> <?php __("Language name", "translatepress-multilingual"); ?> <?php __("Native name", "translatepress-multilingual"); ?> <?php __("ISO Code", "translatepress-multilingual"); ?> <?php __("Flag URL", "translatepress-multilingual"); ?> <?php __("Text RTL", "translatepress-multilingual"); ?> <?php __("Custom language", "translatepress-multilingual"); ?> <?php __("Disable dynamic translation", "translatepress-multilingual"); ?> <?php __("It disables detection of strings displayed dynamically using JavaScript. <br/>Strings loaded via a server side AJAX call will still be translated.", "translatepress-multilingual"); ?> <?php __("Disable translation for gettext strings", "translatepress-multilingual"); ?> <?php __("Gettext Strings are strings outputted by themes and plugins. <br> Translating these types of strings through TranslatePress can be unnecessary if they are already translated using the .po/.mo translation file system.<br>Enabling this option can improve the page load performance of your site in certain cases. The disadvantage is that you can no longer edit gettext translations using TranslatePress, nor benefit from automatic translation on these strings.", "translatepress-multilingual"); ?> <?php __("Gettext Strings translation is disabled", "translatepress-multilingual"); ?> <?php __("To enable it go to ", "translatepress-multilingual"); ?> <?php __("TranslatePress->Advanced Settings->Debug->Disable translation for gettext strings", "translatepress-multilingual"); ?> <?php __(" and uncheck the Checkbox.", "translatepress-multilingual"); ?> <?php __("Dismiss", "translatepress-multilingual"); ?> <?php __("Exclude translated links from sitemap", "translatepress-multilingual"); ?> <?php __("Do not include translated links in sitemaps generated by SEO plugins.<br/>Requires <a href=\"https://translatepress.com/docs/addons/seo-pack/?utm_source=tp-advanced&utm_medium=client-site&utm_campaign=miscellaneous\" title=\"TranslatePress Add-on SEO Pack documentation\" target=\"_blank\"> SEO Pack Add-on</a> to be installed and activated.", "translatepress-multilingual"); ?> <?php __("Disable post container tags for post title", "translatepress-multilingual"); ?> <?php __("It disables search indexing the post title in translated languages.<br/>Useful when the title of the post doesn't allow HTML thus breaking the page.", "translatepress-multilingual"); ?> <?php __("Disable post container tags for post content", "translatepress-multilingual"); ?> <?php __("It disables search indexing the post content in translated languages.<br/>Useful when the content of the post doesn't allow HTML thus breaking the page.", "translatepress-multilingual"); ?> <?php __("Do not translate certain paths", "translatepress-multilingual"); ?> <?php __("Choose what paths can be translated. Supports wildcard at the end of the path.<br>For example, to exclude https://example.com/some/path you can either use the rule /some/path/ or /some/*.<br>Enter each rule on it's own line. To exclude the home page use {{home}}.", "translatepress-multilingual"); ?> <?php __("Exclude Paths From Translation", "translatepress-multilingual"); ?> <?php __("Translate Only Certain Paths", "translatepress-multilingual"); ?> <?php __("Enable the hreflang x-default tag for language:", "translatepress-multilingual"); ?> <?php __("Enables the hreflang=\"x-default\" for an entire language. See documentation for more details.", "translatepress-multilingual"); ?> <?php __("Translate numbers and numerals", "translatepress-multilingual"); ?> <?php __("Enable translation of numbers ( e.g. phone numbers)", "translatepress-multilingual"); ?> <?php __("Selector", "translatepress-multilingual"); ?> <?php __("Exclude from dynamic translation", "translatepress-multilingual"); ?> <?php __("Do not dynamically translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being translated using JavaScript.<br/>These strings will still be translated on the server side if possible.", "translatepress-multilingual"); ?> <?php __("Gettext String", "translatepress-multilingual"); ?> <?php __("Exclude Gettext Strings", "translatepress-multilingual"); ?> <?php __("Exclude these strings from being translated as Gettext strings by TranslatePress. Leave the domain empty to take into account any Gettext string.<br/>Can still be translated through po/mo files.", "translatepress-multilingual"); ?> <?php __("Exclude selectors only from automatic translation", "translatepress-multilingual"); ?> <?php __("Do not automatically translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being automatically translated.<br>Manual translation of these strings is still possible.", "translatepress-multilingual"); ?> <?php __("Exclude selectors from translation", "translatepress-multilingual"); ?> <?php __("Do not translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being translated.<br>These strings cannot be translated manually nor automatically.", "translatepress-multilingual"); ?> <?php __("String", "translatepress-multilingual"); ?> <?php __("Exclude strings from automatic translation", "translatepress-multilingual"); ?> <?php __("Do not automatically translate these strings (ex. names, technical words...)<br>Paragraphs containing these strings will still be translated except for the specified part.", "translatepress-multilingual"); ?> <?php __("Fix broken HTML", "translatepress-multilingual"); ?> <?php __("General attempt to fix broken or missing HTML on translated pages.<br/>", "translatepress-multilingual"); ?> <?php __("Force slash at end of home url:", "translatepress-multilingual"); ?> <?php __("Ads a slash at the end of the home_url() function", "translatepress-multilingual"); ?> <?php __("Show Both (recommended)", "translatepress-multilingual"); ?> <?php __("Remove Country Locale", "translatepress-multilingual"); ?> <?php __("Remove Region Independent Locale", "translatepress-multilingual"); ?> <?php __("Remove duplicate hreflang", "translatepress-multilingual"); ?> <?php __("Choose which hreflang tags will appear on your website.<br/>We recommend showing both types of hreflang tags as indicated by <a href=\"https://developers.google.com/search/docs/advanced/crawling/localized-versions\" title=\"Google Crawling\" target=\"_blank\">Google documentation</a>.<br/>Removing Country Locale when having multiple Country Locales of the same language (ex. English UK and English US) will result in showing one hreflang tag with link to just one of the region locales for that language.", "translatepress-multilingual"); ?> <?php __("Default (example: en-US, fr-CA, etc.)", "translatepress-multilingual"); ?> <?php __("Regional (example: en, fr, es, etc.)", "translatepress-multilingual"); ?> <?php __("HTML Lang Attribute Format", "translatepress-multilingual"); ?> <?php __("Change lang attribute of the html tag to a format that includes country regional or not. <br>In HTML, the lang attribute (<html lang=\"en-US\">) should be used to specify the language of text content so that the browser can correctly display or process your content (eg. for hyphenation, styling, spell checking, etc).", "translatepress-multilingual"); ?> <?php __("Load legacy Language Switcher", "translatepress-multilingual"); ?> <?php __("Applies to all types of language switchers (floating, shortcode, and menu). When enabled, the site will revert to using the original Language Switcher configured in the General Settings tab, replacing the new customizable version. Your existing switcher settings will remain saved, but they will be ignored while this option is active.", "translatepress-multilingual"); ?> <?php __("Load legacy SEO Pack Add-On", "translatepress-multilingual"); ?> <?php __("In case the recent migration to the new slug rewrite is causing trouble, set this to Yes to use the old method <br> Please <a href=\"https://translatepress.com/support/open-ticket/?utm_source=tp-advanced&utm_medium=client-site&utm_campaign=troubleshooting\" target=\"_blank\">open a support ticket</a> letting us know of the issues you are having.", "translatepress-multilingual"); ?> <?php __("Manual Translation Only", "translatepress-multilingual"); ?> <?php __("TranslatePress pro-actively scans and saves strings in the database when users access translated pages. <br>This setting disables this functionality and only allows translation and string saving when inside the Translation Editor. <br>Also disables machine translation outside the Translation Editor, giving you better control over character spending, by translating only the pages you visit in the Translation Editor.", "translatepress-multilingual"); ?> <?php __("Open language switcher only on click", "translatepress-multilingual"); ?> <?php __("Open the language switcher shortcode by clicking on it instead of hovering.<br> Close it by clicking on it, anywhere else on the screen or by pressing the escape key. This will affect only the shortcode language switcher.", "translatepress-multilingual"); ?> <?php __("Show opposite language in the language switcher", "translatepress-multilingual"); ?> <?php __("Transforms the language switcher into a button showing the other available language, not the current one.<br> Only works when there are exactly two languages, the default one and a translation one.<br>This will affect the shortcode language switcher and floating language switcher as well.<br> To achieve this in menu language switcher go to Appearance->Menus->Language Switcher and select Opposite Language.", "translatepress-multilingual"); ?> <?php __("<a href=\"%s\">Click here</a> to access the database optimization tool.", "translatepress-multilingual"); ?> <?php __("It helps remove possible duplicate translations, clear unnecessary data and repair possible metadata issues.", "translatepress-multilingual"); ?> <?php __("<a href=\"%s\" target=\"_blank\">Here</a> you can observe the last 5 SQL errors relevant to TranslatePress if they exist.", "translatepress-multilingual"); ?> <?php __("Troubleshooting", "translatepress-multilingual"); ?> <?php __("Exclude Gettext strings", "translatepress-multilingual"); ?> <?php __("Debug", "translatepress-multilingual"); ?> <?php __("Custom languages", "translatepress-multilingual"); ?> <?php __("Miscellaneous options", "translatepress-multilingual"); ?> <?php __("Exclude strings & pages", "translatepress-multilingual"); ?> <?php __("Automatic Translation Memory", "translatepress-multilingual"); ?> <?php __("Serve same translation for similar text. The strings need to have a percentage of 95% similarity.<br>Helps prevent losing existing translation when correcting typos or making minor adjustments to the original text. <br>If a translation already exists for a very similar original string, it will automatically be used for the current original string.<br>Does not work when making changes to a text that is part of a translation block unless the new text is manually merged again in a translation block.<br>Each string needs to have a minimum of 50 characters.", "translatepress-multilingual"); ?> <?php __("WARNING: This feature can negatively impact page loading times in secondary languages, particularly with large databases (for example websites with a lot of pages or products). If you experience slow loading times, disable this and try again.", "translatepress-multilingual"); ?> <?php __("Fix missing dynamic content", "translatepress-multilingual"); ?> <?php __("May help fix missing content inserted using JavaScript. <br> It shows dynamically inserted content in original language for a moment before the translation request is finished.", "translatepress-multilingual"); ?> <?php __("Filter Gettext wrapping from post content and title", "translatepress-multilingual"); ?> <?php __("Filters gettext wrapping such as #!trpst#trp-gettext from all updated post content and post title. Does not affect previous post content. <br/><strong>Database backup is recommended before switching on.</strong>", "translatepress-multilingual"); ?> <?php __("Filter Gettext wrapping from post meta", "translatepress-multilingual"); ?> <?php __("Filters gettext wrapping such as #!trpst#trp-gettext from all updated post meta. Does not affect previous post meta. <br/><strong>Database backup is recommended before switching on.</strong>", "translatepress-multilingual"); ?> <?php __("Google Translate v2", "translatepress-multilingual"); ?> <?php __("Google Translate API Key", "translatepress-multilingual"); ?> <?php __("Add your API Key here...", "translatepress-multilingual"); ?> <?php __("Visit <a href=\"https://cloud.google.com/docs/authentication/api-keys\" target=\"_blank\">this link</a> to see how you can set up an API key, <strong>control API costs</strong> and set HTTP referrer restrictions.", "translatepress-multilingual"); ?> <?php __("Your HTTP referrer is: %s", "translatepress-multilingual"); ?> <?php __("There was an error on the server processing your Google Translate key.", "translatepress-multilingual"); ?> <?php __("There was an error with your Google Translate key.", "translatepress-multilingual"); ?> <?php __("Please check your TranslatePress license key.", "translatepress-multilingual"); ?> <?php __("TranslatePress AI", "translatepress-multilingual"); ?> <?php __("No Active License Detected for this website.", "translatepress-multilingual"); ?> <?php __("In order to enable Automatic Translation using TranslatePress AI, you need a license key by creating a free account.", "translatepress-multilingual"); ?> <?php __("Create your Free Account", "translatepress-multilingual"); ?> <?php __(" or ", "translatepress-multilingual"); ?> <?php __("Enter your license key", "translatepress-multilingual"); ?> <?php __(" Or %1$spurchase one here%2$s", "translatepress-multilingual"); ?> <?php __("Your free account includes: ", "translatepress-multilingual"); ?> <?php __("Get more AI Tokens and unlock all AI features with TranslatePress Pro.", "translatepress-multilingual"); ?> <?php __("You have a valid %s <strong>license</strong>.", "translatepress-multilingual"); ?> <?php __(" words remaining. ", "translatepress-multilingual"); ?> <?php __("Recheck", "translatepress-multilingual"); ?> <?php __("Rechecking...", "translatepress-multilingual"); ?> <?php __("Done.", "translatepress-multilingual"); ?> <?php __("Manage your license & quota on the %s", "translatepress-multilingual"); ?> <?php __("TranslatePress.com Account Page", "translatepress-multilingual"); ?> <?php __("SEO support for page slug, page title, description and Facebook and Twitter social graph information. The HTML lang attribute is properly set.", "translatepress-multilingual"); ?> <?php __("Enable Modules", "translatepress-multilingual"); ?> <?php __("Enable Add-on modules to extend TranslatePress and enhance the functionality of your translated site.", "translatepress-multilingual"); ?> <?php __("More functionality with TranslatePress Pro.", "translatepress-multilingual"); ?> <?php __("Already a Pro User?", "translatepress-multilingual"); ?> <?php __("Activate License Key", "translatepress-multilingual"); ?> <?php __("This add-on is not available on your current plan.", "translatepress-multilingual"); ?> <?php __("Continue", "translatepress-multilingual"); ?> <?php __("The link you followed has expired. Please reload the page and try again.", "translatepress-multilingual"); ?> <?php __("A valid license is required to enable Automatic Translation.", "translatepress-multilingual"); ?> <?php __("Your license key is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=tp-ai\">https://translatepress.com/account</a> -> Manage Sites.", "translatepress-multilingual"); ?> <?php __("<p><strong>License key mismatch.</strong> The license you entered doesn't match the TranslatePress version you have installed.</p><p>Please check that you've installed the correct version for your license from your TranslatePress account.</p>", "translatepress-multilingual"); ?> <?php __("Automatically translate your website using TranslatePress AI.", "translatepress-multilingual"); ?> <?php __("In order to enable Automatic Translation using TranslatePress AI, please enter your license key from", "translatepress-multilingual"); ?> <?php __("your account.", "translatepress-multilingual"); ?> <?php __("Skip and continue with manual translation »", "translatepress-multilingual"); ?> <?php __("Get Your Free TranslatePress AI License", "translatepress-multilingual"); ?> <?php __("Generate License", "translatepress-multilingual"); ?> <?php __("Creating a free account includes: ", "translatepress-multilingual"); ?> <?php __("Are you a TranslatePress PRO user?", "translatepress-multilingual"); ?> <?php __("Install & Activate your pro plugin.", "translatepress-multilingual"); ?> <?php __("Skip this step", "translatepress-multilingual"); ?> <?php __("The email address you added is incorrect.", "translatepress-multilingual"); ?> <?php __("Setup Complete", "translatepress-multilingual"); ?> <?php __("You're ready to start translating!", "translatepress-multilingual"); ?> <?php __("You have successfully set up TranslatePress for your website.", "translatepress-multilingual"); ?> <?php __("Receive ", "translatepress-multilingual"); ?> <?php __("Sign me up to the Newsletter", "translatepress-multilingual"); ?> <?php __("Start translating", "translatepress-multilingual"); ?> <?php __("Sign Up and Start translating", "translatepress-multilingual"); ?> <?php __("Please upload a TranslatePress Pro plugin file.", "translatepress-multilingual"); ?> <?php __("Upload error: ", "translatepress-multilingual"); ?> <?php __("Install failed: ", "translatepress-multilingual"); ?> <?php __("Plugin installed, but entry file not found. ", "translatepress-multilingual"); ?> <?php __("Activation error: ", "translatepress-multilingual"); ?> <?php __("Invalid plugin specified.", "translatepress-multilingual"); ?> <?php __("Invalid action specified.", "translatepress-multilingual"); ?> <?php __("Plugin deactivation failed.", "translatepress-multilingual"); ?> <?php __("Plugin activation failed: ", "translatepress-multilingual"); ?> <?php __("First, install and activate TranslatePress Pro", "translatepress-multilingual"); ?> <?php __("Please upload the TranslatePress PRO zip archive from your", "translatepress-multilingual"); ?> <?php __("TranslatePress Account", "translatepress-multilingual"); ?> <?php __("Install and Activate", "translatepress-multilingual"); ?> <?php __("Installed Pro versions", "translatepress-multilingual"); ?> <?php __("« Go back", "translatepress-multilingual"); ?> <?php __("Activate License »", "translatepress-multilingual"); ?> <?php __("You are trying to add an invalid additional language. Please select a valid option.", "translatepress-multilingual"); ?> <?php __("Please add an additional language.", "translatepress-multilingual"); ?> <?php __("You are trying to add an invalid default language. Please select a valid option.", "translatepress-multilingual"); ?> <?php __("You need to select a default language.", "translatepress-multilingual"); ?> <?php __("Configure Site Languages", "translatepress-multilingual"); ?> <?php __("Select the default and additional languages for your website.", "translatepress-multilingual"); ?> <?php __("You can edit your site languages at any point.", "translatepress-multilingual"); ?> <?php __("Additional Language", "translatepress-multilingual"); ?> <?php __("Choose a secondary language...", "translatepress-multilingual"); ?> <?php __("Add Language", "translatepress-multilingual"); ?> <?php __("Choose a language...", "translatepress-multilingual"); ?> <?php __("Add more than two languages with TranslatePress Pro.", "translatepress-multilingual"); ?> <?php __("Your license key is disabled for this URL. Re-enable it from <a target=\"_blank\" href=\"https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=activate-license\">https://translatepress.com/account</a> -> Manage Sites.", "translatepress-multilingual"); ?> <?php __("Add your License Key", "translatepress-multilingual"); ?> <?php __("Add your License Key to unlock all premium features. Find the License Key in your", "translatepress-multilingual"); ?> <?php __("Your license is valid and active.", "translatepress-multilingual"); ?> <?php __("« Go Back", "translatepress-multilingual"); ?> <?php __("Set up Language Switcher", "translatepress-multilingual"); ?> <?php __("Select the style of the language switcher. You will find more ways to display it, in plugin settings.", "translatepress-multilingual"); ?> <?php __("Displays a small language drop-down across your website, in a corner of your choosing.", "translatepress-multilingual"); ?> <?php __("Switcher Location", "translatepress-multilingual"); ?> <?php __("Apply a Template", "translatepress-multilingual"); ?> <?php __("You can customize the design later", "translatepress-multilingual"); ?> <?php __("Default Template", "translatepress-multilingual"); ?> <?php __("Dark Template", "translatepress-multilingual"); ?> <?php __("Border Template", "translatepress-multilingual"); ?> <?php __("Border", "translatepress-multilingual"); ?> <?php __("Transparent Template", "translatepress-multilingual"); ?> <?php __("Transparent", "translatepress-multilingual"); ?> <?php __("Welcome to TranslatePress", "translatepress-multilingual"); ?> <?php __("Quick guided setup to configure TranslatePress in no time!", "translatepress-multilingual"); ?> <?php __("It takes less than a minute.", "translatepress-multilingual"); ?> <?php __("Scanning item %1$d of %2$d...", "translatepress-multilingual"); ?> <?php __("Manually translated", "translatepress-multilingual"); ?> <?php __("Automatically translated", "translatepress-multilingual"); ?> <?php __("Not translated", "translatepress-multilingual"); ?> <?php __("Bulk Actions", "translatepress-multilingual"); ?> <?php __("Delete entries", "translatepress-multilingual"); ?> <?php __("Edit", "translatepress-multilingual"); ?> <?php __("Delete", "translatepress-multilingual"); ?> <?php __("Filter", "translatepress-multilingual"); ?> <?php __("Clear filters", "translatepress-multilingual"); ?> <?php __("Add New", "translatepress-multilingual"); ?> <?php __("Rescan plugins and theme for strings", "translatepress-multilingual"); ?> <?php __("Scanning plugins and theme for strings...", "translatepress-multilingual"); ?> <?php __("Plugins and theme scan is complete", "translatepress-multilingual"); ?> <?php __("Plugins and theme scan did not finish due to an error", "translatepress-multilingual"); ?> <?php __("Import / Export", "translatepress-multilingual"); ?> <?php __("items", "translatepress-multilingual"); ?> <?php __("of", "translatepress-multilingual"); ?> <?php __("See More", "translatepress-multilingual"); ?> <?php __("See Less", "translatepress-multilingual"); ?> <?php __("Apply", "translatepress-multilingual"); ?> <?php __("No strings match your query.", "translatepress-multilingual"); ?> <?php __("Try to rescan plugins and theme for strings.", "translatepress-multilingual"); ?> <?php __("An error occurred while loading results. Most likely you were logged out. Reload page?", "translatepress-multilingual"); ?> <?php __("found in translation", "translatepress-multilingual"); ?> <?php __("Select All", "translatepress-multilingual"); ?> <?php __("Select Visible", "translatepress-multilingual"); ?> <?php __("You are about to perform this action on all the strings matching your filter, not just the visibly checked. To perform the action only to the visible strings click \"Select Visible\" from the table header dropdown.", "translatepress-multilingual"); ?> <?php __("You are about to perform this action only on the visible strings. To perform the action on all the strings matching the filter click \"Select All\" from the table header dropdown.", "translatepress-multilingual"); ?> <?php __("To continue please type the word:", "translatepress-multilingual"); ?> <?php __("The word typed was incorrect. Action was cancelled.", "translatepress-multilingual"); ?> <?php __("in", "translatepress-multilingual"); ?> <?php __("Warning: This action cannot be undone. Deleting a string will remove its current translation. The original string will appear again in this interface after TranslatePress detects it. This action is NOT equivalent to excluding the string from being translated again.", "translatepress-multilingual"); ?> <?php __("%d original entries and their translations were deleted.", "translatepress-multilingual"); ?> <?php __("Navigate to next page", "translatepress-multilingual"); ?> <?php __("Navigate to previous page", "translatepress-multilingual"); ?> <?php __("Navigate to first page", "translatepress-multilingual"); ?> <?php __("Navigate to last page", "translatepress-multilingual"); ?> <?php __("Type a page number to navigate to", "translatepress-multilingual"); ?> <?php __("Incorrect page number. Type a page number between 1 and total number of pages", "translatepress-multilingual"); ?> <?php __("Search original and translated strings containing typed keywords while also matching selected filters. Place string in quotes for exact match: \"string\"", "translatepress-multilingual"); ?> <?php __("Filter strings according to selected translation status, filters and keywords and selected filters", "translatepress-multilingual"); ?> <?php __("Removes selected filters", "translatepress-multilingual"); ?> <?php __("See options for selecting all strings", "translatepress-multilingual"); ?> <?php __("Click to sort strings by this column", "translatepress-multilingual"); ?> <?php __("Language in which the translation status filter applies. Leave unselected for the translation status to apply to ANY language", "translatepress-multilingual"); ?> <?php __("Search", "translatepress-multilingual"); ?> <?php __("Slugs that are not found in either one of the other categories.", "translatepress-multilingual"); ?> <?php __("Plugins and Theme String Translation", "translatepress-multilingual"); ?> <?php __("Gettext", "translatepress-multilingual"); ?> <?php __("Search Gettext Strings", "translatepress-multilingual"); ?> <?php __("ID", "translatepress-multilingual"); ?> <?php __("Original String", "translatepress-multilingual"); ?> <?php __("Translation", "translatepress-multilingual"); ?> <?php __("Filter by domain", "translatepress-multilingual"); ?> <?php __("Filter by type", "translatepress-multilingual"); ?> <?php __("Email text", "translatepress-multilingual"); ?> <?php __("Emails String Translation", "translatepress-multilingual"); ?> <?php __("Emails", "translatepress-multilingual"); ?> <?php __("Search Email Strings", "translatepress-multilingual"); ?> <?php __("User Inputted String Translation", "translatepress-multilingual"); ?> <?php __("Regular", "translatepress-multilingual"); ?> <?php __("Search Regular Strings", "translatepress-multilingual"); ?> <?php __("Filter by Translation Block", "translatepress-multilingual"); ?> <?php __("URL Slugs Translation", "translatepress-multilingual"); ?> <?php __("String Translation Editor", "translatepress-multilingual"); ?> <?php __("Post Slug", "translatepress-multilingual"); ?> <?php __("Invalid language code", "translatepress-multilingual"); ?> <?php __("First by browser language, then IP address (recommended)", "translatepress-multilingual"); ?> <?php __("First by IP address, then by browser language", "translatepress-multilingual"); ?> <?php __("Only by browser language", "translatepress-multilingual"); ?> <?php __("Only by IP address", "translatepress-multilingual"); ?> <?php __("A popup appears asking the user if they want to be redirected", "translatepress-multilingual"); ?> <?php __("Redirect directly (*not recommended)", "translatepress-multilingual"); ?> <?php __("Pop-up window over the content", "translatepress-multilingual"); ?> <?php __("Hello bar before the content", "translatepress-multilingual"); ?> <?php __("WARNING. Cannot determine your language preference based on your current IP.<br>This is most likely because the website is on a local environment.", "translatepress-multilingual"); ?> <?php __("Go to <a href=\"%s\" target=\"_self\">Advanced</a> tab to change this feature's settings", "translatepress-multilingual"); ?> <?php __("User Language Detection Method", "translatepress-multilingual"); ?> <?php __("Select how the language should be detected for first time visitors.<br>The visitor's last displayed language will be remembered through cookies.", "translatepress-multilingual"); ?> <?php __("User Notification Popup", "translatepress-multilingual"); ?> <?php __("A popup appears asking the user if they want to be redirected.", "translatepress-multilingual"); ?> <?php __("Popup Type", "translatepress-multilingual"); ?> <?php __("Popup Text", "translatepress-multilingual"); ?> <?php __("The same text is displayed in all languages. <br>A selecting language switcher will be appended to the pop-up. The detected language is pre-selected.", "translatepress-multilingual"); ?> <?php __("Button Text", "translatepress-multilingual"); ?> <?php __("Write the text you wish to appear on the button..", "translatepress-multilingual"); ?> <?php __("Close Button Text", "translatepress-multilingual"); ?> <?php __("Write the text you wish to appear on the close button. Leave empty for just the close button.", "translatepress-multilingual"); ?> <?php __("Bad request. There was an error accessing the DeepL API.", "translatepress-multilingual"); ?> <?php __("The API key entered is invalid.", "translatepress-multilingual"); ?> <?php __("The API resource could not be found.", "translatepress-multilingual"); ?> <?php __("The request size is too large.", "translatepress-multilingual"); ?> <?php __("The request is too long.", "translatepress-multilingual"); ?> <?php __("Too many requests. Please try again later.", "translatepress-multilingual"); ?> <?php __("Your translation quota has been reached.", "translatepress-multilingual"); ?> <?php __("We could not process your request. Please try again later.", "translatepress-multilingual"); ?> <?php __("There is an error on the DeepL service and your request could not be processed.", "translatepress-multilingual"); ?> <?php __("DeepL API Type", "translatepress-multilingual"); ?> <?php __("Pro", "translatepress-multilingual"); ?> <?php __("Free", "translatepress-multilingual"); ?> <?php __("Select the type of DeepL API you want to use.", "translatepress-multilingual"); ?> <?php __("DeepL API Key", "translatepress-multilingual"); ?> <?php __("Visit <a href=\"%s\" target=\"_blank\">this link</a> to see how you can set up an API key and control API costs.", "translatepress-multilingual"); ?> <?php __("Translator", "translatepress-multilingual"); ?> <?php __(" TranslatePress Settings", "translatepress-multilingual"); ?> <?php __("Allow this user to translate the website.", "translatepress-multilingual"); ?> <?php __("(inactive)", "translatepress-multilingual"); ?> <?php __("Taxonomy Slugs", "translatepress-multilingual"); ?> <?php __("Search Taxonomy Slugs", "translatepress-multilingual"); ?> <?php __("Taxonomy Slug", "translatepress-multilingual"); ?> <?php __("Term Slugs", "translatepress-multilingual"); ?> <?php __("Search Term Slugs", "translatepress-multilingual"); ?> <?php __("Term Slug", "translatepress-multilingual"); ?> <?php __("Taxonomy", "translatepress-multilingual"); ?> <?php __("Filter by Taxonomy", "translatepress-multilingual"); ?> <?php __("Post Slugs", "translatepress-multilingual"); ?> <?php __("Search Post Slugs", "translatepress-multilingual"); ?> <?php __("Post ID", "translatepress-multilingual"); ?> <?php __("Post Type", "translatepress-multilingual"); ?> <?php __("Filter by Post Type", "translatepress-multilingual"); ?> <?php __("Published", "translatepress-multilingual"); ?> <?php __("Any Post Status", "translatepress-multilingual"); ?> <?php __("Post Type Base Slugs", "translatepress-multilingual"); ?> <?php __("Post Type Base Slug", "translatepress-multilingual"); ?> <?php __("Search Post Type Base Slugs", "translatepress-multilingual"); ?> <?php __("WooCommerce Slugs", "translatepress-multilingual"); ?> <?php __("WooCommerce Slug", "translatepress-multilingual"); ?> <?php __("Search WooCommerce Slugs", "translatepress-multilingual"); ?> <?php __("Other Slugs", "translatepress-multilingual"); ?> <?php __("Search Other Slugs", "translatepress-multilingual"); ?> includes/compatibility-functions.php 0000777 00000334635 15251156640 0013774 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** Compatibility functions with WP core and various themes and plugins*/ /** * Remove '?fl_builder' query param from edit translation url (when clicking the admin bar button to enter the translation Editor) * * Otherwise after publishing out of BB and clicking TP admin bar button, it’s still showing the BB interface * * @param $url * * @return bool */ function trp_beaver_builder_compatibility( $url ){ $url = remove_query_arg('fl_builder', $url ); return esc_url ($url); } add_filter( 'trp_edit_translation_url', 'trp_beaver_builder_compatibility' ); /** * Mb Strings missing PHP library error notice */ function trp_mbstrings_notification(){ echo '<div class="notice notice-error"><p>' . wp_kses( __( '<strong>TranslatePress</strong> requires <strong><a href="http://php.net/manual/en/book.mbstring.php">Multibyte String PHP library</a></strong>. Please contact your server administrator to install it on your server.','translatepress-multilingual' ), [ 'a' => [ 'href' => [] ], 'strong' => [] ] ) . '</p></div>'; } function trp_missing_mbstrings_library( $allow_to_run ){ if ( ! extension_loaded('mbstring') ) { add_action( 'admin_menu', 'trp_mbstrings_notification' ); return false; } return $allow_to_run; } add_filter( 'trp_allow_tp_to_run', 'trp_missing_mbstrings_library' ); /** * Don't have html inside menu title tags. Some themes just put in the title the content of the link without striping HTML */ add_filter( 'nav_menu_link_attributes', 'trp_remove_html_from_menu_title', 10, 3); function trp_remove_html_from_menu_title( $atts, $item, $args ){ if( isset( $atts['title'] ) ) $atts['title'] = wp_strip_all_tags($atts['title']); return $atts; } /** * Rework wp_trim_words so we can trim Chinese, Japanese and Thai words since they are based on characters as words. * * @since 1.3.0 * * @param string $text Text to trim. * @param int $num_words Number of words. Default 55. * @param string $more Optional. What to append if $text needs to be trimmed. Default '…'. * @return string Trimmed text. */ function trp_wp_trim_words( $text, $num_words, $more, $original_text ) { if ( null === $more ) { $more = __( '…' );//phpcs:ignore } // what we receive is the short text in the filter $text = $original_text; $text = wp_strip_all_tags( $text ); $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $default_language= $settings["default-language"]; $char_is_word = false; foreach (array('ja', 'tw', 'zh') as $lang){ if (strpos($default_language, $lang) !== false){ $char_is_word = true; } } if ( $char_is_word && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) { $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' ); preg_match_all( '/./u', $text, $words_array ); $words_array = array_slice( $words_array[0], 0, $num_words + 1 ); $sep = ''; } else { $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY ); $sep = ' '; } if ( count( $words_array ) > $num_words ) { array_pop( $words_array ); $text = implode( $sep, $words_array ); $text = $text . $more; } else { $text = implode( $sep, $words_array ); } return $text; } add_filter('wp_trim_words', 'trp_wp_trim_words', 100, 4); /** * Use home_url in the https://www.peepso.com/ ajax front-end url so strings come back translated. * * @since 1.3.1 * * @param array $data Peepso data * @return array */ add_filter( 'peepso_data', 'trp_use_home_url_in_peepso_ajax' ); function trp_use_home_url_in_peepso_ajax( $data ){ if ( is_array( $data ) && isset( $data['ajaxurl_legacy'] ) ){ $data['ajaxurl_legacy'] = home_url( '/peepsoajax/' ); } return $data; } /** * Compatibility with Peepso urls having extra / due their link builder not considering home urls having trailing slashes */ add_filter('peepso_get_page', 'trp_remove_peepso_double_slash', 10, 2); function trp_remove_peepso_double_slash( $page, $name){ // avoid accidentally replacing // from http:// $page = str_replace('http://', 'http:/', $page ); $page = str_replace('https://', 'https:/', $page ); $page = str_replace('//', '/', $page ); // place it back $page = str_replace('https:/', 'https://', $page ); $page = str_replace('http:/', 'http://', $page ); return $page; }; /** * Filter ginger_iframe_banner and ginger_text_banner to use shortcodes so our conditional lang shortcode works. * * @since 1.3.1 * * @param string $content * @return string */ add_filter('ginger_iframe_banner', 'trp_do_shortcode', 999 ); add_filter('ginger_text_banner', 'trp_do_shortcode', 999 ); function trp_do_shortcode($content){ return do_shortcode(stripcslashes($content)); } /** * Compatibility with Woocommerce Print Products * * @param $bool * @param $output * @return bool|mixed */ add_filter( 'trp_skip_gettext_processing', 'trp_woo_strip_gettext_from_print_products' ); function trp_woo_strip_gettext_from_print_products( $bool ){ if ( isset( $_REQUEST['print-products'] ) && $_REQUEST['print-products'] == 'pdf' && class_exists('\WooCommerce_Print_Products') ) { return true; } return $bool; } add_filter('trp_stop_translating_page', 'trp_woo_pdf_print_products', 10, 2); function trp_woo_pdf_print_products( $bool, $output ){ if ( isset( $_REQUEST['print-products'] ) && $_REQUEST['print-products'] == 'pdf' && class_exists('\WooCommerce_Print_Products') ) { return true; } return $bool; } /** * DK PDF compatibility * * The DK PDF plugin seems to not work at all. Even when TranslatePress is deactivated, there are critical errors and notices in debug.log */ add_filter( 'trp_skip_gettext_processing', 'trp_dk_pdf_strip_gettext_from_pdf' ); function trp_dk_pdf_strip_gettext_from_pdf( $bool ){ if ( isset( $_GET['pdf'] ) && ( class_exists( 'DKPDF' ) || defined( 'DKPDF_VERSION' ) ) ){ return true; } return $bool; } add_filter('trp_stop_translating_page', 'trp_do_not_translate_dk_pdf', 10, 2); function trp_do_not_translate_dk_pdf($translate, $output){ if ( isset( $_GET['pdf'] ) && ( class_exists( 'DKPDF' ) || defined( 'DKPDF_VERSION' ) ) ){ return true; } return $translate; } /** * Compatibility with Invoices for WooCommerce * Do not translate url's like this as it brakes them because they are PDF's: https://ro.wordpress.org/plugins/woocommerce-pdf-invoices/ */ add_filter( 'trp_skip_gettext_processing', 'trp_invoices_for_woocommerce_strip_gettext_from_pdf', 10, 4 ); function trp_invoices_for_woocommerce_strip_gettext_from_pdf( $bool, $translation, $text, $domain ){ if ( isset( $_GET['wc-ajax'] ) && $_GET['wc-ajax'] == "checkout" && class_exists( '\BEWPI_Invoice' ) && ((trim( $domain ) === 'woocommerce-pdf-invoice') || ( $text == 'Cash on delivery' && trim($domain) == 'woocommerce') ) ) { return true; } return $bool; } add_filter('trp_stop_translating_page', 'trp_do_not_translate_pdf_param', 10, 2); function trp_do_not_translate_pdf_param($translate, $output){ if ( isset( $_GET['bewpi_action'] ) && class_exists( '\BEWPI_Invoice' ) ){ return true; } return $translate; } /** * Compatibility with WooCommerce PDF Invoices & Packing Slips * https://wordpress.org/plugins/woocommerce-pdf-invoices-packing-slips/ * * @since 1.4.3 * */ // fix attachment name in email add_filter( 'wpo_wcpdf_filename', 'trp_woo_pdf_invoices_and_packing_slips_compatibility' ); // fix #trpgettext inside invoice pdf add_filter( 'wpo_wcpdf_get_html', 'trp_woo_pdf_invoices_and_packing_slips_compatibility'); function trp_woo_pdf_invoices_and_packing_slips_compatibility($title){ if ( class_exists( 'TRP_Translation_Manager' ) ) { return TRP_Translation_Manager::strip_gettext_tags($title); } } // fix font of pdf breaking because of str_get_html() call inside translate_page() add_filter( 'trp_stop_translating_page', 'trp_woo_pdf_invoices_and_packing_slips_compatibility_dont_translate_pdf', 10, 2 ); function trp_woo_pdf_invoices_and_packing_slips_compatibility_dont_translate_pdf( $bool, $output ){ if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'generate_wpo_wcpdf' ) { return true; } return $bool; } /** * Compatibility with WooCommerce PDF Invoices (woocommerce-ultimate-pdf-invoices) * https://www.welaunch.io/en/product/woocommerce-pdf-invoices/ * * @since 1.4.3 * */ add_filter( 'woocommerce_pdf_invoices_content', 'trp_woo_ultimate_pdf_invoices_compatibility'); add_filter( 'woocommerce_pdf_invoices_order_data', 'trp_woo_ultimate_pdf_invoices_data_compatibility'); function trp_woo_ultimate_pdf_invoices_compatibility($title){ if ( class_exists( 'TRP_Translation_Manager' ) ) { return TRP_Translation_Manager::strip_gettext_tags($title); } } function trp_woo_ultimate_pdf_invoices_data_compatibility($data_array){ if ( class_exists( 'TRP_Translation_Manager' ) ) { $data_array = array_map('TRP_Translation_Manager::strip_gettext_tags',$data_array ); } return $data_array; } /** * Compatibility with WooCommerce PDF Catalog (woocommerce-pdf-catalog) * https://www.welaunch.io/en/product/woocommerce-pdf-catalog/ * * @since 2.2.7 * */ add_filter( 'trp_stop_translating_page', 'trp_woocommerce_pdf_catalog_compatibility_dont_translate_pdf', 10, 2 ); function trp_woocommerce_pdf_catalog_compatibility_dont_translate_pdf( $bool, $output ){ if ( isset( $_REQUEST['pdf-catalog'] ) ) { return true; } return $bool; } /** * Compatibility with YITH WooCommerce */ add_filter( 'trp_skip_gettext_processing', 'trp_woo_strip_gettext_from_yith_pdf', 10, 4 ); function trp_woo_strip_gettext_from_yith_pdf( $bool, $translation, $text, $domain ){ if ( isset( $_GET['wc-ajax'] ) && $_GET['wc-ajax'] == 'checkout' && class_exists( 'YITH_Checkout_Addon' ) && ((trim( $domain ) === 'yith-woocommerce-pdf-invoice') || ( $text == 'N/A' && trim($domain) == 'woocommerce') ) ){ return true; } return $bool; } add_filter( 'trp_stop_translating_page', 'trp_woo_pdf_invoices_compatibility_dont_translate_pdf', 10, 2 ); function trp_woo_pdf_invoices_compatibility_dont_translate_pdf( $bool, $output ){ if ( isset( $_REQUEST['type'] ) && $_REQUEST['type'] == 'proforma' && class_exists( 'YITH_Checkout_Addon' ) ) { return true; } return $bool; } /** * Compatibility with WooCommerce order notes * * When a new order is placed in secondary languages, in admin area WooCommerce->Orders->Edit Order, the right sidebar contains Order notes which can contain #trpst tags. * * @since 1.4.3 */ // old orders add_filter( 'woocommerce_get_order_note', 'trp_woo_notes_strip_trpst' ); // new orders add_filter( 'woocommerce_new_order_note_data', 'trp_woo_notes_strip_trpst' ); function trp_woo_notes_strip_trpst( $note_array ){ foreach ( $note_array as $item => $value ){ $note_array[$item] = TRP_Translation_Manager::strip_gettext_tags( $value ); } return $note_array; } /* * Compatibility with WooCommerce back-end display order shipping taxes */ add_filter('woocommerce_order_item_display_meta_key','trp_woo_data_strip_trpst'); add_filter('woocommerce_order_item_get_method_title','trp_woo_data_strip_trpst'); function trp_woo_data_strip_trpst( $data ){ return TRP_Translation_Manager::strip_gettext_tags( $data ); } /** * Compatibility with WooCommerce country list on checkout. * * Skip detection by translate-dom-changes of the list of countries * */ add_filter( 'trp_skip_selectors_from_dynamic_translation', 'trp_woo_skip_dynamic_translation' ); function trp_woo_skip_dynamic_translation( $skip_selectors ){ if( class_exists( 'WooCommerce' ) ) { $add_skip_selectors = array( '#billing_country', '#shipping_country', '#billing_state', '#shipping_state', '#select2-billing_country-results', '#select2-billing_state-results', '#select2-shipping_country-results', '#select2-shipping_state-results' ); return array_merge( $skip_selectors, $add_skip_selectors ); } return $skip_selectors; } /** * Prevent translation of names and addresses in WooCommerce emails. */ add_action( 'woocommerce_email_customer_details', 'trp_woo_prevent_address_from_translation_in_emails' ); function trp_woo_prevent_address_from_translation_in_emails(){ add_filter( 'woocommerce_order_get_formatted_shipping_address', 'trp_woo_address_no_translate', 10, 3 ); add_filter( 'woocommerce_order_get_formatted_billing_address', 'trp_woo_address_no_translate', 10, 3 ); } function trp_woo_address_no_translate( $address, $raw_address, $order ){ return empty( $address ) ? $address : '<span data-no-translation>' . $address . '</span>'; } /** * Compatibility with WooCommerce product variation. * * Add span tag to woocommerce product variation name. * * Product variation name keep changes, but the prefix is the same. Wrap the prefix to allow translating that part separately. */ add_filter( 'woocommerce_product_variation_title', 'trp_woo_wrap_variation', 8, 4); function trp_woo_wrap_variation($name, $product, $title_base, $title_suffix){ $separator = '<span> - </span>'; return $title_suffix ? $title_base . $separator . $title_suffix : $title_base; } // trpgettext tags don't get escaped because they add <small> tags through a regex. add_filter( 'qm/output/title', 'trp_qm_strip_gettext', 100); function trp_qm_strip_gettext( $data ){ if ( is_array( $data ) ) { foreach( $data as $key => $value ){ $data[$key] = trp_qm_strip_gettext($value); } }else { // remove small tags $data = preg_replace('(<(\/)?small>)', '', $data); // strip gettext (not needed, they are just numbers shown in admin bar anyway) $data = TRP_Translation_Manager::strip_gettext_tags( $data ); // add small tags back the same way they do it in the filter 'qm/output/title' $data = preg_replace( '#\s?([^0-9,\.]+)#', '<small>$1</small>', $data ); } return $data; } /** * Compatibility with SeedProd Coming Soon * * Manually include the scripts and styles if do_action('enqueue_scripts') is not called */ add_filter( 'trp_translated_html', 'trp_force_include_scripts', 10, 4 ); function trp_force_include_scripts( $final_html, $TRP_LANGUAGE, $language_code, $preview_mode ){ if ( $preview_mode ){ $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); $trp_data = $translation_render->get_trp_data(); $scripts_and_styles = apply_filters('trp_editor_missing_scripts_and_styles', array( 'jquery' => "<script type='text/javascript' src='" . includes_url( '/js/jquery/jquery.js' ) . "'></script>", 'trp-iframe-preview-script.js' => "<script type='text/javascript' src='" . TRP_PLUGIN_URL . "assets/js/trp-iframe-preview-script.js'></script>", 'trp-translate-dom-changes.js' => "<script>trp_data = '" . addslashes(json_encode($trp_data) ) . "'; trp_data = JSON.parse(trp_data);</script><script type='text/javascript' src='" . TRP_PLUGIN_URL . "assets/js/trp-translate-dom-changes.js'></script>", 'trp-preview-iframe-style-css' => "<link rel='stylesheet' id='trp-preview-iframe-style-css' href='" . TRP_PLUGIN_URL . "assets/css/trp-preview-iframe-style.css' type='text/css' media='all' />", 'dashicons' => "<link rel='stylesheet' id='dashicons-css' href='" . includes_url( '/css/dashicons.min.css' ) . "' type='text/css' media='all' />" )); $missing_script = ''; foreach($scripts_and_styles as $key => $value ){ if ( strpos( $final_html, $key ) === false ){ $missing_script .= $value; } } if ( $missing_script !== '' ){ $html = TranslatePress\str_get_html( $final_html, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT ); if ( $html === false ) { return $final_html; } $body = $html->find( 'body', 0 ); if ( $body ) { $body->innertext = $body->innertext . $missing_script; } $final_html = $html->save(); } } return $final_html; } /* * Compatibility with plugins sending Gettext strings in requests such as Cartflows * * Strip gettext wrappings from the requests made from http->post() */ // Strip of gettext wrappings all the values of the body request array add_filter( 'http_request_args', 'trp_strip_trpst_from_requests', 10, 2 ); function trp_strip_trpst_from_requests($args, $url){ if( is_array( $args['body'] ) ) { array_walk_recursive( $args['body'], 'trp_array_walk_recursive_strip_gettext_tags' ); }else{ $args['body'] = TRP_Translation_Manager::strip_gettext_tags( $args['body'] ); } return $args; } function trp_array_walk_recursive_strip_gettext_tags( &$value ){ $value = TRP_Translation_Manager::strip_gettext_tags( $value ); } // Strip of gettext wrappings the customer_name and customer_email keys. Found in WC Stripe and Cartflows add_filter( 'wc_stripe_payment_metadata', 'trp_strip_request_metadata_keys' ); function trp_strip_request_metadata_keys( $metadata ){ foreach( $metadata as $key => $value ) { $stripped_key = TRP_Translation_Manager::strip_gettext_tags( $key ); if ( $stripped_key != $key ) { $metadata[ $stripped_key ] = $value; unset( $metadata[ $key ] ); } } return $metadata; } /** * Compatibility with NextGEN Gallery * * They start an output buffer at init -1 (before ours at init 0). They print footer scripts after we run translate_page, * resulting in outputting scripts that won't be stripped of trpst trp-gettext wrappings. * This includes WooCommerce Checkout scripts, resulting in trpst wrappings around form fields like Street Address. * Another issue is that translation editor is a blank page. * * We cannot move their hook to priority 1 because we do not have access to the object that gets hooked is not retrievable so we can't call remove_filter() * Also we cannot simply disable ngg using run_ngg_resource_manager hook because we would be disabling features of their plugin. * * So the only solution that works is to move our hook to -2. */ add_filter( 'trp_start_output_buffer_priority', 'trp_nextgen_compatibility' ); function trp_nextgen_compatibility( $priority ){ if ( class_exists( 'C_Photocrati_Resource_Manager' ) ) { return '-2'; } return $priority; } /** * Compatibility with NextGEN Gallery * * This plugin is adding wp_footer forcefully in a shutdown hook and appends it to "</body>" which bring up admin bar in translation editor. * * This filter prevents ngg from hooking the filters to alter the html. */ add_filter( 'run_ngg_resource_manager', 'trp_nextgen_disable_nextgen_in_translation_editor'); function trp_nextgen_disable_nextgen_in_translation_editor( $bool ){ if ( isset( $_REQUEST['trp-edit-translation'] ) && sanitize_text_field( $_REQUEST['trp-edit-translation'] ) === 'true' ) { return false; } return $bool; } /** * Compatibility with WooCommerce added to cart message * * Makes sure title of product is translated. * * The title of product is added through sprintf %s of a Gettext. * */ add_filter( 'the_title', 'trp_woo_translate_product_title_added_to_cart', 10, 2 ); function trp_woo_translate_product_title_added_to_cart( ...$args ){ // fix themes that don't implement the_title filter correctly. Works on PHP 5.6 >. // Implemented this because users we getting this error frequently. if( isset($args[0])){ $title = $args[0]; } else { $title = ''; } if( class_exists( 'WooCommerce' ) ){ if ( version_compare( PHP_VERSION, '5.4.0', '>=' ) ) { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15);//set a limit if it is supported to improve performance } else{ $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } $list_of_functions = apply_filters( 'trp_woo_translate_title_before_translate_page', array( 'wc_add_to_cart_message' ) ); if( !empty( $callstack_functions ) ) { foreach ( $callstack_functions as $callstack_function ) { if ( in_array( $callstack_function['function'], $list_of_functions ) ) { $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); $title = $translation_render->translate_page($title); break; } } } } return $title; } /** * Compatibility with WooCommerce "remove from cart" action * * In some cases (eg for Taiwanese) the product name and double quotes &ldquo &rdquo HTML entities * were translated/parsed wrongly. * We provide a fix by adding spaces between the quotes and product name * */ if( class_exists( 'WooCommerce' ) ) { add_filter( 'woocommerce_cart_item_removed_title', 'trp_woo_fix_product_remove_from_cart_notice', 10, 2 ); function trp_woo_fix_product_remove_from_cart_notice($message, $cart_item){ $product = wc_get_product( $cart_item['product_id'] ); if ($product){ $message = sprintf( _x( '“ %s ”', 'Item name in quotes', 'woocommerce' ), $product->get_name() ); //phpcs:ignore } return $message; } } /** * Compatibility with WooTour plugin * * They replace spaces (" ") with \u0020, after we apply #trpst and because we don't strip them it breaks html */ add_action('init', 'trp_wootour_add_gettext_filter'); function trp_wootour_add_gettext_filter(){ if ( class_exists( 'WooTour_Booking' ) ){ add_filter('gettext', 'trp_wootour_exclude_gettext_strings', 1000, 3 ); } } function trp_wootour_exclude_gettext_strings($translation, $text, $domain){ if ( $domain == 'woo-tour' ){ if ( in_array( $text, array( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ) ) ){ return TRP_Translation_Manager::strip_gettext_tags( $translation ); } } return $translation; } /** * Compatibility: WooCommerce cart product name translation normalization * * WHAT THIS FIX DOES * Normalizes the product name text inside the cart item link to exactly match `get_the_title( $product_id )`, * so TranslatePress see the same character used in product titles everywhere. * * By forcing the cart item name to the exact output of `get_the_title( $product_id )`, we ensure consistent characters. * * CONDITIONS * * 1) The cart is rendered with the [woocommerce_cart] shortcode (classic/cart template), * NOT the Cart Block. With the Cart Block, this filter typically won't run on the same markup. * 2) The product name contains a '-' character (ASCII `-`, U+002D). * * You can reproduce by: * - Using a simple product with `-` in its title, OR * - Using a variable product where the variation title includes `-` (variations usually append attributes with `-`). * */ add_filter( 'woocommerce_cart_item_name', 'trp_woo_cart_item_name', 8, 3 ); function trp_woo_cart_item_name( $product_name, $cart_item, $cart_item_key ){ if ( !strpos( $product_name, '</a>' ) ) return $product_name; $product_id = $cart_item['variation_id'] ?? $cart_item['product_id'] ?? null; if ( !$product_id ) return $product_name; $title = get_the_title( $product_id ); if ( empty( $title ) ) return $product_name; preg_match_all('~<a(.*?)href="([^"]+)"(.*?)>~', $product_name, $matches); if ( !isset( $matches[2][0] ) ) return $product_name; $product_name = sprintf( '<a href="%s">%s</a>', esc_url( $matches[2][0] ), $title ); return $product_name; } /** * Compatibility with WooCommerce PDF Invoices & Packing Slips * * Translate product name and variation (meta) in pdf invoices. */ add_filter( 'wpo_wcpdf_order_item_data', 'trp_woo_wcpdf_translate_product_name', 10, 3 ); function trp_woo_wcpdf_translate_product_name( $data, $order, $type ){ if ( isset( $data['name'] ) ) { $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component('translation_render'); remove_filter( 'trp_stop_translating_page', 'trp_woo_pdf_invoices_and_packing_slips_compatibility_dont_translate_pdf', 10 ); $data['name'] = $translation_render->translate_page($data['name']); if ( isset( $data['meta'] ) ) { $data['meta'] = $translation_render->translate_page($data['meta']); } add_filter( 'trp_stop_translating_page', 'trp_woo_pdf_invoices_and_packing_slips_compatibility_dont_translate_pdf', 10, 2 ); } return $data; } /** * Compatibility with WooCommerce Checkout Add-Ons plugin * * Exclude name of "paid add-on" item from being run through gettext. * * No other filters were found. Advanced settings strip meta did not work. * It's being added through WC->add_fee and inserted directly in db in custom table. */ add_action( 'woocommerce_cart_calculate_fees', 'trp_woo_checkout_add_ons_filter_trpstr', 10, 2); function trp_woo_checkout_add_ons_filter_trpstr(){ if ( class_exists('WC_Checkout_Add_Ons_Frontend') ) { add_filter('trp_skip_gettext_processing', 'trp_woo_checkout_exclude_strings', 1000, 4); } } function trp_woo_checkout_exclude_strings( $return, $translation, $text, $domain) { if ( $domain === 'woocommerce-checkout-add-ons' ) { $add_ons = wc_checkout_add_ons()->get_add_ons(); foreach ($add_ons as $add_on) { if ( $add_on->name === $text) return true; } } return $return; } /** * Compatibility with WooCommerce Fondy Payment gateway */ add_action('init', 'trp_woo_fondy_payment_gateway_add_gettext_filter'); function trp_woo_fondy_payment_gateway_add_gettext_filter(){ if ( class_exists( 'WC_fondy' ) ){ add_filter('gettext', 'trp_woo_fondy_payment_gateway_exclude_gettext_strings', 1000, 3 ); } } function trp_woo_fondy_payment_gateway_exclude_gettext_strings($translation, $text, $domain){ if ( $domain == 'fondy-woocommerce-payment-gateway' && $text == 'Order: ' ){ return TRP_Translation_Manager::strip_gettext_tags( $translation ); } return $translation; } /** * Compatibility with Woocommerce Product Filters plugin, unknown author * This is NOT about the plugin made by WBM https://woobewoo.com/, nor by barn2.com * * They stop the buffering at priority -150 and that leaves #trpst style tags before we get to remove them * * The caveat to removing or adding a foreign filter is that it can be done via * a) static class call or b) through an object instance * * In this case we obtained access to global objects set by the WCPF plugin * and their public methods. * */ add_action( 'init', 'trp_woo_product_filters', 10 ); function trp_woo_product_filters(){ if( isset( $GLOBALS['wcpf_plugin'] ) && class_exists( 'WooCommerce_Product_Filter_Plugin\Filters' ) ){ $wcpf_plugin = $GLOBALS['wcpf_plugin']; $component_register = $wcpf_plugin->get_component_register(); $filters = $component_register->get('Filters'); $hook_manager = $filters->get_hook_manager(); $hook_manager->remove_action( 'shutdown', 'end_of_buffering', -150 ); $hook_manager->add_action( 'shutdown', 'end_of_buffering', 100 ); } } /** * Compatibility with WooCommerce Product Filters by barn2 * https://barn2.com/wordpress-plugins/woocommerce-product-filters/ * * Set chunk size to 0 because the result of the wcf_fetch_data is HTML instead of JSON, causing errors in browser console */ if ( class_exists( 'Barn2\Plugin\WC_Filters\Plugin_Factory' ) ) { add_filter( "trp_output_buffer_chunk_size", "trp_set_chunk_size_to_zero", 10, 1 ); } function trp_set_chunk_size_to_zero( $chunk_size ) { $chunk_size = 0; return $chunk_size; } /** * Compatibility with Elementor Popups Links * * The url is urlencoded so we add the language to it but we shouldn't. * */ add_filter('trp_skip_url_for_language', 'trp_skip_elementor_popup_action_from_url_converter', 10, 2); function trp_skip_elementor_popup_action_from_url_converter($value, $url){ if(strpos($url, '%23elementor-action') !== false){ return true; } return $value; } /** * Strip gettext wrapping from get_the_date function parameter $d */ add_filter('get_the_date','trp_strip_gettext_from_get_the_date', 1, 3); function trp_strip_gettext_from_get_the_date($the_date, $d = NULL, $post = NULL){ if ( $d === NULL || $post === NULL ){ return $the_date; } $d = TRP_Translation_Manager::strip_gettext_tags( $d ); $post = get_post( $post ); if ( ! $post ) { return false; } if ( '' == $d ) { $the_date = get_post_time( get_option( 'date_format' ), false, $post, true ); } else { $the_date = get_post_time( $d, false, $post, true ); } return $the_date; } /** * Compatibility with Affiliate Theme * It's adding parameters found in the filter forms automatically, braking the query. * TranslatePress adds the trp-form-language for other reasons. So we need to remove it in this case. * https://affiliatetheme.io * */ add_filter('at_set_product_filter_query', 'trp_remove_lang_param_from_query'); function trp_remove_lang_param_from_query($args){ if ( isset( $args['meta_query'] ) && is_array( $args['meta_query']) ){ foreach($args['meta_query'] as $key => $value){ if ($value['key'] == 'trp-form-language'){ unset( $args['meta_query'][$key] ); } } $args['meta_query'] = array_values($args['meta_query']); } return $args; } /** * Set user prefered language to the language he was present on new user creation. * Only set it if an existing locale isn't set already, in case the registration comes from a form that sets the locale manually. * */ add_action( 'user_register', 'trp_add_user_prefered_language', 10 ); function trp_add_user_prefered_language($user_id) { global $TRP_LANGUAGE; if ( ! empty( $TRP_LANGUAGE ) ) { $user_locale = get_user_meta( $user_id, 'locale', true ); if ( empty( $user_locale ) ) { update_user_meta( $user_id, 'locale', $TRP_LANGUAGE ); } } } /* * Dflip Compatibility * With Secondary Language First, it deferes jquery and scripts don't load on the Elementor Editor. * Not sure exactly what's causing. I assume it's because Elementor loads with Ajax certain elements and that comes back broken somehow. */ add_action('wp_enqueue_scripts', 'trp_remove_dflip_defer_script', 9999); function trp_remove_dflip_defer_script(){ if(class_exists('DFlip')){ $dflip_instance = DFlip::get_instance(); remove_filter( 'script_loader_tag', array( $dflip_instance, 'add_defer_attribute' ), 10, 2 ); } } /** * Ignore WooCommerce display_name gettext * _x( '%1$s %2$s', 'display name', 'woocommerce' ) || wordpress\wp-content\plugins\woocommerce\includes\class-wc-customer.php * _x( '%1$s %2$s', 'Display name based on first name and last name') || wordpress\wp-includes\user.php * This will insert trpstr strings in the database. So just ignore it. * */ add_filter('trp_skip_gettext_processing', 'trp_exclude_woo_display_name_gettext', 2000, 4 ); function trp_exclude_woo_display_name_gettext ( $return, $translation, $text, $domain ){ if($text == '%1$s %2$s' && $domain == 'woocommerce'){ return true; } if($text == '%1$s %2$s' && $domain == 'default'){ return true; } return $return; } /** Compatibility with superfly menu plugin. * * Moving their script later so that dynamic translation detects their strings. */ add_action('wp_head','trp_superfly_change_menu_loading_hook', 5); function trp_superfly_change_menu_loading_hook(){ if ( remove_action ('wp_head', 'sf_dynamic') ){ add_action ('wp_print_footer_scripts', 'sf_dynamic', 20); } } /** * Compatibility with Yoast SEO Canonical URL and Opengraph URL * Yoast places the canonical wrongly and it's not processed correctly. */ add_filter( 'wpseo_canonical', 'trp_wpseo_canonical_compat', 99999, 2); function trp_wpseo_canonical_compat( $canonical, $presentation_class = null ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $canonical = $url_converter->get_url_for_language( $TRP_LANGUAGE, $canonical, '' ); return $canonical; }; add_filter( 'wpseo_opengraph_url', 'trp_opengraph_url', 99999 ); function trp_opengraph_url( $url ) { global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $url = $url_converter->get_url_for_language($TRP_LANGUAGE, $url, ''); return $url; } /** * Compatibility with RankMath SEO Canonical URL and OpenGraph URL * RankMath places the canonical wrongly and it's not processed correctly. */ add_filter( 'rank_math/frontend/canonical', 'trp_rankmath_canonical_compat', 99999 ); function trp_rankmath_canonical_compat( $canonical ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $canonical = $url_converter->get_url_for_language( $TRP_LANGUAGE, $canonical, '' ); return $canonical; } add_filter( 'rank_math/opengraph/url', 'trp_rankmath_opengraph_url', 99999 ); function trp_rankmath_opengraph_url( $url ) { global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $url = $url_converter->get_url_for_language( $TRP_LANGUAGE, $url, '' ); return $url; } /** * Compatibility with Oxygen plugin * * Improves stylesheet loading time by disabling gettext and regular text detection for pages loaded with xlink=css */ add_action( 'trp_before_running_hooks', 'trp_oxygen_remove_gettext_hooks', 10, 1 ); function trp_oxygen_remove_gettext_hooks( $trp_loader ) { if ( isset( $_REQUEST['xlink'] ) && $_REQUEST['xlink'] === 'css' ) { $trp = TRP_Translate_Press::get_trp_instance(); $gettext_manager = $trp->get_component( 'gettext_manager' ); $translation_render = $trp->get_component( 'translation_render' ); $trp_loader->remove_hook( 'init', 'create_gettext_translated_global', $gettext_manager ); $trp_loader->remove_hook( 'init', 'initialize_gettext_processing', $gettext_manager ); $trp_loader->remove_hook( 'shutdown', 'machine_translate_gettext', $gettext_manager ); $trp_loader->remove_hook( 'init', 'start_output_buffer', $translation_render ); $trp_loader->remove_hook( 'the_title', 'wrap_with_post_id', $translation_render ); $trp_loader->remove_hook( 'the_content', 'wrap_with_post_id', $translation_render ); } } /** * Compatibility with Oxygen plugin for search * Basically they use shortcodes to output content so we wrap the shortcode output for certain shortcodes */ if( function_exists('ct_is_show_builder') ) { add_filter('do_shortcode_tag', 'tp_oxygen_search_compatibility', 10, 4); function tp_oxygen_search_compatibility($output, $tag, $attr, $m){ // we're skiping the oxygen $tag as that one represents a dynamic shortcode based on custom fields. At times it contains images, links, numbers. Rarely we see actual content. if( $tag === 'ct_headline' || $tag === 'ct_text_block' ) { global $post, $TRP_LANGUAGE; if (empty($post->ID)) return $output; //we try to wrap only the actual content of the post if (!is_main_query()) return $output; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); if ($TRP_LANGUAGE !== $settings['default-language']) { if (is_singular() && !empty($post->ID)) { $output = "<trp-post-container data-trp-post-id='" . $post->ID . "'>" . $output . "</trp-post-container>";//changed " to ' to not break cases when the filter is applied inside an html attribute (title for example) } } } return $output; } /** * Disable TRP when the Oxygen Builder is being loaded */ add_filter( 'trp_stop_translating_page', 'trp_oxygen_disable_trp_in_builder', 10, 2); function trp_oxygen_disable_trp_in_builder($bool, $output){ if( defined( 'SHOW_CT_BUILDER' ) ) return true; return $bool; } /** * Hide Floating Language Switcher when the Oxygen is shown */ add_filter( 'trp_floating_ls_html', 'trp_page_builder_compatibility_disable_language_switcher' ); function trp_page_builder_compatibility_disable_language_switcher( $html ){ if( isset( $_GET['ct_builder'] ) && $_GET['ct_builder'] == 'true' ) return ''; return $html; } } if( function_exists( 'ct_is_show_builder' ) || defined( 'FL_BUILDER_VERSION' ) ){ /** * Used to redirect Oxygen Builder front-end to the default language. * Hooked before TRP_Language_Switcher::redirect_to_correct_language() so we don't redirect twice */ add_action( 'template_redirect', 'trp_page_builder_compatibility_redirect_to_default_language', 10 ); function trp_page_builder_compatibility_redirect_to_default_language(){ if( !is_admin() && ( ( isset( $_GET['ct_builder'] ) && $_GET['ct_builder'] == 'true' ) || isset( $_GET['fl_builder'] ) ) ){ $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); $settings = ( new TRP_Settings() )->get_settings(); $current_url = $url_converter->cur_page_url(); $current_lang = $url_converter->get_lang_from_url_string( $current_url ); if( ( $current_lang == null && $settings['add-subdirectory-to-default-language'] == 'yes' ) || ( $current_lang != null && $current_lang != $settings['default-language'] ) ){ $link_to_redirect = $url_converter->get_url_for_language( $settings['default-language'], null, '' ); if( $link_to_redirect != $current_url ){ wp_redirect( $link_to_redirect, 301 ); exit; } } } } /** * Disable automatic language redirect when the Oxygen or Beaver Builders are showing */ add_filter( 'trp_ald_enqueue_redirecting_script', 'trp_ald_dont_redirect_inside_page_builders'); function trp_ald_dont_redirect_inside_page_builders( $enqueue_redirecting_script ){ if( ( isset( $_GET['ct_builder'] ) && $_GET['ct_builder'] == 'true' ) || isset( $_GET['fl_builder'] ) ){ return false; } return $enqueue_redirecting_script; } } /** * Compatibility with Brizy editor */ add_filter( 'trp_enable_dynamic_translation', 'trp_brizy_disable_dynamic_translation' ); function trp_brizy_disable_dynamic_translation( $enable ){ if ( isset( $_REQUEST['brizy-edit-iframe'] ) ){ return false; } return $enable; } /** * Compatibility with Brizy PRO menu, the language switcher inside the menu does not work fully yet * Compatibility with Brizy assets loading with language slug in url (the 'process' function) */ if( defined( 'BRIZY_PRO_VERSION' ) || defined( 'BRIZY_VERSION' ) ){ add_filter( 'trp_home_url', 'trp_brizy_menu_pro_compatibility', 10, 5 ); function trp_brizy_menu_pro_compatibility( $new_url, $abs_home, $TRP_LANGUAGE, $path, $url ){ if ( version_compare( PHP_VERSION, '5.4.0', '>=' ) ) { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15);//set a limit if it is supported to improve performance } else{ $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } $list_of_functions = array( 'restoreSiteUrl', 'process' ); if( !empty( $callstack_functions ) ) { foreach ( $callstack_functions as $callstack_function ) { if ( in_array( $callstack_function['function'], $list_of_functions ) ) { return $url; } } } return $new_url; } } /** * Compatibility with woocommerce-pdf-vouchers plugin, removed language from download link of the vouchers */ if( defined( 'WOO_VOU_PLUGIN_VERSION' ) ){ add_filter( 'trp_home_url', 'trp_woocommerce_pdf_vouchers_download_file_compatibility', 10, 5 ); function trp_woocommerce_pdf_vouchers_download_file_compatibility( $new_url, $abs_home, $TRP_LANGUAGE, $path, $url ){ if ( version_compare( PHP_VERSION, '5.4.0', '>=' ) ) { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15);//set a limit if it is supported to improve performance } else{ $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } $list_of_functions = array( 'get_item_download_url' ); if( !empty( $callstack_functions ) ) { foreach ( $callstack_functions as $callstack_function ) { if ( in_array( $callstack_function['function'], $list_of_functions ) ) { return $url; } } } return $new_url; } } /** * Compatibility with Advanced WooCommerce Search 1/2 * Returns post ids where searched key matches translated version of post. */ add_filter( 'aws_search_results_products_ids', 'trp_aws_search_results_products_ids', 10, 2 ); function trp_aws_search_results_products_ids( $posts_ids, $s ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); if ( $TRP_LANGUAGE !== $settings['default-language'] ) { $trp_search = $trp->get_component( 'search' ); $search_result_ids = $trp_search->get_post_ids_containing_search_term($s, null); if (!empty ( $search_result_ids) ) { return $search_result_ids; } } return $posts_ids; } /** * Compatibility with Advanced WooCommerce Search 2/2 * Solves issue with caching results in a different language */ add_filter( 'wpml_current_language', 'trp_aws_current_language' ); function trp_aws_current_language( $lang ) { if ( class_exists( 'AWS_Main' ) ) { global $TRP_LANGUAGE; $lang = $TRP_LANGUAGE; } return $lang; } /** * Compatibility with thrive Arhitect plugin which does a "nice" little trick with remove_all_filters( 'template_include' ); so we need to stop that or else it will not load our translation editor */ add_filter('tcb_allow_landing_page_edit', 'trp_thrive_arhitect_compatibility'); add_filter('tcb_is_editor_page', 'trp_thrive_arhitect_compatibility');//this is for Thrive theme function trp_thrive_arhitect_compatibility($bool) { if (isset($_REQUEST['trp-edit-translation'])) $bool = false; return $bool; } // do not redirect the URL's that are used inside Thrive Architect Editor add_filter( 'trp_allow_language_redirect', 'trp_thrive_no_redirect_in_editor', 10, 3 ); function trp_thrive_no_redirect_in_editor( $allow_redirect, $needed_language, $current_page_url ){ if ( strpos($current_page_url, 'tve=true&tcbf')!== false ){ return false; } return $allow_redirect; }; // skip the URL's that are used inside Thrive Architect Editor as they are stripped of parameters in certain cases and the editor isn't working. add_filter('trp_skip_url_for_language', 'trp_thrive_skip_language_in_editor', 10, 2); function trp_thrive_skip_language_in_editor($skip, $url){ if ( strpos($url, 'tve=true&tcbf') !== false ){ return true; } return $skip; } /** * Compatibility with the RECON gateway for woocommerce. We must not send the "trp-form-language" hidden field in the post request to the gateway */ if( class_exists('WC_Gateway_RECON') ) { add_filter('trp_form_inputs', 'trp_recon_gateway_compatibility', 10, 4); function trp_recon_gateway_compatibility($input, $trp_language, $slug, $row) { if (isset($row->attr['name']) && $row->attr['name'] === 'checkout') { $input = ''; } return $input; } } /* * Add compatibility for tribe events that crash the JS for the input not having an error field assigned to it. * It expects a div with a class="error" like so: <div class="tribe-common-b3 tribe-tickets__form-field-description tribe-common-a11y-hidden error">Your first and last names are required</div> */ if (class_exists('Tribe__Tickets__Main')){ add_filter( 'trp_form_inputs', 'trp_tribe_tickets_form_compatibility', 10, 4 ); } function trp_tribe_tickets_form_compatibility( $input, $trp_language, $slug, $row ) { if ( isset( $row->attr['class'] ) ) { $classes = explode( ' ', $row->attr['class'] ); foreach ( $classes as $class ) { if ( strpos( $class, 'tribe-tickets' ) !== false ) { $input = ''; break; } } } return $input; } /** * Compatibility with Classified Listing plugin Search in secondary language */ // do not return inline autocomplete because when clicking the results, the input is filled with original title instead of translated add_filter( 'rtcl_inline_search_autocomplete_args', 'trp_rtcl_autocomplete_search_results', 10, 2 ); function trp_rtcl_autocomplete_search_results( $args, $request ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); if ( $TRP_LANGUAGE !== $settings['default-language'] ) { $args['post__in'] = array('1'); return $args; } return $args; } // Otherwise trp-post-container is not added add_action( 'wp_body_open', 'trp_overrule_main_query_condition', 10, 2 ); function trp_overrule_main_query_condition(){ if ( class_exists('Rtcl') ) { add_filter( 'trp_wrap_with_post_id_overrule', '__return_false' ); } } /** * Otherwise trp-post-container is stripped * * Applied this solution permanently. It's problematic with Elementor and WooCommerce too. */ add_filter( 'wp_kses_allowed_html', 'trp_prevent_kses_from_stripping_trp_post_container', 10, 2 ); function trp_prevent_kses_from_stripping_trp_post_container( $allowedposttags, $context ) { if ( $context === 'post' ){ $allowedposttags['trp-post-container'] = array( 'data-trp-post-id' => true ); } return $allowedposttags; } // Filter search results to show secondary language results add_action('rtcl_listing_query', 'trp_rtcl_search_results', 10, 2); function trp_rtcl_search_results ($q, $t){ if ( empty( $q->get('s')) ){ return; } global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); if ( $TRP_LANGUAGE !== $settings['default-language'] ) { $trp_search = $trp->get_component( 'search' ); $search_result_ids = $trp_search->get_post_ids_containing_search_term($q->get('s'), null); $q->set('s', ''); if ( empty($search_result_ids)){ $search_result_ids = array(0); } $q->set('post__in', $search_result_ids ); } } /* * Compatibility with AddToAny Share Buttons * * Skip detection by translate-dom-changes of the url change when hitting the share button * */ add_filter( 'trp_skip_selectors_from_dynamic_translation', 'trp_add_to_any_skip_dynamic_translation' ); function trp_add_to_any_skip_dynamic_translation( $skip_selectors ){ if( function_exists( 'A2A_SHARE_SAVE_init' ) ) { $add_skip_selectors = array( '.addtoany_list' ); return array_merge( $skip_selectors, $add_skip_selectors ); } return $skip_selectors; } /* * Compatibility with Uncode theme menu on mobile * * Skip detection by translate-dom-changes of the url change when hitting the menu * */ add_filter( 'trp_skip_selectors_from_dynamic_translation', 'trp_uncode_skip_dynamic_translation' ); function trp_uncode_skip_dynamic_translation( $skip_selectors ){ if( function_exists( 'uncode_setup' ) ) { $add_skip_selectors = array( '.menu-horizontal .menu-smart' ); return array_merge( $skip_selectors, $add_skip_selectors ); } return $skip_selectors; } /* * Compatibility with PDF Embedder Premium Secure * * Skips link processing if ?pdfemb-serveurl is in the url * */ // first we need to disable adding the language to home_url() because the plugin is using it to construct a url. add_filter( 'trp_home_url', 'trp_skip_home_url_processing_for_pdfemb_server_url', 10, 5 ); function trp_skip_home_url_processing_for_pdfemb_server_url( $new_url, $abs_home, $TRP_LANGUAGE, $path, $url ){ if( class_exists( 'core_pdf_embedder' ) ){ $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $list_of_functions = array( 'modify_pdfurl' ) ; if( !empty( $callstack_functions ) ) { foreach ( $callstack_functions as $callstack_function ) { if ( in_array( $callstack_function['function'], $list_of_functions ) ) { $new_url = $url; break; } } } } return $new_url; } // and after that we need to make sure we're not adding the language when we process the url's in the page. add_filter( 'trp_skip_url_for_language', 'trp_skip_link_processing_for_pdfemb_server_url', 10, 2 ); function trp_skip_link_processing_for_pdfemb_server_url( $skip, $url ){ if( strpos($url, '?pdfemb-serveurl') !== false ) { $skip = true; } return $skip; } /** * Add compatibility with blockquote tweet button in elementor * if the quote has one parameter it will be automatically translated, if not then you need to use conditional language shortcodes */ add_filter( 'wp_parse_str', 'trp_elementor_blockquote_translate_tweet_button' ); function trp_elementor_blockquote_translate_tweet_button( $array ){ if( array_key_exists( 'text', $array ) ){ if ( version_compare( PHP_VERSION, '5.4.0', '>=' ) ) { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15);//set a limit if it is supported to improve performance } else{ $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } if( !empty( $callstack_functions ) ) { foreach ($callstack_functions as $callstack_function) { if ( $callstack_function['function'] === 'render_content' ) { //enable conditional language shortcode $array['text'] = do_shortcode( $array['text'] ); //try to eliminate the author from the text before we try to translate it $tweet_link_text = $array['text']; $tweet_link_text = explode( ' — ', $tweet_link_text ); if( count( $tweet_link_text ) > 1 ){ $quote_author = array_pop( $tweet_link_text ); } $tweet_link_text = implode( ' — ', $tweet_link_text ); //try and translate the text $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); $array['text'] = $translation_render->translate_page($tweet_link_text); //add author if it was eliminated if(!empty($quote_author) ) $array['text'] = $array['text'] . ' — ' . $translation_render->translate_page($quote_author); break; } } } } return $array; } /** * Add compatibility with blockquote tweet button in elementor pro that had the link broken, it doubled the language in the url */ if( function_exists('elementor_pro_load_plugin') ) { add_filter('trp_home_url', 'trp_elementor_blockquote_tweet_button_url', 10, 5); function trp_elementor_blockquote_tweet_button_url($new_url, $abs_home, $TRP_LANGUAGE, $path, $url) { if (version_compare(PHP_VERSION, '5.4.0', '>=')) { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15);//set a limit if it is supported to improve performance } else { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } $list_of_functions = array('render'); if (!empty($callstack_functions)) { foreach ($callstack_functions as $callstack_function) { if (in_array($callstack_function['function'], $list_of_functions) && isset($callstack_function['class']) && $callstack_function['class'] === 'ElementorPro\Modules\Blockquote\Widgets\Blockquote') { return $url; } } } return $new_url; } } /** * Add compatibility with Elementor so we allow conditional shortcodes in post excerpt * this allows twitter button to have a translated text */ if( defined('ELEMENTOR_VERSION') ) { add_filter('the_post', 'trp_elementor_translate_tweet_button_excerpt'); function trp_elementor_translate_tweet_button_excerpt($post){ if (!empty($post->post_excerpt)) { $post->post_excerpt = do_shortcode($post->post_excerpt); } return $post; } } /** * Add current-menu-item css class to menu items in WP Nav Menu * * Don't add them to language switcher items. * Always adds them to secondary languages. * Add them to default language if Use subdirectory is set to Yes */ add_filter('wp_nav_menu_objects', 'trp_add_current_menu_item_css_class'); function trp_add_current_menu_item_css_class( $items ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); add_filter('pre_get_posts', 'trp_the_event_calendar_set_query_to_true', 2, 1); foreach( $items as $item ){ if ( !( $TRP_LANGUAGE === $settings['default-language'] && isset( $settings['add-subdirectory-to-default-language']) && $settings['add-subdirectory-to-default-language'] !== 'yes' ) && !in_array( 'current-menu-item', $item->classes ) && !in_array( 'menu-item-object-language_switcher', $item->classes ) && ( !empty($item->url) && $item->url !== '#') ){ $url_for_language = $url_converter->get_url_for_language( $TRP_LANGUAGE, $item->url ); $url_for_language = strpos( $url_for_language, '#' ) ? substr( $url_for_language, 0, strpos( $url_for_language, '#' ) ) : $url_for_language; $cur_page_url = set_url_scheme( untrailingslashit( $url_converter->cur_page_url() ) ); if ( untrailingslashit( $url_for_language ) == untrailingslashit( $cur_page_url ) ){ $item->classes[] = 'current-menu-item'; } } if(!in_array('current-language-menu-item', $item->classes) && in_array('menu-item-object-language_switcher', $item->classes)){ $current_language = $url_converter->get_lang_from_url_string($item->url); if($current_language == null){ $current_language = $settings['default-language']; } if($current_language == $TRP_LANGUAGE){ $item->classes[] = 'current-language-menu-item'; } } } remove_filter('pre_get_posts', 'trp_the_event_calendar_set_query_to_true', 2); return $items; } /** * Function needed to set tribe_suppress_query_filters to false in query in order to avoid errors with The Event Calendar * * @param $query * @return mixed */ function trp_the_event_calendar_set_query_to_true($query){ $query->set('tribe_suppress_query_filters', false); return $query; } /** * Compatibility with xstore theme ajax search on other languages than english and when automatic translation was on * a class from the search form got translated */ if( function_exists('initial_ETC') ) { add_filter('trp_skip_gettext_processing', 'trp_exclude_xstore_search_class', 999, 4); function trp_exclude_xstore_search_class($return, $translation, $text, $domain){ if (version_compare(PHP_VERSION, '5.4.0', '>=')) { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15);//set a limit if it is supported to improve performance } else { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } $list_of_functions = array(); if (!empty($callstack_functions)) { foreach ($callstack_functions as $callstack_function) { $list_of_functions[] = $callstack_function['function']; } } if (in_array('esc_attr_e', $list_of_functions) && in_array('header_content_callback', $list_of_functions)) return true; return $return; } } /** * Add compatibility with Business Directory Plugin that requires to update permalinks when we are on other languages or else it will throw a 404 error */ if( defined( 'WPBDP_PLUGIN_FILE' ) ) { add_filter('trp_prevent_permalink_update_on_other_languages', 'trp_prevent_permalink_update_on_other_languages'); function trp_prevent_permalink_update_on_other_languages($bool){ return false; } } /** * Exclude some problematic gettext strings from being translated */ add_filter('trp_skip_gettext_processing', 'trp_exclude_problematic_gettext_strings', 999, 4 ); function trp_exclude_problematic_gettext_strings ( $return, $translation, $text, $domain ){ $exclude_strings = array( // some examples on how to use: (domain is optional) //array( 'string' => 'Some Text', 'domain' => 'some-domain' ) //array( 'string' => 'Some Other Text' ) array( 'string' => 'Ștefan Vodă' )//this is translated by Google Translate into german as "Fan Vod" and the quotes create problems ); foreach( $exclude_strings as $string_details ){ if( $text === $string_details['string'] ){ if( empty( $string_details['domain'] ) ) return true; else if( $domain === $string_details['domain'] ) return true; } } return $return; } /** * Compatibility with WooCommerce API * * Particularly with Paypal and myPOS checkout * * When the IPN request comes do not translate anything outputted. * MyPOS expects "OK" string which does not have to be translated as regular string. * Paypal had trpst in the details sent. */ add_action( 'woocommerce_api_request', 'trp_woo_wc_api_handle_api_request', 1 ); function trp_woo_wc_api_handle_api_request( ){ add_filter( 'trp_skip_gettext_processing', '__return_true' ); add_filter( 'trp_stop_translating_page', '__return_true' ); } /** * Compatibility with WooCommerce Min/Max Quantities that wrongly add data-quantity attribute two times on the link and our parser breaks this. The update of the parser to 1.9.1 should render this redundant */ if( class_exists('WC_Min_Max_Quantities') ) { add_filter('woocommerce_loop_add_to_cart_link', 'trp_check_duplicate_quantity_attribute_on_link', 99, 2); function trp_check_duplicate_quantity_attribute_on_link($html, $product){ $occurrences = substr_count($html, " data-quantity="); if ($occurrences > 1) { $html = preg_replace('/(data-quantity="\d+"(?!.*data-quantity="\d+"))/', '', $html, 1); } return $html; } } /** * Add here compatibility with search plugins */ add_filter('trp_force_search', 'trp_force_search' ); function trp_force_search( $bool ){ //force search in xstore theme ajax search if( isset( $_REQUEST['action'] ) && $_REQUEST['action'] === 'etheme_ajax_search' ) $bool = true; //compatibility with WooCommerce Product Search plugin if( class_exists('WooCommerce_Product_Search_Service') ) { if (isset($_REQUEST['action']) && $_REQUEST['action'] === 'product_search') $bool = true; } return $bool; } /** * Compatibility with WooCommerce Product Search plugin * The only way I found is to hijack the cache in the get_post_ids_for_request() function from WooCommerce_Product_Search_Service class. It comes with a loss of performance */ if( class_exists('WooCommerce_Product_Search_Service') ) { add_filter('woocommerce_product_search_request_search_query', 'trp_woocommerce_product_search_compatibility'); function trp_woocommerce_product_search_compatibility($search_query) { global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component('settings'); $settings = $trp_settings->get_settings(); if ($TRP_LANGUAGE !== $settings['default-language']) { $title = isset($_REQUEST[WooCommerce_Product_Search_Service::TITLE]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::TITLE]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_TITLE; $excerpt = isset($_REQUEST[WooCommerce_Product_Search_Service::EXCERPT]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::EXCERPT]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_EXCERPT; $content = isset($_REQUEST[WooCommerce_Product_Search_Service::CONTENT]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::CONTENT]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_CONTENT; $tags = isset($_REQUEST[WooCommerce_Product_Search_Service::TAGS]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::TAGS]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_TAGS; $sku = isset($_REQUEST[WooCommerce_Product_Search_Service::SKU]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::SKU]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_SKU; $categories = isset($_REQUEST[WooCommerce_Product_Search_Service::CATEGORIES]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::CATEGORIES]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_CATEGORIES; $attributes = isset($_REQUEST[WooCommerce_Product_Search_Service::ATTRIBUTES]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::ATTRIBUTES]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_ATTRIBUTES; $variations = isset($_REQUEST[WooCommerce_Product_Search_Service::VARIATIONS]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::VARIATIONS]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_VARIATIONS; $min_price = isset($_REQUEST[WooCommerce_Product_Search_Service::MIN_PRICE]) ? WooCommerce_Product_Search_Service::to_float($_REQUEST[WooCommerce_Product_Search_Service::MIN_PRICE]) : null;//phpcs:ignore $max_price = isset($_REQUEST[WooCommerce_Product_Search_Service::MAX_PRICE]) ? WooCommerce_Product_Search_Service::to_float($_REQUEST[WooCommerce_Product_Search_Service::MAX_PRICE]) : null;//phpcs:ignore if ($min_price !== null && $min_price <= 0) { $min_price = null; } if ($max_price !== null && $max_price <= 0) { $max_price = null; } if ($min_price !== null && $max_price !== null && $max_price < $min_price) { $max_price = null; } $on_sale = isset($_REQUEST[WooCommerce_Product_Search_Service::ON_SALE]) ? intval($_REQUEST[WooCommerce_Product_Search_Service::ON_SALE]) > 0 : WooCommerce_Product_Search_Service::DEFAULT_ON_SALE; //this is how they get the key in the method get_cache_key() $cache_key = md5(implode('-', array( 'title' => $title, 'excerpt' => $excerpt, 'content' => $content, 'tags' => $tags, 'sku' => $sku, 'categories' => $categories, 'attributes' => $attributes, 'variations' => $variations, 'search_query' => $search_query, 'min_price' => $min_price, 'max_price' => $max_price, 'on_sale' => $on_sale ))); $trp_search = $trp->get_component('search'); $include = $trp_search->get_post_ids_containing_search_term($search_query, null); wp_cache_set($cache_key, $include, WooCommerce_Product_Search_Service::POST_CACHE_GROUP, WooCommerce_Product_Search_Service::CACHE_LIFETIME); } return $search_query; } } /** * Strip tags manually from a problematic string coming from the My Listing theme */ add_action('init', 'trp_mylisting_hook_exclude_string' ); function trp_mylisting_hook_exclude_string(){ if( class_exists( 'MyListing\\App' ) ){ add_filter('gettext_with_context', 'trp_mylisting_exclude_string', 101, 4 ); } } function trp_mylisting_exclude_string( $translation, $text, $context, $domain ){ if( $domain == 'my-listing' && $text == 'my-listings' ) $translation = TRP_Translation_Manager::strip_gettext_tags( $translation ); return $translation; } /** * Compatibility with Google Site Kit plugin * * Problem was that Site Kit dashboard kept disconnecting, thinking the url must have changed. * * To replicate, set TP option "Add language to subdirectory" Yes and use Complianz plugin, wizard step 2, * to perform re-scan of cookies. This triggered the disconnect. */ add_filter('googlesitekit_canonical_home_url', 'trp_googlesitekit_compatibility_home_url' ); function trp_googlesitekit_compatibility_home_url( $url ) { $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); return $url_converter->get_abs_home(); } /** * Compatibility with WPEngine hosting * * Detect and handle query length limiting feature of WPEngine. Without this check, the query returns no results as if * there were no translations found. This results in duplicate row inserting and unnecessary automatic translation * usage. */ add_filter('trp_get_existing_translations', 'trp_wpengine_query_limit_check', 10, 3 ); function trp_wpengine_query_limit_check($dictionary, $prepared_query, $strings_array){ if ( function_exists('is_wpe') && ( !defined ('WPE_GOVERNOR') || ( defined ('WPE_GOVERNOR') && WPE_GOVERNOR != false ) ) && strlen($prepared_query) >= 16000 ){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $trp_query->maybe_record_automatic_translation_error(array( 'details' => esc_html__("Detected long query limitation on WPEngine hosting. Some large pages may appear untranslated. You can remove limitation by adding the following to your site’s wp-config.php: define( 'WPE_GOVERNOR', false ); ", 'translatepress-multilingual')), true ); return false; }else{ return $dictionary; } } /** * Compatibility with Dokan plugin * * Dates are run through gettext and the this breaks further functions because of wrappings */ if ( class_exists('WeDevs_Dokan')) { add_filter( 'trp_skip_gettext_processing', 'trp_exclude_dokan_date_strings', 20, 4 ); } function trp_exclude_dokan_date_strings($return, $translation, $text, $domain) { $skip_text = array('Y/m/d g:i:s A', 'Y/m/d'); if ($domain == 'dokan' && in_array( $text, $skip_text) ){ return true; } return $return; } function trp_add_language_to_pms_wppb_restriction_redirect_url( $redirect_url ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); return $url_converter->get_url_for_language( $TRP_LANGUAGE, $redirect_url, '' ); } if( defined( 'PMS_VERSION' ) ) add_filter( 'pms_restricted_post_redirect_url', 'trp_add_language_to_pms_wppb_restriction_redirect_url' ); if( function_exists( 'wppb_plugin_init' ) ) add_filter( 'wppb_restricted_post_redirect_url', 'trp_add_language_to_pms_wppb_restriction_redirect_url' ); /** * Compatibility with wp-Typography * The $filters array is set to empty, so it does not affect the strings anymore in the function trp_remove_filters_wp_typography. * Then it is reset with a higher priority by calling the function process() inside the trp_add_filters_wp_typography function. */ if(class_exists('WP_Typography')) { add_action('plugins_loaded', 'trp_wp_typography'); } function trp_wp_typography(){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component('settings'); $settings = $trp_settings->get_settings(); if ($TRP_LANGUAGE !== $settings['default-language']) { add_filter( 'typo_content_filters', 'trp_remove_filters_wp_typography' ); add_filter( 'trp_translated_html', 'trp_add_filters_wp_typography', 100000, 1 ); add_filter('run_wptexturize', '__return_null', 11); } } function trp_remove_filters_wp_typography($filters){ $filters = []; return $filters; } function trp_add_filters_wp_typography($final_html){ $wpt= WP_Typography::get_instance(); add_filter('run_wptexturize', '__return_false', 11); $final_html = $wpt->process($final_html, $is_title = false, $force_feed = false, null ); return $final_html; } /* * Compatibility with All In One SEO Pack */ if(function_exists('aioseo')){ if (version_compare(PHP_VERSION, '5.4.0', '>=')) { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 15);//set a limit if it is supported to improve performance } else { $callstack_functions = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); } if (!empty($callstack_functions)) { foreach ( $callstack_functions as $callstack_function ) { if ( isset($callstack_function["object"]->{"callbacks"}) ) { foreach ($callstack_function["object"]->{"callbacks"}[10] as $key=>$value){ if(strpos($key, 'actionScheduler')){ if(array_key_exists('breadcrumbs_archiveFormat', $callstack_function["object"]->{"callbacks"}[10][ $key ]["function"][0]->{"options"}->{"localized"} )) { add_action( 'trp_before_running_hooks', 'trp_AIOSEO_remove_gettext_hooks', 10, 1 ); } } } } } } } function trp_AIOSEO_remove_gettext_hooks($trp_loader){ $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); $trp_loader->remove_hook( 'the_title', 'wrap_with_post_id', $translation_render ); } /** * Compatibility with Elementor/Divi/WPBakery when "Use a subdirectory for the default language" is set to Yes * Making sure the page edited with Elementor/Divi/WPBakery appears in the default language instead of the first language from the Language list */ add_filter( 'trp_needed_language', 'trp_page_builders_compatibility_with_subdirectory_for_default_language', 10, 4 ); function trp_page_builders_compatibility_with_subdirectory_for_default_language( $needed_language, $lang_from_url, $settings, $trp) { if ( ( ( isset( $_GET['action'] ) && $_GET['action'] === 'elementor' ) || isset( $_GET['elementor-preview'] ) ) //Elementor || ( ( isset( $_GET['et_fb'] ) && $_GET['et_fb'] === '1' ) && ( isset( $_GET['PageSpeed'] ) && $_GET['PageSpeed'] === "off" ) ) //Divi || ( ( isset( $_GET['vc_action'] ) && $_GET['vc_action'] === 'vc_inline' ) || ( isset( $_GET['vc_editable'] ) && $_GET['vc_editable'] === 'true' ) ) ) { //WPBakery $needed_language = $settings['default-language']; } return $needed_language; } /** * Compatibility with Give WP plugin. * * When automatic translation is active and we are on secondary language, clicking the Donate button will not redirect you to the confirmation page. * This happens because "Give WP" expects an admin ajax request to return "success" but TP translates it in another language. */ add_filter( 'trp_stop_translating_page', 'trp_give_wp_compatibility', 10, 2 ); function trp_give_wp_compatibility( $bool, $output ){ if ( isset( $_REQUEST['give_ajax'] ) && $_REQUEST['give_ajax'] == 'true' ) { return true; } return $bool; } /* * Divi is filtering the locale which is in turn accessed on every gettext call by TranslatePress. Together these two things slow down the site to 20+ seconds * The fix is to remove the Divi hook and replace it with another one that caches the result, so it's fast. * Ideally this is a fix Divi should do, however, it negatively impacts TP, so we're doing it for them. */ add_filter('locale', 'trp_remove_divi_locale_filter', 999999); function trp_remove_divi_locale_filter($lang){ remove_filter( 'locale', 'et_divi_maybe_change_frontend_locale' ); return $lang; } /** * This function, checks if the Divi plugin is not installed first. * If it's not installed, it returns the original locale. * If it is installed, it will then access the theme options and check if the 'divi_disable_translations' is found in the cache. * If the value is not found in the cache, it retrieves it from the database using get_option('et_divi'). * If the value retrieved from the database is also false, it sets it to 'not_set' in the cache. * Then, it checks if the value of theme_options is 'not_set'. If it is, it returns the input locale without making any changes. * If it is, it returns 'en_US', otherwise it returns the original locale. **/ function trp_et_divi_maybe_change_frontend_locale( $locale ) { if ( !defined( 'ET_CORE_PATH' ) ) { return $locale; } $cache_key = 'et_divi_option'; $theme_options = wp_cache_get( $cache_key ); $option_name = 'divi_disable_translations'; if (false === $theme_options) { $theme_options = get_option( 'et_divi' ); if ( false === $theme_options ) { $theme_options = 'not_set'; } wp_cache_set( $cache_key, $theme_options ); } if ( 'not_set' === $theme_options ) { return $locale; } if ( !isset( $theme_options[ $option_name ] ) ) { return $locale; } if ( 'on' === $theme_options[ $option_name ] ) { return 'en_US'; } return $locale; } add_filter( 'locale', 'trp_et_divi_maybe_change_frontend_locale' ); /* * Register old advanced settings if they are checked */ add_action('admin_init', 'trp_register_old_advanced_settings'); function trp_register_old_advanced_settings( $bool ) { $option = get_option('trp_advanced_settings', true); if (isset($option['fix_broken_html']) && $option['fix_broken_html'] === 'yes') { add_filter('trp_register_advanced_settings', 'trp_register_fix_broken_html', 50); } } add_filter('trp_ald_popup_options_array', 'trp_keep_no_popup_setting_for_redirect_directly', 10, 1); function trp_keep_no_popup_setting_for_redirect_directly($array_popup_options){ $option_ald = get_option('trp_ald_settings', true); if (isset($option_ald['popup_option']) && $option_ald['popup_option'] !== 'no_popup' && version_compare(TRP_IN_ALD_PLUGIN_VERSION, '1.1', '>=') ) { unset($array_popup_options['no_popup']); } return $array_popup_options; } /** * Prevent trp-sortable-languages.js script from running * * We have merged the code from trp-sortable-languages.js in trp-back-end-script.js in TP ver. 2.5.3 but we still need trp-sortable-languages for backwards compatibility * If the version of TranslatePress is at least 2.5.3, prevent the OLD trp-sortable-languages.js script from running * Newer versions of TP Pro have a super simplified trp-sortable-languages.js file that is required, but it's loaded from a different hook. So this is still needed here. */ add_action( 'trp_before_running_hooks', 'trpc_prevent_sortable_script_from_loading' ); function trpc_prevent_sortable_script_from_loading( $trp_loader ){ if ( version_compare(TRP_PLUGIN_VERSION, '2.5.4', '>=' ) ) { $trp_loader->remove_hook( 'admin_enqueue_scripts', 'enqueue_sortable_language_script' ); } } add_filter('trp_advanced_tab_add_element', 'trp_compatibility_for_adl_127_version', 20); function trp_compatibility_for_adl_127_version( $settings ){ foreach ( $settings as $key => $setting ) { if ( $setting['name'] === 'automatic_user_language_detection' && !isset( $setting['id'] ) ) { $settings[$key]['id'] = 'ald_settings'; } } return $settings; } add_action( 'before_woocommerce_init', function() { if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', TRP_PLUGIN_DIR . 'index.php', true ); } } ); /** * Compatibility with RankMath */ add_filter( 'rank_math/analytics/get_translated_objects', 'trp_rank_math_get_translated_items', 10, 1 ); function trp_rank_math_get_translated_items( $post_id ) { if ( ! class_exists( 'TRP_Translate_Press' ) || !function_exists('trp_translate')) { return $post_id; } $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $settings_component = $trp->get_component( 'settings' ); $trp_settings = $settings_component->get_settings(); // Needed because adding language slug in urls is not performed by default in admin area. add_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' ); $permalink = get_permalink( $post_id ); $translated_items = []; $languages = $trp_settings['publish-languages']; foreach ( $languages as $language ) { $url = esc_url( $url_converter->get_url_for_language( $language, $permalink, '' ) ); /** * Google API and get_permalink sends URL Encoded strings so we need * to urldecode in order to get them to match with whats saved in DB. */ $parse_url = wp_parse_url( urldecode( $url ) ); if ( ! $parse_url ) { continue; } if ( empty( $parse_url['path'] ) ) { continue; } $title = get_the_title( $post_id ); // Get translated title, if possible. if( $language != $trp_settings['default-language'] ){ $title = trp_translate( $title, $language, false ); } // Push translated URL into array. array_push( $translated_items, [ 'url' => $parse_url['path'], 'title' => $title, ] ); } // Revert to default functionality. remove_filter( 'trp_add_language_to_home_url_check_for_admin', '__return_false' ); return $translated_items; } /** *The manually translated slug being overwritten by automatic translation was caused by a conflict with the events calendar. In the function * include_slug_for_machine_translation(add-ons-advanced/seo-pack/includes/class-slug-manager.php, line 431), line 499 we have the line * $translated_base_slug = $this->get_translated_rewrite_base_slug( $post_type_string, $language_code, false );.Because the events calendar * adds a slug called ‘tribe_events’ this was registered as $post_type_string, and this slug does not exist amongst the existing post-type base * slugs saved in DB, so it was returning false and the slug was added to the translatable_information array so it was sent to automatic translation. * The client translated the slug for Portuguese in English so in line 502, $original_base_slug = $this->get_rewrite_base_slug( $post_type_object, * $post_type_string );, the translated slug was returned and passed through automatic translation which returned the slug in portugese and * overwrriten the human translated slug by the client. * * As a solution, if the events calendar is active, we use a filter of post type base slugs that should not be passed through automatic translation. */ if (class_exists("Tribe__Events__Adjacent_Events")) { add_filter('trp_filter_post_type_base_slugs_from_automatic_translation', 'trp_stop_automatic_translation_for_certain_post_type_base_slugs', 10, 2); } function trp_stop_automatic_translation_for_certain_post_type_base_slugs( $bool, $post_type_base_slug_to_avoid ) { $array_of_post_type_base_slugs_that_should_not_be_passed_through_automatic_translation = array("tribe_events"); if (in_array($post_type_base_slug_to_avoid, $array_of_post_type_base_slugs_that_should_not_be_passed_through_automatic_translation)){ $bool = false; } return $bool; } /** * Compatibility with Duplicate Page plugin */ if (class_exists("duplicate_page")) { add_action('save_post', 'trp_add_hook_for_delete', 10, 1); } function trp_add_hook_for_delete( $post_id ) { global $trp_post_id_for_deleting_duplicate_posts_slugs_from_db; if (did_action('admin_action_dt_duplicate_post_as_draft')) { $trp_post_id_for_deleting_duplicate_posts_slugs_from_db = $post_id; add_action('shutdown', 'trp_delete_slug_translation_from_duplicated_pages' ); } } function trp_delete_slug_translation_from_duplicated_pages() { global $trp_post_id_for_deleting_duplicate_posts_slugs_from_db; global $wpdb; $sql = $wpdb->prepare("DELETE FROM $wpdb->postmeta WHERE post_id = %d AND (meta_key LIKE %s OR meta_key LIKE %s);", $trp_post_id_for_deleting_duplicate_posts_slugs_from_db, '%'. $wpdb->esc_like('trp_automatically_translated_slug') .'%', '%'. $wpdb->esc_like('trp_translated_slug') .'%'); $wpdb->query( $sql ); unset( $GLOBALS['trp_post_id_for_deleting_duplicate_posts_slugs_from_db'] ); } /** * Exclude Query Monitor gettext strings from being processed */ if ( class_exists( 'QueryMonitor' ) ) { add_filter( 'trp_skip_gettext_processing', 'trp_exclude_query_monitor_strings', 10, 4 ); add_filter( 'trp_no_translate_selectors', 'trp_exclude_query_monitor_selector' ); add_filter( 'trp_skip_selectors_from_dynamic_translation', 'trp_exclude_query_monitor_selector' ); } function trp_exclude_query_monitor_strings( $bool, $translation, $text, $domain ){ if ( trim( $domain ) === 'query-monitor' ) return true; return $bool; } /** * Exclude Query Monitor selector from being translated */ function trp_exclude_query_monitor_selector( $skip_selectors ) { $skip_selectors[] = '#query-monitor-main'; return $skip_selectors; } /** * Compatibility with Complianz plugin blocking trp_data script */ // Whitelisting inline script for Complianz add_filter ( 'cmplz_service_category', 'trp_cmplz_whitelist_script', 10 , 3 ); function trp_cmplz_whitelist_script( $category, $total_match, $found ){ if ( $found && false !== strpos( $total_match, 'trp-dynamic-translator-js-extra' ) ) { $category = 'functional'; // add cmplz-script for Marketing and cmplz-stats for Statistics } return $category; } /** * Compatibility with Fluent Forms * Do not Translate Fluent Forms ajax submit calls for uploaded media */ add_filter('trp_stop_translating_page', 'trp_do_not_translate_fluent_form_submit', 1000000, 2); function trp_do_not_translate_fluent_form_submit($translate, $output){ if ( isset( $_POST['action'] ) && $_POST['action'] == 'fluentform_file_upload'){ return true; } return $translate; } /** * Do not Translate WooCommerce Bookings */ add_filter('trp_stop_translating_page', 'trp_do_not_translate_woo_bookings_cost_calculator', 10, 2); function trp_do_not_translate_woo_bookings_cost_calculator($translate, $output){ if ( isset( $_POST['action'] ) && $_POST['action'] == 'wc_bookings_calculate_costs'){ return true; } return $translate; } add_action('init', 'trp_woo_bookings_gettext_filter'); function trp_woo_bookings_gettext_filter(){ if ( isset( $_POST['action'] ) && $_POST['action'] == 'wc_bookings_calculate_costs'){ add_filter('gettext', 'trp_woo_bookings_exclude_gettext_strings', 1000, 3 ); } } function trp_woo_bookings_exclude_gettext_strings($translation, $text, $domain){ if ( isset( $_POST['action'] ) && $_POST['action'] == 'wc_bookings_calculate_costs' ){ return TRP_Translation_Manager::strip_gettext_tags( $translation ); } return $translation; } /** * Add support for the content feed and excerpt feed so they get translated. * They can be manually translated from String Translation -> Regular. * For most contents, they will work with content the client already sees in the front-end. */ add_filter( 'the_excerpt_rss', 'trp_translate_the_excerpt_rss', 10, 1); function trp_translate_the_excerpt_rss( $output ){ $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); return $translation_render->translate_page($output); }; add_filter( 'the_content_feed', 'trp_translate_the_content_feed', 10, 2); function trp_translate_the_content_feed( $content, $feed_type ){ $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); return $translation_render->translate_page($content); }; // Add a filter to stop translating pages for PDF files associated with WP Job Board Pro add_filter('trp_stop_translating_page', 'trp_block_wpjb_pro_pdf_translation', 10, 2); /** * Blocks the translation of PDF files generated by WP Job Board Pro. * * This function checks if the current request is for a WP Job Board Pro AJAX call * related to PDF files. If so, it prevents these PDF files from being translated by TranslatePress. * * @param bool $bool The initial state determining if the page should be translated. * @param mixed $output The output or content potentially subject to translation. * @return bool True if the translation should be blocked for the current request, otherwise returns the original state. */ function trp_block_wpjb_pro_pdf_translation( $bool, $output ) { // Check if the current request is an AJAX call related to WP Job Board Pro PDF files if ( !empty( $_REQUEST['wjbp-ajax'] ) ) { // Block the translation of the PDF file by returning true return true; } // Return the initial state if the condition is not met return $bool; } // Compatibility with Brikk theme if ( function_exists( 'brikk_utilities_load_textdomain' ) ) { add_filter( 'trp_skip_form_action', 'trp_exclude_brikk_theme_form_action', 10, 2 ); } function trp_exclude_brikk_theme_form_action( $skip_this_action, $form_action ) { if ( $form_action == "1" || $form_action == "0" ) return true; return $skip_this_action; } /** * Compatibility with PWA plugin https://wordpress.org/plugins/pwa/ */ add_filter('trp_stop_translating_page', 'trp_do_not_translate_service_worker_pages', 10, 2); function trp_do_not_translate_service_worker_pages($translate, $output){ if( isset( $_SERVER['REQUEST_URI'] ) ) $request_uri = esc_url_raw( $_SERVER['REQUEST_URI'] ); else $request_uri = ''; if( strpos( $request_uri, 'wp.serviceworker' ) !== false ){ return true; } return $translate; } if ( class_exists( 'WooCommerce' ) ){ add_action('plugins_loaded', 'trp_check_if_woo_language_po_file_exists'); } function trp_check_if_woo_language_po_file_exists() { $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); if ( $settings['default-language'] != 'en_US' && !file_exists(WP_LANG_DIR . "/plugins/woocommerce-{$settings['default-language']}.po")){ trp_download_woo_po_file_for_default_language( $settings['default-language'] ); } } function trp_download_woo_po_file_for_default_language( $default_language ){ $path_for_po_file_in_the_requested_language = trp_get_translation_woo_po_files_url( $default_language ); if ( $path_for_po_file_in_the_requested_language ) { $language_pack_url = $path_for_po_file_in_the_requested_language; $save_to = WP_LANG_DIR . "/plugins"; file_put_contents( "woocommerce-{$default_language}.zip", file_get_contents( $path_for_po_file_in_the_requested_language) ); $zip = new ZipArchive; $res = $zip->open( "woocommerce-{$default_language}.zip"); if ( $res === TRUE ) { $zip->extractTo( $save_to, array( "woocommerce-{$default_language}.po" ) ); $zip->close(); return true; } } return false; } function trp_get_translation_woo_po_files_url( $default_language ) { $translations_api_url = "https://api.wordpress.org/translations/plugins/1.0/?slug=woocommerce"; $response = wp_remote_get($translations_api_url, array('timeout' => 300)); if (is_wp_error($response)) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (empty($data['translations'])) { return false; } foreach ($data['translations'] as $translation) { if ($translation['language'] === $default_language) { return $translation['package']; } } return false; } /** * Hooked to trp_get_url_for_language, used in case the pro version of TP was not updated or legacy SEO Pack is in use * * @param $new_url * @param $url * @param $language * @return mixed|string|null */ function trp_get_url_for_language_backwards_compatibility( $new_url, $url, $language ){ global $trp_current_url_term_slug, $trp_current_url_taxonomy; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $upgrade = new TRP_Upgrade( $settings ); if ( $upgrade->is_seo_pack_minimum_version_met() && ( !isset( $settings['trp_advanced_settings']['load_legacy_seo_pack'] ) || $settings['trp_advanced_settings']['load_legacy_seo_pack'] === 'no' ) ) return $new_url; // Abort -- New system can be used, process URL via get_slug_translated_url_for_language $url_converter = $trp->get_component( 'url_converter' ); $debug = false; global $TRP_LANGUAGE; $trp_language_copy = $TRP_LANGUAGE; $url_obj = trp_cache_get('url_obj_' . hash('md4', $url), 'trp'); $abs_home_url_obj = trp_cache_get('url_obj_' . hash('md4', $url_converter->get_abs_home() ), 'trp'); $possible_post_id = trp_cache_get( 'possible_post_id_'. hash('md4', $url ), 'trp' ); if ( $possible_post_id ){ $post_id = $possible_post_id; trp_bulk_debug($debug, array('url' => $url, 'found post id' => $post_id, 'for language' => $TRP_LANGUAGE)); } else { $post_id = url_to_postid( $url ); wp_cache_set( 'possible_post_id_' . hash('md4', $url ), $post_id, 'trp' ); if ( $post_id ) { trp_bulk_debug($debug, array('url' => $url, 'found post id' => $post_id, 'for default language' => $TRP_LANGUAGE)); } if ( $post_id == 0 ) { /* try again but this time switch to default language home_url * becasue url_to_postid() uses the global language setting to accurately retrieve a post ID */ $TRP_LANGUAGE = $settings['default-language']; add_filter('trp_keep_permalinks_unchanged', '__return_true' ); /* In order to accurately find the post ID the passed URL to url_to_postid() needs to be accurate * if the option add subdir to default language is on we need to add that to the URL */ $possible_url = $url; if (isset ($settings['add-subdirectory-to-default-language']) && $settings['add-subdirectory-to-default-language'] === 'yes' && $url_converter->get_lang_from_url_string( $url ) == null ){ $possible_url = $url_converter->add_language_to_home_url($url, $url_obj->getPath(), $url_obj->getScheme(), get_current_blog_id() ); } $post_id = url_to_postid( $possible_url ); wp_cache_set( 'possible_post_id_' . hash('md4', $possible_url ), $post_id, 'trp' ); if($post_id){ trp_bulk_debug($debug, array('url' => $url, 'found post id' => $post_id, 'for default language' => $TRP_LANGUAGE)); } remove_filter('trp_keep_permalinks_unchanged', '__return_true' ); $TRP_LANGUAGE = $trp_language_copy; } } $TRP_LANGUAGE = $url_converter->get_lang_from_url_string( $url ); if ($TRP_LANGUAGE == null){ $TRP_LANGUAGE = $settings['default-language']; } $new_url_has_been_determined = false; if( $post_id ){ /* * We need to find if the current URL (either passed as parameter or found via cur_page_url) * has extra arguments compared to its permalink. * We need the permalink based on the language IN THE URL, not the one passed to this function, * as that represents the language to be translated into. * * WE ARE NOT USING \TranslatePress\Uri * due to URL's having extra path elements after the permalink slug. Using the class would strip those end points. * */ $processed_permalink = get_permalink($post_id); $url_to_replace = ( $url_obj->isSchemeless() ) ? ( $url_obj->hasAnchor() || $url_obj->hasQueryParam() ) ? trailingslashit( home_url() ) . ltrim($url, '/') :trailingslashit(trailingslashit( home_url() ) . ltrim($url, '/') ) :$url; $arguments = str_replace(untrailingslashit($processed_permalink), '', $url_to_replace ); // if nothing was replaced, something was wrong, just use the normal permalink without any arguments. if( $arguments == $url_to_replace ) { $arguments = ''; //try again, this time trying to correct url_to_replace to include subdirectory if (isset ($settings['add-subdirectory-to-default-language']) && $settings['add-subdirectory-to-default-language'] === 'yes' && $url_converter->get_lang_from_url_string( $url_to_replace ) == null ) { $possible_url_to_replace = $url_converter->add_language_to_home_url( $url, ( empty( $url_obj->getQuery() ) ) ? (( empty( $url_obj->getFragment() ) ) ? $url_obj->getPath() : $url_obj->getPath() . '#' . $url_obj->getFragment()) : (( empty( $url_obj->getFragment() ) ) ? rtrim( $url_obj->getPath(), '/' ) . '/?' . $url_obj->getQuery() : rtrim( $url_obj->getPath(), '/' ) . '/?' . $url_obj->getQuery() . '#' . $url_obj->getFragment() ), $url_obj->getScheme(), get_current_blog_id() ); $arguments = str_replace( untrailingslashit( $processed_permalink ), '', $possible_url_to_replace ); if ( $arguments == $possible_url_to_replace ) { $arguments = ''; } } } $TRP_LANGUAGE = $language; $new_url = trailingslashit( get_permalink($post_id) ) . ltrim($arguments, '/'); trp_bulk_debug($debug, array('url' => $url, 'new url' => $new_url, 'found post id' => $post_id, 'url type' => 'based on permalink', 'for language' => $TRP_LANGUAGE)); $TRP_LANGUAGE = $trp_language_copy; $new_url_has_been_determined = true; } if( isset( $trp_current_url_term_slug ) && isset($trp_current_url_taxonomy) && $new_url_has_been_determined === false ){ // check here if it is a term link $current_term_link = get_term_link( $trp_current_url_term_slug, $trp_current_url_taxonomy); if (!is_wp_error($current_term_link)){ $TRP_LANGUAGE = $language; $check_term_link = get_term_link($trp_current_url_term_slug, $trp_current_url_taxonomy); if ( !is_wp_error($check_term_link) && strpos(urldecode( $url ), $current_term_link) === 0 ) { $new_url = str_replace( $current_term_link, $check_term_link, urldecode( $url ) ); $new_url_has_been_determined = true; } $TRP_LANGUAGE = $trp_language_copy; } } /** * We try to look for a possible posts archive link that can be on the front page or another page in order to add pagination. */ $url_stripped = $url; $posts_archive_link = get_post_type_archive_link('post'); if( !empty($url_obj->getQuery()) ){ $url_stripped = strtok($url_stripped, '?'); } $url_stripped = rtrim($url_stripped, '/'); $posts_archive_link = strtok($posts_archive_link, '?'); $posts_archive_link = rtrim($url_converter->maybe_add_pagination_to_blog_page($posts_archive_link), '/'); if( is_home() && $url_stripped === $posts_archive_link && ( isset( $_SERVER['REQUEST_URI'] ) && strpos( esc_url_raw( $_SERVER['REQUEST_URI'] ), 'sitemap') === false && strpos( esc_url_raw( $_SERVER['REQUEST_URI'] ), '.xml') === false ) && $new_url_has_been_determined === false) {//for some reason in yoast sitemap is_home() is true ..so we need to check if we are not in the sitemap itself $TRP_LANGUAGE = $language; if ( empty($url_obj->getQuery()) ) { $new_url = $url_converter->maybe_add_pagination_to_blog_page( trailingslashit(get_post_type_archive_link( 'post' ) )); } else { $new_url = rtrim( $url_converter->maybe_add_pagination_to_blog_page( get_post_type_archive_link( 'post' ) ), '/') . '/?' . $url_obj->getQuery(); } $TRP_LANGUAGE = $trp_language_copy; $new_url_has_been_determined = true; } if ($new_url_has_been_determined === false){ // we're just adding the new language to the url $new_url_obj = $url_obj; if ($abs_home_url_obj->getPath() == "/") { $abs_home_url_obj->setPath(''); } if ($url_converter->get_lang_from_url_string($url) === null) { // these are the custom url. They don't have language $abs_home_considered_path = trim(str_replace( $abs_home_url_obj->getPath() !== null ? $abs_home_url_obj->getPath() : '', '', $url_obj->getPath()), '/'); $new_url_obj->setPath(trailingslashit(trailingslashit(strval($abs_home_url_obj->getPath())) . trailingslashit($url_converter->get_url_slug($language)) . $abs_home_considered_path)); $new_url = $new_url_obj->getUri(); trp_bulk_debug($debug, array('url' => $url, 'new url' => $new_url, 'lang' => $language, 'url type' => 'custom url without language parameter')); } else { // these have language param in them and we need to replace them with the new language $abs_home_considered_path = trim(str_replace($abs_home_url_obj->getPath() !== null ? $abs_home_url_obj->getPath() : '', '', $url_obj->getPath()), '/'); $no_lang_orig_path = explode('/', $abs_home_considered_path); unset($no_lang_orig_path[0]); $no_lang_orig_path = implode('/', $no_lang_orig_path); if (!$url_converter->get_url_slug($language)) { $url_lang_slug = ''; } else { $url_lang_slug = trailingslashit($url_converter->get_url_slug($language)); } $new_url_obj->setPath(trailingslashit(trailingslashit($abs_home_url_obj->getPath() !== null ? $abs_home_url_obj->getPath() : '') . $url_lang_slug . ltrim($no_lang_orig_path, '/'))); $new_url = $new_url_obj->getUri(); trp_bulk_debug($debug, array('url' => $url, 'new url' => $new_url, 'lang' => $language, 'url type' => 'custom url with language', 'abs home path' => $abs_home_url_obj->getPath())); } } $TRP_LANGUAGE = $trp_language_copy; return $new_url; } add_filter('trp_get_url_for_language', 'trp_get_url_for_language_backwards_compatibility', 10, 3 ); /** * Redirects to the translated version of the `redirect_url` based on the current language. * * @param string $redirect_url Original URL. */ function trp_compatibility_profile_builder_redirect( $redirect_url ) { $trp_instance = TRP_Translate_Press::get_trp_instance(); global $TRP_LANGUAGE; $url_converter = $trp_instance->get_component( 'url_converter' ); return $url_converter->get_url_for_language( $TRP_LANGUAGE, $redirect_url, '' ); } add_filter( 'wppb_register_redirect', 'trp_compatibility_profile_builder_redirect' ); add_filter( 'wppb_edit_profile_redirect', 'trp_compatibility_profile_builder_redirect' ); /** * Compatibility with WPBakery in edit mode. It adds parameters to the URL that get processed by get_url_for_language and should not. */ add_filter('trp_curpageurl', 'trp_wpbackery_compatibility_remove_params_curpageurl'); add_filter('trp_get_url_for_language', 'trp_wpbackery_compatibility_remove_params', 10, 3); function trp_wpbackery_compatibility_remove_params($url, $language, $args) { return trp_wpbackery_compatibility_strip_params($url); } function trp_wpbackery_compatibility_remove_params_curpageurl($url) { return trp_wpbackery_compatibility_strip_params($url); } function trp_wpbackery_compatibility_strip_params($url) { // Only proceed if WPBakery is active if (!class_exists('Vc_Manager')) { return $url; } $params_to_remove = array('vc_editable', 'vc_post_id', '_vcnonce'); // Use TranslatePress URI class for proper URL handling $uri = new \TranslatePress\Uri($url); // Return original URL if no query parameters exist if (!$uri->hasQueryParam()) { return $url; } $query_string = $uri->getQuery(); // Check if any WPBakery params are present before processing $has_wpbakery_params = false; foreach ($params_to_remove as $param) { if (strpos($query_string, $param . '=') !== false) { $has_wpbakery_params = true; break; } } // Return original URL if no WPBakery params are found if (!$has_wpbakery_params) { return $url; } // Parse and clean query parameters $query = array(); parse_str($query_string, $query); foreach ($params_to_remove as $param) { unset($query[$param]); } // Set the cleaned query back to the URI $new_query = http_build_query($query); $uri->setQuery($new_query); return $uri->getUri(); } /** * Add compatibility fix for LiteSpeed Cache and it's ESI feature * https://docs.litespeedtech.com/lscache/lscwp/cache/#esi-tab * @param $url * @return mixed|string */ function trp_use_lightspeedcache_esi_referer($url){ if( strpos($url, 'lsesi=') > 0 && !empty($_SERVER['ESI_REFERER']) ){ return esc_url_raw($_SERVER['ESI_REFERER']); } return $url; } if (class_exists('LiteSpeed\ESI')) { add_filter('trp_curpageurl', 'trp_use_lightspeedcache_esi_referer'); } /** * Filter the canonical URL generated by SEOPress to use the translated version * when viewing a non-default TranslatePress language. * * This function hooks into the `seopress_titles_canonical` filter and replaces * the `<link rel="canonical">` tag's href attribute with the language-specific * URL generated by TranslatePress. * * Safeguards: * - Leaves canonical untouched if: * - No canonical tag is found * - Current language is the default language * - Canonical points to a different host * - TranslatePress URL converter is unavailable * - The current request is for the homepage (empty path) * * @since 2.10.4 * @hooked seopress_titles_canonical - 10 * * @param string $link_rel_canonical_html The original HTML for the canonical link tag as output by SEOPress. * @return string The modified HTML for the canonical link tag, or the original if no changes are needed. */ function trp_filter_seopress_titles_canonical( $link_rel_canonical_html ) { if ( false === stripos( $link_rel_canonical_html, 'rel="canonical"' ) ) return $link_rel_canonical_html; global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $settings = $trp->get_component( 'settings' )->get_settings(); $url_converter = $trp->get_component( 'url_converter' ); if ( $settings['default-language'] === $TRP_LANGUAGE ) return $link_rel_canonical_html; if ( !method_exists( $url_converter, 'get_url_for_language' ) ) return $link_rel_canonical_html; // Extract the current canonical URL from the link tag if ( !preg_match( '#<link\s+rel=["\']canonical["\']\s+href=["\']([^"\']+)#i', $link_rel_canonical_html, $m ) ) return $link_rel_canonical_html; $canonical_url = html_entity_decode( $m[1], ENT_QUOTES ); // decode in case attrs were escaped // Convert the canonical URL to the current language (handles Multiple Domains subdomains) $new_url = $url_converter->get_url_for_language( $TRP_LANGUAGE, $canonical_url, '' ); if ( empty( $new_url ) || $new_url === $canonical_url ) return $link_rel_canonical_html; // Replace the href in the original <link rel="canonical"> tag (keep everything else as-is). $replacement_url_attr = esc_url( $new_url ); $link_rel_canonical_html = preg_replace( '#(<link\s+rel=["\']canonical["\']\s+href=["\'])([^"\']+)(["\'])#i', '$1' . $replacement_url_attr . '$3', $link_rel_canonical_html, 1 ); return $link_rel_canonical_html; } add_filter( 'seopress_titles_canonical', 'trp_filter_seopress_titles_canonical' , 10 ); /** * Filter the og:url meta tag generated by SEOPress to use the translated URL * including translated slugs when viewing a non-default TranslatePress language. * * This function hooks into the `seopress_social_og_url` filter and replaces * the og:url content attribute with the properly translated URL. * * @since 2.10.4 * @hooked seopress_social_og_url - 10 * * @param string $og_url_html The original HTML for the og:url meta tag as output by SEOPress. * @return string The modified HTML for the og:url meta tag, or the original if no changes are needed. */ function trp_filter_seopress_social_og_url( $og_url_html ) { if ( false === stripos( $og_url_html, 'og:url' ) ) return $og_url_html; global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $settings = $trp->get_component( 'settings' )->get_settings(); $url_converter = $trp->get_component( 'url_converter' ); if ( $settings['default-language'] === $TRP_LANGUAGE ) return $og_url_html; if ( !method_exists( $url_converter, 'get_url_for_language' ) ) return $og_url_html; // Extract the current og:url from the meta tag if ( !preg_match( '#<meta\s+property=["\']og:url["\']\s+content=["\']([^"\']+)#i', $og_url_html, $m ) ) return $og_url_html; $og_url = html_entity_decode( $m[1], ENT_QUOTES ); // Use the current page URL with translated slugs instead of the URL from SEOPress // SEOPress uses $wp->request which contains the rewritten (untranslated) slug $new_url = $url_converter->cur_page_url(); if ( empty( $new_url ) || $new_url === $og_url ) return $og_url_html; // Replace the content in the original og:url meta tag $replacement_url = esc_url( $new_url ); $og_url_html = preg_replace( '#(<meta\s+property=["\']og:url["\']\s+content=["\'])([^"\']+)(["\'])#i', '$1' . $replacement_url . '$3', $og_url_html, 1 ); return $og_url_html; } add_filter( 'seopress_social_og_url', 'trp_filter_seopress_social_og_url', 10 ); /** * Remove Breakdance's template override when TranslatePress editors are active. * * Breakdance overrides the template resolution process by hooking into * the `template_include` filter. This interferes with TranslatePress' * Translation Editor and String Translation Editor. To prevent conflicts, * this function removes Breakdance's filter when either of those editors * are active. * * * @since 2.10.4 * * @hooked plugins_loaded - 20 * * @return void */ function trp_breakdance_compat__remove_filter() { $is_editor = isset( $_GET['trp-edit-translation'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['trp-edit-translation'] ) ); $is_strings = isset( $_GET['trp-string-translation'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['trp-string-translation'] ) ); // Only proceed if one of the TRP editors is active if ( !( $is_editor || $is_strings ) ) return; // Only remove if Breakdance actually hooked its template override if ( has_filter( 'template_include', 'Breakdance\\ActionsFilters\\template_include' ) ) remove_filter( 'template_include', 'Breakdance\\ActionsFilters\\template_include', 1000000 ); } add_action( 'plugins_loaded', 'trp_breakdance_compat__remove_filter', 20 ); /* * Add support for Simple Download Manager on certain hosts (not replicated locally) * Having TP installed will brake archives, an extra line gets added to the archive processing due to output buffer. * Do not translate url's like this as it brakes them because they are archives's: https://translatepress.ddev.site/ro/?sdm_process_download=1&download_id=95 */ add_action( 'trp_before_running_hooks', 'trp_sdm_compat_remove_hooks_that_start_object_buffer', 10, 1); function trp_sdm_compat_remove_hooks_that_start_object_buffer( $trp_loader ) { if ( isset( $_GET['sdm_process_download'] ) && isset( $_GET['download_id'] ) ) { add_filter( 'trp_skip_gettext_processing', '__return_true' ); $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); $trp_loader->remove_hook( 'init', 'start_output_buffer', $translation_render ); } } /* * Disable gettext translation of the job manager slugs as they conflict with TranslatePress. */ add_filter( 'gettext_with_context', 'trp_ignore_wp_job_manager_slugs', 99, 4 ); function trp_ignore_wp_job_manager_slugs( $translation, $text, $context = null, $domain = null ) { static $targets = [ 'job', 'job-category', 'job-type', 'job-listings' ]; if ( $domain == 'wp-job-manager' && in_array( $text, $targets, true ) ) { return $text; // Always return the original untranslated string } return $translation; } /** * Add trp-post-container wrapper to Divi module outputs * TP is not adding any trp-post-container except here. * * @param string $output The module HTML output * @param string $render_slug The module slug (e.g., 'et_pb_text', 'et_pb_post_title') * @param object $module The module object * @return string Modified output with trp-post-container wrapper */ add_filter('et_module_shortcode_output', 'trp_divi_wrap_module_with_post_id', 10, 3); function trp_divi_wrap_module_with_post_id($output, $render_slug, $module) { global $post, $TRP_LANGUAGE; // Check if we have a valid post ID if (empty($post->ID)) { return $output; } // Get TranslatePress settings $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component('settings'); $settings = $trp_settings->get_settings(); // Only wrap on non-default language if ($TRP_LANGUAGE !== $settings['default-language']) { // Only wrap modules that typically contain translatable text content $modules_to_wrap = apply_filters('trp_divi_modules_to_wrap', array( 'et_pb_text', 'et_pb_post_title', 'et_pb_post_content', 'et_pb_blurb', 'et_pb_cta', 'et_pb_accordion', 'et_pb_toggle', 'et_pb_tabs', 'et_pb_testimonial', 'et_pb_pricing_tables', 'et_pb_number_counter', 'et_pb_countdown_timer' )); if (in_array($render_slug, $modules_to_wrap)) { $output = "<trp-post-container data-trp-post-id='" . $post->ID . "'>" . $output . "</trp-post-container>"; } } return $output; } includes/class-woocommerce-emails.php 0000777 00000036202 15251156640 0013774 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Woocommerce_Emails{ public function __construct(){} public function initialize_hooks(){ // Save current language for user every time wp_footer is loaded add_action( 'wp_footer', array( $this, 'save_current_language' ) ); // In order for the email translation to work properly, WC_VERSION needs to be >= 6.8.0 if( defined( 'WC_VERSION' ) && version_compare( WC_VERSION, '6.8.0' ) >= 0 ) { // Save user language on checkout add_action( 'woocommerce_checkout_update_order_meta', array( $this, 'save_language_on_checkout' ), 10, 2 ); add_action( 'woocommerce_store_api_checkout_update_order_meta', array( $this, 'save_language_on_checkout_store_api' ), 10, 1 ); // WooCommerce email notifications add_action( 'woocommerce_order_status_processing_to_cancelled_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_on-hold_to_cancelled_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_completed_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_pending_to_on-hold_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_failed_to_on-hold_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_cancelled_to_on-hold_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_cancelled_to_processing_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_failed_to_processing_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_on-hold_to_processing_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_pending_to_processing_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_fully_refunded_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_partially_refunded_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_pending_to_failed_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_on-hold_to_failed_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_pending_to_completed_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_failed_to_completed_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_cancelled_to_completed_notification', array( $this, 'store_email_order_id' ), 5, 1 ); add_action( 'woocommerce_order_status_failed_notification', array( $this, 'store_email_order_id' ), 5, 1 ); // WooCommerce emails when resent by admin add_action( 'woocommerce_before_resend_order_emails', array( $this, 'prepare_order_id_for_resend_emails' ), 5, 2 ); // WooCommerce note to customer email add_action( 'woocommerce_new_customer_note_notification', array( $this, 'prepare_order_id_for_note_emails' ), 5, 1 ); // Hijack execution to translate emails in user language accordingly add_filter( 'woocommerce_allow_switching_email_locale', array( $this, 'trp_woo_setup_locale' ), 10, 2 ); add_filter( 'woocommerce_allow_restoring_email_locale', array( $this, 'trp_woo_restore_locale' ), 10, 2 ); } } /** * Save user language on WooCommerce checkout * * @param $order_id * @param $posted * @return void */ public function save_language_on_checkout( $order_id, $posted ) { global $TRP_LANGUAGE, $TRP_EMAIL_ORDER; $order = wc_get_order($order_id); $user_id = $order->get_user_id(); $TRP_EMAIL_ORDER = $order_id; if( $user_id != 0 ){ $user_preferred_language = get_user_meta($user_id, 'trp_language', true); $always_use_this_language = get_user_meta( $user_id, 'trp_always_use_this_language', true ); if (!empty($always_use_this_language) && $always_use_this_language == 'yes' && !empty($user_preferred_language) ){ update_user_meta( $user_id, 'trp_language', $user_preferred_language ); trp_woo_hpos_manipulate_post_meta( $order_id, 'trp_language', $user_preferred_language, 'update' ); }else { update_user_meta( $user_id, 'trp_language', $TRP_LANGUAGE ); trp_woo_hpos_manipulate_post_meta( $order_id, 'trp_language', $TRP_LANGUAGE, 'update' ); } } else{ trp_woo_hpos_manipulate_post_meta( $order_id, 'trp_language', $TRP_LANGUAGE, 'update' ); } } /** * Fires when the Checkout Block/Store API updates an order's meta data. * * @param $order * @return void */ public function save_language_on_checkout_store_api( $order ) { global $TRP_LANGUAGE, $TRP_EMAIL_ORDER; $user_id = $order->get_user_id(); $order_id = $order->get_id(); $TRP_EMAIL_ORDER = $order_id; if( $user_id != 0 ){ $user_preferred_language = get_user_meta($user_id, 'trp_language', true); $always_use_this_language = get_user_meta( $user_id, 'trp_always_use_this_language', true ); if (!empty($always_use_this_language) && $always_use_this_language == 'yes' && !empty($user_preferred_language) ){ update_user_meta( $user_id, 'trp_language', $user_preferred_language ); trp_woo_hpos_manipulate_post_meta( $order_id, 'trp_language', $user_preferred_language, 'update' ); }else { update_user_meta( $user_id, 'trp_language', $TRP_LANGUAGE ); trp_woo_hpos_manipulate_post_meta( $order_id, 'trp_language', $TRP_LANGUAGE, 'update' ); } } else{ trp_woo_hpos_manipulate_post_meta( $order_id, 'trp_language', $TRP_LANGUAGE, 'update' ); } } /** * Save current user language * * The hook was added on 'wp_footer' to prevent logout or backend admin actions from resetting $TRP_LANGUAGE to TRP default language * * @return void */ public function save_current_language(){ global $TRP_LANGUAGE; $user_id = get_current_user_id(); if( $user_id > 0 ){ $language_meta = get_user_meta( $user_id, 'trp_language', true); $always_use_this_language = get_user_meta( $user_id, 'trp_always_use_this_language', true ); if( $language_meta != $TRP_LANGUAGE && $always_use_this_language !== 'yes') { update_user_meta( $user_id, 'trp_language', $TRP_LANGUAGE ); } } } /** * Store order id in a separate global to access its value later in the execution * * @param $order_id * @return void */ public function store_email_order_id( $order_id ) { global $TRP_EMAIL_ORDER; $TRP_EMAIL_ORDER = $order_id; } /** * Prepare order id for resend emails * * @param $order * @param $email_type * @return void */ public function prepare_order_id_for_resend_emails( $order, $email_type ) { if( $email_type == 'customer_invoice' ) $this->store_email_order_id( $order->get_id() ); } /** * Prepare order id for note emails * * @param $note_and_order_id * @return void */ public function prepare_order_id_for_note_emails( $note_and_order_id ) { $this->store_email_order_id( $note_and_order_id['order_id'] ); } /** * Set the language for WooCommerce emails according to the user information: * user profile language for admin AND language metadata for customer * * @param $bool * @param $wc_email * @return false */ public function trp_woo_setup_locale( $bool, $wc_email ) { global $TRP_EMAIL_ORDER, $TRP_LANGUAGE; $order = false; $is_customer_email = $wc_email->is_customer_email(); if ( $TRP_EMAIL_ORDER ) { $order = wc_get_order( $TRP_EMAIL_ORDER ); } $trp_settings = TRP_Translate_Press::get_trp_instance()->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $default_language = $settings["default-language"]; /** * At this point in the execution, $wc_email->get_recipient() returns null and throws a PHP warning inside WooCommerce /woocommerce/includes/emails/class-wc-email.php * This is why we use $wc_email->get_option( 'recipient' ). It properly returns the recipient in the case of admin emails. * * We treat customer emails differently. */ $recipients = $wc_email->get_option( 'recipient' ); /** * When dealing with customer emails, recipient will not be set. We need to retrieve it via get_billing_email(). */ if ( $is_customer_email && is_a( $order, 'WC_Order' ) && empty( $recipients ) ) $recipients = $order->get_billing_email(); if ( empty( $recipients ) ) { $recipients = []; } elseif ( !is_array($recipients) ) { $recipients = explode( ',', $recipients ); } $language = $TRP_LANGUAGE; $user_id = 0; if( $is_customer_email ){ if ( $order ) { $user_id = $order->get_user_id(); if ( $user_id > 0 ) { $language = get_user_meta( $user_id, 'trp_language', true ); } else { $language = trp_woo_hpos_get_post_meta( $TRP_EMAIL_ORDER, 'trp_language', true ); } } } else{ if( ! empty( $recipients ) && count( $recipients ) == 1 ){ $registered_user = get_user_by( 'email', $recipients[0] ); if( $registered_user ){ // If language is set to site default, user object won't have a locale set. Fallback to WPLANG. In case WPLANG is not set either, fallback to default language if ( !empty( $registered_user->locale ) ){ $language = $registered_user->locale; } else { $wplang = get_option( 'WPLANG' ); $language = !empty( $wplang ) ? $wplang : $default_language; } } else { $language = trp_woo_hpos_get_post_meta( $TRP_EMAIL_ORDER, 'trp_language', true ); } } } $language = apply_filters( 'trp_woo_email_language', $language, $is_customer_email, $recipients, $user_id ); if ( empty( $language ) ) $language = $TRP_LANGUAGE; trp_switch_language( $language ); add_filter( 'trp_allow_gettext_write', '__return_true' ); $this->reload_woocommerce_textdomain(); $this->bootstrap_trp_gettext_for_emails(); // calls necessary because the default additional_content field of an email is localized before this point and stored in a variable in the previous locale $wc_email->init_form_fields(); $wc_email->init_settings(); return false; } /** * Restore locale after email is sent * * @param $bool * @param $wc_email * @return false */ public function trp_woo_restore_locale( $bool, $wc_email ) { trp_restore_language(); $this->reload_woocommerce_textdomain(); return false; } /** * Ensure TranslatePress gettext is active for the current email language. * * WooCommerce's emails are not always triggered in a normal frontend request. * They can be sent asynchronously (e.g. admin changing an order status, a * payment processor callback marking the order as paid, or cron/CLI jobs). * * In those cases, TranslatePress’s gettext global ($trp_translated_gettext_texts) * and filters are never initialized in time because the email is rendered much * later in the request lifecycle. * * To guarantee that TP’s database-backed translations are available for * the strings in WooCommerce emails, we: * - check if at least one of TP’s WooCommerce gettext filters is already attached, * - if not, force creation of the gettext global and force-attach the filters. * * This way, even when emails are sent outside a normal page render, the * gettext translations stored in TranslatePress are applied correctly. */ private function bootstrap_trp_gettext_for_emails() { $trp = TRP_Translate_Press::get_trp_instance(); $gettext_manager = $trp->get_component( 'gettext_manager' ); $pg = $gettext_manager->get_gettext_component( 'process_gettext' ); // If at least one core handler is already attached, return if ( has_filter( 'gettext', [ $pg, 'woocommerce_process_gettext_strings_no_context' ] ) ) return; if ( !$trp->get_component( 'machine_translator') || get_class( $trp->get_component( 'machine_translator' ) ) === TRP_Machine_Translator::class ) $trp->init_machine_translation(); // Machine translator should be initialized by the get_trp_instance() call. In the case of cron jobs, it is not - so we initialize it here manually. // Bypass processing_gettext_is_needed usual checks. Otherwise, the below method calls wouldn't go through add_filter( 'trp_processing_gettext_is_needed', '__return_true' ); $gettext_manager->create_gettext_translated_global(); $gettext_manager->call_gettext_filters( 'woocommerce_' ); } function reload_woocommerce_textdomain() { $domain = 'woocommerce'; $locale = apply_filters( 'plugin_locale', get_locale(), $domain ); $custom_translation_path = WP_LANG_DIR . '/woocommerce/woocommerce-' . $locale . '.mo'; $global_translation_path = WP_LANG_DIR . '/plugins/woocommerce-' . $locale . '.mo'; $bundled_translation_path = trailingslashit( WC()->plugin_path() ) . 'i18n/languages/woocommerce-' . $locale . '.mo'; unload_textdomain( $domain ); // Custom file present: mimic WC if ( is_readable( $custom_translation_path ) ) { load_textdomain( $domain, $custom_translation_path ); if ( is_readable( $global_translation_path ) ) { load_textdomain( $domain, $global_translation_path ); } return true; } if ( is_readable( $global_translation_path ) ) { load_textdomain( $domain, $global_translation_path ); return true; } if ( is_readable( $bundled_translation_path ) ) { load_textdomain( $domain, $bundled_translation_path ); return true; } return false; } } includes/class-url-converter.php 0000777 00000131136 15251156640 0013016 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Url_Converter * * Manages urls of translated pages. */ class TRP_Url_Converter { protected $absolute_home; protected $settings; protected $admin_url; /** * TRP_Url_Converter constructor. * * @param array $settings Settings option. */ public function __construct( $settings ){ $this->settings = $settings; //$admin_url is declared here because it was causing a conflict with Ultimate Dashboard since there was an action hooked on site_url $this->admin_url = strtolower( admin_url() ); } /** * Add language code as a subdirectory after home url. * * Hooked to home_url. * * @param string $url Given Url. * @param string $path Given path. * @param string $orig_scheme Scheme. * @param int $blog_id Blog id. * @return string */ public function add_language_to_home_url( $url, $path, $orig_scheme, $blog_id ){ global $TRP_LANGUAGE; //if this is not set then don't do anything as this is an exception/error and $TRP_LANGUAGE should always be set if( empty( $TRP_LANGUAGE ) ) return $url; if ( isset( $this->settings['add-subdirectory-to-default-language'] ) && $this->settings['add-subdirectory-to-default-language'] == 'no' && $TRP_LANGUAGE == $this->settings['default-language'] ) { return $url; } if( apply_filters( 'trp_add_language_to_home_url_check_for_admin', true, $url, $path ) && ( is_customize_preview() || $this->is_admin_request() || $this->is_sitemap_path( $path ) || $this->url_is_file( $path ) ) ) return $url; $url_slug = $this->get_url_slug( $TRP_LANGUAGE ); //if this is not set then don't do anything as this is an exception/error if we don't have an $url_slug we don't need to do anything if( empty( $url_slug ) ) return $url; $abs_home = $this->get_abs_home(); if ( trp_force_slash_at_end_of_link( $this->settings ) ) { $new_url = trailingslashit( trailingslashit( $abs_home ) . $url_slug ); } else { $new_url = trailingslashit( $abs_home ) . $url_slug; } if ( ! empty( $path ) ){ $new_url = trailingslashit($new_url) . ltrim( $path, '/'); } return apply_filters( 'trp_home_url', $new_url, $abs_home, $TRP_LANGUAGE, $path, $url ); } /** * Check if this is a request at the backend. * * @return bool true if is admin request, otherwise false. */ public function is_admin_request() { $current_url = $this->cur_page_url( false ); // we can't use wp_get_referer() It looks like it creates an infinite loop because it calls home_url() and we're filtering that // array('http','https') is added because of a compatibility issue with Scriptless Social Sharing that created an infinite loop //because this function is hooked to 'locale' and reaches at a certain point a function hooked to 'kses_allowed_protocols' //Scriptless Social Sharing had a function hooked to the same filter and it created an infinit loop $referrer = ''; if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) { $referrer = wp_unslash( esc_url_raw( $_REQUEST['_wp_http_referer'], array( 'http', 'https' ) ) ); } else if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) { $referrer = wp_unslash( esc_url_raw( $_SERVER['HTTP_REFERER'], array( 'http', 'https' ) ) ); } //consider an admin request a call to the rest api that came from the admin area if( false !== strpos( $current_url, '/wp-json/' ) && 0 === strpos( $referrer, $this->admin_url ) ){ return true; } /** * Check if this is a admin request. If true, it * could also be a AJAX request from the frontend. */ if ( 0 === strpos( $current_url, $this->admin_url ) ) { /** * Check if the user comes from a admin page. */ if ( 0 === strpos( $referrer, $this->admin_url ) ) { return true; } else { if ( function_exists( 'wp_doing_ajax' ) ) { return ! wp_doing_ajax(); } else { return ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ); } } } else { return false; } } /** * A function that is used inside the home_url filter to detect if the current link is a sitemap link * @param $path the path that is passed inside home_url * @return bool */ public function is_sitemap_path( $path = '' ) { global $wp_current_filter; if( empty( $path ) || $path === '/' ){ $path = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( $_SERVER['REQUEST_URI'] ) : ''; } // Verify that this is a sitemap url and that it contains the .xml extension if( strpos($path, 'sitemap') !== false && strpos($path, '.xml') !== false && // Bypass this check if we're on certain filters in order to be able to generate other language urls !in_array( 'wpseo_sitemap_url', $wp_current_filter ) && !in_array( 'seopress_sitemaps_url', $wp_current_filter ) && !in_array( 'rank_math/sitemap/url', $wp_current_filter ) && !in_array( 'aiosp_sitemap_data', $wp_current_filter ) && !in_array( 'aioseo_sitemap_terms', $wp_current_filter ) && !in_array( 'aioseo_sitemap_posts', $wp_current_filter ) && !in_array( 'slim_seo_sitemap_post', $wp_current_filter ) && !in_array( 'slim_seo_sitemap_term', $wp_current_filter ) && !in_array( 'slim_seo_sitemap_homepage', $wp_current_filter ) && !in_array( 'slim_seo_sitemap_post_type_archive', $wp_current_filter ) ){ return true; } // check if it's a stylesheet for xml. SEO Press uses it. if (strpos( $path, 'sitemap') !== false && strpos( $path, '.xsl') !== false ){ return true; } return false; } /** * Add Hreflang entries for each language to Header. */ public function add_hreflang_to_head() { // exclude hreflang for URL $default_language = $this->settings["default-language"]; $original_url = str_replace( '#TRPLINKPROCESSED', '', $this->get_url_for_language( $default_language ) ); if ( apply_filters( 'trp-exclude-hreflang', false, $original_url ) ) { return; } $languages = $this->settings['publish-languages']; if ( isset( $_GET['trp-edit-translation'] ) && $_GET['trp-edit-translation'] == 'preview' ) { $languages = $this->settings['translation-languages']; } $region_independent_languages = array(); $hreflang_duplicates = array(); $hreflang_duplicates_region_independent = array(); foreach ( $languages as $language ) { if ( apply_filters( 'trp_add_country_hreflang_tags', true ) ) { $hreflang = $this->strip_formality_from_language_code( $language ); // returns the language without formality // hreflang should have - instead of _ . For example: en-EN, not en_EN like the locale $hreflang = str_replace( '_', '-', $hreflang ); $hreflang = apply_filters( 'trp_hreflang', $hreflang, $language ); $hreflang_duplicates[] = $hreflang; echo '<link rel="alternate" hreflang="' . esc_attr( $hreflang ) . '" href="' . esc_url( $this->get_url_for_language( $language ) ) . '"/>' . "\n"; } if ( apply_filters( 'trp_add_region_independent_hreflang_tags', true ) ) { $language_independent_hreflang = strtok( $language, '_' ); $language_independent_hreflang = apply_filters( 'trp_hreflang', $language_independent_hreflang, $language ); if ( !empty( $language_independent_hreflang ) && !in_array( $language_independent_hreflang, $region_independent_languages ) ) { $region_independent_languages[] = $language_independent_hreflang; $hreflang_duplicates_region_independent[ $language ] = '<link rel="alternate" hreflang="' . esc_attr( $language_independent_hreflang ) . '" href="' . esc_url( $this->get_url_for_language( $language ) ) . '"/>' . "\n"; } } } foreach ( $languages as $language ) { $language_hreflang = strtok( $language, '_' ); $language_hreflang = apply_filters( 'trp_hreflang', $language_hreflang, $language ); if ( !in_array( $language_hreflang, $hreflang_duplicates ) ) { if ( isset( $hreflang_duplicates_region_independent[ $language ] ) ) { echo $hreflang_duplicates_region_independent[ $language ]; /* phpcs:ignore */ /* escaped inside the array */ } } } if ( !empty( $this->settings['trp_advanced_settings']['enable_hreflang_xdefault'] ) && $this->settings['trp_advanced_settings']['enable_hreflang_xdefault'] != 'disabled' && in_array( $this->settings['trp_advanced_settings']['enable_hreflang_xdefault'], $this->settings['translation-languages'] ) ) { $default_lang = $this->settings['trp_advanced_settings']['enable_hreflang_xdefault']; echo '<link rel="alternate" hreflang="x-default" href="' . esc_url( $this->get_url_for_language( $default_lang ) ) . '"/>' . "\n"; } } /** * Strips formality from the language code - e.g. de_DE_formal => de_DE * * Otherwise, it would lead to unidentified hreflang values * * @param string $language language code * @return string */ public function strip_formality_from_language_code( $language ){ return str_replace( ['_formal', '_informal'], '', $language ); } /** * Function that replace iso 639-2 and iso 639-3 with iso 639-1 because this is the official one used for hreflang. */ public function replace_iso_2_with_iso_3_for_hreflang($hreflang, $language = null){ $hreflang_iso_1 = apply_filters('trp_add_hreflang_correct_iso_code', array( 'bel' => 'be' )); foreach ($hreflang_iso_1 as $iso_2 => $iso_1) { if ( $hreflang === $iso_2 ) { return $iso_1; } } return $hreflang; } /** * Function that changes the lang attribute in the html tag to the current language. * * @param string $output * @return string */ public function change_lang_attr_in_html_tag( $output ){ global $TRP_LANGUAGE; $tp_lang = str_replace('_formal', '', $TRP_LANGUAGE); // de-de-formal is not a valid lang attribute. $lang = get_bloginfo('language'); if ( $lang && !empty($tp_lang) ) { if ( apply_filters( 'trp_add_default_lang_tags', true ) ) { $output = str_replace( 'lang="' . $lang . '"', 'lang="' . str_replace( '_', '-', $tp_lang ) . '"', $output ); } if ( apply_filters( 'trp_add_regional_lang_tags', true ) ) { $language = strtok($tp_lang, '_'); $output = str_replace( 'lang="' . $lang . '"', 'lang="' . $language . '"', $output ); } } return $output; } /** * @param $output * @return $output * * adds a new attribute in footer, tp_language_lang, for Automatic User Language Detection to rely on for finding the current language */ public function add_tp_language_lang_attribute(){ global $TRP_LANGUAGE; $html ='<template id="tp-language" data-tp-language="'. esc_attr($TRP_LANGUAGE) . '"></template>'; echo $html; /* phpcs:ignore *///ignored because the html is constructed by us } /** * Checks if the URL is eligible for translation (e.g. Is not a file, sitemap, etc.) * * In case the URL is not eligible for translation, it caches it - so we know not to process it the next time. * In case the URL is eligible for translation, it will return an array containing the hash used for accessing the cache. * * @param $cache_key * @param $language * @param $url * @param $trp_link_is_processed * @return array|string */ public function check_if_url_is_valid_and_set_cache( $cache_key, $language, $url, $trp_link_is_processed = '' ){ $debug = false; global $TRP_LANGUAGE; if ( apply_filters( 'trp_skip_url_for_language', false, $url ) ){ return (string) $url; } $hash = hash( 'md4', (string) $language . (string) $url . (string) $trp_link_is_processed . (string) $TRP_LANGUAGE ); $cached_url = trp_cache_get( $cache_key . $hash, 'trp' ); if ( $cached_url !== false ){ return $cached_url; } if ( empty( $language ) ) { $language = $TRP_LANGUAGE; } $url_obj = trp_cache_get('url_obj_' . hash('md4', $url), 'trp'); if ( $url_obj === false ){ $url_obj = new \TranslatePress\Uri($url); wp_cache_set('url_obj_' . hash('md4', $url), $url_obj, 'trp' ); } $abs_home_url_obj = trp_cache_get('url_obj_' . hash('md4', $this->get_abs_home() ), 'trp'); if ( $abs_home_url_obj === false ){ $abs_home_url_obj = new \TranslatePress\Uri( $this->get_abs_home() ); wp_cache_set('url_obj_' . hash('md4', $this->get_abs_home()), $abs_home_url_obj, 'trp' ); } if ( $TRP_LANGUAGE == $this->settings['default-language'] ){ $trp_link_is_processed = ''; } if ( $this->is_sitemap_path($url_obj->getPath()) ){ trp_bulk_debug( $debug, array( 'url' => $url, 'abort' => 'is file' ) ); wp_cache_set($cache_key . $hash, $url . $trp_link_is_processed, 'trp'); return $url . $trp_link_is_processed; //abort for files } if ( $this->url_is_file($url) ){ trp_bulk_debug($debug, array('url' => $url, 'abort' => 'is file')); wp_cache_set($cache_key . $hash, $url . $trp_link_is_processed, 'trp'); return $url . $trp_link_is_processed; //abort for files } if ( !$url_obj->isSchemeless() && $url_obj->getScheme() != 'http' && $url_obj->getScheme() != 'https' ){ trp_bulk_debug($debug, array('url' => $url, 'abort' => "is different scheme ".$url_obj->getScheme())); wp_cache_set($cache_key . $hash, $url . $trp_link_is_processed, 'trp'); return $url . $trp_link_is_processed; // abort for non-http/https links } if ( $url_obj->isSchemeless() && !$url_obj->getPath() ){ trp_bulk_debug($debug, array('url' => $url, 'abort' => "is anchor or has get params")); wp_cache_set($cache_key . $hash, $url, 'trp'); return $url; // abort for anchors or params only. } if ( $url_obj->getHost() && $abs_home_url_obj->getHost() && $url_obj->getHost() != $abs_home_url_obj->getHost() ){ // Allow addons (like Multiple Domains) to recognize additional domains as internal $is_external = apply_filters( 'trp_is_external_link', true, $url, $this->get_abs_home() ); if ( $is_external ) { trp_bulk_debug($debug, array('url' => $url, 'abort' => "is external url ")); wp_cache_set($cache_key . $hash, $url, 'trp'); return $url; // abort for external url's } } if ( $this->get_lang_from_url_string($url) === null && $this->settings['default-language'] === $language && $this->settings['add-subdirectory-to-default-language'] !== 'yes' ){ trp_bulk_debug($debug, array('url' => $url, 'abort' => "URL already has the correct language added to it and default language has subdir")); wp_cache_set($cache_key . $hash, $url, 'trp'); return $url; } if ( strpos( $url, '/wp-json' ) !== false || strpos( $url, '/wp-admin' ) !== false ) { trp_bulk_debug($debug, array('url' => $url, 'abort' => 'is wp-json or admin link')); wp_cache_set($cache_key . $hash, $url . $trp_link_is_processed, 'trp'); return $url . $trp_link_is_processed; // abort for wp-json or admin links } return [ 'no_cache' => true, 'hash' => $hash ]; // URL is eligible for translation and not cached } /** * Returns language-specific url for given language. * * Defaults to current Url and current language. * * @param string $language Language code that we want to translate into. * @param string $url Url to encode. * @param string $trp_link_is_processed * @return string */ public function get_url_for_language ( $language = null, $url = null, $trp_link_is_processed = '#TRPLINKPROCESSED') { $debug = false; // initializations global $TRP_LANGUAGE; if ( $TRP_LANGUAGE == $this->settings['default-language'] ){ $trp_link_is_processed = ''; } if ( empty($url) ){ $url = $this->cur_page_url( false ); } $url = apply_filters( 'trp_pre_get_url_for_language', $url, $language, $this->get_abs_home(), $this->get_lang_from_url_string( $url ), $this->get_url_slug( $language ) ); $cached_url = $this->check_if_url_is_valid_and_set_cache('get_url_for_language_', $language, $url, $trp_link_is_processed ); if ( !isset( $cached_url['no_cache'] ) ) return $cached_url; $hash = $cached_url['hash']; $url_obj = trp_cache_get('url_obj_' . hash('md4', $url), 'trp'); if ( $url_obj === false ){ $url_obj = new \TranslatePress\Uri($url); wp_cache_set('url_obj_' . hash('md4', $url), $url_obj, 'trp' ); } $abs_home_url_obj = trp_cache_get('url_obj_' . hash('md4', $this->get_abs_home() ), 'trp'); if ( $abs_home_url_obj === false ){ $abs_home_url_obj = new \TranslatePress\Uri( $this->get_abs_home() ); wp_cache_set('url_obj_' . hash('md4', $this->get_abs_home()), $abs_home_url_obj, 'trp' ); } // we're just adding the new language to the url $new_url_obj = clone $url_obj; if ($abs_home_url_obj->getPath() == "/") { $abs_home_url_obj->setPath(''); } $lang_from_url_string = $this->get_lang_from_url_string($url); if ( $lang_from_url_string === null) { // these are the custom url. They don't have language $abs_home_considered_path = trim(str_replace( strval( $abs_home_url_obj->getPath() ), '', strval( $url_obj->getPath() )), '/'); $new_url_obj->setPath(trailingslashit(trailingslashit(strval($abs_home_url_obj->getPath())) . trailingslashit($this->get_url_slug($language)) . $abs_home_considered_path)); $new_url = $new_url_obj->getUri(); trp_bulk_debug($debug, array('url' => $url, 'new url' => $new_url, 'lang' => $language, 'url type' => 'custom url without language parameter')); } else if ( $lang_from_url_string === $language ){ trp_bulk_debug($debug, array('url' => $url, 'abort' => "URL already has the correct language added to it")); } else { // these have language param in them and we need to replace them with the new language $abs_home_considered_path = trim(str_replace(strval ( $abs_home_url_obj->getPath() ) ,'', strval( $url_obj->getPath() )), '/'); $no_lang_orig_path = explode('/', $abs_home_considered_path); unset($no_lang_orig_path[0]); $no_lang_orig_path = implode('/', $no_lang_orig_path); if (!$this->get_url_slug($language)) { $url_lang_slug = ''; } else { $url_lang_slug = trailingslashit($this->get_url_slug($language)); } $new_url_obj->setPath(trailingslashit(trailingslashit(strval($abs_home_url_obj->getPath())) . $url_lang_slug . ltrim($no_lang_orig_path, '/'))); $new_url = $new_url_obj->getUri(); trp_bulk_debug($debug, array('url' => $url, 'new url' => $new_url, 'lang' => $language, 'url type' => 'custom url with language', 'abs home path' => $abs_home_url_obj->getPath())); } // Only when SEO Pack is not active, allow WooCommerce links to be translated. Otherwise, SEO Pack will handle this /* fix links for woocommerce on language switcher for product categories and product tags */ if( class_exists( 'WooCommerce' ) && !class_exists( 'TRP_IN_Seo_Pack' ) && $lang_from_url_string !== $language ){ $english_woocommerce_slugs = array('product-category', 'product-tag', 'product'); foreach ($english_woocommerce_slugs as $english_woocommerce_slug){ // current woo slugs are based on the localized default language OR the current language $current_slug = trp_get_transient( 'tp_'.$english_woocommerce_slug.'_'. $this->settings['default-language'] ); if( $current_slug === false ){ $current_slug = trp_x( $english_woocommerce_slug, 'slug', 'woocommerce', $this->settings['default-language'] ); set_transient( 'tp_'.$english_woocommerce_slug.'_'. $this->settings['default-language'], $current_slug, 12 * HOUR_IN_SECONDS ); } //only replace url here if we are in a default Woocommerce case, meaning the slug in Permalinks page is not changed manually by the user if( $this->trp_get_woocommerce_saved_permalink($english_woocommerce_slug) === $current_slug ) { if (strpos($new_url, '/' . $current_slug . '/') === false) { $current_slug = trp_get_transient('tp_' . $english_woocommerce_slug . '_' . $TRP_LANGUAGE); if ($current_slug === false) { $current_slug = trp_x($english_woocommerce_slug, 'slug', 'woocommerce', $TRP_LANGUAGE); set_transient('tp_' . $english_woocommerce_slug . '_' . $TRP_LANGUAGE, $current_slug, 12 * HOUR_IN_SECONDS); } } $translated_slug = trp_get_transient('tp_' . $english_woocommerce_slug . '_' . $language); if ($translated_slug === false) { $translated_slug = trp_x($english_woocommerce_slug, 'slug', 'woocommerce', $language); set_transient('tp_' . $english_woocommerce_slug . '_' . $language, $translated_slug, 12 * HOUR_IN_SECONDS); } $new_url = str_replace('/' . $current_slug . '/', '/' . $translated_slug . '/', $new_url); } } } if ( empty( $new_url ) ) { $new_url = $url; } //when using this filter, if the user did not run the updater for slugs, calling this function will result in using the fallback functions in // SeoPack->class-slug-manager.php->get_slug_translated_url_for_language->get_slugs_pairs_based_on_language $new_url = apply_filters( 'trp_get_url_for_language', $new_url, $url, $language, $this->get_abs_home(), $lang_from_url_string, $this->get_url_slug( $language ) ); wp_cache_set('get_url_for_language_' . $hash, $new_url . $trp_link_is_processed, 'trp'); return $new_url . $trp_link_is_processed; } /** * Check is a url is an actual file on the server, in which case don't add a language param. * * @param string $url * @return bool */ public function url_is_file( $url = null ){ $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component("translation_render"); $home_url = untrailingslashit( $this->get_abs_home() ); if ( empty( $url ) || $translation_render->is_external_link($url, $home_url ) ){ // Use unfiltered home_url due to infinite loop $return = false; }else { if ( strpos( $url, 'wp-content/uploads' ) !== false ) { $return = true; }else { $path = trailingslashit( ABSPATH ) . str_replace( untrailingslashit( $this->get_abs_home() ), '', $url ); if(apply_filters('trp_is_file', true, $path)) { $return = @is_file( $path ); }else{ $return = true; } } } return apply_filters( 'trp_url_is_file', $return, $url, $this->get_abs_home() ); } public function does_url_contains_array($return, $path){ $elements_to_avoid = apply_filters( 'trp_elements_to_avoid_when_is_file_is_called', array("index.php", "/../")); foreach ($elements_to_avoid as $element){ if( strpos($path, $element) !== false ){ $return = false; return $return; } } return $return; } /** * Check for a spacial type of URL. Currently includes mailto, tel, callto URL types. * * @param string $url * @return bool */ public function url_is_extra( $url ){ $allowed = array( 'mailto', 'tel', 'callto' ); $parsed = parse_url($url); if (is_array($parsed) && isset( $parsed['scheme'] )){ return in_array( $parsed['scheme'], $allowed ); } else { return false; } } /** * Get language code slug to use in url. * * @param string $language_code Full language code. * @param bool $accept_empty_return Whether to take into account the add-subdirectory-to-default-language setting. * @return string Url slug. */ public function get_url_slug( $language_code, $accept_empty_return = true ){ $url_slug = $language_code; if( isset( $this->settings['url-slugs'][$language_code] ) ) { $url_slug = $this->settings['url-slugs'][$language_code]; } if ( $accept_empty_return && isset( $this->settings['add-subdirectory-to-default-language'] ) && $this->settings['add-subdirectory-to-default-language'] == 'no' && $language_code == $this->settings['default-language'] ) { $url_slug = ''; } return $url_slug; } /** * Return absolute home url as stored in database, unfiltered. * * @return string */ public function get_abs_home() { $this->absolute_home = trp_cache_get('get_abs_home', 'trp'); if ( $this->absolute_home !== false ){ return $this->absolute_home; } global $wpdb; // returns the unfiltered home_url by directly retrieving it from wp_options. $this->absolute_home = $this->absolute_home ? $this->absolute_home : ( ! is_multisite() && defined( 'WP_HOME' ) ? WP_HOME : ( is_multisite() && ! is_main_site() ? ( preg_match( '/^(https)/', get_option( 'home' ) ) === 1 ? 'https://' : 'http://' ) . $wpdb->get_var( " SELECT CONCAT(b.domain, b.path) FROM {$wpdb->blogs} b WHERE blog_id = {$wpdb->blogid} LIMIT 1" ) : $wpdb->get_var( " SELECT option_value FROM {$wpdb->options} WHERE option_name = 'home' LIMIT 1" ) ) ); if( empty($this->absolute_home) ){ $this->absolute_home = get_option("siteurl"); } // home_url can have a space in front braking TP. $this->absolute_home = trim($this->absolute_home); if ( apply_filters('trp_adjust_absolute_home_https_based_on_server_variable', true) ) { // always return absolute_home based on the http or https version of the current page request. This means no more redirects. if ( !empty( $_SERVER['HTTPS'] ) && strtolower( sanitize_text_field( $_SERVER['HTTPS'] ) ) != 'off' ) { $this->absolute_home = str_replace( 'http://', 'https://', $this->absolute_home ); } else { $this->absolute_home = str_replace( 'https://', 'http://', $this->absolute_home ); } } $this->absolute_home = apply_filters('trp_filter_absolute_home_result', $this->absolute_home); wp_cache_set( 'get_abs_home', $this->absolute_home, 'trp' ); return $this->absolute_home; } /** * Return the language code from the url. * * Uses current url if none given. * * @param string $url Url. * @return string|null Language code or null if not found */ public function get_lang_from_url_string( $url = null ) { if ( ! $url ){ $url = $this->cur_page_url(); } $language = trp_cache_get('url_language_' . hash('md4', $url) , 'trp' ); if ( $language !== false ){ return $language; } $url_obj = trp_cache_get('url_obj_' . hash('md4', $url), 'trp'); if( $url_obj === false ){ $url_obj = new \TranslatePress\Uri($url); wp_cache_set('url_obj_' . hash('md4', $url), $url_obj, 'trp' ); } $abs_home_url_obj = trp_cache_get('url_obj_' . hash('md4', $this->get_abs_home() ), 'trp'); if( $abs_home_url_obj === false ){ $abs_home_url_obj = new \TranslatePress\Uri( $this->get_abs_home() ); wp_cache_set('url_obj_' . hash('md4', $this->get_abs_home()), $abs_home_url_obj, 'trp' ); } if( $url_obj->getPath() ){ if ($abs_home_url_obj->getPath() == "/"){ $abs_home_url_obj->setPath(''); } $abs_home = $abs_home_url_obj->getPath(); //in some cases $abs_home_url_obj->getPath() can be null and this causes a PHP 8 notice if ($abs_home !== null) { $abs_home = $abs_home_url_obj->getPath(); }else{ $abs_home = ''; } //we make sure that the path is the actual path and not a folder $possible_path = trp_remove_prefix($abs_home, $url_obj->getPath()); $lang = ltrim( $possible_path,'/' ); $lang = explode('/', $lang); if( $lang == false ){ wp_cache_set('url_language_' . hash('md4', $url), null, 'trp'); return null; } // If we have a language in the URL, the first element of the array should be it. $lang = $lang[0]; $lang = apply_filters( 'trp_get_lang_from_url_string', $lang, $url ); // the lang slug != actual lang. So we need to do array_search so we don't end up with en instead of en_US if( isset($this->settings['url-slugs']) && in_array($lang, $this->settings['url-slugs']) ){ $language = array_search($lang, $this->settings['url-slugs']); if ( in_array( $language, $this->settings['publish-languages'] ) || ( in_array( $language, $this->settings['translation-languages'] ) && current_user_can(apply_filters( 'trp_translating_capability', 'manage_options' )) ) ) { wp_cache_set( 'url_language_' . hash( 'md4', $url ), $language, 'trp' ); return $language; } } } wp_cache_set('url_language_' . hash('md4', $url), null, 'trp'); return null; } /** * Return current page url. * Always using $this->get_abs_home(), instead of home_url() since that one is filtered by TP * Function is cached for both bool values of $translated_slugs * * @return string * * The returned value is the current url with the language slug in it. (ex: /en/ ) * The actual path slugs will be translated or not according to the $translated_slugs parameter. * * The function may return the translated slugs even though false was passed if the function is called before * plugins_loaded priority 1. Basically before \TRP_IN_SP_Slug_Manager::translate_request_uri() * Caching of untranslated slugs url is deleted there in order to properly regenerate that version */ public function cur_page_url( $translated_slugs = true ) { $translated_slugs = ( $translated_slugs ) ? '_translated_slugs' : '_untranslated_slugs'; $req_uri = trp_cache_get( 'cur_page_url' . $translated_slugs, 'trp' ); if ( $req_uri ){ return $req_uri; } $req_uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( $_SERVER['REQUEST_URI'] ) : ''; // strval converts null to empty string. $this->get_abs_home() can be null and this causes a PHP 8 notice. $abs_home = strval( $this->get_abs_home() ); $abs_home_path_url = parse_url($abs_home, PHP_URL_PATH); $home_path = ($abs_home_path_url !== null )? trim($abs_home_path_url, '/') : ''; $home_path_regex = sprintf( '|^%s|i', preg_quote( $home_path, '|' ) ); // Trim path info from the end and the leading home path from the front. $req_uri = ltrim( $req_uri, '/' ); $req_uri = preg_replace( $home_path_regex, '', $req_uri ); $req_uri = trim( $abs_home, '/' ) . '/' . ltrim( $req_uri, '/' ); if ( function_exists('apply_filters') ) $req_uri = apply_filters('trp_curpageurl', $req_uri); wp_cache_set( 'cur_page_url' . $translated_slugs, $req_uri, 'trp' ); return $req_uri; } /** * we need to modify the permalinks structure for woocommerce when we switch languages * when woo registers post_types and taxonomies in the rewrite parameter of the function they change the slugs of the items (they are localized with _x ) * we can't flush the permalinks on every page load so we filter the rewrite_rules option */ public function woocommerce_filter_permalinks_on_other_languages( $rewrite_rules ){ // Only when SEO Pack add-on is disabled. Otherwise, SEO Pack uses a different system to handle this if ( class_exists( 'WooCommerce' ) && !class_exists( 'TRP_IN_Seo_Pack' ) ) { global $TRP_LANGUAGE; if( $TRP_LANGUAGE != $this->settings['default-language'] ){ global $default_language_wc_permalink_structure; //we use a global because apparently you can't do switch to locale and restore multiple times. I should keep an eye on this /* get rewrite rules from original language */ if( empty($default_language_wc_permalink_structure) ) { $default_language_wc_permalink_structure = trp_get_transient( 'tp_default_language_wc_permalink_structure_'.$this->settings['default-language'] ); if( $default_language_wc_permalink_structure === false ) { $default_language_wc_permalink_structure = array(); $default_language_wc_permalink_structure['product_rewrite_slug'] = trp_x('product', 'slug', 'woocommerce', $this->settings['default-language']); $default_language_wc_permalink_structure['category_rewrite_slug'] = trp_x('product-category', 'slug', 'woocommerce', $this->settings['default-language']); $default_language_wc_permalink_structure['tag_rewrite_slug'] = trp_x('product-tag', 'slug', 'woocommerce', $this->settings['default-language']); set_transient('tp_default_language_wc_permalink_structure_' . $this->settings['default-language'], $default_language_wc_permalink_structure, 12 * HOUR_IN_SECONDS); } } $current_language_permalink_structure = trp_get_transient( 'tp_current_language_wc_permalink_structure_'.$TRP_LANGUAGE ); if( $current_language_permalink_structure === false ) { //always generate the slugs for defaults on the current language $current_language_permalink_structure = array(); $current_language_permalink_structure['product_rewrite_slug'] = trp_x('product', 'slug', 'woocommerce', $TRP_LANGUAGE); $current_language_permalink_structure['category_rewrite_slug'] = trp_x('product-category', 'slug', 'woocommerce', $TRP_LANGUAGE); $current_language_permalink_structure['tag_rewrite_slug'] = trp_x('product-tag', 'slug', 'woocommerce', $TRP_LANGUAGE); set_transient( 'tp_current_language_wc_permalink_structure_'.$TRP_LANGUAGE, $current_language_permalink_structure, 12 * HOUR_IN_SECONDS ); } $new_rewrite_rules = array(); $search = array( '/^'.$default_language_wc_permalink_structure['product_rewrite_slug'].'\//', '/^'.$default_language_wc_permalink_structure['category_rewrite_slug'].'\//', '/^'.$default_language_wc_permalink_structure['tag_rewrite_slug'].'\//' ); $replace = array( $current_language_permalink_structure['product_rewrite_slug'].'/', $current_language_permalink_structure['category_rewrite_slug'].'/', $current_language_permalink_structure['tag_rewrite_slug'].'/' ); if( !empty( $rewrite_rules ) && is_array($rewrite_rules) ) { foreach ($rewrite_rules as $rewrite_key => $rewrite_values) { $new_rewrite_rules[preg_replace($search, $replace, $rewrite_key)] = preg_replace($search, $replace, $rewrite_values); } } } } if( !empty($new_rewrite_rules) ) { return $new_rewrite_rules; } else return $rewrite_rules; } /* on frontend on other languages dinamically generate the woo permalink structure for the default slugs */ public function woocommerce_filter_permalink_option( $value ){ $trp = TRP_Translate_Press::get_trp_instance(); $upgrade = $trp->get_component( 'upgrade' ); if ( class_exists( 'TRP_IN_Seo_Pack' ) && $upgrade->is_seo_pack_minimum_version_met() && ( !isset( $this->settings['trp_advanced_settings']['load_legacy_seo_pack'] ) || $this->settings['trp_advanced_settings']['load_legacy_seo_pack'] === 'no' ) ) { return $value; } // Only when SEO Pack add-on is disabled. Otherwise, SEO Pack uses a different system to handle this global $TRP_LANGUAGE, $trp_wc_permalinks; //keep the unfiltered value in a global, we might need it later if( !isset( $trp_wc_permalinks ) ) $trp_wc_permalinks = $value; if( $TRP_LANGUAGE != $this->settings['default-language'] ) { if( trim($value['product_base'], '/') === trp_x( 'product', 'slug', 'woocommerce', $this->settings['default-language'] ) ){ $value['product_base'] = ''; /* in ajax it seems the language is not set correctly and we get the slug for the original language if we leave it blank. detected in sober theme Will only do it for products for now as I am not 100% sure it won't impact other things */ if( wp_doing_ajax() ){ $value['product_base'] = trp_x( 'product', 'slug', 'woocommerce', $TRP_LANGUAGE ); } }else{ // if the custom base permalink starts with product, WooCommerce will translate it when on other languages if ( substr( $value['product_base'], 0, strlen('/product/' ) ) === '/product/' ) { $value['product_base'] = substr_replace( $value['product_base'], '/' . trp_x( 'product', 'slug', 'woocommerce', $TRP_LANGUAGE ) . '/', 0, strlen('/product/' ) ); } } if( trim($value['category_base'], '/') === trp_x( 'product-category', 'slug', 'woocommerce', $this->settings['default-language'] ) ){ $value['category_base'] = ''; } if( trim($value['tag_base'], '/') === trp_x( 'product-tag', 'slug', 'woocommerce', $this->settings['default-language'] ) ){ $value['tag_base'] = ''; } } return $value; } /** * Prevent the rewrite_rules option to change when we are not on the default language so we don't get translated data in the database * Basically update_option for rewrite_rules does nothing * @param $value * @param $old_value * @return mixed */ public function prevent_permalink_update_on_other_languages( $value, $old_value ){ global $TRP_LANGUAGE; if( apply_filters( 'trp_keep_permalinks_unchanged', false ) || ( isset($TRP_LANGUAGE) && $TRP_LANGUAGE != $this->settings['default-language'] && apply_filters( 'trp_prevent_permalink_update_on_other_languages', true ) ) ) { $value = $old_value; } return $value; } /** * Function that deletes old woocommerce transients so the new one are generated correctly * @param $value * @return void */ public function delete_woocommerce_transient_permalink($value){ // Only when SEO Pack add-on is disabled. Otherwise, SEO Pack uses a different system to handle this if( class_exists( 'WooCommerce' ) && !class_exists( 'TRP_IN_Seo_Pack' ) ) { $english_woocommerce_slugs = array( 'product-category', 'product-tag', 'product', 'default_language_wc_permalink_structure', 'current_language_wc_permalink_structure' ); foreach ( $english_woocommerce_slugs as $english_woocommerce_slug ) { delete_transient( 'tp_' . $english_woocommerce_slug . '_' . $this->settings['default-language'] ); foreach ( $this->settings['translation-languages'] as $language ) { delete_transient( 'tp_' . $english_woocommerce_slug . '_' . $language ); } } } return $value; } /** * Function that adds pagination to a blog page if it is necessary * @param $url * @return string */ function maybe_add_pagination_to_blog_page( $url ){ $pagenum = get_query_var( 'paged' ); if( !empty( $pagenum ) ) { global $wp_rewrite; $url = trailingslashit( $url ) . user_trailingslashit($wp_rewrite->pagination_base . '/' . $pagenum, 'paged' ); } return $url; } /** * Try to get the value that is displayed in the Permalinks settings * @param $english_woocommerce_slug * @return false|mixed|void */ function trp_get_woocommerce_saved_permalink( $english_woocommerce_slug ){ $wc_options = get_option('woocommerce_permalinks'); switch($english_woocommerce_slug){ case 'product-category': $option_index = 'category_base'; break; case 'product-tag': $option_index = 'tag_base'; break; case 'product': $option_index = 'product_base'; break; default: $option_index = ''; } if( !empty( $wc_options ) && !empty( $wc_options[$option_index] ) ) return trim( $wc_options[$option_index], '/' ); elseif( empty( $wc_options[$option_index] ) ){//if it's the default from _x() it won't save in the db $current_slug = trp_get_transient( 'tp_'.$english_woocommerce_slug.'_'. $this->settings['default-language'] ); if( $current_slug === false ){ $current_slug = trp_x( $english_woocommerce_slug, 'slug', 'woocommerce', $this->settings['default-language'] ); set_transient( 'tp_'.$english_woocommerce_slug.'_'. $this->settings['default-language'], $current_slug, 12 * HOUR_IN_SECONDS ); } return $current_slug; } else return $english_woocommerce_slug;//always return something } /** * Takes the URL as a parameters and returns its path with no language slug * * Duplicated function for SEO Pack * * @param $url * @return string */ public function get_path_no_lang_slug_from_url( $url ) { $language = $this->get_lang_from_url_string( $url ); $url_lang_slug = $language !== null ? $this->get_url_slug( $language ) : ''; $url_object = trp_cache_get( 'url_obj_' . hash( 'md4', $url ), 'trp' ); if ( $url_object === false ) { $url_object = new \TranslatePress\Uri( $url ); wp_cache_set( 'url_obj_' . hash( 'md4', $url ), $url_object, 'trp' ); } // null or empty string if ( empty( $url_lang_slug ) ) { $path_no_lang_slug = $url_object->getPath(); } else { $path_no_lang_slug = preg_replace( '/\/' . preg_quote( $url_lang_slug, '/' ) . '\/?/', '/', $url_object->getPath(), 1 ); } // Returning the path using strval() to avoid an empty check. return strval( $path_no_lang_slug ); } } includes/class-edd-sl-plugin-updater.php 0000777 00000120566 15251156640 0014322 0 ustar 00 <?php // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) exit; /** * Allows plugins to use their own update API. * * @author Easy Digital Downloads * @version 1.6.13 */ if( !class_exists('TRP_EDD_SL_Plugin_Updater') ) { class TRP_EDD_SL_Plugin_Updater { private $api_url = ''; private $api_data = array(); private $name = ''; private $slug = ''; private $version = ''; private $wp_override = false; private $cache_key = ''; private $beta = ''; /** * Class constructor. * * @uses plugin_basename() * @uses hook() * * @param string $_api_url The URL pointing to the custom API endpoint. * @param string $_plugin_file Path to the plugin file. * @param array $_api_data Optional data to send with API calls. */ public function __construct($_api_url, $_plugin_file, $_api_data = null) { global $edd_plugin_data; $this->api_url = trailingslashit($_api_url); $this->api_data = $_api_data; $this->name = plugin_basename($_plugin_file); $this->slug = basename($_plugin_file, '.php'); // IMPORTANT TranslatePress modification. if ( $this->slug === 'index') { // $this->slug is the add-on file name. For Deepl and Translator accounts the file name is 'index' causing a conflict. $this->slug = dirname( plugin_basename( $_plugin_file ) ); } // end modification $this->version = $_api_data['version']; $this->wp_override = isset($_api_data['wp_override']) ? (bool)$_api_data['wp_override'] : false; $this->beta = !empty($this->api_data['beta']) ? true : false; $this->cache_key = md5(serialize($this->slug . $this->api_data['license'] . $this->beta)); $edd_plugin_data[$this->slug] = $this->api_data; // Set up hooks. $this->init(); } /** * Set up WordPress filters to hook into WP's update process. * * @uses add_filter() * * @return void */ public function init() { add_filter('pre_set_site_transient_update_plugins', array($this, 'check_update')); add_filter('plugins_api', array($this, 'plugins_api_filter'), 10, 3); remove_action('after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10); add_action('after_plugin_row_' . $this->name, array($this, 'show_update_notification'), 10, 2); add_action('admin_init', array($this, 'show_changelog')); } /** * Check for Updates at the defined API endpoint and modify the update array. * * This function dives into the update API just when WordPress creates its update array, * then adds a custom API call and injects the custom plugin data retrieved from the API. * It is reassembled from parts of the native WordPress plugin update code. * See wp-includes/update.php line 121 for the original wp_update_plugins() function. * * @uses api_request() * * @param array $_transient_data Update array build by WordPress. * @return array Modified update array with custom plugin data. */ public function check_update($_transient_data) { global $pagenow; if (!is_object($_transient_data)) { $_transient_data = new stdClass; } if ('plugins.php' == $pagenow && is_multisite()) { return $_transient_data; } if (!empty($_transient_data->response) && !empty($_transient_data->response[$this->name]) && false === $this->wp_override) { return $_transient_data; } $version_info = $this->get_cached_version_info(); if (false === $version_info) { $version_info = $this->api_request('plugin_latest_version', array('slug' => $this->slug, 'beta' => $this->beta)); $this->set_version_info_cache($version_info); } if (false !== $version_info && is_object($version_info) && isset($version_info->new_version)) { if (version_compare($this->version, $version_info->new_version, '<')) { $_transient_data->response[$this->name] = $version_info; } $_transient_data->last_checked = current_time('timestamp'); $_transient_data->checked[$this->name] = $this->version; } return $_transient_data; } /** * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise! * * @param string $file * @param array $plugin */ public function show_update_notification($file, $plugin) { if (is_network_admin()) { return; } if (!current_user_can('update_plugins')) { return; } if (!is_multisite()) { return; } if ($this->name != $file) { return; } // Remove our filter on the site transient remove_filter('pre_set_site_transient_update_plugins', array($this, 'check_update'), 10); $update_cache = get_site_transient('update_plugins'); $update_cache = is_object($update_cache) ? $update_cache : new stdClass(); if (empty($update_cache->response) || empty($update_cache->response[$this->name])) { $version_info = $this->get_cached_version_info(); if (false === $version_info) { $version_info = $this->api_request('plugin_latest_version', array('slug' => $this->slug, 'beta' => $this->beta)); $this->set_version_info_cache($version_info); } if (!is_object($version_info)) { return; } if (version_compare($this->version, $version_info->new_version, '<')) { $update_cache->response[$this->name] = $version_info; } $update_cache->last_checked = current_time('timestamp'); $update_cache->checked[$this->name] = $this->version; set_site_transient('update_plugins', $update_cache); } else { $version_info = $update_cache->response[$this->name]; } // Restore our filter add_filter('pre_set_site_transient_update_plugins', array($this, 'check_update')); if (!empty($update_cache->response[$this->name]) && version_compare($this->version, $version_info->new_version, '<')) { // build a plugin list row, with update notification $wp_list_table = _get_list_table('WP_Plugins_List_Table'); # <tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"> echo '<tr class="plugin-update-tr" id="' . esc_attr( $this->slug ) . '-update" data-slug="' . esc_attr( $this->slug ) . '" data-plugin="' . esc_attr( $this->slug ) . '/' . esc_attr( $file ) . '">'; echo '<td colspan="3" class="plugin-update colspanchange">'; echo '<div class="update-message notice inline notice-warning notice-alt">'; $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'); if (empty($version_info->download_link)) { printf( __('There is a new version of %1$s available. %2$sView version %3$s details%4$s.', 'translatepress-multilingual'), //phpcs:ignore esc_html($version_info->name), '<a target="_blank" class="thickbox" href="' . esc_url($changelog_link) . '">', esc_html($version_info->new_version), '</a>' ); // get license status $license_status = get_option( 'trp_license_status' ); if( !empty($license_status) ) { $license_state = trp_get_license_status(); if( $license_state === 'expired' ) { // [utm5] printf( __('To enable updates, your licence needs to be renewed. Please go to the %1$sTranslatePress Account%2$s page and login to renew.', 'translatepress-multilingual'), //phpcs:ignore '<a target="_blank" href="https://translatepress.com/account/?utm_source=wp-plugins-page&utm_medium=client-site&utm_campaign=expired-license">', '</a>' ); } elseif( $license_state !== 'valid' ){ printf( __('To enable updates, please go to the %1$slicense page%2$s and check that you have a valid license.', 'translatepress-multilingual'), //phpcs:ignore esc_url( admin_url( 'admin.php?page=trp_license_key' ) ), '</a>' ); } } else{ // [utm6] printf( __('To enable updates, please %1$senter your license key%2$s. Need a license key? %3$sPurchase one now%4$s.', 'translatepress-multilingual'), //phpcs:ignore esc_url( admin_url( 'admin.php?page=trp_license_key' ) ), '</a>', '<a target="_blank" href="https://translatepress.com/pricing/?utm_source=wp-plugins-page&utm_medium=client-site&utm_campaign=pro-no-active-license">', '</a>' ); } } else { printf( __('There is a new version of %1$s available. %2$sView version %3$s details%4$s or %5$supdate now%6$s.', 'translatepress-multilingual'), //phpcs:ignore esc_html($version_info->name), '<a target="_blank" class="thickbox" href="' . esc_url($changelog_link) . '">', esc_html($version_info->new_version), '</a>', '<a href="' . esc_url(wp_nonce_url(self_admin_url('update.php?action=upgrade-plugin&plugin=') . $this->name, 'upgrade-plugin_' . $this->name)) . '">', '</a>' ); } do_action("in_plugin_update_message-{$file}", $plugin, $version_info); echo '</div></td></tr>'; } } /** * Updates information on the "View version x.x details" page with custom data. * * @uses api_request() * * @param mixed $_data * @param string $_action * @param object $_args * @return object $_data */ public function plugins_api_filter($_data, $_action = '', $_args = null) { if ($_action != 'plugin_information') { return $_data; } if (!isset($_args->slug) || ($_args->slug != $this->slug)) { return $_data; } $to_send = array( 'slug' => $this->slug, 'is_ssl' => is_ssl(), 'fields' => array( 'banners' => array(), 'reviews' => false ) ); $cache_key = 'edd_api_request_' . md5(serialize($this->slug . $this->api_data['license'] . $this->beta)); // Get the transient where we store the api request for this plugin for 24 hours $edd_api_request_transient = $this->get_cached_version_info($cache_key); //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. if (empty($edd_api_request_transient)) { $api_response = $this->api_request('plugin_information', $to_send); // Expires in 3 hours $this->set_version_info_cache($api_response, $cache_key); if (false !== $api_response) { $_data = $api_response; } } else { $_data = $edd_api_request_transient; } // Convert sections into an associative array, since we're getting an object, but Core expects an array. if (isset($_data->sections) && !is_array($_data->sections)) { $new_sections = array(); foreach ($_data->sections as $key => $value) { $new_sections[$key] = $value; } $_data->sections = $new_sections; } // Convert banners into an associative array, since we're getting an object, but Core expects an array. if (isset($_data->banners) && !is_array($_data->banners)) { $new_banners = array(); foreach ($_data->banners as $key => $value) { $new_banners[$key] = $value; } $_data->banners = $new_banners; } return $_data; } /** * Disable SSL verification in order to prevent download update failures * * @param array $args * @param string $url * @return object $array */ public function http_request_args($args, $url) { $verify_ssl = $this->verify_ssl(); if (strpos($url, 'https://') !== false && strpos($url, 'edd_action=package_download')) { $args['sslverify'] = $verify_ssl; } return $args; } /** * Calls the API and, if successfull, returns the object delivered by the API. * * @uses get_bloginfo() * @uses wp_remote_post() * @uses is_wp_error() * * @param string $_action The requested action. * @param array $_data Parameters for the API action. * @return false|object */ private function api_request($_action, $_data) { global $wp_version; $data = array_merge($this->api_data, $_data); if ($data['slug'] != $this->slug) { return; } if ($this->api_url == trailingslashit(home_url())) { return false; // Don't allow a plugin to ping itself } $api_params = array( 'edd_action' => 'get_version', 'license' => !empty($data['license']) ? $data['license'] : '', 'item_name' => isset($data['item_name']) ? $data['item_name'] : false, 'item_id' => isset($data['item_id']) ? $data['item_id'] : false, 'version' => isset($data['version']) ? $data['version'] : false, 'slug' => $data['slug'], 'author' => $data['author'], 'url' => home_url(), 'beta' => !empty($data['beta']), ); $verify_ssl = $this->verify_ssl(); $request = wp_remote_post($this->api_url, array('timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params)); if (!is_wp_error($request)) { $request = json_decode(wp_remote_retrieve_body($request)); } if ($request && isset($request->sections)) { $request->sections = maybe_unserialize($request->sections); } else { $request = false; } if ($request && isset($request->banners)) { $request->banners = maybe_unserialize($request->banners); } if (!empty($request->sections)) { foreach ($request->sections as $key => $section) { $request->$key = (array)$section; } } return $request; } public function show_changelog() { global $edd_plugin_data; if (empty($_REQUEST['edd_sl_action']) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action']) { return; } if (empty($_REQUEST['plugin'])) { return; } if (empty($_REQUEST['slug'])) { return; } if (!current_user_can('update_plugins')) { wp_die( esc_html__('You do not have permission to install plugin updates', 'translatepress-multilingual'), esc_html__('Error', 'translatepress-multilingual'), array('response' => 403)); } $data = $edd_plugin_data[sanitize_text_field( $_REQUEST['slug'] )]; $beta = !empty($data['beta']) ? true : false; $cache_key = md5('edd_plugin_' . sanitize_key($_REQUEST['plugin']) . '_' . $beta . '_version_info'); $version_info = $this->get_cached_version_info($cache_key); if (false === $version_info) { $api_params = array( 'edd_action' => 'get_version', 'item_name' => isset($data['item_name']) ? $data['item_name'] : false, 'item_id' => isset($data['item_id']) ? $data['item_id'] : false, 'slug' => sanitize_text_field( $_REQUEST['slug'] ), 'author' => $data['author'], 'url' => home_url(), 'beta' => !empty($data['beta']) ); $verify_ssl = $this->verify_ssl(); $request = wp_remote_post($this->api_url, array('timeout' => 15, 'sslverify' => $verify_ssl, 'body' => $api_params)); if (!is_wp_error($request)) { $version_info = json_decode(wp_remote_retrieve_body($request)); } if (!empty($version_info) && isset($version_info->sections)) { $version_info->sections = maybe_unserialize($version_info->sections); } else { $version_info = false; } if (!empty($version_info)) { foreach ($version_info->sections as $key => $section) { $version_info->$key = (array)$section; } } $this->set_version_info_cache($version_info, $cache_key); } if (!empty($version_info) && isset($version_info->sections['changelog'])) { echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $version_info->sections['changelog'] ) . '</div>'; } exit; } public function get_cached_version_info($cache_key = '') { if (empty($cache_key)) { $cache_key = $this->cache_key; } $cache = get_option($cache_key); if (empty($cache['timeout']) || current_time('timestamp') > $cache['timeout']) { return false; // Cache is expired } return json_decode($cache['value']); } public function set_version_info_cache($value = '', $cache_key = '') { if (empty($cache_key)) { $cache_key = $this->cache_key; } $data = array( 'timeout' => strtotime('+3 hours', current_time('timestamp')), 'value' => json_encode($value) ); update_option($cache_key, $data); } /** * Returns if the SSL of the store should be verified. * * @since 1.6.13 * @return bool */ private function verify_ssl() { return (bool)apply_filters('edd_sl_api_request_verify_ssl', true, $this); } } } if( !class_exists('TRP_LICENSE_PAGE') ) { class TRP_LICENSE_PAGE { public function __construct(){ } public function license_menu() { add_submenu_page( 'TRPHidden', 'TranslatePress License', 'TRPHidden', 'manage_options', 'trp_license_key', array($this, 'license_page') ); } public function register_license_setting(){ register_setting( 'trp_license_key', 'trp_license_key', array( $this, 'sanitize_license_key' ) ); } public function sanitize_license_key( $license_key ) { return sanitize_text_field( trim( $license_key ) ); } public function license_page() { $trp = TRP_Translate_Press::get_trp_instance(); // force check license when accessing the License Tab. $trp->get_component('plugin_updater')->force_check_license('true'); $license = get_option('trp_license_key'); // don't show the license in html $license = str_repeat("*", strlen($license)); $status = get_option('trp_license_status'); $details = get_option('trp_license_details'); $action = 'options.php'; ob_start(); require TRP_PLUGIN_DIR . 'partials/license-settings-page.php'; echo ob_get_clean();//phpcs:ignore } public function license_activation_message() { if ( isset( $_GET['trp_sl_activation'] ) && ! empty( $_GET['message'] ) && isset( $_GET['trp_license_nonce'] ) && wp_verify_nonce( sanitize_text_field( $_GET['trp_license_nonce'] ), 'trp_license_display_message' ) ) { return wp_kses_post( urldecode( $_GET['message'] ) );//phpcs:ignore } return ''; } } } class TRP_Plugin_Updater{ private $store_url; public function __construct(){ // Use constant from wp-config.php if defined, otherwise use default URL $this->store_url = defined('TRP_STORE_URL') ? TRP_STORE_URL : "https://translatepress.com"; } protected function get_option( $license_key_option ){ return get_option( $license_key_option ); } protected function delete_option( $license_key_option ){ delete_option( $license_key_option ); } protected function update_option( $license_key_option, $value ){ update_option( $license_key_option, $value ); } protected function license_page_url( ){ return admin_url( 'admin.php?page=trp_license_key' ); } public function edd_sanitize_license( $new ) { $new = sanitize_text_field($new); $old = $this->get_option( 'trp_license_key' ); if( $old && $old != $new ) { $this->delete_option( 'trp_license_status' ); // new license has been entered, so must reactivate } return $new; } /** * This function is run when wordpress checks for updates ( twice a day I believe ) * @param $transient_data * @return mixed */ public function check_license( $transient_data ){ if( empty( $transient_data->response ) ) return $transient_data; if ( false === ( $trp_check_license = get_transient( 'trp_checked_licence' ) ) ) { $this->force_check_license(); set_transient( 'trp_checked_licence', 'yes', DAY_IN_SECONDS ); } return $transient_data; } /** * This function is run when accessing the license page. * @return null */ public function force_check_license($api_cache_bypass = 'false'){ $license = trim( $this->get_option( 'trp_license_key' ) ); $license_information_for_all_addons = array(); $license_status = 'invalid'; // by default this is invalid. $trp = TRP_Translate_Press::get_trp_instance(); if (!empty($trp->tp_product_name)) { foreach ($trp->tp_product_name as $active_pro_addon_name) { // data to send in our API request $api_params = array( 'edd_action' => 'activate_license', //as the license is already activated this does not do anything. We could use check_license action but it gives different results so we can't use it consistently with the result we get from the moment we activate it 'license' => $license, 'item_name' => urlencode($active_pro_addon_name), // the name of our product in EDD 'url' => home_url() ); if($api_cache_bypass){ $api_params['cache_bypass'] = $api_cache_bypass; } if( !empty( $license ) || get_option( 'trp_plugin_optin' ) == 'yes' ){ $api_params['machine_translated_strings_data'] = json_encode( get_option( 'trp_machine_translated_characters', array() ), JSON_HEX_QUOT ); } // Store debug information in transients with obfuscated license $debug_params = $api_params; if (!empty($debug_params['license']) && strlen($debug_params['license']) > 10) { $debug_params['license'] = substr($debug_params['license'], 0, 5) . str_repeat('*', strlen($debug_params['license']) - 10) . substr($debug_params['license'], -5); } set_transient('trp_debug_force_check_license_request', array( 'url' => $this->store_url, 'params' => $debug_params, 'timestamp' => current_time('mysql') ), 60); // Call the custom API. $response = wp_remote_post($this->store_url, array('timeout' => 15, 'sslverify' => false, 'body' => $api_params)); // Store response debug information set_transient('trp_debug_force_check_license_response', array( 'response_code' => is_wp_error($response) ? 'ERROR' : wp_remote_retrieve_response_code($response), 'response_body' => is_wp_error($response) ? $response->get_error_message() : wp_remote_retrieve_body($response), 'timestamp' => current_time('mysql') ), 60); // make sure the response came back okay if (!is_wp_error($response)) { $license_data = json_decode(wp_remote_retrieve_body($response)); $license_status = $license_data->license; // $license_data->license will be either "valid" or "invalid" if (false === $license_data->success) { $license_information_for_all_addons['invalid'][] = $license_data; break;//we only need one failure } else { $license_information_for_all_addons['valid'][] = $license_data; } } } } //store the license reponse for each addon in the database $this->update_option('trp_license_details', $license_information_for_all_addons); // $license_data->license will be either "valid" or "invalid" $this->update_option( 'trp_license_status', $license_status ); if( !$license ){ //we need to throw a notice if we have a pro addon active and no license entered $license_information_for_all_addons['invalid'][] = (object) array( 'error' => 'missing' ); $this->update_option('trp_license_details', $license_information_for_all_addons); } } /* * This is triggered on admin_init inside class-translate-press.php * It's stupid and should be refactored so it's in the same flow as the license_page() function in TRP_LICENSE_PAGE class * The messages are duplicated in the includes/onboarding/class-license.php since we can't use this function as it is */ public function activate_license() { // listen for our activate button to be clicked if( isset( $_POST['trp_edd_license_activate'] ) ) { // run a quick security check if( ! check_admin_referer( 'trp_license_nonce', 'trp_license_nonce' ) ) return; // get out if we didn't click the Activate button if( !current_user_can( 'manage_options' ) ) return; if ( isset( $_POST['trp_license_key'] ) && preg_match('/^[*]+$/', $_POST['trp_license_key']) && strlen( $_POST['trp_license_key'] ) > 5 ) { //phpcs:ignore // pressed submit without altering the existing license key (containing only * as outputted by default) // useful for Deactivating/Activating valid license back $license = get_option('trp_license_key', ''); }else{ // save the license $license = $this->edd_sanitize_license( trim( $_POST['trp_license_key'] ) );//phpcs:ignore $this->update_option( 'trp_license_key', $license ); } $message = array();//we will check the license for each addon and we will sotre the messages in an array $license_information_for_all_addons = array(); $trp = TRP_Translate_Press::get_trp_instance(); if( !empty( $trp->tp_product_name ) ){ foreach ($trp->tp_product_name as $active_pro_addon_name ){ // data to send in our API request $api_params = array( 'edd_action' => 'activate_license', 'cache_bypass' => 'true', 'license' => $license, 'item_name' => urlencode( $active_pro_addon_name ), // the name of our product in EDD 'url' => home_url() ); if( !empty( $license ) || get_option( 'trp_plugin_optin' ) == 'yes' ){ $api_params['machine_translated_strings_data'] = json_encode( get_option( 'trp_machine_translated_characters', array() ), JSON_HEX_QUOT ); } // Store debug information in transients with obfuscated license $debug_params = $api_params; if (!empty($debug_params['license']) && strlen($debug_params['license']) > 10) { $debug_params['license'] = substr($debug_params['license'], 0, 5) . str_repeat('*', strlen($debug_params['license']) - 10) . substr($debug_params['license'], -5); } set_transient('trp_debug_activate_license_request', array( 'url' => $this->store_url, 'params' => $debug_params, 'timestamp' => current_time('mysql') ), 60); // Call the custom API. $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) ); // Store response debug information set_transient('trp_debug_activate_license_response', array( 'response_code' => is_wp_error($response) ? 'ERROR' : wp_remote_retrieve_response_code($response), 'response_body' => is_wp_error($response) ? $response->get_error_message() : wp_remote_retrieve_body($response), 'timestamp' => current_time('mysql') ), 60); // make sure the response came back okay if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { $response_error_message = ''; if ( is_wp_error( $response ) && ! empty( $response->get_error_message() ) ) { $response_error_message = $response->get_error_message(); } $message[] = ! empty( $response_error_message ) ? $response_error_message : __( 'An error occurred, please try again.', 'translatepress-multilingual' ); } else { $license_data = json_decode( wp_remote_retrieve_body( $response ) ); if ( false === $license_data->success ) { switch( $license_data->error ) { case 'expired' : $message[] = sprintf( __( 'Your license key expired on %s.', 'translatepress-multilingual' ), date_i18n( get_option( 'date_format' ), strtotime( $license_data->expires, current_time( 'timestamp' ) ) ) ); break; case 'revoked' : $message[] = __( 'Your license key has been disabled.', 'translatepress-multilingual' ); break; case 'missing' : $message[] = __( 'Your TranslatePress license key is invalid or missing.', 'translatepress-multilingual' ); break; case 'invalid' : case 'site_inactive' : //[utm7] $message[] = __( 'Your license key is disabled for this URL. Re-enable it from <a target="_blank" href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-deactivated">https://translatepress.com/account</a> -> Manage Sites.', 'translatepress-multilingual' ); break; case 'item_name_mismatch' : $message[] = __( '<p><strong>License key mismatch.</strong> The license you entered doesn’t match the TranslatePress version you have installed.</p><p>Please check that you’ve installed the correct version for your license from your TranslatePress account.</p>' , 'translatepress-multilingual' ); if( !empty( $license_data->item_name ) && urldecode( $license_data->item_name ) === 'TranslatePress' ) { $message[] = "<p>" . __( 'If you have only the free plugin installed but added a paid license, please install the paid plugin from your TranslatePress account.' , 'translatepress-multilingual' ) . "</p>"; } break; case 'no_activations_left': $message[] = __( 'Your license key has reached its activation limit.', 'translatepress-multilingual' ); if( !empty( $license_data->item_name ) && urldecode( $license_data->item_name ) !== 'TranslatePress Developer' ) //[utm8] $message[] = sprintf( __( 'Upgrade your plan to add more sites. %1$sUpgrade now%2$s', 'translatepress-multilingual' ), '<a href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=activation-limit" target="_blank" class="button-primary">', '</a>' ); break; case 'website_already_on_free_license': $message[] = __( 'This website is already activated under a free license. Each website can only use one free license.', 'translatepress-multilingual' ); break; default : $message[] = __( 'An error occurred, please try again.', 'translatepress-multilingual' ); break; } $license_information_for_all_addons['invalid'][] = $license_data; } else{ $license_information_for_all_addons['valid'][] = $license_data; trp_mtapi_sync_license_call( $license ); } } } } //store the license reponse for each addon in the database $this->update_option( 'trp_license_details', $license_information_for_all_addons ); // Check if anything passed on a message constituting a failure if ( ! empty( $message ) ) { $message = implode( "<br/>", array_unique($message) );//if we got the same message for multiple addons show just one, and add a br in case we show multiple messages $redirect = add_query_arg( array( 'trp_sl_activation' => 'false', 'message' => urlencode( $message ), 'trp_license_nonce' => wp_create_nonce('trp_license_display_message') ), $this->license_page_url() ); wp_redirect( $redirect ); exit(); } // $license_data->license will be either "valid" or "invalid" $this->update_option( 'trp_license_status', $license_data->license ); wp_redirect( add_query_arg( array( 'trp_sl_activation' => 'true', 'message' => urlencode( __( 'You have successfully activated your license', 'translatepress-multilingual' ) ), 'trp_license_nonce' => wp_create_nonce('trp_license_display_message')), $this->license_page_url() ) ); exit(); } } function deactivate_license() { // listen for our activate button to be clicked if( isset( $_POST['trp_edd_license_deactivate'] ) ) { // run a quick security check if( ! check_admin_referer( 'trp_license_nonce', 'trp_license_nonce' ) ) return; // get out if we didn't click the Activate button if( !current_user_can( 'manage_options' ) ) return; // retrieve the license from the database $license = trim( $this->get_option( 'trp_license_key' ) ); $trp = TRP_Translate_Press::get_trp_instance(); if( !empty( $trp->tp_product_name ) ){ foreach ($trp->tp_product_name as $active_pro_addon_name ){//this loop will actually run just once, as we redirect at the end in all cases // data to send in our API request $api_params = array( 'edd_action' => 'deactivate_license', 'license' => $license, 'item_name' => urlencode( $active_pro_addon_name ), // the name of our product in EDD 'url' => home_url() ); // Call the custom API. $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) ); // make sure the response came back okay if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { if ( is_wp_error( $response ) ) { $message = $response->get_error_message(); } else { $message = __( 'An error occurred, please try again.', 'translatepress-multilingual' ); } $redirect = add_query_arg( array( 'trp_sl_activation' => 'false', 'message' => urlencode( $message ), 'trp_license_nonce' => wp_create_nonce('trp_license_display_message') ), $this->license_page_url() ); wp_redirect( $redirect ); exit(); } // decode the license data $license_data = json_decode( wp_remote_retrieve_body( $response ) ); // $license_data->license will be either "deactivated" or "failed" // regardless, we delete the record in the client website. Otherwise, if he tries to add a new license, he can't. if( $license_data->license == 'deactivated' || $license_data->license == 'failed') { delete_option( 'trp_license_status' ); delete_option( 'trp_license_details' ); } wp_redirect( $this->license_page_url() ); exit(); } } } } } includes/class-support-chat.php 0000777 00000041107 15251156640 0012636 0 ustar 00 <?php /** * Support Chat Widget * * Displays a chat-like popup showing recent WordPress.org forum topics * and encourages users to open support tickets. * * @package TranslatePress * @since 3.0.9 */ if ( ! defined( 'ABSPATH' ) ) exit; /** * Class TRP_Support_Chat * * Handles the support chat widget functionality */ class TRP_Support_Chat { /** * RSS Feed URL for the plugin support forum */ const FEED_URL = 'https://wordpress.org/support/plugin/translatepress-multilingual/feed/'; /** * Support forum URL */ const FORUM_URL = 'https://wordpress.org/support/plugin/translatepress-multilingual/'; /** * New topic URL */ const NEW_TOPIC_URL = 'https://wordpress.org/support/plugin/translatepress-multilingual/#new-topic-0'; /** * Transient key for caching forum posts */ const CACHE_KEY = 'trp_support_forum_posts'; /** * Cache duration in seconds (1 hour) */ const CACHE_DURATION = HOUR_IN_SECONDS; /** * Number of posts to display */ const POSTS_COUNT = 5; /** * User meta key for last viewed timestamp */ const LAST_VIEWED_META_KEY = 'trp_support_chat_last_viewed'; /** * Instance */ private static $instance = null; /** * Get instance */ public static function get_instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ private function __construct() { // Only load for free version users in admin $tp_product_name = TRP_Translate_Press::set_tp_product_name_static(); if ( ! array_key_exists( 'translatepress-multilingual', $tp_product_name ) ) { return; } add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_action( 'admin_footer', array( $this, 'render_chat_widget' ) ); add_action( 'wp_ajax_trp_get_forum_posts', array( $this, 'ajax_get_forum_posts' ) ); add_action( 'wp_ajax_trp_mark_forum_posts_read', array( $this, 'ajax_mark_forum_posts_read' ) ); } /** * Check if we should show the chat widget * * @return bool */ private function should_show_widget() { if ( ! $this->is_translatepress_page() ) { return false; } $screen = get_current_screen(); if ( $screen && $screen->id === 'admin_page_trp-onboarding' ) { return false; } return true; } /** * Check if current page is a TranslatePress admin page * * @return bool */ private function is_translatepress_page() { if ( ! is_admin() ) { return false; } $screen = get_current_screen(); if ( ! $screen ) { return false; } // Check for TranslatePress pages $trp_pages = array( 'settings_page_translate-press', 'admin_page_trp_license_key', 'admin_page_trp_addons_page', 'admin_page_trp_advanced_page', 'admin_page_trp_machine_translation', 'admin_page_trp_test_machine_api', 'admin_page_trp_optin_page', 'admin_page_trp_update_database', 'admin_page_trp_language_switcher', 'admin_page_trp-onboarding', 'admin_page_trp_error_manager', ); foreach ( $trp_pages as $page ) { if ( $screen->id === $page ) { return true; } } return false; } /** * Enqueue assets */ public function enqueue_assets() { if ( ! $this->should_show_widget() ) { return; } wp_enqueue_style( 'trp-support-chat', TRP_PLUGIN_URL . 'assets/css/trp-support-chat.css', array(), TRP_PLUGIN_VERSION ); wp_enqueue_script( 'trp-support-chat', TRP_PLUGIN_URL . 'assets/js/trp-support-chat.js', array(), TRP_PLUGIN_VERSION, true ); // Compute new post count from cached data, fetching if needed on first visit $new_count = 0; $last_viewed = (int) get_user_meta( get_current_user_id(), self::LAST_VIEWED_META_KEY, true ); $cached_posts = get_transient( self::CACHE_KEY ); if ( false === $cached_posts ) { $cached_posts = $this->get_forum_posts(); } if ( is_array( $cached_posts ) ) { foreach ( $cached_posts as $post ) { if ( ! empty( $post['timestamp'] ) && $post['timestamp'] > $last_viewed ) { $new_count++; } } } wp_localize_script( 'trp-support-chat', 'trpSupportChat', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'trp_support_chat' ), 'forumUrl' => self::FORUM_URL, 'newTopicUrl' => self::NEW_TOPIC_URL, 'newCount' => $new_count, 'lastViewed' => $last_viewed, 'strings' => array( 'title' => __( 'Need Help?', 'translatepress-multilingual' ), 'subtitle' => __( 'Recent community discussions', 'translatepress-multilingual' ), 'loading' => __( 'Loading...', 'translatepress-multilingual' ), 'error' => __( 'Unable to load forum posts', 'translatepress-multilingual' ), 'askQuestion' => __( 'Ask a Question', 'translatepress-multilingual' ), 'viewAll' => __( 'View All Topics', 'translatepress-multilingual' ), 'postedBy' => __( 'by', 'translatepress-multilingual' ), 'encourageTitle' => __( 'Have a question?', 'translatepress-multilingual' ), 'encourageText' => __( 'Get help directly from the plugin developers, suggest improvements, or share your feedback!', 'translatepress-multilingual' ), 'tipTitle' => __( 'Tip for faster help:', 'translatepress-multilingual' ), 'tipText' => __( 'Include what you tried, what you expected, and what happened. Screenshots help!', 'translatepress-multilingual' ), ), ) ); } /** * Render chat widget HTML */ public function render_chat_widget() { if ( ! $this->should_show_widget() ) { return; } ?> <div id="trp-support-chat-widget" class="trp-support-chat" style="display: none;"> <!-- Chat Toggle Button - Bar style --> <button type="button" class="trp-support-chat__toggle" aria-label="<?php esc_attr_e( 'Toggle support chat', 'translatepress-multilingual' ); ?>"> <span class="trp-support-chat__toggle-content"> <span class="trp-support-chat__toggle-icon trp-support-chat__toggle-icon--chat"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"> <path d="M12 2C6.48 2 2 6.04 2 11c0 2.21.89 4.22 2.34 5.75L2 22l5.25-2.34C8.78 20.53 10.35 21 12 21c5.52 0 10-4.04 10-9s-4.48-9-10-9zm0 16c-1.34 0-2.62-.29-3.78-.82l-.37-.18-2.49 1.11.98-2.58-.28-.4C4.74 13.98 4 12.55 4 11c0-3.87 3.59-7 8-7s8 3.13 8 7-3.59 7-8 7z"/> <circle cx="8" cy="11" r="1.5"/> <circle cx="12" cy="11" r="1.5"/> <circle cx="16" cy="11" r="1.5"/> </svg> </span> <span class="trp-support-chat__toggle-icon trp-support-chat__toggle-icon--close"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor"> <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> </svg> </span> <span class="trp-support-chat__toggle-text"> <?php esc_html_e( 'Need Help?', 'translatepress-multilingual' ); ?> <span class="trp-support-chat__toggle-subtext"><?php esc_html_e( 'Ask the community', 'translatepress-multilingual' ); ?></span> </span> </span> <span class="trp-support-chat__badge"></span> </button> <!-- Chat Window --> <div class="trp-support-chat__window"> <!-- Header --> <div class="trp-support-chat__header"> <div class="trp-support-chat__header-content"> <div class="trp-support-chat__avatar"> <img src="<?php echo esc_url( TRP_PLUGIN_URL . 'assets/images/tp-logo-square-light.svg' ); ?>" alt="TranslatePress" width="32" height="32"> </div> <div class="trp-support-chat__header-text"> <h4 class="trp-support-chat__title"></h4> <p class="trp-support-chat__subtitle"></p> </div> </div> <button type="button" class="trp-support-chat__close" aria-label="<?php esc_attr_e( 'Close', 'translatepress-multilingual' ); ?>"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="currentColor"> <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> </svg> </button> </div> <!-- Body --> <div class="trp-support-chat__body"> <!-- Encourage section - at top --> <div class="trp-support-chat__encourage"> <div class="trp-support-chat__encourage-card"> <h5 class="trp-support-chat__encourage-title"></h5> <p class="trp-support-chat__encourage-text"></p> </div> <div class="trp-support-chat__tip"> <strong class="trp-support-chat__tip-title"></strong> <p class="trp-support-chat__tip-text"></p> </div> </div> <!-- Recent discussions label --> <div class="trp-support-chat__section-label"></div> <!-- Loading state --> <div class="trp-support-chat__loading"> <div class="trp-support-chat__spinner"></div> <span></span> </div> <!-- Posts list --> <div class="trp-support-chat__posts"></div> </div> <!-- Footer --> <div class="trp-support-chat__footer"> <a href="<?php echo esc_url( self::NEW_TOPIC_URL ); ?>" target="_blank" rel="noopener" class="trp-support-chat__btn trp-support-chat__btn--primary"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"> <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> </svg> <span></span> </a> <a href="<?php echo esc_url( self::FORUM_URL ); ?>" target="_blank" rel="noopener" class="trp-support-chat__btn trp-support-chat__btn--secondary"> <span></span> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"> <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/> </svg> </a> </div> </div> </div> <?php } /** * AJAX handler to get forum posts */ public function ajax_get_forum_posts() { check_ajax_referer( 'trp_support_chat', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => 'Unauthorized' ) ); } $posts = $this->get_forum_posts(); if ( is_wp_error( $posts ) ) { wp_send_json_error( array( 'message' => $posts->get_error_message() ) ); } wp_send_json_success( array( 'posts' => $posts ) ); } /** * AJAX handler to mark forum posts as read */ public function ajax_mark_forum_posts_read() { check_ajax_referer( 'trp_support_chat', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => 'Unauthorized' ) ); } update_user_meta( get_current_user_id(), self::LAST_VIEWED_META_KEY, time() ); wp_send_json_success(); } /** * Get forum posts from RSS feed * * @return array|WP_Error */ private function get_forum_posts() { // Try to get from cache $cached = get_transient( self::CACHE_KEY ); if ( false !== $cached ) { return $cached; } // Fetch RSS feed $response = wp_remote_get( self::FEED_URL, array( 'timeout' => 4, 'sslverify' => true, ) ); if ( is_wp_error( $response ) ) { set_transient( self::CACHE_KEY, array(), self::CACHE_DURATION ); return $response; } $body = wp_remote_retrieve_body( $response ); if ( empty( $body ) ) { set_transient( self::CACHE_KEY, array(), self::CACHE_DURATION ); return new WP_Error( 'empty_feed', __( 'Empty feed response', 'translatepress-multilingual' ) ); } // Parse XML libxml_use_internal_errors( true ); $xml = simplexml_load_string( $body ); if ( false === $xml ) { set_transient( self::CACHE_KEY, array(), self::CACHE_DURATION ); return new WP_Error( 'parse_error', __( 'Unable to parse feed', 'translatepress-multilingual' ) ); } $posts = array(); $count = 0; if ( isset( $xml->channel->item ) ) { foreach ( $xml->channel->item as $item ) { if ( $count >= self::POSTS_COUNT ) { break; } // Get dc:creator namespace $dc = $item->children( 'http://purl.org/dc/elements/1.1/' ); // Clean up title - strip HTML tags and decode entities $title = (string) $item->title; $title = strip_tags( $title ); $title = html_entity_decode( $title, ENT_QUOTES, 'UTF-8' ); $title = trim( $title ); $posts[] = array( 'title' => $title, 'link' => (string) $item->link, 'date' => $this->format_date( (string) $item->pubDate ), 'timestamp' => (int) strtotime( (string) $item->pubDate ), 'author' => isset( $dc->creator ) ? (string) $dc->creator : '', ); $count++; } } // Cache the results set_transient( self::CACHE_KEY, $posts, self::CACHE_DURATION ); return $posts; } /** * Format date for display * * @param string $date_string * @return string */ private function format_date( $date_string ) { $timestamp = strtotime( $date_string ); if ( ! $timestamp ) { return ''; } $now = time(); $diff = $now - $timestamp; // Less than a day ago if ( $diff < DAY_IN_SECONDS ) { $hours = floor( $diff / HOUR_IN_SECONDS ); if ( $hours < 1 ) { return __( 'Just now', 'translatepress-multilingual' ); } /* translators: %d: number of hours */ return sprintf( _n( '%d hour ago', '%d hours ago', $hours, 'translatepress-multilingual' ), $hours ); } // Less than a week ago if ( $diff < WEEK_IN_SECONDS ) { $days = floor( $diff / DAY_IN_SECONDS ); /* translators: %d: number of days */ return sprintf( _n( '%d day ago', '%d days ago', $days, 'translatepress-multilingual' ), $days ); } // More than a week, show date return date_i18n( get_option( 'date_format' ), $timestamp ); } } // Initialize add_action( 'admin_init', array( 'TRP_Support_Chat', 'get_instance' ) ); includes/class-install-plugins.php 0000777 00000011603 15251156640 0013330 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Install_Plugins { public function get_plugin_slugs() { $slugs = array( 'pb' => array( 'all_slugs' => array( 'profile-builder/index.php', 'profile-builder-hobbyist/index.php', 'profile-builder-pro/index.php' ), 'install_slug' => 'profile-builder/index.php', 'plugin_zip' => 'https://downloads.wordpress.org/plugin/profile-builder.zip' ), 'pms' => array( 'all_slugs' => array( 'paid-member-subscriptions/index.php' ), 'install_slug' => 'paid-member-subscriptions/index.php', 'plugin_zip' => 'https://downloads.wordpress.org/plugin/paid-member-subscriptions.zip' ), 'wha' => array( 'all_slugs' => array( 'wp-webhooks/wp-webhooks.php' ), 'install_slug' => 'wp-webhooks/wp-webhooks.php', 'plugin_zip' => 'https://downloads.wordpress.org/plugin/wp-webhooks.3.3.1.zip' ) ); return apply_filters( 'trp_plugin_install_slugs', $slugs ); } public function install_plugins_request(){ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { check_ajax_referer( 'trp_install_plugins', 'security' ); if ( ! current_user_can( 'install_plugins' ) ) { wp_die( -1, 403 ); } if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_install_plugins' && !empty( $_POST['plugin_slug'] ) ) { $plugin_slug = sanitize_text_field($_POST['plugin_slug']); $short_slugs = $this->get_plugin_slugs(); if ( isset( $short_slugs[$plugin_slug]) ){ if ( $this->install_upgrade_activate($plugin_slug) ){ $message = esc_html__('Active', 'translatepress-multilingual'); }else{ $message = wp_kses( sprintf( __('Could not install. Try again from <a href="%s" >Plugins Dashboard.</a>', 'translatepress-multilingual'), admin_url('plugins.php') ), array('a' => array( 'href' => array() ) ) ); } wp_die( trp_safe_json_encode( $message ));//phpcs:ignore } } } wp_die(); } public function install_upgrade_activate( $short_slug ) { $short_slugs = $this->get_plugin_slugs(); $install_slug = $short_slugs[ $short_slug ]['install_slug']; $plugin_zip = $short_slugs[ $short_slug ]['plugin_zip']; if ( $this->is_plugin_installed( $short_slug ) ) { $this->upgrade_plugin( $install_slug ); $installed = true; } else { $installed = $this->install_plugin( $plugin_zip ); } if ( !is_wp_error( $installed ) && $installed ) { $activate = activate_plugin( $install_slug ); if ( is_null( $activate ) ) { return true; } } return false; } public function is_plugin_installed( $short_slug ) { $short_slugs = $this->get_plugin_slugs(); $all_slugs = $short_slugs[ $short_slug ]['all_slugs']; if ( !function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = get_plugins(); foreach( $all_slugs as $slug ) { if ( !empty( $all_plugins[ $slug ] ) ) { return true; } } return false; } public function is_plugin_active($short_slug){ $short_slugs = $this->get_plugin_slugs(); $all_slugs = $short_slugs[ $short_slug ]['all_slugs']; foreach( $all_slugs as $slug ) { if ( is_plugin_active( $slug ) ) { return true; } } return false; } public function install_plugin( $plugin_zip ) { include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; wp_cache_flush(); $upgrader = new Plugin_Upgrader(); // do not output any messages $upgrader->skin = new Automatic_Upgrader_Skin(); $installed = $upgrader->install( $plugin_zip ); return $installed; } public function upgrade_plugin( $plugin_slug ) { include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; wp_cache_flush(); $upgrader = new Plugin_Upgrader(); // do not output any messages $upgrader->skin = new Automatic_Upgrader_Skin(); $upgraded = $upgrader->upgrade( $plugin_slug ); return $upgraded; } } if( !function_exists( 'wppb_activate_plugin_redirect' ) ){ function wppb_activate_plugin_redirect(){ // do nothing, just override pb function in order to not redirect on activation } } includes/class-machine-translator-logger.php 0000777 00000027056 15251156640 0015264 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Machine_Translator_Logger { protected $settings; protected $query; protected $url_converter; protected $counter_date; protected $limit; protected $error_manager; /** * TRP_Machine_Translator_Logger constructor. * * @param array $settings Settings option. */ public function __construct( $settings ){ $this->settings = $settings; $this->counter_date = $this->get_mt_option('machine_translation_counter_date', date ("Y-m-d" )); $this->limit = intval( $this->get_mt_option('machine_translation_limit', 1000000) ); // if a new day has passed, update the counter and date $this->maybe_reset_counter_date(); add_action('trp_is_deepl_glossary_id_valid', array( $this, 'show_notice_if_glossary_id_invalid'), 10, 1 ); } public function get_todays_character_count() { if ( $this->quota_exceeded() ) { return $this->limit; } else { return $this->get_current_counter(); } } public function log( $args = array() ){ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->query ) $this->query = $trp->get_component('query'); if ( ! $this->url_converter ) $this->url_converter = $trp->get_component('url_converter'); if( empty($args) ) return false; if( $this->get_mt_option('machine_translation_log', false) !== 'yes' ) return false; if( !$this->query->check_machine_translation_log_table() ) return false; // expected structure. $log = array( 'url' => $this->url_converter->cur_page_url(), 'strings' => $args['strings'], 'characters' => $this->count(unserialize($args['strings'])), 'response' => $args['response'], 'lang_source' => $args['lang_source'], 'lang_target' => $args['lang_target'], 'timestamp' => date ("Y-m-d H:i:s" ) ); $table_name = $this->query->db->prefix . 'trp_machine_translation_log'; $query = "INSERT INTO `$table_name` ( `url`, `strings`, `characters`, `response`, `lang_source`, `lang_target`, `timestamp` ) VALUES (%s, %s, %s, %s, %s, %s, %s)"; $prepared_query = $this->query->db->prepare( $query, $log ); $this->query->db->get_results( $prepared_query, OBJECT_K ); if ( $this->query->db->last_error !== '' ) return false; return true; } private function count($strings){ if( !is_array($strings) ) return 0; $char_number = 0; foreach($strings as $string) $char_number += strlen($string); return $char_number; } public function count_towards_quota($strings){ $count = $this->count($strings); $this->count_machine_translated_characters( $count ); return $this->increase_counter_with_value( $count ); } /** * Increase existing counter with the provided value. * It does NOT replace the existing counter with the provided value. It adds to it. * * Uses a query that locks read on specific table row to avoid concurrency issues * * Returns the new character count after update * * @param $number_of_characters * @return int */ public function increase_counter_with_value( $number_of_characters ){ global $wpdb; $set_transient = false; // Start transaction $wpdb->query( 'START TRANSACTION;' ); // Query to select the option value $select_query = " SELECT option_value FROM {$wpdb->options} WHERE option_name = 'trp_machine_translation_counter' LIMIT 1 FOR UPDATE; "; $pre_update_character_count = $wpdb->get_var( $select_query ); if ( $pre_update_character_count === null ) { // option not set yet $insert_query = $wpdb->prepare( " INSERT INTO {$wpdb->options} (option_name, option_value) VALUES ('trp_machine_translation_counter', %d ); ", $number_of_characters ); $wpdb->query( $insert_query ); $pre_update_character_count = 0; $set_transient = true; } else { // Query to update the option value $update_query = $wpdb->prepare( " UPDATE {$wpdb->options} SET option_value = %d WHERE option_name = 'trp_machine_translation_counter'; ", $pre_update_character_count + $number_of_characters ); $wpdb->query( $update_query ); } // Commit the transaction $wpdb->query( 'COMMIT;' ); if ( $set_transient === true ){ $transient = get_transient('trp_machine_translation_counter_safety_reset'); if ( $transient ){ $wpdb->last_error = 'Machine translation counter was reset twice in a day. Unless an intentional action was performed on the DB that would affect trp_machine_translation_counter option from wp_options, please check for automatic translation character counting issues.'; }else{ set_transient('trp_machine_translation_counter_safety_reset', true, 12 * HOUR_IN_SECONDS); } } if ( !empty( $wpdb->last_error ) ) { if ( !$this->error_manager ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $this->error_manager->record_error( array( 'last_error_updating_character_count' => $wpdb->last_error, 'disable_automatic_translations' => true ) ); delete_transient('trp_machine_translation_counter_safety_reset'); if ( !is_numeric( $pre_update_character_count ) ) { $pre_update_character_count = 0; } } return $pre_update_character_count + $number_of_characters; } /** * Use only if really needed. It always performs a query that is never cached. * * Used instead of get_option() to bypass caching * * @return void */ public function get_current_counter() { global $wpdb; $select_query = " SELECT option_value FROM {$wpdb->options} WHERE option_name = 'trp_machine_translation_counter' LIMIT 1 "; $character_count = $wpdb->get_var( $select_query ); // option not set yet if ( $character_count === null ){ $character_count = 0; } if ( !empty( $wpdb->last_error ) ) { if ( !$this->error_manager ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $this->error_manager->record_error( array( 'last_error_selecting_character_count' => $wpdb->last_error, 'disable_automatic_translations' => true ) ); $character_count = null; } return $character_count; } /** * Use only if really needed. It always performs a query that is never cached. * * @return bool */ public function quota_exceeded(){ $counter = $this->get_current_counter(); if ( $counter !== null && $this->limit >= $counter ) { // quota NOT exceeded // for some reason this condition is hard to comprehend by my brain // thus the unneeded comment. return false; } // we've exceeded our daily quota $this->update_options( array( array( 'name' => 'machine_translation_trigger_quota_notification', 'value' => true ) ) ); return true; } public function maybe_reset_counter_date(){ // if the day has not passed if ( $this->counter_date === date ( "Y-m-d" ) ) return false; $options = array( // there is a new day array( 'name' => 'machine_translation_counter_date', 'value' => date( "Y-m-d" ), ), // clear the notification array( 'name' => 'machine_translation_trigger_quota_notification', 'value' => false, ), ); $this->update_options( $options ); // clear the counter update_option('trp_machine_translation_counter', 0); return true; } private function get_mt_option($option_name, $default){ return isset( $this->settings['trp_machine_translation_settings'][$option_name] ) ? $this->settings['trp_machine_translation_settings'][$option_name] : $default; } private function update_options( $options ){ $machine_translation_settings = $this->settings['trp_machine_translation_settings']; foreach( $options as $option ){ $this->settings['trp_machine_translation_settings'][$option['name']] = $option['value']; $machine_translation_settings[$option['name']] = $option['value']; } update_option( 'trp_machine_translation_settings', $machine_translation_settings ); } public function sanitize_settings($mt_settings ){ $machine_translation_settings = $this->settings['trp_machine_translation_settings']; if( isset( $machine_translation_settings['machine_translation_counter_date'] ) ) $mt_settings['machine_translation_counter_date'] = $machine_translation_settings['machine_translation_counter_date']; if( !empty( $mt_settings['machine_translation_log'] ) ) $mt_settings['machine_translation_log'] = sanitize_text_field( $mt_settings['machine_translation_log'] ); else $mt_settings['machine_translation_log'] = 'no'; return $mt_settings; } public function count_machine_translated_characters( $count ){ $machine_translated_characters = get_option( 'trp_machine_translated_characters', array() ); $current_month = date( 'm-Y' ); if( isset( $machine_translated_characters[ $current_month ] ) ) $machine_translated_characters[ $current_month ] = $machine_translated_characters[ $current_month ] + $count; else $machine_translated_characters[ $current_month ] = $count; update_option( 'trp_machine_translated_characters', $machine_translated_characters, false ); } public function show_notice_if_glossary_id_invalid( $response ){ global $wpdb; if ( is_array( $response ) && ! is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code']) && $response['response']['code'] !== 200 ) { $response_body = json_decode( $response['body'] ); if ( isset( $response_body->message ) ) { if ( !$this->error_manager ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } if ( strpos( strtolower( $response_body->message), 'glossary' ) !== false ) { $wpdb->last_error = ' The glossary ID provided for DeepL translation request was invalid. Please check again'; $this->error_manager->record_error( array( 'glossary_id_is_invalid' => $wpdb->last_error, 'disable_automatic_translations' => true ) ); } } } } } includes/gutenberg-blocks/ls-shortcode/block.json 0000777 00000001714 15251156640 0016221 0 ustar 00 { "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "trp/language-switcher", "title": "Language Switcher", "category": "trp-block", "description": "Displays the Language Switcher.", "keywords": [ "Language Switcher", "Translate", "TranslatePress", "Language Names", "Flags" ], "textdomain": "translatepress-multilingual", "attributes": { "display_setting": { "type": "string", "default": "" }, "is_preview": { "type": "boolean", "default": false }, "is_editor": { "type": "boolean", "default": false } }, "example": { "attributes": { "display_setting": "", "is_preview": true, "is_editor": true } }, "editorStyle": "file:../../../assets/css/trp-language-switcher-v2.css", "editorScript": "trp-block-ls-shortcode" } includes/gutenberg-blocks/ls-shortcode/ls-shortcode.php 0000777 00000036213 15251156640 0017355 0 ustar 00 <?php // Exit if accessed directly if ( !defined( 'ABSPATH' ) ) exit; /** * Register: PHP. */ add_action( 'init', function () { wp_register_script( 'trp-block-ls-shortcode', add_query_arg( [ 'action' => 'trp-block-ls-shortcode.js', ], admin_url( 'admin-ajax.php' ) ), [ 'wp-blocks', 'wp-element', 'wp-editor' ], microtime(), true ); register_block_type( __DIR__, [ 'render_callback' => function ( $attributes, $content ) { ob_start(); do_action( 'trp/language-switcher/render_callback', $attributes, $content ); return ob_get_clean(); }, ] ); } ); /** * Render: PHP. * * @param array $attributes Optional. Block attributes. Default empty array. * @param string $content Optional. Block content. Default empty string. */ add_action( 'trp/language-switcher/render_callback', function ( $attributes, $content ) { if ( $attributes['is_preview'] ) { echo '<style> .trp-language-switcher{ position: relative; display: inline-block; padding: 0; border: 0; margin: 2px; box-sizing: border-box; } .trp-language-switcher > div { box-sizing: border-box; padding:3px 20px 3px 5px; border: 1px solid #c1c1c1; border-radius: 3px; background-image: linear-gradient(45deg, transparent 50%, gray 50%), linear-gradient(135deg, gray 50%, transparent 50%); background-position: calc(100% - 8px) calc(1em + 0px), calc(100% - 3px) calc(1em + 0px); background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; background-color: #fff; } .trp-language-switcher > div > a { display: block; padding: 2px; border-radius: 3px; color: rgb(7, 105, 173); } .trp-language-switcher > div > a:hover { background: #f1f1f1; } .trp-language-switcher > div > a.trp-ls-shortcode-disabled-language { cursor: default; } .trp-language-switcher > div > a.trp-ls-shortcode-disabled-language:hover { background: none; } .trp-language-switcher > div > a > img{ display: inline; margin: 0 3px; width: 18px; height: 12px; border-radius: 0; } .trp-language-switcher .trp-ls-shortcode-current-language{ display: inline-block; } .trp-language-switcher:focus .trp-ls-shortcode-current-language, .trp-language-switcher:hover .trp-ls-shortcode-current-language{ visibility: hidden; } .trp-language-switcher .trp-ls-shortcode-language{ display: inline-block; height: 1px; overflow: hidden; visibility: hidden; z-index: 1; max-height: 250px; overflow-y: auto; left: 0; top: 0; min-height: auto; } .trp-language-switcher:focus .trp-ls-shortcode-language, .trp-language-switcher:hover .trp-ls-shortcode-language{ visibility: visible; max-height: 250px; height: auto; overflow-y: auto; position: absolute; left: 0; top: 0; display: inline-block !important; min-height: auto; } </style>'; } $atts = [ 'display_setting' => ($attributes['display_setting'] !== '') ? ' display="' . esc_html( $attributes['display_setting'] ) . '"' : '', 'is_editor' => ($attributes['is_editor']) ? ' is_editor="true"' : '', ]; echo '<div class="trp-block-container">' . do_shortcode( '[language-switcher ' . $atts['display_setting'] . $atts['is_editor'] . ']' ) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped }, 10, 2 ); /** * Register: JavaScript. */ add_action( 'wp_ajax_trp-block-ls-shortcode.js', function () { header( 'Content-Type: text/javascript' ); $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings_object = $trp->get_component( 'settings' ); $ls_options = $trp_settings_object->get_language_switcher_options(); unset( $ls_options['full-names-no-html'] ); //only menu ls has this option ?> ( function ( blocks, i18n, element, serverSideRender, blockEditor, components ) { var { __ } = i18n; var el = element.createElement; var PanelBody = components.PanelBody; var SelectControl = components.SelectControl; var ToggleControl = components.ToggleControl; var TextControl = components.TextControl; var InspectorControls = wp.editor.InspectorControls; blocks.registerBlockType( 'trp/language-switcher', { icon: el('svg', { width: 24, height: 24, viewBox: '0 0 500 500' }, el( 'path', { d: "M29.77 482c-1.7 0-5.23 0-8.16-2.56-3.51-3.07-3.5-7.37-3.5-9 .1-139.89.11-286.19 0-447.26 0-1.47 0-5.38 3-8.38s6.91-3 8.38-3q106.14.06 212.26.06c80.88 0 160 0 235-.08 1.51 0 5.51 0 8.55 3s3 6.84 3 8.68c-.08 62.79-.07 128.25-.07 186V249l-5.86 1.62a11.12 11.12 0 0 1-3 .42 10.74 10.74 0 0 1-2.13-.21c-.17 29-.14 58.39-.11 86.86v106.25c0 16.58-10.33 26.89-26.88 26.9H347.81c-24.82 0-57.54 0-90.77.19a12.33 12.33 0 0 1 .15 3.89l-.85 7h-73.92c-48.95 0-101.31 0-152.59.08z", fill: "#fff" } ), el( 'path', { d: "M247.88 481.93l-2.73-1c-1.08-.38-2.18-.76-3.26-1.17-13.8-5.25-22-15.65-23.77-30.09a44.93 44.93 0 0 1-.3-5.38V249.2c0-18.11 10.26-32.09 26.73-36.51a42.83 42.83 0 0 1 11.28-1.19h121.55c4.88 0 7.53 1.48 8.89 3.42a16.24 16.24 0 0 1 8.86-3.36c1-.08 2-.09 2.73-.09h47.94c23.72 0 33.52 6.67 41.93 28.54l.53 1.39v210.26l-.14.74-.32 1.78c-.92 5.18-2 11.06-6.44 16.51a28.87 28.87 0 0 1-21.58 10.78l-.55.46zm115.31-241.35H257c-8.17 0-10.05 1.87-10.05 10v192c0 8.35 1.68 10 10 10h192.42c7.94 0 9.66-1.75 9.66-9.85V250.37c0-8-1.74-9.76-9.81-9.77H398c-.79 0-1.81 0-2.91-.1-7.17-.58-12.37-5-13.64-11.39a13.73 13.73 0 0 1-10.83 10.95 28.27 28.27 0 0 1-6.08.53z", } ), el( 'path', { d: "M359.24 240.44l.55-28.92h41.79v13.1h.51l-.07 16zM119.8 283.19h-62c-23.85 0-38.67-14.9-38.68-38.85V50.95c0-22.81 15.3-38.14 38.05-38.14h194.29c22.71 0 38 15.14 38 37.68.07 38.86.05 78.36 0 116.57v28.14a23.57 23.57 0 0 1-.74 6.58A13.84 13.84 0 0 1 275 212.09h-.3c-7-.14-12.29-4.38-13.76-11.08a28.78 28.78 0 0 1-.5-6.57V52.41c0-8.86-1.57-10.42-10.49-10.42H57.9c-7.7 0-9.58 1.86-9.58 9.47v192.93c0 8 1.76 9.69 9.88 9.69h142.05c6.84 0 11.3 1.45 14.47 4.7a13.71 13.71 0 0 1 3.71 10.22c-.14 5.29-2.68 14.16-18.13 14.16z", } ), el( 'path', { d: "M197.07 223.84a12.71 12.71 0 0 1-6.27-1.68 153.34 153.34 0 0 1-17.54-11.71 166 166 0 0 1-18.95-16.27A192 192 0 0 1 121 220.33l-.58.36c-.7.45-1.52 1-2.46 1.46a14.1 14.1 0 0 1-6.43 1.63 11.63 11.63 0 0 1-10.08-5.73c-3.6-5.93-1.82-12.77 4.33-16.64a183.08 183.08 0 0 0 22.94-16.59c1.56-1.34 3.11-2.8 4.61-4.21s3.1-2.9 4.71-4.32A198.71 198.71 0 0 1 112 134.67l-.29-.61a26.52 26.52 0 0 1-1.17-2.67c-2.43-6.63.22-13.11 6.45-15.76a12.42 12.42 0 0 1 4.88-1 11.89 11.89 0 0 1 10.68 7.1c2.82 5.78 6 12.33 9.63 18.36a194.55 194.55 0 0 0 12.17 18 188 188 0 0 0 26.35-47.7c-5.38 0-11.24.05-18.43.05H92.51c-4.64 0-8.42-1.36-10.93-3.92a11.25 11.25 0 0 1-3.18-8.28c.09-5.5 3.89-11.94 14.29-12h25.9c9.12 0 16.66 0 23.58.09v-.64c.24-9.33 6.27-13.54 12.13-13.54s11.86 4.26 12 13.71v.4h50.14a14.5 14.5 0 0 1 7.23 1.51 11.67 11.67 0 0 1 6.19 12.85 11.44 11.44 0 0 1-11.38 9.76c-2.34.07-4.67.11-6.93.11-1.86 0-3.7 0-5.49-.07a197.76 197.76 0 0 1-35.59 66 181.87 181.87 0 0 0 29.12 23l1 .62a33.7 33.7 0 0 1 3.07 2c5.34 4 6.74 10.86 3.34 16.26a11.78 11.78 0 0 1-10 5.6zm206.12 201.27c-2.93 0-10.15-1.07-14-10.94l-.67-1.74c-2.37-6.13-4.79-12.42-7.05-18.74-3.51 0-7.38.05-12 .05h-33.02c-4.59 0-8.42 0-11.91-.08-2.05 5.85-4.3 11.64-6.48 17.25l-1.31 3.4c-3.77 9.74-11 10.79-13.93 10.79a15 15 0 0 1-4.89-.85 14.17 14.17 0 0 1-8.45-7.39c-1.22-2.61-2.2-7 0-12.81 14.83-39 30.63-80.55 47-123.41 3.87-10.15 11.18-12.28 16.64-12.28h.45c7.69.18 13.51 4.54 16.43 12.28l21.06 55.84q12.72 33.69 25.38 67.29c3.4 9 .37 17-7.74 20.23a14.75 14.75 0 0 1-5.51 1.1zm-7.74-213.01c-6.64-.07-14.36-4.35-14.39-16.22v-38.93c-.08-21.95-14.77-36.77-36.59-37a14.36 14.36 0 0 1 1.73 5.41 14.11 14.11 0 0 1-3.2 10.48 14 14 0 0 1-11 5.34 16.51 16.51 0 0 1-9.82-3.46c-8-5.9-17.35-12.89-26.63-20-5.65-4.32-6.83-9.09-6.82-12.33s1.21-8 6.92-12.39c7.57-5.76 15.93-12 26.29-19.67a17.12 17.12 0 0 1 10.2-3.74 13.84 13.84 0 0 1 10.95 5.52 14.15 14.15 0 0 1 3.1 10.53 14.5 14.5 0 0 1-1.67 5.14 72.88 72.88 0 0 1 15.11 1.57 64.54 64.54 0 0 1 50.55 61.93c.18 11.06.13 22.27.08 33.11v8.64c0 9.59-5.9 16-14.61 16zM175.3 425.06A14.67 14.67 0 0 1 161.17 409a14.41 14.41 0 0 1 1.69-5.1c-38.09-.47-65.68-28.26-65.73-66.42V298.55c0-7.61 3.71-13.18 10.23-15.29a15.37 15.37 0 0 1 4.66-.74c8.08 0 14.21 6.68 14.26 15.54v38.56c0 23 14 37.44 36.63 38.13a13.64 13.64 0 0 1 .95-15.23 13.83 13.83 0 0 1 11.29-5.94 18.12 18.12 0 0 1 10.83 4c9.38 7 17.19 12.84 25.16 18.88 5 3.77 7.48 8.1 7.48 12.87 0 3.25-1.27 8.1-7.33 12.7-8.07 6.14-16.54 12.49-25.91 19.43a16.92 16.92 0 0 1-10.08 3.6z", } ), el( 'path', { d: "M339.85 353.73l5.82-15.22 7.53-19.73 7.43 19.77c1.82 4.82 3.75 10 5.74 15.21l4.1 10.84h-34.79z", fill: "#fff" } ), ), attributes: { display_setting : { type: 'string', default: '', }, is_preview : { type: 'boolean', default: false, }, is_editor : { type: 'boolean', default: true, }, }, edit: function ( props ) { return [ el( 'div', Object.assign( blockEditor.useBlockProps(), { key: 'trp/language-switcher/render' } ), el( serverSideRender, { block: 'trp/language-switcher', attributes: props.attributes, } ) ), <?php if ( $trp->get_component( 'language_switcher_tab' )->is_legacy_enabled() ) : ?> el( InspectorControls, { key: 'trp/language-switcher/inspector' }, [ el( PanelBody, { title: __( 'Language Switcher Settings' , 'translatepress-multilingual' ), key: 'trp/language-switcher/inspector/ls-settings' }, [ el( SelectControl, { label: __( 'Display' , 'translatepress-multilingual' ), key: 'trp/language_switcher/inspector/ls_settings/display_setting', help: __( 'Choose how to display the language names and whether to add flags.' , 'translatepress-multilingual' ), value: props.attributes.display_setting, options: [ { label: __( 'Default setting' , 'translatepress-multilingual' ), value: '' }, <?php foreach ( $ls_options as $key => $ls_option ) { ?> { label: '<?php echo esc_html( $ls_option['label'] ) ?>', value: '<?php echo esc_html( $key ) ?>' }, <?php } ?> ], onChange: ( value ) => { props.setAttributes( { display_setting: value } ); }, } ) ] ) ] ) <?php endif; ?> ]; } } ); } )( window.wp.blocks, window.wp.i18n, window.wp.element, window.wp.serverSideRender, window.wp.blockEditor, window.wp.components ); <?php exit; } ); includes/gutenberg-blocks/class-gutenberg-blocks.php 0000777 00000004423 15251156640 0016677 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Gutenberg_Blocks { private $settings; public function __construct( $settings ) { $this->settings = $settings; include_once( TRP_PLUGIN_DIR . 'includes/gutenberg-blocks/ls-shortcode/ls-shortcode.php' ); include_once( TRP_PLUGIN_DIR . 'includes/gutenberg-blocks/block-language-restriction/block-language-restriction.php' ); if ( version_compare( get_bloginfo( 'version' ), '5.8', '>=' ) ) { add_filter( 'block_categories_all', array( $this, 'register_layout_category' ) ); } else { add_filter( 'block_categories', array( $this, 'register_layout_category' ) ); } add_action( 'enqueue_block_editor_assets', array( $this, 'block_editor_enqueue' ) ); } public function block_editor_enqueue() { global $pagenow; $trp = TRP_Translate_Press::get_trp_instance(); if ( !$trp->get_component( 'language_switcher_tab' )->is_legacy_enabled() ) TRP_Language_Switcher_V2::instance()->enqueue_assets(); // only enqueue the assets if legacy is disabled if ( $pagenow === 'widgets.php' ) { $arrDeps = [ 'wp-blocks', 'wp-dom', 'wp-dom-ready', 'wp-edit-widgets', 'lodash' ]; } elseif ( $pagenow === 'customize.php' ) { $arrDeps = [ 'wp-blocks', 'wp-dom', 'wp-dom-ready', 'lodash' ]; } else { $arrDeps = [ 'wp-blocks', 'wp-dom', 'wp-dom-ready', 'wp-edit-post', 'lodash' ]; } $languagesObject = $trp->get_component( 'languages' ); $published_languages = $languagesObject->get_language_names( $this->settings['publish-languages'] ); wp_enqueue_script( 'trp-block-language-restriction', TRP_PLUGIN_URL . 'includes/gutenberg-blocks/block-language-restriction/build/index.js', $arrDeps, TRP_PLUGIN_VERSION ); wp_localize_script('trp-block-language-restriction', 'trpBlockEditorData', [ 'all_languages' => $published_languages, 'plugin_url' => TRP_PLUGIN_URL ] ); } public function register_layout_category( $categories ) { $categories[] = array( 'slug' => 'trp-block', 'title' => 'TranslatePress' ); return $categories; } } includes/gutenberg-blocks/block-language-restriction/block-language-restriction.php 0000777 00000004151 15251156640 0024771 0 ustar 00 <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly function trp_render_blocks( $block_content, $block ) { $block_attrs = $block['attrs']['TrpContentRestriction'] ?? null; // Abort if the block does not have the content restriction settings attribute if ( !isset( $block_attrs ) || empty( $block_attrs['selected_languages'] ) ) return $block_content; global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $languagesObject = $trp->get_component( 'languages' ); $settings = ( $trp->get_component( 'settings' ) )->get_settings(); $published_languages = $languagesObject->get_language_names( $settings['publish-languages'] ); $current_language_name = $published_languages[$TRP_LANGUAGE]; $should_exclude_block = $block_attrs['restriction_type'] === 'include' && !in_array( $current_language_name, $block_attrs['selected_languages'] ) || $block_attrs['restriction_type'] === 'exclude' && in_array( $current_language_name, $block_attrs['selected_languages'] ); if ( $should_exclude_block ) return ''; return $block_content; } add_filter( 'render_block', 'trp_render_blocks', 10, 2 ); /** * Adds the `trpContentRestriction` attribute to all blocks */ add_action( 'wp_loaded', 'trp_add_custom_attributes_to_blocks', 199 ); function trp_add_custom_attributes_to_blocks() { $registered_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered(); foreach( $registered_blocks as $name => $block ) { $block->attributes['TrpContentRestriction'] = [ 'type' => 'object', 'properties' => [ 'restriction_type' => [ 'type' => 'string', ], 'selected_languages' => [ 'type' => 'array', ], 'panel_open' => [ 'type' => 'boolean', ], ], 'default' => [ 'restriction_type' => 'exclude', 'selected_languages' => [], 'panel_open' => true, ], ]; } } includes/gutenberg-blocks/block-language-restriction/build/index.js 0000777 00000005455 15251156640 0021616 0 ustar 00 (()=>{"use strict";const e=window.React,t=window.lodash,n=window.wp.hooks,l=window.wp.compose,r=window.wp.i18n,s=window.wp.blockEditor,a=window.wp.components;function o({attributes:n,setAttributes:l}){const{TrpContentRestriction:s}=n,o=trpBlockEditorData.all_languages,i=Object.keys(o).map((e=>o[e])),c="include"===s.restriction_type?(0,r.__)("Choose in which languages to show the block.","translatepress-multilingual"):(0,r.__)("Choose from which languages the block is excluded.","translatepress-multilingual");return(0,e.createElement)(e.Fragment,null,(0,e.createElement)("p",null,c),(0,e.createElement)(a.__experimentalToggleGroupControl,{isBlock:!0,label:(0,r.__)("Content Restriction Mode","translatepress-multilingual"),value:s.restriction_type,onChange:e=>l({TrpContentRestriction:(0,t.assign)({...s},{restriction_type:e})})},(0,e.createElement)(a.__experimentalToggleGroupControlOption,{value:"include",label:(0,r.__)("Include","translatepress-multilingual")}),(0,e.createElement)(a.__experimentalToggleGroupControlOption,{value:"exclude",label:(0,r.__)("Exclude","translatepress-multilingual")})),(0,e.createElement)(a.FormTokenField,{label:(0,r.__)("Select language(s)","translatepress-multilingual"),suggestions:i,value:s.selected_languages,onChange:e=>{l({TrpContentRestriction:(0,t.assign)({...s},{selected_languages:e})})},__experimentalValidateInput:e=>i.includes(e),__experimentalExpandOnFocus:!0,__experimentalShowHowTo:!1,__experimentalRenderItem:({item:t})=>{const n=Object.keys(o).find((e=>o[e]===t)),l=trpBlockEditorData.plugin_url+"/assets/images/flags/"+n+".png";return(0,e.createElement)("span",{style:{display:"flex",alignItems:"center"}},(0,e.createElement)("img",{alt:`Flag for ${l}`,src:l,style:{marginRight:8}}),t)}}))}function i(n){const{attributes:l,setAttributes:i}=n,{TrpContentRestriction:c}=l;return(0,t.has)(l,"TrpContentRestriction")?(0,e.createElement)(s.InspectorControls,null,(0,e.createElement)(a.PanelBody,{title:(0,r.__)("TranslatePress Language Restriction","translatepress-multilingual"),className:"translatepress-content-restriction-settings",initialOpen:c.panel_open,onToggle:e=>i({TrpContentRestriction:(0,t.assign)({...c},{panel_open:!c.panel_open})})},(0,e.createElement)(o,{...n}))):null}(0,n.addFilter)("blocks.registerBlockType","translatepress/attributes",(function(e){return e.attributes=(0,t.assign)(e.attributes,{TrpContentRestriction:{type:"object",properties:{restriction_type:{type:"string"},selected_languages:{type:"array"},panel_open:{type:"bool"}},default:{restriction_type:"exclude",selected_languages:[],panel_open:!0}}}),e}));const c=(0,l.createHigherOrderComponent)((t=>n=>(0,e.createElement)(e.Fragment,null,(0,e.createElement)(t,{...n}),(0,e.createElement)(i,{...n}))),"blockTrpContentRestrictionControls");(0,n.addFilter)("editor.BlockEdit","translatepress/inspector-controls",c,100)})(); includes/gutenberg-blocks/block-language-restriction/build/index.asset.php 0000777 00000000256 15251156640 0023101 0 ustar 00 <?php return array('dependencies' => array('lodash', 'react', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-hooks', 'wp-i18n'), 'version' => '0621ac568f4888376dde'); includes/gutenberg-blocks/block-language-restriction/src/components/ControlsCommon.jsx 0000777 00000006277 15251156640 0025533 0 ustar 00 import { assign } from 'lodash'; import { __ } from '@wordpress/i18n'; import { __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption } from '@wordpress/components' import { FormTokenField } from '@wordpress/components'; export default function ControlsCommon({ attributes, setAttributes }) { const { TrpContentRestriction } = attributes; const allLanguages = trpBlockEditorData.all_languages; const languageNames = Object.keys( allLanguages ).map( key => allLanguages[key] ) const handleLanguagePick = (newSelectedLanguages) => { setAttributes({ TrpContentRestriction: assign( { ...TrpContentRestriction }, { selected_languages: newSelectedLanguages } ), }); }; const helpText = TrpContentRestriction.restriction_type === 'include' ? __("Choose in which languages to show the block.", "translatepress-multilingual") : __("Choose from which languages the block is excluded.", "translatepress-multilingual"); const validateInput = ( token ) => { return languageNames.includes(token); }; const renderItem = ({ item }) => { const languageCode = Object.keys(allLanguages).find( (key) => allLanguages[key] === item ); const flag = trpBlockEditorData.plugin_url + '/assets/images/flags/' + languageCode + '.png'; return ( <span style={{ display: 'flex', alignItems: 'center' }}> <img alt={`Flag for ${flag}`} src={flag} style={{ marginRight: 8 }}/> {item} </span> ); }; return ( <> <p>{helpText}</p> <ToggleGroupControl isBlock label={__("Content Restriction Mode", "translatepress-multilingual")} value={TrpContentRestriction.restriction_type} onChange={(value) => setAttributes({ TrpContentRestriction: assign( { ...TrpContentRestriction }, { restriction_type: value } // Set "include" or "exclude" ), }) } > <ToggleGroupControlOption value="include" label={__("Include", "translatepress-multilingual")} /> <ToggleGroupControlOption value="exclude" label={__("Exclude", "translatepress-multilingual")} /> </ToggleGroupControl> <FormTokenField label={__("Select language(s)", 'translatepress-multilingual')} suggestions={languageNames} value={TrpContentRestriction.selected_languages} onChange={handleLanguagePick} __experimentalValidateInput={validateInput} __experimentalExpandOnFocus __experimentalShowHowTo={false} __experimentalRenderItem={renderItem} /> </> ); } includes/gutenberg-blocks/block-language-restriction/src/index.js 0000777 00000006052 15251156640 0021300 0 ustar 00 import { assign, has } from "lodash"; import { addFilter } from "@wordpress/hooks"; import { createHigherOrderComponent } from "@wordpress/compose"; import { __ } from "@wordpress/i18n"; import { InspectorControls } from "@wordpress/block-editor"; import { PanelBody } from "@wordpress/components"; import ControlsCommon from './components/ControlsCommon' /** * Add the language restriction inspector controls in the editor */ function TrpBlockContentRestrictionControls(props) { const { attributes, setAttributes } = props; const { TrpContentRestriction } = attributes; // Abort if the block type does not have the TrpContentRestriction attribute registered if ( !has(attributes, "TrpContentRestriction") ) return null; return ( <InspectorControls> <PanelBody title={__( "TranslatePress Language Restriction", "translatepress-multilingual", )} className="translatepress-content-restriction-settings" initialOpen={TrpContentRestriction.panel_open} onToggle={(value) => setAttributes({ TrpContentRestriction: assign( { ...TrpContentRestriction }, { panel_open: !TrpContentRestriction.panel_open }, ), }) } > <ControlsCommon {...props} /> </PanelBody> </InspectorControls> ); } /** * Add the content restriction settings attribute */ function TrpContentRestrictionAttributes( settings ) { let contentRestrictionAttributes = { TrpContentRestriction: { type: "object", properties: { restriction_type: { type: "string", }, selected_languages: { type: "array" }, panel_open: { type: "bool", }, }, default: { restriction_type: "exclude", selected_languages: [], panel_open: true, }, }, }; settings.attributes = assign( settings.attributes, contentRestrictionAttributes, ); return settings; } addFilter( "blocks.registerBlockType", "translatepress/attributes", TrpContentRestrictionAttributes, ); /** * Filter the block edit object and add content restriction controls */ const blockTrpContentRestrictionControls = createHigherOrderComponent( (BlockEdit) => { return (props) => { return ( <> <BlockEdit {...props} /> <TrpBlockContentRestrictionControls {...props} /> </> ); }; }, "blockTrpContentRestrictionControls", ); addFilter( "editor.BlockEdit", "translatepress/inspector-controls", blockTrpContentRestrictionControls, 100, // above Advanced controls ); includes/class-translation-render.php 0000777 00000362742 15251156640 0014033 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Translation_Render * * Translates pages. */ class TRP_Translation_Render{ protected $settings; protected $machine_translator; /* @var TRP_Query */ protected $trp_query; /* @var TRP_Url_Converter */ protected $url_converter; /* @var TRP_Translation_Manager */ protected $translation_manager; protected $common_html_tags; /** * TRP_Translation_Render constructor. * * @param array $settings Settings options. */ public function __construct( $settings ){ $this->settings = $settings; // apply_filters only once instead of everytime is_html() is used $this->common_html_tags = implode( '|', apply_filters('trp_common_html_tags', array( 'html', 'body', 'table', 'tbody', 'thead', 'th', 'td', 'tr', 'div', 'p', 'span', 'b', 'a', 'strong', 'center', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img' ) ) ); } /** * Start Output buffer to translate page. */ public function start_output_buffer(){ global $TRP_LANGUAGE; //when we check if is an ajax request in frontend we also set proper REQUEST variables and language global so we need to run this for every buffer $ajax_on_frontend = TRP_Gettext_Manager::is_ajax_on_frontend();//TODO refactor this function si it just checks and does not set variables if( ( is_admin() && !$ajax_on_frontend ) || trp_is_translation_editor( 'true' ) ){ return;//we have two cases where we don't do anything: we are on the admin side and we are not in an ajax call or we are in the left side of the translation editor } else { global $trp_output_buffer_started;//use this global so we know that we started the output buffer. we can check it for instance when wrapping gettext mb_http_output("UTF-8"); if ( $TRP_LANGUAGE == $this->settings['default-language'] && !trp_is_translation_editor() ) { // on default language when we are not in editor we just need to clear any trp tags that could still be present and handle links for special situation $chunk_size = ($this->handle_custom_links_for_default_language() ) ? 0 : 4096; $chunk_size = apply_filters("trp_output_buffer_chunk_size", $chunk_size); ob_start(array( $this, 'render_default_language' ), $chunk_size); $trp_output_buffer_started = true; } else { ob_start(array($this, 'translate_page'));//everywhere else translate the page $trp_output_buffer_started = true; } } } /** * Function to hide php errors and notice and instead log them in debug.log so we don't store the notice strings inside the db if WP_DEBUG is on */ public function trp_debug_mode_off(){ if ( WP_DEBUG ) { ini_set('display_errors', 0); ini_set('log_errors', 1); ini_set('error_log', WP_CONTENT_DIR . '/debug.log'); } } /** * Forces the language to be the first non default one in the preview translation editor. * We're doing this because we need the ID's. * Otherwise we're just returning the global $TRP_LANGUAGE * * @return string Language code. */ protected function force_language_in_preview(){ global $TRP_LANGUAGE; if ( in_array( $TRP_LANGUAGE, $this->settings['translation-languages'] ) ) { if ( $TRP_LANGUAGE == $this->settings['default-language'] ){ // in the translation editor we need a different language then the default because we need string ID's. // so we're forcing it to the first translation language because if it's the default, we're just returning the $output if ( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview' ) { if( count( $this->settings['translation-languages'] ) > 1 ){ foreach ($this->settings['translation-languages'] as $language) { if ($language != $TRP_LANGUAGE) { // return the first language not default. only used for preview mode return $language; } } } else{ return $TRP_LANGUAGE; } } }else { return $TRP_LANGUAGE; } } return false; } /** * Trim strings. * This function is kept for backwards compatibility for earlier versions of SEO Pack Add-on * * @deprecated * @param string $string Raw string. * @return string Trimmed string. */ public function full_trim( $string ) { return trp_full_trim( $string ); } /** * Preview mode string category name for give node type. * * @param string $current_node_type Node type. * @return string Category name. */ protected function get_node_type_category( $current_node_type ){ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $string_groups = $this->translation_manager->string_groups(); $node_type_categories = apply_filters( 'trp_node_type_categories', array( $string_groups['metainformation'] => array( 'meta_desc', 'page_title', 'meta_desc_img' ), $string_groups['images'] => array( 'image_src', 'picture_source_srcset', 'picture_image_src' ), $string_groups['videos'] => array( 'video_src', 'video_poster', 'video_source_src'), $string_groups['audios'] => array( 'audio_src', 'audio_source_src'), )); foreach( $node_type_categories as $category_name => $node_groups ){ if ( in_array( $current_node_type, $node_groups ) ){ return $category_name; } } return $string_groups['stringlist']; } /** * String description to be used in preview mode dropdown list of strings. * * @param object $current_node Current node. * @return string Node description. */ protected function get_node_description( $current_node ){ $node_type_descriptions = apply_filters( 'trp_node_type_descriptions', array( array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'description', 'description' => esc_html__( 'Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'article:section', 'description' => esc_html__( 'Article Section', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'article:tag', 'description' => esc_html__( 'Article Tag', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:title', 'description' => esc_html__( 'OG Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:site_name', 'description' => esc_html__( 'OG Site Name', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:description', 'description' => esc_html__( 'OG Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'property', 'value' => 'og:image:alt', 'description' => esc_html__( 'OG Image Alt', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'twitter:title', 'description' => esc_html__( 'Twitter Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'twitter:description', 'description' => esc_html__( 'Twitter Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'twitter:image:alt', 'description' => esc_html__( 'Twitter Image Alt', 'translatepress-multilingual' ) ), array( 'type' => 'page_title', 'description' => esc_html__( 'Page Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'DC.Title', 'description' => esc_html__( 'Dublin Core Title', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc', 'attribute' => 'name', 'value' => 'DC.Description', 'description' => esc_html__( 'Dublin Core Description', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc_img', 'attribute' => 'property', 'value' => 'og:image', 'description' => esc_html__( 'OG Image', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc_img', 'attribute' => 'property', 'value' => 'og:image:secure_url', 'description' => esc_html__( 'OG Image Secure URL', 'translatepress-multilingual' ) ), array( 'type' => 'meta_desc_img', 'attribute' => 'name', 'value' => 'twitter:image', 'description' => esc_html__( 'Twitter Image', 'translatepress-multilingual' ) ), )); foreach( $node_type_descriptions as $node_type_description ){ if ( isset( $node_type_description['attribute'] )) { $attribute = $node_type_description['attribute']; } if ( $current_node['type'] == $node_type_description['type'] && ( ( isset( $node_type_description['attribute'] ) && isset( $current_node['node']->$attribute ) && $current_node['node']->$attribute == $node_type_description['value'] ) || ( ! isset( $node_type_description['attribute'] ) ) ) ) { return $node_type_description['description']; } } return ''; } /** * Specific trim made for translation block string * * Problem especially for nbsp; which gets saved like that in DB. Then, in translation-render, the string arrives with nbsp; rendered to actual space character. * Used before inserting in db, and when trying to match on translation-render. * * wp_strip_all_tags was moved before html_entity_decode functions because quotes (& #039;) within text * within html tag attributes would be decoded into a quote ' and made wp_strip_tags to cut more text based on the incorrect html. * Example of html that broke before this change: <div title='Voir les détails de l'analyse de sécurité'></div> * * @param $string * * @return string */ public function trim_translation_block( $string ){ return preg_replace('/\s+/', ' ', html_entity_decode( htmlspecialchars_decode( wp_strip_all_tags(trp_full_trim( $string )), ENT_QUOTES ) ) ) ; } /** * Recursive function that checks if a DOM node contains certain tags or not * @param $row * @param $tags * @return bool */ public function check_children_for_tags( $row, $tags ){ foreach ( $row->children as $child ) { if ( in_array( $child->tag, $tags ) ) { return true; } else { if ( $this->check_children_for_tags( $child, $tags ) ) { return true; } } } return false; } /** * Return translation block if matches any existing translation block from db * * Return null if not found * * @param $row * @param $all_existing_translation_blocks * @param $merge_rules * * @return bool */ public function find_translation_block( $row, $all_existing_translation_blocks, $merge_rules ){ if ( in_array( $row->tag, $merge_rules['top_parents'] ) ){ //$row->innertext is very intensive on dom nodes that have a lot of children so we try here to eliminate as many as possible here // the ideea is that if a dom node contains any top parent tags for blocks it can't be a block itself so we skip it $skip = $this->check_children_for_tags( $row, $merge_rules['top_parents'] ); if( !$skip ) { $trimmed_inner_text = $this->trim_translation_block($row->innertext); foreach ($all_existing_translation_blocks as $existing_translation_block) { if ($existing_translation_block->trimmed_original == $trimmed_inner_text) { return $existing_translation_block; } } } } return null; } /** * Function that translates the content post title, site title and post content in oembed response * * @param $data * @param $post * @param $width * @param $height * * @return array */ public function oembed_response_data($data, $post, $width, $height ){ if ( !empty( $data )) { $translatable_items = apply_filters( 'trp_oembed_response_data_translatable_items', array('title', 'html', 'provider_name') ); foreach( $translatable_items as $item ){ if ( isset( $data[$item] ) ) { $data[$item] = $this->translate_page( $data[$item] ); } } } // Otherwise we incorrectly unescape the sequence to end CDATA from ']]>' to ']]>' breaking the xml. It needs to stay escaped in oembed response data. remove_filter( 'trp_before_translate_content', array( $this, 'handle_cdata'), 1000); return $data; } /** * Handle generic REST API translations using configurable rules * hooked on rest_pre_echo_response in class-translate-press.php * @param array $result * @param WP_REST_Server $server * @param WP_REST_Request $request * @return array */ public function handle_generic_rest_api_translations( $result, $server, $request ) { $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $language = $url_converter->get_lang_from_url_string( $url_converter->cur_page_url() ); if ( $language == $this->settings['default-language'] || $language == null ) { return $result; // exit early in default language. } // Get REST API translation configuration $translation_config = $this->get_rest_api_translation_config(); // Check if this request matches any configured REST API paths $route = $request->get_route(); $matching_config = $this->find_matching_rest_api_config( $route, $translation_config ); if ( ! $matching_config ) { return $result; // No translation rules for this route } // Translate the REST API response data using the matching configuration if ( is_array( $result ) ) { $max_depth = apply_filters( 'trp_rest_api_translation_max_depth', 5 ); $result = $this->translate_rest_api_data_recursive( $result, $matching_config, $language, 0, $max_depth ); } return $result; } /** * Get REST API translation configuration * @return array */ private function get_rest_api_translation_config() { $default_config = array( 'wp/v2/search' => array( 'title' ), // Search API 'wp/v2/comments' => array( 'content' ), // Comments API 'wc/store/' => array( 'name', 'description', 'short_description' ) // WooCommerce Store API ); // Add all WordPress post types dynamically $post_types = get_post_types( array( 'public' => true, 'show_in_rest' => true ), 'objects' ); foreach ( $post_types as $post_type ) { $rest_base = $post_type->rest_base ? $post_type->rest_base : $post_type->name; $default_config['wp/v2/' . $rest_base] = array( 'title', 'content', 'excerpt', 'name', 'description' ); } // Add all taxonomies dynamically $taxonomies = get_taxonomies( array( 'public' => true, 'show_in_rest' => true ), 'objects' ); foreach ( $taxonomies as $taxonomy ) { $rest_base = $taxonomy->rest_base ? $taxonomy->rest_base : $taxonomy->name; $default_config['wp/v2/' . $rest_base] = array( 'name', 'description' ); } return apply_filters( 'trp_rest_api_translation_config', $default_config ); } /** * Find matching configuration for a REST API route * @param string $route * @param array $config * @return array|false */ private function find_matching_rest_api_config( $route, $config ) { foreach ( $config as $pattern => $keys ) { if ( strpos( $route, $pattern ) !== false ) { return $keys; } } return false; } /** * Recursively translate REST API data based on configuration * @param array $data * @param array $translatable_keys * @param string $language * @param int $current_depth * @param int $max_depth * @return array */ private function translate_rest_api_data_recursive( $data, $translatable_keys, $language, $current_depth = 0, $max_depth = 5 ) { if ( $current_depth >= $max_depth || ! is_array( $data ) ) { return $data; } $skip_shortcode_translation = apply_filters( 'trp_rest_api_skip_shortcode_translation', true, $data ); foreach ( $translatable_keys as $field ) { // Check for direct field first if ( isset( $data[$field] ) && is_string( $data[$field] ) ) { // Skip shortcodes due to MT quota consumption if ( $this->rest_value_has_shortcode( $data[$field] ) && $skip_shortcode_translation ) { continue; } $data[$field] = $this->translate_page( $data[$field] ); } // For title, content, excerpt - also check .rendered subfield elseif ( in_array( $field, array( 'title', 'content', 'excerpt' ) ) ) { if ( isset( $data[$field]['rendered'] ) && is_string( $data[$field]['rendered'] ) ) { if ( $this->rest_value_has_shortcode( $data[ $field]['rendered'] ) && $skip_shortcode_translation ) { continue; } $data[$field]['rendered'] = $this->translate_page( $data[$field]['rendered'] ); } } } // Handle arrays and nested objects recursively foreach ( $data as $key => $value ) { if ( is_array( $value ) ) { $data[$key] = $this->translate_rest_api_data_recursive( $value, $translatable_keys, $language, $current_depth + 1, $max_depth ); } } // Handle special case for slug translation if available if ( isset( $data['slug'] ) && is_string( $data['slug'] ) && class_exists( 'TRP_Slug_Query' ) ) { $trp_slug_query = new TRP_Slug_Query(); $slug_array = array( $data['slug'] ); $translated_slugs = $trp_slug_query->get_translated_slugs_from_original( $slug_array, $language ); if ( !empty( $translated_slugs ) && isset( $translated_slugs[$data['slug']] ) ) { $data['slug'] = $translated_slugs[$data['slug']]; } } return $data; } /** * Check if a value contains shortcodes. * * @param string $value * @return bool */ private function rest_value_has_shortcode( $value ) { if ( ! is_string( $value ) || strpos( $value, '[' ) === false ) { return false; } $pattern = get_shortcode_regex(); if ( ! empty( $pattern ) && preg_match( '/' . $pattern . '/s', $value ) ) { return true; } return preg_match( '/\\[[^\\]]+\\]/', $value ) === 1; } /** * Finding translateable strings and replacing with translations. * * Method called for output buffer. * * @param string $output Entire HTML page as string. * @return string Translated HTML page. */ public function translate_page( $output ){ if ( apply_filters( 'trp_stop_translating_page', false, $output ) ){ return $output; } global $TRP_HDOM_QUOTE_DEFAULT; $TRP_HDOM_QUOTE_DEFAULT = apply_filters('trp_hdom_quote_default_double_quotes', '"'); global $trp_editor_notices; /* replace our special tags so we have valid html */ $output = str_ireplace('#!trpst#', '<', $output); $output = str_ireplace('#!trpen#', '>', $output); $output = apply_filters('trp_before_translate_content', $output); /* remove unwanted tags. For example,we're removing script and style because they should not be translated and if they are large cause big performance issues */ $excluded_tags = apply_filters('trp_excluded_tags_from_translation', array('script', 'style')); $output_with_excluded_tags_removed = $this->remove_tags_from_output($output, $excluded_tags); // $removed_tags = array('output' => '$output string', 'excluded_tags' => array()); $output = apply_filters('trp_after_excluded_tags_from_translation', $output_with_excluded_tags_removed['output']); if ( $output == false || !is_string( $output ) || strlen( $output ) < 1 ) { return $output; } if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } if( $this->url_converter->is_sitemap_path( $this->url_converter->cur_page_url( false )) ){ return $output; } /* make sure we only translate on the rest_prepare_$post_type filter in REST requests and not the whole json */ /* in certain cases $wp_rewrite is null, so it trows a fatal error. This is just a quick fix. The actual issue is probably in WordPress core * see taskid #2pjped */ global $wp_rewrite; if( is_object($wp_rewrite) ) { if( strpos( $this->url_converter->cur_page_url( false ), get_rest_url() ) !== false && current_filter() !== 'oembed_response_data' && current_filter() !== 'rest_pre_echo_response' && current_filter() !== 'wp_mail' ) { $trpremoved = $this->remove_trp_html_tags( $output ); /* add back the excluded tags like script and style to the html */ $trpremoved = $this->add_excluded_tags_after_translation( $trpremoved, $output_with_excluded_tags_removed['excluded_tags'] ); return $trpremoved; } } /* don't do anything on xmlrpc.php */ if( strpos( $this->url_converter->cur_page_url( false ), 'xmlrpc.php' ) !== false ){ $trpremoved = $this->remove_trp_html_tags( $output ); /* add back the excluded tags like script and style to the html */ $trpremoved = $this->add_excluded_tags_after_translation( $trpremoved, $output_with_excluded_tags_removed['excluded_tags'] ); return $trpremoved; } global $TRP_LANGUAGE; $language_code = $this->force_language_in_preview(); if ($language_code === false) { /* add back the excluded tags like script and style to the html */ $output = $this->add_excluded_tags_after_translation( $output, $output_with_excluded_tags_removed['excluded_tags'] ); return $output; } if ( $language_code == $this->settings['default-language'] ){ // Don't translate regular strings (non-gettext) when we have no other translation languages except default language ( count( $this->settings['publish-languages'] ) > 1 ) $translate_normal_strings = false; }else{ $translate_normal_strings = true; } $translate_normal_strings = apply_filters( 'trp_translate_regular_strings', $translate_normal_strings ); $preview_mode = isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview'; $json_array = json_decode( $output, true ); /* If we have a json response we need to parse it and only translate the nodes that contain html * * Removed is_ajax_on_frontend() check because we need to capture custom ajax events. * Decided that if $output is json decodable it's a good enough check to handle it this way. * We have necessary checks so that we don't get to this point when is_admin(), or when language is default. */ if( $json_array && $json_array != $output ) { /* if it's one of our own ajax calls don't do nothing */ if ( ! empty( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 && $_REQUEST['action'] != 'trp_split_translation_block' ){ /* add back the excluded tags like script and style to the html */ $output = $this->add_excluded_tags_after_translation( $output, $output_with_excluded_tags_removed['excluded_tags'] ); return $output; } //check if we have a json response if ( ! empty( $json_array ) ) { if( is_array( $json_array ) ) { array_walk_recursive($json_array, array($this, 'translate_json')); }else { $json_array = $this->translate_page($json_array); } } return trp_safe_json_encode( $json_array ); } /** * Tries to fix the HTML document. It is off by default. Use at own risk. * Solves the problem where a duplicate attribute inside a tag causes the plugin to remove the duplicated attribute and all the other attributes to the right of the it. */ $output = apply_filters( 'trp_pre_translating_html', $output ); $no_translate_attribute = 'data-no-translation'; $no_auto_translate_attribute = 'data-no-auto-translation'; $translateable_strings = array(); $translateable_strings_manual = array(); $skip_machine_translating_strings = array(); $do_not_add_this_alug_to_dictionary_table = array(); $nodes = array(); $nodes_manual = array(); $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } if ( ! $this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $html = TranslatePress\str_get_html($output, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT); if ( $html === false ){ $trpremoved = $this->remove_trp_html_tags( $output ); /* add back the excluded tags like script and style to the html */ $trpremoved = $this->add_excluded_tags_after_translation( $trpremoved, $output_with_excluded_tags_removed['excluded_tags'] ); return $trpremoved; } $count_translation_blocks = 0; if ( $translate_normal_strings ) { $all_existing_translation_blocks = $this->trp_query->get_all_translation_blocks( $language_code ); // trim every translation block original now, to avoid over-calling trim function later foreach ( $all_existing_translation_blocks as $key => $existing_tb ) { $all_existing_translation_blocks[ $key ]->trimmed_original = $this->trim_translation_block( $all_existing_translation_blocks[ $key ]->original ); } /* Try to find if there are any blocks in the output for translation. * If the output is an actual html page, use only the innertext of body tag * Else use the entire output (ex. the output is from JSON REST API content, or just a string) */ $html_body = $html->find('body', 0 ); $output_to_translate = ( $html_body ) ? $html_body->innertext : $output; $trimmed_html_body = $this->trim_translation_block( $output_to_translate ); foreach( $all_existing_translation_blocks as $key => $existing_translation_block ){ if ( (empty($existing_translation_block->trimmed_original )) || (strpos( $trimmed_html_body, $existing_translation_block->trimmed_original ) === false )){ unset($all_existing_translation_blocks[$key] );//if it isn't present remove it, this way we don't look for them on pages that don't contain blocks } } $count_translation_blocks = count( $all_existing_translation_blocks );//see here how many remain on the current page $merge_rules = $this->translation_manager->get_merge_rules(); } /** * When we are in the translation editor: Intercept the trp-gettext that was wrapped around all the gettext texts, grab the attribute data-trpgettextoriginal * which contains the original translation id and move it to the parent node if the parent node only contains that string then remove the wrap trp-gettext, otherwise replace it with another tag. * Also set a no-translation attribute. * When we are in a live translation case: Intercept the trp-gettext that was wrapped around all the gettext texts, set a no-translation attribute to the parent node if the parent node only contains that string * then remove the wrap trp-gettext, otherwise replace the wrap with another tag and do the same to it * We identified two cases: the wrapper trp-gettext can be as a node in the dome or ot can be inside a html attribute ( for example value ) * and we need to treat them differently */ /* store the nodes in arrays so we can sort the $trp_rows which contain trp-gettext nodes from the DOM according to the number of children and we process the simplest first */ $trp_rows = array(); $trp_attr_rows = array(); foreach ( $html->find("*[!nuartrebuisaexiteatributulasta]") as $k => $row ){ if( $row->hasAttribute('data-trpgettextoriginal') ){ $trp_rows[count( $row->children )][] = $row; } else{ if( $row->nodetype !== 5 && $row->nodetype !== 3 )//add all tags that are not root or text, text nodes can't have attributes $trp_attr_rows[] = $row; if ( $translate_normal_strings && $count_translation_blocks > 0 ) { $translation_block = $this->find_translation_block( $row, $all_existing_translation_blocks, $merge_rules ); if ( $translation_block ) { $existing_classes = $row->getAttribute( 'class' ); if ( $translation_block->block_type == 1 ) { $found_inner_translation_block = false; foreach ( $row->children() as $child ) { if ( $this->find_translation_block( $child, array( $translation_block ), $merge_rules ) != null ) { $found_inner_translation_block = true; break; } } if ( ! $found_inner_translation_block ) { // make sure we find it later exactly the way it is in DB $row->innertext = $translation_block->original; $row->setAttribute( 'class', $existing_classes . ' translation-block' ); } } else if ( $preview_mode && $translation_block->block_type == 2 && $translation_block->status != 0 ) { // refactor to not do this for each $row->setAttribute( 'data-trp-translate-id', $translation_block->id ); $row->setAttribute( 'data-trp-translate-id-deprecated', $translation_block->id ); $row->setAttribute( 'class', $existing_classes . 'trp-deprecated-tb' ); } } } } } /* sort them here ascending by key where the key is the number of children */ /* here we add support for gettext inside gettext */ ksort($trp_rows); foreach( $trp_rows as $level ){ foreach( $level as $row ){ $original_gettext_translation_id = $row->getAttribute('data-trpgettextoriginal'); /* Parent node has no other children and no other innertext besides the current node */ if( count( $row->parent()->children ) == 1 && $row->parent()->innertext == $row->outertext ){ $row->outertext = $row->innertext(); $row->parent()->setAttribute($no_translate_attribute, ''); $row->parent()->setAttribute('data-trp-gettext', ''); // we are in the editor if (isset($_REQUEST['trp-edit-translation']) && $_REQUEST['trp-edit-translation'] == 'preview') { //move up the data-trpgettextoriginal attribute $row->parent()->setAttribute('data-trpgettextoriginal', $original_gettext_translation_id); } } else{ /* Setting this attribute using setAttribute function actually changes the $html object. Important for not detecting this gettext as a regular string in the next lines using find() */ $row->setAttribute($no_translate_attribute, ''); /* Changes made to outertext take place only after saving the html object to a string */ $row->outertext = '<trp-wrap class="trp-wrap" data-no-translation'; if (isset($_REQUEST['trp-edit-translation']) && $_REQUEST['trp-edit-translation'] == 'preview') { $row->outertext .= ' data-trpgettextoriginal="'. $original_gettext_translation_id .'"'; } $row->outertext .= '>'.$row->innertext().'</trp-wrap>'; } } } foreach( $trp_attr_rows as $row ){ $all_attributes = $row->getAllAttributes(); if( !empty( $all_attributes ) ) { foreach ($all_attributes as $attr_name => $attr_value) { if (strpos($attr_value, 'trp-gettext ') !== false) { //if we have json content in the value of the attribute, we don't do anything. The trp-wrap will be removed later in the code if (is_array($json_array = json_decode( html_entity_decode( $attr_value, ENT_QUOTES ), true ) ) ) { continue; } // convert to a node $node_from_value = TranslatePress\str_get_html(html_entity_decode(htmlspecialchars_decode($attr_value, ENT_QUOTES)), true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT); if ( $node_from_value === false ){ continue; } foreach ($node_from_value->find('trp-gettext') as $nfv_row) { $nfv_row->outertext = $nfv_row->innertext(); $saved_node_from_value = $node_from_value->save(); // attributes of these tags are not handled well by the parser so don't escape them [see iss6264] if ( $row->tag != 'script' && $row->tag != 'style' ){ $saved_node_from_value = esc_attr($saved_node_from_value); } $row->setAttribute($attr_name, $saved_node_from_value ); $row->setAttribute($no_translate_attribute . '-' . $attr_name, ''); // we are in the editor if (isset($_REQUEST['trp-edit-translation']) && $_REQUEST['trp-edit-translation'] == 'preview') { $original_gettext_translation_id = $nfv_row->getAttribute('data-trpgettextoriginal'); $row->setAttribute('data-trpgettextoriginal-' . $attr_name, $original_gettext_translation_id); } } } } } } if ( ! $translate_normal_strings ) { /* save it as a string */ $trpremoved = $html->save(); /* perform preg replace on the remaining trp-gettext tags */ $trpremoved = $this->remove_trp_html_tags($trpremoved ); /* add back the excluded tags like script and style to the html */ $trpremoved = $this->add_excluded_tags_after_translation( $trpremoved, $output_with_excluded_tags_removed['excluded_tags'] ); return $trpremoved; } $no_translate_selectors = apply_filters( 'trp_no_translate_selectors', array( '#wpadminbar' ), $TRP_LANGUAGE ); $ignore_cdata = apply_filters('trp_ignore_cdata', true ); $translate_encoded_html_as_string = apply_filters('trp_translate_encoded_html_as_string', false ); $translate_encoded_html_as_html = apply_filters('trp_translate_encoded_html_as_html', true ); // used for skipping minified scripts but can be used for anything $skip_strings_containing_key_terms = apply_filters('trp_skip_strings_containing_key_terms', array( array( 'terms'=> array( 'function', 'return', 'if', '==' ), 'operator' => 'and' ) ) ); /* * process the types of strings we can currently have: no-translate, translation-block, text, input, textarea, etc. */ foreach ( $no_translate_selectors as $no_translate_selector ){ foreach ( $html->find( $no_translate_selector ) as $k => $row ){ $row->setAttribute( $no_translate_attribute, '' ); } } $no_auto_translate_selectors = apply_filters( 'trp_no_auto_translate_selectors', array( ), $TRP_LANGUAGE ); foreach ( $no_auto_translate_selectors as $no_auto_translate_selector ){ foreach ( $html->find( $no_auto_translate_selector ) as $k => $row ){ $row->setAttribute( $no_auto_translate_attribute, '' ); } } foreach ( $html->find('.translation-block') as $row ){ $trimmed_string = trp_full_trim( $row->innertext ); $parent = $row->parent(); if( $trimmed_string!="" && $parent->tag!="script" && $parent->tag!="style" && $parent->tag != 'title' && strpos($row->outertext,'[vc_') === false && !$this->trp_is_numeric($trimmed_string) && !preg_match('/^\d+%$/',$trimmed_string) && $row->find_ancestor_tag( 'script' ) === null // sometimes the script/style has an html tree that gets detected, so script/style is not a direct parent && $row->find_ancestor_tag( 'style' ) === null && !$this->has_ancestor_attribute( $row, $no_translate_attribute ) ) { $string_count = array_push( $translateable_strings, $trimmed_string ); array_push( $nodes, array('node' => $row, 'type' => 'block')); if ( ! apply_filters( 'trp_allow_machine_translation_for_string', true, $trimmed_string, null, null, $row ) ){ array_push( $skip_machine_translating_strings, $trimmed_string ); } //add data-trp-post-id attribute if needed $nodes = $this->maybe_add_post_id_in_node( $nodes, $row, $string_count ); } } foreach ( $html->find('trptext') as $row ){ $outertext = $row->outertext; $parent = $row->parent(); $trimmed_string = trp_full_trim( $outertext ); if( $trimmed_string!="" && $parent->tag!="script" && $parent->tag!="style" && $parent->tag != 'title' && $parent->tag != 'textarea' //explicitly exclude textarea strings && strpos($outertext,'[vc_') === false && !$this->trp_is_numeric($trimmed_string) && !preg_match('/^\d+%$/',$trimmed_string) && !$this->has_ancestor_attribute( $row, $no_translate_attribute ) && !$this->has_ancestor_class( $row, 'translation-block') && $row->find_ancestor_tag( 'script' ) === null // sometimes the script/style has an html tree that gets detected, so script/style is not a direct parent && $row->find_ancestor_tag( 'style' ) === null && ( !$ignore_cdata || ( strpos($trimmed_string, '<![CDATA[') !== 0 && strpos($trimmed_string, '<![CDATA[') !== 0 ) ) && (strpos($trimmed_string, 'BEGIN:VCALENDAR') !== 0) && !$this->contains_substrings($trimmed_string, $skip_strings_containing_key_terms ) ) { if ( !$translate_encoded_html_as_string ){ $is_html = false; if ( $translate_encoded_html_as_html ){ if ( $this->is_html($trimmed_string) ){ // prevent potential infinite loops. Only call translate_page once recursively add_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $row->outertext = str_replace( $trimmed_string, $this->translate_page( $trimmed_string ), $row->outertext ); remove_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $is_html = true; }else { $entity_decoded_trimmed_string = html_entity_decode( $trimmed_string ); if ( $this->is_html( $entity_decoded_trimmed_string ) ) { // prevent potential infinite loops. Only call translate_page once recursively add_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $row->outertext = str_replace( $trimmed_string, htmlentities( $this->translate_page( $entity_decoded_trimmed_string ) ), $row->outertext ); remove_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $is_html = true; } } } if ( $is_html ) { continue; } } // $translateable_strings array needs to be in sync in $nodes array $string_count = array_push( $translateable_strings, $trimmed_string ); $node_type_to_push = ( in_array( $parent->tag, array( 'button', 'option' ) ) ) ? $parent->tag : 'text'; array_push($nodes, array('node' => $row, 'type' => $node_type_to_push )); if ( ! apply_filters( 'trp_allow_machine_translation_for_string', true, $trimmed_string, null, null, $row ) ){ array_push( $skip_machine_translating_strings, $trimmed_string ); } if ( $parent->tag == 'a' && ! apply_filters( 'trp_allow_machine_translation_for_url', true, $trimmed_string ) ){ array_push( $skip_machine_translating_strings, $trimmed_string ); array_push( $do_not_add_this_alug_to_dictionary_table, $trimmed_string ); } //add data-trp-post-id attribute if needed $nodes = $this->maybe_add_post_id_in_node( $nodes, $row, $string_count ); } $row = apply_filters( 'trp_process_other_text_nodes', $row ); } //set up general links variables $home_url = home_url(); $node_accessors = $this->get_node_accessors(); foreach( $node_accessors as $node_accessor_key => $node_accessor ){ if ( isset( $node_accessor['selector'] ) ){ foreach ( $html->find( $node_accessor['selector'] ) as $k => $row ){ $current_node_accessor_selector = $node_accessor['accessor']; $trimmed_string = trp_full_trim( $row->$current_node_accessor_selector ); $translate_href = false; if ( $current_node_accessor_selector === 'href' ) { $translate_href = ( $this->is_external_link( $trimmed_string, $home_url ) || $this->url_converter->url_is_file( $trimmed_string ) || $this->url_converter->url_is_extra($trimmed_string) ); $translate_href = apply_filters( 'trp_translate_this_href', $translate_href, $row, $TRP_LANGUAGE, $trimmed_string ); $trimmed_string = ( $translate_href ) ? $trimmed_string : ''; } // outside preview mode we build the $translateable_strings_manual array for href // the similar condition above needs to remain in place for backwords compatibility with the filter trp_translate_this_href if ( $translate_href && !$preview_mode ) { $translateable_strings_manual[] = html_entity_decode( $trimmed_string ); $nodes_manual[] = array('node' => $row, 'type' => $node_accessor_key); // reset the string so it's excluded from $translateable_strings (no longer inserted in the database in front-end) $trimmed_string = ''; } // outside preview mode we build the $translateable_strings_manual array for src if ( $current_node_accessor_selector === 'src' && !$preview_mode && $trimmed_string != ''){ $translateable_strings_manual[] = html_entity_decode( $trimmed_string ); $nodes_manual[] = array('node' => $row, 'type' => $node_accessor_key); // reset the string so it's excluded from $translateable_strings (no longer inserted in the database in front-end) $trimmed_string = ''; } if( $trimmed_string!="" && !$this->trp_is_numeric($trimmed_string) && !preg_match('/^\d+%$/',$trimmed_string) && !$this->has_ancestor_attribute( $row, $no_translate_attribute ) && !$this->has_ancestor_attribute( $row, $no_translate_attribute . '-' . $current_node_accessor_selector ) && !$this->has_ancestor_class( $row, 'translation-block') && $row->tag != 'link' && ( !$ignore_cdata || ( strpos($trimmed_string, '<![CDATA[') !== 0 && strpos($trimmed_string, '<![CDATA[') !== 0 ) ) && (strpos($trimmed_string, 'BEGIN:VCALENDAR') !== 0 ) && !$this->contains_substrings($trimmed_string, $skip_strings_containing_key_terms ) ) { $entity_decoded_trimmed_string = html_entity_decode( $trimmed_string ); if ( !$translate_encoded_html_as_string ){ if ( $translate_encoded_html_as_html ){ if ( $this->is_html($entity_decoded_trimmed_string) ){ // prevent potential infinite loops. Only call translate_page once recursively add_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); $row->setAttribute( $current_node_accessor_selector, str_replace( $trimmed_string, esc_attr( htmlentities($this->translate_page( $entity_decoded_trimmed_string )) ), $row->$current_node_accessor_selector ) ); remove_filter( 'trp_translate_encoded_html_as_html', '__return_false' ); continue; } } } array_push( $translateable_strings, $entity_decoded_trimmed_string ); array_push( $nodes, array( 'node'=>$row, 'type' => $node_accessor_key ) ); if ( ! apply_filters( 'trp_allow_machine_translation_for_string', true, $entity_decoded_trimmed_string, $current_node_accessor_selector, $node_accessor, $row ) ){ array_push( $skip_machine_translating_strings, $entity_decoded_trimmed_string ); } } } } } $translateable_information = array( 'translateable_strings' => $translateable_strings, 'nodes' => $nodes ); $translateable_information = apply_filters( 'trp_translateable_strings', $translateable_information, $html, $no_translate_attribute, $TRP_LANGUAGE, $language_code, $this ); $translateable_strings = $translateable_information['translateable_strings']; $nodes = $translateable_information['nodes']; if ( !empty( $translateable_information['nodes'] ) ) { foreach ( $translateable_information['nodes'] as $key => $node ) { if ( $node['type'] === 'post' || $node['type'] === 'term' || $node['type'] === 'taxonomy' || $node['type'] === 'post-type-base' || $node['type'] === 'other' ) { if ( $node['skip_automatic_translation'] === true){ $skip_machine_translating_strings[] = $translateable_information['translateable_strings'][$key]; } } } } // serving translations, inserting strings in the database $translated_strings = $this->process_strings( $translateable_strings, $language_code, null, $skip_machine_translating_strings, $do_not_add_this_alug_to_dictionary_table ); // serving translations for manual strings in the front-end: hrefs, src if ( !$preview_mode ){ $translateable_information_manual = apply_filters( 'trp_translateable_strings_manual', array( 'translateable_strings_manual' => $translateable_strings_manual, 'nodes_manual' => $nodes_manual ), $html, $no_translate_attribute, $TRP_LANGUAGE, $language_code, $this ); $translateable_strings_manual = $translateable_information_manual['translateable_strings_manual']; $nodes_manual = $translateable_information_manual['nodes_manual']; $translated_strings_manual_dictionary = $this->trp_query->get_existing_translations( array_values( $translateable_strings_manual ), $language_code ); $translated_strings_manual = array(); foreach ( $translateable_strings_manual as $i => $string_manual ) { if ( isset( $translated_strings_manual_dictionary[ $string_manual ]->translated ) && !empty( $translated_strings_manual_dictionary[ $string_manual ]->translated )) { $translated_strings_manual[$i] = $translated_strings_manual_dictionary[ $string_manual ]->translated; } } foreach ( $nodes_manual as $i => $node_manual ) { if ( !isset( $translated_strings_manual[$i] ) || !isset( $node_accessors [$node_manual['type']] ) ){ continue; } $current_node_accessor = $node_accessors[$node_manual['type']]; $accessor = $current_node_accessor[ 'accessor' ]; if ( $current_node_accessor[ 'attribute' ] ){ $translateable_string_manual = $this->maybe_correct_translatable_string( $translateable_strings_manual[$i], $node_manual['node']->getAttribute( $accessor ) ); $node_manual['node']->setAttribute( $accessor, str_replace( $translateable_string_manual, esc_attr( $translated_strings_manual[$i] ), $node_manual['node']->getAttribute( $accessor ) ) ); do_action( 'trp_set_translation_for_attribute', $node_manual['node'], $accessor, $translated_strings_manual[$i] ); }else{ $translateable_string_manual = $this->maybe_correct_translatable_string( $translateable_strings_manual[$i], $node_manual['node']->$accessor ); $nodes[$i]['node']->$accessor = str_replace( $translateable_string_manual, trp_sanitize_string($translated_strings_manual[$i]), $node_manual['node']->$accessor ); } } do_action('trp_translateable_information_manual', $translateable_information_manual, $translated_strings_manual, $language_code); } do_action('trp_translateable_information', $translateable_information, $translated_strings, $language_code); //check for post_id meta on original strings, and insert for non existing /* * - get only strings that have the post_id in the nodes from $translateable_information * - get the original id's for these string from the original table * - see which of these id's have the meta with the current post_id value and insert into the meta table the ones that don't * */ if( !empty($translateable_information['nodes']) ){ $strings_in_post_content = array(); foreach ( $translateable_information['nodes'] as $i => $node ){ if( !empty( $node['post_id'] ) ){ $strings_in_post_content['strings'][] = $translateable_information['translateable_strings'][$i]; $strings_in_post_content['post_ids'][] = $node['post_id']; } } if( !empty( $strings_in_post_content ) ){ //try to do this only once a day to decrease query load $current_permalink = get_permalink(); $set_meta_for_this_url = get_transient('processed_original_string_meta_post_id_for_' . hash('md4', $current_permalink)); if( $set_meta_for_this_url === false ){ $original_string_ids = $this->trp_query->get_original_string_ids($strings_in_post_content['strings']); if( !empty( $original_string_ids ) ){ //there is a correlation between the two arrays $this->trp_query->set_original_string_meta_post_id( $original_string_ids, $strings_in_post_content['post_ids'] ); } set_transient('processed_original_string_meta_post_id_for_' . hash('md4', $current_permalink), 'done', 60*60*24 ); } } } if ( $preview_mode ) { $translated_string_ids = $this->trp_query->get_string_ids($translateable_strings, $language_code); } foreach ( $nodes as $i => $node ) { $translation_available = isset( $translated_strings[$i] ); if ( ! ( $translation_available || $preview_mode ) || !isset( $node_accessors [$nodes[$i]['type']] )){ continue; } $current_node_accessor = $node_accessors[ $nodes[$i]['type'] ]; $accessor = $current_node_accessor[ 'accessor' ]; if ( $translation_available && isset( $current_node_accessor ) && ! ( $preview_mode && ( $this->settings['default-language'] == $TRP_LANGUAGE ) ) ) { $translateable_string = $translateable_strings[$i]; if ( $current_node_accessor[ 'attribute' ] ){ $translateable_string = $this->maybe_correct_translatable_string( $translateable_string, $nodes[$i]['node']->getAttribute( $accessor ) ); $nodes[$i]['node']->setAttribute( $accessor, str_replace( $translateable_string, esc_attr( $translated_strings[$i] ), $nodes[$i]['node']->getAttribute( $accessor ) ) ); do_action( 'trp_set_translation_for_attribute', $nodes[$i]['node'], $accessor, $translated_strings[$i] ); }else{ $translateable_string = $this->maybe_correct_translatable_string( $translateable_string, $nodes[$i]['node']->$accessor ); $nodes[$i]['node']->$accessor = str_replace( $translateable_string, trp_sanitize_string($translated_strings[$i]), $nodes[$i]['node']->$accessor ); } } if ( $preview_mode && !empty($translated_string_ids) ) { if ( $accessor == 'outertext' && $nodes[$i]['type'] != 'button' ) { $outertext_details = '<translate-press data-trp-translate-id="' . $translated_string_ids[$translateable_strings[$i]]->id . '" data-trp-node-group="' . $this->get_node_type_category( $nodes[$i]['type'] ) . '"'; if ( $this->get_node_description( $nodes[$i] ) ) { $outertext_details .= ' data-trp-node-description="' . $this->get_node_description($nodes[$i] ) . '"'; } $outertext_details .= '>' . $nodes[$i]['node']->outertext . '</translate-press>'; $nodes[$i]['node']->outertext = $outertext_details; } else { // button, option can not be detected by the pencil, but the parent can. if( $nodes[$i]['type'] == 'button' || $nodes[$i]['type'] == 'option' ) { $nodes[$i]['node'] = $nodes[$i]['node']->parent(); } // video without a src can't be detected. So when we detect a video > source tag // we add the ID to the parent video tag as well if( $nodes[$i]['type'] == 'video_source_src' || $nodes[$i]['type'] == 'audio_source_src' || $nodes[$i]['type'] == 'picture_source_srcset') { $parent = $nodes[$i]['node']->parent(); if (!array_key_exists('src', $parent->attr)){ $parent->setAttribute('data-trp-translate-id-' . $accessor, $translated_string_ids[ $translateable_strings[$i] ]->id ); $parent->setAttribute('data-trp-node-group-' . $accessor, $this->get_node_type_category( $nodes[$i]['type'] ) ); } } $nodes[$i]['node']->setAttribute('data-trp-translate-id-' . $accessor, $translated_string_ids[ $translateable_strings[$i] ]->id ); $nodes[$i]['node']->setAttribute('data-trp-node-group-' . $accessor, $this->get_node_type_category( $nodes[$i]['type'] ) ); if ( $this->get_node_description( $nodes[$i] ) ) { $nodes[$i]['node']->setAttribute('data-trp-node-description-' . $accessor, $this->get_node_description($nodes[$i])); } } } } // We need to save here in order to access the translated links too. $handle_custom_links_in_translation_blocks = $this->settings['force-language-to-custom-links'] == 'yes'; if( apply_filters('tp_handle_custom_links_in_translation_blocks', $handle_custom_links_in_translation_blocks) ) { $html_string = $html->save(); $html = TranslatePress\str_get_html($html_string, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT); if ( $html === false ){ /* add back the excluded tags like script and style to the html */ $html_string = $this->add_excluded_tags_after_translation( $html_string, $output_with_excluded_tags_removed['excluded_tags'] ); return $html_string; } } $html = $this->handle_custom_links_and_forms( $html ); // Append an html table containing the errors $trp_editor_notices = apply_filters( 'trp_editor_notices', $trp_editor_notices ); if ( trp_is_translation_editor('preview') && $trp_editor_notices != '' ){ $body = $html->find('body', 0 ); if ( $body ) { $body->innertext = '<div data-no-translation class="trp-editor-notices">' . $trp_editor_notices . "</div>" . $body->innertext; } } $final_html = $html->save(); /* add back the excluded tags like script and style to the html */ $final_html = $this->add_excluded_tags_after_translation( $final_html, $output_with_excluded_tags_removed['excluded_tags'] ); /* perform preg replace on the remaining trp-gettext tags */ $final_html = $this->remove_trp_html_tags( $final_html ); return apply_filters( 'trp_translated_html', $final_html, $TRP_LANGUAGE, $language_code, $preview_mode ); } public function handle_custom_links_and_forms( $html ){ global $TRP_LANGUAGE; $preview_mode = isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview'; $home_url = home_url(); $admin_url = admin_url(); $wp_login_url = wp_login_url(); $no_translate_attribute = 'data-no-translation'; if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } // force custom links to have the correct language foreach( $html->find('a[href!="#"]') as $a_href) { $a_href->href = apply_filters( 'trp_href_from_translated_page', $a_href->href, $this->settings['default-language'] ); if($a_href->href === true){ $a_href->href = ''; // an empty href <a href>Link</a> causes href to be true instead of empty string. Exit early and do not use get_url_for_lang() on it. continue; } $url = trim($a_href->href); $url = $this->maybe_is_local_url($url, $home_url); $is_external_link = $this->is_external_link( $url, $home_url ); $is_admin_link = $this->is_admin_link($url, $admin_url, $wp_login_url); if( $preview_mode && ! $is_external_link ){ $a_href->setAttribute( 'data-trp-original-href', $url ); } if ( ( $TRP_LANGUAGE != $this->settings['default-language'] || $this->settings['add-subdirectory-to-default-language'] == 'yes' ) && $this->settings['force-language-to-custom-links'] == 'yes' && !$is_external_link && !$this->url_converter->url_is_file( $url ) && ( $this->url_converter->get_lang_from_url_string( $url ) == null || ( isset ($this->settings['add-subdirectory-to-default-language']) && $this->settings['add-subdirectory-to-default-language'] === 'yes' && $this->url_converter->get_lang_from_url_string( $url ) === $this->settings['default-language'] ) ) && !$is_admin_link && strpos($url, '#TRPLINKPROCESSED') === false && ( !$this->has_ancestor_attribute( $a_href, $no_translate_attribute ) || $this->has_ancestor_attribute($a_href, 'data-trp-gettext') ) // add language param to link if it's inside a gettext ){ $a_href->href = apply_filters( 'trp_force_custom_links', $this->url_converter->get_url_for_language( $TRP_LANGUAGE, $url, '' ), $url, $TRP_LANGUAGE, $a_href ); $url = $a_href->href; } if( $preview_mode && ( $is_external_link || $this->is_different_language( $url ) || $is_admin_link ) ) { $a_href->setAttribute( 'data-trp-unpreviewable', 'trp-unpreviewable' ); } $a_href->href = str_replace('#TRPLINKPROCESSED', '', $a_href->href); } // pass the current language in forms where the action does not contain the language // based on this we're filtering wp_redirect to include the proper URL when returning to the current page. foreach ( $html->find('form') as $k => $row ){ $form_action = $row->action; $is_admin_link = $this->is_admin_link( $form_action, $admin_url, $wp_login_url ); $skip_this_action = apply_filters( 'trp_skip_form_action', false, $form_action ); if( !$is_admin_link && !$skip_this_action && !$this->is_external_link( $form_action, $home_url ) ) { $row->setAttribute( 'data-trp-original-action', $row->action ); $row->innertext .= apply_filters( 'trp_form_inputs', '<input type="hidden" name="trp-form-language" value="' . $this->settings['url-slugs'][ $TRP_LANGUAGE ] . '"/>', $TRP_LANGUAGE, $this->settings['url-slugs'][ $TRP_LANGUAGE ], $row ); $is_external_link = $this->is_external_link( $form_action, $home_url ); if ( !empty( $form_action ) && $this->settings['force-language-to-custom-links'] == 'yes' && !$is_external_link && strpos( $form_action, '#TRPLINKPROCESSED' ) === false ) { /* $form_action can have language slug in a secondary language but the path slugs in original language. * By converting to default language first, it helps set the language slug to default language * while keeping the path slugs unchanged (no language coincidences should appear because we check * for uniqueness between secondary language translations and originals other than its own) * Use filter trp_change_form_action to hardcode particular cases */ $action_in_default_language = $this->url_converter->get_url_for_language( $this->settings['default-language'], $form_action, '' ); $action_in_current_language = $this->url_converter->get_url_for_language( $TRP_LANGUAGE, $action_in_default_language, '' ); $row->action = apply_filters( 'trp_change_form_action', $action_in_current_language, $action_in_default_language, $TRP_LANGUAGE ); } // this should happen regardless of whether we made changes above $row->action = str_replace( '#TRPLINKPROCESSED', '', esc_url( $row->action ) ); } } foreach ( $html->find('link') as $link ) { if ( isset( $link->href ) ) { if ( isset( $link->rel ) && ( $link->rel == 'next' || $link->rel == 'prev' ) ) $link->href = $this->url_converter->get_url_for_language( $TRP_LANGUAGE, $link->href ); $link->href = str_replace('#TRPLINKPROCESSED', '', $link->href); } } return $html; } public function is_first_language_not_default_language(){ return ( isset( $this->settings['add-subdirectory-to-default-language'] ) && $this->settings['add-subdirectory-to-default-language'] == 'yes' && isset( $this->settings['publish-languages'][0] ) && $this->settings['default-language'] != $this->settings['publish-languages'][0] ); } /* * Adjust translatable string so that it must match the content of the node value * * We use str_replace method in order to preserve any existent spacing before or after the string. * If the encoding of the node is not the same as the translatable string then the string won't match so try applying htmlentities. * If that doesn't work either, just forget about any possible before and after spaces. * */ public function maybe_correct_translatable_string( $translatable_string, $node_value ){ if ( strpos ( $node_value, $translatable_string ) === false ){ $translatable_string = htmlentities( $translatable_string ); if ( strpos ( $node_value, $translatable_string ) === false ){ $translatable_string = $node_value; } } return $translatable_string; } public function maybe_add_post_id_in_node( $nodes, $row, $string_count ){ $post_container_node = $this->has_ancestor_attribute( $row, 'data-trp-post-id' ); if( $post_container_node && $post_container_node->attr['data-trp-post-id'] ) { $nodes[$string_count - 1]['post_id'] = $post_container_node->attr['data-trp-post-id']; } return $nodes; } /* * Update other image attributes (srcset) with the translated image * * Hooked to trp_set_translation_for_attribute */ public function translate_image_srcset_attributes( $node, $accessor, $translated_string){ if( $accessor === 'src' ) { $srcset = $node->getAttribute( 'srcset' ); $datasrcset = $node->getAttribute( 'data-srcset' ); if ( $srcset || $datasrcset ) { $attachment_id = attachment_url_to_postid( $translated_string ); if ( $attachment_id ) { $translated_srcset = ''; if ( function_exists( 'wp_get_attachment_image_srcset' ) ) { // get width of the image in order, to set the largest possible size for srcset $meta_data = wp_get_attachment_metadata( $attachment_id ); $width = ( $meta_data && isset( $meta_data['width'] ) ) ? $meta_data['width'] : 'large'; $translated_srcset = wp_get_attachment_image_srcset( $attachment_id, $width ); } if ( $srcset ){ $node->setAttribute( 'srcset', $translated_srcset ); } if ( $datasrcset ){ $node->setAttribute( 'data-srcset', $translated_srcset ); } } else { $node->setAttribute( 'srcset', '' ); $node->setAttribute( 'data-srcset', '' ); } } if ( $node->getAttribute( 'data-src' ) ) { $node->setAttribute( 'data-src', $translated_string ); } } } /* * Do not automatically translate src and href attributes * * Hooked to trp_allow_machine_translation_for_string */ public function allow_machine_translation_for_string( $allow, $entity_decoded_trimmed_string, $current_node_accessor_selector, $node_accessor ){ $skip_attributes = apply_filters( 'trp_skip_machine_translation_for_attr', array( 'href', 'src', 'poster', 'srcset' ) ); if ( in_array( $current_node_accessor_selector, $skip_attributes ) ){ // do not machine translate href and src return false; } return $allow; } /* * Do not automatically translate html nodes with data-no-auto-translation attribute * * Hooked to trp_allow_machine_translation_for_string */ function skip_automatic_translation_for_no_auto_translation_selector($allow, $entity_decoded_trimmed_string, $current_node_accessor_selector, $node_accessor, $row){ $no_auto_translate_attribute = 'data-no-auto-translation'; if ( $this->has_ancestor_attribute( $row, $no_auto_translate_attribute ) || ( $current_node_accessor_selector !== null && $this->has_ancestor_attribute( $row, $no_auto_translate_attribute . '-' . $current_node_accessor_selector )) ){ return false; } return $allow; } /* * Do not automatically translate numbers, emails and base64 images * * Hooked to trp_allow_machine_translation_for_string */ public function skip_strings_that_cannot_be_auto_translated( $allow, $entity_decoded_trimmed_string, $current_node_accessor_selector, $node_accessor, $row ) { if ( is_numeric( $entity_decoded_trimmed_string ) || $this->looks_like_email( $entity_decoded_trimmed_string ) || ( strncmp( $entity_decoded_trimmed_string, 'data:image/', 11 ) === 0 && strpos( $entity_decoded_trimmed_string, ';base64,', 11 ) !== false ) ) { $allow = false; } return $allow; } /** * Very fast is_email function. Not 100% strict, but good enough for deciding to not auto translate it * * @param $s * @return bool */ public function looks_like_email( $s ) { $len = strlen( $s ); // Length sanity (fast integer checks) if ( $len < 6 || $len > 254 ) { return false; } // Single @ check (cheaper than regex) if ( substr_count( $s, '@' ) !== 1 ) { return false; } $at = strpos( $s, '@' ); // @ cannot be first or last if ( $at === 0 || $at === $len - 1 ) { return false; } // No spaces if ( strpos( $s, ' ' ) !== false ) { return false; } // Dot must exist after @ with at least one char in between $lastDot = strrpos( $s, '.' ); if ( $lastDot === false || $lastDot < $at + 2 || $lastDot === $len - 1 ) { return false; } // Regex only for plausible candidates return preg_match( '/^[^\s@]+@[^\s@]+\.[^\s@]+$/', $s ) === 1; } /** * function that removes any unwanted leftover <trp-gettext> tags * @param $string * @return string|string[]|null */ function remove_trp_html_tags( $string ){ $string = preg_replace( '/(<|<)trp-gettext (.*?)(>|>)/i', '', $string ); $string = preg_replace( '/(<|<)(\\\\)*\/trp-gettext(>|>)/i', '', $string ); // In case we have a gettext string which was run through rawurlencode(). See more details on iss6563 $string = preg_replace( '/%23%21trpst%23trp-gettext(.*?)%23%21trpen%23/i', '', $string ); $string = preg_replace( '/%23%21trpst%23%2Ftrp-gettext%23%21trpen%23/i', '', $string ); $string = preg_replace( '/%23%21trpst%23%5C%2Ftrp-gettext%23%21trpen%23/i', '', $string ); if (!isset($_REQUEST['trp-edit-translation']) || $_REQUEST['trp-edit-translation'] != 'preview') { $string = preg_replace('/(<|<)trp-wrap (.*?)(>|>)/i', '', $string); $string = preg_replace('/(<|<)(\\\\)*\/trp-wrap(>|>)/i', '', $string); } //remove post containers before outputting $string = preg_replace( '/(<|<)trp-post-container (.*?)(>|>)/i', '', $string ); $string = preg_replace( '/(<|<)(\\\\)*\/trp-post-container(>|>)/i', '', $string ); return $string; } /** * Callback for the array_walk_recursive to translate json. It translates the values in the resulting json array if they contain html * @param $value */ function translate_json (&$value) { //check if it a html text and translate $html_decoded_value = html_entity_decode( (string) $value ); if ( $html_decoded_value != strip_tags( $html_decoded_value ) ) { $value = $this->translate_page( $value ); /*the translate-press tag can appear on a gettext string without html and should not be left in the json as we don't know how it will be inserted into the page by js */ $value = preg_replace( '/(<|<)translate-press (.*?)(>|>)/', '', $value ); $value = preg_replace( '/(<|<)(\\\\)*\/translate-press(>|>)/', '', $value ); } } /** * Callback for the array_walk_recursive to process links inside json elements that might contain HTML. It processes the values in the resulting json array if they contain html * @param $value */ function custom_links_and_forms_json (&$value) { //check if it a html text and translate $html_decoded_value = html_entity_decode( (string) $value ); if ( $html_decoded_value != strip_tags( $html_decoded_value ) ) { $html = TranslatePress\str_get_html( $value, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT ); if( $html ) { $html = $this->handle_custom_links_and_forms($html); $value = $html->save(); } } } public function handle_custom_links_for_default_language(){ return ( $this->settings['force-language-to-custom-links'] == 'yes' && $this->is_first_language_not_default_language() && apply_filters('trp_handle_custom_links_and_forms_in_default_language', true ) ); } /** * Function that should be called only on the default language and when we are not in the editor mode and it is designed as a fallback to clear * any trp gettext tags that we added and for some reason show up although they should not * @param $output * @return mixed */ public function render_default_language( $output ){ if ( $this->handle_custom_links_for_default_language() && !apply_filters( 'trp_stop_translating_page', false, $output ) ) { $json_array = json_decode( $output, true ); /* If we have a json response we need to parse it and only translate the nodes that contain html * * Removed is_ajax_on_frontend() check because we need to capture custom ajax events. * Decided that if $output is json decodable it's a good enough check to handle it this way. * We have necessary checks so that we don't get to this point when is_admin(), or when language is not default. */ if( $json_array && $json_array != $output ) { /* if it's one of our own ajax calls don't do nothing */ if ( ! empty( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 && $_REQUEST['action'] != 'trp_split_translation_block' ) { return $output; } //check if we have a json response if ( ! empty( $json_array ) ) { if( is_array( $json_array ) ) { array_walk_recursive($json_array, array($this, 'custom_links_and_forms_json')); } else { $html = TranslatePress\str_get_html( $json_array, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT ); if( $html ) { $html = $this->handle_custom_links_and_forms($html); $json_array = $html->save(); } } } return trp_safe_json_encode( $json_array ); } $html = TranslatePress\str_get_html( $output, true, true, TRP_DEFAULT_TARGET_CHARSET, false, TRP_DEFAULT_BR_TEXT, TRP_DEFAULT_SPAN_TEXT ); if( $html ) { $html = $this->handle_custom_links_and_forms($html); $output = $html->save(); } } return TRP_Translation_Manager::strip_gettext_tags($output); } /** * Whether given url links to an external domain. * * @param string $url Url. * @param string $home_url Optional home_url so we avoid calling the home_url() inside loops. * @return bool Whether given url links to an external domain. */ public function is_external_link( $url, $home_url = '' ){ // Abort if parameter URL is empty if( empty($url) ) { return false; } if ( strpos( $url, '#' ) === 0 || strpos( $url, '/' ) === 0){ return false; } // Parse home URL and parameter URL $link_url = parse_url( $url ); if( empty( $home_url ) ) $home_url = home_url(); $home_url_parsed = parse_url( $home_url ); // Decide on target if( !isset ($link_url['host'] ) || $link_url['host'] == $home_url_parsed['host'] ) { // Is an internal link return false; } else { // Allow addons (like Multiple Domains) to recognize additional domains as internal return apply_filters( 'trp_is_external_link', true, $url, $home_url ); } } /** * Checks to see if the user didn't incorrectly formated a url that's different from the home_url * Takes into account http, https, www and all the possible combinations between them. * * @param string $url Url. * @param string $home_url Optional home_url so we avoid calling the home_url() inside loops. * @return string Correct URL that's the same structure as home_url */ public function maybe_is_local_url( $url, $home_url='' ){ if ( apply_filters('disable_maybe_is_local_url', false) ){ return $url; } // Abort if parameter URL is empty if( empty($url) ) { return $url; } if ( strpos( $url, '#' ) === 0 || strpos( $url, '/' ) === 0){ return $url; } // Parse home URL and parameter URL $link_url = parse_url( $url ); if( empty( $home_url ) ) $home_url = home_url(); $home_url = parse_url( $home_url ); // Decide on target if( !isset ($link_url['host'] ) || $link_url['host'] == $home_url['host'] || !isset ( $link_url['scheme'] ) ) { // Is an internal link return $url; } else { // test out possible local urls that the user might have mistyped $valid_local_prefix = array('http://', 'https://', 'http://www.', 'https://www.'); foreach ($valid_local_prefix as $prefix){ foreach ($valid_local_prefix as $replacement_prefix){ if( str_replace($prefix, $replacement_prefix, $link_url['scheme'] . '://' . $link_url['host']) == $home_url['scheme'] . '://' .$home_url['host'] ){ return str_replace($prefix, $replacement_prefix, $url); } } } // Is an external link return $url; } } /** * Whether given url links to a different language than the current one. * * @param string $url Url. * @return bool Whether given url links to a different language than the current one. */ protected function is_different_language( $url ){ global $TRP_LANGUAGE; if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } $lang = $this->url_converter->get_lang_from_url_string( $url ); if ( $lang == null ){ $lang = $this->settings['default-language']; } if ( $lang == $TRP_LANGUAGE ){ return false; }else{ return true; } } /** * Whether given url links to an admin page. * * @param string $url Url. * @return bool Whether given url links to an admin page. * * It's always been private, do not make public in the future so we don't use it in one of the paid addons, * causing Fatal Errors for users who update the Paid but not the Free */ public function is_admin_link( $url, $admin_url = '', $wp_login_url = '' ){ if( empty( $admin_url ) ) $admin_url = admin_url(); if( empty( $wp_login_url ) ) $wp_login_url = wp_login_url(); if ( strpos( $url, $admin_url ) !== false || strpos( $url, $wp_login_url ) !== false ){ $is_admin_link = true; } else { $is_admin_link = false; } return apply_filters('trp_is_admin_link', $is_admin_link, $url, $admin_url, $wp_login_url); } /** * Return translations for given strings in given language code. * * Also stores new strings, calls automatic translations and stores new translations. * * @param $translateable_strings * @param $language_code * @return array */ public function process_strings( $translateable_strings, $language_code, $block_type = null, $skip_machine_translating_strings = array(), $do_not_add_this_alug_to_dictionary_table = array() ) { if ( !in_array( $language_code, $this->settings['translation-languages'] ) || $language_code === $this->settings['default-language'] ) { return array(); } if ( !$this->machine_translator ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->machine_translator = $trp->get_component( 'machine_translator' ); } $translated_strings = array(); $machine_translation_available = $this->machine_translator ? $this->machine_translator->is_available( array( $this->settings['default-language'], $language_code ) ) : false; $originals_without_translation_in_db_that_are_similar_with_already_translated_strings = array(); if ( !$this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component( 'query' ); } // get existing translations $dictionary = $this->trp_query->get_existing_translations( array_values( $translateable_strings ), $language_code ); if ( $dictionary === false ) { return array(); } $new_strings = array(); $machine_translatable_strings = array(); /** * The filter 'trp_add_similar_and_original_strings_to_db' becomes true only when TranslatePress Settings->Advanced Settings->Serve similar translations for strings that are almost identical * is set to 'Yes' * This filter appears in multiple places in this class since we use the function process_strings to make sure all the strings are inserted in the DataBase with their corresponding translation * The similar strings we reference are strings that are almost identical with strings that are in the DB and have translations. * We use those translated strings to translate the similar strings so the user or the translation engine does not have to do it anymore and we can * display them in frontend (for more information about how this process is done please look at the file tranlatepress\includes\advanced-settings\serve-similar-translation.php) * * In the code below we are populating the array '$originals_without_translation_in_db_that_are_similar_with_already_translated_strings' with original strings * from the page that do not have a translation, but are almost identical with strings that are already in DB with translation. * A similar string is determined by looking at its value in $dictionary (an array that contains all the strings on the page). The $dictionary has as keys * the strings mentioned before, which are objects containing more arguments, one of them being original. However, in the case of a similar original string * without translation, the argument 'original' is the almost identical string that has a translation stored in the DB. * * We collect these similar strings in an array so we can merge them later in new_strings in order to be introduced into the DB. */ if ( apply_filters( 'trp_add_similar_and_original_strings_to_db', false ) ) { foreach ( array_keys( $dictionary ) as $value ) { if ( $value != $dictionary[ $value ]->original ) { $originals_without_translation_in_db_that_are_similar_with_already_translated_strings[] = $value; } } } foreach ( $translateable_strings as $i => $string ) { if ( isset( $dictionary[ $string ]->translated ) && empty( $dictionary[ $string ]->translated ) ) { /* If we have an empty string with a status != NOT TRANSLATED, it's possible we are dealing with * an intentional thing. After Automatic translation, a text can be translated with unallowed html * thus being stored in DB as empty string with status = MACHINE TRANSLATED. By doing continue; we avoid * re-autotranslating over and over again. */ continue; } // prevent accidentally machine translated strings from db such as for src to be displayed $skip_string = in_array( $string, $skip_machine_translating_strings ); if ( isset( $dictionary[ $string ]->translated ) && $dictionary[ $string ]->status == $this->trp_query->get_constant_machine_translated() && $skip_string ) { continue; } //strings existing in database, if ( isset( $dictionary[ $string ]->translated ) ) { $translated_strings[ $i ] = $dictionary[ $string ]->translated; } elseif ( in_array( $string, $do_not_add_this_alug_to_dictionary_table )) { //do not add excluded links to dictionary continue; }else{ $new_strings[ $i ] = $translateable_strings[ $i ]; // if the string is not a url then allow machine translation for it if ( !$this->url_converter ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } if ( $machine_translation_available && !$skip_string && filter_var( $new_strings[ $i ], FILTER_VALIDATE_URL ) === false && !$this->url_converter->url_is_extra( $new_strings[ $i ] ) ) { $machine_translatable_strings[ $i ] = $new_strings[ $i ]; } } /** * Here we look through the $translateable_strings found and if they are also in the array $originals_without_translation_in_db_that_are_similar_with_already_translated_strings * then we assign them to new_strings to prepare them for being inserted into DB. */ if ( apply_filters( 'trp_add_similar_and_original_strings_to_db', false ) ) { if ( in_array( $translateable_strings[ $i ], $originals_without_translation_in_db_that_are_similar_with_already_translated_strings ) ) { $new_strings[ $i ] = $translateable_strings[ $i ]; } } } $untranslated_list = $this->trp_query->get_untranslated_strings( $new_strings, $language_code ); $update_strings = array(); $unique_original_strings_with_machine_translations = array(); // machine translate new strings if ( $machine_translation_available ) { $machine_strings = $this->machine_translator->translate( $machine_translatable_strings, $language_code, $this->settings['default-language'] ); $unique_original_strings_with_machine_translations = array_keys( $machine_strings ); } /** * If the option is activated,we use the below code so the variable $original_to_be_synced contains the array of strings that are to be sent to a machine translation * engine together with the similar one, so they have entries into the table the trp_original_strings */ $originals_to_be_synced= array_merge( $unique_original_strings_with_machine_translations, $originals_without_translation_in_db_that_are_similar_with_already_translated_strings ); $original_inserts = $this->trp_query->original_strings_sync( $language_code, $originals_to_be_synced ); if ( $machine_translation_available ) { // insert unique machine translations into db. Only for strings newly discovered foreach ( $unique_original_strings_with_machine_translations as $string ) { $id = ( isset( $untranslated_list[ $string ] ) ) ? $untranslated_list[ $string ]->id : NULL; array_push( $update_strings, array( 'id' => $id, 'original_id' => $original_inserts[ $string ]->id, 'original' => trp_sanitize_string( $string, false ), 'translated' => trp_sanitize_string( $machine_strings[ $string ] ), 'status' => $this->trp_query->get_constant_machine_translated() ) ); } }else{ $machine_strings = false; } // update existing strings without translation if we have one now. also, do not insert duplicates for existing untranslated strings in db foreach( $new_strings as $i => $string ){ if ( !isset($translated_strings[$i]) && isset( $machine_strings[$string] ) ) { $sanitized_machine_string = trp_sanitize_string( $machine_strings[$string] ); if ( !empty( $sanitized_machine_string ) ){ // Unallowed HTML can be turned into empty string. Show original text instead $translated_strings[$i] = $sanitized_machine_string; } } /** * In this code we add the original similar strings, that have now more arguments, including the translation taken from the similar string in * DB, to the $update_strings array, following now to update the field in the DB with all the information gathered. * * We check if the new_string is not already in the DB and if it is we unset it in order to avoid multiple entries in DB for thr same string. * The similar strings have status 3 in DB. */ if ( apply_filters('trp_add_similar_and_original_strings_to_db', false) ) { if (isset($new_strings[$i]) && in_array($new_strings[$i],$originals_without_translation_in_db_that_are_similar_with_already_translated_strings ) ) { $id = ( isset( $untranslated_list[$string] ) ) ? $untranslated_list[$string]->id : NULL; array_push( $update_strings, array( 'id' => $id, 'original' => $new_strings[$i], 'translated' => trp_sanitize_string( $dictionary[$string]->translated ), 'status' => $this->trp_query->get_constant_similar_translated(), 'original_id' => $original_inserts[ $string ]->id) ); unset($new_strings[$i]); } } if ( isset( $untranslated_list[ $string ] ) || isset( $machine_strings[ $string ] ) ) { unset( $new_strings[ $i ] ); } } // Allow filtering whether to save strings to database (for manual translation only mode) if ( apply_filters( 'trp_allow_string_saving', true, $new_strings, $update_strings ) ) { $this->trp_query->insert_strings( $new_strings, $language_code, $block_type ); $this->trp_query->update_strings( $update_strings, $language_code, array( 'id','original', 'translated', 'status', 'original_id' ) ); } return $translated_strings; } /** * Whether given node has ancestor with given attribute. * * @param object $node Html Node. * @param string $attribute Attribute to search for. * @return mixed Whether given node has ancestor with given attribute. */ public function has_ancestor_attribute($node,$attribute) { $currentNode = $node; if ( isset( $node->$attribute ) ){ return $node; } while($currentNode->parent() && $currentNode->parent()->tag!="html") { if(isset($currentNode->parent()->$attribute)) return $currentNode->parent(); else $currentNode = $currentNode->parent(); } return false; } /** * Whether given node has ancestor with given class. * * @param object $node Html Node. * @param string $class class to search for * @return bool Whether given node has ancestor with given class. */ public function has_ancestor_class($node, $class) { $currentNode = $node; while($currentNode->parent() && $currentNode->parent()->tag!="html") { if(isset($currentNode->parent()->class) && strpos($currentNode->parent()->class, $class) !== false) { return true; } else { $currentNode = $currentNode->parent(); } } return false; } /** * Which attributes to translate and how to access them * * Nodes with "selector" attribute are automatically searched for in PHP translate_page and in JS translate-dom-changes * * @return array */ public function get_node_accessors(){ return apply_filters( 'trp_node_accessors', array( 'text' => array( 'accessor' => 'outertext', 'attribute' => false ), 'block' => array( 'accessor' => 'innertext', 'attribute' => false ), 'image_src' => array( 'selector' => 'img[src]', 'accessor' => 'src', 'attribute' => true ), 'submit' => array( 'selector' => 'input[type=\'submit\'],input[type=\'button\'], input[type=\'reset\']', 'accessor' => 'value', 'attribute' => true ), 'placeholder' => array( 'selector' => 'input[placeholder],textarea[placeholder]', 'accessor' => 'placeholder', 'attribute' => true ), 'title' => array( 'selector' => '[title]', 'accessor' => 'title', 'attribute' => true ), 'a_href' => array( 'selector' => 'a[href]', 'accessor' => 'href', 'attribute' => true ), 'button' => array( 'accessor' => 'outertext', 'attribute' => false ), 'option' => array( 'accessor' => 'innertext', 'attribute' => false ), 'aria_label' => array( 'selector' => '[aria-label]', 'accessor' => 'aria-label', 'attribute' => true ), 'video_src' => array( 'selector' => 'video[src]', 'accessor' => 'src', 'attribute' => true ), 'video_poster' => array( 'selector' => 'video[poster]', 'accessor' => 'poster', 'attribute' => true ), 'video_source_src' => array( 'selector' => 'video source[src]', 'accessor' => 'src', 'attribute' => true ), 'audio_src' => array( 'selector' => 'audio[src]', 'accessor' => 'src', 'attribute' => true ), 'audio_source_src' => array( 'selector' => 'audio source[src]', 'accessor' => 'src', 'attribute' => true ), 'picture_image_src' => array( 'selector' => 'picture image[src]', 'accessor' => 'src', 'attribute' => true ), 'picture_source_srcset' => array( 'selector' => 'picture source[srcset]', 'accessor' => 'srcset', 'attribute' => true ), )); } public function get_accessors_array( $prefix = '' ){ $accessor_array = array(); $node_accessors_array = $this->get_node_accessors(); foreach ( $node_accessors_array as $node_accessor ){ if ( isset ( $node_accessor['accessor'] ) ){ $accessor_array[] = $prefix . $node_accessor['accessor']; } } return array_values( array_unique( $accessor_array ) ); } /* * Enqueue scripts on all pages */ public function enqueue_scripts() { // so far only when woocommerce is active we need to enqueue this script on all pages if ( class_exists( 'WooCommerce' ) ){ wp_enqueue_script('trp-frontend-compatibility', TRP_PLUGIN_URL . 'assets/js/trp-frontend-compatibility.js', array(), TRP_PLUGIN_VERSION ); } } public function get_trp_data(){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $nonces = $this->translation_manager->editor_nonces(); $language_to_query = $TRP_LANGUAGE; if ( $TRP_LANGUAGE == $this->settings['default-language'] ) { foreach ($this->settings['translation-languages'] as $language) { if ( $language != $this->settings['default-language'] ) { $language_to_query = $language; break; } } } $language_to_query = ( count ( $this->settings['translation-languages'] ) < 2 ) ? '' : $language_to_query; return array( 'trp_custom_ajax_url' => apply_filters( 'trp_custom_ajax_url', TRP_PLUGIN_URL . 'includes/trp-ajax.php' ), 'trp_wp_ajax_url' => apply_filters( 'trp_wp_ajax_url', admin_url( 'admin-ajax.php' ) ), 'trp_language_to_query' => $language_to_query, 'trp_original_language' => $this->settings['default-language'], 'trp_current_language' => $TRP_LANGUAGE, 'trp_skip_selectors' => apply_filters( 'trp_skip_selectors_from_dynamic_translation', array( '[data-no-translation]', '[data-no-dynamic-translation]', '[data-trp-translate-id-innertext]', 'script', 'style', 'head', 'trp-span', 'translate-press' ), $TRP_LANGUAGE, $this->settings ), // data-trp-translate-id-innertext refers to translation block and it shouldn't be detected 'trp_base_selectors' => $this->get_base_attribute_selectors(), 'trp_attributes_selectors' => $this->get_node_accessors(), 'trp_attributes_accessors' => $this->get_accessors_array(), 'gettranslationsnonceregular' => $nonces['gettranslationsnonceregular'], 'showdynamiccontentbeforetranslation' => apply_filters( 'trp_show_dynamic_content_before_translation', false ), 'skip_strings_from_dynamic_translation' => apply_filters( 'trp_skip_strings_from_dynamic_translation', array() ), 'skip_strings_from_dynamic_translation_for_substrings' => apply_filters( 'trp_skip_strings_from_dynamic_translation_for_substrings', array( 'href' => array('amazon-adsystem', 'googleads', 'g.doubleclick') ) ), 'duplicate_detections_allowed' => apply_filters( 'trp_duplicate_detections_allowed', 100 ), 'trp_translate_numerals_opt' => isset ($this->settings["trp_advanced_settings"]["enable_numerals_translation"]) ? $this->settings["trp_advanced_settings"]["enable_numerals_translation"] : 'no', 'trp_no_auto_translation_selectors' => apply_filters( 'trp_no_auto_translate_selectors', array( '[data-no-auto-translation]' ), $TRP_LANGUAGE ) ); } /** * Enqueue dynamic translation script. */ public function enqueue_dynamic_translation(){ $enable_dynamic_translation = apply_filters( 'trp_enable_dynamic_translation', true ); if ( ! $enable_dynamic_translation ){ return; } global $TRP_LANGUAGE; if ( $TRP_LANGUAGE != $this->settings['default-language'] || ( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview' ) ) { $this->output_dynamic_translation_script(); } } /** * If is_late_dom_html_plugin_active() returns true, echo script on shutdown hook priority 10 * * Otherwise, enqueue script * * @see is_late_dom_html_plugin_active() * */ public function output_dynamic_translation_script(){ $script_src = TRP_PLUGIN_URL . 'assets/js/trp-translate-dom-changes.js'; $trp_data = $this->get_trp_data(); $trp_plugin_ver = TRP_PLUGIN_VERSION; $echo_scripts = function() use ( $script_src, $trp_data, $trp_plugin_ver ){ echo '<script type="text/javascript" id="trp-dynamic-translator-js-extra"> var trp_data = ' . json_encode( $trp_data ) . ';</script>'; echo '<script src="' . esc_url( $script_src ) . '?ver=' . esc_attr($trp_plugin_ver) .'" id="trp-dynamic-translator-js"></script>'; }; if ( is_late_dom_html_plugin_active() ){ add_action( 'shutdown', $echo_scripts ); return; } wp_enqueue_script('trp-dynamic-translator', $script_src, array('jquery'), TRP_PLUGIN_VERSION, true ); wp_localize_script('trp-dynamic-translator', 'trp_data', $trp_data ); } /** * Skip base selectors (data-trp-translate-id, data-trpgettextoriginal etc.) * * The base selectors (without any suffixes) are placed only if their children do not contain any nodes that are translatable * * hooked to trp_skip_selectors_from_dynamic_translation * * @param $skip_selectors * * @return array */ public function skip_base_attributes_from_dynamic_translation( $skip_selectors ){ $base_attributes = $this->get_base_attribute_selectors(); $selectors_to_skip = array(); foreach( $base_attributes as $base_attribute ){ $selectors_to_skip[] = '[' . $base_attribute . ']'; } return array_merge( $skip_selectors, $selectors_to_skip ); } /* * Get base attribute selectors */ public function get_base_attribute_selectors(){ return apply_filters( 'trp_base_attribute_selectors', array( 'data-trp-translate-id', 'data-trpgettextoriginal', 'data-trp-post-slug' ) ); } /** * Add a filter on the wp_mail function so we allow shortcode usage and run it through our translate function so it cleans it up nice and maybe even replace some strings * @param $args * @return array */ public function wp_mail_filter( $args ) { if ( ! is_array( $args ) ) { return $args; } if ( empty( $args['to'] ) ) { return $args; } global $TRP_LANGUAGE; $initial_language = $TRP_LANGUAGE; $recipient = $args['to']; // Normalize $recipient to a single email string (first recipient only - that's the main one) if ( is_array( $recipient ) ) { $first = reset( $recipient ); $recipient = is_string( $first ) ? $first : ''; } $recipient = (string) $recipient; // Keep only the first comma-separated entry if multiple are present in the string $recipient = trim( strtok( $recipient, ',' ) ); if ( $recipient !== '' ) { trp_switch_to_preffered_language( $recipient ); } $whitelisted_shortcodes = apply_filters( 'trp_whitelisted_shortcodes_for_wp_mail', array( 'trp_language', 'language-include', 'language-exclude' ) ); if ( array_key_exists( 'subject', $args ) ) { $args['subject'] = $this->translate_page( trp_do_these_shortcodes( $args['subject'], $whitelisted_shortcodes ) ); } if ( array_key_exists( 'message', $args ) ) { $args['message'] = $this->translate_page( trp_do_these_shortcodes( $args['message'], $whitelisted_shortcodes ) ); } // Switch back to the language used initially $TRP_LANGUAGE = $initial_language; return $args; } /** * Filters the location redirect to add the preview parameter to the next page * @param $location * @param $status * @return string * @since 1.0.8 */ public function force_preview_on_url_redirect( $location, $status ){ if( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview' ){ $location = add_query_arg( 'trp-edit-translation', 'preview', $location ); } return $location; } /** * Filters the location redirect to add the current language based on the trp-form-language parameter * @param $location * @param $status * @return string * @since 1.1.2 */ public function force_language_on_form_url_redirect( $location, $status ){ if( isset( $_REQUEST[ 'trp-form-language' ] ) && !empty($_REQUEST[ 'trp-form-language' ]) ){ $form_language_slug = sanitize_text_field($_REQUEST[ 'trp-form-language' ]); $form_language = array_search($form_language_slug, $this->settings['url-slugs']); if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } $location = $this->url_converter->get_url_for_language( $form_language, $location ); } return $location; } /** * Filters the output buffer of ajax calls that return json and adds the preview arg to urls * @param $output * @return string * @since 1.0.8 */ public function force_preview_on_url_in_ajax( $output ){ if ( TRP_Gettext_Manager::is_ajax_on_frontend() && isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] === 'preview' && $output != false ) { $result = json_decode($output, TRUE); if ( json_last_error() === JSON_ERROR_NONE) { if( !is_array( $result ) )//make sure we send an array as json_decode even with true parameter might not return one $result = array($result); array_walk_recursive($result, array($this, 'callback_add_preview_arg')); $output = trp_safe_json_encode($result); } //endif } //endif return $output; } /** * Adds preview query arg to links that are url's. callback specifically for the array_walk_recursive function * @param $item * @param $key * @return string * @internal param $output * @since 1.0.8 */ function callback_add_preview_arg(&$item, $key){ if ( filter_var($item, FILTER_VALIDATE_URL) !== FALSE ) { $item = add_query_arg( 'trp-edit-translation', 'preview', $item ); } } /** * Filters the output buffer of ajax calls that return json and adds the preview arg to urls * @param $output * @return string * @since 1.1.2 */ public function force_form_language_on_url_in_ajax( $output ){ if ( TRP_Gettext_Manager::is_ajax_on_frontend() && isset( $_REQUEST[ 'trp-form-language' ] ) && !empty( $_REQUEST[ 'trp-form-language' ] ) ) { $result = json_decode($output, TRUE); if ( is_array( $result ) && json_last_error() === JSON_ERROR_NONE) { array_walk_recursive($result, array($this, 'callback_add_language_to_url')); $output = trp_safe_json_encode($result); } //endif } //endif return $output; } /** * Adds preview query arg to links that are url's. callback specifically for the array_walk_recursive function * @param $item * @param $key * @return string * @internal param $output * @since 1.1.2 */ function callback_add_language_to_url(&$item, $key){ if ( filter_var($item, FILTER_VALIDATE_URL) !== FALSE ) { $form_language_slug = isset( $_REQUEST[ 'trp-form-language' ] ) ? sanitize_text_field($_REQUEST[ 'trp-form-language' ]) : ''; $form_language = array_search($form_language_slug, $this->settings['url-slugs']); if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } $item = $this->url_converter->get_url_for_language( $form_language, $item ); $item = str_replace('#TRPLINKPROCESSED', '', $item); } } /** * Function that reverses CDATA string replacement from the content because it breaks the renderer * @param $output * @return mixed */ public function handle_cdata( $output ){ $output = str_replace( ']]>', ']]>', $output ); return $output; } /** * Function always renders the default language wptexturize characters instead of the translated ones for secondary languages. * @param string * @param string * @param string * @param string * @return string */ function fix_wptexturize_characters( $translated, $text, $context, $domain ){ global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $default_language= $settings["default-language"]; // it's reversed because the same string ’ is replaced differently based on context and we can't have the same key twice on an array $list_of_context_text = array( 'opening curly double quote' => '“', 'closing curly double quote' => '”', 'apostrophe' => '’', 'prime' => '′', 'double prime' => '″', 'opening curly single quote' => '‘', 'closing curly single quote' => '’', 'en dash' => '–', 'em dash' => '—', 'Comma-separated list of words to texturize in your language' => "'tain't,'twere,'twas,'tis,'twill,'til,'bout,'nuff,'round,'cause,'em", 'Comma-separated list of replacement words in your language' => '’tain’t,’twere,’twas,’tis,’twill,’til,’bout,’nuff,’round,’cause,’em' ); if( $default_language != $TRP_LANGUAGE && array_key_exists($context, $list_of_context_text) && in_array($text, $list_of_context_text) ){ return trp_x( $text, $context, '', $default_language ); } return $translated; } /** * Function that wraps the post title and the post content in a custom trp wrap trp-post-container so we can know in * the function translate_page() if a string is part of the content of a post so we can store a meta that gives the string context * @param $content * @param null $id * @return string */ function wrap_with_post_id( $content, $id = null ){ global $post, $TRP_LANGUAGE, $wp_query; if( empty($post->ID) ) return $content; //we try to wrap only the actual content of the post and not when the filters are executed in SEO plugins for example if( ( !$wp_query->in_the_loop || !is_main_query() ) && apply_filters('trp_wrap_with_post_id_overrule', true ) ) return $content; //for the_tile filter we have an $id and we can compare it with the post we are on ..to avoid wrapping titles in menus for example if( !is_null( $id ) && $id !== $post->ID ){ return $content; } if ( $TRP_LANGUAGE !== $this->settings['default-language'] ) { if ( is_singular() && !empty($post->ID)) { $content = "<trp-post-container data-trp-post-id='" . $post->ID . "'>" . $content . "</trp-post-container>";//changed " to ' to not break cases when the filter is applied inside an html attribute (title for example) } } return $content; } /** * Function that wraps around the PHP's is_numeric function and adds an additional check, * namely the option to translate numerals/numbers to be on. * @param $str * @return bool */ function trp_is_numeric($str){ if (is_numeric($str)){ if (isset($this->settings["trp_advanced_settings"]["enable_numerals_translation"]) && $this->settings["trp_advanced_settings"]["enable_numerals_translation"] === 'yes') { return false; } else { return true; } } else { return false; } } /** * Whether a text contains html tags. * Match an opening or closing tag among given html tags. * @param $string * @return bool */ public function is_html( $string ){ $pattern = '/<\/?(' . $this->common_html_tags . ')(\s[^>]*)?(\s?\/)?\>/'; return preg_match($pattern, $string, $matches); } /** * Matches the conditions set by $key_term_arrays * * See $skip_strings_containing_key_terms for config * * @param $string * @param $key_terms_arrays * @return bool */ public function contains_substrings($string, $key_terms_arrays){ foreach( $key_terms_arrays as $key_terms ) { if ( !empty( $key_terms['operator'] ) && !empty( $key_terms['terms'] ) && is_array( $key_terms ) ) { if ( $key_terms['operator'] == 'or' ){ foreach ( $key_terms['terms'] as $term ) { if ( stripos( $string, $term ) !== false ) { return true; } } } if ( $key_terms['operator'] == 'and' ){ foreach ( $key_terms['terms'] as $array_key => $term ) { if ( stripos( $string, $term ) !== false ) { unset($key_terms['terms'][$array_key]); if ( count ($key_terms['terms'] ) == 0 ){ return true; } } } } } } return false; } /** * Searches for strings that are emails that go through antispambot() function in wp and saves them in the db not html encoded. * Hooks on the trp_translateable_strings filter defined in translate_page() * * @param $translateable_information * @param $html * @param $no_translate_attribute * @param $global_TRP_LANGUAGE * @param $language_code * @param $instance_TRP_Translation_Render * @return array */ public function antispambot_infinite_detection_fix( $translateable_information, $html, $no_translate_attribute, $global_TRP_LANGUAGE, $language_code, $instance_TRP_Translation_Render) { if (!is_array($translateable_information['translateable_strings'])){ return $translateable_information; } foreach ($translateable_information['translateable_strings'] as $key => $string){ $translateable_information['translateable_strings'][$key] = is_email(html_entity_decode($string)) ? html_entity_decode($string) : $string; } return $translateable_information; } /** * Remove excluded tags before translation * and replaces them with <trp-replace-$index></trp-replace-$index> * * @param $output * @param $excluded_tags * @return array */ private function remove_tags_from_output($output, $excluded_tags) { if (!is_string($output) || !is_array($excluded_tags)) { return array('output' => $output, 'excluded_tags' => array()); } $trp_excluded_replacements = []; $index = 0; $result = ''; $offset = 0; while (true) { $found = false; foreach ($excluded_tags as $tag) { $start = stripos($output, "<$tag", $offset); // We keep the first found tag - min($start_for_tag_script, $start_for_tag_style) where $start < $found['pos'] if ($start !== false && ($found === false || $start < $found['pos'])) { $found = [ 'pos' => $start, 'tag' => $tag ]; } } if ($found === false) { // No more excluded tags found $result .= substr($output, $offset); break; } $start = $found['pos']; $tag = $found['tag']; // Add everything before the tag $result .= substr($output, $offset, $start - $offset); // Find closing tag </tag> $end = stripos($output, "</$tag", $start); if ($end === false) { // malformed: no closing tag $result .= substr($output, $start); break; } // Move to closing ">" $close_pos = strpos($output, '>', $end); if ($close_pos === false) { // malformed: no ">" $result .= substr($output, $start); break; } /** * Preserve <script type="application/ld+json"> blocks, * so they remain in the DOM and can be processed by * translate_schema_data() via trp_process_other_text_nodes. */ if ( $tag === 'script' ) { $open_tag_end = strpos( $output, '>', $start ); if ( $open_tag_end === false ) { // malformed: no ">" on opening tag $result .= substr( $output, $start ); break; } // Opening <script ...> tag only $opening_tag_html = substr( $output, $start, $open_tag_end - $start + 1 ); // If this is JSON-LD, keep the whole block untouched if ( stripos( $opening_tag_html, 'application/ld+json' ) !== false ) { // Append the full <script>...</script> block $result .= substr( $output, $start, $close_pos - $start + 1 ); $offset = $close_pos + 1; // Do NOT record a replacement / placeholder for this one continue; } } // Full <tag>...</tag> $tag_html = substr($output, $start, $close_pos - $start + 1); // Save replacement $trp_excluded_replacements[$index] = $tag_html; // Insert placeholder $result .= "<trp-replace-$index></trp-replace-$index>"; // Advance offset $offset = $close_pos + 1; $index++; } return array('output' => $result, 'excluded_tags' => $trp_excluded_replacements); } private function add_excluded_tags_after_translation($final_html, $trp_excluded_replacements) { if (!is_string($final_html) || !is_array($trp_excluded_replacements) || empty($trp_excluded_replacements)) { return $final_html; } foreach ($trp_excluded_replacements as $index => $tag_html) { $placeholder = "<trp-replace-$index></trp-replace-$index>"; $final_html = str_replace($placeholder, $tag_html, $final_html); } return $final_html; } } includes/functions.php 0000777 00000112531 15251156640 0011112 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Outputs language switcher. * * Uses customization options from Shortcode language switcher. */ function trp_the_language_switcher(){ $trp = TRP_Translate_Press::get_trp_instance(); $language_switcher = $trp->get_component( 'language_switcher' ); echo $language_switcher->language_switcher(); /* phpcs:ignore */ /* escaped inside the function */ } /** * Wrapper function for json_encode to eliminate possible UTF8 special character errors * @param $value * @return mixed|string|void */ function trp_safe_json_encode($value){ if (version_compare(PHP_VERSION, '5.4.0') >= 0 && apply_filters('trp_safe_json_encode_pretty_print', true )) { $encoded = json_encode($value, JSON_PRETTY_PRINT); } else { $encoded = json_encode($value); } switch (json_last_error()) { case JSON_ERROR_NONE: return $encoded; case JSON_ERROR_DEPTH: return 'Maximum stack depth exceeded'; // or trigger_error() or throw new Exception() case JSON_ERROR_STATE_MISMATCH: return 'Underflow or the modes mismatch'; // or trigger_error() or throw new Exception() case JSON_ERROR_CTRL_CHAR: return 'Unexpected control character found'; case JSON_ERROR_SYNTAX: return 'Syntax error, malformed JSON'; // or trigger_error() or throw new Exception() case JSON_ERROR_UTF8: $clean = trp_utf8ize($value); return trp_safe_json_encode($clean); default: return 'Unknown error'; // or trigger_error() or throw new Exception() } } /** * Helper function for trp_safe_json_encode that helps eliminate utf8 json encode errors * @param $mixed * @return array|string */ function trp_utf8ize($mixed) { if (is_array($mixed)) { foreach ($mixed as $key => $value) { $mixed[$key] = trp_utf8ize($value); } } else if (is_string ($mixed)) { return utf8_encode($mixed); } return $mixed; } /** * function that gets the translation for a string with context directly from a .mo file * @TODO this was developped firstly for woocommerce so it maybe needs further development. */ function trp_x( $text, $context, $domain, $language ) { $original_text = $text; $cache_key = 'trp_x_' . md5( $text . $context . $domain . $language ); $new_text = wp_cache_get( $cache_key ); if ( $new_text !== false ) { return $new_text; } /* try to find the correct path for the textdomain */ $path_cache_key = 'trp_x_path_' . md5( $domain . $language ); $path = wp_cache_get( $path_cache_key ); if ( $path === false ) { $path = trp_find_translation_location_for_domain( $domain, $language ); wp_cache_set( $path_cache_key, $path ); } if ( !empty( $path ) ) { $mo_file = trp_cache_get( 'trp_x_' . $domain . '_' . $language ); if ( false === $mo_file ) { $mo_file = new MO(); $mo_file->import_from_file( $path ); wp_cache_set( 'trp_x_' . $domain . '_' . $language, $mo_file ); } if ( !$mo_file ) { $return = apply_filters( 'trp_x', $text, $original_text, $context, $domain, $language ); wp_cache_set( $cache_key, $return ); return $return; } if ( !empty( $mo_file->entries[ $context . '' . $text ] ) ) { $text = $mo_file->entries[ $context . '' . $text ]->translations[0]; } } $return = apply_filters( 'trp_x', $text, $original_text, $context, $domain, $language ); wp_cache_set( $cache_key, $return ); return $return; } /** * updated function that gets the translation for a string with context directly from a .po file * @TODO the initial trp_x function was returning the translation in english for the slugs I tried to search even if they were translation for them * the trp_x function also searches the .mo file witch doesn't seem to be the right file, but the .po file instead */ function trp_x_updated( $original_text, $context, $domain, $language ){ // Define the base path to the plugin's languages directory $basePath = WP_CONTENT_DIR . '/languages/plugins/'; // Form the path to the .po file $poFilePath = $basePath . $domain . '-' . $language . '.po'; if (!file_exists($poFilePath)) { return $original_text; } $poContent = file_get_contents($poFilePath); $pattern = '/msgctxt\s+"'.preg_quote($context, '/').'"\s+msgid\s+"'.preg_quote($original_text, '/').'"\s+msgstr\s+"(.*?)"/s'; if (preg_match($pattern, $poContent, $matches)) { return stripslashes($matches[1]); } return $original_text; } /** * Function that tries to find the path for a translation file defined by textdomain and language * @param $domain the textdomain of the string that you want the translation for * @param $language the language in which you want the translation * @return string the path of the mo file if it is found else an empty string */ function trp_find_translation_location_for_domain( $domain, $language ){ global $trp_template_directory; if ( !isset($trp_template_directory)){ // "caching" this because it sometimes leads to increased page load time due to many calls $trp_template_directory = get_template_directory(); } $path = ''; if( file_exists( WP_LANG_DIR . '/plugins/'. $domain .'-' . $language . '.mo') ) { $path = WP_LANG_DIR . '/plugins/'. $domain .'-' . $language . '.mo'; } elseif ( file_exists( WP_LANG_DIR . '/themes/'. $domain .'-' . $language . '.mo') ){ $path = WP_LANG_DIR . '/themes/'. $domain .'-' . $language . '.mo'; } elseif( $domain === '' && file_exists( WP_LANG_DIR . '/' . $language . '.mo')){ $path = WP_LANG_DIR . '/' . $language . '.mo'; } else { $possible_translation_folders = array( '', 'languages/', 'language/', 'translations/', 'translation/', 'lang/' ); foreach( $possible_translation_folders as $possible_translation_folder ){ if (file_exists($trp_template_directory . '/' . $possible_translation_folder . $domain . '-' . $language . '.mo')) { $path = $trp_template_directory . '/' . $possible_translation_folder . $domain . '-' . $language . '.mo'; } elseif ( file_exists(WP_PLUGIN_DIR . '/' . $domain . '/' . $possible_translation_folder . $domain . '-' . $language . '.mo') ) { $path = WP_PLUGIN_DIR . '/' . $domain . '/' . $possible_translation_folder . $domain . '-' . $language . '.mo'; } } } return $path; } /** * Function that appends the affiliate_id to a given url * @param $link string the given url to append * @return string url with the added affiliate_id */ function trp_add_affiliate_id_to_link( $link ){ //Avangate Affiliate Network $avg_affiliate_id = get_option('translatepress_avg_affiliate_id'); if ( !empty( $avg_affiliate_id ) ) { $link = add_query_arg( 'avgref', $avg_affiliate_id, $link ); } else{ // AffiliateWP $affiliate_id = get_option('translatepress_affiliate_id'); if ( !empty( $affiliate_id ) ) { $link = add_query_arg( 'ref', $affiliate_id, $link ); } } return esc_url( apply_filters( 'trp_affiliate_link', $link ) ); } /** * Function that makes string safe for display. * * Can be used on original or translated string. * Removes any unwanted html code from the string. * Do not confuse with trim. */ function trp_sanitize_string( $filtered, $execute_wp_kses = true ){ // Numbers and strings are ok. Unexpected arrays, objects or null are not ok. if (!is_scalar($filtered)) return ''; $filtered = preg_replace( '/<script\b[^>]*>(.*?)<\/script>/is', '', $filtered ); // don't remove \r \n \t. They are part of the translation, they give structure and context to the text. //$filtered = preg_replace( '/[\r\n\t ]+/', ' ', $filtered ); $filtered = trim( $filtered ); $found = false; while ( preg_match('/%[a-f0-9]{2}/i', $filtered, $match) ) { $filtered = str_replace($match[0], '', $filtered); $found = true; } if ( $found ) { // Strip out the whitespace that may now exist after removing the octets. $filtered = trim( preg_replace('/ +/', ' ', $filtered) ); } if ( $execute_wp_kses ){ $filtered = trp_wp_kses( $filtered ); } return $filtered; } function trp_wp_kses($string){ if ( apply_filters('trp_apply_wp_kses_on_strings', true) ){ add_filter( 'wp_kses_allowed_html', 'trp_prevent_kses_from_stripping_trp_wbr_tag', 10, 2 ); $string = wp_kses_post($string); remove_filter('wp_kses_allowed_html', 'trp_prevent_kses_from_stripping_trp_wbr_tag', 10); } return $string; } function trp_prevent_kses_from_stripping_trp_wbr_tag( $allowedposttags, $context ){ if ( $context === 'post' ){ $allowedposttags['wbr'] = true; } return $allowedposttags; } /** * function that checks if $_REQUEST['trp-edit-translation'] is set or if it has a certain value */ function trp_is_translation_editor( $value = '' ){ if( isset( $_REQUEST['trp-edit-translation'] ) ){ if( !empty( $value ) ) { if( $_REQUEST['trp-edit-translation'] === $value ) { return true; } else{ return false; } } else{ $possible_values = array ('preview', 'true'); if( in_array( $_REQUEST['trp-edit-translation'], $possible_values ) ) { return true; } } } return false; } function trp_remove_accents( $string ){ if ( !preg_match('/[\x80-\xff]/', $string) ) return $string; $seems_utf = ( function_exists( 'wp_is_valid_utf8' ) ) ? wp_is_valid_utf8( $string ) : seems_utf8( $string ); if ( $seems_utf ) { $chars = array( // Decompositions for Latin-1 Supplement 'ª' => 'a', 'º' => 'o', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'AE','Ç' => 'C', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'TH','ß' => 's', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'ae','ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ý' => 'y', 'þ' => 'th', 'ÿ' => 'y', 'Ø' => 'O', // Decompositions for Latin Extended-A 'Ā' => 'A', 'ā' => 'a', 'Ă' => 'A', 'ă' => 'a', 'Ą' => 'A', 'ą' => 'a', 'Ć' => 'C', 'ć' => 'c', 'Ĉ' => 'C', 'ĉ' => 'c', 'Ċ' => 'C', 'ċ' => 'c', 'Č' => 'C', 'č' => 'c', 'Ď' => 'D', 'ď' => 'd', 'Đ' => 'D', 'đ' => 'd', 'Ē' => 'E', 'ē' => 'e', 'Ĕ' => 'E', 'ĕ' => 'e', 'Ė' => 'E', 'ė' => 'e', 'Ę' => 'E', 'ę' => 'e', 'Ě' => 'E', 'ě' => 'e', 'Ĝ' => 'G', 'ĝ' => 'g', 'Ğ' => 'G', 'ğ' => 'g', 'Ġ' => 'G', 'ġ' => 'g', 'Ģ' => 'G', 'ģ' => 'g', 'Ĥ' => 'H', 'ĥ' => 'h', 'Ħ' => 'H', 'ħ' => 'h', 'Ĩ' => 'I', 'ĩ' => 'i', 'Ī' => 'I', 'ī' => 'i', 'Ĭ' => 'I', 'ĭ' => 'i', 'Į' => 'I', 'į' => 'i', 'İ' => 'I', 'ı' => 'i', 'IJ' => 'IJ','ij' => 'ij', 'Ĵ' => 'J', 'ĵ' => 'j', 'Ķ' => 'K', 'ķ' => 'k', 'ĸ' => 'k', 'Ĺ' => 'L', 'ĺ' => 'l', 'Ļ' => 'L', 'ļ' => 'l', 'Ľ' => 'L', 'ľ' => 'l', 'Ŀ' => 'L', 'ŀ' => 'l', 'Ł' => 'L', 'ł' => 'l', 'Ń' => 'N', 'ń' => 'n', 'Ņ' => 'N', 'ņ' => 'n', 'Ň' => 'N', 'ň' => 'n', 'ʼn' => 'n', 'Ŋ' => 'N', 'ŋ' => 'n', 'Ō' => 'O', 'ō' => 'o', 'Ŏ' => 'O', 'ŏ' => 'o', 'Ő' => 'O', 'ő' => 'o', 'Œ' => 'OE','œ' => 'oe', 'Ŕ' => 'R','ŕ' => 'r', 'Ŗ' => 'R','ŗ' => 'r', 'Ř' => 'R','ř' => 'r', 'Ś' => 'S','ś' => 's', 'Ŝ' => 'S','ŝ' => 's', 'Ş' => 'S','ş' => 's', 'Š' => 'S', 'š' => 's', 'Ţ' => 'T', 'ţ' => 't', 'Ť' => 'T', 'ť' => 't', 'Ŧ' => 'T', 'ŧ' => 't', 'Ũ' => 'U', 'ũ' => 'u', 'Ū' => 'U', 'ū' => 'u', 'Ŭ' => 'U', 'ŭ' => 'u', 'Ů' => 'U', 'ů' => 'u', 'Ű' => 'U', 'ű' => 'u', 'Ų' => 'U', 'ų' => 'u', 'Ŵ' => 'W', 'ŵ' => 'w', 'Ŷ' => 'Y', 'ŷ' => 'y', 'Ÿ' => 'Y', 'Ź' => 'Z', 'ź' => 'z', 'Ż' => 'Z', 'ż' => 'z', 'Ž' => 'Z', 'ž' => 'z', 'ſ' => 's', // Decompositions for Latin Extended-B 'Ș' => 'S', 'ș' => 's', 'Ț' => 'T', 'ț' => 't', // Euro Sign '€' => 'E', // GBP (Pound) Sign '£' => '', // Vowels with diacritic (Vietnamese) // unmarked 'Ơ' => 'O', 'ơ' => 'o', 'Ư' => 'U', 'ư' => 'u', // grave accent 'Ầ' => 'A', 'ầ' => 'a', 'Ằ' => 'A', 'ằ' => 'a', 'Ề' => 'E', 'ề' => 'e', 'Ồ' => 'O', 'ồ' => 'o', 'Ờ' => 'O', 'ờ' => 'o', 'Ừ' => 'U', 'ừ' => 'u', 'Ỳ' => 'Y', 'ỳ' => 'y', // hook 'Ả' => 'A', 'ả' => 'a', 'Ẩ' => 'A', 'ẩ' => 'a', 'Ẳ' => 'A', 'ẳ' => 'a', 'Ẻ' => 'E', 'ẻ' => 'e', 'Ể' => 'E', 'ể' => 'e', 'Ỉ' => 'I', 'ỉ' => 'i', 'Ỏ' => 'O', 'ỏ' => 'o', 'Ổ' => 'O', 'ổ' => 'o', 'Ở' => 'O', 'ở' => 'o', 'Ủ' => 'U', 'ủ' => 'u', 'Ử' => 'U', 'ử' => 'u', 'Ỷ' => 'Y', 'ỷ' => 'y', // tilde 'Ẫ' => 'A', 'ẫ' => 'a', 'Ẵ' => 'A', 'ẵ' => 'a', 'Ẽ' => 'E', 'ẽ' => 'e', 'Ễ' => 'E', 'ễ' => 'e', 'Ỗ' => 'O', 'ỗ' => 'o', 'Ỡ' => 'O', 'ỡ' => 'o', 'Ữ' => 'U', 'ữ' => 'u', 'Ỹ' => 'Y', 'ỹ' => 'y', // acute accent 'Ấ' => 'A', 'ấ' => 'a', 'Ắ' => 'A', 'ắ' => 'a', 'Ế' => 'E', 'ế' => 'e', 'Ố' => 'O', 'ố' => 'o', 'Ớ' => 'O', 'ớ' => 'o', 'Ứ' => 'U', 'ứ' => 'u', // dot below 'Ạ' => 'A', 'ạ' => 'a', 'Ậ' => 'A', 'ậ' => 'a', 'Ặ' => 'A', 'ặ' => 'a', 'Ẹ' => 'E', 'ẹ' => 'e', 'Ệ' => 'E', 'ệ' => 'e', 'Ị' => 'I', 'ị' => 'i', 'Ọ' => 'O', 'ọ' => 'o', 'Ộ' => 'O', 'ộ' => 'o', 'Ợ' => 'O', 'ợ' => 'o', 'Ụ' => 'U', 'ụ' => 'u', 'Ự' => 'U', 'ự' => 'u', 'Ỵ' => 'Y', 'ỵ' => 'y', // Vowels with diacritic (Chinese, Hanyu Pinyin) 'ɑ' => 'a', // macron 'Ǖ' => 'U', 'ǖ' => 'u', // acute accent 'Ǘ' => 'U', 'ǘ' => 'u', // caron 'Ǎ' => 'A', 'ǎ' => 'a', 'Ǐ' => 'I', 'ǐ' => 'i', 'Ǒ' => 'O', 'ǒ' => 'o', 'Ǔ' => 'U', 'ǔ' => 'u', 'Ǚ' => 'U', 'ǚ' => 'u', // grave accent 'Ǜ' => 'U', 'ǜ' => 'u', ); // Used for locale-specific rules $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $default_language= $settings["default-language"]; $locale = $default_language; if ( 'de_DE' == $locale || 'de_DE_formal' == $locale || 'de_CH' == $locale || 'de_CH_informal' == $locale ) { $chars[ 'Ä' ] = 'Ae'; $chars[ 'ä' ] = 'ae'; $chars[ 'Ö' ] = 'Oe'; $chars[ 'ö' ] = 'oe'; $chars[ 'Ü' ] = 'Ue'; $chars[ 'ü' ] = 'ue'; $chars[ 'ß' ] = 'ss'; } elseif ( 'da_DK' === $locale ) { $chars[ 'Æ' ] = 'Ae'; $chars[ 'æ' ] = 'ae'; $chars[ 'Ø' ] = 'Oe'; $chars[ 'ø' ] = 'oe'; $chars[ 'Å' ] = 'Aa'; $chars[ 'å' ] = 'aa'; } elseif ( 'ca' === $locale ) { $chars[ 'l·l' ] = 'll'; } elseif ( 'sr_RS' === $locale || 'bs_BA' === $locale ) { $chars[ 'Đ' ] = 'DJ'; $chars[ 'đ' ] = 'dj'; } $string = strtr($string, $chars); } else { $chars = array(); // Assume ISO-8859-1 if not UTF-8 $chars['in'] = "\x80\x83\x8a\x8e\x9a\x9e" ."\x9f\xa2\xa5\xb5\xc0\xc1\xc2" ."\xc3\xc4\xc5\xc7\xc8\xc9\xca" ."\xcb\xcc\xcd\xce\xcf\xd1\xd2" ."\xd3\xd4\xd5\xd6\xd8\xd9\xda" ."\xdb\xdc\xdd\xe0\xe1\xe2\xe3" ."\xe4\xe5\xe7\xe8\xe9\xea\xeb" ."\xec\xed\xee\xef\xf1\xf2\xf3" ."\xf4\xf5\xf6\xf8\xf9\xfa\xfb" ."\xfc\xfd\xff"; $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy"; $string = strtr($string, $chars['in'], $chars['out']); $double_chars = array(); $double_chars['in'] = array("\x8c", "\x9c", "\xc6", "\xd0", "\xde", "\xdf", "\xe6", "\xf0", "\xfe"); $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); $string = str_replace($double_chars['in'], $double_chars['out'], $string); } return $string; }; /** * Output an SVG depending on case. * * @param string $icon The icon to output. Default no icon. */ function trp_output_svg( $icon = '' ) { switch ( $icon ) { case 'check': ?> <svg class="trp-svg-icon fas-check-circle"><use xlink:href="#check-circle"></use></svg> <?php break; case 'error': ?> <svg class="trp-svg-icon fas-times-circle"><use xlink:href="#times-circle"></use></svg> <?php break; default: break; } } /** * Debuger function. Mainly designed for the get_url_for_language() function * * @since 1.3.6 * * @param bool $enabled * @param array $logger */ function trp_bulk_debug($debug = false, $logger = array()){ if(!$debug){ return; } error_log('---------------------------------------------------------'); $key_length = ''; foreach ($logger as $key => $value){ if ( strlen($key) > $key_length) $key_length = strlen($key); } foreach ($logger as $key => $value){ error_log("$key : " . str_repeat(' ', $key_length - strlen($key)) . $value); } error_log('---------------------------------------------------------'); } /** * Used for showing useful notice in Translation Editor * * @return bool */ function trp_is_paid_version() { // Check if TranslatePress paid plugins are active $paid_plugins = array( 'TranslatePress - Personal' => 'translatepress-personal/index.php', 'TranslatePress - Business' => 'translatepress-business/index.php', 'TranslatePress - Developer' => 'translatepress-developer/index.php' ); $active_plugins = get_option('active_plugins', array()); foreach ($paid_plugins as $plugin_file) { if (is_array($active_plugins) && in_array($plugin_file, $active_plugins)) { return true; } } //list of class names $addons = apply_filters( 'trp_paid_addons', array( 'TRP_IN_Automatic_Language_Detection', 'TRP_IN_Browse_as_other_Role', 'TRP_IN_Extra_Languages', 'TRP_IN_Navigation_Based_on_Language', 'TRP_IN_Seo_Pack', 'TRP_IN_Translator_Accounts', 'TRP_Automatic_Language_Detection', 'TRP_Browse_as_other_Role', 'TRP_Extra_Languages', 'TRP_Navigation_Based_on_Language', 'TRP_Seo_Pack', 'TRP_Translator_Accounts', ) ); foreach ( $addons as $className ) { if ( class_exists( $className ) ) { return true; } } return false; } /** * Execute do_shortcode with a specific list of tags * * @param $content string String to execute do_shortcode on * @param $tags_allowed array Array of tags allowed to be executed * @return string string Resulted string */ function trp_do_these_shortcodes( $content, $tags_allowed ){ global $shortcode_tags; $copy_shortcode_tags = $shortcode_tags; // select the allowed shortocde tags from the global array $allowed_shortcode_tags = array(); foreach( $shortcode_tags as $shortcode_tag_key => $shortcode_tag_value){ if ( in_array( $shortcode_tag_key, $tags_allowed ) ){ $allowed_shortcode_tags[$shortcode_tag_key] = $shortcode_tag_value; } } // only execute these shortcode tags on the content $shortcode_tags = $allowed_shortcode_tags; // run shortcode $return_content = do_shortcode($content); // revert changes to shortcode_tags array $shortcode_tags = $copy_shortcode_tags; return $return_content; } /** * Obtains a list of TP languages. Can be without the default one * in which case use the parameter nodefault set to 'nodefault' * * @param string $nodefault param used to return published languages without default one * @return mixed array with key/value pairs of published language codes and names * */ function trp_get_languages($nodefault=null) { $trp_obj = TRP_Translate_Press::get_trp_instance(); $settings_obj = $trp_obj->get_component('settings'); $lang_obj = $trp_obj->get_component('languages'); $default_lang_labels = $settings_obj->get_setting('default-language'); $published_lang = $settings_obj->get_setting('publish-languages'); $published_lang_labels = $lang_obj->get_language_names($published_lang); if (isset($nodefault) && $nodefault === 'nodefault'){ unset ($published_lang_labels[$default_lang_labels]); } return ($published_lang_labels); } /** * Wrapper function for wp_cache_get() that bypasses cache if TRP_DEBUG is on * @param int|string $key The key under which the cache contents are stored. * @param string $group Optional. Where the cache contents are grouped. Default empty. * @param bool $force Optional. Whether to force an update of the local cache * from the persistent cache. Default false. * @param bool $found Optional. Whether the key was found in the cache (passed by reference). * Disambiguates a return of false, a storable value. Default null. * @return mixed|false The cache contents on success, false on failure to retrieve contents or false when WP_DEBUG is on * */ function trp_cache_get( $key, $group = '', $force = false, &$found = null ){ if( defined( 'TRP_DEBUG' ) && TRP_DEBUG == true ) return false; $cache = wp_cache_get( $key, $group, $force, $found ); return $cache; } /** * Wrapper function for get_transient() that bypasses cache if TRP_DEBUG is on */ function trp_get_transient( $transient ){ if( ( defined( 'TRP_DEBUG' ) && TRP_DEBUG == true ) || defined( 'TRP_DEBUG_TRANSIENT' ) && TRP_DEBUG_TRANSIENT == true ) return false; return get_transient($transient); } /** * Determine if the setting in Advanced Options should make us add a slash at end of string * @param $settings the TranslatePress settings object * @return bool */ function trp_force_slash_at_end_of_link( $settings ){ if ( !empty( $settings['trp_advanced_settings'] ) && isset( $settings['trp_advanced_settings']['force_slash_at_end_of_links'] ) && $settings['trp_advanced_settings']['force_slash_at_end_of_links'] === 'yes' ) return true; else return false; } /** * This function is used by users to create their own language switcher. *It returns an array with all the necessary information for the user to create their own custom language switcher. * * @return array * * The array returned has the following indexes: language_name, language_code, short_language_name, flag_link, current_page_url */ function trp_custom_language_switcher() { $trp = TRP_Translate_Press::get_trp_instance(); $trp_languages = $trp->get_component( 'languages' ); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $languages_to_display = $settings['publish-languages']; $translation_languages = $trp_languages->get_language_names( $languages_to_display ); $url_converter = $trp->get_component( 'url_converter' ); $custom_ls_array = array(); foreach ( $translation_languages as $item => $language ) { $custom_ls_array[ $item ]['language_name'] = $language; $custom_ls_array[ $item ]['language_code'] = $item; $custom_ls_array[ $item ]['short_language_name'] = $url_converter->get_url_slug( $item, false ); $flags_path = TRP_PLUGIN_URL . 'assets/images/flags/'; $flags_path = apply_filters( 'trp_flags_path', $flags_path, $item ); $flag_file_name = $item . '.png'; $flag_file_name = apply_filters( 'trp_flag_file_name', $flag_file_name, $item ); $custom_ls_array[ $item ]['flag_link'] = esc_url( $flags_path . $flag_file_name ); $custom_ls_array[ $item ]['current_page_url'] = esc_url( $url_converter->get_url_for_language( $item, null, '' ) ); } return $custom_ls_array; } /**Function that provides translation for a specific text or html content, into another language. * The function can be used by third party plugin/theme authors. * * @param string $content is the content you want to translate, it must be in the default language and it can be any text or html code * @param string $language is the language you want to translate the content into, if it is left undefined the content will be translated * to the current language; it's set to current language by default * @param bool $prevent_over_translation is a parameter that prevents the translated content from being translated again during the translation * of the page. This can be set to false if the translated content is used in a way that TranslatePress can't detect the text. * It's set to true by default * @return string is the translated content in the chosen language */ function trp_translate( $content, $language = null, $prevent_over_translation = true ){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_render = $trp->get_component( 'translation_render' ); global $TRP_LANGUAGE; $lang_backup = $TRP_LANGUAGE; if ($language !== null){ $TRP_LANGUAGE = $language; } $translated_custom_content = $trp_render->translate_page($content); if ($prevent_over_translation === true){ $translated_custom_content = '<span data-no-translation>' . $translated_custom_content .'</span>'; } $TRP_LANGUAGE = $lang_backup; return $translated_custom_content; } /** * Function that returns the license status of the TranslatePress plugin. * @return string */ function trp_get_license_status(){ $license_details = get_option( 'trp_license_details' ); $is_demosite = ( strpos(site_url(), 'https://demo.translatepress.com' ) !== false ); $status = 'free-version'; if( !empty($license_details) && !$is_demosite) { /* if we have any invalid response for any of the addon show just the error notification and ignore any valid responses */ if ( !empty( $license_details['invalid'] ) ) { $status = 'invalid'; //take the first addon details (it should be the same for the rest of the invalid ones) $license_detail = $license_details['invalid'][0]; if( $license_detail->error == 'missing' ) $status = 'missing'; elseif( $license_detail->error == 'expired' ){ $status = 'expired'; }elseif( $license_detail->error == 'revoked' ){ $status = 'revoked'; } }elseif( !empty( $license_details['valid'] ) ){ $status = 'valid'; } } return $status; } /** * Used by third parties to briefly switch language such as when sending an email * To get a user's preferred language use this code: get_user_meta( $user_id, 'trp_language', true ); * * @param $language * @return void */ function trp_switch_language($language){ global $TRP_LANGUAGE, $TRP_LANGUAGE_COPY, $TRP_LANGUAGE_ORIGINAL; $language = trp_validate_language( $language ); $TRP_LANGUAGE_ORIGINAL = $TRP_LANGUAGE; $TRP_LANGUAGE = $language; $TRP_LANGUAGE_COPY = $language; // Because of 'trp_before_translate_content' filter function is_ajax_frontend() is called and it changes the global $TRP_LANGUAGE according to the url from which it was called. // Function trp_reset_language() is added on the hook in order to set global $TRP_LANGUAGE according to our need for the email language instead. add_filter( 'trp_before_translate_content', 'trp_reset_language', 99999999 ); switch_to_locale($language); add_filter( 'plugin_locale', 'trp_get_locale', 99999999); } /** * Switch to a user's preferred language based on the recipient email. * * For managerial users (admin-like roles), prefer user->locale with fallback * to WPLANG, then trp_language. * * For non-managerial users, prefer trp_language, with fallback to locale, then WPLANG. * * @param string $email * @return void */ function trp_switch_to_preffered_language( $email ) { $email = trim( (string) $email ); if ( $email === '' ) return; $user = get_user_by( 'email', $email ); if ( ! ( $user instanceof WP_User ) ) return; $user_roles = is_array( $user->roles ) ? $user->roles : array(); $trp_settings = TRP_Translate_Press::get_trp_instance()->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $default_language = $settings["default-language"]; /** * Roles considered "managerial" for email language purposes. * * @param string[] $roles */ $managerial_roles = apply_filters( 'trp_managerial_roles_for_email_language', [ 'administrator', 'editor', 'shop_manager' ] ); $is_managerial = !empty( array_intersect( $managerial_roles, $user_roles ) ); if ( $is_managerial ) { // Managerial: prefer locale, then WPLANG, then default_language if ( !empty( $user->locale ) ) { $language = $user->locale; } else { $wplang = get_option( 'WPLANG' ); $language = !empty( $wplang ) ? $wplang : $default_language; } } else { // Non-managerial: prefer trp_language. $language = get_user_meta( $user->ID, 'trp_language', true ); if ( empty( $language ) ) { if ( !empty( $user->locale ) ) { $language = $user->locale; } else { $wplang = get_option( 'WPLANG' ); $language = !empty( $wplang ) ? $wplang : $default_language; } } } if ( empty( $language ) ) return; trp_switch_language( $language ); } /** * Return $TRP_LANGUAGE as plugin locale * * @return mixed */ function trp_get_locale() { global $TRP_LANGUAGE; return $TRP_LANGUAGE; } /** * The value of $TRP_LANGUAGE is set according to the url, which can be problematic in some cases when sending emails * Restore the $TRP_LANGUAGE value in which email will be sent * * @param $output * @return mixed */ function trp_reset_language( $output ){ global $TRP_LANGUAGE, $TRP_LANGUAGE_COPY; $TRP_LANGUAGE = $TRP_LANGUAGE_COPY; return $output; } /** * Return a valid TRP language in which the email will be sent * * @param $language * @return mixed */ function trp_validate_language( $language ){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); if( empty( $language ) || !in_array( $language, $settings['translation-languages'] ) ){ $language = $settings['default-language']; } return $language; } /** * Used by third parties to restore original language after using trp_switch_language */ function trp_restore_language(){ global $TRP_LANGUAGE, $TRP_LANGUAGE_ORIGINAL; remove_filter( 'trp_before_translate_content', 'trp_reset_language' ); restore_previous_locale(); remove_filter( 'plugin_locale', 'trp_get_locale' ); $TRP_LANGUAGE = $TRP_LANGUAGE_ORIGINAL; } /** * Determine user language * * @param $user_id * @return mixed */ function trp_get_user_language( $user_id ){ return trp_validate_language( get_user_meta( $user_id, 'trp_language', true ) ); } /** * Wrapper function for WooCommerce HPOS add, delete and update operations * Falls back to the traditional post_meta operations * * @param $order_id int Post ID * @param $meta_key string Metadata key * @param $meta_value mixed Metadata value * @param $operation_type string Parameter used to determine the type of operation that needs to be performed. * Accepts: add / delete / update */ function trp_woo_hpos_manipulate_post_meta( $order_id, $meta_key, $meta_value, $operation_type ){ if ( class_exists( 'Automattic\WooCommerce\Utilities\OrderUtil' ) && Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ) { $order = wc_get_order( $order_id ); $function = $operation_type . '_meta_data'; $order->$function( $meta_key, $meta_value ); $order->save(); return; } $function = $operation_type . '_post_meta'; $function( $order_id, $meta_key, $meta_value ); } /** * Wrapper function for WooCommerce HPOS get operation * Falls back to the traditional post_meta operation * * @param $order_id int Post ID * @param $meta_key string Metadata key * @param $single bool Whether to return a single value or not. Default: false * @return mixed An array of values if `$single` is false. The value of the meta field if `$single` is true. False for an invalid `$post_id` (non-numeric, zero, or negative value). An empty string if a valid but non-existing post ID is passed. */ function trp_woo_hpos_get_post_meta( $order_id, $meta_key, $single = false ){ if ( class_exists( 'Automattic\WooCommerce\Utilities\OrderUtil' ) && Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ) { $order = wc_get_order( $order_id ); if ( !$order ) return false; return $order->get_meta( $meta_key, $single ); } return get_post_meta( $order_id, $meta_key, $single ); } /** * Helper function that determines if we should output the dynamic translation script later than usual * * Some plugins add HTML to the DOM very late in the page load cycle, so the site becomes slow due our mutation observer capturing it * * @return bool */ function is_late_dom_html_plugin_active(){ $classes_array = ['QueryMonitor']; // for the moment, only Query Monitor matches the criteria foreach ( $classes_array as $class ){ if ( class_exists( $class ) ) return true; } return apply_filters( 'trp_delay_dom_changes_script', false ); } /** * Helper function to remove a prefix from a string. * If we do str_replace that will remove it from the entire string, wherever it finds it. * * @return string */ function trp_remove_prefix($prefix = '', $string = '') { // Check if the path starts with the prefix if (!empty($prefix)){ if (strpos($string, $prefix) === 0) { // Remove the prefix from the path return substr_replace($string, '', 0, strlen($prefix)); } } // If the prefix is not at the start, return the path unchanged return $string; } /** * Obfuscate sensitive data in JSON response strings. * * @param string $string The JSON response string. * @return string The modified JSON response string with obfuscated sensitive data. */ function trp_obfuscate_sensitive_data_in_json_response( $string ) { $response_data = json_decode( $string, true ); if ( json_last_error() === JSON_ERROR_NONE ) { if ( isset( $response_data['customer_name'] ) ) { $response_data['customer_name'] = substr($response_data['customer_name'], 0, 2) . str_repeat('*', strlen($response_data['customer_name']) - 5) . substr($response_data['customer_name'], -3); } if ( isset( $response_data['customer_email'] ) ) { $response_data['customer_email'] = substr($response_data['customer_email'], 0, 2) . str_repeat('*', strlen($response_data['customer_email']) - 5) . substr($response_data['customer_email'], -3); } $string = json_encode( $response_data ); } return $string; } includes/external-functions.php 0000777 00000013110 15251156640 0012723 0 ustar 00 <?php /** * Trim strings. * * @param string $string Raw string. * * @param array $args Array of options eg. enable numerals translation * * @return string Trimmed string. */ /* NB: We don't always have access to WP get_option, for instance while calling trp_full_trim inside trp-ajax */ /* So this falls back to the option being transmitted either as a param from another function or obtained directly if get_option is available */ function trp_full_trim( $string, $args = array() ) { if((is_array($string)) || (is_object($string))){ return ""; } if ( !isset( $args['numerals']) ) { if ( function_exists( 'get_option' ) ) { $opt = get_option( 'trp_advanced_settings', false ); if ( isset( $opt["enable_numerals_translation"] ) ) { $args['numerals'] = $opt["enable_numerals_translation"]; } else { $args['numerals'] = "no"; } } else { $args['numerals'] = "no"; } } /* Apparently the � char in the trim function turns some strings in an empty string so they can't be translated but I don't really know if we should remove it completely Removed chr( 194 ) . chr( 160 ) because it altered some special characters (¿¡) Also removed \xA0 (the same as chr(160) for altering special characters */ //$word = trim($word," \t\n\r\0\x0B\xA0�".chr( 194 ) . chr( 160 ) ); /* Solution to replace the chr(194).chr(160) from trim function, in order to escape the whitespace character ( \xc2\xa0 ), an old bug that couldn't be replicated anymore. */ /* Trim nbsp the same way as the whitespace (chr194 chr160) above */ $prefixes = array( "\xc2\xa0", " " ); do{ $previous_iteration_string = $string; $string = trim($string, " \t\n\r\0\x0B"); foreach( $prefixes as $prefix ) { $prefix_length = strlen($prefix); if (substr($string, 0, $prefix_length) == $prefix) { $string = substr($string, $prefix_length); } if (substr($string, -$prefix_length, $prefix_length) == $prefix) { $string = substr($string, 0, -$prefix_length); } } }while( $string != $previous_iteration_string ); if ($args['numerals'] === "yes") { $filter_string = " \t\n\r\0\x0B\xA0�.,/`~!@#\$€£%^&*():;-_=+[]{}\\|?/<>'\""; } else { $filter_string = " \t\n\r\0\x0B\xA0�.,/`~!@#\$€£%^&*():;-_=+[]{}\\|?/<>1234567890'\""; } if ( strip_tags( $string ) === '' || trim( $string, $filter_string ) === '' ) { // Needs decoding otherwise some strings with special characters won't get detected. // Example in Hebrew: אוכל ותרבות // Placed inside the "if" to avoid calling html_entity_decode so often as this is a very used function $decoded_string = html_entity_decode( $string ); if ( trim( $decoded_string, $filter_string ) === '' ) { $string = ''; } } return $string; } function trp_sort_dictionary_by_original( $dictionaries, $type, $group, $languageForId ){ $array = array(); foreach( $dictionaries as $language => $dictionary ){ if ( isset( $dictionary['default-language'] ) && $dictionary['default-language'] == true ){ continue; } foreach( $dictionary as $string ) { $string = (object)$string; if ( isset( $string->original ) ){ $found = false; $string->editedTranslation = $string->translated; foreach( $array as $key => $row ){ if ( $row['original'] == $string->original ){ if ( !isset( $string->domain ) || ( isset($row['originalId']) && $row['originalId'] == $string->ot_id && $row['pluralForm'] == (int)$string->plural_form ) /*|| ( $string->plural_form == 0 && $string->domain == $row['description'] )*/ ) { $array[ $key ]['translationsArray'][ $language ] = $string; unset( $array[ $key ]['translationsArray'][ $language ]->original ); $found = true; if ( isset($string->domain) ){ $array[ $key ]['description'] = $string->domain; $array[ $key ]['domain'] = $string->domain; } if ( $language == $languageForId ){ $array[ $key ][ 'dbID' ] = $string->id; } break; } } } if ( ! $found ){ $new_entry = array( 'type' => $type, 'group' => $group, 'translationsArray' => array( $language => $string ), 'original' => $string->original ); unset($string->original); if ( isset( $string->domain ) ){ $new_entry['description'] = $string->domain; } if ( isset( $string->original_plural ) ){ $new_entry['originalPlural'] = $string->original_plural; } if ( isset( $string->context ) ){ $new_entry['context'] = $string->context; } if ( $type === 'gettext' ){ $new_entry['pluralForm'] = ( isset( $string->plural_form) ) ? $string->plural_form : 0; } if ( isset( $string->ot_id ) ){ $new_entry['originalId'] = $string->ot_id; } if ( $language == $languageForId ){ $new_entry['dbID'] = $string->id; } if ( isset( $new_entry['translationsArray'][$language]->block_type ) ){ $new_entry['blockType'] = $new_entry['translationsArray'][$language]->block_type; } $array[] = $new_entry; } } } } return $array; } function trp_is_valid_language_code( $language_code ){ // allowed characters A-Z a-z 0-9 - _ if ( empty($language_code) || preg_match('/[^A-Za-z0-9\-_]/i', $language_code ) ) { return false; }else{ return true; } } includes/class-elementor-language-for-blocks.php 0000777 00000025454 15251156640 0016026 0 ustar 00 <?php // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) exit; use Elementor\Controls_Manager; class TRP_Elementor { private static $_instance = null; public $locations = array( array( 'element' => 'common', 'action' => '_section_style', ), array( 'element' => 'section', 'action' => 'section_advanced', ), array( 'element' => 'container', 'action' => 'section_layout', ) ); public $section_name_show = 'trp_section_show'; public $section_name_exclude = 'trp_section_exclude'; /** * Register plugin action hooks and filters */ public function __construct() { // Register new section to display restriction controls $this->register_sections(); // Setup controls $this->register_controls(); // Filter widget content add_filter( 'elementor/widget/render_content', array( $this, 'widget_render' ), 10, 2 ); // Filter sections display & add custom messages add_action( 'elementor/frontend/section/should_render', array( $this, 'section_render' ), 10, 2 ); // Filter container display add_action( 'elementor/frontend/container/should_render', array( $this, 'section_render' ), 10, 2 ); // Add data-no-translation to elements that are restricted to a particular language add_action( 'elementor/element/after_add_attributes', array( $this, 'add_attributes' ) ); add_filter( 'trp_allow_language_redirect', array( $this, 'trp_elementor_compatibility' ) ); // Disable Element Cache when Language Restriction rules are setup for an element add_filter( 'elementor/element/is_dynamic_content', array( $this, 'are_language_restriction_rules_setup' ), 20, 3 ); } /** * * Ensures only one instance of the class is loaded or can be loaded. * * @return TRP_Elementor An instance of the class. */ public static function instance() { if ( is_null( self::$_instance ) ) self::$_instance = new self(); return self::$_instance; } private function register_sections() { foreach( $this->locations as $where ){ add_action( 'elementor/element/'.$where['element'].'/'.$where['action'].'/after_section_end', array( $this, 'add_section_show' ), 10, 2 ); add_action( 'elementor/element/'.$where['element'].'/'.$where['action'].'/after_section_end', array( $this, 'add_section_exclude' ), 10, 2 ); } } // Register controls to sections and widgets private function register_controls() { foreach( $this->locations as $where ){ add_action('elementor/element/'.$where['element'].'/'.$this->section_name_show.'/before_section_end', array( $this, 'add_controls_show' ), 10, 2 ); add_action('elementor/element/'.$where['element'].'/'.$this->section_name_exclude.'/before_section_end', array( $this, 'add_controls_exclude' ), 10, 2 ); } } public function add_section_show( $element, $args ) { $exists = \Elementor\Plugin::instance()->controls_manager->get_control_from_stack( $element->get_unique_name(), $this->section_name_show ); if( !is_wp_error( $exists ) ) return false; $element->start_controls_section( $this->section_name_show, array( 'tab' => Controls_Manager::TAB_ADVANCED, 'label' => __( 'Restrict by Language', 'translatepress-multilingual' ) ) ); $element->end_controls_section(); } public function add_section_exclude( $element, $args ) { $exists = \Elementor\Plugin::instance()->controls_manager->get_control_from_stack( $element->get_unique_name(), $this->section_name_exclude ); if( !is_wp_error( $exists ) ) return false; $element->start_controls_section( $this->section_name_exclude, array( 'tab' => Controls_Manager::TAB_ADVANCED, 'label' => __( 'Exclude from Language', 'translatepress-multilingual' ) ) ); $element->end_controls_section(); } // Define controls public function add_controls_show( $element, $args ) { $element_type = $element->get_type(); $element->add_control( 'trp_language_restriction', array( 'label' => __( 'Restrict element to language', 'translatepress-multilingual' ), 'type' => Controls_Manager::SWITCHER, 'description' => __( 'Show this element only in one language.', 'translatepress-multilingual' ), ) ); $element->add_control( 'trp_language_restriction_automatic_translation', array( 'label' => __( 'Enable translation', 'translatepress-multilingual' ), 'type' => Controls_Manager::SWITCHER, 'description' => __( 'Allow translation to the corresponding language only if the content is written in the default language.', 'translatepress-multilingual' ), ) ); $element->add_control( 'trp_language_restriction_heading', array( 'label' => __( 'Select language', 'translatepress-multilingual' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ) ); $trp = TRP_Translate_Press::get_trp_instance(); $trp_languages = $trp->get_component( 'languages' ); $trp_settings = $trp->get_component( 'settings' ); $published_languages = $trp_languages->get_language_names( $trp_settings->get_settings()['publish-languages'] ); $element->add_control( 'trp_restricted_languages', array( 'type' => Controls_Manager::SELECT2, 'options' => $published_languages, 'label_block' => 'true', 'description' => __( 'Choose in which language to show this element.', 'translatepress-multilingual' ), ) ); } public function add_controls_exclude( $element, $args ) { $element_type = $element->get_type(); $element->add_control( 'trp_exclude_handler', array( 'label' => __( 'Exclude element from language', 'translatepress-multilingual' ), 'type' => Controls_Manager::SWITCHER, 'description' => __( 'Exclude this element from specific languages.', 'translatepress-multilingual' ), ) ); $element->add_control( 'trp_excluded_heading', array( 'label' => __( 'Select languages', 'translatepress-multilingual' ), 'type' => Controls_Manager::HEADING, 'separator' => 'before', ) ); $trp = TRP_Translate_Press::get_trp_instance(); $trp_languages = $trp->get_component( 'languages' ); $trp_settings = $trp->get_component( 'settings' ); $published_languages = $trp_languages->get_language_names( $trp_settings->get_settings()['publish-languages'] ); $element->add_control( 'trp_excluded_languages', array( 'type' => Controls_Manager::SELECT2, 'options' => $published_languages, 'multiple' => 'true', 'label_block' => 'true', 'description' => __( 'Choose from which languages to exclude this element.', 'translatepress-multilingual' ), ) ); $message = '<p>' . __( 'This element will still be visible when you are translating your website through the Translation Editor.', 'translatepress-multilingual' ) . '</p>'; $message .= '<p>' . __( 'The content of this element should be written in the default language.', 'translatepress-multilingual' ) . '</p>'; $element->add_control( 'trp_excluded_message', array( 'type' => Controls_Manager::RAW_HTML, 'raw' => $message, ) ); } // Verifies if element is hidden public function is_hidden( $element ) { $settings = $element->get_settings(); if( isset( $settings['trp_language_restriction'] ) && $settings['trp_language_restriction'] == 'yes' && !empty( $settings['trp_restricted_languages'] ) ){ $current_language = get_locale(); if( $current_language != $settings['trp_restricted_languages'] ) return true; } if( !isset( $_GET['trp-edit-translation'] ) && isset( $settings['trp_exclude_handler'] ) && $settings['trp_exclude_handler'] == 'yes' && !empty( $settings['trp_excluded_languages'] ) ){ $current_language = get_locale(); if( in_array( $current_language, $settings['trp_excluded_languages'] ) ) return true; } return false; } // Widget display & custom messages public function widget_render( $content, $widget ) { if( $this->is_hidden( $widget ) ){ if( \Elementor\Plugin::$instance->editor->is_edit_mode() ) return $content; return '<style>' . $widget->get_unique_selector() . '{display:none !important}</style>'; } return $content; } // Section display public function section_render( $should_render, $element ) { if( $this->is_hidden( $element ) === true ) return false; return $should_render; } public function add_attributes( $element ){ $settings = $element->get_settings(); if( isset( $settings['trp_language_restriction'] ) && $settings['trp_language_restriction'] == 'yes' && !empty( $settings['trp_restricted_languages'] ) && isset( $settings['trp_language_restriction_automatic_translation'] ) && $settings['trp_language_restriction_automatic_translation'] != 'yes') $element->add_render_attribute( '_wrapper', 'data-no-translation' ); } /** * Do not redirect when elementor preview is present * * @param $allow_redirect * * @return bool */ public function trp_elementor_compatibility( $allow_redirect ){ // compatibility with Elementor preview. Do not redirect to subdir language when elementor preview is present. if ( isset( $_GET['elementor-preview'] ) ) return false; return $allow_redirect; } public function are_language_restriction_rules_setup( $is_dynamic_content, $data, $element ){ if( empty( $data['settings'] ) ) return $is_dynamic_content; if( isset( $data['settings']['trp_language_restriction'] ) && $data['settings']['trp_language_restriction'] == 'yes' ) return true; if( isset( $data['settings']['trp_exclude_handler'] ) && $data['settings']['trp_exclude_handler'] == 'yes' ) return true; if( isset( $data['settings']['trp_restricted_languages'] ) && !empty( $data['settings']['trp_restricted_languages'] ) ) return true; if( isset( $data['settings']['trp_excluded_languages'] ) && !empty( $data['settings']['trp_excluded_languages'] ) ) return true; return $is_dynamic_content; } } // Instantiate Plugin Class TRP_Elementor::instance(); includes/class-translation-memory.php 0000777 00000010400 15251156640 0014041 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Translation_Memory { protected $db; protected $settings; /* @var TRP_Query */ protected $trp_query; const MIN_NUMBER_OF_CHARS_FOR_FULLTEXT = 20; /** * TRP_Translation_Memory constructor. * @param $settings */ public function __construct( $settings ){ global $wpdb; $this->db = $wpdb; $this->settings = $settings; } /** * Finding similar strings in the database and returning an array with possible translations. * * * @param string $string The original string we're searching a similar one. * @param string $table_name The table where we should look for similar strings in. Default dictionary. * @param int $number The number of similar strings we want to return. * @return array Array with (original => translated ) pairs based on the number of strings we should account for. Empty array if nothing is found. */ public function get_similar_string_translation( $string, $number, $table_name ){ if( empty($table_name) ){ return array(); } $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } $query = ''; $query .= "SELECT original,translated, status FROM `" . sanitize_text_field( $table_name ) . "` WHERE status != " . TRP_Query::NOT_TRANSLATED . " AND `original` != '%s' AND MATCH(original) AGAINST ('%s' IN NATURAL LANGUAGE MODE ) LIMIT " . $number; $query = $this->db->prepare( $query, array($string, $string) ); $result = $this->db->get_results( $query, ARRAY_A ); return $result; } /** * Ajax Callback for getting similar translations for strings. * * @return string Json Array with (original => translated ) pairs based on the number of strings we should account for. Empty json array if nothing is found. */ public function ajax_get_similar_string_translation(){ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { if (isset($_POST['action']) && $_POST['action'] === 'trp_get_similar_string_translation' && !empty($_POST['original_string']) && !empty($_POST['language']) && !empty($_POST['type']) && in_array($_POST['language'], $this->settings['translation-languages']) ) { if ( ! current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { wp_die( -1, 403 ); } global $TRP_LANGUAGE; check_ajax_referer('getsimilarstring', 'security'); $string = ( isset($_POST['original_string']) ) ? $_POST['original_string'] : '';//phpcs:ignore $language_code = ( isset($_POST['language']) ) ? sanitize_text_field( $_POST['language'] ) : $TRP_LANGUAGE; $type = ( isset($_POST['type']) ) ? sanitize_text_field( $_POST['type'] ) : ''; $number = ( isset($_POST['number']) ) ? (int) $_POST['number'] : 3; $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } $table_name = null; // there is no dictionary table with the default language if ( $language_code !== $this->settings['default-language'] ) { // data-trp-translate-id, data-trp-translate-id-innertext are in the wp_trp_dictionary_* tables $table_name = $this->trp_query->get_table_name( $language_code ); } if( $type == "gettext" ){ $table_name = $this->trp_query->get_gettext_table_name( $language_code ); } if ( $table_name === null ) { $dictionary = array(); }else{ $dictionary = $this->get_similar_string_translation( $string, $number, $table_name ); } echo json_encode($dictionary); wp_die(); } } echo json_encode(array()); wp_die(); } } includes/class-preferred-user-language.php 0000777 00000011277 15251156640 0014725 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Preferred_User_Language{ protected $settings; /** @var TRP_Languages */ protected $trp_languages; /** @var TRP_Translate_Press */ protected $trp; public function __construct(){} public function get_published_languages(){ if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } if ( ! $this->settings ){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $this->settings = $trp_settings->get_settings(); } $languages_to_display = $this->settings['publish-languages']; $published_languages = $this->trp_languages->get_language_names( $languages_to_display ); return $published_languages; } public function always_use_this_language($user){ global $TRP_LANGUAGE; $published_languages = $this->get_published_languages(); $user_ID = 0; $user_ID = $user->ID; $language = $TRP_LANGUAGE; if ($user_ID > 0) { $language = get_user_meta( $user_ID, 'trp_language', true ); } if(empty($language) || ! array_key_exists($language, $published_languages) ){ $language = $this->settings['default-language']; } $last_visited_language = $published_languages[$language]; $always_use_this_language = get_user_meta( $user_ID, 'trp_always_use_this_language', true ); ?> <h3><?php esc_html_e( 'TranslatePress Preferred User Language', 'translatepress-multilingual' ); ?></h3> <table class="form-table"> <tr> <th><label for="preferred_language"><?php esc_html_e( 'Preferred language to navigate the site', 'translatepress-multilingual' ); ?></label></th> <td> <select style="width: 350px" name="trp_selected_language"> <option value="<?php echo esc_attr( $language ); ?>"><?php echo esc_html($last_visited_language); ?></option> <?php foreach ($published_languages as $language_code => $language_name){ if ($language_code != $language){ ?> <option title="<?php echo esc_attr( $language_code ); ?>" value="<?php echo esc_attr( $language_code ); ?>"> <?php echo esc_html( $language_name ); ?> </option> <?php } } ?> </select> <p class="description"> <?php echo wp_kses_post( __( "The language is automatically set based by the last visited language by the user." , 'translatepress-multilingual' ) ); ?> </p> </td> </tr> </table> <table class="form-table"> <tr> <th></th> <td> <label><input type="checkbox" id="always_use_this_language_checkbox" name="trp_always_use_this_language_checkbox" value="yes" <?php if (!empty($always_use_this_language) && $always_use_this_language == 'yes'){ ?> checked <?php } ?>> <strong><?php esc_html_e( 'Always use this language', 'translatepress-multilingual' ); ?></strong> </input></label> <p class="description"> <?php echo wp_kses_post( __( "By checking this setting the preferred language will remain the one selected above, without the possibility of being changed in the frontend.<br>This language will be used in different operations such as sending email to the user." , 'translatepress-multilingual' ) ); ?> </p> </td> </tr> </table> <?php } public function update_profile_fields($user_id) { if ( ! current_user_can( 'edit_user', $user_id ) ) { return false; } $published_languages = $this->get_published_languages(); if(isset($_POST['trp_selected_language']) && in_array($_POST['trp_selected_language'], $this->settings['publish-languages']) && trp_is_valid_language_code($_POST['trp_selected_language'] ) ) { /* phpcs:ignore */ update_user_meta( $user_id, 'trp_language', $_POST['trp_selected_language'] ); /* phpcs:ignore */ /* the variable was checked in the if statement */ }else{ update_user_meta( $user_id, 'trp_language', $this->settings['default-language'] ); } if(isset($_POST['trp_always_use_this_language_checkbox']) && $_POST['trp_always_use_this_language_checkbox'] == 'yes') { update_user_meta( $user_id, 'trp_always_use_this_language', "yes" ); }else{ update_user_meta( $user_id, 'trp_always_use_this_language', "no" ); } } } includes/class-languages.php 0000777 00000134031 15251156640 0012152 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Languages * * Provides available languages, with name and code. */ class TRP_Languages{ protected $languages = array(); protected $wp_languages; protected $wp_languages_backup = array(); protected $settings; protected $is_admin_request; /** * Returns array of all possible languages. * * @param string $english_or_native_name 'english_name' | 'native_name' * @return array Returns associative array with language code as key and language name as value. */ public function get_languages( $english_or_native_name = 'english_name' ){ if ( empty( $this->languages[$english_or_native_name] ) ) { $wp_languages = $this->get_wp_languages(); foreach ( $wp_languages as $wp_language ) { $this->languages[$english_or_native_name][$wp_language['language']] = $wp_language[$english_or_native_name]; } } return apply_filters( 'trp_languages', $this->languages[$english_or_native_name], $english_or_native_name ); } /** Set proper locale when changing languages with translatepress * * @param $locale * @return mixed */ public function change_locale( $locale ){ if ( $this->is_admin_request === null ){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_is_admin_request = $trp->get_component( 'url_converter' ); $this->is_admin_request= $trp_is_admin_request->is_admin_request(); } if ( $this->is_admin_request ){ return $locale; } global $TRP_LANGUAGE; if( !empty($TRP_LANGUAGE) ){ $locale = $TRP_LANGUAGE; } return $locale; } /** * Returns all languages information provided by WP. * * @uses wp_get_available_translations() * * @return array WP languages information. */ public function get_wp_languages(){ if ( empty( $this->wp_languages ) ){ require_once( ABSPATH . 'wp-admin/includes/translation-install.php' ); $this->wp_languages = wp_get_available_translations(); if ( count( $this->wp_languages ) == 0 ) { $this->wp_languages = $this->get_wp_languages_backup(); } } $default = array( 'en_US' => array( 'language' => 'en_US', 'english_name'=> 'English (United States)', 'native_name' => 'English', 'iso' => array( 'en' ) ) ); return apply_filters( 'trp_wp_languages', $default + $this->wp_languages ); } /** * Returns iso language codes for provided array. * * Iso codes are short language codes with no localization. Not to be confused with full language codes. * Iso codes are not unique. * * @param array $language_codes String array of language codes for which to return iso codes. * @param bool $map_google_codes Whether to return Google API compatible codes. * @return array String array of iso codes. */ public function get_iso_codes( $language_codes, $map_google_codes = true ){ if ( !in_array( 'en_US', $language_codes ) ){ $language_codes[] = 'en_US'; } $iso_codes = array(); $wp_languages = $this->get_wp_languages(); $map_wp_codes_to_google = apply_filters( 'trp_map_wp_codes_to_google', array( 'zh_HK' => 'zh-TW', 'zh_TW' => 'zh-TW', 'zh_CN' => 'zh-CN', 'nb_NO' => 'no' ) ); foreach ( $language_codes as $language_code ) { if ( $map_google_codes && isset( $map_wp_codes_to_google[$language_code] ) ){ $iso_codes[$language_code] = $map_wp_codes_to_google[$language_code]; }else { foreach ($wp_languages as $wp_language) { if ($wp_language['language'] == $language_code) { $iso_codes[$language_code] = reset($wp_language['iso']); break; } } } } return $iso_codes; } /** * Return an array of all language codes. * * @return array Array of language codes. */ public function get_all_language_codes(){ return array_keys ( $this->get_languages() ); } /** * Returns array of full language names for the provided array of language codes. * * English_or_native_name parameter set to null means to obey the admin settings. * * @param array $language_codes String array of language codes. * @param string $english_or_native_name 'english_name' | 'native_name' | null * @return array Associative array with language code as key and full language name as value */ public function get_language_names( $language_codes, $english_or_native_name = null ){ if ( !$english_or_native_name ){ if ( !$this->settings ){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $this->settings = $trp_settings->get_settings(); } $english_or_native_name = $this->settings['native_or_english_name']; } $return = array(); $languages = $this->get_languages( $english_or_native_name ); foreach ( $language_codes as $language_code ){ if( isset( $languages[$language_code] ) ) { $return[$language_code] = apply_filters( 'trp_language_name', $languages[$language_code], $language_code, $english_or_native_name, $language_codes ); } } return $return; } /** * Returns substring of the string from the beginning until the occurrence of character * * If character not found do nothing. * * @param $string string String to trim * @param $character string Delimitator string * * @return string */ public function string_trim_after_character( $string, $character ){ if ( strpos( $string, $character ) !== false ) { $string = substr($string, 0, strpos($string, $character )); } return $string; } /** * Return true if the language (without country) of the language_code is present multiple times in the array * * (ex. For language code en_UK, language_code_array [en_US, en_UK], return true) * * @param $language_code string Language code (ex. en_US) * @param $language_code_array array Array of language codes * * @return bool */ public function duplicated_language( $language_code, $language_code_array ){ // strip country from code ( ex. en_US => en ) $stripped_language_code = $this->string_trim_after_character( $language_code, "_" ); foreach ( $language_code_array as $key => $value ){ $stripped_value = $this->string_trim_after_character( $value, "_" ); if ( $language_code != $value && $stripped_language_code == $stripped_value ){ return true; } } return false; } /** * Return the short language name for English name. * * @param string $name Original language name. * @param string $code Language code. * @param string $english_or_native 'english_name' | 'native_name' * @return string Short language name. */ public function beautify_language_name( $name, $code, $english_or_native, $language_codes ){ $wp_lang = $this->get_wp_languages(); if ( $english_or_native == 'english_name' ) { if ( ! $this->duplicated_language( $code, $language_codes) && (!isset($wp_lang[$code]['is_custom_language']) || (isset($wp_lang[$code]['is_custom_language']) && $wp_lang[$code]['is_custom_language'] !== true))){ $name = $this->string_trim_after_character( $name, " (" ); } } return apply_filters( 'trp_beautify_language_name', $name, $code, $english_or_native, $language_codes ); } /** * Return language arrays with English languages first * * @param $languages_array array Languages array * @param $english_or_native_name string 'english_name' | 'native_name' * * @return array */ public function reorder_languages( $languages_array, $english_or_native_name ){ $english_array = array(); // Remove English (United States) language before sorting $keyToMoveFirst = 'en_US'; if ( isset( $languages_array[ $keyToMoveFirst ] ) ) { $english_united_states = $languages_array[ $keyToMoveFirst ]; unset( $languages_array[ $keyToMoveFirst ] ); } // Remove English (United Kingdom) language before sorting $keyToMoveSecond = 'en_GB'; if ( isset( $languages_array[ $keyToMoveSecond ] ) ) { $english_united_kingdom = $languages_array[ $keyToMoveSecond ]; unset( $languages_array[ $keyToMoveSecond ] ); } // Sort languages by name asort($languages_array); foreach( $languages_array as $key => $value ){ if ( $this->string_trim_after_character( $key, '_' ) == 'en' ){ $english_array[$key] = $value; unset( $languages_array[$key] ); } } // Add English languages back $languages_array = $english_array + $languages_array; // Move English (United Kingdom) language to the second position of the array if ( isset( $english_united_kingdom ) ) { $languages_array = array( $keyToMoveSecond => $english_united_kingdom ) + $languages_array; } // Move English (United States) language to the first position of the array if ( isset( $english_united_states ) ) { $languages_array = array( $keyToMoveFirst => $english_united_states ) + $languages_array; } return $languages_array; } /** * Return back-up array with full language name information. * * Used in case the connection with WP fails via wp_get_available_translations() call. * * @return array Array with full language information. */ public function get_wp_languages_backup(){ $string = '{"translations":[{"language":"af","version":"4.8","updated":"2017-06-23 21:35:47","english_name":"Afrikaans","native_name":"Afrikaans","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/af.zip","iso":{"1":"af","2":"afr"},"strings":{"continue":"Gaan voort"}},{"language":"ar","version":"4.8","updated":"2017-07-09 03:55:46","english_name":"Arabic","native_name":"\u0627\u0644\u0639\u0631\u0628\u064a\u0629","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ar.zip","iso":{"1":"ar","2":"ara"},"strings":{"continue":"\u0627\u0644\u0645\u062a\u0627\u0628\u0639\u0629"}},{"language":"ary","version":"4.7.5","updated":"2017-01-26 15:42:35","english_name":"Moroccan Arabic","native_name":"\u0627\u0644\u0639\u0631\u0628\u064a\u0629 \u0627\u0644\u0645\u063a\u0631\u0628\u064a\u0629","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/ary.zip","iso":{"1":"ar","3":"ary"},"strings":{"continue":"\u0627\u0644\u0645\u062a\u0627\u0628\u0639\u0629"}},{"language":"as","version":"4.7.2","updated":"2016-11-22 18:59:07","english_name":"Assamese","native_name":"\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/as.zip","iso":{"1":"as","2":"asm","3":"asm"},"strings":{"continue":""}},{"language":"azb","version":"4.7.2","updated":"2016-09-12 20:34:31","english_name":"South Azerbaijani","native_name":"\u06af\u0624\u0646\u0626\u06cc \u0622\u0630\u0631\u0628\u0627\u06cc\u062c\u0627\u0646","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/azb.zip","iso":{"1":"az","3":"azb"},"strings":{"continue":"Continue"}},{"language":"az","version":"4.7.2","updated":"2016-11-06 00:09:27","english_name":"Azerbaijani","native_name":"Az\u0259rbaycan dili","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/az.zip","iso":{"1":"az","2":"aze"},"strings":{"continue":"Davam"}},{"language":"bel","version":"4.8","updated":"2017-06-17 20:31:44","english_name":"Belarusian","native_name":"\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f \u043c\u043e\u0432\u0430","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/bel.zip","iso":{"1":"be","2":"bel"},"strings":{"continue":"\u041f\u0440\u0430\u0446\u044f\u0433\u043d\u0443\u0446\u044c"}},{"language":"bg_BG","version":"4.8","updated":"2017-06-18 19:16:01","english_name":"Bulgarian","native_name":"\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/bg_BG.zip","iso":{"1":"bg","2":"bul"},"strings":{"continue":"\u041d\u0430\u043f\u0440\u0435\u0434"}},{"language":"bn_BD","version":"4.7.2","updated":"2017-01-04 16:58:43","english_name":"Bengali","native_name":"\u09ac\u09be\u0982\u09b2\u09be","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/bn_BD.zip","iso":{"1":"bn"},"strings":{"continue":"\u098f\u0997\u09bf\u09df\u09c7 \u099a\u09b2."}},{"language":"bo","version":"4.7.2","updated":"2016-09-05 09:44:12","english_name":"Tibetan","native_name":"\u0f56\u0f7c\u0f51\u0f0b\u0f61\u0f72\u0f42","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/bo.zip","iso":{"1":"bo","2":"tib"},"strings":{"continue":"\u0f58\u0f74\u0f0b\u0f58\u0f50\u0f74\u0f51\u0f0d"}},{"language":"bs_BA","version":"4.7.2","updated":"2016-09-04 20:20:28","english_name":"Bosnian","native_name":"Bosanski","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/bs_BA.zip","iso":{"1":"bs","2":"bos"},"strings":{"continue":"Nastavi"}},{"language":"ca","version":"4.8","updated":"2017-06-16 11:47:56","english_name":"Catalan","native_name":"Catal\u00e0","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ca.zip","iso":{"1":"ca","2":"cat"},"strings":{"continue":"Continua"}},{"language":"ceb","version":"4.7.2","updated":"2016-03-02 17:25:51","english_name":"Cebuano","native_name":"Cebuano","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/ceb.zip","iso":{"2":"ceb","3":"ceb"},"strings":{"continue":"Padayun"}},{"language":"cs_CZ","version":"4.7.2","updated":"2017-01-12 08:46:26","english_name":"Czech","native_name":"\u010ce\u0161tina\u200e","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/cs_CZ.zip","iso":{"1":"cs","2":"ces"},"strings":{"continue":"Pokra\u010dovat"}},{"language":"cy","version":"4.8","updated":"2017-06-14 13:21:24","english_name":"Welsh","native_name":"Cymraeg","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/cy.zip","iso":{"1":"cy","2":"cym"},"strings":{"continue":"Parhau"}},{"language":"da_DK","version":"4.8","updated":"2017-06-14 23:24:44","english_name":"Danish","native_name":"Dansk","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/da_DK.zip","iso":{"1":"da","2":"dan"},"strings":{"continue":"Forts\u00e6t"}},{"language":"de_CH","version":"4.8","updated":"2017-06-15 21:25:12","english_name":"German (Switzerland)","native_name":"Deutsch (Schweiz)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/de_CH.zip","iso":{"1":"de"},"strings":{"continue":"Weiter"}},{"language":"de_DE_formal","version":"4.8","updated":"2017-07-04 12:57:09","english_name":"German (Formal)","native_name":"Deutsch (Sie)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/de_DE_formal.zip","iso":{"1":"de"},"strings":{"continue":"Weiter"}},{"language":"de_CH_informal","version":"4.8","updated":"2017-06-15 08:50:23","english_name":"German (Switzerland, Informal)","native_name":"Deutsch (Schweiz, Du)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/de_CH_informal.zip","iso":{"1":"de"},"strings":{"continue":"Weiter"}},{"language":"de_DE","version":"4.8","updated":"2017-07-08 16:08:42","english_name":"German","native_name":"Deutsch","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/de_DE.zip","iso":{"1":"de"},"strings":{"continue":"Weiter"}},{"language":"dzo","version":"4.7.2","updated":"2016-06-29 08:59:03","english_name":"Dzongkha","native_name":"\u0f62\u0fab\u0f7c\u0f44\u0f0b\u0f41","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/dzo.zip","iso":{"1":"dz","2":"dzo"},"strings":{"continue":""}},{"language":"el","version":"4.8","updated":"2017-06-21 18:05:57","english_name":"Greek","native_name":"\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/el.zip","iso":{"1":"el","2":"ell"},"strings":{"continue":"\u03a3\u03c5\u03bd\u03ad\u03c7\u03b5\u03b9\u03b1"}},{"language":"en_NZ","version":"4.8","updated":"2017-06-17 08:09:19","english_name":"English (New Zealand)","native_name":"English (New Zealand)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/en_NZ.zip","iso":{"1":"en","2":"eng","3":"eng"},"strings":{"continue":"Continue"}},{"language":"en_ZA","version":"4.7.5","updated":"2017-01-26 15:53:43","english_name":"English (South Africa)","native_name":"English (South Africa)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/en_ZA.zip","iso":{"1":"en","2":"eng","3":"eng"},"strings":{"continue":"Continue"}},{"language":"en_GB","version":"4.8","updated":"2017-06-15 07:18:00","english_name":"English (UK)","native_name":"English (UK)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/en_GB.zip","iso":{"1":"en","2":"eng","3":"eng"},"strings":{"continue":"Continue"}},{"language":"en_AU","version":"4.8","updated":"2017-06-15 05:14:35","english_name":"English (Australia)","native_name":"English (Australia)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/en_AU.zip","iso":{"1":"en","2":"eng","3":"eng"},"strings":{"continue":"Continue"}},{"language":"en_CA","version":"4.8","updated":"2017-06-23 16:48:27","english_name":"English (Canada)","native_name":"English (Canada)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/en_CA.zip","iso":{"1":"en","2":"eng","3":"eng"},"strings":{"continue":"Continue"}},{"language":"eo","version":"4.8","updated":"2017-06-27 10:36:23","english_name":"Esperanto","native_name":"Esperanto","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/eo.zip","iso":{"1":"eo","2":"epo"},"strings":{"continue":"Da\u016drigi"}},{"language":"es_AR","version":"4.8","updated":"2017-06-20 00:55:30","english_name":"Spanish (Argentina)","native_name":"Espa\u00f1ol de Argentina","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/es_AR.zip","iso":{"1":"es","2":"spa"},"strings":{"continue":"Continuar"}},{"language":"es_MX","version":"4.8","updated":"2017-06-16 17:22:41","english_name":"Spanish (Mexico)","native_name":"Espa\u00f1ol de M\u00e9xico","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/es_MX.zip","iso":{"1":"es","2":"spa"},"strings":{"continue":"Continuar"}},{"language":"es_ES","version":"4.8","updated":"2017-07-02 08:44:01","english_name":"Spanish (Spain)","native_name":"Espa\u00f1ol","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/es_ES.zip","iso":{"1":"es"},"strings":{"continue":"Continuar"}},{"language":"es_CO","version":"4.7.5","updated":"2017-01-26 15:54:37","english_name":"Spanish (Colombia)","native_name":"Espa\u00f1ol de Colombia","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/es_CO.zip","iso":{"1":"es","2":"spa"},"strings":{"continue":"Continuar"}},{"language":"es_GT","version":"4.7.5","updated":"2017-01-26 15:54:37","english_name":"Spanish (Guatemala)","native_name":"Espa\u00f1ol de Guatemala","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/es_GT.zip","iso":{"1":"es","2":"spa"},"strings":{"continue":"Continuar"}},{"language":"es_CL","version":"4.7.2","updated":"2016-11-28 20:09:49","english_name":"Spanish (Chile)","native_name":"Espa\u00f1ol de Chile","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/es_CL.zip","iso":{"1":"es","2":"spa"},"strings":{"continue":"Continuar"}},{"language":"es_PE","version":"4.7.2","updated":"2016-09-09 09:36:22","english_name":"Spanish (Peru)","native_name":"Espa\u00f1ol de Per\u00fa","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/es_PE.zip","iso":{"1":"es","2":"spa"},"strings":{"continue":"Continuar"}},{"language":"es_VE","version":"4.8","updated":"2017-07-07 00:53:01","english_name":"Spanish (Venezuela)","native_name":"Espa\u00f1ol de Venezuela","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/es_VE.zip","iso":{"1":"es","2":"spa"},"strings":{"continue":"Continuar"}},{"language":"et","version":"4.7.2","updated":"2017-01-27 16:37:11","english_name":"Estonian","native_name":"Eesti","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/et.zip","iso":{"1":"et","2":"est"},"strings":{"continue":"J\u00e4tka"}},{"language":"eu","version":"4.8","updated":"2017-06-21 08:00:44","english_name":"Basque","native_name":"Euskara","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/eu.zip","iso":{"1":"eu","2":"eus"},"strings":{"continue":"Jarraitu"}},{"language":"fa_IR","version":"4.8","updated":"2017-06-09 15:50:45","english_name":"Persian","native_name":"\u0641\u0627\u0631\u0633\u06cc","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/fa_IR.zip","iso":{"1":"fa","2":"fas"},"strings":{"continue":"\u0627\u062f\u0627\u0645\u0647"}},{"language":"fi","version":"4.8","updated":"2017-06-08 18:25:22","english_name":"Finnish","native_name":"Suomi","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/fi.zip","iso":{"1":"fi","2":"fin"},"strings":{"continue":"Jatka"}},{"language":"fr_BE","version":"4.8","updated":"2017-06-23 06:47:57","english_name":"French (Belgium)","native_name":"Fran\u00e7ais de Belgique","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/fr_BE.zip","iso":{"1":"fr","2":"fra"},"strings":{"continue":"Continuer"}},{"language":"fr_CA","version":"4.8","updated":"2017-07-05 17:58:06","english_name":"French (Canada)","native_name":"Fran\u00e7ais du Canada","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/fr_CA.zip","iso":{"1":"fr","2":"fra"},"strings":{"continue":"Continuer"}},{"language":"fr_FR","version":"4.8","updated":"2017-07-07 13:48:37","english_name":"French (France)","native_name":"Fran\u00e7ais","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/fr_FR.zip","iso":{"1":"fr"},"strings":{"continue":"Continuer"}},{"language":"gd","version":"4.7.2","updated":"2016-08-23 17:41:37","english_name":"Scottish Gaelic","native_name":"G\u00e0idhlig","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/gd.zip","iso":{"1":"gd","2":"gla","3":"gla"},"strings":{"continue":"Lean air adhart"}},{"language":"gl_ES","version":"4.8","updated":"2017-06-17 20:40:15","english_name":"Galician","native_name":"Galego","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/gl_ES.zip","iso":{"1":"gl","2":"glg"},"strings":{"continue":"Continuar"}},{"language":"gu","version":"4.8","updated":"2017-06-07 12:07:46","english_name":"Gujarati","native_name":"\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/gu.zip","iso":{"1":"gu","2":"guj"},"strings":{"continue":"\u0a9a\u0abe\u0ab2\u0ac1 \u0ab0\u0abe\u0a96\u0ab5\u0ac1\u0a82"}},{"language":"haz","version":"4.4.2","updated":"2015-12-05 00:59:09","english_name":"Hazaragi","native_name":"\u0647\u0632\u0627\u0631\u0647 \u06af\u06cc","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.4.2\/haz.zip","iso":{"3":"haz"},"strings":{"continue":"\u0627\u062f\u0627\u0645\u0647"}},{"language":"he_IL","version":"4.8","updated":"2017-06-15 13:33:29","english_name":"Hebrew","native_name":"\u05e2\u05b4\u05d1\u05b0\u05e8\u05b4\u05d9\u05ea","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/he_IL.zip","iso":{"1":"he"},"strings":{"continue":"\u05d4\u05de\u05e9\u05da"}},{"language":"hi_IN","version":"4.8","updated":"2017-06-17 08:25:42","english_name":"Hindi","native_name":"\u0939\u093f\u0928\u094d\u0926\u0940","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/hi_IN.zip","iso":{"1":"hi","2":"hin"},"strings":{"continue":"\u091c\u093e\u0930\u0940"}},{"language":"hr","version":"4.8","updated":"2017-07-02 07:13:09","english_name":"Croatian","native_name":"Hrvatski","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/hr.zip","iso":{"1":"hr","2":"hrv"},"strings":{"continue":"Nastavi"}},{"language":"hu_HU","version":"4.7.2","updated":"2017-01-26 15:48:39","english_name":"Hungarian","native_name":"Magyar","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/hu_HU.zip","iso":{"1":"hu","2":"hun"},"strings":{"continue":"Folytat\u00e1s"}},{"language":"hy","version":"4.7.2","updated":"2016-12-03 16:21:10","english_name":"Armenian","native_name":"\u0540\u0561\u0575\u0565\u0580\u0565\u0576","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/hy.zip","iso":{"1":"hy","2":"hye"},"strings":{"continue":"\u0547\u0561\u0580\u0578\u0582\u0576\u0561\u056f\u0565\u056c"}},{"language":"id_ID","version":"4.8","updated":"2017-06-08 21:11:01","english_name":"Indonesian","native_name":"Bahasa Indonesia","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/id_ID.zip","iso":{"1":"id","2":"ind"},"strings":{"continue":"Lanjutkan"}},{"language":"is_IS","version":"4.7.5","updated":"2017-04-13 13:55:54","english_name":"Icelandic","native_name":"\u00cdslenska","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/is_IS.zip","iso":{"1":"is","2":"isl"},"strings":{"continue":"\u00c1fram"}},{"language":"it_IT","version":"4.8","updated":"2017-07-04 13:01:37","english_name":"Italian","native_name":"Italiano","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/it_IT.zip","iso":{"1":"it","2":"ita"},"strings":{"continue":"Continua"}},{"language":"ja","version":"4.8","updated":"2017-06-25 11:16:15","english_name":"Japanese","native_name":"\u65e5\u672c\u8a9e","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ja.zip","iso":{"1":"ja"},"strings":{"continue":"\u7d9a\u3051\u308b"}},{"language":"ka_GE","version":"4.8","updated":"2017-06-12 09:20:11","english_name":"Georgian","native_name":"\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ka_GE.zip","iso":{"1":"ka","2":"kat"},"strings":{"continue":"\u10d2\u10d0\u10d2\u10e0\u10eb\u10d4\u10da\u10d4\u10d1\u10d0"}},{"language":"kab","version":"4.8","updated":"2017-07-03 15:14:56","english_name":"Kabyle","native_name":"Taqbaylit","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/kab.zip","iso":{"2":"kab","3":"kab"},"strings":{"continue":"Kemmel"}},{"language":"km","version":"4.7.2","updated":"2016-12-07 02:07:59","english_name":"Khmer","native_name":"\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/km.zip","iso":{"1":"km","2":"khm"},"strings":{"continue":"\u1794\u1793\u17d2\u178f"}},{"language":"ko_KR","version":"4.8","updated":"2017-06-19 07:08:35","english_name":"Korean","native_name":"\ud55c\uad6d\uc5b4","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ko_KR.zip","iso":{"1":"ko","2":"kor"},"strings":{"continue":"\uacc4\uc18d"}},{"language":"ckb","version":"4.7.2","updated":"2017-01-26 15:48:25","english_name":"Kurdish (Sorani)","native_name":"\u0643\u0648\u0631\u062f\u06cc\u200e","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/ckb.zip","iso":{"1":"ku","3":"ckb"},"strings":{"continue":"\u0628\u0647\u200c\u0631\u062f\u0647\u200c\u0648\u0627\u0645 \u0628\u0647\u200c"}},{"language":"lo","version":"4.7.2","updated":"2016-11-12 09:59:23","english_name":"Lao","native_name":"\u0e9e\u0eb2\u0eaa\u0eb2\u0ea5\u0eb2\u0ea7","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/lo.zip","iso":{"1":"lo","2":"lao"},"strings":{"continue":"\u0e95\u0ecd\u0ec8\u200b\u0ec4\u0e9b"}},{"language":"lt_LT","version":"4.8","updated":"2017-07-05 11:43:04","english_name":"Lithuanian","native_name":"Lietuvi\u0173 kalba","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/lt_LT.zip","iso":{"1":"lt","2":"lit"},"strings":{"continue":"T\u0119sti"}},{"language":"lv","version":"4.7.5","updated":"2017-03-17 20:40:40","english_name":"Latvian","native_name":"Latvie\u0161u valoda","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/lv.zip","iso":{"1":"lv","2":"lav"},"strings":{"continue":"Turpin\u0101t"}},{"language":"mk_MK","version":"4.7.5","updated":"2017-01-26 15:54:41","english_name":"Macedonian","native_name":"\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438 \u0458\u0430\u0437\u0438\u043a","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/mk_MK.zip","iso":{"1":"mk","2":"mkd"},"strings":{"continue":"\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438"}},{"language":"ml_IN","version":"4.7.2","updated":"2017-01-27 03:43:32","english_name":"Malayalam","native_name":"\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/ml_IN.zip","iso":{"1":"ml","2":"mal"},"strings":{"continue":"\u0d24\u0d41\u0d1f\u0d30\u0d41\u0d15"}},{"language":"mn","version":"4.7.2","updated":"2017-01-12 07:29:35","english_name":"Mongolian","native_name":"\u041c\u043e\u043d\u0433\u043e\u043b","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/mn.zip","iso":{"1":"mn","2":"mon"},"strings":{"continue":"\u04ae\u0440\u0433\u044d\u043b\u0436\u043b\u04af\u04af\u043b\u044d\u0445"}},{"language":"mr","version":"4.8","updated":"2017-07-05 19:40:47","english_name":"Marathi","native_name":"\u092e\u0930\u093e\u0920\u0940","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/mr.zip","iso":{"1":"mr","2":"mar"},"strings":{"continue":"\u0938\u0941\u0930\u0941 \u0920\u0947\u0935\u093e"}},{"language":"ms_MY","version":"4.7.5","updated":"2017-03-05 09:45:10","english_name":"Malay","native_name":"Bahasa Melayu","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/ms_MY.zip","iso":{"1":"ms","2":"msa"},"strings":{"continue":"Teruskan"}},{"language":"my_MM","version":"4.1.18","updated":"2015-03-26 15:57:42","english_name":"Myanmar (Burmese)","native_name":"\u1017\u1019\u102c\u1005\u102c","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.1.18\/my_MM.zip","iso":{"1":"my","2":"mya"},"strings":{"continue":"\u1006\u1000\u103a\u101c\u1000\u103a\u101c\u102f\u1015\u103a\u1006\u1031\u102c\u1004\u103a\u1015\u102b\u104b"}},{"language":"nb_NO","version":"4.8","updated":"2017-06-26 11:11:30","english_name":"Norwegian (Bokm\u00e5l)","native_name":"Norsk bokm\u00e5l","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/nb_NO.zip","iso":{"1":"nb","2":"nob"},"strings":{"continue":"Fortsett"}},{"language":"ne_NP","version":"4.8","updated":"2017-06-23 11:30:58","english_name":"Nepali","native_name":"\u0928\u0947\u092a\u093e\u0932\u0940","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ne_NP.zip","iso":{"1":"ne","2":"nep"},"strings":{"continue":"\u091c\u093e\u0930\u0940 \u0930\u093e\u0916\u094d\u0928\u0941\u0939\u094b\u0938\u094d"}},{"language":"nl_BE","version":"4.8","updated":"2017-06-20 17:04:00","english_name":"Dutch (Belgium)","native_name":"Nederlands (Belgi\u00eb)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/nl_BE.zip","iso":{"1":"nl","2":"nld"},"strings":{"continue":"Doorgaan"}},{"language":"nl_NL","version":"4.8","updated":"2017-06-26 13:23:34","english_name":"Dutch","native_name":"Nederlands","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/nl_NL.zip","iso":{"1":"nl","2":"nld"},"strings":{"continue":"Doorgaan"}},{"language":"nl_NL_formal","version":"4.7.5","updated":"2017-02-16 13:24:21","english_name":"Dutch (Formal)","native_name":"Nederlands (Formeel)","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/nl_NL_formal.zip","iso":{"1":"nl","2":"nld"},"strings":{"continue":"Doorgaan"}},{"language":"nn_NO","version":"4.8","updated":"2017-06-08 13:05:53","english_name":"Norwegian (Nynorsk)","native_name":"Norsk nynorsk","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/nn_NO.zip","iso":{"1":"nn","2":"nno"},"strings":{"continue":"Hald fram"}},{"language":"oci","version":"4.7.2","updated":"2017-01-02 13:47:38","english_name":"Occitan","native_name":"Occitan","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/oci.zip","iso":{"1":"oc","2":"oci"},"strings":{"continue":"Contunhar"}},{"language":"pa_IN","version":"4.7.2","updated":"2017-01-16 05:19:43","english_name":"Punjabi","native_name":"\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/pa_IN.zip","iso":{"1":"pa","2":"pan"},"strings":{"continue":"\u0a1c\u0a3e\u0a30\u0a40 \u0a30\u0a71\u0a16\u0a4b"}},{"language":"pl_PL","version":"4.8","updated":"2017-06-30 13:42:57","english_name":"Polish","native_name":"Polski","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/pl_PL.zip","iso":{"1":"pl","2":"pol"},"strings":{"continue":"Kontynuuj"}},{"language":"ps","version":"4.1.18","updated":"2015-03-29 22:19:48","english_name":"Pashto","native_name":"\u067e\u069a\u062a\u0648","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.1.18\/ps.zip","iso":{"1":"ps","2":"pus"},"strings":{"continue":"\u062f\u0648\u0627\u0645 \u0648\u0631\u06a9\u0693\u0647"}},{"language":"pt_BR","version":"4.8","updated":"2017-06-21 17:29:18","english_name":"Portuguese (Brazil)","native_name":"Portugu\u00eas do Brasil","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/pt_BR.zip","iso":{"1":"pt","2":"por"},"strings":{"continue":"Continuar"}},{"language":"pt_PT","version":"4.8","updated":"2017-06-23 10:24:37","english_name":"Portuguese (Portugal)","native_name":"Portugu\u00eas","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/pt_PT.zip","iso":{"1":"pt"},"strings":{"continue":"Continuar"}},{"language":"rhg","version":"4.7.2","updated":"2016-03-16 13:03:18","english_name":"Rohingya","native_name":"Ru\u00e1inga","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/rhg.zip","iso":{"3":"rhg"},"strings":{"continue":""}},{"language":"ro_RO","version":"4.8","updated":"2017-06-18 18:31:34","english_name":"Romanian","native_name":"Rom\u00e2n\u0103","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ro_RO.zip","iso":{"1":"ro","2":"ron"},"strings":{"continue":"Continu\u0103"}},{"language":"ru_RU","version":"4.8","updated":"2017-06-15 13:54:09","english_name":"Russian","native_name":"\u0420\u0443\u0441\u0441\u043a\u0438\u0439","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ru_RU.zip","iso":{"1":"ru","2":"rus"},"strings":{"continue":"\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c"}},{"language":"sah","version":"4.7.2","updated":"2017-01-21 02:06:41","english_name":"Sakha","native_name":"\u0421\u0430\u0445\u0430\u043b\u044b\u044b","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/sah.zip","iso":{"2":"sah","3":"sah"},"strings":{"continue":"\u0421\u0430\u043b\u0495\u0430\u0430"}},{"language":"si_LK","version":"4.7.2","updated":"2016-11-12 06:00:52","english_name":"Sinhala","native_name":"\u0dc3\u0dd2\u0d82\u0dc4\u0dbd","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/si_LK.zip","iso":{"1":"si","2":"sin"},"strings":{"continue":"\u0daf\u0dd2\u0d9c\u0da7\u0db8 \u0d9a\u0dbb\u0d9c\u0dd9\u0db1 \u0dba\u0db1\u0dca\u0db1"}},{"language":"sk_SK","version":"4.8","updated":"2017-06-15 09:02:13","english_name":"Slovak","native_name":"Sloven\u010dina","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/sk_SK.zip","iso":{"1":"sk","2":"slk"},"strings":{"continue":"Pokra\u010dova\u0165"}},{"language":"sl_SI","version":"4.8","updated":"2017-06-08 15:29:14","english_name":"Slovenian","native_name":"Sloven\u0161\u010dina","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/sl_SI.zip","iso":{"1":"sl","2":"slv"},"strings":{"continue":"Nadaljuj"}},{"language":"sq","version":"4.7.5","updated":"2017-04-24 08:35:30","english_name":"Albanian","native_name":"Shqip","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/sq.zip","iso":{"1":"sq","2":"sqi"},"strings":{"continue":"Vazhdo"}},{"language":"sr_RS","version":"4.8","updated":"2017-06-08 11:06:53","english_name":"Serbian","native_name":"\u0421\u0440\u043f\u0441\u043a\u0438 \u0458\u0435\u0437\u0438\u043a","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/sr_RS.zip","iso":{"1":"sr","2":"srp"},"strings":{"continue":"\u041d\u0430\u0441\u0442\u0430\u0432\u0438"}},{"language":"sv_SE","version":"4.8","updated":"2017-06-27 07:35:06","english_name":"Swedish","native_name":"Svenska","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/sv_SE.zip","iso":{"1":"sv","2":"swe"},"strings":{"continue":"Forts\u00e4tt"}},{"language":"szl","version":"4.7.2","updated":"2016-09-24 19:58:14","english_name":"Silesian","native_name":"\u015al\u014dnsk\u014f g\u014fdka","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/szl.zip","iso":{"3":"szl"},"strings":{"continue":"K\u014dntynuowa\u0107"}},{"language":"ta_IN","version":"4.7.2","updated":"2017-01-27 03:22:47","english_name":"Tamil","native_name":"\u0ba4\u0bae\u0bbf\u0bb4\u0bcd","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/ta_IN.zip","iso":{"1":"ta","2":"tam"},"strings":{"continue":"\u0ba4\u0bca\u0b9f\u0bb0\u0bb5\u0bc1\u0bae\u0bcd"}},{"language":"te","version":"4.7.2","updated":"2017-01-26 15:47:39","english_name":"Telugu","native_name":"\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/te.zip","iso":{"1":"te","2":"tel"},"strings":{"continue":"\u0c15\u0c4a\u0c28\u0c38\u0c3e\u0c17\u0c3f\u0c02\u0c1a\u0c41"}},{"language":"th","version":"4.7.2","updated":"2017-01-26 15:48:43","english_name":"Thai","native_name":"\u0e44\u0e17\u0e22","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/th.zip","iso":{"1":"th","2":"tha"},"strings":{"continue":"\u0e15\u0e48\u0e2d\u0e44\u0e1b"}},{"language":"tl","version":"4.7.2","updated":"2016-12-30 02:38:08","english_name":"Tagalog","native_name":"Tagalog","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/tl.zip","iso":{"1":"tl","2":"tgl"},"strings":{"continue":"Magpatuloy"}},{"language":"tr_TR","version":"4.8","updated":"2017-06-19 13:54:12","english_name":"Turkish","native_name":"T\u00fcrk\u00e7e","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/tr_TR.zip","iso":{"1":"tr","2":"tur"},"strings":{"continue":"Devam"}},{"language":"tt_RU","version":"4.7.2","updated":"2016-11-20 20:20:50","english_name":"Tatar","native_name":"\u0422\u0430\u0442\u0430\u0440 \u0442\u0435\u043b\u0435","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/tt_RU.zip","iso":{"1":"tt","2":"tat"},"strings":{"continue":"\u0434\u04d9\u0432\u0430\u043c \u0438\u0442\u04af"}},{"language":"tah","version":"4.7.2","updated":"2016-03-06 18:39:39","english_name":"Tahitian","native_name":"Reo Tahiti","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/tah.zip","iso":{"1":"ty","2":"tah","3":"tah"},"strings":{"continue":""}},{"language":"ug_CN","version":"4.7.2","updated":"2016-12-05 09:23:39","english_name":"Uighur","native_name":"Uy\u01a3urq\u0259","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.2\/ug_CN.zip","iso":{"1":"ug","2":"uig"},"strings":{"continue":"\u062f\u0627\u06cb\u0627\u0645\u0644\u0627\u0634\u062a\u06c7\u0631\u06c7\u0634"}},{"language":"uk","version":"4.8","updated":"2017-07-01 22:52:09","english_name":"Ukrainian","native_name":"\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/uk.zip","iso":{"1":"uk","2":"ukr"},"strings":{"continue":"\u041f\u0440\u043e\u0434\u043e\u0432\u0436\u0438\u0442\u0438"}},{"language":"ur","version":"4.8","updated":"2017-07-02 09:17:00","english_name":"Urdu","native_name":"\u0627\u0631\u062f\u0648","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/ur.zip","iso":{"1":"ur","2":"urd"},"strings":{"continue":"\u062c\u0627\u0631\u06cc \u0631\u06a9\u06be\u06cc\u06ba"}},{"language":"uz_UZ","version":"4.7.5","updated":"2017-05-13 09:55:38","english_name":"Uzbek","native_name":"O\u2018zbekcha","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/uz_UZ.zip","iso":{"1":"uz","2":"uzb"},"strings":{"continue":"Davom etish"}},{"language":"vi","version":"4.8","updated":"2017-06-15 11:24:18","english_name":"Vietnamese","native_name":"Ti\u1ebfng Vi\u1ec7t","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/vi.zip","iso":{"1":"vi","2":"vie"},"strings":{"continue":"Ti\u1ebfp t\u1ee5c"}},{"language":"zh_HK","version":"4.8","updated":"2017-06-15 13:17:37","english_name":"Chinese (Hong Kong)","native_name":"\u9999\u6e2f\u4e2d\u6587\u7248\t","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/zh_HK.zip","iso":{"1":"zh","2":"zho"},"strings":{"continue":"\u7e7c\u7e8c"}},{"language":"zh_TW","version":"4.8","updated":"2017-07-05 10:14:12","english_name":"Chinese (Taiwan)","native_name":"\u7e41\u9ad4\u4e2d\u6587","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.8\/zh_TW.zip","iso":{"1":"zh","2":"zho"},"strings":{"continue":"\u7e7c\u7e8c"}},{"language":"zh_CN","version":"4.7.5","updated":"2017-01-26 15:54:45","english_name":"Chinese (China)","native_name":"\u7b80\u4f53\u4e2d\u6587","package":"http:\/\/downloads.wordpress.org\/translation\/core\/4.7.5\/zh_CN.zip","iso":{"1":"zh","2":"zho"},"strings":{"continue":"\u7ee7\u7eed"}}]}'; $decoded = json_decode( $string, true ); return $decoded['translations']; } /** * Merge extra languages with existing WP languages but don't overwrite WP languages * * @param $languages * @return mixed */ public function add_extra_languages( $languages ) { $extra_languages = $this->get_extra_languages(); foreach ( $extra_languages as $key => $extra_language ) { // check just in case WP adds a language from the extra languages array if ( isset( $languages[ $key ] ) ) { continue; } else { $languages[ $key ] = $extra_language; } } return $languages; } /** * Languages supported by DeepL but not found in WP * * @return array */ public function get_extra_languages() { return array( 'ace' => array( 'language' => 'ace', 'english_name' => 'Acehnese', 'native_name' => 'Acehnese', 'iso' => array( 'ace' ) ), 'ay' => array( 'language' => 'ay', 'english_name' => 'Aymara', 'native_name' => 'Aymara', 'iso' => array( 'ay' ) ), 'ba' => array( 'language' => 'ba', 'english_name' => 'Bashkir', 'native_name' => 'Bashkir', 'iso' => array( 'ba' ) ), 'bho' => array( 'language' => 'bho', 'english_name' => 'Bhojpuri', 'native_name' => 'Bhojpuri', 'iso' => array( 'bho' ) ), 'br' => array( 'language' => 'br', 'english_name' => 'Breton', 'native_name' => 'Breton', 'iso' => array( 'br' ) ), 'ga' => array( 'language' => 'ga', 'english_name' => 'Irish', 'native_name' => 'Irish', 'iso' => array( 'ga' ) ), 'gn' => array( 'language' => 'gn', 'english_name' => 'Guarani', 'native_name' => 'Guarani', 'iso' => array( 'gn' ) ), 'gom' => array( 'language' => 'gom', 'english_name' => 'Konkani', 'native_name' => 'Konkani', 'iso' => array( 'gom' ) ), 'ha' => array( 'language' => 'ha', 'english_name' => 'Hausa', 'native_name' => 'Hausa', 'iso' => array( 'ha' ) ), 'ht' => array( 'language' => 'ht', 'english_name' => 'Haitian Creole', 'native_name' => 'Haitian Creole', 'iso' => array( 'ht' ) ), 'ig' => array( 'language' => 'ig', 'english_name' => 'Igbo', 'native_name' => 'Igbo', 'iso' => array( 'ig' ) ), 'kmr' => array( 'language' => 'kmr', 'english_name' => 'Kurdish (Kurmanji)', 'native_name' => 'Kurdish (Kurmanji)', 'iso' => array( 'kmr' ) ), 'la' => array( 'language' => 'la', 'english_name' => 'Latin', 'native_name' => 'Latin', 'iso' => array( 'la' ) ), 'lb' => array( 'language' => 'lb', 'english_name' => 'Luxembourgish', 'native_name' => 'Luxembourgish', 'iso' => array( 'lb' ) ), 'lmo' => array( 'language' => 'lmo', 'english_name' => 'Lombard', 'native_name' => 'Lombard', 'iso' => array( 'lmo' ) ), 'ln' => array( 'language' => 'ln', 'english_name' => 'Lingala', 'native_name' => 'Lingala', 'iso' => array( 'ln' ) ), 'mai' => array( 'language' => 'mai', 'english_name' => 'Maithili', 'native_name' => 'Maithili', 'iso' => array( 'mai' ) ), 'mg' => array( 'language' => 'mg', 'english_name' => 'Malagasy', 'native_name' => 'Malagasy', 'iso' => array( 'mg' ) ), 'mi' => array( 'language' => 'mi', 'english_name' => 'Maori', 'native_name' => 'Maori', 'iso' => array( 'mi' ) ), 'mt' => array( 'language' => 'mt', 'english_name' => 'Maltese', 'native_name' => 'Maltese', 'iso' => array( 'mt' ) ), 'pag' => array( 'language' => 'pag', 'english_name' => 'Pangasinan', 'native_name' => 'Pangasinan', 'iso' => array( 'pag' ) ), 'pam' => array( 'language' => 'pam', 'english_name' => 'Kapampangan', 'native_name' => 'Kapampangan', 'iso' => array( 'pam' ) ), 'prs' => array( 'language' => 'prs', 'english_name' => 'Dari', 'native_name' => 'Dari', 'iso' => array( 'prs' ) ), 'qu' => array( 'language' => 'qu', 'english_name' => 'Quechua', 'native_name' => 'Quechua', 'iso' => array( 'qu' ) ), 'sa' => array( 'language' => 'sa', 'english_name' => 'Sanskrit', 'native_name' => 'Sanskrit', 'iso' => array( 'sa' ) ), 'scn' => array( 'language' => 'scn', 'english_name' => 'Sicilian', 'native_name' => 'Sicilian', 'iso' => array( 'scn' ) ), 'su' => array( 'language' => 'su', 'english_name' => 'Sundanese', 'native_name' => 'Sundanese', 'iso' => array( 'su' ) ), 'tg' => array( 'language' => 'tg', 'english_name' => 'Tajik', 'native_name' => 'Tajik', 'iso' => array( 'tg' ) ), 'tk' => array( 'language' => 'tk', 'english_name' => 'Turkmen', 'native_name' => 'Turkmen', 'iso' => array( 'tk' ) ), 'tn' => array( 'language' => 'tn', 'english_name' => 'Tswana', 'native_name' => 'Tswana', 'iso' => array( 'tn' ) ), 'ts' => array( 'language' => 'ts', 'english_name' => 'Tsonga', 'native_name' => 'Tsonga', 'iso' => array( 'ts' ) ), 'wo' => array( 'language' => 'wo', 'english_name' => 'Wolof', 'native_name' => 'Wolof', 'iso' => array( 'wo' ) ), 'xh' => array( 'language' => 'xh', 'english_name' => 'Xhosa', 'native_name' => 'Xhosa', 'iso' => array( 'xh' ) ), 'yue' => array( 'language' => 'yue', 'english_name' => 'Cantonese', 'native_name' => 'Cantonese', 'iso' => array( 'yue' ) ) ); } } includes/advanced-settings/manual-translation-only.php 0000777 00000006010 15251156640 0017267 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_manual_translation_only', 1090); function trp_register_manual_translation_only( $settings_array ){ $settings_array[] = array( 'name' => 'manual_translation_only', 'type' => 'checkbox', 'label' => esc_html__( 'Manual Translation Only', 'translatepress-multilingual' ), 'description' => wp_kses( __( "TranslatePress pro-actively scans and saves strings in the database when users access translated pages. <br>This setting disables this functionality and only allows translation and string saving when inside the Translation Editor. <br>Also disables machine translation outside the Translation Editor, giving you better control over character spending, by translating only the pages you visit in the Translation Editor.", 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } // Filter to restrict string saving to translation editor only add_filter('trp_allow_string_saving', 'trp_restrict_string_saving_to_editor', 10, 3); function trp_restrict_string_saving_to_editor($allow, $new_strings, $update_strings) { $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['manual_translation_only'] ) && $option['manual_translation_only'] === 'yes' ) { // Allow string saving for TRP editor AJAX actions (they have their own nonce verification) if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 ) { return $allow; } // Only allow string saving if we're in the translation editor if ( !isset($_GET['trp-edit-translation']) || $_GET['trp-edit-translation'] !== 'preview' ) { return false; } } return $allow; } // Hook to disable machine translation outside translation editor add_filter('trp_machine_translator_is_available', 'trp_disable_machine_translation_outside_editor', 10); function trp_disable_machine_translation_outside_editor($is_available) { $advanced_option = get_option( 'trp_advanced_settings', true ); if ( isset( $advanced_option['manual_translation_only'] ) && $advanced_option['manual_translation_only'] === 'yes' ) { // Allow machine translation for TRP editor AJAX actions (they have their own nonce verification) if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 ) { return $is_available; } // If not in translation editor, disable machine translation if ( !isset($_GET['trp-edit-translation']) || $_GET['trp-edit-translation'] !== 'preview' ) { // Modify the machine translation setting to 'no' $is_available = false; } } return $is_available; } includes/advanced-settings/strip-gettext-post-meta.php 0000777 00000005013 15251156640 0017233 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_strip_gettext_post_meta', 70 ); function trp_register_strip_gettext_post_meta( $settings_array ){ $settings_array[] = array( 'name' => 'strip_gettext_post_meta', 'type' => 'checkbox', 'label' => esc_html__( 'Filter Gettext wrapping from post meta', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Filters gettext wrapping such as #!trpst#trp-gettext from all updated post meta. Does not affect previous post meta. <br/><strong>Database backup is recommended before switching on.</strong>', 'translatepress-multilingual' ), array( 'br' => array(), 'strong' => array()) ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ); return $settings_array; } /** * Stripped gettext wrapping from wp_update_post_meta */ add_action( 'added_post_meta', 'trp_filter_trpgettext_from_updated_post_meta', 10, 4); add_action( 'updated_postmeta', 'trp_filter_trpgettext_from_updated_post_meta', 10, 4); function trp_filter_trpgettext_from_updated_post_meta($meta_id, $object_id, $meta_key, $meta_value){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['strip_gettext_post_meta'] ) && $option['strip_gettext_post_meta'] === 'yes' && class_exists( 'TRP_Translation_Manager' ) ){ if ( is_serialized($meta_value) ){ // Security fix: Skip processing serialized data to prevent PHP object injection // Only process plain text meta values // https://www.php.net/manual/en/function.unserialize.php // Do not pass untrusted user input to unserialize() regardless of the options value of allowed_classes. // Unserialization can result in code being loaded and executed due to object instantiation and autoloading, and a malicious user may be able to exploit this. return; }else{ $stripped_meta_value = trp_strip_gettext_array( $meta_value ); } if ( $stripped_meta_value != $meta_value){ remove_action('updated_postmeta','trp_filter_trpgettext_from_updated_post_meta' ); update_post_meta( $object_id, $meta_key, $stripped_meta_value ); add_action( 'updated_postmeta', 'trp_filter_trpgettext_from_updated_post_meta', 10, 4); } } } function trp_strip_gettext_array( $value ){ if ( is_array( $value ) ){ foreach( $value as $key => $item ){ $value[$key] = trp_strip_gettext_array( $item ); } return $value; }else{ return TRP_Translation_Manager::strip_gettext_tags( $value ); } } includes/advanced-settings/serve-similar-translation.php 0000777 00000013776 15251156640 0017636 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter('trp_register_advanced_settings', 'serve_similar_translation', 1050); function serve_similar_translation($settings_array) { $settings_array[] = array( 'name' => 'serve_similar_translation', 'type' => 'checkbox', 'label' => esc_html__('Automatic Translation Memory', 'translatepress-multilingual'), 'description' => wp_kses(__('Serve same translation for similar text. The strings need to have a percentage of 95% similarity.<br>Helps prevent losing existing translation when correcting typos or making minor adjustments to the original text. <br>If a translation already exists for a very similar original string, it will automatically be used for the current original string.<br>Does not work when making changes to a text that is part of a translation block unless the new text is manually merged again in a translation block.<br>Each string needs to have a minimum of 50 characters.', 'translatepress-multilingual'), array('br' => array())) . '<br><p class="trp-settings-warning" style="width: 100%;">' . esc_html__( 'WARNING: This feature can negatively impact page loading times in secondary languages, particularly with large databases (for example websites with a lot of pages or products). If you experience slow loading times, disable this and try again.', 'translatepress-multilingual') . '</p>', 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } add_filter('trp_add_similar_and_original_strings_to_db', 'trp_add_similar_and_original_strings_to_db'); function trp_add_similar_and_original_strings_to_db($bool){ $bool = true; return $bool; }; /** * In this function we are trying to find original similar strings on the page that are almost identical with a string that exists in DB with a translation * The purpose of this advanced setting is to ease the work of the user or to lower the cost of automatic translation by translating almost identical strings * with an already existing translation in DB */ add_filter( 'trp_get_existing_translations', 'trp_serve_similar_translations', 5, 5); function trp_serve_similar_translations ( $dictionary, $prepared_query, $strings_array, $language_code, $block_type ){ if( isset($_GET['trp-edit-translation']) ){ return $dictionary; } $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $table_name = $trp_query->get_table_name($language_code); $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['serve_similar_translation'] ) && $option['serve_similar_translation'] === 'yes' ) { //here we set the minimum number of characters a string should have to be considered for checking similarity //in our case is set to 50 characters but it can be changed $minimal_characters_per_strings_considered_for_similarity = apply_filters('trp_minimal_characters_per_strings_considered_for_similarity', 50); //here we set the minimal percentage of compatibility between the string in the DB and the similar ones //we set it at 95% but it can be changed $minimal_percent_of_compatibility = apply_filters('trp_minimal_percent_of_compatibility_for_strings_to_be_similar', 0.95); foreach ( $strings_array as $string ) { //we try to get the translated strings from the dictionary if ( !isset( $dictionary[ $string ] ) ) { $result = false; $query = "SELECT original,translated, status FROM `" . sanitize_text_field( $table_name ) . "` WHERE status != " . TRP_Query::NOT_TRANSLATED . " AND `original` != '%s' AND MATCH(original) AGAINST ('%s' IN NATURAL LANGUAGE MODE ) LIMIT 1"; $query = $trp_query->db->prepare( $query, array( $string, $string ) ); $result = $trp_query->db->get_results( $query, OBJECT_K ); if ( !empty( $result ) ) { // we reset the found query which has multiple arguments to the $original argument which is the unaltered string in the default language // after this, we check the minimal length of the two strings and use the function 'trp_dice_match' to determine the percentage of similarity // if the percentage is higher or equal to the chosen value, the similar string gets all the arguments from the string in DB including the translation $original = reset( $result )->original; if ( strlen( $string ) >= $minimal_characters_per_strings_considered_for_similarity && strlen( $original ) >= $minimal_characters_per_strings_considered_for_similarity && trp_dice_match( $string, $original ) >= $minimal_percent_of_compatibility) { $dictionary[ $string ] = reset( $result ); } } } } } return $dictionary; } // https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient // PHP clone of https://github.com/stephenjjbrown/string-similarity-js/blob/master/src/string-similarity.ts function trp_dice_match($string1, $string2) { // we're ignoring punctuation and making everything lowercase in hopes of getting better matches. $string1 = strtolower(str_replace(['?', '!', '.', ',', ';', ':'], '', $string1)); $string2 = strtolower(str_replace(['?', '!', '.', ',', ';', ':'], '', $string2)); $map = array(); for ($i = 0; $i < strlen($string1) - 1; $i++ ){ $substr1 = substr($string1, $i, 2); $value = (isset($map[$substr1])) ? $map[$substr1] + 1 : 1; $map[$substr1] = $value; } $match = 0; for ($j = 0; $j < strlen($string2) - 1; $j++){ $substr2 = substr($string2, $j, 2); $count = (isset($map[$substr2])) ? $map[$substr2] : 0; if ($count > 0){ $map[$substr2] = $count - 1; $match++; } } return ($match * 2) / (strlen($string1) + strlen($string2) - 2); } includes/advanced-settings/exclude-gettext-strings.php 0000777 00000004066 15251156640 0017312 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_exclude_gettext_strings', 100 ); function trp_register_exclude_gettext_strings( $settings_array ){ $settings_array[] = array( 'name' => 'exclude_gettext_strings', 'type' => 'list', 'columns' => array( 'string' => __('Gettext String', 'translatepress-multilingual' ), 'domain' => __('Domain', 'translatepress-multilingual') ), 'label' => esc_html__( 'Exclude Gettext Strings', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Exclude these strings from being translated as Gettext strings by TranslatePress. Leave the domain empty to take into account any Gettext string.<br/>Can still be translated through po/mo files.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'exclude_strings', 'container' => 'exclude_gettext_strings' ); return $settings_array; } /** * Exclude gettext from being translated */ add_action( 'init', 'trp_load_exclude_strings' ); function trp_load_exclude_strings(){ $option = get_option( 'trp_advanced_settings', true ); if( isset( $option['exclude_gettext_strings'] ) && count( $option['exclude_gettext_strings']['string'] ) > 0 ) add_filter('trp_skip_gettext_processing', 'trp_exclude_strings', 1000, 4 ); } function trp_exclude_strings ( $return, $translation, $text, $domain ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['exclude_gettext_strings'] ) ) { foreach( $option['exclude_gettext_strings']['string'] as $key => $string ){ if((empty(trim($string))) && (trim($domain ) === trim( $option['exclude_gettext_strings']['domain'][$key]))){ return true; } if( trim( $text ) === trim( $string ) ){ if( empty( $option['exclude_gettext_strings']['domain'][$key] ) ) return true; else if( trim( $domain ) === trim( $option['exclude_gettext_strings']['domain'][$key] ) ) return true; } } } return $return; } includes/advanced-settings/custom-language.php 0000777 00000007303 15251156640 0015600 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter('trp_register_advanced_settings', 'trp_register_custom_language', 2285); /* * To use the 'mixed' type for advanced settings, there needs to be specified the type of the control * There are 4 options to choose from: * text: simple textbox * textarea: classic textarea used in TP advanced options * select: a dropdown select box with the possible options set in a sub-array * like 'option_name' => array ('label'=> esc_html__( 'Option label', 'translatepress-multilingual' ), 'type' => 'select', 'values' => array ( __('Volvo','translatepress-multilingual') , __('Saab', 'translatepress-multilingual'), __('Scania', 'translatepress-multilingual') ) ), * * * checkbox: a classic checkbox with the checked value always set to 'yes' and the unchecked value to empty. * For the elements that don't require pre-determined values, leave the 'values' array empty * */ function trp_register_custom_language($settings_array){ $first_description = wp_kses( __( 'To edit an existing TranslatePress language, input the language code and fill in only the columns you want to overwrite (e.g. Language name, Flag).<br>You can also add new custom languages. They will be available under General settings, All Languages list, where the URL slug can be edited.' , 'translatepress-multilingual' ), [ 'br' => [] ] ); $second_description = wp_kses( __( 'For custom flag, first upload the image in media library then paste the URL.<br>Changing or deleting a custom language will impact translations and site URL\'s.<br>The Language code and the ISO Code should contain only alphabetical values, numerical values, "-" and "_".<br>The ISO Codes can be found on <a href = "https://cloud.google.com/translate/docs/languages" target = "_blank">Google ISO Codes</a> and <a href = "https://www.deepl.com/docs-api/translating-text/" target = "_blank">DeepL Target Codes</a>.' , 'translatepress-multilingual' ), array( 'br' => array(), 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ) )); $settings_array[] = array( 'name' => 'custom_language', 'columns' => array ( 'cuslangcode' => array ('label' => esc_html__( 'Language code', 'translatepress-multilingual' ), 'type' => 'text', 'values' => '', 'placeholder' => 'e.g. en_US', 'required' => true ), 'cuslangname' => array ('label' => esc_html__( 'Language name', 'translatepress-multilingual' ), 'type' => 'text', 'values' => '', 'placeholder' => '', 'required' => false ), 'cuslangnative' => array ('label' => esc_html__( 'Native name', 'translatepress-multilingual' ), 'type' => 'text', 'values' => '', 'placeholder' => '', 'required' => false ), 'cuslangiso' => array ('label' => esc_html__( 'ISO Code', 'translatepress-multilingual' ), 'type' => 'text', 'values' => '', 'placeholder' => 'e.g. en', 'required' => false ), 'cuslangflag' => array ('label' => esc_html__( 'Flag URL', 'translatepress-multilingual' ), 'type' => 'text', 'values' => '', 'placeholder' => '', 'required' => false ), 'cuslangisrtl' => array ('label' => esc_html__( 'Text RTL', 'translatepress-multilingual' ), 'type' => 'checkbox', 'values' => '', 'placeholder' => '', 'required' => false ), ), 'type' => 'mixed', 'label' => esc_html__( 'Custom language', 'translatepress-multilingual' ), /* phpcs:ignore */ 'first_description' => $first_description, /* phpcs:ignore */ 'second_description' => $second_description, 'id' => 'custom_language', 'container' => 'custom_language', ); return $settings_array; } includes/advanced-settings/custom-date-format.php 0000777 00000002315 15251156640 0016216 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Register advanced configuration option for custom date formatting for every translated language * The settings uses the 'input_array' advanced setting * Saves as a key-value pair * */ add_filter( 'trp_register_advanced_settings', 'trp_register_language_date_format', 1205 ); function trp_register_language_date_format( $settings_array ){ $settings_array[] = array( 'name' => 'language_date_format', 'rows' => trp_get_languages("nodefault"), 'default' => '', 'type' => 'input_array', 'label' => esc_html__( 'Date format', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Customize the date formatting per each translated language.<br/>Leave empty for default WP setting or see more information <a href="https://wordpress.org/support/article/formatting-date-and-time/" title="Formatting Date and Time" target="_blank">here</a>', 'translatepress-multilingual' ), array( 'br' => array(), 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ) )), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } includes/advanced-settings/hreflang-remove-locale.php 0000777 00000004614 15251156640 0017025 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_hreflang_remove_locale', 1000 ); function trp_register_hreflang_remove_locale( $settings_array ){ $settings_array[] = array( 'name' => 'hreflang_remove_locale', 'type' => 'radio', 'options' => array( 'show_both', 'remove_country_locale', 'remove_region_independent_locale' ), 'default' => 'show_both', 'labels' => array( esc_html__( 'Show Both (recommended)', 'translatepress-multilingual' ), esc_html__( 'Remove Country Locale', 'translatepress-multilingual' ), esc_html__( 'Remove Region Independent Locale', 'translatepress-multilingual' ) ), 'label' => esc_html__( 'Remove duplicate hreflang', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Choose which hreflang tags will appear on your website.<br/>We recommend showing both types of hreflang tags as indicated by <a href="https://developers.google.com/search/docs/advanced/crawling/localized-versions" title="Google Crawling" target="_blank">Google documentation</a>.<br/>Removing Country Locale when having multiple Country Locales of the same language (ex. English UK and English US) will result in showing one hreflang tag with link to just one of the region locales for that language.', 'translatepress-multilingual' ), array( 'br' => array(), 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ) ) ), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } add_filter( 'trp_add_country_hreflang_tags', 'trp_display_country_hreflang_tag' ); function trp_display_country_hreflang_tag( $display ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['hreflang_remove_locale'] ) && $option['hreflang_remove_locale'] === 'remove_country_locale' ) { return false; } return $display; } add_filter( 'trp_add_region_independent_hreflang_tags', 'trp_display_region_independent_hreflang_tag' ); function trp_display_region_independent_hreflang_tag( $display ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['hreflang_remove_locale'] ) && $option['hreflang_remove_locale'] === 'remove_region_independent_locale' ) { return false; } return $display; } includes/advanced-settings/exclude-selectors-automatic-translation.php 0000777 00000003366 15251156640 0022464 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Add automatic translate exclude selectors. */ add_filter( 'trp_register_advanced_settings', 'trp_register_exclude_selectors_automatic_translation', 120 ); function trp_register_exclude_selectors_automatic_translation( $settings_array ){ $settings_array[] = array( 'name' => 'exclude_selectors_from_automatic_translation', 'type' => 'list_input', 'columns' => array( 'selector' => __('Selector', 'translatepress-multilingual' ), ), 'label' => esc_html__( 'Exclude selectors only from automatic translation', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Do not automatically translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being automatically translated.<br>Manual translation of these strings is still possible.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'exclude_strings', 'container' => 'exclude_selectors_at', ); return $settings_array; } add_filter( 'trp_no_auto_translate_selectors', 'trp_skip_automatic_translation_for_selectors' ); function trp_skip_automatic_translation_for_selectors( $skip_selectors ){ $option = get_option( 'trp_advanced_settings', true ); $add_skip_selectors = array( ); if ( isset( $option['exclude_selectors_from_automatic_translation'] ) && is_array( $option['exclude_selectors_from_automatic_translation']['selector'] ) ) { $add_skip_selectors = $option['exclude_selectors_from_automatic_translation']['selector']; } return array_merge( $skip_selectors, $add_skip_selectors ); } includes/advanced-settings/open-language-switcher-shortcode-on-click.php 0000777 00000004376 15251156640 0022551 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_open_language_switcher_shortcode_on_click', 1350 ); function trp_open_language_switcher_shortcode_on_click( $settings_array ){ $settings_array[] = array( 'name' => 'open_language_switcher_shortcode_on_click', 'type' => 'checkbox', 'label' => esc_html__( 'Open language switcher only on click', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Open the language switcher shortcode by clicking on it instead of hovering.<br> Close it by clicking on it, anywhere else on the screen or by pressing the escape key. This will affect only the shortcode language switcher.', 'translatepress-multilingual' ), array( 'br' => array()) ), 'id' => 'miscellaneous_options', 'container' => 'language_switcher' ); return $settings_array; } function trp_lsclick_enqueue_scriptandstyle() { wp_enqueue_script('trp-clickable-ls-js', TRP_PLUGIN_URL . 'assets/js/trp-clickable-ls.js', array('jquery'), TRP_PLUGIN_VERSION, true ); wp_add_inline_style('trp-language-switcher-style', '.trp_language_switcher_shortcode .trp-language-switcher .trp-ls-shortcode-current-language.trp-ls-clicked{ visibility: hidden; } .trp_language_switcher_shortcode .trp-language-switcher:hover div.trp-ls-shortcode-current-language{ visibility: visible; } .trp_language_switcher_shortcode .trp-language-switcher:hover div.trp-ls-shortcode-language{ visibility: hidden; height: 1px; } .trp_language_switcher_shortcode .trp-language-switcher .trp-ls-shortcode-language.trp-ls-clicked, .trp_language_switcher_shortcode .trp-language-switcher:hover .trp-ls-shortcode-language.trp-ls-clicked{ visibility:visible; height:auto; position: absolute; left: 0; top: 0; display: inline-block !important; }'); } function trp_open_language_switcher_on_click(){ $option = get_option( 'trp_advanced_settings', true ); if(isset($option['open_language_switcher_shortcode_on_click']) && $option['open_language_switcher_shortcode_on_click'] !== 'no'){ add_action( 'wp_enqueue_scripts', 'trp_lsclick_enqueue_scriptandstyle', 99 ); } } trp_open_language_switcher_on_click(); includes/advanced-settings/load-legacy-seo-pack.php 0000777 00000002557 15251156640 0016374 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_load_legacy_seo_pack', 90 ); function trp_register_load_legacy_seo_pack( $settings_array ){ // only add this if seo pack is active $add_ons_settings = get_option( 'trp_add_ons_settings', array() ); if( isset( $add_ons_settings['tp-add-on-seo-pack/tp-seo-pack.php'] ) && $add_ons_settings['tp-add-on-seo-pack/tp-seo-pack.php'] ){ $settings_array[] = array( 'name' => 'load_legacy_seo_pack', 'type' => 'checkbox', 'label' => esc_html__( 'Load legacy SEO Pack Add-On', 'translatepress-multilingual' ), //[utm41] 'description' => wp_kses( __( 'In case the recent migration to the new slug rewrite is causing trouble, set this to Yes to use the old method <br> Please <a href="https://translatepress.com/support/open-ticket/?utm_source=tp-advanced&utm_medium=client-site&utm_campaign=troubleshooting" target="_blank">open a support ticket</a> letting us know of the issues you are having.', 'translatepress-multilingual' ), array( 'br' => array(), 'a' => array( 'href' => array(), 'target' => array() ) ) ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ); } return $settings_array; } includes/advanced-settings/show-dynamic-content-before-translation.php 0000777 00000002472 15251156640 0022355 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_show_dynamic_content_before_translation', 20 ); function trp_register_show_dynamic_content_before_translation( $settings_array ){ $settings_array[] = array( 'name' => 'show_dynamic_content_before_translation', 'type' => 'checkbox', 'label' => esc_html__( 'Fix missing dynamic content', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'May help fix missing content inserted using JavaScript. <br> It shows dynamically inserted content in original language for a moment before the translation request is finished.', 'translatepress-multilingual' ), array( 'br' => array()) ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ); return $settings_array; } /** * Apply "show dynamic content before translation" fix only on front page */ add_filter( 'trp_show_dynamic_content_before_translation', 'trp_show_dynamic_content_before_translation' ); function trp_show_dynamic_content_before_translation( $allow ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['show_dynamic_content_before_translation'] ) && $option['show_dynamic_content_before_translation'] === 'yes' ){ return true; } return $allow; } includes/advanced-settings/enable-numerals-translation.php 0000777 00000001253 15251156640 0020111 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter('trp_register_advanced_settings', 'trp_register_enable_numerals_translation', 1081); function trp_register_enable_numerals_translation($settings_array) { $settings_array[] = array( 'name' => 'enable_numerals_translation', 'type' => 'checkbox', 'label' => esc_html__('Translate numbers and numerals', 'translatepress-multilingual'), 'description' => esc_html__('Enable translation of numbers ( e.g. phone numbers)', 'translatepress-multilingual'), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } includes/advanced-settings/exclude-dynamic-selectors.php 0000777 00000003005 15251156640 0017554 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_skip_dynamic_selectors', 110 ); function trp_register_skip_dynamic_selectors( $settings_array ){ $settings_array[] = array( 'name' => 'skip_dynamic_selectors', 'type' => 'list_input', 'columns' => array( 'selector' => __('Selector', 'translatepress-multilingual' ), ), 'label' => esc_html__( 'Exclude from dynamic translation', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Do not dynamically translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being translated using JavaScript.<br/>These strings will still be translated on the server side if possible.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'exclude_strings', 'container' => 'exclude_dynamic_strings' ); return $settings_array; } add_filter( 'trp_skip_selectors_from_dynamic_translation', 'trp_skip_dynamic_translation_for_selectors' ); function trp_skip_dynamic_translation_for_selectors( $skip_selectors ){ $option = get_option( 'trp_advanced_settings', true ); $add_skip_selectors = array( ); if ( isset( $option['skip_dynamic_selectors'] ) && is_array( $option['skip_dynamic_selectors']['selector'] ) ) { $add_skip_selectors = $option['skip_dynamic_selectors']['selector']; } return array_merge( $skip_selectors, $add_skip_selectors ); } includes/advanced-settings/remove-duplicates-from-db.php 0000777 00000002261 15251156640 0017457 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_remove_duplicate_entries_from_db', 530 ); function trp_register_remove_duplicate_entries_from_db( $settings_array ){ $settings_array[] = array( 'name' => 'remove_duplicate_entries_from_db', 'type' => 'text', 'label' => esc_html__( 'Optimize TranslatePress database tables', 'translatepress-multilingual' ), 'description' => wp_kses_post( sprintf( __( '<a href="%s">Click here</a> to access the database optimization tool.', 'translatepress-multilingual' ), admin_url('admin.php?page=trp_remove_duplicate_rows') ) ) . '<br>' . esc_html__('It helps remove possible duplicate translations, clear unnecessary data and repair possible metadata issues.','translatepress-multilingual') . '<br>' . wp_kses_post(sprintf( __( '<a href="%s" target="_blank">Here</a> you can observe the last 5 SQL errors relevant to TranslatePress if they exist.', 'translatepress-multilingual' ), admin_url('admin.php?page=trp_error_manager') ) ), 'id' => 'debug', 'container' => 'debug' ); return $settings_array; } includes/advanced-settings/fix-broken-html.php 0000777 00000001655 15251156640 0015517 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); function trp_register_fix_broken_html( $settings_array ){ $settings_array[] = array( 'name' => 'fix_broken_html', 'type' => 'checkbox', 'label' => esc_html__( 'Fix broken HTML', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'General attempt to fix broken or missing HTML on translated pages.<br/>', 'translatepress-multilingual' ), array( 'br' => array(), 'strong' => array() ) ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ); return $settings_array; } add_filter('trp_try_fixing_invalid_html', 'trp_fix_broken_html'); function trp_fix_broken_html($allow) { $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['fix_broken_html'] ) && $option['fix_broken_html'] === 'yes' ) { return true; } return $allow; } includes/advanced-settings/exclude-selectors.php 0000777 00000003020 15251156640 0016127 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_exclude_selectors', 110 ); function trp_register_exclude_selectors( $settings_array ){ $settings_array[] = array( 'name' => 'exclude_translate_selectors', 'type' => 'list_input', 'columns' => array( 'selector' => __('Selector', 'translatepress-multilingual' ), ), 'label' => esc_html__( 'Exclude selectors from translation', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Do not translate strings that are found in html nodes matching these selectors.<br>Excludes all the children of HTML nodes matching these selectors from being translated.<br>These strings cannot be translated manually nor automatically.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'exclude_strings', 'container' => 'exclude_selectors' ); return $settings_array; } add_filter( 'trp_no_translate_selectors', 'trp_skip_translation_for_selectors' ); function trp_skip_translation_for_selectors( $skip_selectors ){ $option = get_option( 'trp_advanced_settings', true ); $add_skip_selectors = array( ); if ( isset( $option['exclude_translate_selectors'] ) && is_array( $option['exclude_translate_selectors']['selector'] ) ) { $add_skip_selectors = $option['exclude_translate_selectors']['selector']; } return array_merge( $skip_selectors, $add_skip_selectors ); } includes/advanced-settings/disable-gettext-strings.php 0000777 00000012177 15251156640 0017266 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_translation_for_gettext_strings', 523 ); function trp_translation_for_gettext_strings( $settings_array ){ $settings_array[] = array( 'name' => 'disable_translation_for_gettext_strings', 'type' => 'checkbox', 'label' => esc_html__( 'Disable translation for gettext strings', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Gettext Strings are strings outputted by themes and plugins. <br> Translating these types of strings through TranslatePress can be unnecessary if they are already translated using the .po/.mo translation file system.<br>Enabling this option can improve the page load performance of your site in certain cases. The disadvantage is that you can no longer edit gettext translations using TranslatePress, nor benefit from automatic translation on these strings.', 'translatepress-multilingual' ), array( 'br' => array()) ), 'id' => 'debug', 'container' => 'debug' ); return $settings_array; } add_action( 'trp_before_running_hooks', 'trp_remove_hooks_to_disable_gettext_translation', 10, 1); function trp_remove_hooks_to_disable_gettext_translation( $trp_loader ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['disable_translation_for_gettext_strings'] ) && $option['disable_translation_for_gettext_strings'] === 'yes' ) { $trp = TRP_Translate_Press::get_trp_instance(); $gettext_manager = $trp->get_component( 'gettext_manager' ); $trp_loader->remove_hook( 'init', 'create_gettext_translated_global', $gettext_manager ); $trp_loader->remove_hook( 'shutdown', 'machine_translate_gettext', $gettext_manager ); } } add_filter( 'trp_skip_gettext_querying', 'trp_skip_gettext_querying', 10, 4 ); function trp_skip_gettext_querying( $skip, $translation, $text, $domain ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['disable_translation_for_gettext_strings'] ) && $option['disable_translation_for_gettext_strings'] === 'yes' ) { return true; } return $skip; } add_action( 'trp_editor_notices', 'display_message_for_disable_gettext_in_editor', 10, 1 ); function display_message_for_disable_gettext_in_editor( $trp_editor_notices ) { $option = get_option( 'trp_advanced_settings', true ); // Skip if user dismissed it if ( get_user_meta( get_current_user_id(), '_trp_dismissed_gettext_notice', true ) ) { return $trp_editor_notices; } if ( isset( $option['disable_translation_for_gettext_strings'] ) && $option['disable_translation_for_gettext_strings'] === 'yes' ) { $url = add_query_arg( array( 'page' => 'trp_advanced_page#debug_options', ), site_url('wp-admin/admin.php') ); $ajax_url = admin_url( 'admin-ajax.php' ); $html = "<div id='trp-gettext-notice' class='trp-notice trp-notice-warning'>"; $html .= '<p><strong>' . esc_html__( 'Gettext Strings translation is disabled', 'translatepress-multilingual' ) . '</strong></p>'; $html .= '<p>' . esc_html__( 'To enable it go to ', 'translatepress-multilingual' ) . '<a class="trp-link-primary" target="_blank" href="' . esc_url( $url ) . '">' . esc_html__( 'TranslatePress->Advanced Settings->Debug->Disable translation for gettext strings', 'translatepress-multilingual' ) . '</a>' . esc_html__(' and uncheck the Checkbox.', 'translatepress-multilingual') .'</p>'; // Custom dismiss link $html .= '<a href="#" id="trp-dismiss-gettext-notice" class="trp-button-primary">'. esc_html__('Dismiss', 'translatepress-multilingual') .'</a>'; $dismiss_nonce = wp_create_nonce( 'trp_dismiss_gettext_notice' ); // Inline JS with ajax URL hardcoded $html .= "<script> document.addEventListener('DOMContentLoaded', function(){ var btn = document.getElementById('trp-dismiss-gettext-notice'); if (btn) { btn.addEventListener('click', function(e){ e.preventDefault(); var notice = document.getElementById('trp-gettext-notice'); if (notice) notice.style.display = 'none'; var xhr = new XMLHttpRequest(); xhr.open('POST', '" . esc_url( $ajax_url ) . "', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('action=trp_dismiss_gettext_notice&security=" . esc_js( $dismiss_nonce ) . "'); }); } }); </script>"; $html .= '</div>'; $trp_editor_notices = $html; } return $trp_editor_notices; } // Handle AJAX dismiss add_action( 'wp_ajax_trp_dismiss_gettext_notice', function() { check_ajax_referer( 'trp_dismiss_gettext_notice', 'security' ); if ( current_user_can( 'edit_posts' ) ) { update_user_meta( get_current_user_id(), '_trp_dismissed_gettext_notice', true ); } wp_die(); }); includes/advanced-settings/force-slash-at-end-of-links.php 0000777 00000001273 15251156640 0017601 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter('trp_register_advanced_settings', 'trp_register_force_slash_in_home_url', 1071); function trp_register_force_slash_in_home_url($settings_array) { $settings_array[] = array( 'name' => 'force_slash_at_end_of_links', 'type' => 'checkbox', 'label' => esc_html__('Force slash at end of home url:', 'translatepress-multilingual'), 'description' => wp_kses(__('Ads a slash at the end of the home_url() function', 'translatepress-multilingual'), array('br' => array())), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } includes/advanced-settings/opposite-flag-shortcode.php 0000777 00000011621 15251156640 0017244 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_show_opposite_flag_language_switcher_shortcode', 1250 ); function trp_show_opposite_flag_language_switcher_shortcode( $settings_array ){ $settings_array[] = array( 'name' => 'show_opposite_flag_language_switcher_shortcode', 'type' => 'checkbox', 'label' => esc_html__( 'Show opposite language in the language switcher', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Transforms the language switcher into a button showing the other available language, not the current one.<br> Only works when there are exactly two languages, the default one and a translation one.<br>This will affect the shortcode language switcher and floating language switcher as well.<br> To achieve this in menu language switcher go to Appearance->Menus->Language Switcher and select Opposite Language.', 'translatepress-multilingual' ), array( 'br' => array()) ), 'id' => 'miscellaneous_options', 'container' => 'language_switcher' ); return $settings_array; } function trp_opposite_ls_current_language( $current_language, $published_languages, $TRP_LANGUAGE, $settings ){ if ( count ( $published_languages ) == 2 ) { foreach ($published_languages as $code => $name) { if ($code != $TRP_LANGUAGE) { $current_language['code'] = $code; $current_language['name'] = $name; break; } } } return $current_language; } function trp_opposite_ls_other_language( $other_language, $published_languages, $TRP_LANGUAGE, $settings ){ if ( count ( $published_languages ) == 2 ) { $other_language = array(); foreach ($published_languages as $code => $name) { if ($code != $TRP_LANGUAGE) { $other_language[$code] = $name; break; } } } return $other_language; } function trp_opposite_ls_hide_disabled_language($return, $current_language, $current_language_preference, $settings){ if ( count( $settings['publish-languages'] ) == 2 ){ return false; } return $return; } function trp_enqueue_language_switcher_shortcode_scripts(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_languages = $trp->get_component( 'languages' ); $trp_settings = $trp->get_component( 'settings' ); $published_languages = $trp_languages->get_language_names( $trp_settings->get_settings()['publish-languages'] ); if(count ( $published_languages ) == 2 ) { wp_add_inline_style( 'trp-language-switcher-style', '.trp-language-switcher > div { padding: 3px 5px 3px 5px; background-image: none; text-align: center;}' ); } } function trp_opposite_ls_floating_current_language($current_language, $published_languages, $TRP_LANGUAGE, $settings){ if ( count ( $published_languages ) == 2 ) { foreach ($published_languages as $code => $name) { if ($code != $TRP_LANGUAGE) { $current_language['code'] = $code; $current_language['name'] = $name; break; } } } return $current_language; } function trp_opposite_ls_floating_other_language( $other_language, $published_languages, $TRP_LANGUAGE, $settings ){ if ( count ( $published_languages ) == 2 ) { $other_language = array(); foreach ($published_languages as $code => $name) { if ($code != $TRP_LANGUAGE) { $other_language[$code] = $name; break; } } } return $other_language; } function trp_opposite_ls_floating_hide_disabled_language($return, $current_language, $settings){ if ( count( $settings['publish-languages'] ) == 2 ){ return false; } return $return; } function trp_show_opposite_flag_settings(){ $option = get_option( 'trp_advanced_settings', true ); if(isset($option['show_opposite_flag_language_switcher_shortcode']) && $option['show_opposite_flag_language_switcher_shortcode'] !== 'no'){ add_filter( 'trp_ls_shortcode_current_language', 'trp_opposite_ls_current_language', 10, 4 ); add_filter( 'trp_ls_shortcode_other_languages', 'trp_opposite_ls_other_language', 10, 4 ); add_filter( 'trp_ls_shortcode_show_disabled_language', 'trp_opposite_ls_hide_disabled_language', 10, 4 ); add_action( 'wp_enqueue_scripts', 'trp_enqueue_language_switcher_shortcode_scripts', 20 ); add_action('trp_ls_floating_current_language', 'trp_opposite_ls_floating_current_language', 10, 4); add_action('trp_ls_floating_other_languages', 'trp_opposite_ls_floating_other_language', 10, 4); add_action('trp_ls_floater_show_disabled_language', 'trp_opposite_ls_floating_hide_disabled_language', 10, 3 ); } } trp_show_opposite_flag_settings(); includes/advanced-settings/exclude-words-from-auto-translate.php 0000777 00000003021 15251156640 0021165 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_exclude_words_from_auto_translate', 100 ); function trp_register_exclude_words_from_auto_translate( $settings_array ){ $settings_array[] = array( 'name' => 'exclude_words_from_auto_translate', 'type' => 'list_input', 'columns' => array( 'words' => __('String', 'translatepress-multilingual' ), ), 'label' => esc_html__( 'Exclude strings from automatic translation', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Do not automatically translate these strings (ex. names, technical words...)<br>Paragraphs containing these strings will still be translated except for the specified part.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'exclude_strings', 'container' => 'exclude_at_strings' ); return $settings_array; } add_filter( 'trp_exclude_words_from_automatic_translation', 'trp_exclude_words_from_auto_translate' ); function trp_exclude_words_from_auto_translate( $exclude_words ){ $option = get_option( 'trp_advanced_settings', true ); $add_skip_selectors = array( ); if ( isset( $option['exclude_words_from_auto_translate'] ) && is_array( $option['exclude_words_from_auto_translate']['words'] ) ) { $exclude_words = array_merge( $exclude_words, $option['exclude_words_from_auto_translate']['words'] ); } return $exclude_words; } includes/advanced-settings/strip-gettext-post-content.php 0000777 00000003200 15251156640 0017753 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_strip_gettext_post_content', 60 ); function trp_register_strip_gettext_post_content( $settings_array ){ $settings_array[] = array( 'name' => 'strip_gettext_post_content', 'type' => 'checkbox', 'label' => esc_html__( 'Filter Gettext wrapping from post content and title', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Filters gettext wrapping such as #!trpst#trp-gettext from all updated post content and post title. Does not affect previous post content. <br/><strong>Database backup is recommended before switching on.</strong>', 'translatepress-multilingual' ), array( 'br' => array(), 'strong' => array()) ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ); return $settings_array; } /** * Strip gettext wrapping from post title and content. * They will be regular strings, written in the language they were submitted. * Filter called both for wp_insert_post and wp_update_post */ add_filter('wp_insert_post_data', 'trp_filter_trpgettext_from_post_content', 10, 2 ); function trp_filter_trpgettext_from_post_content($data, $postarr ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['strip_gettext_post_content'] ) && $option['strip_gettext_post_content'] === 'yes' && class_exists( 'TRP_Translation_Manager' ) ){ $data['post_content'] = TRP_Translation_Manager::strip_gettext_tags($data['post_content']); $data['post_title'] = TRP_Translation_Manager::strip_gettext_tags($data['post_title']); } return $data; } includes/advanced-settings/load-legacy-language-switcher.php 0000777 00000001766 15251156640 0020304 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_load_legacy_language_switcher', 90 ); function trp_register_load_legacy_language_switcher( $settings_array ){ $settings_array[] = array( 'name' => 'load_legacy_language_switcher', 'type' => 'checkbox', 'label' => esc_html__( 'Load legacy Language Switcher', 'translatepress-multilingual' ), 'description' => esc_html__( 'Applies to all types of language switchers (floating, shortcode, and menu). When enabled, the site will revert to using the original Language Switcher configured in the General Settings tab, replacing the new customizable version. Your existing switcher settings will remain saved, but they will be ignored while this option is active.', 'translatepress-multilingual' ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ); return $settings_array; } includes/advanced-settings/disable-dynamic-translation.php 0000777 00000002724 15251156640 0020070 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_disable_dynamic_translation', 30 ); function trp_register_disable_dynamic_translation( $settings_array ){ $settings_array[] = array( 'name' => 'disable_dynamic_translation', 'type' => 'checkbox', 'label' => esc_html__( 'Disable dynamic translation', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'It disables detection of strings displayed dynamically using JavaScript. <br/>Strings loaded via a server side AJAX call will still be translated.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ); return $settings_array; } add_filter( 'trp_enable_dynamic_translation', 'trp_adst_disable_dynamic' ); function trp_adst_disable_dynamic( $enable ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['disable_dynamic_translation'] ) && $option['disable_dynamic_translation'] === 'yes' ){ return false; } return $enable; } add_filter( 'trp_editor_missing_scripts_and_styles', 'trp_adst_disable_dynamic2' ); function trp_adst_disable_dynamic2( $scripts ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['disable_dynamic_translation'] ) && $option['disable_dynamic_translation'] === 'yes' ){ unset($scripts['trp-translate-dom-changes.js']); } return $scripts; } includes/advanced-settings/html-lang-remove-locale.php 0000777 00000003677 15251156640 0017132 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_html_lang_attribute', 1001 ); function trp_register_html_lang_attribute( $settings_array ){ $settings_array[] = array( 'name' => 'html_lang_remove_locale', 'type' => 'radio', 'options' => array( 'default', 'regional' ), 'default' => 'default', 'labels' => array( esc_html__( 'Default (example: en-US, fr-CA, etc.)', 'translatepress-multilingual' ), esc_html__( 'Regional (example: en, fr, es, etc.)', 'translatepress-multilingual' ) ), 'label' => esc_html__( 'HTML Lang Attribute Format', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Change lang attribute of the html tag to a format that includes country regional or not. <br>In HTML, the lang attribute (<html lang="en-US">) should be used to specify the language of text content so that the browser can correctly display or process your content (eg. for hyphenation, styling, spell checking, etc).', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } add_filter( 'trp_add_default_lang_tags', 'trp_display_default_lang_tag' ); function trp_display_default_lang_tag( $display ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['html_lang_remove_locale'] ) && $option['html_lang_remove_locale'] === 'default' ) { return true; } return false; } add_filter( 'trp_add_regional_lang_tags', 'trp_display_regional_lang_tag' ); function trp_display_regional_lang_tag( $display ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['html_lang_remove_locale'] ) && $option['html_lang_remove_locale'] === 'regional' ) { return true; } return false; } includes/advanced-settings/disable-post-container-tags.php 0000777 00000005752 15251156640 0020015 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** Post title */ add_filter( 'trp_register_advanced_settings', 'trp_register_disable_post_container_tags_for_post_title', 510 ); function trp_register_disable_post_container_tags_for_post_title( $settings_array ){ $settings_array[] = array( 'name' => 'disable_post_container_tags_for_post_title', 'type' => 'checkbox', 'label' => esc_html__( 'Disable post container tags for post title', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'It disables search indexing the post title in translated languages.<br/>Useful when the title of the post doesn\'t allow HTML thus breaking the page.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'debug', 'container' => 'debug' ); return $settings_array; } add_filter( 'trp_before_running_hooks', 'trp_remove_hooks_to_disable_post_title_search_wraps' ); function trp_remove_hooks_to_disable_post_title_search_wraps( $trp_loader ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['disable_post_container_tags_for_post_title'] ) && $option['disable_post_container_tags_for_post_title'] === 'yes' ) { $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); $trp_loader->remove_hook( 'the_title', 'wrap_with_post_id', $translation_render ); } } /** Post content */ add_filter( 'trp_register_advanced_settings', 'trp_register_disable_post_container_tags_for_post_content', 520 ); function trp_register_disable_post_container_tags_for_post_content( $settings_array ){ $settings_array[] = array( 'name' => 'disable_post_container_tags_for_post_content', 'type' => 'checkbox', 'label' => esc_html__( 'Disable post container tags for post content', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'It disables search indexing the post content in translated languages.<br/>Useful when the content of the post doesn\'t allow HTML thus breaking the page.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'id' => 'debug', 'container' => 'debug' ); return $settings_array; } add_filter( 'trp_before_running_hooks', 'trp_remove_hooks_to_disable_post_content_search_wraps' ); function trp_remove_hooks_to_disable_post_content_search_wraps( $trp_loader ){ $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['disable_post_container_tags_for_post_content'] ) && $option['disable_post_container_tags_for_post_content'] === 'yes' ) { $trp = TRP_Translate_Press::get_trp_instance(); $translation_render = $trp->get_component( 'translation_render' ); $trp_loader->remove_hook( 'the_content', 'wrap_with_post_id', $translation_render ); remove_action( 'do_shortcode_tag', 'tp_oxygen_search_compatibility', 10, 4 ); } } includes/advanced-settings/do-not-translate-certain-paths.php 0000777 00000070537 15251156640 0020451 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_do_not_translate_certain_paths', 1 ); function trp_register_do_not_translate_certain_paths( $settings_array ){ $settings_array[] = array( 'type' => 'custom', 'name' => 'translateable_content', 'rows' => array( 'option' => 'radio', 'paths' => 'textarea' ), 'label' => esc_html__( 'Do not translate certain paths', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Choose what paths can be translated. Supports wildcard at the end of the path.<br>For example, to exclude https://example.com/some/path you can either use the rule /some/path/ or /some/*.<br>Enter each rule on it\'s own line. To exclude the home page use {{home}}.', 'translatepress-multilingual' ), array( 'br' => array() )), 'id' => 'exclude_strings', 'container' => 'exclude_paths' ); return $settings_array; } add_filter( 'trp_advanced_setting_custom_translateable_content', 'trp_output_do_not_translate_certain_paths' ); function trp_output_do_not_translate_certain_paths( $setting ){ $trp_settings = ( new TRP_Settings() )->get_settings(); ?> <div id="trp-adv-translate-certain-paths" class="trp_advanced_flex_box"> <div class='trp-settings-options__wrapper'> <span class="trp-description-text"><?php echo wp_kses_post( $setting['description'] ); ?></span> <div class="trp-radio__wrapper"> <label class="trp-primary-text"> <input type='radio' name="trp_advanced_settings[<?php echo esc_attr( $setting['name'] ); ?>][option]" value="exclude" <?php echo isset( $trp_settings['trp_advanced_settings'][$setting['name']]['option'] ) && $trp_settings['trp_advanced_settings'][$setting['name']]['option'] == 'exclude' ? 'checked' : ''; ?>> <?php esc_html_e( 'Exclude Paths From Translation', 'translatepress-multilingual' ); ?> </label> <label class="trp-primary-text"> <input type='radio' name="trp_advanced_settings[<?php echo esc_attr( $setting['name'] ); ?>][option]" value="include" <?php echo isset( $trp_settings['trp_advanced_settings'][$setting['name']]['option'] ) && $trp_settings['trp_advanced_settings'][$setting['name']]['option'] == 'include' ? 'checked' : ''; ?> > <?php esc_html_e( 'Translate Only Certain Paths', 'translatepress-multilingual' ); ?> </label> </div> <textarea class="trp-textarea-big" name="trp_advanced_settings[<?php echo esc_attr( $setting['name'] ); ?>][paths]"><?php echo isset( $trp_settings['trp_advanced_settings'][$setting['name']]['paths'] ) ? esc_textarea( $trp_settings['trp_advanced_settings'][$setting['name']]['paths'] ) : ''; ?></textarea> </div> </div> <?php } function trp_test_current_slug( &$current_slug, &$array_slugs ) { $current_slug = trim($current_slug, "/"); // Explode get params $current_slug = explode( '?', $current_slug ); $settings = get_option( 'trp_settings', false ); $default_lang_slug_if_subdir_on_default = false; // If get params then store in $current_slug the part thats important to us if( isset( $current_slug[1] ) ){ $current_get = $current_slug[1]; $current_slug = $current_slug[0]; } else { $current_slug = $current_slug[0]; } // we need to check the default language slug as well for {{home)) if the "Use a subdirectory for the default language" is checked if ( isset( $settings['add-subdirectory-to-default-language'] ) && $settings['add-subdirectory-to-default-language'] == 'yes' ){ $default_lang_slug_if_subdir_on_default = $settings['url-slugs'][$settings['default-language']]; } // Test if current slug should be home. If not then split the slug on "/" and save the individual strings in $array_slugs if( empty( $current_slug ) || $current_slug == '/' || $current_slug == '' || ( !empty( $default_lang_slug_if_subdir_on_default ) && $current_slug == $default_lang_slug_if_subdir_on_default )){ $array_slugs[0] = "{{home}}"; $current_slug = "{{home}}"; } else { $array_slugs = explode( "/", $current_slug ); } } function trp_return_exclude_include_url($paths, $current_slug, $array_slugs) { // $paths contains all the paths set in the advance tab foreach( $paths as $path ) { if ( !empty( $path ) ) { $path = trim( $path, "/" ); // If $current_path is exactly $path and $path doesn't contain "/*" if ( ( untrailingslashit( $current_slug ) == untrailingslashit( $path ) || strcmp( $current_slug, $path ) == 0 ) && strpos( $path, '*' ) == false ) return true; // Elseif $current path contains "/*" elseif ( strpos( $path, '*' ) !== false ) { $path = str_replace( '/*', '', $path ); // $array_paths contains each part of $path split on "/" $array_paths = explode( "/", $path ); // If $current_slug has more values than $path if ( count( $array_slugs ) > count( $array_paths ) ) { $compare_slugs = true; // Comparing each value from $array_paths and $array_slugs in the same order foreach ( $array_paths as $key => $array_path ) { // Testing if the values are different if ( strcmp( $array_slugs[ $key ], $array_path ) !== 0 ) $compare_slugs = false; } // If all the values are identical if ( $compare_slugs === true ) return true; } } } } } // Prevent TranslatePress from loading on excluded pages add_action( 'trp_allow_tp_to_run', 'trp_exclude_include_paths_to_run_on', 2 ); function trp_exclude_include_paths_to_run_on(){ if( is_admin() ) return true; if( isset( $_GET['trp-edit-translation'] ) && ( $_GET['trp-edit-translation'] == 'true' || $_GET['trp-edit-translation'] == 'preview' ) ) return true; if( isset( $_GET['trp-string-translation'] ) && $_GET['trp-string-translation'] == 'true' ) return true; $settings = get_option( 'trp_settings', false ); $advanced_settings = get_option( 'trp_advanced_settings', false ); if( empty( $advanced_settings ) || !isset( $advanced_settings['translateable_content'] ) || !isset( $advanced_settings['translateable_content']['option'] ) || empty( $advanced_settings['translateable_content']['paths'] ) ) return true; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); $current_lang = $url_converter->get_lang_from_url_string( $url_converter->cur_page_url() ); if( empty( $current_lang ) ) $current_lang = $settings['default-language']; if ( $url_converter->is_sitemap_path() ) return true; // Skip checks if this is not the default language if( !empty( $current_lang ) && $settings['default-language'] != $current_lang ) return true; $paths = trp_dntcp_get_paths(); $site_url_components = parse_url( get_home_url() ); $current_slug = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( $_SERVER['REQUEST_URI'] ) : ''; if ( isset( $site_url_components['path'] ) ) { // remove site_url path from $current_slug being taken into account for subdirectories like http://localhost/wordpress $current_slug = str_replace( trim( $site_url_components['path'] ), '', $current_slug ); } $replace = '\/'; if( isset( $settings['add-subdirectory-to-default-language'] ) && $settings['add-subdirectory-to-default-language'] == 'yes' ) { $replace .= $settings['url-slugs'][ $current_lang ]; $current_slug = preg_replace( "/$replace/i", '', ltrim( $current_slug, '/' ), 1); } // $array_slugs contains each part of $curent_slug split on "/" $array_slugs = array(); trp_test_current_slug($current_slug, $array_slugs ); if( $advanced_settings['translateable_content']['option'] == 'exclude' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return false; } else if( $advanced_settings['translateable_content']['option'] == 'include' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return true; return false; } return true; } add_filter( 'trp_allow_language_redirect', 'trp_exclude_include_do_not_redirect_on_excluded_pages', 20, 3 ); function trp_exclude_include_do_not_redirect_on_excluded_pages( $redirect, $language, $url ){ if( isset( $_GET['trp-edit-translation'] ) && ( $_GET['trp-edit-translation'] == 'true' || $_GET['trp-edit-translation'] == 'preview' ) ) return $redirect; if( isset( $_GET['trp-string-translation'] ) && $_GET['trp-string-translation'] == 'true' ) return $redirect; $settings = get_option( 'trp_settings', false ); $advanced_settings = get_option( 'trp_advanced_settings', false ); if( empty( $advanced_settings ) || !isset( $advanced_settings['translateable_content'] ) || !isset( $advanced_settings['translateable_content']['option'] ) || empty( $advanced_settings['translateable_content']['paths'] ) ) return $redirect; if( empty( $language ) || $language != $settings['default-language'] ) return $redirect; $replace = trailingslashit( home_url() ); $current_slug = str_replace( $replace, '', trailingslashit( $url ) ); $paths = trp_dntcp_get_paths(); // $array_slugs contains each part of $curent_slug split on "/" $array_slugs = array(); trp_test_current_slug($current_slug, $array_slugs ); if( $advanced_settings['translateable_content']['option'] == 'exclude' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return false; } else if( $advanced_settings['translateable_content']['option'] == 'include' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return $redirect; return false; } return $redirect; } /** * The function verifies if we are on an excluded path and automatically redirects to the default language in that case. * The function '$url_converter->get_url_for_language( $settings['default-language'], null, '' )' is needed in the case we are on a page with a different * language code then the default and the path is the one excluded, so we need to get the correct url in the default language. * * Redirects to the excluded page in the default language. */ add_action( 'template_redirect', 'trp_exclude_include_redirect_to_default_language', 1 ); function trp_exclude_include_redirect_to_default_language(){ if( isset( $_GET['trp-edit-translation'] ) && ( $_GET['trp-edit-translation'] == 'true' || $_GET['trp-edit-translation'] == 'preview' ) ) return; if( isset( $_GET['trp-string-translation'] ) && $_GET['trp-string-translation'] == 'true' ) return; if( is_admin() ) return; // On mapped domains, trp_dntcp_redirect_excluded_paths_on_mapped_domains() handles redirects // This function's URL building logic doesn't work correctly with Multiple Domains if ( function_exists( 'trp_dntcp_is_on_mapped_domain' ) && trp_dntcp_is_on_mapped_domain() ) return; $settings = get_option( 'trp_settings', false ); $advanced_settings = get_option( 'trp_advanced_settings', false ); if( empty( $advanced_settings ) || !isset( $advanced_settings['translateable_content'] ) || !isset( $advanced_settings['translateable_content']['option'] ) || empty( $advanced_settings['translateable_content']['paths'] ) ) return; global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); $current_original_url = $url_converter->get_url_for_language( $settings['default-language'], null, '' ); // Attempt to redirect on default language only if the current URL contains the language if( !isset( $TRP_LANGUAGE ) || $settings['default-language'] == $TRP_LANGUAGE ){ $language = $url_converter->get_lang_from_url_string( $current_original_url ); if( $language === null ) return; } $absolute_home = $url_converter->get_abs_home(); $path_no_domain = trp_remove_prefix($absolute_home, $current_original_url ); // Take into account the subdirectory for default language option if ( isset( $settings['add-subdirectory-to-default-language'] ) && $settings['add-subdirectory-to-default-language'] == 'yes' ) { $absolute_home_with_lang = trailingslashit( $absolute_home ) . $settings['url-slugs'][ $settings['default-language'] ]; }else{ $absolute_home_with_lang = $absolute_home; } $current_slug = str_replace( $absolute_home_with_lang, '', untrailingslashit( $current_original_url ) ); $paths = trp_dntcp_get_paths(); // Remove language from this URL if present $searchText = '\/' . $settings['url-slugs'][$settings['default-language']]; $path_no_domain = preg_replace( "/$searchText/i", '' , $path_no_domain, 1 ); $current_original_url = $absolute_home . $path_no_domain; // $array_slugs contains each part of $curent_slug split on "/" $array_slugs = array(); trp_test_current_slug($current_slug, $array_slugs ); if( $advanced_settings['translateable_content']['option'] == 'exclude' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) if( $url_converter->cur_page_url() != $current_original_url ){ $status = apply_filters( 'trp_redirect_status', 301, 'redirect_to_default_language_because_link_is_excluded_from_translation' ); wp_redirect( $current_original_url, $status ); exit; } } else if( $advanced_settings['translateable_content']['option'] == 'include' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return; if( $url_converter->cur_page_url() != $current_original_url ){ $status = apply_filters( 'trp_redirect_status', 301, 'redirect_to_default_language_because_link_is_excluded_from_translation' ); wp_redirect( $current_original_url, $status ); exit; } } } // only force custom links in paths that are translatable add_filter( 'trp_force_custom_links', 'trp_exclude_include_filter_custom_links', 10, 4); function trp_exclude_include_filter_custom_links( $new_url, $url, $TRP_LANGUAGE, $a_href ){ if( isset( $_GET['trp-edit-translation'] ) && ( $_GET['trp-edit-translation'] == 'true' || $_GET['trp-edit-translation'] == 'preview' ) ) return $new_url; if( isset( $_GET['trp-string-translation'] ) && $_GET['trp-string-translation'] == 'true' ) return $new_url; $advanced_settings = get_option( 'trp_advanced_settings', false ); $settings = get_option( 'trp_settings', false ); if( empty( $advanced_settings ) || !isset( $advanced_settings['translateable_content'] ) || !isset( $advanced_settings['translateable_content']['option'] ) || empty( $advanced_settings['translateable_content']['paths'] ) ) return $new_url; global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); if( !isset( $TRP_LANGUAGE ) || $settings['default-language'] == $TRP_LANGUAGE ) return $new_url; $current_original_url = $url_converter->get_url_for_language( $settings['default-language'], $new_url, '' ); // Remove language from this URL if present $current_original_url = str_replace( '/' . $settings['url-slugs'][$settings['default-language']], '', $current_original_url ); $absolute_home = $url_converter->get_abs_home(); $current_slug = str_replace( $absolute_home, '', untrailingslashit( $current_original_url ) ); $paths = trp_dntcp_get_paths(); // $array_slugs contains each part of $curent_slug split on "/" $array_slugs = array(); trp_test_current_slug($current_slug, $array_slugs ); if( $advanced_settings['translateable_content']['option'] == 'exclude' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return $current_original_url; } else if( $advanced_settings['translateable_content']['option'] == 'include' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return $new_url; return $current_original_url; } return $new_url; } add_action( 'init', 'trp_exclude_include_add_sitemap_filter' ); function trp_exclude_include_add_sitemap_filter(){ if (class_exists('TRP_IN_Seo_Pack')) add_filter( 'trp_xml_sitemap_output_for_url', 'trp_exclude_include_filter_sitemap_links', 10, 6 ); } function trp_exclude_include_filter_sitemap_links( $new_output, $output, $settings, $alternate, $all_lang_urls, $url ){ $advanced_settings = get_option( 'trp_advanced_settings', false ); $settings = get_option( 'trp_settings', false ); if( empty( $advanced_settings ) || !isset( $advanced_settings['translateable_content'] ) || !isset( $advanced_settings['translateable_content']['option'] ) || empty( $advanced_settings['translateable_content']['paths'] ) ) return $new_output; global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); if( empty( $url['loc'] ) ) return $new_output; $current_original_url = $url_converter->get_url_for_language( $settings['default-language'], $url['loc'], '' ); $absolute_home = $url_converter->get_abs_home(); $current_slug = str_replace( $absolute_home, '', untrailingslashit( $current_original_url ) ); $paths = trp_dntcp_get_paths(); // $array_slugs contains each part of $curent_slug split on "/" $array_slugs = array(); trp_test_current_slug($current_slug, $array_slugs ); if( $advanced_settings['translateable_content']['option'] == 'exclude' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return $output; } else if( $advanced_settings['translateable_content']['option'] == 'include' ){ if ( trp_return_exclude_include_url($paths, $current_slug, $array_slugs) ) return $new_output; return $output; } return $new_output; } /** * Get excluded/included paths. Transforms all urls from absolute to relative paths * Takes into account if there are links with default language subdirectory, otherwise redirect loop happens * * @return string[] */ function trp_dntcp_get_paths() { $settings = get_option( 'trp_settings', false ); $advanced_settings = get_option( 'trp_advanced_settings', false ); if ( empty( $advanced_settings['translateable_content']['paths'] ) ) return []; $paths = explode( "\n", str_replace( "\r", "", $advanced_settings['translateable_content']['paths'] ) ); add_filter('trp_home_url', 'trp_dntcp_get_abs_home_url', 10,2 ); $home_url_no_subdir = home_url(); remove_filter('trp_home_url', 'trp_dntcp_get_abs_home_url', 10 ); $home_urls = array(); if ( isset( $settings['add-subdirectory-to-default-language'] ) && $settings['add-subdirectory-to-default-language'] == 'yes' ) //order of home_urls[] items is important $home_urls[] = preg_quote( trailingslashit( $home_url_no_subdir ) . $settings['url-slugs'][ $settings['default-language'] ], '/' ); $home_urls[] = preg_quote( $home_url_no_subdir, '/' ); // remove absolute home from them if exists foreach ( $paths as &$path ) { foreach ( $home_urls as $home_url ) { $path = preg_replace( '/^' . $home_url . '/is', '', $path ); } } return $paths; } function trp_dntcp_get_abs_home_url($new_url, $abs_home){ return $abs_home; } add_filter( "trp_allow_machine_translation_for_url", 'trp_dntcp_exclude_links_from_automatic_translation', 10, 2); function trp_dntcp_exclude_links_from_automatic_translation( $excluded, $url_verification ){ $advanced_settings = get_option( 'trp_advanced_settings', false ); $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); $absolute_home = $url_converter->get_abs_home(); // Take into account the subdirectory for default language option if ( isset( $settings['add-subdirectory-to-default-language'] ) && $settings['add-subdirectory-to-default-language'] == 'yes' ) $absolute_home = trailingslashit( $absolute_home ) . $settings['url-slugs'][$settings['default-language']]; $current_slug = str_replace( $absolute_home, '', $url_verification ); $paths = trp_dntcp_get_paths(); $array_slugs = array(); trp_test_current_slug($current_slug, $array_slugs ); if( isset( $advanced_settings['translateable_content']['option']) && $advanced_settings['translateable_content']['option'] == 'exclude' ) { if ( trp_return_exclude_include_url( $paths, $current_slug, $array_slugs ) ) { return false; } } return $excluded; } /** * Check if the current URL is excluded from translation based on the * "Do not translate certain paths" / "Translate only certain paths" setting. * * Takes into account: * - site installed in subdirectory * - "Use a subdirectory for the default language" * - {{home}} mapping * - both "exclude" and "include" modes * * @return bool True if the current URL should be treated as excluded from translation. */ function trp_dntcp_is_current_url_excluded() { if ( is_admin() ) { return false; } $settings = get_option( 'trp_settings', false ); $advanced_settings = get_option( 'trp_advanced_settings', false ); // No configuration -> nothing is excluded. if ( empty( $advanced_settings ) || ! isset( $advanced_settings['translateable_content'] ) || empty( $advanced_settings['translateable_content']['paths'] ) || empty( $advanced_settings['translateable_content']['option'] ) ) { return false; } $mode = $advanced_settings['translateable_content']['option']; // 'exclude' or 'include' $paths = trp_dntcp_get_paths(); // Build current slug in the same way as trp_exclude_include_paths_to_run_on() $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $current_lang = $url_converter->get_lang_from_url_string( $url_converter->cur_page_url() ); if ( empty( $current_lang ) && isset( $settings['default-language'] ) ) { $current_lang = $settings['default-language']; } $site_url_components = parse_url( get_home_url() ); $current_slug = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( $_SERVER['REQUEST_URI'] ) : ''; // Remove site_url path for installs in subdirectories (e.g. http://localhost/wordpress) if ( isset( $site_url_components['path'] ) && $site_url_components['path'] !== '' ) { $current_slug = str_replace( trim( $site_url_components['path'] ), '', $current_slug ); } // Take into account subdirectory for the default language or other languages. // This mirrors the logic used in trp_exclude_include_paths_to_run_on(). if ( isset( $settings['add-subdirectory-to-default-language'] ) && $settings['add-subdirectory-to-default-language'] === 'yes' && ! empty( $current_lang ) && isset( $settings['url-slugs'][ $current_lang ] ) ) { $replace = '\/' . $settings['url-slugs'][ $current_lang ]; $current_slug = preg_replace( "/$replace/i", '', ltrim( $current_slug, '/' ), 1 ); } $array_slugs = array(); trp_test_current_slug( $current_slug, $array_slugs ); $matched = trp_return_exclude_include_url( $paths, $current_slug, $array_slugs ); // In "exclude" mode: listed paths are excluded. if ( $mode === 'exclude' ) return (bool) $matched; // In "include" mode: ONLY listed paths are translatable; all others are excluded if ( $mode === 'include' ) return !$matched; return false; } /** * Redirect excluded paths on mapped domains to the main domain * * When a path is excluded from translation, it should only be accessible on the main domain. * Accessing an excluded path on a language-mapped domain (e.g., ro.example.com/excluded-page/) * should redirect to the main domain (e.g., example.com/excluded-page/). */ add_action( 'template_redirect', 'trp_dntcp_redirect_excluded_paths_on_mapped_domains', 0 ); function trp_dntcp_redirect_excluded_paths_on_mapped_domains() { if ( is_admin() ) { return; } // Check if current path is excluded from translation if ( ! trp_dntcp_is_current_url_excluded() ) { return; } // Check if we're on a mapped domain (not the main domain) if ( ! trp_dntcp_is_on_mapped_domain() ) { return; } // Get the URL in the default language (with original/untranslated slugs) $settings = get_option( 'trp_settings', array() ); $default_language = isset( $settings['default-language'] ) ? $settings['default-language'] : 'en_US'; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); // Get current URL and convert to default language (this reverses slug translation) $current_url = $url_converter->cur_page_url(); $redirect_url = $url_converter->get_url_for_language( $default_language, $current_url, '' ); // Ensure the redirect URL uses the main domain $main_domain = get_option( 'home' ); $parsed_redirect = parse_url( $redirect_url ); $parsed_main = parse_url( $main_domain ); if ( isset( $parsed_redirect['path'] ) ) { $redirect_url = trailingslashit( $main_domain ) . ltrim( $parsed_redirect['path'], '/' ); if ( isset( $parsed_redirect['query'] ) ) { $redirect_url .= '?' . $parsed_redirect['query']; } } $status = apply_filters( 'trp_redirect_status', 301, 'redirect_excluded_path_from_mapped_domain_to_main' ); wp_redirect( $redirect_url, $status ); exit; } /** * Check if the current request is on a language-mapped domain (not the main domain) * * @return bool True if on a mapped domain, false if on the main domain */ function trp_dntcp_is_on_mapped_domain() { $settings = get_option( 'trp_settings', array() ); // Check if Multiple Domains mappings exist if ( empty( $settings['trp-multiple-domains'] ) || ! is_array( $settings['trp-multiple-domains'] ) ) { return false; } // Get current host, stripping port if present (e.g. "example.com:8080" → "example.com") // so the value matches what parse_url()['host'] returns on the comparison side. $current_host = ''; if ( ! empty( $_SERVER['HTTP_X_FORWARDED_HOST'] ) ) { $current_host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_HOST'] ) ); } elseif ( ! empty( $_SERVER['HTTP_HOST'] ) ) { $current_host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ); } elseif ( isset( $_SERVER['SERVER_NAME'] ) ) { $current_host = sanitize_text_field( wp_unslash( $_SERVER['SERVER_NAME'] ) ); } $current_host = strtok( $current_host, ':' ); if ( empty( $current_host ) ) { return false; } // Get main domain host $main_domain = get_option( 'home' ); $parsed_main = parse_url( $main_domain ); $main_host = isset( $parsed_main['host'] ) ? $parsed_main['host'] : ''; // If we're on the main domain, return false if ( strcasecmp( $current_host, $main_host ) === 0 ) { return false; } // Check if current host matches any mapped domain foreach ( $settings['trp-multiple-domains'] as $language_code => $mapping ) { if ( empty( $mapping['enabled'] ) || empty( $mapping['domain'] ) ) { continue; } $parsed_mapped = parse_url( $mapping['domain'] ); $mapped_host = isset( $parsed_mapped['host'] ) ? $parsed_mapped['host'] : ''; if ( strcasecmp( $current_host, $mapped_host ) === 0 ) { return true; // We're on a mapped domain } } return false; } includes/advanced-settings/separators.php 0000777 00000014055 15251156640 0014672 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_troubleshoot_separator', 5 ); function trp_register_troubleshoot_separator( $settings_array ){ $settings_array[] = array( 'name' => 'troubleshoot_options', 'type' => 'separator', 'label' => esc_html__( 'Troubleshooting', 'translatepress-multilingual' ), 'no-border' => true, 'id' =>'troubleshooting', ); return $settings_array; } add_filter( 'trp_register_advanced_settings', 'trp_register_container_titles' ); function trp_register_container_titles( $settings_array ){ $container_titles = [ [ 'name' => 'exclude_gettext_strings_title', 'type' => 'container_title', 'label' => esc_html__( 'Exclude Gettext strings', 'translatepress-multilingual' ), 'id' => 'exclude_strings', 'container' => 'exclude_gettext_strings' ], [ 'name' => 'exclude_at_strings_title', 'type' => 'container_title', 'label' => esc_html__( 'Exclude strings from automatic translation', 'translatepress-multilingual' ), 'id' => 'exclude_strings', 'container' => 'exclude_at_strings' ], [ 'name' => 'exclude_dynamic_strings_title', 'type' => 'container_title', 'label' => esc_html__( 'Exclude from dynamic translation', 'translatepress-multilingual' ), 'id' => 'exclude_strings', 'container' => 'exclude_dynamic_strings' ], [ 'name' => 'exclude_selectors_title', 'type' => 'container_title', 'label' => esc_html__( 'Exclude selectors from translation', 'translatepress-multilingual' ), 'id' => 'exclude_strings', 'container' => 'exclude_selectors' ], [ 'name' => 'exclude_selectors_at_title', 'type' => 'container_title', 'label' => esc_html__( 'Exclude selectors only from automatic translation', 'translatepress-multilingual' ), 'id' => 'exclude_strings', 'container' => 'exclude_selectors_at' ], [ 'name' => 'exclude_paths_title', 'type' => 'container_title', 'label' => esc_html__( 'Do not translate certain paths', 'translatepress-multilingual' ), 'id' => 'exclude_strings', 'container' => 'exclude_paths' ], [ 'name' => 'troubleshooting_title', 'type' => 'container_title', 'label' => esc_html__( 'Troubleshooting', 'translatepress-multilingual' ), 'id' => 'troubleshooting', 'container' => 'troubleshooting' ], [ 'name' => 'debug_title', 'type' => 'container_title', 'label' => esc_html__( 'Debug', 'translatepress-multilingual' ), 'id' => 'debug', 'container' => 'debug' ], [ 'name' => 'custom_language_title', 'type' => 'container_title', 'label' => esc_html__( 'Custom languages', 'translatepress-multilingual' ), 'id' => 'custom_language', 'container' => 'custom_language' ], [ 'name' => 'misc_options_title', 'type' => 'container_title', 'label' => esc_html__( 'Miscellaneous options', 'translatepress-multilingual' ), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ], [ 'name' => 'language_switcher_title', 'type' => 'container_title', 'label' => esc_html__( 'Language Switcher', 'translatepress-multilingual' ), 'id' => 'miscellaneous_options', 'container' => 'language_switcher' ] ]; return array_merge( $settings_array, $container_titles ); } add_filter( 'trp_register_advanced_settings', 'trp_register_exclude_separator', 95 ); function trp_register_exclude_separator( $settings_array ){ $settings_array[] = array( 'name' => 'exclude_strings', 'type' => 'separator', 'label' => esc_html__( 'Exclude strings & pages', 'translatepress-multilingual' ), 'id' =>'exclude_strings', ); return $settings_array; } add_filter( 'trp_register_advanced_settings', 'trp_register_debug_separator', 500 ); function trp_register_debug_separator( $settings_array ){ $settings_array[] = array( 'name' => 'debug_options', 'type' => 'separator', 'label' => esc_html__( 'Debug', 'translatepress-multilingual' ), 'id' => 'debug', ); return $settings_array; } add_filter( 'trp_register_advanced_settings', 'trp_register_miscellaneous_separator', 1000 ); function trp_register_miscellaneous_separator( $settings_array ){ $settings_array[] = array( 'name' => 'miscellaneous_options', 'type' => 'separator', 'label' => esc_html__( 'Miscellaneous options', 'translatepress-multilingual' ), 'id' => 'miscellaneous_options', ); return $settings_array; } add_filter( 'trp_register_advanced_settings', 'trp_register_custom_language_separator', 2000 ); function trp_register_custom_language_separator( $settings_array ){ $settings_array[] = array( 'name' => 'custom_language', 'type' => 'separator', 'label' => esc_html__( 'Custom language', 'translatepress-multilingual' ), 'id' => 'custom_language', ); return $settings_array; } includes/advanced-settings/disable-languages-sitemap.php 0000777 00000002717 15251156640 0017520 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_disable_languages_sitemap', 1090); function trp_register_disable_languages_sitemap( $settings_array ){ $settings_array[] = array( 'name' => 'disable_languages_sitemap', 'type' => 'checkbox', 'label' => esc_html__( 'Exclude translated links from sitemap', 'translatepress-multilingual' ), //[utm40] 'description' => wp_kses( __( 'Do not include translated links in sitemaps generated by SEO plugins.<br/>Requires <a href="https://translatepress.com/docs/addons/seo-pack/?utm_source=tp-advanced&utm_medium=client-site&utm_campaign=miscellaneous" title="TranslatePress Add-on SEO Pack documentation" target="_blank"> SEO Pack Add-on</a> to be installed and activated.', 'translatepress-multilingual' ), array( 'br' => array(), 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ) ) ), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } add_filter('trp_disable_languages_sitemap', 'trp_disable_languages_sitemap_function'); function trp_disable_languages_sitemap_function($allow) { $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['disable_languages_sitemap'] ) && $option['disable_languages_sitemap'] === 'yes' ) { return true; } return $allow; } includes/advanced-settings/enable-hreflang-xdefault.php 0000777 00000005706 15251156640 0017336 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_register_advanced_settings', 'trp_register_enable_hreflang_xdefault', 1100 ); function trp_register_enable_hreflang_xdefault( $settings_array ){ $settings_array[] = array( 'name' => 'enable_hreflang_xdefault', 'type' => 'custom', 'default' => 'disabled', 'label' => esc_html__( 'Enable the hreflang x-default tag for language:', 'translatepress-multilingual' ), 'description' => wp_kses( __( 'Enables the hreflang="x-default" for an entire language. See documentation for more details.', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'options' => trp_get_lang_for_xdefault(), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options', ); return $settings_array; } function trp_get_lang_for_xdefault(){ $published_lang_labels = trp_get_languages(); return array_merge(['disabled' => 'Disabled'], $published_lang_labels); } add_filter( 'trp_advanced_setting_custom_enable_hreflang_xdefault', 'trp_output_enable_hreflang_xdefault' ); function trp_output_enable_hreflang_xdefault( $setting ){ $trp_settings = ( new TRP_Settings() )->get_settings(); $adv_option = $trp_settings['trp_advanced_settings']; $checked = ( isset( $adv_option[ $setting['name'] ] ) && $adv_option[ $setting['name'] ] !== 'disabled' ) || ( isset( $adv_option[ $setting['name'] . '-checkbox' ] ) && $adv_option[ $setting['name'] . '-checkbox' ] === 'yes' ) ? 'checked' : ''; $select = "<select class='trp-select' name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "]'>"; foreach ( $setting['options'] as $option_key => $option_value ) { if ( $option_key === 'disabled' ) continue; $selected = $adv_option[ $setting['name'] ] === $option_key ? ' selected' : ''; $select .= "<option value='". esc_attr( $option_key ) ."' $selected>". esc_html( $option_value )."</option>"; } $select .= "</select>"; $html = "<div class='trp-settings-custom-checkbox__wrapper'> <div class='trp-settings-checkbox'> <input type='checkbox' id='" . esc_attr( $setting['name'] ) . "-checkbox' name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "-checkbox]' value='yes' " . $checked . " /> <label for='" . esc_attr( $setting['name'] ) . "-checkbox' class='trp-checkbox-label'> <div class='trp-checkbox-content'> <span class='trp-primary-text-bold'>" . esc_html( $setting['label'] ) . "</span> <span class='trp-description-text'>" . wp_kses_post( $setting['description'] ) . "</span> </div> </label> </div> $select </div>"; return $html; } includes/class-upgrade.php 0000777 00000255231 15251156640 0011641 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Upgrade * * When changing plugin version, do the necessary checks and database upgrades. */ class TRP_Upgrade { protected $settings; protected $db; /* @var TRP_Query */ protected $trp_query; /** Major slug translation refactoring released in this version */ const MINIMUM_SP_VERSION = '1.4.6'; /** Used to check if the currently installed Pro version matches the minimum required version */ const MINIMUM_PERSONAL_VERSION = '1.4.3'; const MINIMUM_DEVELOPER_VERSION = '1.5.6'; /** * TRP_Upgrade constructor. * * @param $settings */ public function __construct( $settings ){ global $wpdb; $this->db = $wpdb; $this->settings = $settings; } /** * Register Settings subpage for TranslatePress */ public function register_menu_page(){ add_submenu_page( 'TRPHidden', 'TranslatePress Remove Duplicate Rows', 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_remove_duplicate_rows', array($this, 'trp_remove_duplicate_rows') ); add_submenu_page( 'TRPHidden', 'TranslatePress Update Database', 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_update_database', array( $this, 'trp_update_database_page' ) ); } /** * When changing plugin version, call certain database upgrade functions. * */ public function check_for_necessary_updates(){ $trp = TRP_Translate_Press::get_trp_instance(); if( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } $stored_database_version = get_option('trp_plugin_version'); if( empty($stored_database_version) ){ $this->check_if_gettext_tables_exist(); }else{ // Updates that require admins to trigger manual update of db because of long duration. Set an option in DB if this is the case. $updates = $this->get_updates_details(); foreach ( $updates as $update ) { if ( version_compare( $update['version'], $stored_database_version, '>' ) ) { update_option( $update['option_name'], 'no' ); } } // Updates that can be done right way. They should take very little time. if ( version_compare( $stored_database_version, '1.3.0', '<=' ) ) { $this->trp_query->check_for_block_type_column(); $this->check_if_gettext_tables_exist(); } if ( version_compare($stored_database_version, '1.5.3', '<=')) { $this->add_full_text_index_to_tables(); } if ( version_compare($stored_database_version, '1.6.1', '<=')) { $this->upgrade_machine_translation_settings(); } if ( version_compare( $stored_database_version, '1.6.5', '<=' ) ) { $this->trp_query->check_for_original_id_column(); $this->trp_query->check_original_table(); $this->trp_query->check_original_meta_table(); } if ( version_compare($stored_database_version, '1.9.8', '<=')) { $this->set_force_slash_at_end_of_links(); } if ( version_compare( $stored_database_version, '2.3.7', '<=' ) ) { $gettext_normalization = $this->trp_query->get_query_component('gettext_normalization'); $gettext_normalization->check_for_gettext_original_id_column(); $gettext_table_creation = $this->trp_query->get_query_component('gettext_table_creation'); $gettext_table_creation->check_gettext_original_table(); $gettext_table_creation->check_gettext_original_meta_table(); } if ( version_compare($stored_database_version, '2.1.0', '<=')){ $this->add_iso_code_to_language_code(); } if ( version_compare($stored_database_version, '2.1.2', '<=')){ $this->create_opposite_ls_option(); } if( version_compare( $stored_database_version, '2.2.2', '<=' ) ){ $this->migrate_auto_translate_slug_to_automatic_translation(); } if ( version_compare( $stored_database_version, '2.7.2', '<=' ) ) { $this->migrate_machine_translation_counter(); } if ( version_compare( $stored_database_version, '2.7.4', '<=' ) ) { $this->add_tp_block_index(); } if ( version_compare( $stored_database_version, '2.8.4', '<=' ) ) { $this->dont_update_db_if_seopack_inactive(); $this->set_the_options_set_in_db_optimization_tool_to_no(); } if ( version_compare( $stored_database_version, '2.9.7', '==' ) ) { $this->set_publish_languages_from_translation_languages(); } if ( version_compare( $stored_database_version, '2.10', '<') ) { $this->migrate_to_language_switcher_v2(); } /** * Write an upgrading function above this comment to be executed only once: while updating plugin to a higher version. * Use example condition: version_compare( $stored_database_version, '2.9.9', '<=') * where 2.9.9 is the current version, and 3.0.0 will be the updated version where this code will be launched. */ } // don't update the db version unless they are different. Otherwise the query is run on every page load. if( version_compare( TRP_PLUGIN_VERSION, $stored_database_version, '!=' ) ){ update_option( 'trp_plugin_version', TRP_PLUGIN_VERSION ); } } public function migrate_auto_translate_slug_to_automatic_translation(){ $option = get_option( 'trp_advanced_settings', true ); $mt_settings_option = get_option( 'trp_machine_translation_settings' ); if( !isset( $mt_settings_option['automatically-translate-slug'] ) ){ if( !isset( $option['enable_auto_translate_slug'] ) || $option['enable_auto_translate_slug'] == '' || $option['enable_auto_translate_slug'] == 'no' ){ $mt_settings_option['automatically-translate-slug'] = 'no'; } else{ $mt_settings_option['automatically-translate-slug'] = 'yes'; } update_option( 'trp_machine_translation_settings', $mt_settings_option ); } } /** * Iterates over all languages to call gettext table checking */ public function check_if_gettext_tables_exist(){ $trp = TRP_Translate_Press::get_trp_instance(); if( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } $gettext_table_creation = $this->trp_query->get_query_component('gettext_table_creation'); if( !empty( $this->settings['translation-languages'] ) ){ foreach( $this->settings['translation-languages'] as $site_language_code ){ $gettext_table_creation->check_gettext_table($site_language_code); } } $gettext_table_creation->check_gettext_original_table(); $gettext_table_creation->check_gettext_original_meta_table(); } /** * @param string $key of updates details array * @return string * @see get_updates_details() */ public function get_updates_processing_message( $key ){ $messages = [ 'remove_cdata_original_and_dictionary_rows' => __('Removing cdata dictionary strings for language %s...', 'translatepress-multilingual' ), 'remove_untranslated_links_dictionary_rows' => __('Removing untranslated dictionary links for language %s...', 'translatepress-multilingual' ), 'remove_duplicate_gettext_rows' => __('Removing duplicated gettext strings for language %s...', 'translatepress-multilingual' ), 'remove_duplicate_dictionary_rows' => __('Removing duplicated dictionary strings for language %s...', 'translatepress-multilingual' ), 'remove_duplicate_untranslated_dictionary_rows' => __('Removing untranslated dictionary strings where translation is available for language %s...', 'translatepress-multilingual' ), 'original_id_insert_166' => __('Inserting original strings for language %s...', 'translatepress-multilingual' ), 'original_id_cleanup_166' => __('Cleaning original strings table for language %s...', 'translatepress-multilingual' ), 'original_id_update_166' => __('Updating original string ids for language %s...', 'translatepress-multilingual' ), 'regenerate_original_meta' => __('Regenerating original meta table for language %s...', 'translatepress-multilingual' ), 'clean_original_meta' => __('Cleaning original meta table for language %s...', 'translatepress-multilingual' ), 'replace_original_id_null' => __('Replacing original id NULL with value for language %s...', 'translatepress-multilingual' ), 'gettext_original_id_insert' => __('Inserting gettext original strings for language %s...', 'translatepress-multilingual' ), 'gettext_original_id_cleanup' => __('Cleaning gettext original strings table for language %s...', 'translatepress-multilingual' ), 'gettext_original_id_update' => __('Updating gettext original string ids for language %s...', 'translatepress-multilingual' ), 'migrate_old_slugs_to_the_new_translate_table_structure_post_type_and_tax_284' => __( 'Migrating taxonomy and post type base slugs to new table structure...', 'translatepress-multilingual' ), 'migrate_old_slugs_to_the_new_translate_table_structure_post_meta_284' => __( 'Migrating post slugs to new table structure for language %s...', 'translatepress-multilingual' ), 'migrate_old_slugs_to_the_new_translate_table_structure_term_meta_284' => __( 'Migrating term slugs to new table structure for language %s...', 'translatepress-multilingual' ), 'show_error_db_message' => __( 'Finishing up...', 'translatepress-multilingual' ) ]; return in_array( $key, array_keys( $messages ) ) ? $messages[$key] : null; } public function get_updates_details(){ return apply_filters( 'trp_updates_details', array( 'remove_cdata_original_and_dictionary_rows' => array( 'version' => '0', 'option_name' => 'trp_remove_cdata_original_and_dictionary_rows', 'callback' => array( $this->trp_query,'remove_cdata_in_original_and_dictionary_tables'), 'batch_size' => 1000, 'message_initial' => '', ), 'remove_untranslated_links_dictionary_rows' => array( 'version' => '0', 'option_name' => 'trp_remove_untranslated_links_dictionary_rows', 'callback' => array( $this->trp_query,'remove_untranslated_links_in_dictionary_table'), 'batch_size' => 10000, 'message_initial' => '', ), 'full_trim_originals_140' => array( 'version' => '1.4.0', 'option_name' => 'trp_updated_database_full_trim_originals_140', 'callback' => array( $this, 'trp_updated_database_full_trim_originals_140' ), 'batch_size' => 200 ), 'gettext_empty_rows_145' => array( 'version' => '1.4.5', 'option_name' => 'trp_updated_database_gettext_empty_rows_145', 'callback' => array( $this,'trp_updated_database_gettext_empty_rows_145'), 'batch_size' => 20000 ), 'remove_duplicate_gettext_rows' => array( 'version' => '0', 'option_name' => 'trp_remove_duplicate_gettext_rows', 'callback' => array( $this->trp_query,'remove_duplicate_rows_in_gettext_table'), 'batch_size' => 5000, 'message_initial' => '', ), 'remove_duplicate_dictionary_rows' => array( 'version' => '0', 'option_name' => 'trp_remove_duplicate_dictionary_rows', 'callback' => array( $this->trp_query,'remove_duplicate_rows_in_dictionary_table'), 'batch_size' => 1000, 'message_initial' => '', ), 'original_id_insert_166' => array( 'version' => '1.6.6', 'option_name' => 'trp_updated_database_original_id_insert_166', 'callback' => array( $this,'trp_updated_database_original_id_insert_166'), 'batch_size' => 1000, ), 'original_id_cleanup_166' => array( 'version' => '1.6.6', 'option_name' => 'trp_updated_database_original_id_cleanup_166', 'callback' => array( $this,'trp_updated_database_original_id_cleanup_166'), 'progress_message' => 'clean', 'batch_size' => 1000, 'message_initial' => '', ), 'original_id_update_166' => array( 'version' => '1.6.6', 'option_name' => 'trp_updated_database_original_id_update_166', 'callback' => array( $this,'trp_updated_database_original_id_update_166'), 'batch_size' => 5000, 'message_initial' => '', ), 'regenerate_original_meta' => array( 'version' => '0', // independent of tp version, available only on demand 'option_name' => 'trp_regenerate_original_meta_table', 'callback' => array( $this,'trp_regenerate_original_meta_table'), 'batch_size' => 200, 'message_initial' => '', ), 'clean_original_meta' => array( 'version' => '0', // independent of tp version, available only on demand 'option_name' => 'trp_clean_original_meta_table', 'callback' => array( $this,'trp_clean_original_meta_table'), 'batch_size' => 20000, 'message_initial' => '', ), 'replace_original_id_null' => array( 'version' => '0', // independent of tp version, available only on demand 'option_name' => 'trp_replace_original_id_null', 'callback' => array( $this,'trp_replace_original_id_null'), 'batch_size' => 50, 'message_initial' => '', ), 'gettext_original_id_insert' => array( 'version' => '2.3.8', 'option_name' => 'trp_updated_database_gettext_original_id_insert', 'callback' => array( $this,'trp_updated_database_gettext_original_id_insert'), 'batch_size' => 1000, ), 'gettext_original_id_cleanup' => array( 'version' => '2.3.8', 'option_name' => 'trp_updated_database_gettext_original_id_cleanup', 'callback' => array( $this,'trp_updated_database_gettext_original_id_cleanup'), 'progress_message' => 'clean', 'batch_size' => 1000, 'message_initial' => '', ), 'gettext_original_id_update' => array( 'version' => '2.3.8', 'option_name' => 'trp_updated_database_gettext_original_id_update', 'callback' => array( $this,'trp_updated_database_gettext_original_id_update'), 'batch_size' => 5000, 'message_initial' => '', ), 'migrate_old_slugs_to_the_new_translate_table_structure_post_type_and_tax_284' => array( 'version' => '0', 'option_name' => 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_type_and_tax_284', 'callback' => array( $this, 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_type_and_tax_284' ), 'batch_size' => 1000000, 'message_initial' => '', 'execute_only_once' => true ), 'migrate_old_slugs_to_the_new_translate_table_structure_post_meta_284' => array( 'version' => '0', 'option_name' => 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_meta_284', 'callback' => array( $this, 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_meta_284' ), 'batch_size' => 500, 'message_initial' => '', ), 'migrate_old_slugs_to_the_new_translate_table_structure_term_meta_284' => array( 'version' => '0', 'option_name' => 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_term_meta_284', 'callback' => array( $this, 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_term_meta_284' ), 'batch_size' => 500, 'message_initial' => '', ), /** Add new entries above this line * Write 3.0.0 if 2.9.9 is the current version, and 3.0.0 will be the updated version where this code will be launched. */ 'show_error_db_message' => array( 'version' => '0', // independent of tp version, available only on demand 'option_name' => 'trp_show_error_db_message', 'callback' => array( $this, 'trp_successfully_run_database_optimization' ), 'batch_size' => 10, 'message_initial' => '', 'execute_only_once' => true ) ) ); } /** * Show admin notice about updating database */ public function show_admin_notice(){ $notifications = TRP_Plugin_Notifications::get_instance(); if ( $notifications->is_plugin_page() || ( isset( $GLOBALS['PHP_SELF']) && ( $GLOBALS['PHP_SELF'] === '/wp-admin/index.php' || $GLOBALS['PHP_SELF'] === '/wp-admin/plugins.php' ) ) ) { if ( ( isset( $_GET['page'] ) && $_GET['page'] == 'trp_update_database' ) ) { return; } $updates_needed = $this->get_updates_details(); $option_db_error_message = get_option( $updates_needed['show_error_db_message']['option_name'] ); foreach ( $updates_needed as $update ) { $option = get_option( $update['option_name'], 'is not set' ); if ( $option === 'no' && $option_db_error_message !== 'no' ) { add_action( 'admin_notices', array( $this, 'admin_notice_update_database' ) ); break; } } } } /** * Print admin notice message */ public function admin_notice_update_database() { $url = add_query_arg( array( 'page' => 'trp_update_database', ), site_url('wp-admin/admin.php') ); // maybe change notice color to blue #28B1FF $html = '<div id="message" class="updated">'; $html .= '<p><strong>' . esc_html__( 'TranslatePress data update', 'translatepress-multilingual' ) . '</strong> – ' . esc_html__( 'We need to update your translations database to the latest version.', 'translatepress-multilingual' ) . '</p>'; $html .= '<p class="submit"><a href="' . esc_url( $url ) . '" onclick="return confirm( \'' . __( 'IMPORTANT: It is strongly recommended to first backup the database!\nAre you sure you want to continue?', 'translatepress-multilingual' ) . '\');" class="button-primary">' . esc_html__( 'Run the updater', 'translatepress-multilingual' ) . '</a></p>'; $html .= '</div>'; echo $html;//phpcs:ignore } public function trp_successfully_run_database_optimization($language_code= null, $inferior_size = null, $batch_size = null){ delete_option('trp_show_error_db_message'); return true; } public function show_admin_error_message(){ if ( ( isset( $_GET[ 'page'] ) && $_GET['page'] == 'trp_update_database' ) ){ return; } $updates_needed = $this->get_updates_details(); $option_db_error_message = get_option($updates_needed['show_error_db_message']['option_name'], 'is not set' ); if ( $option_db_error_message === 'no' ) { add_action( 'admin_notices', array( $this, 'trp_admin_notice_error_database' ) ); } } public function trp_admin_notice_error_database(){ echo '<div class="notice notice-error is-dismissible"> <p>' . wp_kses( sprintf( __('Database optimization did not complete successfully. We recommend restoring the original database or <a href="%s" >trying again.</a>', 'translatepress-multilingual'), admin_url('admin.php?page=trp_update_database') ), array('a' => array( 'href' => array() ) ) ) .'</p> </div>'; } public function trp_update_database_page(){ require_once TRP_PLUGIN_DIR . 'partials/trp-update-database.php'; } /** * Call all functions to update database * * hooked to wp_ajax_trp_update_database */ public function trp_update_database(){ if ( ! current_user_can( apply_filters('trp_update_database_capability', 'manage_options') ) ){ $this->stop_and_print_error( __('Update aborted! Your user account doesn\'t have the capability to perform database updates.', 'translatepress-multilingual' ) ); } $nonce = isset( $_REQUEST['trp_updb_nonce'] ) ? wp_verify_nonce( sanitize_text_field( $_REQUEST['trp_updb_nonce'] ), 'tpupdatedatabase' ) : false; if ( $nonce === false ){ $this->stop_and_print_error( __('Update aborted! Invalid nonce.', 'translatepress-multilingual' ) ); } $request = array(); $request['progress_message'] = ''; $updates_needed = $this->get_updates_details(); if (isset($_REQUEST['initiate_update']) && $_REQUEST['initiate_update']=== "true" ){ update_option('trp_show_error_db_message', 'no'); } if ( empty ( $_REQUEST['trp_updb_action'] ) ){ foreach( $updates_needed as $update_action_key => $update ) { $option = get_option( $update['option_name'], 'is not set' ); if ( $option === 'no' ) { $_REQUEST['trp_updb_action'] = $update_action_key; break; } } if ( empty ( $_REQUEST['trp_updb_action'] ) ){ $back_to_settings_button = '<a class="trp-submit-btn button" href="' . site_url('wp-admin/options-general.php?page=translate-press') . '">' . esc_html__('Back to TranslatePress Settings', 'translatepress-multilingual' ) . '</a>'; // finished successfully echo json_encode( array( 'trp_update_completed' => 'yes', 'progress_message' => '<p><strong>' . __('Successfully updated database!', 'translatepress-multilingual' ) . '</strong></p>' . $back_to_settings_button )); wp_die(); }else{ $_REQUEST['trp_updb_lang'] = $this->settings['translation-languages'][0]; $_REQUEST['trp_updb_batch'] = 0; $updb_action = sanitize_text_field( $_REQUEST['trp_updb_action'] ); $update_message_initial = isset( $updates_needed[ $updb_action ]['message_initial'] ) ? $updates_needed[ $updb_action ]['message_initial'] : __('Updating database to version %s+', 'translatepress-multilingual' ); $update_message_processing = $this->get_updates_processing_message( $updb_action ) ? $this->get_updates_processing_message( $updb_action ) : __('Processing table for language %s...', 'translatepress-multilingual' ); if ($updates_needed[ $updb_action ]['version'] != 0) { $request['progress_message'] .= '<p>' . sprintf( $update_message_initial, $updates_needed[ $updb_action ]['version'] ) . '</p>'; } $request['progress_message'] .= '<br>' . sprintf( $update_message_processing, sanitize_text_field( $_REQUEST['trp_updb_lang'] ) );//phpcs:ignore } }else{ if ( !isset( $updates_needed[ $_REQUEST['trp_updb_action'] ] ) ){ $this->stop_and_print_error( __('Update aborted! Incorrect action.', 'translatepress-multilingual' ) ); } if ( !in_array( $_REQUEST['trp_updb_lang'], $this->settings['translation-languages'] ) ) {//phpcs:ignore $this->stop_and_print_error( __('Update aborted! Incorrect language code.', 'translatepress-multilingual' ) ); } } $request['trp_updb_action'] = sanitize_text_field( $_REQUEST['trp_updb_action'] ); if ( !empty( $_REQUEST['trp_updb_batch'] ) && (int) $_REQUEST['trp_updb_batch'] > 0 ) { $get_batch = (int)$_REQUEST['trp_updb_batch']; }else{ $get_batch = 0; } $extra_params = isset( $_REQUEST['trp_updb_extra_params'] ) ? json_decode(base64_decode(sanitize_text_field($_REQUEST['trp_updb_extra_params'] )), true) : array(); if (!is_array($extra_params)) { $extra_params = array(); } $request['trp_updb_batch'] = 0; $update_details = $updates_needed[ sanitize_text_field( $_REQUEST['trp_updb_action'] )]; $batch_size = apply_filters( 'trp_updb_batch_size', $update_details['batch_size'], sanitize_text_field( $_REQUEST['trp_updb_action'] ), $update_details ); $language_code = isset( $_REQUEST['trp_updb_lang'] ) ? sanitize_text_field( $_REQUEST['trp_updb_lang'] ) : ''; if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $start_time = microtime(true); $duration = 0; while( $duration < 2 ){ $inferior_limit = $batch_size * $get_batch; $callback_return = call_user_func( $update_details['callback'], $language_code, $inferior_limit, $batch_size, $extra_params ); if ( (isset($callback_return['finalize_with_language']) && $callback_return['finalize_with_language']) || ($callback_return === true) ) { break; }else { $get_batch = $get_batch + 1; $extra_params = isset($callback_return['extra_params']) ? $callback_return['extra_params'] : array(); } $stop_time = microtime( true ); $duration = $stop_time - $start_time; } // the callback functions return different true or an array with finalized_with_language bool and extra_params based on what they need. // In case call_user_func fails with string, object, etc, it will continue with the callback and batching. // For example, if the function called uses to much memory, it will just continue. $finalized_with_language = (isset($callback_return['finalize_with_language']) && $callback_return['finalize_with_language']) || ($callback_return === true); if ( !$finalized_with_language ) { $request['trp_updb_batch'] = $get_batch; $request['trp_updb_extra_params'] = isset($callback_return['extra_params']) ? $callback_return['extra_params'] : array(); } if ( $finalized_with_language ) { // finished with the current language $index = array_search( $language_code, $this->settings['translation-languages'] ); if ( isset ( $this->settings['translation-languages'][ $index + 1 ] ) && (!isset($update_details['execute_only_once']) || $update_details['execute_only_once'] == false)) { // next language code in array $request['trp_updb_lang'] = $this->settings['translation-languages'][ $index + 1 ]; $request['progress_message'] .= __( ' done.', 'translatepress-multilingual' ) . '</br>'; $update_message_processing = $this->get_updates_processing_message( sanitize_text_field( $_REQUEST['trp_updb_action'] ) ) ? $this->get_updates_processing_message( sanitize_text_field( $_REQUEST['trp_updb_action'] ) ) : __( 'Processing table for language %s...', 'translatepress-multilingual' ); $request['progress_message'] .= '</br>' . sprintf( $update_message_processing, $request['trp_updb_lang'] ); } else { // finish action due to completing all the translation languages $request['progress_message'] .= __( ' done.', 'translatepress-multilingual' ) . '</br>'; $request['trp_updb_lang'] = ''; $option_result = get_option( $update_details['option_name'], 'no' ); // the next IF is helpful in case we set the option to something else (such as seopack_inactive) during update if ( $option_result === 'no' ) { // setting option to yes will stop showing the admin notice update_option( $update_details['option_name'], 'yes' ); } $request['trp_updb_action'] = ''; } }else{ $request['trp_updb_lang'] = $language_code; $request['progress_message'] .= '.'; } if ( $this->db->last_error != '' ){ $request['progress_message'] = '<p><strong>SQL Error:</strong> ' . esc_html($this->db->last_error) . '</p>' . $request['progress_message']; } $query_arguments = array( 'action' => 'trp_update_database', 'trp_updb_action' => $request['trp_updb_action'], 'trp_updb_lang' => $request['trp_updb_lang'], 'trp_updb_batch' => $request['trp_updb_batch'], 'trp_updb_extra_params' => isset($request['trp_updb_extra_params']) ? base64_encode(json_encode($request['trp_updb_extra_params'])) : base64_encode(json_encode(array())), 'trp_updb_nonce' => wp_create_nonce('tpupdatedatabase'), 'trp_update_completed' => 'no', 'progress_message' => $request['progress_message'] ); echo( json_encode( $query_arguments )); wp_die(); } public function stop_and_print_error( $error_message ){ $back_to_settings_button = '<a class="trp-submit-btn button" href="' . site_url('wp-admin/options-general.php?page=translate-press') . '">' . esc_html__('Back to TranslatePress Settings', 'translatepress-multilingual' ) . '</a>'; $query_arguments = array( 'trp_update_completed' => 'yes', 'progress_message' => '<p><strong>' . $error_message . '</strong></strong></p>' . $back_to_settings_button ); echo( json_encode( $query_arguments )); wp_die(); } /** * Get all originals from the table, trim them and update originals back into table * * @param string $language_code Language code of the table * @param int $inferior_limit Omit first X rows * @param int $batch_size How many rows to query * * @return bool */ public function trp_updated_database_full_trim_originals_140( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } if ( $language_code == $this->settings['default-language']){ // default language doesn't have a dictionary table return true; } $strings = $this->trp_query->get_rows_from_location( $language_code, $inferior_limit, $batch_size, array( 'id', 'original' ) ); if ( count( $strings ) == 0 ) { return true; } foreach( $strings as $key => $string ){ $strings[$key]['original'] = trp_full_trim( $strings[ $key ]['original'] ); } // overwrite original only $this->trp_query->update_strings( $strings, $language_code, array( 'id', 'original' ) ); return false; } /** * Delete all empty gettext rows * * @param string $language_code Language code of the table * @param int $inferior_limit Omit first X rows * @param int $batch_size How many rows to query * * @return bool */ public function trp_updated_database_gettext_empty_rows_145( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $rows_affected = $this->trp_query->delete_empty_gettext_strings( $language_code, $batch_size ); if ( $rows_affected > 0 ){ return false; }else{ return true; } } /** * Normalize original ids for all dictionary entries * * @param string $language_code Language code of the table * @param int $inferior_limit Omit first X rows * @param int $batch_size How many rows to query * * @return bool */ public function trp_updated_database_original_id_insert_166( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $rows_inserted = $this->trp_query->original_ids_insert( $language_code, $inferior_limit, $batch_size ); if ( $rows_inserted > 0 ){ return false; }else{ return true; } } public function trp_updated_database_original_id_cleanup_166( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $this->trp_query->original_ids_cleanup(); return true; } /** * Normalize original ids for all dictionary entries * * @param string $language_code Language code of the table * @param int $inferior_limit Omit first X rows * @param int $batch_size How many rows to query * * @return bool */ public function trp_updated_database_original_id_update_166( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $rows_updated = $this->trp_query->original_ids_reindex( $language_code, $inferior_limit, $batch_size ); if ( $rows_updated > 0 ){ return false; }else { return true; } } public function trp_prepare_options_for_database_optimization(){ if ( !current_user_can( 'manage_options' ) || !isset( $_GET['trp_rm_nonce'] ) ) { return; } if ( !wp_verify_nonce( sanitize_text_field( $_GET['trp_rm_nonce'] ), 'tpremoveduplicaterows' ) ){ exit; } $redirect = false; if(isset( $_GET['trp_rm_duplicates_gettext'] )){ update_option('trp_remove_duplicate_gettext_rows', 'no'); $redirect = true; } if(isset( $_GET['trp_rm_duplicates_dictionary'] )){ update_option('trp_remove_duplicate_dictionary_rows', 'no'); update_option('trp_remove_duplicate_untranslated_dictionary_rows', 'no'); $redirect = true; } if ( isset( $_GET['trp_rm_duplicates_original_strings'] ) ){ $this->trp_remove_duplicate_original_strings(); $redirect = true; } if ( isset( $_GET['trp_rm_cdata_original_and_dictionary'])){ update_option('trp_remove_cdata_original_and_dictionary_rows', 'no'); $redirect = true; } if ( isset( $_GET['trp_rm_untranslated_links'] ) ){ update_option('trp_remove_untranslated_links_dictionary_rows', 'no'); $redirect = true; } if ( isset( $_GET['trp_replace_original_id_null'] ) ){ update_option('trp_replace_original_id_null', 'no'); $redirect = true; } if ( $redirect ) { $url = add_query_arg( array( 'page' => 'trp_update_database' ), site_url( 'wp-admin/admin.php' ) ); wp_safe_redirect( $url ); exit; } } /** * Remove duplicate rows from DB for trp_dictionary tables. * Removes untranslated strings if there is a translated version. * * Iterates over languages. Each language is iterated in batches of 10 000 * * Not accessible from anywhere else * http://example.com/wp-admin/admin.php?page=trp_remove_duplicate_rows */ public function trp_remove_duplicate_rows(){ if ( ! current_user_can( 'manage_options' ) ){ return; } // prepare page structure require_once TRP_PLUGIN_DIR . 'partials/trp-remove-duplicate-rows.php'; } public function enqueue_update_script( $hook ) { if ( $hook === 'admin_page_trp_update_database' ) { wp_enqueue_script( 'trp-update-database', TRP_PLUGIN_URL . 'assets/js/trp-update-database.js', array( 'jquery', ), TRP_PLUGIN_VERSION ); } wp_localize_script( 'trp-update-database', 'trp_updb_localized ', array( 'admin_ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce('tpupdatedatabase') ) ); } /** * Add full text index on the dictionary and gettext tables. * Gets executed once after update. */ private function add_full_text_index_to_tables(){ $table_names = $this->trp_query->get_all_table_names('', array()); $gettext_table_names = $this->trp_query->get_all_gettext_table_names(); foreach (array_merge($table_names, $gettext_table_names) as $table_name){ $possible_index = "SHOW INDEX FROM {$table_name} WHERE Key_name = 'original_fulltext';"; if ($this->db->query($possible_index) === 1){ continue; }; $sql_index = "CREATE FULLTEXT INDEX original_fulltext ON `" . $table_name . "`(original);"; $this->db->query( $sql_index ); } } /** * Moving some settings from trp_settings option to trp_machine_translation_settings * * Upgrade settings from TP version 1.5.8 or earlier to 1.6.2 */ private function upgrade_machine_translation_settings(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component('settings' ); $machine_translation_settings = get_option( 'trp_machine_translation_settings', false ); $default_machine_translation_settings = $trp_settings->get_default_trp_machine_translation_settings(); if ( $machine_translation_settings == false ) { // 1.5.8 did not have any machine_settings so port g-translate-key and g-translate settings if exists $machine_translation_settings = $default_machine_translation_settings; // move the old API key option if (!empty($this->settings['g-translate-key'] ) ) { $machine_translation_settings['google-translate-key'] = $this->settings['g-translate-key']; } // enable machine translation if it was activated before if (!empty($this->settings['g-translate']) && $this->settings['g-translate'] == 'yes'){ $machine_translation_settings['machine-translation'] = 'yes'; } update_option('trp_machine_translation_settings', $machine_translation_settings); }else{ // targeting 1.5.9 to 1.6.1 where incomplete machine-translation settings may have resulted $machine_translation_settings = array_merge( $default_machine_translation_settings, $machine_translation_settings ); update_option('trp_machine_translation_settings', $machine_translation_settings); } } /** * */ private function set_force_slash_at_end_of_links(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component('settings' ); $settings = $trp_settings->get_settings(); if( !empty( $settings['trp_advanced_settings'] ) && !isset( $settings['trp_advanced_settings']['force_slash_at_end_of_links'] ) ){ $advanced_settings = $settings['trp_advanced_settings']; $advanced_settings['force_slash_at_end_of_links'] = 'yes'; update_option('trp_advanced_settings', $advanced_settings ); } } public function add_iso_code_to_language_code(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component('settings' ); $settings = $trp_settings->get_settings(); if(isset($settings['trp_advanced_settings']) && isset($settings['trp_advanced_settings']['custom_language']) ){ $advanced_settings = $settings['trp_advanced_settings']; if(!isset($advanced_settings['custom_language']['cuslangcode'])){ $advanced_settings['custom_language']['cuslangcode'] = $advanced_settings['custom_language']['cuslangiso']; } update_option('trp_advanced_settings', $advanced_settings); } } public function create_opposite_ls_option(){ add_filter('wp_loaded', array($this, 'call_create_menu_entries')); } public function call_create_menu_entries(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component('settings' ); $settings = $trp_settings->get_settings(); $trp_settings->create_menu_entries( $settings['publish-languages'] ); } public function trp_remove_duplicate_original_strings(){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $this->trp_query->rename_originals_table(); $this->trp_query->check_original_table(); update_option( 'trp_updated_database_original_id_insert_166', 'no' ); update_option( 'trp_updated_database_original_id_cleanup_166', 'no' ); update_option( 'trp_updated_database_original_id_update_166', 'no' ); update_option( 'trp_regenerate_original_meta_table', 'no' ); update_option( 'trp_clean_original_meta_table', 'no' ); } public function trp_regenerate_original_meta_table($language_code, $inferior_limit, $batch_size ){ if ( $language_code != $this->settings['default-language']) { // perform regeneration of original meta table only once return true; } if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $this->trp_query->regenerate_original_meta_table($inferior_limit, $batch_size); $last_id = $this->db->get_var("SELECT MAX(meta_id) FROM " . $this->trp_query->get_table_name_for_original_meta() ); if ( $last_id < $inferior_limit ){ // reached end of table return true; }else{ // not done. get another batch return false; } } public function trp_clean_original_meta_table($language_code, $inferior_limit, $batch_size){ if ( $language_code != $this->settings['default-language']) { // perform regeneration of original meta table only once return true; } if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $rows_affected = $this->trp_query->clean_original_meta( $batch_size ); if ( $rows_affected > 0 ){ return false; }else{ $old_originals_table = get_option( 'trp_original_strings_table_for_recovery', '' ); if ( !empty ( $old_originals_table) && strpos($old_originals_table, 'trp_original_strings1') !== false ) { delete_option('trp_original_strings_table_for_recovery'); $this->trp_query->drop_table( $old_originals_table ); } return true; } } /** * Normalize original ids for all gettext entries * * @param string $language_code Language code of the table * @param int $inferior_limit Omit first X rows * @param int $batch_size How many rows to query * * @return bool */ public function trp_updated_database_gettext_original_id_insert( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $gettext_normalization = $this->trp_query->get_query_component('gettext_normalization'); $rows_inserted = $gettext_normalization->gettext_original_ids_insert( $language_code, $inferior_limit, $batch_size ); $last_id = $this->trp_query->get_last_id( $this->trp_query->get_gettext_table_name($language_code) ); if ( $inferior_limit + $batch_size <= $last_id ){ return false; }else{ return true; } } /** * Removes possible duplicates from within gettext_original_strings table * * @param $language_code * @param $inferior_limit * @param $batch_size * @return bool */ public function trp_updated_database_gettext_original_id_cleanup( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $gettext_normalization = $this->trp_query->get_query_component('gettext_normalization'); $gettext_normalization->gettext_original_ids_cleanup(); return true; } /** * Normalize original ids for all gettext entries * * @param string $language_code Language code of the table * @param int $inferior_limit Omit first X rows * @param int $batch_size How many rows to query * * @return bool */ public function trp_updated_database_gettext_original_id_update( $language_code, $inferior_limit, $batch_size ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $gettext_normalization = $this->trp_query->get_query_component('gettext_normalization'); $rows_updated = $gettext_normalization->gettext_original_ids_reindex( $language_code, $inferior_limit, $batch_size ); if ( $rows_updated > 0 ){ return false; }else { return true; } } /** * * Hooked to admin_init */ public function show_notification_about_add_ons_removal(){ //if it's triggered in the frontend we need this include if( !function_exists('is_plugin_active') ) include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); $old_addon_list = array( 'tp-add-on-automatic-language-detection/tp-automatic-language-detection.php', 'tp-add-on-browse-as-other-roles/tp-browse-as-other-role.php', 'tp-add-on-deepl/index.php', 'tp-add-on-extra-languages/tp-extra-languages.php', 'tp-add-on-navigation-based-on-language/tp-navigation-based-on-language.php', 'tp-add-on-seo-pack/tp-seo-pack.php', 'tp-add-on-translator-accounts/index.php', ); foreach( $old_addon_list as $addon_slug ) { if (is_plugin_active($addon_slug)) { $notifications = TRP_Plugin_Notifications::get_instance(); $notification_id = 'trp_add_ons_removal'; //[utm37] $url_info = 'https://translatepress.com/docs/installation/upgrade-to-version-2-0-5-or-newer/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=tp-bundle-update'; //[utm38] $url_account = 'https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=tp-bundle-update'; $message = '<p style="padding-right:30px;">' . sprintf(__( 'All individual TranslatePress add-on plugins <a href="%1$s" target="_blank">have been discontinued</a> and are now included in the premium Personal, Business and Developer versions of TranslatePress. Please log into your <a href="%2$s" target="_blank">account page</a>, download the new premium version and install it. Your individual addons settings will be ported over.' , 'translatepress-multilingual' ), esc_url($url_info), esc_url($url_account)) . '</p>'; //make sure to use the trp_dismiss_admin_notification arg $message .= '<a href="' . add_query_arg(array('trp_dismiss_admin_notification' => $notification_id)) . '" type="button" class="notice-dismiss" style="text-decoration: none;z-index:100;"><span class="screen-reader-text">' . esc_html__('Dismiss this notice.', 'translatepress-multilingual') . '</span></a>'; $notifications->add_notification($notification_id, $message, 'trp-notice trp-narrow notice error is-dismissible', true, array('translate-press'), true); break; } } } /** * There is a very unfortunate error where the original_id is NULL for some gettext strings * We have to check is this is the case and create arrays that would help the editor to not get stuck and complete the original_id field. * We do this by getting the id from the wp_trp_gettext_original_strings and updating the wp_trp_gettext_current_language table with the original ids. */ public function trp_replace_original_id_null($language_code, $inferior_limit, $batch_size){ global $wpdb; $db = $wpdb; $dictionary = array(); $gettext_with_null_original_id_array= array(); $original_id_get_ids_sync = array(); $insert_gettext_original_id = array(); if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); /* @var TRP_Query */ $this->trp_query = $trp->get_component( 'query' ); } $last_id = $this->trp_query->get_last_id( $this->trp_query->get_gettext_table_name( $language_code ) ); while ( $last_id > $inferior_limit ) { $dictionary = $this->trp_query->get_all_gettext_strings($language_code, $inferior_limit, $batch_size); $inferior_limit = $inferior_limit + $batch_size; if (!empty($dictionary)) { foreach ($dictionary as $current_language_string) { if ($current_language_string['tt_original_id'] == NULL || $current_language_string['tt_original_id'] == 0) { $gettext_with_null_original_id_array[] = array( 'original' => $current_language_string['tt_original'], 'id' => $current_language_string['id'], 'domain' => $current_language_string['tt_domain'], ); } } $gettext_insert_update = $this->trp_query->get_query_component('gettext_insert_update'); if (count($gettext_with_null_original_id_array) > 0) { foreach ($gettext_with_null_original_id_array as $item) { $original_id_get_ids_sync[] = $item; } } $original_ids_null = $gettext_insert_update->gettext_original_strings_sync($original_id_get_ids_sync, false); if (count($original_ids_null) > 0) { foreach ($original_ids_null as $key => $value) { $insert_gettext_original_id[] = array( 'id' => $gettext_with_null_original_id_array[$key]['id'], 'original' => $gettext_with_null_original_id_array[$key]['original'], 'original_id' => $value, ); } $gettext_insert_update->update_gettext_strings($insert_gettext_original_id, $language_code, array('id', 'original', 'original_id')); } } $original_id_get_ids_sync = array(); $gettext_with_null_original_id_array =array(); } return true; } /* * @IMPORTANT * Here is the beginning of the slug data migration functions */ /* * Functions that returns the name of the table needed for meta type slug inner join table * wp_posts or wp_terms */ public function trp_get_table_name_for_original_join_in_meta_based_slugs( $meta_type ){ if ( $meta_type == "postmeta" ) { return 'posts'; } if ( $meta_type == "termmeta" ) { return 'terms'; } } /* * Function that takes the name of the meta key to determine if the translation was manual or automatic */ public function trp_get_meta_based_slug_status( $meta_values ) { if ( preg_match( '/automatically/', $meta_values['meta_key'] ) != false ) { return '1'; } elseif ( preg_match( '/translated/', $meta_values['meta_key'] ) != false ) { return '2'; } return '0'; } /* * Function that gets the original slug by the selected [term/post]_id and forms the array structure needed for migration */ public function trp_get_original_slugs_for_meta_based_slugs( $meta_type, $meta_slugs_and_ids, $language_code ) { $extracted_slugs_array = array(); if ( $meta_type == 'postmeta' ) { foreach ( $meta_slugs_and_ids as $meta_values ) { if ( !empty( $meta_values['post_name'] ) ) { $extracted_slugs_array[ $meta_values['post_name'] ]["original"] = $meta_values['post_name']; $extracted_slugs_array[ $meta_values['post_name'] ]["type"] = 'post'; if ( isset( $meta_values['meta_value'] ) ) { $extracted_slugs_array[ $meta_values['post_name'] ]["language"] = $language_code; $extracted_slugs_array[ $meta_values['post_name'] ]["status"] = $this->trp_get_meta_based_slug_status( $meta_values ); $extracted_slugs_array[ $meta_values['post_name'] ]["translated"] = $meta_values['meta_value']; } } } return $extracted_slugs_array; } elseif ( $meta_type == 'termmeta' ) { foreach ( $meta_slugs_and_ids as $meta_values ) { if ( !empty( $meta_values['slug'] ) ) { $extracted_slugs_array[ $meta_values['slug'] ]["original"] = $meta_values['slug']; $extracted_slugs_array[ $meta_values['slug'] ]["type"] = 'term'; if ( isset( $meta_values['meta_value'] ) ) { $extracted_slugs_array[ $meta_values['slug'] ]["language"] = $language_code; $extracted_slugs_array[ $meta_values['slug'] ]["status"] = $this->trp_get_meta_based_slug_status( $meta_values ); $extracted_slugs_array[ $meta_values['slug'] ]["translated"] = $meta_values['meta_value']; } } } return $extracted_slugs_array; } return $extracted_slugs_array; } public function get_last_id_for_meta_based_slugs( $meta_type, $language_code ){ global $wpdb; $table_name = $wpdb->prefix . $meta_type; $select_query = "SELECT COUNT(*) FROM `" . $table_name . "` "; $select_query .= "WHERE ( meta_key LIKE %s OR meta_key LIKE %s )"; $prepared_query = $wpdb->prepare( $select_query, '%' . $wpdb->esc_like( 'trp_automatically_translated_slug_' . $language_code ) . '%', '%' . $wpdb->esc_like( 'trp_translated_slug_'. $language_code ) . '%' ); $extracted_number = $wpdb->get_var( $prepared_query ); $last_id = intval( $extracted_number ); return $last_id; } /* * ~~~~~~~~ META BASED SLUG EXTRACTION ~~~~~~~~ * * Function that gather all the information needed for the meta based slugs ro form a complete array with all the necessary * items for migration * * LOGISTIC: - the meta based slugs are saved in the *meta tables from wordpress with the meta key providing the information * if the slug was manually or automatically translated, as well as, the language in which the slug was translated * - the [post/term]_id tells as which post/page the original slug van be found * - in the term case the slug can be found in the slug field dictated by the term_id * - in the post case the slug can be found in the post_name field dictated by the post_id */ public function trp_get_meta_based_slugs_from_db_284( $meta_type, $language_code, $inferior_limit, $batch_size ) { global $wpdb; $table_name_for_originals_for_the_selected_meta_type = $this->trp_get_table_name_for_original_join_in_meta_based_slugs( $meta_type ); $extracted_slugs_array = array(); if ( $meta_type == 'postmeta' ) { $select_query = "SELECT meta_key, meta_value, trp_original_name.post_name FROM `" . $wpdb->postmeta . "` as trp_translation "; $select_query .= "INNER JOIN `" . $wpdb->posts . "` as trp_original_name "; $select_query .= "ON trp_translation.post_id = trp_original_name.ID "; $select_query .= "WHERE ( meta_key LIKE %s OR meta_key LIKE %s ) LIMIT %d OFFSET %d"; $prepared_query = $wpdb->prepare( $select_query, '%' . $wpdb->esc_like( 'trp_automatically_translated_slug_' . $language_code ) . '%', '%' . $wpdb->esc_like( 'trp_translated_slug_' . $language_code ) . '%', $batch_size, $inferior_limit ); $meta_slugs_and_ids = $wpdb->get_results( $prepared_query, 'ARRAY_A' ); $extracted_slugs_array = $this->trp_get_original_slugs_for_meta_based_slugs( 'postmeta', $meta_slugs_and_ids, $language_code ); }elseif ( $meta_type == 'termmeta' ) { $select_query = "SELECT meta_key, meta_value, trp_original_name.slug FROM `" . $wpdb->termmeta . "` as trp_translation "; $select_query .= "INNER JOIN `" . $wpdb->terms . "` as trp_original_name "; $select_query .= "ON trp_translation.term_id = trp_original_name.term_id "; $select_query .= "WHERE ( meta_key LIKE %s OR meta_key LIKE %s ) LIMIT %d OFFSET %d"; $prepared_query = $wpdb->prepare( $select_query, '%' . $wpdb->esc_like( 'trp_automatically_translated_slug_' . $language_code ) . '%', '%' . $wpdb->esc_like( 'trp_translated_slug_' . $language_code ) . '%', $batch_size, $inferior_limit ); $meta_slugs_and_ids = $wpdb->get_results( $prepared_query, 'ARRAY_A' ); $extracted_slugs_array = $this->trp_get_original_slugs_for_meta_based_slugs( 'termmeta', $meta_slugs_and_ids, $language_code ); } return $extracted_slugs_array; } /* * ~~~~~~~~ OPTION BASED SLUGS EXTRACTION ~~~~~~~~ * * In this function we extract the option based slugs and we arrange them in the needed structure for data migration * * LOGUSTIC: - the oprion based slugs are stored in the wp_options table under the option name of * trp_taxonomy_slug_translation * * trp_post_type_base_slug_translation * - we extract the information from the field with is now stored in a nested array and refector it to the structure * for slug data migration */ public function trp_get_option_based_slugs_from_db_284( $option_name ) { $data = get_option( $option_name, array() ); $extracted_slugs_array = array(); $woocommerce_permalink_options = get_option( 'woocommerce_permalinks', false ); foreach ( $data as $key => $values_array ) { $extracted_slugs_array[ $key ]["original"] = trim( $values_array["original"], '/' ); if ( $values_array['type'] == 'post-type-base-slug' || $values_array['type'] == 'post-type-base' ) { $extracted_slugs_array[ $key ]["type"] = 'post-type-base'; if ( apply_filters( 'trp_migrate_post_type_or_tax_original_only_if_active', true, $extracted_slugs_array[ $key ]["original"], $values_array['type'] ) && ( !post_type_exists( $extracted_slugs_array[ $key ]["original"] ) && ( $woocommerce_permalink_options === false || $extracted_slugs_array[ $key ]["original"] !== trim( $woocommerce_permalink_options['product_base'], '/' ) ) ) ) { continue; } }else{ if ( apply_filters( 'trp_migrate_post_type_or_tax_original_only_if_active', true, $extracted_slugs_array[ $key ]["original"], $values_array['type'] ) && ( !taxonomy_exists( $extracted_slugs_array[ $key ]["original"] ) && ( $woocommerce_permalink_options === false || ( $extracted_slugs_array[ $key ]["original"] !== $woocommerce_permalink_options['category_base'] && $extracted_slugs_array[ $key ]["original"] !== $woocommerce_permalink_options['tag_base'] ) ) ) ) { continue; } $extracted_slugs_array[ $key ]["type"] = 'taxonomy'; } foreach ( $values_array["translationsArray"] as $lang => $translation_element ) { if ( $translation_element["status"] == 0 ){ continue; } $extracted_slugs_array[ $key ][ $lang ]["language"] = $lang; $extracted_slugs_array[ $key ][ $lang ]["status"] = $translation_element["status"]; $extracted_slugs_array[ $key ][ $lang ]["translated"] = $translation_element["translated"]; } } return $extracted_slugs_array; } /* * All the extracted slugs from the old db are ordered when extrated * * The structure of slug type option based extracted array is: * * (example using term slugs) * taxonomy_slugs: * taxonomy_slug_name: * original * type * language: * status * language * translated * * The structure of meta based slugs extracted array is: * meta_slug_name: * original * type * language: * status * language * translated * */ /* * Migrating the option based slugs from the old structure to the new one * */ public function trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_type_and_tax_284() { if ( class_exists( 'TRP_Slug_Query' ) ) { $trp = TRP_Translate_Press::get_trp_instance(); $slug_query = new TRP_Slug_Query(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $languages_to_verify = $settings['translation-languages']; $extracted_slugs_array = array(); $extracted_slugs_array['taxonomy_slugs'] = $this->trp_get_option_based_slugs_from_db_284( 'trp_taxonomy_slug_translation' ); $extracted_slugs_array['post_type_base_slugs'] = $this->trp_get_option_based_slugs_from_db_284( 'trp_post_type_base_slug_translation' ); foreach ( $languages_to_verify as $language_code ) { if ( !( $language_code == $settings['default-language'] ) ) { foreach ( $extracted_slugs_array as $key_slug_original => $slug_originals ) { $array_for_translated_slugs = array(); $language_is_found_in_this_array_of_slugs = false; foreach ( $slug_originals as $keep_slug_to_determine_all_slugs_translation => $slug_original ) { if ( isset( $slug_original[ $language_code ] ) ) { $language_is_found_in_this_array_of_slugs = true; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['original'] = $slug_original['original']; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['translated'] = $slug_original[ $language_code ]['translated']; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['status'] = $slug_original[ $language_code ]['status']; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['type'] = $slug_original['type']; } } if ( $language_is_found_in_this_array_of_slugs ) { $slug_query->insert_slugs( $array_for_translated_slugs, $language_code ); } } } } return true; }else{ update_option( 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_type_and_tax_284', 'seopack_inactive' ); return true; } } /* * Migrating the meta based post slugs from the old structure to the new one * * Using batches */ public function trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_meta_284( $language_code, $inferior_limit, $batch_size ) { if ( class_exists( 'TRP_Slug_Query' ) ) { $trp = TRP_Translate_Press::get_trp_instance(); $slug_query = new TRP_Slug_Query(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $last_id = $this->get_last_id_for_meta_based_slugs( 'postmeta', $language_code ); $extracted_slugs_array_post = array(); $extracted_slugs_array_post = $this->trp_get_meta_based_slugs_from_db_284( 'postmeta', $language_code, $inferior_limit, $batch_size ); $array_for_translated_slugs = array(); $language_is_found_in_this_array_of_slugs = false; foreach ( $extracted_slugs_array_post as $keep_slug_to_determine_all_slugs_translation => $slug_original ) { $language_is_found_in_this_array_of_slugs = true; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['original'] = $keep_slug_to_determine_all_slugs_translation; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['type'] = $slug_original['type']; if ( isset( $slug_original['translated'] ) ) { $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['translated'] = $slug_original['translated']; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['status'] = $slug_original['status']; } } if ( $language_is_found_in_this_array_of_slugs ) { $slug_query->insert_slugs( $array_for_translated_slugs, $language_code ); } if ( $inferior_limit + $batch_size <= $last_id ) { return false; } else { return true; } }else{ update_option( 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_meta_284', 'seopack_inactive' ); return true; } } /* * Migrating the meta based term slugs from the old structure to the new one * * Using batches */ public function trp_migrate_old_slug_to_new_parent_and_translate_slug_table_term_meta_284( $language_code, $inferior_limit, $batch_size ) { if ( class_exists( 'TRP_Slug_Query' ) ) { $trp = TRP_Translate_Press::get_trp_instance(); $slug_query = new TRP_Slug_Query(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $last_id = $this->get_last_id_for_meta_based_slugs( 'termmeta', $language_code ); $extracted_slugs_array_term = array(); $extracted_slugs_array_term = $this->trp_get_meta_based_slugs_from_db_284( 'termmeta', $language_code, $inferior_limit, $batch_size ); $array_for_translated_slugs = array(); $language_is_found_in_this_array_of_slugs = false; foreach ( $extracted_slugs_array_term as $keep_slug_to_determine_all_slugs_translation => $slug_original ) { $language_is_found_in_this_array_of_slugs = true; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['original'] = $keep_slug_to_determine_all_slugs_translation; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['type'] = $slug_original['type']; if ( isset( $slug_original['translated'] ) ) { $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['translated'] = $slug_original['translated']; $array_for_translated_slugs[ $keep_slug_to_determine_all_slugs_translation ]['status'] = $slug_original['status']; } } if ( $language_is_found_in_this_array_of_slugs ) { $slug_query->insert_slugs( $array_for_translated_slugs, $language_code ); } if ( $inferior_limit + $batch_size <= $last_id ) { return false; } else { return true; } }else{ update_option( 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_term_meta_284', 'seopack_inactive' ); return true; } } /** * Migrate machine_translation_counter to a standalone row in the wp_options. * It's necessary for running atomic queries on it * * @return void */ public function migrate_machine_translation_counter() { $mt_settings_option = get_option( 'trp_machine_translation_settings', null ); if ( $mt_settings_option && isset( $mt_settings_option['machine_translation_counter'] ) ) { update_option( 'trp_machine_translation_counter', $mt_settings_option['machine_translation_counter'] ); } } /* * Since Version 2.7.5 * Adding index on the column block_type in dictionary table for performance improvement * In the function that creates the dictionaty tables, a syntax to add this index was also written */ public function add_tp_block_index() { global $wpdb; $prefix = $wpdb->prefix; $suffix = 'trp_dictionary_'; $tables = $wpdb->get_col("SHOW TABLES LIKE '%$prefix%$suffix%'"); foreach ($tables as $table) { $indexes = $wpdb->get_results("SHOW INDEXES FROM $table WHERE Key_name = 'block_type'"); if (empty($indexes)) { $columns = $wpdb->get_results("DESCRIBE $table"); if (array_search('block_type', wp_list_pluck($columns, 'Field')) !== false) { //using %1s because a sql syntax error was persistant when using %s; basically the %s added ''(quotes) around the table name //after searhing online, it was sugested to use %i which worked fine but using %1s also seems o work $query = $wpdb->prepare("ALTER TABLE %1s ADD INDEX block_type ( block_type )", $table); $wpdb->query($query); } } } } /** * @return void * * Verifies if the options set to 'no' in DB optimization tool are 'no' and, if so, setting them to 'yes' */ public function set_the_options_set_in_db_optimization_tool_to_no(){ $array_of_options_to_check_and_set_for_db_optimization = array( "trp_regenerate_original_meta_table", "trp_clean_original_meta_table", "trp_updated_database_original_id_insert_166", "trp_updated_database_original_id_cleanup_166", "trp_updated_database_original_id_update_166", "trp_remove_duplicate_dictionary_rows", "trp_remove_duplicate_gettext_rows", "trp_remove_duplicate_untranslated_dictionary_rows", "trp_remove_cdata_original_and_dictionary_rows", "trp_remove_untranslated_links_dictionary_rows", "trp_replace_original_id_null" ); foreach ( $array_of_options_to_check_and_set_for_db_optimization as $option ){ if ( ( get_option( $option, 'not_set' ) == 'no' ) ){ update_option( $option, 'yes' ); } } } /** * Used to check if the minimum pro plugin version (required after refactoring slug translation) is installed. * * @return bool */ public function is_seo_pack_minimum_version_met(){ if ( !defined('TRP_IN_SP_PLUGIN_VERSION' ) ) return true; return version_compare( TRP_IN_SP_PLUGIN_VERSION, self::MINIMUM_SP_VERSION, '>=' ); } public function dont_update_db_if_seopack_inactive(){ $array_of_option_names = ['trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_type_and_tax_284','trp_migrate_old_slug_to_new_parent_and_translate_slug_table_post_meta_284','trp_migrate_old_slug_to_new_parent_and_translate_slug_table_term_meta_284']; foreach ($array_of_option_names as $option ) { $option_result = get_option( $option, 'not_set' ); if ( $option_result === 'yes' ) { continue; } if ( $option_result === 'no' && !class_exists( 'TRP_Slug_Query' ) ) { update_option( $option, 'seopack_inactive' ); delete_option('trp_show_error_db_message'); } } } /** * Fix bug specific to 2.9.7 version where if the user saved settings, the publish-languages were lost * * @return void */ public function set_publish_languages_from_translation_languages() { $extra_languages_is_active = class_exists( 'TRP_IN_Extra_Languages' ); $trp_settings = get_option( 'trp_settings' ); if ( !$extra_languages_is_active && count( $trp_settings['translation-languages'] ) <= 2 && ( empty( array_diff( $trp_settings['translation-languages'], $trp_settings['publish-languages'] ) ) || empty( array_diff( $trp_settings['publish-languages'], $trp_settings['translation-languages'] ) ) ) ) { $trp_settings['publish-languages'] = $trp_settings['translation-languages']; update_option( 'trp_settings', $trp_settings ); } } /** * Seed Language Switcher V2 settings on version change for upgraded sites. * - New installs (no stored version) are ignored (defaults applied later by LS tab). * - Upgrades: ensure option exists and enable legacy mode. * */ public function migrate_to_language_switcher_v2(): void { // Seed from defaults or existing option $defaults = TRP_Language_Switcher_Tab::default_switcher_config(); $cfg = get_option('trp_language_switcher_settings', null); if ( !is_array($cfg) || empty($cfg) ) { $cfg = $defaults; } // Ensure branches exist foreach (['floater','shortcode','menu'] as $scope) { if ( empty($cfg[$scope]) || !is_array($cfg[$scope]) ) { $cfg[$scope] = $defaults[$scope]; } if ( empty( $cfg[$scope]['layoutCustomizer'] ) || !is_array( $cfg[$scope]['layoutCustomizer'] ) ) { $cfg[$scope]['layoutCustomizer'] = $defaults[$scope]['layoutCustomizer']; } foreach (['desktop','mobile'] as $device) { if ( empty( $cfg[$scope]['layoutCustomizer'][$device] ) || !is_array( $cfg[$scope]['layoutCustomizer'][$device] ) ) { $cfg[$scope]['layoutCustomizer'][$device] = $defaults[$scope]['layoutCustomizer'][$device]; } } } // Local utilities $map_preset = static function( string $preset ): array { switch ( $preset ) { case 'full-names': return ['flagIconPosition' => 'hide', 'languageNames' => 'full']; case 'short-names': return ['flagIconPosition' => 'hide', 'languageNames' => 'short']; case 'flags-full-names': return ['flagIconPosition' => 'before', 'languageNames' => 'full']; case 'flags-short-names': return ['flagIconPosition' => 'before', 'languageNames' => 'short']; case 'only-flags': return ['flagIconPosition' => 'before', 'languageNames' => 'none']; default: return ['flagIconPosition' => 'before', 'languageNames' => 'full']; } }; $apply_layout = static function ( array $layout, array $pairs ): array { foreach ( [ 'desktop', 'mobile' ] as $device ) { if ( ! isset( $layout[ $device ] ) || ! is_array( $layout[ $device ] ) ) { $layout[ $device ] = []; } foreach ( $pairs as $k => $v ) { $layout[ $device ][ $k ] = $v; // force overwrite } } return $layout; }; $cfg['floater']['enabled'] = $this->settings['trp-ls-floater'] === 'yes'; // Map legacy presets -> new layout options $shortcode_preset = isset($this->settings['shortcode-options']) ? sanitize_key($this->settings['shortcode-options']) : ''; $cfg['shortcode']['layoutCustomizer'] = $apply_layout( $cfg['shortcode']['layoutCustomizer'], $map_preset($shortcode_preset) ); $menu_preset = isset($this->settings['menu-options']) ? sanitize_key($this->settings['menu-options']) : ''; $cfg['menu']['layoutCustomizer'] = $apply_layout( $cfg['menu']['layoutCustomizer'], $map_preset($menu_preset) ); $floater_preset = isset($this->settings['floater-options']) ? sanitize_key($this->settings['floater-options']) : ''; $cfg['floater']['layoutCustomizer'] = $apply_layout( $cfg['floater']['layoutCustomizer'], $map_preset($floater_preset) ); // ===== Floater position → layoutCustomizer (both devices) ===== $allowed_positions = ['bottom-right','bottom-left','top-right','top-left']; $position = isset($this->settings['floater-position']) ? sanitize_key($this->settings['floater-position']) : ''; if ( !in_array($position, $allowed_positions, true) ) { $position = $defaults['floater']['layoutCustomizer']['desktop']['position']; } $cfg['floater']['layoutCustomizer'] = $apply_layout( $cfg['floater']['layoutCustomizer'], ['position' => $position] ); // Floater color scheme $color_scheme = isset($this->settings['floater-color']) ? sanitize_key($this->settings['floater-color']) : 'light'; if ( $color_scheme === 'dark' ) { $cfg['floater']['bgColor'] = '#000000'; $cfg['floater']['bgHoverColor'] = '#444444'; $cfg['floater']['textColor'] = '#ffffff'; $cfg['floater']['textHoverColor'] = '#eeeeee'; $cfg['floater']['borderColor'] = 'transparent'; } else { $cfg['floater']['bgColor'] = '#ffffff'; $cfg['floater']['bgHoverColor'] = '#0000000D'; $cfg['floater']['textColor'] = '#143852'; $cfg['floater']['textHoverColor'] = '#1D2327'; $cfg['floater']['borderColor'] = '#1438521A'; } $cfg['floater']['showPoweredBy'] = ( isset($this->settings['trp-ls-show-poweredby']) && $this->settings['trp-ls-show-poweredby'] === 'yes' ); // Advanced settings -> new flags (only if missing) $adv_settings = ( isset($this->settings['trp_advanced_settings']) && is_array($this->settings['trp_advanced_settings']) ) ? $this->settings['trp_advanced_settings'] : []; if ( !isset($cfg['shortcode']['clickLanguage']) ) { $cfg['shortcode']['clickLanguage'] = ( isset($adv_settings['open_language_switcher_shortcode_on_click']) && $adv_settings['open_language_switcher_shortcode_on_click'] === 'yes' ); } if ( !isset($cfg['floater']['oppositeLanguage']) ) { $is_opposite_language_enabled = ( isset($adv_settings['show_opposite_flag_language_switcher_shortcode']) && $adv_settings['show_opposite_flag_language_switcher_shortcode'] === 'yes' ); $cfg['floater']['oppositeLanguage'] = $is_opposite_language_enabled; $cfg['shortcode']['oppositeLanguage'] = $is_opposite_language_enabled; } $adv_settings['load_legacy_language_switcher'] = 'yes'; update_option( 'trp_advanced_settings', $adv_settings ); update_option( 'trp_language_switcher_settings', $cfg ); update_option( 'trp_ls_v2_migrated_from_legacy', 'yes' ); } /** * If user migrated from the legacy switcher, register a dismissible admin notice. */ public function show_language_switcher_v2_intro_notice() : void { if ( ! is_admin() || ! current_user_can( apply_filters( 'trp_settings_capability', 'manage_options' ) ) ) return; $notification_id = 'trp_ls_v2_intro'; // Dismissed if ( get_user_meta( get_current_user_id(), $notification_id . '_dismiss_notification', true ) ) return; $migrated = get_option( 'trp_ls_v2_migrated_from_legacy', 'no' ) === 'yes'; if ( !$migrated ) return; $settings_url = add_query_arg( [ 'trp_dismiss_admin_notification' => $notification_id ], admin_url( 'admin.php?page=trp_language_switcher' ) ); $logo_url = trailingslashit( TRP_PLUGIN_URL ) . 'assets/images/tp-logo.png'; $dismiss_link = '<a style="text-decoration:none;z-index:100;" href="' . esc_url( add_query_arg( [ 'trp_dismiss_admin_notification' => $notification_id ] ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; //[utm39] $docs_url = 'https://translatepress.com/docs/settings/language-switcher/?utm_source=tp-language-switcher&utm_medium=client-site&utm_campaign=ls-legacy#legacy-mode'; $css = ' .trp-ls-v2-card{display:grid;gap:8px;padding:10px;} .trp-ls-v2-header{display:flex;align-items:center;gap:8px;} .trp-ls-v2-card .trp-ls-v2-logo img{max-width:36px;} .trp-ls-v2-card .trp-ls-v2-heading{margin:0;font-size:16px;line-height:1.3;} .trp-ls-v2-card .trp-ls-v2-desc{margin:0;color:#3c434a;} .trp-ls-v2-card .trp-ls-v2-cta{margin:0;display:flex;align-items:center;gap:15px;} .trp-ls-v2-card .trp-ls-v2-cta .trp-submit-btn{color:#ffffff; background:#2271B1;border-radius:5px;font-size:14px;border:1px solid #2271B1;padding:4px 12px;min-height:40px;cursor:pointer;} .trp-ls-v2-card .trp-ls-v2-cta .trp-submit-btn:hover{background:transparent !important;color:#2271B1;border-color:#2271B1} .trp-ls-v2-card .trp-ls-v2-cta .trp-doc-link{font-size:14px;text-decoration:none;color:#2271B1} .trp-ls-v2-card .trp-ls-v2-cta .trp-doc-link:hover{text-decoration:underline} '; add_action( 'admin_head', function () use ( $css ) : void { echo '<style id="trp-ls-v2-admin-css">' . $css . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped }); $message = '<div class="trp-ls-v2-card">'; $message .= '<div class="trp-ls-v2-header">'; $message .= '<div class="trp-ls-v2-logo"><img src="' . esc_url( $logo_url ) . '" alt="TranslatePress logo" /></div>'; $message .= '<p class="trp-ls-v2-heading"><strong>' . esc_html__( 'Brand-new Language Switcher Settings are here!', 'translatepress-multilingual' ) . '</strong></p>'; $message .= '</div>'; $message .= '<p class="trp-ls-v2-desc">' . esc_html__( 'Explore pre-made templates, switch colors, flag styles, spacing, layouts & more. Use the live preview to perfect your switcher in seconds.', 'translatepress-multilingual' ) . '</p>'; $message .= '<div class="trp-ls-v2-cta">' . '<a class="trp-submit-btn button" href="' . esc_url( $settings_url ) . '">' . esc_html__( 'Start customizing', 'translatepress-multilingual' ) . '</a>' . '<a class="trp-doc-link" href="' . esc_url( $docs_url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Read documentation', 'translatepress-multilingual' ) . '</a>' . '</div>'; $message .= $dismiss_link; $message .= '</div>'; $notifications = TRP_Plugin_Notifications::get_instance(); $notifications->add_notification( $notification_id, $message, 'trp-notice notice notice-info is-dismissible', false, [ 'translate-press' ], true ); } } includes/class-settings.php 0000777 00000106537 15251156640 0012056 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Settings * * In charge of the settings page and settings option. */ class TRP_Settings{ protected $settings; protected $trp_query; protected $url_converter; protected $trp_languages; protected $machine_translator; protected $loader; /** * Return array of customization options for language switchers. * * Customization options include whether to add flags, full names or short names. * Used for all types of language switchers. * * @return array Array with customization options. */ public function get_language_switcher_options(){ $ls_options = apply_filters( 'trp_language_switcher_output', array( 'full-names' => array( 'full_names' => true, 'short_names' => false, 'flags' => false, 'no_html' => false, 'label' => __( 'Full Language Names', 'translatepress-multilingual' ) ), 'short-names' => array( 'full_names' => false, 'short_names' => true, 'flags' => false, 'no_html' => false, 'label' => __( 'Short Language Names', 'translatepress-multilingual' ) ), 'flags-full-names' => array( 'full_names' => true, 'short_names' => false, 'flags' => true, 'no_html' => false, 'label' => __( 'Flags with Full Language Names', 'translatepress-multilingual' ) ), 'flags-short-names' => array( 'full_names' => false, 'short_names' => true, 'flags' => true, 'no_html' => false, 'label' => __( 'Flags with Short Language Names', 'translatepress-multilingual' ) ), 'only-flags' => array( 'full_names' => false, 'short_names' => false, 'flags' => true, 'no_html' => false, 'label' => __( 'Only Flags', 'translatepress-multilingual' ) ), 'full-names-no-html' => array( 'full_names' => false, 'short_names' => false, 'flags' => false, 'no_html' => true, 'label' => __( 'Full Language Names No HTML', 'translatepress-multilingual' ) ) ) ); return $ls_options; } /** * Echo HTML for selecting language from all available languages in settings. * * @param string $ls_type shortcode_options | menu_options | floater_options * @param string $ls_setting The selected language switcher customization setting (get_language_switcher_options()) */ public function output_language_switcher_select( $ls_type, $ls_setting ){ $ls_options = $this->get_language_switcher_options(); // Use the full names no HTML option only for the menu - for extra compatibility with certain themes and menus if ($ls_type !== 'menu-options'){ unset($ls_options['full-names-no-html']); } $output = '<select id="' . esc_attr( $ls_type ) . '" name="trp_settings[' . esc_attr( $ls_type ) .']" class="trp-select trp-ls-select-option">'; foreach( $ls_options as $key => $ls_option ){ $selected = ( $ls_setting == $key ) ? 'selected' : ''; $output .= '<option value="' . esc_attr( $key ) . '" ' . esc_attr( $selected ) . ' >' . esc_html( $ls_option['label'] ). '</option>'; } $output .= '</select>'; echo $output;/* phpcs:ignore */ /* escaped above */ } /** * Echo html for selecting language selector position. * * @param string $ls_position The selected language switcher position */ public function output_language_switcher_floater_possition( $ls_position ){ $ls_options = array( 'bottom-right' => array( 'label' => __( 'Bottom Right', 'translatepress-multilingual' ) ), 'bottom-left' => array( 'label' => __( 'Bottom Left', 'translatepress-multilingual' ) ), 'top-right' => array( 'label' => __( 'Top Right', 'translatepress-multilingual' ) ), 'top-left' => array( 'label' => __( 'Top Left', 'translatepress-multilingual' ) ), ); $output = '<select id="floater-position" name="trp_settings[floater-position]" class="trp-select trp-ls-select-option">'; foreach( $ls_options as $key => $ls_option ){ $selected = ( $ls_position == $key ) ? 'selected' : ''; $output .= '<option value="' . esc_attr( $key ) . '" ' . esc_attr( $selected ) . ' >' . esc_html( $ls_option['label'] ). '</option>'; } $output .= '</select>'; echo $output; /* phpcs:ignore */ /* escaped above */ } /** * Echo html for selecting language selector color. * * @param string $ls_color The selected language switcher color. */ public function output_language_switcher_floater_color( $ls_color ){ $ls_options = array( 'dark' => array( 'label' => __( 'Dark', 'translatepress-multilingual' ) ), 'light' => array( 'label' => __( 'Light', 'translatepress-multilingual' ) ) ); $output = '<select id="floater-color" name="trp_settings[floater-color]" class="trp-select trp-ls-select-option">'; foreach( $ls_options as $key => $ls_option ){ $selected = ( $ls_color == $key ) ? 'selected' : ''; $output .= '<option value="' . esc_attr( $key ) . '" ' . esc_attr( $selected ) . ' >' . esc_html( $ls_option['label'] ). '</option>'; } $output .= '</select>'; echo $output; /* phpcs:ignore */ /* escaped above */ } /** * Returns settings_option. * * @return array Settings option. */ public function get_settings(){ if ( $this->settings == null ){ $this->set_options(); } return $this->settings; } /** * Returns the value of an individual setting or the default provided. * * @param string $name * @param default mixed * * @return mixed Setting Value */ public function get_setting($name, $default = null){ if( array_key_exists($name, $this->settings ) ){ return maybe_unserialize($this->settings[$name]); } else { return $default; } } /** * Register Settings subpage for TranslatePress */ public function register_menu_page(){ add_options_page( 'TranslatePress', 'TranslatePress', apply_filters( 'trp_settings_capability', 'manage_options' ), 'translate-press', array( $this, 'settings_page_content' ) ); add_submenu_page( 'TRPHidden', 'TranslatePress Addons', 'TRPHidden', 'manage_options', 'trp_addons_page', array($this, 'addons_page_content') ); } /** * Settings page content. */ public function settings_page_content(){ if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } $languages = $this->trp_languages->get_languages( 'english_name' ); $is_legacy_switcher = ( $this->settings['trp_advanced_settings']['load_legacy_language_switcher'] ?? 'no' ) === 'yes'; require_once TRP_PLUGIN_DIR . 'partials/main-settings-page.php'; } /** * Addons page content. */ public function addons_page_content(){ $trp = TRP_Translate_Press::get_trp_instance(); $install_plugins = $trp->get_component('install_plugins'); $active_plugin = __( 'Deactivate', 'translatepress-multilingual' ); $inactive_plugin = __( 'Install & Activate', 'translatepress-multilingual' ); $inactive_and_installed = __( 'Activate', 'translatepress-multilingual' ); $plugins = array( 'pb', 'pms', 'wha' ); $plugin_settings = array(); foreach($plugins as $plugin ){ $plugin_settings[$plugin] = array(); if ( $install_plugins->is_plugin_active( $plugin ) ) { $plugin_settings[$plugin]['install_button'] = $active_plugin; $plugin_settings[$plugin]['disabled'] = ''; $plugin_settings[$plugin]['action'] = 'deactivate'; } elseif ( $install_plugins->is_plugin_installed( $plugin ) ) { $plugin_settings[$plugin]['install_button'] = $inactive_and_installed; $plugin_settings[$plugin]['disabled'] = ''; $plugin_settings[$plugin]['action'] = 'activate'; } else{ $plugin_settings[$plugin]['install_button'] = $inactive_plugin; $plugin_settings[$plugin]['disabled'] = ''; $plugin_settings[$plugin]['action'] = 'install_activate'; } } require_once TRP_PLUGIN_DIR . 'partials/addons-settings-page.php'; } /** * Register settings option. */ public function register_setting(){ register_setting( 'trp_settings', 'trp_settings', array( $this, 'sanitize_settings' ) ); } /** * Sanitizes a settings option after save. * * Updates menu items for languages to be used in Menus. * * @param array $settings Raw settings option. * @return array Sanitized option page. */ public function sanitize_settings( $settings ){ if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component( 'query' ); } if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } if ( !isset ( $settings['default-language'] ) ) { $settings['default-language'] = 'en_US'; } if ( !isset ( $settings['translation-languages'] ) ){ $settings['translation-languages'] = array(); } if ( !isset ( $settings['publish-languages'] ) ){ $settings['publish-languages'] = array(); } $settings['translation-languages'] = array_filter( array_unique( $settings['translation-languages'] ) ); $settings['publish-languages'] = array_filter( array_unique( $settings['publish-languages'] ) ); if ( ! in_array( $settings['default-language'], $settings['translation-languages'] ) ){ array_unshift( $settings['translation-languages'], $settings['default-language'] ); } if ( ! in_array( $settings['default-language'], $settings['publish-languages'] ) ){ array_unshift( $settings['publish-languages'], $settings['default-language'] ); } // check if submitted language codes are valid. Default language is included here too $check_language_codes = array_unique( array_merge($settings['translation-languages'], $settings['publish-languages']) ); foreach($check_language_codes as $check_language_code ){ if ( !trp_is_valid_language_code($check_language_code) ){ add_settings_error( 'trp_advanced_settings', 'settings_error', esc_html__('Invalid language code. Please try again.', 'translatepress-multilingual'), 'error' ); return get_option( 'trp_settings', 'not_set' ); } } if( !empty( $settings['native_or_english_name'] ) ) $settings['native_or_english_name'] = sanitize_text_field( $settings['native_or_english_name'] ); else $settings['native_or_english_name'] = 'english_name'; if( !empty( $settings['add-subdirectory-to-default-language'] ) ) $settings['add-subdirectory-to-default-language'] = sanitize_text_field( $settings['add-subdirectory-to-default-language'] ); else $settings['add-subdirectory-to-default-language'] = 'no'; if( !empty( $settings['force-language-to-custom-links'] ) ) $settings['force-language-to-custom-links'] = sanitize_text_field( $settings['force-language-to-custom-links'] ); else $settings['force-language-to-custom-links'] = 'no'; if ( !empty( $settings['trp-ls-floater'] ) ){ $settings['trp-ls-floater'] = sanitize_text_field( $settings['trp-ls-floater'] ); }else{ $settings['trp-ls-floater'] = 'no'; } $is_legacy_switcher = ( $this->settings['trp_advanced_settings']['load_legacy_language_switcher'] ?? 'no' ) === 'yes'; // Only if legacy switcher is enabled. Those settings are not shown otherwise. if ( $is_legacy_switcher ) { $language_switcher_options = $this->get_language_switcher_options(); if ( ! isset( $language_switcher_options[ $settings['shortcode-options'] ] ) ){ $settings['shortcode-options'] = 'flags-full-names'; } if ( ! isset( $language_switcher_options[ $settings['menu-options'] ] ) ){ $settings['menu-options'] = 'flags-full-names'; } if ( ! isset( $language_switcher_options[ $settings['floater-options'] ] ) ){ $settings['floater-options'] = 'flags-full-names'; } } if ( ! isset( $settings['floater-position'] ) ){ $settings['floater-position'] = 'bottom-right'; } if ( ! isset( $settings['floater-color'] ) ){ $settings['floater-color'] = 'dark'; } if ( !empty( $settings['trp-ls-show-poweredby'] ) ){ $settings['trp-ls-show-poweredby'] = sanitize_text_field( $settings['trp-ls-show-poweredby'] ); }else{ $settings['trp-ls-show-poweredby'] = 'no'; } if ( ! isset( $settings['url-slugs'] ) ){ $settings['url-slugs'] = $this->trp_languages->get_iso_codes( $settings['translation-languages'] ); } foreach( $settings['translation-languages'] as $language_code ){ if ( empty ( $settings['url-slugs'][$language_code] ) ){ $settings['url-slugs'][$language_code] = $language_code; }else{ $settings['url-slugs'][$language_code] = sanitize_title( strtolower( $settings['url-slugs'][$language_code] )) ; } } foreach ($settings['translation-languages'] as $value=>$language){ if(isset($settings['translation-languages-formality'][$value])) { if ( $settings['translation-languages-formality'][ $value ] == 'informal' ) { $settings['translation-languages-formality-parameter'][ $language ] = 'informal'; } else { if ( $settings['translation-languages-formality'][ $value ] == 'formal' ) { $settings['translation-languages-formality-parameter'][ $language ] = 'formal'; } else { $settings['translation-languages-formality-parameter'][ $language ] = 'default'; } } } } unset($settings['translation-languages-formality']); $trp = TRP_Translate_Press::get_trp_instance(); $language_switcher_tab = $trp->get_component('language_switcher_tab'); if ( !$language_switcher_tab->is_legacy_enabled() && count( $settings['publish-languages'] ) > 2 ) { $ls_settings = $language_switcher_tab->get_initial_config(); $new_ls_settings = $ls_settings; $new_ls_settings['floater']['oppositeLanguage'] = false; $new_ls_settings['shortcode']['oppositeLanguage'] = false; if ( $new_ls_settings !== $ls_settings ){ update_option( 'trp_language_switcher_settings', $new_ls_settings ); } } // check for duplicates in url slugs $duplicate_exists = false; foreach( $settings['url-slugs'] as $urlslug ) { if ( count ( array_keys( $settings['url-slugs'], $urlslug ) ) > 1 ){ $duplicate_exists = true; break; } } if ( $duplicate_exists ){ foreach( $settings['translation-languages'] as $language_code ) { $settings['url-slugs'][$language_code] = $language_code; } } $this->create_menu_entries( $settings['publish-languages'] ); $gettext_table_creation = $this->trp_query->get_query_component('gettext_table_creation'); require_once( ABSPATH . 'wp-includes/load.php' ); foreach ( $settings['translation-languages'] as $language_code ){ if ( $settings['default-language'] != $language_code ) { $this->trp_query->check_table( $settings['default-language'], $language_code ); } wp_download_language_pack( $language_code ); $gettext_table_creation->check_gettext_table( $language_code ); } //in version 1.6.6 we normalized the original strings and created new tables $this->trp_query->check_original_table(); $this->trp_query->check_original_meta_table(); $gettext_table_creation->check_gettext_original_table(); $gettext_table_creation->check_gettext_original_meta_table(); // regenerate permalinks in case something changed flush_rewrite_rules(); return apply_filters( 'trp_extra_sanitize_settings', $settings ); } /** * Output admin notices after saving settings. */ public function admin_notices(){ settings_errors( 'trp_settings' ); } /** * Set options array variable to be used across plugin. * * Sets a default option if it does not exist. */ protected function set_options(){ $settings_option = get_option( 'trp_settings', 'not_set' ); // initialize default settings $default = get_locale(); if ( empty( $default ) ){ $default = 'en_US'; } $default_settings = array( 'default-language' => $default, 'translation-languages' => array( $default ), 'publish-languages' => array( $default ), 'native_or_english_name' => 'english_name', 'add-subdirectory-to-default-language' => 'no', 'force-language-to-custom-links' => 'yes', 'trp-ls-floater' => 'yes', 'shortcode-options' => 'flags-full-names', 'menu-options' => 'flags-full-names', 'floater-options' => 'flags-full-names', 'floater-position' => 'bottom-right', 'floater-color' => 'dark', 'trp-ls-show-poweredby' => 'no', 'url-slugs' => array( 'en_US' => 'en', '' ), ); if ( 'not_set' == $settings_option || is_string($settings_option) ){ if ( is_string($settings_option) ){ error_log( 'Invalid trp_settings: ' . json_encode($settings_option) ); } update_option ( 'trp_settings', $default_settings ); $settings_option = $default_settings; }else{ // Add any missing default option for trp_setting foreach ( $default_settings as $key_default_setting => $value_default_setting ){ if ( !isset ( $settings_option[$key_default_setting] ) ) { $settings_option[$key_default_setting] = $value_default_setting; } } } // Might have saved invalid language codes in the past so this code protects against SQL Injections using invalid language codes which are used in queries $check_language_codes = array_unique( array_merge($settings_option['translation-languages'], $settings_option['publish-languages']) ); foreach($check_language_codes as $check_language_code ) { if ( !trp_is_valid_language_code( $check_language_code ) ) { add_filter('plugins_loaded', array($this, 'show_invalid_language_codes_error_notice'), 999999); } } /** * These options (trp_advanced_settings, trp_machine_translation_settings, trp_language_switcher_settings) are not part of the actual trp_settings DB option. * But they are included in $settings variable across TP */ $settings_option['trp_advanced_settings'] = get_option('trp_advanced_settings', array() ); $settings_option['trp_language_switcher_settings'] = get_option( 'trp_language_switcher_settings', [] ); // Add any missing default option for trp_machine_translation_settings $default_trp_machine_translation_settings = $this->get_default_trp_machine_translation_settings(); // a client reported a notice where, in wp_options table, the trp_machine_translation_settings is false // we don't know how ths happened since the setting should be an array, or it shouldn't exist // this couldn't be replicated on a clean instance $trp_check_if_machine_settings_is_array = get_option( 'trp_machine_translation_settings', $default_trp_machine_translation_settings ); if ( is_array( $trp_check_if_machine_settings_is_array )) { $settings_option['trp_machine_translation_settings'] = array_merge( $default_trp_machine_translation_settings, $trp_check_if_machine_settings_is_array ); }else{ $settings_option[ 'trp_machine_translation_settings' ] = $default_trp_machine_translation_settings; } /* @deprecated Setting only used for compatibility with Deepl Add-on 1.0.0 */ if ( $settings_option['trp_machine_translation_settings']['translation-engine'] === 'deepl' && defined( 'TRP_DL_PLUGIN_VERSION' ) && TRP_DL_PLUGIN_VERSION === '1.0.0' ) { $trp_languages = new TRP_Languages(); $settings_option['machine-translate-codes'] = $trp_languages->get_iso_codes($settings_option['translation-languages']); if ( isset( $settings_option['trp_machine_translation_settings']['deepl-api-key'] ) ) { $settings_option['deepl-api-key'] = $settings_option['trp_machine_translation_settings']['deepl-api-key']; } } $this->settings = $settings_option; } public function show_invalid_language_codes_error_notice(){ $trp = TRP_Translate_Press::get_trp_instance(); $error_manager = $trp->get_component( 'error_manager' ); $error_manager->record_error( array( 'message' => esc_html__('Language codes can contain only A-Z a-z 0-9 - _ characters. Check your language codes in TranslatePress General Settings.', 'translatepress-multilingual'), 'notification_id' => 'trp_invalid_language_code' ) ); } public function get_default_trp_machine_translation_settings(){ return apply_filters( 'trp_get_default_trp_machine_translation_settings', array( // default settings for trp_machine_translation_settings 'machine-translation' => 'no', 'translation-engine' => 'mtapi', 'block-crawlers' => 'yes', 'automatically-translate-slug' => 'yes', 'machine_translation_counter_date' => date ("Y-m-d" ), 'machine_translation_limit_enabled' => 'no', 'machine_translation_limit' => 1000000 /* * These settings are merged into the saved DB option. * Be sure to set any checkbox options to 'no' in sanitize_settings. * Unchecked checkboxes don't have a POST value when saving settings, so they will be overwritten by merging. */ )); } /** * Enqueue scripts and styles for settings page. * * @param string $hook Admin page. */ public function enqueue_scripts_and_styles( $hook ) { if( in_array( $hook, [ 'settings_page_translate-press', 'admin_page_trp_license_key', 'admin_page_trp_addons_page', 'admin_page_trp_advanced_page', 'admin_page_trp_machine_translation', 'admin_page_trp_test_machine_api', 'admin_page_trp_optin_page', 'admin_page_trp_remove_duplicate_rows', 'admin_page_trp_update_database', 'admin_page_trp_language_switcher', 'admin_page_trp-onboarding' ] ) ){ wp_enqueue_style( 'trp-settings-style', TRP_PLUGIN_URL . 'assets/css/trp-back-end-style.css', array(), TRP_PLUGIN_VERSION ); } if( in_array( $hook, array( 'settings_page_translate-press', 'admin_page_trp_advanced_page', 'admin_page_trp_machine_translation' ) ) ) { // Base script now handles both free and pro functionality via hooks/filters. // However, we keep loading trp-back-end-script-pro.js for backwords compatibility when TP Free is newer then the Pro Addon. $back_end_script_url = TRP_PLUGIN_URL . 'assets/js/trp-back-end-script.js'; if( defined( 'TRP_IN_EL_PLUGIN_URL' ) && file_exists( TRP_IN_EL_PLUGIN_DIR . 'assets/js/trp-back-end-script-pro.js' ) ) { $license_status = get_option( 'trp_license_status' ); //load the pro script only if the license is valid if( $license_status === 'valid' ) { $back_end_script_url = TRP_IN_EL_PLUGIN_URL . 'assets/js/trp-back-end-script-pro.js'; } } wp_enqueue_script( 'trp-settings-script', $back_end_script_url, array( 'jquery', 'jquery-ui-sortable' ), TRP_PLUGIN_VERSION ); if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } $all_language_codes = $this->trp_languages->get_all_language_codes(); $iso_codes = $this->trp_languages->get_iso_codes( $all_language_codes, false ); $tp_data = get_option('trp_db_stored_data', array() ); $languages_that_support_formality = isset( $tp_data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']] ) ? $tp_data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['formality-supported-languages'] : '' ; wp_localize_script( 'trp-settings-script', 'trp_url_slugs_info', array( 'iso_codes' => $iso_codes, 'languages_that_support_formality' => $languages_that_support_formality, 'max_secondary_languages' => apply_filters( 'trp_secondary_languages', 1 ), 'error_message_duplicate_slugs' => __( 'Error! Duplicate URL slug values.', 'translatepress-multilingual' ), 'error_message_formality' => wp_kses( __( 'You cannot select two languages that have the same <a href="https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes" target="_blank">iso code</a> but different formalities because doing so will lead to duplicate <a href="https://developers.google.com/search/docs/specialty/international/localized-versions" target="_blank">hreflang tags</a>.', 'translatepress-multilingual' ), [ 'a' => [ 'href' => [], 'class' => [], 'rel' => [], 'target' => [] ] ] ), 'error_message_duplicate_languages' => wp_kses( __( 'Duplicate language detected.<br>Each language can only be added once to ensure accurate translation management.<br> Please change the duplicate language entry and try again. ', 'translatepress-multilingual' ), [ 'br' => [] ] ), 'admin-ajax' => admin_url( 'admin-ajax.php' ), 'trp-tpai-recheck-nonce' => wp_create_nonce( 'trp-tpai-recheck' ) ) ); wp_enqueue_script( 'trp-select2-lib-js', TRP_PLUGIN_URL . 'assets/lib/select2-lib/dist/js/select2.min.js', array( 'jquery' ), TRP_PLUGIN_VERSION ); wp_enqueue_style( 'trp-select2-lib-css', TRP_PLUGIN_URL . 'assets/lib/select2-lib/dist/css/select2.min.css', array(), TRP_PLUGIN_VERSION ); } if( in_array( $hook, array( 'admin_page_trp_addons_page' ) ) ) { wp_enqueue_script( 'trp-add-ons-script', TRP_PLUGIN_URL . 'assets/js/trp-back-end-add-ons.js', array( ), TRP_PLUGIN_VERSION, true ); wp_localize_script( 'trp-add-ons-script', 'trp_addons_localized', array( 'admin_ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'trp_install_plugins' )) ); } } /** * Disable the multiple language selector if the license is not valid. * */ public function disable_languages_selector() { $license_status = get_option( 'trp_license_status' ); if( $license_status !== 'valid' ) { remove_all_actions('trp_language_selector'); add_action('trp_language_selector', array($this, 'languages_selector'), 20, 1); } } /** * Output HTML for Translation Language option. * * Hooked to trp_language_selector. * * @param array $languages All available languages. */ public function languages_selector( $languages ){ if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component('url_converter'); } $selected_language_code = ''; require_once TRP_PLUGIN_DIR . 'partials/main-settings-language-selector.php'; } /** * Update language switcher menu items. * * @param array $languages Array of language codes to create menu items for. */ public function create_menu_entries( $languages ){ if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } $published_languages = $this->trp_languages->get_language_names( $languages, 'english_name' ); $published_languages['current_language'] = __( 'Current Language', 'translatepress-multilingual' ); $languages[] = 'current_language'; $posts = get_posts( array( 'post_type' =>'language_switcher', 'posts_per_page' => -1 ) ); if ( count( $published_languages ) == 3 ){ $languages[] = 'opposite_language'; $published_languages['opposite_language'] = __( 'Opposite Language', 'translatepress-multilingual' ); } foreach ( $published_languages as $language_code => $language_name ) { $existing_ls = null; foreach( $posts as $post ){ if ( $post->post_content == $language_code ){ $existing_ls = $post; break; } } $ls = array( 'post_title' => $language_name, 'post_content' => $language_code, 'post_status' => 'publish', 'post_type' => 'language_switcher' ); if ( $existing_ls ){ $ls['ID'] = $existing_ls->ID; wp_update_post( $ls ); }else{ wp_insert_post( $ls ); } } foreach ( $posts as $post ){ if ( ! in_array( $post->post_content, $languages ) ){ wp_delete_post( $post->ID ); } } } /** * Add navigation tabs in settings. * */ public function add_navigation_tabs(){ $tabs = array( array( 'name' => __( 'General', 'translatepress-multilingual' ), 'url' => admin_url( 'options-general.php?page=translate-press' ), 'page' => 'translate-press' ), array( 'name' => __( 'Translate Site', 'translatepress-multilingual' ), 'url' => add_query_arg( 'trp-edit-translation', 'true', home_url() ), 'page' => 'trp_translation_editor' ), array( 'name' => __( 'Addons', 'translatepress-multilingual' ), 'url' => admin_url( 'admin.php?page=trp_addons_page' ), 'page' => 'trp_addons_page' ), ); if( class_exists( 'TRP_LICENSE_PAGE' ) ) { $tabs[] = array( 'name' => __( 'License', 'translatepress-multilingual' ), 'url' => admin_url( 'admin.php?page=trp_license_key' ), 'page' => 'trp_license_key' ); } $tabs = apply_filters( 'trp_settings_tabs', $tabs ); $active_tab = 'translate-press'; if ( isset( $_GET['page'] ) ){ $active_tab = sanitize_text_field( wp_unslash( $_GET['page'] ) ); } require TRP_PLUGIN_DIR . 'partials/settings-navigation-tabs.php'; } /** * Add SVG icon symbols to use throughout the admin. */ public function add_svg_icons() { ?> <svg width="0" height="0" class="hidden"> <symbol aria-hidden="true" data-prefix="fas" data-icon="check-circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="check-circle"> <path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path> </symbol> <symbol aria-hidden="true" data-prefix="fas" data-icon="times-circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="times-circle"> <path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path> </symbol> </svg> <?php } /** * Plugin action links. * * Adds action links to the plugin list table * * Fired by `plugin_action_links` filter. * * @param array $links An array of plugin action links. * * @return array An array of plugin action links. */ public function plugin_action_links( $links ) { $settings_link = sprintf( '<a href="%1$s">%2$s</a>', admin_url( 'options-general.php?page=translate-press' ), __( 'Settings', 'translatepress-multilingual' ) ); array_unshift( $links, $settings_link ); if( !trp_is_paid_version() ) { //[utm29] $links['go_pro'] = sprintf( '<a href="%1$s" target="_blank" style="color: #e76054; font-weight: bold;">%2$s</a>', esc_url( trp_add_affiliate_id_to_link( 'https://translatepress.com/pricing/?utm_source=wp-plugins-page&utm_medium=client-site&utm_campaign=plugins-upsell' ) ), esc_html__( 'Pro Features', 'translatepress-multilingual' ) ); }else { $license_details = get_option( 'trp_license_details' ); $is_demosite = ( strpos( site_url(), 'https://demo.translatepress.com' ) !== false ); if ( !empty( $license_details ) && !$is_demosite ) { if ( !empty( $license_details['invalid'] ) ) { $license_detail = $license_details['invalid'][0]; if ( isset( $license_detail->error ) && $license_detail->error == 'missing' ) { $links['license'] = sprintf( '<a href="%1$s" target="_blank" style="color: #e76054; font-weight: bold;">%2$s</a>', esc_url(trp_add_affiliate_id_to_link( admin_url( '/admin.php?page=trp_license_key' ) ) ), esc_html__( 'Activate License', 'translatepress-multilingual' ) ); } } } } return $links; } } includes/class-plugin-optin.php 0000777 00000052363 15251156640 0012640 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Plugin_Optin { public static $user_name = ''; public static $api_url = 'https://translatepress.com/wp-json/trp-api/'; public static $stats_url = 'https://usagetracker.cozmoslabs.com/update'; public static $plugin_optin_status = ''; public static $plugin_optin_email = ''; public static $plugin_option_key = 'trp_plugin_optin'; public static $plugin_option_email_key = 'trp_plugin_optin_email'; public function __construct(){ if ( !wp_next_scheduled( 'trp_plugin_optin_sync' ) ) wp_schedule_event( time(), 'weekly', 'trp_plugin_optin_sync' ); add_action( 'trp_plugin_optin_sync', array( 'TRP_Plugin_Optin', 'sync_data' ) ); self::$plugin_optin_status = get_option( self::$plugin_option_key, false ); self::$plugin_optin_email = get_option( self::$plugin_option_email_key, false ); } public function redirect_to_plugin_optin_page(){ if( !isset( $_GET['page'] ) ) return; if( self::$plugin_optin_status !== false ) return; // Default/in-plugin tabs will be hardcoded, but anything that is added through hooks will be automatically filled $trp_settings_pages = apply_filters( 'trp_settings_tabs', array() ); if( !empty( $trp_settings_pages ) ){ $pages = array(); foreach( $trp_settings_pages as $page ) { $pages[] = $page['page']; } $trp_settings_pages = $pages; } $trp_settings_pages[] = 'translate-press'; $trp_settings_pages[] = 'trp_addons_page'; $trp_settings_pages[] = 'trp_license_key'; if( !in_array( $_GET['page'], $trp_settings_pages ) ) return; wp_safe_redirect( admin_url( 'admin.php?page=trp_optin_page' ) ); exit(); } public function add_submenu_page_optin() { add_submenu_page( 'TRPHidden', 'TranslatePress Optin', 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_optin_page', array( $this, 'optin_page_content' ) ); } public function optin_page_content(){ require_once TRP_PLUGIN_DIR . 'partials/plugin-optin-page.php'; } public function process_optin_actions(){ if( !isset( $_GET['page'] ) || $_GET['page'] != 'trp_optin_page' || !isset( $_GET['_wpnonce'] ) ) return; if( wp_verify_nonce( sanitize_text_field( $_GET['_wpnonce'] ), 'trp_enable_plugin_optin' ) ){ $args = array( 'method' => 'POST', 'body' => array( 'email' => get_option( 'admin_email' ), 'name' => self::get_user_name(), 'version' => self::get_current_active_version(), ), ); $trp_settings = get_option( 'trp_settings', false ); if( !empty( $trp_settings ) && count( $trp_settings['translation-languages'] ) > 1 ){ $multiple_languages = isset( $trp_settings['translation-languages'] ) && ( $trp_settings['translation-languages'] ) > 1 ? true : false; // also check if custom translation tables are present $translation_tables = false; global $wpdb; $dictionary_table_name = $wpdb->prefix . 'trp_dictionary_' . strtolower( $trp_settings['default-language'] ) . '_'. strtolower( $trp_settings['translation-languages'][1] ); if( $wpdb->get_var( "SHOW TABLES LIKE '$dictionary_table_name'" ) == $dictionary_table_name || (int)$wpdb->get_var( "SELECT COUNT(id) FROM $dictionary_table_name WHERE translated != ''" ) > 25 ) $translation_tables = true; if( $multiple_languages && $translation_tables ) $args['body']['existingSettings'] = true; } $request = wp_remote_post( self::$api_url . 'pluginOptinSubscribe/', $args ); update_option( self::$plugin_option_key, 'yes' ); update_option( self::$plugin_option_email_key, get_option( 'admin_email' ) ); $settings = get_option( 'trp_advanced_settings', array() ); if( empty( $settings ) ) $settings = array( 'plugin_optin_setting' => 'yes' ); else $settings['plugin_optin_setting'] = 'yes'; update_option( 'trp_advanced_settings', $settings ); wp_safe_redirect( admin_url( 'options-general.php?page=translate-press' ) ); exit; } if( wp_verify_nonce( sanitize_text_field( $_GET['_wpnonce'] ), 'trp_disable_plugin_optin' ) ){ update_option( self::$plugin_option_key, 'no' ); $settings = get_option( 'trp_advanced_settings', array() ); if( empty( $settings ) ) $settings = array( 'plugin_optin_setting' => 'no' ); else $settings['plugin_optin_setting'] = 'no'; update_option( 'trp_advanced_settings', $settings ); wp_safe_redirect( admin_url( 'options-general.php?page=translate-press' ) ); exit; } } // Update tags when a paid version is activated public function process_paid_plugin_activation( $plugin ){ if( self::$plugin_optin_status !== 'yes' || self::$plugin_optin_email === false ) return; $target_plugins = [ 'translatepress-personal/index.php', 'translatepress-developer/index.php', 'translatepress-business/index.php' ]; if( !in_array( $plugin, $target_plugins ) ) return; $version = explode( '/', $plugin ); $version = str_replace( 'translatepress-', '', $version[0] ); // Update user version tag $args = array( 'method' => 'POST', 'body' => [ 'email' => self::$plugin_optin_email, 'version' => $version, ], ); $request = wp_remote_post( self::$api_url . 'pluginOptinUpdateVersion/', $args ); } // Update tags when a paid version is deactivated public function process_paid_plugin_deactivation( $plugin ){ if( self::$plugin_optin_status !== 'yes' || self::$plugin_optin_email === false ) return; $target_plugins = [ 'translatepress-personal/index.php', 'translatepress-developer/index.php', 'translatepress-business/index.php' ]; if( !in_array( $plugin, $target_plugins ) ) return; // Update user version tag $args = array( 'method' => 'POST', 'body' => [ 'email' => self::$plugin_optin_email, 'version' => 'free', ], ); $request = wp_remote_post( self::$api_url . 'pluginOptinUpdateVersion/', $args ); } // Advanced settings public function setup_plugin_optin_advanced_setting( $settings_array ){ $settings_array[] = array( 'name' => 'plugin_optin_setting', 'type' => 'checkbox', 'label' => esc_html__( 'Marketing optin', 'translatepress-multilingual' ), 'description' => esc_html__( 'Opt in to our security and feature updates notifications, and non-sensitive diagnostic tracking.', 'translatepress-multilingual' ), 'id' => 'miscellaneous_options', 'container' => 'miscellaneous_options' ); return $settings_array; } public function process_plugin_optin_advanced_setting( $settings, $submitted_settings, $previous_settings ){ if( !isset( $settings['plugin_optin_setting'] ) || $settings['plugin_optin_setting'] == 'no' ){ update_option( self::$plugin_option_key, 'no' ); if( self::$plugin_optin_email === false ) return $settings; $args = array( 'method' => 'POST', 'body' => [ 'email' => self::$plugin_optin_email, ], ); $request = wp_remote_post( self::$api_url . 'pluginOptinArchiveSubscriber/', $args ); } else if ( $settings['plugin_optin_setting'] == 'yes' ){ if( isset( $previous_settings['plugin_optin_setting'] ) && $settings['plugin_optin_setting'] == $previous_settings['plugin_optin_setting'] ){ // if the user has not changed the setting, we don't need to send the data again but if the option is not set, we need to send the data if( self::$plugin_optin_status == 'yes' ) return $settings; } update_option( self::$plugin_option_key, 'yes' ); update_option( self::$plugin_option_email_key, get_option( 'admin_email' ) ); if( self::$plugin_optin_email === false ) return $settings; $args = array( 'method' => 'POST', 'body' => [ 'email' => self::$plugin_optin_email, 'name' => self::get_user_name(), 'version' => self::get_current_active_version(), ], ); $request = wp_remote_post( self::$api_url . 'pluginOptinSubscribe/', $args ); } return $settings; } // Determine current user name public static function get_user_name(){ if( !empty( self::$user_name ) ) return self::$user_name; $user = wp_get_current_user(); $name = $user->display_name; $first_name = get_user_meta( $user->ID, 'first_name', true ); $last_name = get_user_meta( $user->ID, 'last_name', true ); if( !empty( $first_name ) && !empty( $last_name ) ) $name = $first_name . ' ' . $last_name; self::$user_name = $name; return self::$user_name; } // Determine current active plugin version public static function get_current_active_version(){ if( !function_exists( 'is_plugin_active' ) ) include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); if( is_plugin_active( 'translatepress-developer/index.php' ) ) return 'developer'; elseif( is_plugin_active( 'translatepress-business/index.php' ) ) return 'business'; elseif( is_plugin_active( 'translatepress-personal/index.php' ) ) return 'personal'; return 'free'; } public static function sync_data(){ if( self::$plugin_optin_status !== 'yes' ) return; $trp_settings = get_option( 'trp_settings', 'not_set' ); $args = array( 'method' => 'POST', 'body' => array( 'home_url' => home_url(), 'product' => 'trp', 'email' => self::$plugin_optin_email, 'name' => self::get_user_name(), 'version' => self::get_current_active_version(), 'license' => get_option('trp_license_key'), 'active_plugins' => json_encode( get_option( 'active_plugins', array() ) ), 'wp_version' => get_bloginfo('version'), 'wp_locale' => get_locale(), 'plugin_version' => defined( 'TRP_PLUGIN_VERSION' ) ? TRP_PLUGIN_VERSION : '', 'php_version' => defined( 'PHP_VERSION' ) ? PHP_VERSION : '', ), ); // Only send the major version for WordPress and PHP // e.g. 1.x $target_keys = array( 'wp_version', 'php_version' ); foreach( $target_keys as $key ){ $version_number = explode( '.', $args['body'][$key] ); if( isset( $version_number[0] ) && isset( $version_number[1] ) ) $args['body'][$key] = $version_number[0] . '.' . $version_number[1]; } $args = apply_filters( 'cozmoslabs_plugin_optin_trp_metadata', $args ); $request = wp_remote_post( self::$stats_url, $args ); // echo wp_remote_retrieve_body( $request ); // die(); } } if( !class_exists( 'Cozmoslabs_Plugin_Optin_Metadata_Builder' ) ) { /** * Version 1.0.0 */ class Cozmoslabs_Plugin_Optin_Metadata_Builder { public $option_prefix = ''; public $blacklisted_option_slugs = []; public $blacklisted_option_patterns = []; public $blacklisted_option_names = []; protected $metadata; public function __construct(){ $this->metadata = [ 'settings' => [], 'add-ons' => [], 'custom' => [], 'cpt' => [], ]; add_filter( 'cozmoslabs_plugin_optin_'. $this->option_prefix .'metadata', array( $this, 'build_metadata' ) ); } public function build_metadata( $args ){ // Get all options that start with the prefix $options = $this->get_option_keys(); if( !empty( $options ) ){ foreach( $options as $option ){ // exclude exact option names if( in_array( $option['option_name'], $this->blacklisted_option_slugs ) ){ continue; } // exclude patterns if( !empty( $this->blacklisted_option_patterns ) ){ $found_pattern = false; foreach( $this->blacklisted_option_patterns as $pattern ){ if( strpos( $option['option_name'], $pattern ) !== false ){ $found_pattern = true; break; } } if( $found_pattern ) continue; } $option_value = get_option( $option['option_name'], false ); if( !empty( $option_value ) ){ if( is_array( $option_value ) ){ foreach( $option_value as $key => $value ){ if( !is_array( $value ) ){ if( in_array( $key, $this->blacklisted_option_names ) ) unset( $option_value[ $key ] ); } else { if( in_array( $key, $this->blacklisted_option_names ) ) unset( $option_value[ $key ] ); foreach( $value as $key_deep => $value_deep ){ if( in_array( $key_deep, $this->blacklisted_option_names ) ) unset( $option_value[ $key ][ $key_deep ] ); } } } } // cleanup options like array( array( 'abc' ) ) to be array( 'abc' ) if( is_array( $option_value ) && count( $option_value ) == 1 && isset( $option_value[0] ) ) $option_value = $option_value[0]; $this->metadata['settings'][ $option['option_name'] ] = $option_value; } } } // Ability to add custom data $this->metadata = apply_filters( 'cozmoslabs_plugin_optin_'. $this->option_prefix .'metadata_builder_metadata', $this->metadata ); $args['body']['metadata'] = $this->metadata; return $args; } private function get_option_keys(){ global $wpdb; if( empty( $this->option_prefix ) ) return []; $result = $wpdb->get_results( $wpdb->prepare( "SELECT option_name FROM {$wpdb->prefix}options WHERE option_name LIKE %s", $this->option_prefix . '%' ), 'ARRAY_A' ); if( !empty( $result ) ) return $result; return []; } } } class Cozmoslabs_Plugin_Optin_Metadata_Builder_TRP extends Cozmoslabs_Plugin_Optin_Metadata_Builder { public function __construct(){ $this->option_prefix = 'trp_'; parent::__construct(); $this->blacklisted_option_slugs = [ 'trp_ald_plugin_version', 'trp_db_errors', 'trp_db_stored_data', 'trp_in_sp_add_gettext_slugs', 'trp_license_details', 'trp_license_key', 'trp_machine_translated_characters', 'trp_plugin_optin', 'trp_plugin_optin_email', 'trp_plugin_version', 'trp_post_type_base_slug_translation', 'trp_seopack_version', 'trp_show_error_db_message', 'trp_show_notice_about_old_slugs_being_deleted', 'trp_taxonomy_slug_translation', 'trp_updated_database_gettext_original_id_cleanup', 'trp_updated_database_gettext_original_id_insert', 'trp_updated_database_gettext_original_id_update', 'trp_were_old_slug_tables_found', 'trp_add_ons_settings', ]; $this->blacklisted_option_names = [ 'deepl-api-key', 'google-translate-key', ]; $this->blacklisted_option_patterns = [ 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table', 'trp_woo_', ]; add_action( 'cozmoslabs_plugin_optin_'. $this->option_prefix .'metadata_builder_metadata', array( $this, 'build_custom_plugin_metadata' ) ); } public function build_custom_plugin_metadata(){ // add-ons data $this->metadata['addons'] = $this->generate_addon_settings(); $this->metadata['settings'] = $this->process_settings_metadata( $this->metadata['settings'] ); return $this->metadata; } public function generate_addon_settings(){ $add_on_option_slugs = [ 'trp_add_ons_settings', ]; $add_ons = []; foreach( $add_on_option_slugs as $option_slug ){ $option = get_option( $option_slug, false ); if( !empty( $option ) ){ foreach( $option as $slug => $value ){ if( ( is_bool( $value ) && $value == true ) || $value == 'show' ){ $add_on_name = explode( '/', $slug ); $add_on_name = str_replace( 'tp-add-on-', '', $add_on_name[0] ); $add_ons[ $add_on_name ] = true; } } } } // Add integrations as active add-ons if they have restrictions if( !empty( $this->metadata['content_restriction'] ) ) { // Elementor integration if( !empty( $this->metadata['content_restriction']['elementor_restrictions'] ) ) { $add_ons['elementor-integration'] = true; } // Gutenberg integration if( !empty( $this->metadata['content_restriction']['blocks_restrictions'] ) ) { $add_ons['gutenberg-integration'] = true; } } return $add_ons; } public function process_settings_metadata( $settings ){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings_component = $trp->get_component( 'settings' ); $trp_settings = $trp_settings_component->get_settings(); if( !empty( $settings['trp_settings']['translation-languages'] ) ){ $settings['trp_settings']['translation-languages'] = implode( ',', $settings['trp_settings']['translation-languages'] ); } if( !empty( $settings['trp_settings']['publish-languages'] ) ){ $settings['trp_settings']['publish-languages'] = implode( ',', $settings['trp_settings']['publish-languages'] ); } // In addition to Machine Translation being enabled, for the selected translation engine, verify if a license key is set. // If no license key is set, consider machine translation as disabled. if( !empty( $settings['trp_machine_translation_settings']['machine-translation'] ) && $settings['trp_machine_translation_settings']['machine-translation'] == 'yes' && !empty( $settings['trp_machine_translation_settings']['translation-engine'] ) ){ if( $settings['trp_machine_translation_settings']['translation-engine'] == 'mtapi' && empty( $trp_settings['trp_license_key'] ) ){ $settings['trp_machine_translation_settings']['machine-translation'] = 'no'; } else if( $settings['trp_machine_translation_settings']['translation-engine'] == 'google_translate_v2' && empty( $trp_settings['trp_machine_translation_settings']['google-translate-key'] ) ){ $settings['trp_machine_translation_settings']['machine-translation'] = 'no'; } else if( $settings['trp_machine_translation_settings']['translation-engine'] == 'deepl' && empty( $trp_settings['trp_machine_translation_settings']['deepl-api-key'] ) ){ $settings['trp_machine_translation_settings']['machine-translation'] = 'no'; } } return $settings; } } new Cozmoslabs_Plugin_Optin_Metadata_Builder_TRP(); includes/class-language-switcher-v2.php 0000777 00000102245 15251156640 0014144 0 ustar 00 <?php if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class that renders the Vue-configured language switchers. * * Relies on the config stored in $this->settings['language-switcher']: * floater (array) – settings for the floating switcher * shortcode (array) – settings for [language-switcher] shortcode * menu (array) – settings for menu items */ class TRP_Language_Switcher_V2 { private array $settings; private array $config; private TRP_Translate_Press $trp; private TRP_Url_Converter $url_converter; private TRP_Languages $languages; private TRP_Language_Switcher_Tab $language_switcher_tab; private ?string $current_lang = null; private static ?self $instance = null; /** * @var 'desktop' | 'mobile' */ private string $viewport; /** * Get singleton instance. * * @param null $settings * @param null $trp * @return self */ public static function instance( $settings = null, $trp = null ): self { if ( self::$instance === null ) { if ( $settings === null || $trp === null ) throw new RuntimeException( 'TRP_Language_Switcher_V2::instance() requires $settings and $trp when called manually.' ); self::$instance = new self( $settings, $trp ); } return self::$instance; } /** * @param array $settings TRP settings. * @param TRP_Translate_Press $trp TRP root instance. */ private function __construct( array $settings, TRP_Translate_Press $trp ) { $this->settings = $settings; $this->url_converter = $trp->get_component( 'url_converter' ); $this->languages = $trp->get_component( 'languages' ); $this->language_switcher_tab = $trp->get_component( 'language_switcher_tab' ); $this->trp = $trp; $this->viewport = wp_is_mobile() ? 'mobile' : 'desktop'; // In case it's not yet initialized, we initialize it here $this->config = $this->language_switcher_tab->get_initial_config(); /** * Add the shortcode here instead of init, so we can run the render_shortcode function on pages excluded from translation * Needed because otherwise the shortcode wouldn't be processed at all and [language-switcher] text would appear on excluded pages */ add_shortcode( 'language-switcher', [ $this, 'render_shortcode' ] ); add_action( 'plugins_loaded', array( $this, 'resolve_language_context' ), 3 ); // Trigger on plugins loaded with higher priority for compat with Multiple Domains } /** * Initialize language switcher functionalities * * Hooked on init 1 * * @return void */ public function init() { add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] ); add_action( 'wp_footer', [ $this, 'render_floater' ], 99 ); add_filter( 'wp_get_nav_menu_items', [ $this, 'filter_menu_items' ], 10, 3 ); $this->register_ls_menu_switcher(); add_filter( 'get_user_option_metaboxhidden_nav-menus', [ $this, 'cpt_always_visible_in_menus' ] ); } public function enqueue_assets(): void { wp_enqueue_style( 'trp-language-switcher-v2', trailingslashit( TRP_PLUGIN_URL ) . 'assets/css/trp-language-switcher-v2.css', [], TRP_PLUGIN_VERSION ); wp_enqueue_script( 'trp-language-switcher-js-v2', trailingslashit( TRP_PLUGIN_URL ) . 'assets/js/trp-frontend-language-switcher.js', [], TRP_PLUGIN_VERSION ); } /** Make LS CPT available in Menus */ public function register_ls_menu_switcher() : void { register_post_type('language_switcher', [ 'exclude_from_search' => true, 'publicly_queryable' => false, 'show_ui' => true, 'show_in_nav_menus' => true, 'show_in_menu' => false, 'show_in_admin_bar' => false, 'can_export' => false, 'public' => false, 'label' => 'Language Switcher', ]); } /** * Establishes language context for the request and schedules a canonical redirect if needed. * Sets $current_lang/$needed_lang TRP globals; redirects on missing/mismatched language or slug mismatch. * * @return void */ public function resolve_language_context(): void { $lang_from_url = $this->url_converter->get_lang_from_url_string(); // may be null $needed_lang = $this->determine_needed_language($lang_from_url, $this->trp); $this->current_lang = $lang_from_url ?? $needed_lang; global $TRP_LANGUAGE, $TRP_NEEDED_LANGUAGE; $TRP_LANGUAGE = $needed_lang; $allow = apply_filters('trp_allow_language_redirect', true, $needed_lang, $this->url_converter->cur_page_url()); if ( !$allow || trp_dntcp_is_current_url_excluded() ) return; $missing_in_url = ($lang_from_url === null); $add_subdir = ($this->settings['add-subdirectory-to-default-language'] ?? 'no') === 'yes'; $default = $this->settings['default-language'] ?? ''; if ( ( $missing_in_url && $add_subdir ) || ( $missing_in_url && $needed_lang !== $default ) || ( !$missing_in_url && $needed_lang !== $lang_from_url ) ) { $TRP_NEEDED_LANGUAGE = $needed_lang; add_action('template_redirect', [ $this, 'redirect_to_correct_language' ], 10); } } private function determine_needed_language( ?string $lang_from_url, TRP_Translate_Press $trp ): string { if ( $lang_from_url === null ) { if ( ($this->settings['add-subdirectory-to-default-language'] ?? 'no') === 'yes' && isset( $this->settings['publish-languages'][0] ) ) { $needed_language = $this->settings['publish-languages'][0]; } else { $needed_language = $this->settings['default-language']; } } else { $needed_language = $lang_from_url; } return apply_filters( 'trp_needed_language', $needed_language, $lang_from_url, $this->settings, $trp ); } public function redirect_to_correct_language(): void { if ((defined('DOING_AJAX') && DOING_AJAX) || is_customize_preview()) return; if ($this->url_converter->is_sitemap_path()) return; global $TRP_NEEDED_LANGUAGE; $currLang = $this->url_converter->get_lang_from_url_string(); if ( $currLang === $TRP_NEEDED_LANGUAGE ) return; $dest = esc_url_raw((string) apply_filters( 'trp_link_to_redirect_to', $this->url_converter->get_url_for_language($TRP_NEEDED_LANGUAGE, null, ''), $TRP_NEEDED_LANGUAGE )); $should_add_subdir = ( $this->settings['add-subdirectory-to-default-language'] ?? 'no' ) === 'yes'; $status = ( $should_add_subdir && $TRP_NEEDED_LANGUAGE === ( $this->settings['default-language'] ?? '' ) ) ? (int) apply_filters('trp_redirect_status', 301, 'redirect_to_add_subdirectory_to_default_language') : (int) apply_filters('trp_redirect_status', 302, 'redirect_to_a_different_language_according_to_url_slug'); wp_safe_redirect( $dest, $status ); exit; } /** Keep LS box visible in the Menus screen */ public function cpt_always_visible_in_menus( $result ) { if ( is_array( $result ) && in_array( 'add-post-type-language_switcher', $result, true ) ) { $result = array_diff( $result, ['add-post-type-language_switcher'] ); } return $result; } /** * Floating switcher – inserted in footer. */ public function render_floater(): void { if ( !$this->floater_enabled() ) return; $config = $this->config['floater']; $layout = $config['layoutCustomizer'][ $this->viewport ] ?? $config['layoutCustomizer']['desktop']; $name_type = $layout['languageNames'] ?? 'full'; $positionClass = 'trp-switcher-position-' . ( strpos( $layout['position'], 'top' ) !== false ? 'top' : 'bottom' ); $is_opposite = (bool) $config['oppositeLanguage']; $list = $this->get_language_items( $name_type, $is_opposite ); $current_language = $list[0]; global $TRP_LANGUAGE; /** We do this in order to keep the language order consistent. Currently selected language is always first. */ if ( $config['type'] === 'side-by-side' && $TRP_LANGUAGE !== $this->settings['default-language'] ) $list = array_reverse( $list ); $styles = $this->build_floater_style_attr( $config, $layout ); $viewport = $this->viewport; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo apply_filters( 'trp_floater_ls_html_v2', $this->get_template( $this->template_path( "floating-switcher.php" ), compact( 'list', 'styles', 'config', 'viewport', 'positionClass', 'is_opposite', 'current_language' ), true ) ); if ( !empty( $config['enableCustomCss'] ) && !empty( $config['customCss'] ) && is_string( $config['customCss'] ) ) { $css = str_ireplace( '</style', '', $config['customCss'] ); echo '<style id="trp-language-switcher-custom-css">' . $css . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } /** * Shortcode switcher. * * Usage: [language-switcher] * * Reads settings from $this->config['shortcode'] and renders * a dropdown switcher with first item as the current language. * * @param array $atts Shortcode attributes (currently unused). * @return string */ public function render_shortcode( $atts = [] ): string { $loader = $this->trp->get_component( 'loader' ); if ( apply_filters( 'trp_allow_tp_to_run', true, $loader ) === false ) return ''; $atts = shortcode_atts( [ 'is_editor' => 'false', ], $atts, 'language-switcher' ); /** @var bool $is_editor is Gutenberg editor the place of render */ $is_editor = filter_var( $atts['is_editor'], FILTER_VALIDATE_BOOLEAN ); $config = ( isset( $this->config['shortcode'] ) && is_array( $this->config['shortcode'] ) ) ? $this->config['shortcode'] : []; $viewport = $this->viewport; $layout = $config['layoutCustomizer'][ $viewport ] ?? ( $config['layoutCustomizer']['desktop'] ?? [] ); $name_type = $layout['languageNames'] ?? 'full'; $flag_position = $layout['flagIconPosition'] ?? 'before'; $flag_shape = $config['flagShape'] ?? 'rect'; $open_on_click = ! empty( $config['clickLanguage'] ); $flag_ratio = ( $flag_shape === 'square' ) ? 'square' : 'rect'; $is_opposite = (bool) $config['oppositeLanguage']; $list = $this->get_language_items( $name_type, $is_opposite ); if ( empty( $list ) || !isset( $list[0]['code'] ) ) return ''; // nothing to render $item_has_label = $name_type !== 'none'; foreach ( $list as &$item ) { $url = wp_doing_ajax() ? wp_get_referer() : null; $code = $item['code']; $item['url'] = $this->url_converter->get_url_for_language( $code, $url ); $item['flag'] = $this->get_flag_html( $code, $flag_ratio, $item_has_label ); $item['name'] = isset( $item['name'] ) && is_string( $item['name'] ) ? $item['name'] : ''; } unset( $item ); $style_value = $this->build_shortcode_style_value( $config, $layout ); // Render the partial (string), then allow filtering of the final HTML $html = $this->get_template( $this->template_path( 'shortcode-switcher.php' ), compact( 'list', 'config', 'style_value', 'flag_position', 'open_on_click', 'is_editor', 'is_opposite' ), true ); $html = apply_filters( 'trp_shortcode_ls_html_v2', $html, $list, $config, $layout ); if ( ! empty( $config['enableCustomCss'] ) && ! empty( $config['customCss'] ) && is_string( $config['customCss'] ) ) { $css = str_ireplace( '</style', '', $config['customCss'] ); $html .= '<style id="trp-language-switcher-shortcode-custom-css">' . $css . '</style>'; } return $html; } /** * Menu language switcher items. * * @param array $items Menu items. * @param WP_Term $menu Menu term. * @param stdClass $args Nav menu args. * @return array */ public function filter_menu_items( array $items, $menu, $args ): array { if ( empty( $this->config['menu'] ) || !is_array( $this->config['menu'] ) ) { return $items; } $cfg = $this->config['menu']; $layout = $cfg['layoutCustomizer'][ $this->viewport ] ?? ( $cfg['layoutCustomizer']['desktop'] ?? [] ); $flagPos = in_array( ( $layout['flagIconPosition'] ?? 'before' ), [ 'before', 'after', 'hide' ], true ) ? $layout['flagIconPosition'] : 'before'; $nameOpt = in_array( ( $layout['languageNames'] ?? 'full' ), [ 'full', 'short', 'none' ], true ) ? $layout['languageNames'] : 'full'; $shape = in_array( ( $layout['flagShape'] ?? 'rect' ), [ 'rect', 'square', 'rounded' ], true ) ? $layout['flagShape'] : 'rect'; $has_label = $nameOpt !== 'none'; // Used by get_flag_html to choose whether to display alt text or not $user_labels = []; foreach ( $items as $it ) { if ( $it->object !== 'language_switcher' ) continue; $ls_id = $it->object_id ?: get_post_meta( $it->ID ?? 0, '_menu_item_object_id', true ); $ls_post = $ls_id ? get_post( $ls_id ) : null; if ( !$ls_post || $ls_post->post_type !== 'language_switcher' ) continue; $token = $ls_post->post_content; if ( isset( $it->post_title ) && $it->post_title !== '' ) { $user_labels[ $token ] = $it->post_title; } } // Cache display names $published_codes = $this->settings['publish-languages'] ?? []; $full_names = $this->languages->get_language_names( $published_codes ); $current_present = false; // did we see the pseudo 'current_language'? $real_current_indexes = []; // collect real items that equal current language foreach ( $items as $i => $item ) { if ( $item->object !== 'language_switcher' ) continue; $ls_id = $item->object_id ?: get_post_meta( $item->ID ?? 0, '_menu_item_object_id', true ); $ls_post = $ls_id ? get_post( $ls_id ) : null; if ( !$ls_post || $ls_post->post_type !== 'language_switcher' ) continue; $orig = $ls_post->post_content; $code = $orig; if ( $orig === 'current_language' ) { $current_present = true; $code = $this->current_lang; } elseif ( $orig === 'opposite_language' ) { $code = $this->get_opposite_language(); } if ( $orig !== 'current_language' && $code === $this->current_lang && !is_admin() ) { $real_current_indexes[] = $i; } $label_html = !empty( $user_labels[ $orig ] ) && $has_label ? $user_labels[ $orig ] : null; if ( $label_html === null ) { $label_html = $this->build_menu_item_label_viewport( $code, [ 'flagPosition' => $flagPos, 'nameOption' => $nameOpt, 'flagShape' => $shape, ], $full_names ); } else { // Allow flags around a plain user label if configured if ( $flagPos !== 'hide' ) { $flag_html = $this->get_flag_html( $code, $shape, $has_label ); $label_html = ( $flagPos === 'before' ) ? '<span data-no-translation>' . $flag_html . ' <span class="trp-ls-language-name">' . wp_kses_post( $label_html ) . '</span></span>' : '<span data-no-translation><span class="trp-ls-language-name">' . wp_kses_post( $label_html ) . '</span> ' . $flag_html . '</span>'; } else { $label_html = '<span class="trp-ls-language-name" data-no-translation>' . wp_kses_post( $label_html ) . '</span>'; } } $item->url = esc_url( $this->url_converter->get_url_for_language( $code ) ); $item->title = $label_html; $item->classes = array_values( array_unique( array_merge( $item->classes ?? [], [ 'trp-language-switcher-container', 'trp-menu-ls-item', 'trp-menu-ls-' . esc_attr( $this->viewport ), ] ) ) ); if ( $code === $this->current_lang ) $item->classes[] = 'current-language-menu-item'; } if ( $current_present && $real_current_indexes ) { foreach ( array_reverse( $real_current_indexes ) as $idx ) { if ( isset( $items[ $idx ] ) ) { unset( $items[ $idx ] ); } } $items = array_values( $items ); } return $items; } /** * Build one menu item label based on viewport-scoped config: * - flagPosition: 'before'|'after'|'hide' * - nameOption : 'full'|'short'|'none' * - flagShape : 'rect'|'square'|'rounded' * * @param string $code * @param array $opts * @param array $full_names map[code => full name] * @return string HTML */ private function build_menu_item_label_viewport( string $code, array $opts, array $full_names ): string { $flagPos = $opts['flagPosition'] ?? 'before'; $nameOpt = $opts['nameOption'] ?? 'full'; $shape = $opts['flagShape'] ?? 'rect'; $has_label = $nameOpt !== 'none'; $flag_html = $flagPos === 'hide' ? '' : $this->get_flag_html( $code, $shape, $has_label ); $name = ''; if ( $nameOpt === 'full' ) { $name = $full_names[ $code ] ?? $code; } elseif ( $nameOpt === 'short' ) { $name = strtoupper( $this->url_converter->get_url_slug( $code, false ) ); } // 'none' stays as empty string $name_html = $name !== '' ? '<span class="trp-ls-language-name">' . esc_html( $name ) . '</span>' : ''; // Compose order $inner = ($flagPos === 'before') ? trim($flag_html . ' ' . $name_html) : trim($name_html . ' ' . $flag_html); return '<span class="trp-menu-ls-label" data-no-translation title="' . esc_html( $name ) . '">' . $inner . '</span>'; } /** * Language list. * * @param string $language_name_option * @param bool $opposite_only Whether to return only current + opposite language. * @return array */ private function get_language_items( string $language_name_option = 'full', bool $opposite_only = false ): array { $codes = current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ? ($this->settings['translation-languages'] ?? []) : ($this->settings['publish-languages'] ?? []); // Guard: establish a safe "current" $current = $this->current_lang ?: ($this->settings['default-language'] ?? null); if ( !$current || !in_array( $current, $codes, true ) ) { $current = $codes[0] ?? ($this->settings['default-language'] ?? ''); } $name_resolvers = [ 'short' => function () use ( $codes ) { return array_combine( $codes, array_map( fn( $code ) => esc_html( strtoupper( $this->url_converter->get_url_slug( $code, false ) ) ), $codes ) ); }, 'full' => fn() => $this->languages->get_language_names( $codes ), 'none' => fn() => array_fill_keys( $codes, '' ) ]; // Default to 'full' if the option is invalid or missing $resolver = $name_resolvers[ $language_name_option ] ?? $name_resolvers['full']; $names = $resolver(); if ( $opposite_only ) { $opp_code = $this->get_opposite_language(); return [[ 'code' => $opp_code, 'name' => $names[$opp_code] ?? '' ]]; } $list = [ [ 'code' => $current, 'name' => $names[ $current ] ?? '', ] ]; foreach ( $names as $code => $name ) { if ( $code !== $current ) $list[] = [ 'code' => $code, 'name' => $name ]; } return $list; } private function get_opposite_language(): string { foreach ( $this->settings['publish-languages'] as $code ) { if ( $code !== $this->current_lang ) { return $code; } } return $this->current_lang; } /** * Build inline style attribute with CSS variables. * * @param array $cfg Floater config. * @param array $layout Layout settings based on the current viewport. * * @return string */ private function build_floater_style_attr( array $cfg, array $layout ): string { $position = $layout['position'] ?? 'bottom-right'; $largeFont = $cfg['size'] === 'large'; $edgeMap = [ 'bottom-right' => [ '--bottom' => '0px', '--right' => '10vw' ], 'bottom-left' => [ '--bottom' => '0px', '--left' => '10vw' ], 'top-right' => [ '--top' => '0px', '--right' => '10vw' ], 'top-left' => [ '--top' => '0px', '--left' => '10vw' ], ]; $positionVars = $edgeMap[$position] ?? []; $vars = apply_filters( 'trp_floating_language_switcher_style_vars', array_merge( [ '--bg' => $cfg['bgColor'] ?: 'transparent', '--bg-hover' => $cfg['bgHoverColor'] ?: 'transparent', '--text' => $cfg['textColor'] ?: '#000', '--text-hover' => $cfg['textHoverColor'] ?: '#000', '--border' => $cfg['borderWidth'] ? "{$cfg['borderWidth']}px solid {$cfg['borderColor']}" : 'none', '--border-radius' => $cfg['borderRadius'] ? $this->build_radius( $cfg['borderRadius'] ) : '8px 8px 0 0', '--flag-radius' => isset( $cfg['flagRadius'] ) ? "{$cfg['flagRadius']}px" : '2px', '--flag-size' => $largeFont ? '20px' : '18px', '--aspect-ratio' => $cfg['flagShape'] === 'rect' ? '4/3' : '1', '--font-size' => $largeFont ? '16px' : '14px', '--switcher-width' => ( $layout['width'] === 'custom' ? ( $layout['customWidth'] ?? 216 ) . 'px' : 'auto' ), '--switcher-padding' => ( $layout['padding'] === 'custom' ? ( $layout['customPadding'] ?? 0 ). 'px' : '10px 0' ), '--transition-duration' => $cfg['enableTransitions'] ? '0.2s' : '0s' ], $positionVars ) ); $pairs = array(); foreach ( $vars as $k => $v ) { if ( ! is_string( $k ) || ! preg_match( '/^--[a-z0-9-]+$/i', $k ) ) { continue; } $pairs[] = $k . ':' . $v; } return implode( ';', $pairs ); } /** * Build inline style attribute with CSS variables for the shortcode switcher. * * @param array $cfg Shortcode config. * @param array $layout Layout settings based on current viewport. * @return string style="--var: value; ..." */ private function build_shortcode_style_value( array $cfg, array $layout ): string { $large_font = isset( $cfg['size'] ) && $cfg['size'] === 'large'; $font_size = $large_font ? '16px' : '14px'; $flag_size = $large_font ? '20px' : '18px'; // Scalar border radius (shortcode config uses int) $radius_scalar = isset( $cfg['borderRadius'] ) && is_numeric( $cfg['borderRadius'] ) ? (int) $cfg['borderRadius'] : 5; $border_width = isset( $cfg['borderWidth'] ) ? (int) $cfg['borderWidth'] : 0; $border_color = isset( $cfg['borderColor'] ) ? (string) $cfg['borderColor'] : '#1438521a'; $border = $border_width > 0 ? sprintf( '%dpx solid %s', $border_width, $border_color ) : 'none'; $vars = [ '--bg' => isset( $cfg['bgColor'] ) ? (string) $cfg['bgColor'] : '#ffffff', '--bg-hover' => isset( $cfg['bgHoverColor'] ) ? (string) $cfg['bgHoverColor'] : '#0000000d', '--text' => isset( $cfg['textColor'] ) ? (string) $cfg['textColor'] : '#a9adb0', '--text-hover' => isset( $cfg['textHoverColor'] ) ? (string) $cfg['textHoverColor'] : '#1d2327', // Support both a single --border var and split width/color vars (if your CSS uses either). '--border' => $border, '--border-width' => $border_width . 'px', '--border-color' => $border_color, '--border-radius' => $radius_scalar . 'px', '--flag-radius' => isset( $cfg['flagRadius'] ) ? (int) $cfg['flagRadius'] . 'px' : '2px', '--flag-size' => $flag_size, '--aspect-ratio' => ( isset( $cfg['flagShape'] ) && $cfg['flagShape'] === 'rect' ) ? '4/3' : '1', '--font-size' => $font_size, '--transition-duration' => ( $cfg['enableTransitions'] ?? true ) ? '0.2s' : '0s' ]; $pairs = []; foreach ( $vars as $k => $v ) { $pairs[] = $k . ':' . $v; } return implode( ';', $pairs ); } private function build_radius( array $r ): string { return implode( ' ', array_map( static fn( $v ) => intval( $v ) . 'px', $r ) ); } private function template_path( string $file ): string { return trailingslashit( TRP_PLUGIN_DIR ) . 'partials/' . $file; } /** * Tiny templating helper. * * @param string $path Absolute path. * @param array $vars Vars to extract. * @param bool $return Return string or echo. * @return string */ private function get_template( string $path, array $vars = [], bool $return = false ): string { if ( !file_exists( $path ) ) { return ''; } ob_start(); extract( $vars, EXTR_SKIP ); include $path; $content = ob_get_clean(); if ( $return ) { return $content; } // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Template HTML, values escaped in template. echo $content; return ''; } /** * Determine if the floater should render. * * @return bool */ private function floater_enabled(): bool { return !empty( $this->config['floater']['enabled'] ); } /** * Returns the HTML element that shows a flag using the * lipis/flag-icons CSS classes. * * @param string $language_code Either “en”, “en_US”, “pt-BR”, etc. * @param string $shape Flag shape * @param bool $has_label Whether the language item already has a label or not. We use it in order to display alt text on flags only display mode. * @return string <span class="fi fi-xx">…</span> */ public function get_flag_html( string $language_code, string $shape, bool $has_label = false ): string { global $TRP_LANGUAGE; // Allow override via filter (custom flag URL) $flag_path = apply_filters( 'trp_flags_path', '', $language_code ); $name = $this->languages->get_language_names( [ $language_code ] )[ $language_code ] ?? $language_code; $is_current = $language_code === $TRP_LANGUAGE; // Alt logic $alt = $has_label || $is_current ? '' : sprintf( __( 'Change language to %s', 'translatepress-multilingual' ), $name ); $role = $has_label || $is_current ? ' role="presentation"' : ''; $classes = [ 'trp-flag-image' ]; if ( $shape === 'rounded' ) { $classes[] = 'trp-flag-rounded'; } if ( $shape === 'square' ) { $classes[] = 'trp-flag-square'; } if ( filter_var( $flag_path, FILTER_VALIDATE_URL ) ) { $classes[] = 'trp-custom-flag'; $html = sprintf( '<img src="%s" class="%s" alt="%s" loading="lazy" decoding="async" width="18" height="14" />', esc_url( $flag_path ), esc_attr( implode( ' ', $classes ) ), esc_attr( $name ) ); return apply_filters( 'trp_flag_html', $html, $language_code, $flag_path ); } // Decide folder: square/rounded use 1x1, default is 4x3 $ratio = ( $shape === 'square' || $shape === 'rounded' ) ? '1x1' : '4x3'; // Locale-based filename (hyphen → underscore) $locale_file = str_replace( '-', '_', trim( $language_code ) ) . '.svg'; // Absolute URL & path $url = trailingslashit( TRP_PLUGIN_URL ) . 'assets/flags/' . $ratio . '/' . rawurlencode( $locale_file ); $path = trailingslashit( TRP_PLUGIN_DIR ) . 'assets/flags/' . $ratio . '/' . $locale_file; // If missing, output nothing if ( !is_readable( $path ) ) { return ''; } $html = sprintf( '<img src="%s" class="%s" alt="%s"%s loading="lazy" decoding="async" width="18" height="14" />', esc_url( $url ), esc_attr( implode( ' ', $classes ) ), esc_attr( $alt ), $role ); return apply_filters( 'trp_flag_html', $html, $language_code, $url ); } /** * Legacy function to add flag * @important This function is used in WP Rocket plugin. Please don't remove it or change its signature. * * @param $language_code * @param $language_name * @param $location * @return string * @deprecated */ public function add_flag( $language_code, $language_name, $location = NULL ) { $flags_path = TRP_PLUGIN_URL . 'assets/images/flags/'; $flags_path = apply_filters( 'trp_flags_path', $flags_path, $language_code ); $flag_file_name = $language_code . '.png'; if ( $location == 'ls_shortcode' ) { $flag_url = $flags_path . $flag_file_name; return esc_url( $flag_url ); } return $this->get_flag_html( $language_code, 'rect' ); } /** * Legacy function for rendering shortcode LS * * @param $atts * @return void * @deprecated */ public function language_switcher( $atts ){ return $this->render_shortcode(); } /** * Legacy function for rendering floater LS * */ public function add_floater_language_switcher(){ return $this->render_floater(); } /** * Legacy function used in older versions of Automatic Language Detection Add-on * */ public function add_shortcode_preferences( $settings, $language_code, $language_name ) { if ( $settings['flags'] ){ $flag = $this->add_flag($language_code, $language_name); } else { $flag = ''; } if ( $settings['full_names'] ){ $full_name = $language_name; } else { $full_name = ''; } if ( $settings['short_names'] ){ $short_name = strtoupper( $this->url_converter->get_url_slug( $language_code, false ) ); } else { $short_name = ''; } return $flag . ' ' . esc_html( $short_name . $full_name ); } } includes/class-wp-bakery-language-for-blocks.php 0000777 00000023314 15251156640 0015726 0 ustar 00 <?php // Exit if accessed directly if (!defined('ABSPATH')) { exit(); } class TRP_WPBakery { private static $_instance = null; public $shortcode_param_type_dropdown_multi = 'trp_dropdown_multi'; public $param_name_show = 'trp_param_show'; public $param_name_show_language = 'trp_param_show_language'; public $param_name_exclude = 'trp_param_exclude'; public $param_name_exclude_languages = 'trp_param_exclude_languages'; /** * Register plugin action hooks and filters */ public function __construct() { add_action('init', [$this, 'init'], PHP_INT_MAX /* We need to collect all available shortcodes */); add_filter( 'do_shortcode_tag', [$this, 'do_shortcode_tag'], PHP_INT_MAX /* We should be the last one, so no other filter can add additional content */, 3 ); } public function init() { if (class_exists('WPBMap')) { WPBMap::addAllMappedShortcodes(); } $this->register_dropdown_multi(); $this->register_params_show_for_shortcodes(); $this->register_params_exclude_for_shortcodes(); } /** * Modify the output of a given shortcode if TranslatePress is configured. */ public function do_shortcode_tag($output, $tag, $attr) { return $this->is_hidden($attr) ? '' : $output; } /** * Visual Composer does not a multi-dropdown out-of-the-box. But we can add an alternative * `type` which supports the `<select multiple` usage. * * @see https://stackoverflow.com/a/48125515/5506547 */ private function register_dropdown_multi() { vc_add_shortcode_param($this->shortcode_param_type_dropdown_multi, function ($param, $value) { if (!is_array($value)) { $param_value_arr = explode(',', $value); } else { $param_value_arr = $value; } $param_line = ''; $param_line .= '<select multiple name="' . esc_attr($param['param_name']) . '" class="wpb_vc_param_value wpb-input wpb-select ' . esc_attr($param['param_name']) . ' ' . esc_attr($param['type']) . '">'; foreach ($param['value'] as $text_val => $val) { if (is_numeric($text_val) && (is_string($val) || is_numeric($val))) { $text_val = $val; } $selected = ''; if (!empty($param_value_arr) && in_array($val, $param_value_arr)) { $selected = ' selected="selected"'; } $param_line .= '<option class="' . $val . '" value="' . $val . '"' . $selected . '>' . $text_val . '</option>'; } $param_line .= '</select>'; return $param_line; }); } /** * We need to register the parameter attributes for all available shortcodes. * * @see https://kb.wpbakery.com/docs/inner-api/vc_add_param/ */ private function register_params_show_for_shortcodes() { global $shortcode_tags; $shortcode_bases = array_keys($shortcode_tags); $group = $this->get_group(); $attributes_checkbox = [ 'type' => 'checkbox', 'heading' => __('Restrict element to language', 'translatepress-multilingual'), 'param_name' => $this->param_name_show, 'group' => $group, 'description' => __('Show this element only in one language.', 'translatepress-multilingual') ]; $attributes_value = [ 'type' => 'dropdown', 'heading' => __('Select language', 'translatepress-multilingual'), 'param_name' => $this->param_name_show_language, 'group' => $group, 'value' => array_flip($this->get_published_languages(true)), 'description' => __('Choose in which language to show this element.', 'translatepress-multilingual'), 'dependency' => [ 'element' => $this->param_name_show, 'value' => 'true' ] ]; $skip_sc = apply_filters( 'trp_wpbakery_skip_shortcodes', $this->get_skip_sc_array()); foreach ($shortcode_bases as $sh) { if ( !in_array( $sh, $skip_sc ) ) { vc_add_param( $sh, $attributes_checkbox ); vc_add_param( $sh, $attributes_value ); } } } /** * We need to register the parameter attributes for all available shortcodes. * * @see https://kb.wpbakery.com/docs/inner-api/vc_add_param/ */ private function register_params_exclude_for_shortcodes() { global $shortcode_tags; $shortcode_bases = array_keys($shortcode_tags); $group = $this->get_group(); $attributes_checkbox = [ 'type' => 'checkbox', 'heading' => __('Exclude from Language', 'translatepress-multilingual'), 'param_name' => $this->param_name_exclude, 'group' => $group, 'description' => __('Exclude this element from specific languages.', 'translatepress-multilingual') ]; $message = '<p>' . __( 'This element will still be visible when you are translating your website through the Translation Editor.', 'translatepress-multilingual' ) . '</p>'; $message .= '<p>' . __('The content of this element should be written in the default language.', 'translatepress-multilingual') . '</p>'; $attributes_value = [ 'type' => $this->shortcode_param_type_dropdown_multi, 'heading' => __('Select languages', 'translatepress-multilingual'), 'param_name' => $this->param_name_exclude_languages, 'group' => $group, 'value' => array_flip($this->get_published_languages(true)), 'description' => __('Choose from which languages to exclude this element.', 'translatepress-multilingual') . $message, 'dependency' => [ 'element' => $this->param_name_exclude, 'value' => 'true' ] ]; $skip_sc = apply_filters( 'trp_wpbakery_skip_shortcodes', $this->get_skip_sc_array()); foreach ($shortcode_bases as $sh) { if ( !in_array( $sh, $skip_sc ) ) { vc_add_param( $sh, $attributes_checkbox ); vc_add_param( $sh, $attributes_value ); } } } private function get_group() { return __('TranslatePress', 'translatepress-multilingual'); } private function get_published_languages($placeholder = false) { $trp = TRP_Translate_Press::get_trp_instance(); $trp_languages = $trp->get_component('languages'); $trp_settings = $trp->get_component('settings'); $result = $trp_languages->get_language_names($trp_settings->get_settings()['publish-languages']); if ($placeholder) { $result = array_merge(['' => ''], $result); } return $result; } private function is_inline_editor() { return ( isset($_GET['vc_action']) && $_GET['vc_action'] === 'vc_inline' ) || ( isset($_GET['vc_editable']) && $_GET['vc_editable'] === 'true' ); } private function is_hidden($attr) { if (!is_array($attr) || $this->is_inline_editor()) { return false; } // Restrict to only one language if (isset($attr[$this->param_name_show], $attr[$this->param_name_show_language])) { $current_language = get_locale(); if ($current_language !== $attr[$this->param_name_show_language]) { return true; } } // Exclude to multiple languages if (isset($attr[$this->param_name_exclude], $attr[$this->param_name_exclude_languages])) { $current_language = get_locale(); $exclude = explode(',', $attr[$this->param_name_exclude_languages]); if (in_array($current_language, $exclude)) { return true; } } return false; } /** * * Ensures only one instance of the class is loaded or can be loaded. * * @return TRP_WPBakery An instance of the class. */ public static function instance() { if (is_null(self::$_instance)) { self::$_instance = new self(); } return self::$_instance; } /** * * Shortcodes with missing 'params' trigger notice, so don't add TP settings to them * * Also, shortcodes with 'params' set to empty string instead of array trigger fatal error. * * @return array of shortcodes to skip */ public function get_skip_sc_array(){ $skip_sc = array(); $sc = WPBMap::getAllShortCodes(); if ( isset( $sc) && is_array($sc) ){ foreach( $sc as $key => $value ){ if ( isset($sc[$key] ) && (!isset($sc[$key]['params']) || !is_array($sc[$key]['params']) || $this->has_invalid_params($sc[$key]['params']))){ $skip_sc[] = $key; } } } return $skip_sc; } /** * Check if the parameters are valid (have numeric keys). * @param $arr array */ public function has_invalid_params($arr){ $bool=false; foreach (array_keys($arr) as $key){ if(!is_numeric($key)){ $bool = true; break; } } $invalid_params = apply_filters('trp_wp_bakery_invalid_params', $bool, $arr); return $invalid_params; } } // Instantiate Plugin Class TRP_WPBakery::instance(); includes/onboarding/class-addons.php 0000777 00000033525 15251156640 0013604 0 ustar 00 <?php class TRP_Step_Addons implements TRP_Onboarding_Step_Interface { protected WP_Error $errors; protected $settings; protected array $addons; public function __construct( $settings ){ $this->settings = $settings; $this->errors = new WP_Error(); $this->addons = $this->get_addons(); } public function handle($data) { if ($this->errors->has_errors()) { wp_redirect(add_query_arg(['step' => 'languages', 'status' => 'error'])); exit; } $this->save_addons_settings($data); wp_redirect(add_query_arg(['step' => 'finish'])); exit; } public function save_addons_settings($data){ $add_ons_settings = get_option( 'trp_add_ons_settings', array() ); $is_enabled = array(); $post_addons = isset($data['is_active']) ? (array) $data['is_active'] : array(); foreach( $post_addons as $slug => $active ){ if ($this->slug_exists($this->addons, $slug)) { $is_enabled[$slug] = $active; do_action( 'trp_add_ons_activate', $slug ); } } update_option( 'trp_add_ons_settings', $is_enabled ); } public function get_addons() { $addons_strings_array = array( 'advanced' => array( 'header' => array( 'name' => __('Advanced Add-ons', 'translatepress-multilingual'), 'description' => __('These addons extend your translation plugin and are available in the Developer, Business and Personal plans.', 'translatepress-multilingual'), ), 'addons' => array( array('slug' => 'tp-add-on-seo-pack/tp-seo-pack.php', 'type' => 'add-on', 'name' => __('SEO Pack', 'translatepress-multilingual'), 'description' => __('SEO support for page slug, page title, description and Facebook and Twitter social graph information. The HTML lang attribute is properly set.', 'translatepress-multilingual'), 'icon' => TRP_PLUGIN_URL . 'assets/images/seo_icon_translatepress_addon_page.png', ), array('slug' => 'tp-add-on-extra-languages/tp-extra-languages.php', 'type' => 'add-on', 'name' => __('Multiple Languages', 'translatepress-multilingual'), 'description' => __('Add as many languages as you need for your project to go global. Publish your language only when all your translations are done.', 'translatepress-multilingual'), 'icon' => TRP_PLUGIN_URL . 'assets/images/multiple_lang_addon_page.png', ) ) ), 'pro' => array( 'header' => array( 'name' => __('Pro Add-ons', 'translatepress-multilingual'), 'description' => __('These addons extend your translation plugin and are available in the Business and Developer plans.', 'translatepress-multilingual'), ), 'addons' => array( array( 'slug' => 'tp-add-on-deepl/index.php', 'type' => 'add-on', 'name' => __( 'DeepL Automatic Translation', 'translatepress-multilingual' ), 'description' => __( 'Automatically translate your website through the DeepL API.', 'translatepress-multilingual' ), 'icon' => TRP_PLUGIN_URL . 'assets/images/deepl-add-on-page.png', ), array( 'slug' => 'tp-add-on-automatic-language-detection/tp-automatic-language-detection.php', 'type' => 'add-on', 'name' => __( 'Automatic User Language Detection', 'translatepress-multilingual' ), 'description' => __( 'Prompts visitors to switch to their preferred language based on their browser settings or IP address and remembers the last visited language.', 'translatepress-multilingual' ), 'icon' => TRP_PLUGIN_URL . 'assets/images/automatic_user_lang_detection_addon_page.png', ), array( 'slug' => 'tp-add-on-translator-accounts/index.php', 'type' => 'add-on', 'name' => __( 'Translator Accounts', 'translatepress-multilingual' ), 'description' => __( 'Create translator accounts for new users or allow existing users that are not administrators to translate your website.', 'translatepress-multilingual' ), 'icon' => TRP_PLUGIN_URL . 'assets/images/translator_accounts_addon_page.png', ), array( 'slug' => 'tp-add-on-browse-as-other-roles/tp-browse-as-other-role.php', 'type' => 'add-on', 'name' => __( 'Browse As User Role', 'translatepress-multilingual' ), 'description' => __( 'Navigate your website just like a particular user role would. Really useful for dynamic content or hidden content that appears for particular users.', 'translatepress-multilingual' ), 'icon' => TRP_PLUGIN_URL . 'assets/images/browse_as_user_role_addon_page.png', ), array( 'slug' => 'tp-add-on-navigation-based-on-language/tp-navigation-based-on-language.php', 'type' => 'add-on', 'name' => __( 'Navigation Based on Language', 'translatepress-multilingual' ), 'description' => __( 'Configure different menu items for different languages.', 'translatepress-multilingual' ), 'icon' => TRP_PLUGIN_URL . 'assets/images/navigation_based_on_lang_addon_page.png', ), array( 'slug' => 'tp-add-on-multiple-domains/tp-multiple-domains.php', 'type' => 'add-on', 'name' => __( 'Different Domain per Language', 'translatepress-multilingual' ), 'description' => __( 'Connect separate domains or subdomains to each of your translated versions. Strengthen your brand’s local identity and boost SEO performance for every language you support.', 'translatepress-multilingual' ), 'icon' => TRP_PLUGIN_URL . 'assets/images/multiple_domains_addon_page.png', ), ) ) ); return $addons_strings_array; } private function slug_exists($array, $target_slug) { foreach ($array as $key => $value) { if ($key === 'slug' && $value === $target_slug) { return true; } if (is_array($value) && $this->slug_exists($value, $target_slug)) { return true; } } return false; } public function render() { $trp = TRP_Translate_Press::get_trp_instance(); $translatepress_product_name = reset($trp->tp_product_name); $license_status = get_option('trp_license_status'); $license_details = get_option('trp_license_details'); set_transient('trp_onboarding_previous_step', 'addons', 3600); // 1 hour expiry // Validate that we have a valid license for automatic translation if ($license_status !== 'valid' || !isset($license_details['valid'][0])) { $license_status = 'invalid'; } if ($this->errors instanceof WP_Error) { foreach ($this->errors->get_error_messages() as $message) { echo '<div class="notice notice-error"><p>' . esc_html($message) . '</p></div>'; } } $addons_settings = get_option( 'trp_add_ons_settings', array() ); ?> <h1><?php esc_html_e('Enable Modules', 'translatepress-multilingual'); ?></h1> <h3><?php esc_html_e('Enable Add-on modules to extend TranslatePress and enhance the functionality of your translated site.', 'translatepress-multilingual'); ?></h3> <?php if($license_status == 'invalid' || $translatepress_product_name == 'TranslatePress' ) : ?> <div class="trp-extra-languages-error"> <div class="trp-upgrade-notice"> <?php esc_html_e('More functionality with TranslatePress Pro.', 'translatepress-multilingual'); ?> <a href="https://translatepress.com/pricing/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=enable-addons" class="trp-upgrade-notice-button"><span><?php esc_html_e('Upgrade now ↗', 'translatepress-multilingual'); ?></span></a> </div> <p style="padding: 0 1rem;"><?php esc_html_e('Already a Pro User?', 'translatepress-multilingual'); ?> <a href="<?php echo esc_url(add_query_arg(['step' => 'install'])); ?>"> <?php esc_html_e('Activate License Key', 'translatepress-multilingual'); ?></a></p> </div> <?php endif; ?> <form method="post"> <?php wp_nonce_field('trp_onboarding_addons'); ?> <?php $addons = $this->get_addons(); foreach ( $addons as $type => $addon_type ) : ?> <h3 id="trp-addon-text-h3"><?php echo esc_html( $addon_type['header']['name'] ); ?></h3> <h4 id="trp-addon-text-h4"><?php echo esc_html( $addon_type['header']['description'] ); ?></h4> <div class="add-ons-table"> <div id="add-on-row"> <div id="icon" class="trp-cell-add-on manage-column column-icon column-primary"></div> <div id="add_on" class="trp-cell-add-on manage-column column-add_on"></div> <div id="actions" class="trp-cell-add-on manage-column column-actions"></div> </div> <div class="the-list-addon-page"> <?php foreach ( $addon_type['addons'] as $addon) : ?> <?php $disabled = true; if ($translatepress_product_name == 'TranslatePress Personal' && $type == 'advanced') { $disabled = false; } if ($translatepress_product_name == 'TranslatePress Business' || $translatepress_product_name == 'TranslatePress Developer') { $disabled = false; } if ($license_status == 'invalid') { $disabled = true; } $active = false; if (!$disabled) { $active = !empty($addons_settings[$addon['slug']]); } ?> <div class="add-on-row"> <div class="trp-cell-add-on icon column-icon" data-colname> <img class="addon-icon" src="<?php echo esc_html($addon['icon']); ?>" width="81px" height="81px" alt="<?php echo esc_html($addon['name']); ?>"> </div> <div class="trp-cell-add-on column-add_on"> <strong class="trp-add-ons-name trp-accent-text-bold"><?php echo esc_html($addon['name']); ?></strong> <br> <h4 class="trp-primary-text trp-addon-description"><?php echo esc_html($addon['description']); ?></h4> </div> <div class="trp-cell-add-on trp-addon-button"> <div class="trp-switch"> <input type="checkbox" id="trp-id-addon-<?php echo esc_attr($addon['slug']); ?>" class="trp-switch-input" name="is_active[<?php echo esc_attr( $addon['slug']); ?>]" value="yes" <?php echo $disabled ? 'disabled style="opacity:0.5; cursor:not-allowed;"' : ''; ?> <?php checked($active); ?> /> <label for="trp-id-addon-<?php echo esc_attr($addon['slug']); ?>" class="trp-switch-label" <?php echo $disabled ? 'title="'.esc_attr__('This add-on is not available on your current plan.', 'translatepress-multilingual') .'"' : ''; ?> ></label> </div> </div> </div> <?php endforeach; ?> </div> </div> <?php endforeach; ?> <div class="trp-continue-onboarding"><button type="submit" class="trp-submit-btn"><?php esc_html_e('Continue', 'translatepress-multilingual');?></button></div> </form> <?php } } includes/onboarding/class-languages.php 0000777 00000026541 15251156640 0014302 0 ustar 00 <?php class TRP_Step_Languages implements TRP_Onboarding_Step_Interface { protected array $settings; protected TRP_Settings $settings_class; protected array $languages; protected WP_Error $errors; public function __construct( $settings ){ $this->settings = $settings; $trp = TRP_Translate_Press::get_trp_instance(); $this->languages = $trp->get_component('languages')->get_languages(); $this->settings_class = $trp->get_component('settings'); $this->errors = new WP_Error(); $status = get_option('trp_license_status'); $multiple_lang_addon_slug = 'tp-add-on-extra-languages/tp-extra-languages.php'; $addons = get_option('trp_add_ons_settings', array()); $multiple_lang_status = isset($addons[$multiple_lang_addon_slug]) ? $addons[$multiple_lang_addon_slug] : false; if ($status == 'valid' && !array_key_exists('translatepress-multilingual', $trp->tp_product_name)){ // force multiple languages addon to be enabled if license is valid, and we're not on a free license. if(is_array($addons) && !$multiple_lang_status){ $addons[$multiple_lang_addon_slug] = true; } } else { $addons[$multiple_lang_addon_slug] = false; } if ($multiple_lang_status !== $addons[$multiple_lang_addon_slug]){ update_option('trp_add_ons_settings', $addons); wp_redirect( add_query_arg( null, null ) ); exit; } } public function handle($data) { if (!wp_verify_nonce($data['_wpnonce_trp_onboarding_languages'], 'trp_onboarding_languages')) { $this->errors->add('nonce_fail_languages', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); } elseif (empty($data['default_language'])) { $this->errors->add('empty_default_language', __('You need to select a default language.', 'translatepress-multilingual')); } elseif (!$this->valid_language($data['default_language'])) { $this->errors->add('invalid_default_language', __('You are trying to add an invalid default language. Please select a valid option.', 'translatepress-multilingual')); } elseif(empty($data['translation_languages'])) { $this->errors->add('empty_additional_language', __('Please add an additional language.', 'translatepress-multilingual')); } else { foreach ($data['translation_languages'] as $additional_language){ if (!$this->valid_language($additional_language)) { if (!$this->errors->get_error_message('invalid_additional_language')){ $this->errors->add('invalid_additional_language', __('You are trying to add an invalid additional language. Please select a valid option.', 'translatepress-multilingual')); } } } } if (!$this->errors->has_errors()) { // If no errors, we save our data and redirect to next step $this->save_languages($data); wp_redirect(add_query_arg(['step' => 'switcher'])); exit; } } public function render() { // Store that we're on the languages step for install step's "Go back" navigation set_transient('trp_onboarding_previous_step', 'languages', 3600); // 1 hour expiry $default_language = $this->settings['default-language']; $translation_languages = $this->settings['translation-languages']; foreach($translation_languages as $key => $language) { if ($language === $default_language) { unset($translation_languages[$key]); // translation-languages come with the default language as part of them } } $url_slugs = isset($this->settings['url-slugs']) ? $this->settings['url-slugs'] : array(); $default_slug = isset($url_slugs[$default_language]) ? $url_slugs[$default_language] : ''; foreach ($this->errors->get_error_messages() as $message) { echo '<div class="ob-notice ob-notice-error">' . esc_html($message) . '</div>'; } ?> <h1><?php esc_html_e('Configure Site Languages', 'translatepress-multilingual'); ?></h1> <h3><?php esc_html_e('Select the default and additional languages for your website.', 'translatepress-multilingual'); ?><br/> <?php esc_html_e('You can edit your site languages at any point.', 'translatepress-multilingual'); ?></h3> <form method="post"> <?php wp_nonce_field('trp_onboarding_languages', '_wpnonce_trp_onboarding_languages'); ?> <label for="trp-default-language"><?php esc_html_e('Default Language', 'translatepress-multilingual'); ?></label> <div class="trp-default-language trp-language-wrap"> <select name="default_language" class="trp-select2"> <?php foreach ($this->languages as $lang_code => $language) { echo '<option value="' . esc_attr($lang_code) . '"' . selected($lang_code, $default_language, false) . '>' . esc_html($language) . '</option>'; } ?> </select> <div class="trp-slug-field" style="display: none;"><input type="hidden" name="url_slugs[<?php echo esc_attr($default_language); ?>]" value="<?php echo esc_attr($default_slug); ?>"/></div> </div> <p class="trp-onboarding-description"><?php esc_html_e('Select the language your content is written in.', 'translatepress-multilingual'); ?></p> <?php foreach ($translation_languages as $translation_lang_code) : ?> <div class="trp-additional-language trp-language-wrap"> <div class="trp-language-field"> <label><?php esc_html_e('Additional Language', 'translatepress-multilingual'); ?></label> <select name="translation_languages[]" class="trp-select2"> <option value=""><?php esc_html_e('Choose a secondary language...', 'translatepress-multilingual');?></option> <?php foreach ($this->languages as $lang_code => $language) { echo '<option value="' . esc_attr($lang_code) . '" '. selected($lang_code, $translation_lang_code, false) .' >' . esc_html($language) . '</option>'; } ?> </select> </div> <div class="trp-slug-field"> <label><?php esc_html_e('Slug', 'translatepress-multilingual'); ?></label> <input type="text" name="url_slugs[<?php echo esc_attr($translation_lang_code); ?>]" value="<?php echo esc_attr(isset($url_slugs[$translation_lang_code]) ? $url_slugs[$translation_lang_code] : ''); ?>" autocomplete="off" class="trp-language-slug-input"/> </div> <a class="trp-remove-language" href="#"><?php esc_html_e('Remove', 'translatepress-multilingual');?></a> </div> <?php endforeach; ?> <div class="trp-add-language-wrap"> <a id="trp-add-language" href="#" class="trp-button-secondary" style="display: inline-block"><?php esc_html_e('Add Language', 'translatepress-multilingual');?></a> </div> <div class="trp-continue-onboarding"><button class="trp-submit-btn" type="submit"><?php esc_html_e('Continue', 'translatepress-multilingual');?></button></div> </form> <template id="trp-add-language-template"> <div class="trp-additional-language trp-language-wrap"> <div class="trp-language-field"> <label><?php esc_html_e('Additional Language', 'translatepress-multilingual'); ?></label> <select name="translation_languages[]" class="trp-select2"> <option value=""><?php esc_html_e('Choose a language...', 'translatepress-multilingual');?></option> <?php foreach ($this->languages as $lang_code => $language) { echo '<option value="' . esc_attr($lang_code) . '">' . esc_html($language) . '</option>'; } ?> </select> </div> <div class="trp-slug-field"> <label><?php esc_html_e('Slug', 'translatepress-multilingual'); ?></label> <input type="text" name="url_slugs[]" autocomplete="off" class="trp-language-slug-input"/> </div> <a class="trp-remove-language" href="#"><?php esc_html_e('Remove', 'translatepress-multilingual');?></a> </div> </template> <template id="trp-languages-error"> <div class="trp-extra-languages-error"> <div class="trp-upgrade-notice"> <?php esc_html_e('Add more than two languages with TranslatePress Pro.', 'translatepress-multilingual'); ?> <a href="https://translatepress.com/pricing/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=add-languages" class="trp-upgrade-notice-button"><span><?php esc_html_e('Upgrade now ↗', 'translatepress-multilingual'); ?></span></a> </div> <p><?php esc_html_e('Already a Pro User?', 'translatepress-multilingual'); ?> <a href="<?php echo esc_url(add_query_arg(['step' => 'install'])); ?>"> <?php esc_html_e('Activate License Key', 'translatepress-multilingual'); ?></a></p> </div> </template> <?php } private function valid_language($default_language) { if (!array_key_exists($default_language, $this->languages)) { return false; } return true; } /* * Minimal processing before saving Languages settings. * Full processing happens inside TRP_Settings -> sanitize_settings() due to register_setting() triggering a hook on update_option() */ private function save_languages($data){ $trp_settings = get_option('trp_settings', array()); $trp_settings['default-language'] = sanitize_text_field($data['default_language']); if ( !isset ( $data['translation_languages'] ) ){ $trp_settings['translation-languages'] = array(); } $trp_settings['translation-languages'] = array_filter( array_unique( $data['translation_languages'] ) ); if ( !in_array( $data['default_language'], $data['translation_languages'] ) ){ array_unshift( $trp_settings['translation-languages'], $data['default_language'] ); } // we need to add information to published languages as well. $trp_settings['publish-languages'] = $trp_settings['translation-languages']; // map slugs array foreach ($data['url_slugs'] as $slug_key => $url_slug) { if (!in_array($slug_key, $trp_settings['translation-languages'])) { // ignore incorrect slug mappings to not pollute the settings. Might be overkill. unset($data['url_slugs'][$slug_key]); } } $trp_settings['url-slugs'] = $data['url_slugs']; // This is important. Without it the tables are not being generated. $trp_settings = $this->settings_class->sanitize_settings( $trp_settings ); update_option('trp_settings', $trp_settings); } } includes/onboarding/interface-onboarding-step.php 0000777 00000000543 15251156640 0016254 0 ustar 00 <?php if ( ! defined( 'ABSPATH' ) ) exit; interface TRP_Onboarding_Step_Interface { /** * Handle form submission logic for the step. * * @param array $data * @return void */ public function handle( $data ); /** * Render the step's HTML output. * * @return void */ public function render(); } includes/onboarding/class-finish.php 0000777 00000011745 15251156640 0013614 0 ustar 00 <?php class TRP_Step_Finish implements TRP_Onboarding_Step_Interface { protected $settings; protected string $email; protected string $newsletter_checkbox; protected WP_Error $errors; public function __construct( $settings ){ $this->settings = $settings; $this->errors = new WP_Error(); $current_user = wp_get_current_user(); $this->email = $current_user->user_email; $this->newsletter_checkbox = ''; } public function handle($data) { if (!wp_verify_nonce($data['_wpnonce_trp_onboarding_finish'], 'trp_onboarding_finish')) { $this->errors->add('nonce_fail_finish', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); } // Process newsletter subscription if checked if (!empty($data['trp-checkbox-newsletter'])) { $this->email = (empty($data['newsletter-email']) ? '' : $data['newsletter-email']); $this->newsletter_checkbox = $data['trp-checkbox-newsletter']; $newsletter_email = sanitize_email($data['newsletter-email']); if (is_email($newsletter_email)) { $this->process_newsletter_subscription($newsletter_email); } else { $this->errors->add('incorrect_email', __('The email address you added is incorrect.', 'translatepress-multilingual')); } } if (!$this->errors->has_errors()) { // If no errors, we save our data and redirect to next step wp_redirect(add_query_arg('trp-edit-translation', 'true', home_url())); exit; } } public function render() { $tp_green_check = TRP_PLUGIN_URL . 'assets/images/circle-check-filled.svg'; ?> <div class="trp-finish-page-container"> <div class="trp-green-check-logo"> <img src="<?php echo esc_url( $tp_green_check ); ?>" alt="<?php esc_attr_e("Setup Complete", 'translatepress-multilingual'); ?>"> </div> <h1><?php esc_html_e("You're ready to start translating!", 'translatepress-multilingual'); ?></h1> <h3 class="trp-finish-page-text" ><?php esc_html_e('You have successfully set up TranslatePress for your website.', 'translatepress-multilingual'); ?></h3> <form method="post"> <?php wp_nonce_field('trp_onboarding_finish', '_wpnonce_trp_onboarding_finish'); ?> <div class="trp-newsletter"> <input type="checkbox" class="email-subscription-checkbox" name="trp-checkbox-newsletter" id="trp-checkbox-newsletter" <?php checked($this->newsletter_checkbox, 'on'); ?> /> <label for="trp-checkbox-newsletter" title="<?php esc_html_e('Receive ', 'translatepress-multilingual'); ?>"> <?php esc_html_e('Sign me up to the Newsletter', 'translatepress-multilingual'); ?> </label> <div class="email-subscription-wrap"> <?php foreach ($this->errors->get_error_messages() as $message) { echo '<div class="ob-notice ob-notice-error">' . esc_html($message) . '</div>'; } ?> <input id="email-field" type="text" name="newsletter-email" value="<?php echo esc_attr( $this->email ); ?>" /> </div> <button type="submit" class="trp-submit-btn trp-onboarding-finnish start"> <?php esc_html_e('Start translating', 'translatepress-multilingual');?> </button> <button type="submit" class="trp-submit-btn trp-onboarding-finnish start-submit"> <?php esc_html_e('Sign Up and Start translating', 'translatepress-multilingual');?> </button> </div> </form> </div> <?php } private function process_newsletter_subscription($email) { if (!defined('TRP_STORE_URL')) { define('TRP_STORE_URL', 'https://translatepress.com'); } $trp = TRP_Translate_Press::get_trp_instance(); $tp_product_name = reset($trp->tp_product_name); if (empty($tp_product_name)) { $tp_product_name = 'TranslatePress'; } $version_map = [ 'TranslatePress' => 'free', 'TranslatePress Personal' => 'personal', 'TranslatePress Business' => 'business', 'TranslatePress Developer' => 'developer', ]; $version = $version_map[$tp_product_name]; $data = array( 'email' => $email, 'version' => $version ); wp_remote_post(TRP_STORE_URL . '/wp-json/trp-api/emailNewsletterSubscribe', array( 'timeout' => 3, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => json_encode($data) )); } } includes/onboarding/class-autotranslation.php 0000777 00000047154 15251156640 0015566 0 ustar 00 <?php class TRP_Step_AutoTranslation implements TRP_Onboarding_Step_Interface { protected $settings; protected WP_Error $errors; public function __construct($settings) { $this->settings = $settings; $this->errors = new WP_Error(); } public function handle($data) { if (!wp_verify_nonce($data['_wpnonce_trp_onboarding_autotranslation'], 'trp_onboarding_autotranslation')) { $this->errors->add('nonce_fail_languages', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); } // Handle license activation if provided $license = isset($data['trp_license']) ? sanitize_text_field($data['trp_license']) : ''; if (!empty($license)) { // Save the license and trigger license check update_option('trp_license_key', $license); $trp = TRP_Translate_Press::get_trp_instance(); $trp->get_component('plugin_updater')->force_check_license('true'); // Check license validation results $this->check_license_validation_results(); } // Handle automatic translation setting $machine_translation_enabled = isset($data['trp_machine_translation']) && $data['trp_machine_translation'] === 'yes'; // Get current machine translation settings $machine_translation_settings = get_option('trp_machine_translation_settings', array()); if ($machine_translation_enabled) { // Check if license is valid before enabling automatic translation $license_status = get_option('trp_license_status'); $license_details = get_option('trp_license_details'); // Validate that we have a valid license for automatic translation if ($license_status !== 'valid' || !isset($license_details['valid'][0])) { $this->errors->add('license_required', __('A valid license is required to enable Automatic Translation.', 'translatepress-multilingual')); } else { // Save automatic translation setting as enabled $machine_translation_settings['machine-translation'] = 'yes'; } } else { // Save automatic translation setting as disabled $machine_translation_settings['machine-translation'] = 'no'; } // Update the settings regardless of enabled/disabled state update_option('trp_machine_translation_settings', $machine_translation_settings); // Handle errors - don't redirect if there are errors, render() will display them if ($this->errors->has_errors()) { return; } //synchronize EDD license with MTAPI if(!empty($license)){ trp_mtapi_sync_license_call(sanitize_text_field($license)); } // Check if continue button was pressed (hidden input is present) if (isset($data['submit']) && $data['submit'] == 'activate') { // If no errors and continue button was pressed, redirect to addons step wp_redirect(add_query_arg(['step' => 'autotranslation'])); exit; } // If no errors and no continue button (e.g., just license activation), stay on current step wp_redirect(add_query_arg(['step' => 'addons'])); exit; } private function check_license_validation_results() { $license_details = get_option('trp_license_details'); // Check for invalid license details if (!empty($license_details) && !empty($license_details['invalid'])) { $license_detail = $license_details['invalid'][0]; switch($license_detail->error) { case 'expired': $this->errors->add('expired', sprintf( __('Your license key expired on %s.', 'translatepress-multilingual'), date_i18n(get_option('date_format'), strtotime($license_detail->expires, current_time('timestamp'))) )); break; case 'revoked': $this->errors->add('revoked', __('Your license key has been disabled.', 'translatepress-multilingual')); break; case 'missing': $this->errors->add('missing', __('Your TranslatePress license key is invalid or missing.', 'translatepress-multilingual')); break; case 'invalid': case 'site_inactive': $this->errors->add('site_inactive', __('Your license key is disabled for this URL. Re-enable it from <a target="_blank" href="https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=tp-ai">https://translatepress.com/account</a> -> Manage Sites.', 'translatepress-multilingual')); break; case 'item_name_mismatch': $this->errors->add('item_name_mismatch', __('<p><strong>License key mismatch.</strong> The license you entered doesn\'t match the TranslatePress version you have installed.</p><p>Please check that you\'ve installed the correct version for your license from your TranslatePress account.</p>', 'translatepress-multilingual')); break; case 'no_activations_left': $this->errors->add('no_activations_left', __('Your license key has reached its activation limit.', 'translatepress-multilingual')); break; case 'website_already_on_free_license': $this->errors->add('website_already_on_free_license', __('This website is already activated under a free license. Each website can only use one free license.', 'translatepress-multilingual')); break; default: $this->errors->add('license_error', __('An error occurred, please try again.', 'translatepress-multilingual')); break; } } } public function render() { // Store that we're on the autotranslation step for install step's "Go back" navigation set_transient('trp_onboarding_previous_step', 'autotranslation', 3600); // 1 hour expiry ?> <h1><?php esc_html_e('Enable Automatic Translation', 'translatepress-multilingual'); ?></h1> <h3><?php esc_html_e('Automatically translate your website using TranslatePress AI.', 'translatepress-multilingual'); ?></h3> <?php foreach ($this->errors->get_error_messages() as $message) { echo '<div class="ob-notice ob-notice-error">' . wp_kses_post($message) . '</div>'; } ?> <form method="post"> <?php wp_nonce_field('trp_onboarding_autotranslation', '_wpnonce_trp_onboarding_autotranslation'); require_once(TRP_PLUGIN_DIR . "/includes/mtapi/class-mtapi-customer.php"); $trp = TRP_Translate_Press::get_trp_instance(); $translatepress_version_name = reset($trp->tp_product_name); $license = get_option('trp_license_key'); $status = get_option('trp_license_status'); $details = get_option('trp_license_details'); if (!isset($details['valid'][0])) { $status = false; } if ($status === false) : ?> <div class="trp-settings-options-item trp-settings-switch__wrapper"> <div class="trp-switch "> <input type="checkbox" id="trp-machine-translation-enabled" class="trp-switch-input" name="trp_machine_translation" value="yes" disabled > <label for="trp-machine-translation-enabled" class="trp-switch-label"></label> </div> <label for="trp-machine-translation-enabled"><?php esc_html_e('Enable Automatic Translation', 'translatepress-multilingual');?></label> </div> <div class="trp-onboarding-license"> <h4> <img src="<?php echo esc_url(TRP_PLUGIN_URL . 'assets/images/'); ?>ai-icon.svg" width="24" height="24" align="top"/>TranslatePress AI<?php //this is not localized by choice ?> </h4> <p><?php esc_html_e('In order to enable Automatic Translation using TranslatePress AI, please enter your license key from', 'translatepress-multilingual');?> <a href="https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=tp-ai" target="_blank"><?php esc_html_e('your account.', 'translatepress-multilingual');?></a></p> <div> <label for="license-field">License Key</label> <div class="license-field-wrap"> <input id="license-field" type="password" name="trp_license" value="<?php echo esc_attr(get_option('trp_license_key', '')); ?>" required /> <button class="trp-button-secondary" type="submit" name="submit" value="activate"><?php esc_html_e('Activate License', 'translatepress-multilingual');?></button> </div> </div> <?php // Display errors if any if ($this->errors->has_errors()) { foreach ($this->errors->get_error_messages() as $message) { echo '<div id="trp-mtapi-key" class="ob-notice ob-notice-error">' . wp_kses_post($message) . '</div>'; } } else { ?> <div id="trp-mtapi-key" class="ob-notice ob-notice-error"> <?php esc_html_e('No Active License Detected for this website.', 'translatepress-multilingual'); ?> </div> <?php } ?> </div> <?php if ($translatepress_version_name != 'TranslatePress') : ?> <div class="trp-continue-onboarding"> <button class="trp-submit-btn" type="submit" name="submit" value="continue"><?php esc_html_e('Continue', 'translatepress-multilingual');?></button> </div> <div class="trp-primary-text"> <a href="<?php echo esc_url(add_query_arg(['step' => 'addons'])); ?>"><?php esc_html_e('Skip and continue with manual translation »', 'translatepress-multilingual'); ?></a> </div> <?php endif; ?> <?php if ($translatepress_version_name == 'TranslatePress') : ?> <div class="trp-ob-wrap trp-ob-gold-bg"> <div class="trp-ob-generate-license"> <div class="trp-ob-generate-license-header"> <h3><?php esc_html_e("Get Your Free TranslatePress AI License", 'translatepress-multilingual'); ?></h3> </div> <div class="trp-ob-generate-license-button"> <a href="<?php echo esc_url('https://translatepress.com/ai-free/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=tp-ai-free') ?>" target="_blank"> <?php esc_html_e('Generate License', 'translatepress-multilingual'); ?> </a> </div> </div> <p><?php esc_html_e('Creating a free account includes: ', 'translatepress-multilingual'); ?></p> <span class="trp-secondary-text trp-check-text"> <img src="<?php echo esc_url(TRP_PLUGIN_URL . 'assets/images/'); ?>green-circle-check.png" width="20px" height="20px"/> <?php esc_html_e('Access to TranslatePress AI for instant automatic translations', 'translatepress-multilingual'); ?> </span> <span class="trp-secondary-text trp-check-text"> <img src="<?php echo esc_url(TRP_PLUGIN_URL . 'assets/images/'); ?>green-circle-check.png" width="20px" height="20px"/> <?php esc_html_e('2000 AI words to translate automatically', 'translatepress-multilingual'); ?> </span> </div> <div class="trp-ob-wrap trp-ob-grey-bg trp-primary-text trp-ob-center"> <?php esc_html_e('Are you a TranslatePress PRO user?', 'translatepress-multilingual'); ?> <a href="<?php echo esc_url(add_query_arg(['step' => 'install'])); ?>"><?php esc_html_e('Install & Activate your pro plugin.', 'translatepress-multilingual'); ?></a> </div> <div class="trp-ob-wrap trp-primary-text trp-ob-center"> <a href="<?php echo esc_url(add_query_arg(['step' => 'addons'])); ?>"><?php esc_html_e('Skip and continue with manual translation »', 'translatepress-multilingual'); ?></a> </div> <?php endif; // $translatepress_version_name == 'TranslatePress' ?> <?php endif; // $status === false if ($status === 'valid') : $product_name = '<strong>' . str_replace('+', ' ', $details['valid'][0]->item_name) . '</strong>'; // MTAPI_URL needs to be defined in wp-config.php for local host development $mtapi_url = (defined('MTAPI_URL') ? MTAPI_URL : 'https://mtapi.translatepress.com'); $mtapi_server = new TRP_MTAPI_Customer($mtapi_url); $site_status = $mtapi_server->lookup_site($license, home_url()); $site_status['quota'] = isset ($site_status['quota']) ? $site_status['quota'] : 0; set_transient("trp_mtapi_cached_quota", $site_status['quota'], 5 * 60); $quota = ($site_status['quota'] < 500) ? 0 : ceil($site_status['quota'] / 5); // this $total_quota is not correct due to quota_used should account for ALL websites added to this license. // however, in case the site does have a user defined limit, the quota_used is correct. // without further changes to mtapi we don't have a proper way of knowing what's the quota_used. // will hide total_quota and let progress bar in place as the approximation is good enough if (!isset($site_status['quota_used'])) { $site_status['quota_used'] = 0; } $total_quota = ceil(($site_status['quota'] + $site_status['quota_used']) / 5); $formatted_quota = number_format($quota); $formatted_total_quota = number_format($total_quota); $usage_percentage = ($total_quota > 0) ? ($quota / $total_quota) * 100 : 0; ?> <?php // Check current automatic translation setting $machine_translation_settings = get_option('trp_machine_translation_settings', array()); $is_machine_translation_enabled = isset($machine_translation_settings['machine-translation']) && $machine_translation_settings['machine-translation'] === 'yes'; ?> <div class="trp-settings-options-item trp-settings-switch__wrapper"> <div class="trp-switch "> <input type="checkbox" id="trp-machine-translation-enabled" class="trp-switch-input" name="trp_machine_translation" value="yes" <?php checked($is_machine_translation_enabled, true); ?>> <label for="trp-machine-translation-enabled" class="trp-switch-label"></label> </div> <label for="trp-machine-translation-enabled">Enable Automatic Translation</label> </div> <div class="trp-engine trp-automatic-translation-engine__container" id="mtapi"> <span class="trp-primary-text-bold"> <img src="<?php echo esc_url(TRP_PLUGIN_URL . 'assets/images/'); ?>ai-icon.svg" width="24" height="24"/> TranslatePress AI <?php //this is not localized by choice ?> </span> <div class="trp-automatic-translation-license-notice__wrapper"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17 3.33989C18.5083 4.21075 19.7629 5.46042 20.6398 6.96519C21.5167 8.46997 21.9854 10.1777 21.9994 11.9192C22.0135 13.6608 21.5725 15.3758 20.72 16.8946C19.8676 18.4133 18.6332 19.6831 17.1392 20.5782C15.6452 21.4733 13.9434 21.9627 12.2021 21.998C10.4608 22.0332 8.74055 21.6131 7.21155 20.7791C5.68256 19.9452 4.39787 18.7264 3.48467 17.2434C2.57146 15.7604 2.06141 14.0646 2.005 12.3239L2 11.9999L2.005 11.6759C2.061 9.94888 2.56355 8.26585 3.46364 6.79089C4.36373 5.31592 5.63065 4.09934 7.14089 3.25977C8.65113 2.42021 10.3531 1.98629 12.081 2.00033C13.8089 2.01437 15.5036 2.47589 17 3.33989ZM15.707 9.29289C15.5348 9.12072 15.3057 9.01729 15.0627 9.002C14.8197 8.98672 14.5794 9.06064 14.387 9.20989L14.293 9.29289L11 12.5849L9.707 11.2929L9.613 11.2099C9.42058 11.0607 9.18037 10.9869 8.9374 11.0022C8.69444 11.0176 8.46541 11.121 8.29326 11.2932C8.12112 11.4653 8.01768 11.6943 8.00235 11.9373C7.98702 12.1803 8.06086 12.4205 8.21 12.6129L8.293 12.7069L10.293 14.7069L10.387 14.7899C10.5624 14.926 10.778 14.9998 11 14.9998C11.222 14.9998 11.4376 14.926 11.613 14.7899L11.707 14.7069L15.707 10.7069L15.79 10.6129C15.9393 10.4205 16.0132 10.1802 15.9979 9.93721C15.9826 9.69419 15.8792 9.46509 15.707 9.29289Z" fill="#4AB067"/> </svg> <span id="trp-mtapi-key" class="trp-primary-text"><?php printf(wp_kses(__('You have a valid %s <strong>license</strong>.', 'translatepress-multilingual'), array('strong' => array())), wp_kses($product_name, array('strong' => array())) ); ?> </span> </div> <span class="trp-secondary-text"> <?php echo "<strong>" . esc_html($formatted_quota) . "</strong>" . esc_html__(' words remaining. ', 'translatepress-multilingual'); ?> </span> <div class="trp-quota-bar"> <div class="trp-quota-progress" style="width: <?php echo esc_attr($usage_percentage); ?>%;"></div> </div> <span class="trp-secondary-text"> <?php printf( esc_html__('Manage your license & quota on the %s', 'translatepress-multilingual'), '<a href="' . esc_url('https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=tp-ai') . '" target="_blank" class="trp-settings-link"> ' . esc_html__('TranslatePress.com Account Page', 'translatepress-multilingual') . '</a>' ); ?> </span> </div> <div class="trp-continue-onboarding"> <button class="trp-submit-btn" type="submit" name="submit" value="continue"><?php esc_html_e('Continue', 'translatepress-multilingual');?></button> </div> <!--<div class="trp-primary-text trp-ob-skip"> <a href="<?php echo esc_url(add_query_arg(['step' => 'addons'])); ?>"><?php esc_html_e('Skip this step', 'translatepress-multilingual'); ?></a> </div>--> <?php endif; ?> </form> <?php } } includes/onboarding/class-install.php 0000777 00000023107 15251156640 0013775 0 ustar 00 <?php class TRP_Step_Install implements TRP_Onboarding_Step_Interface { protected array $settings; // tp-testing-page needed for testing functionality since pro plugins get disabled by the development version private array $pro_slugs = array('tp-testing-page', 'translatepress-personal', 'translatepress-business', 'translatepress-developer'); protected WP_Error $errors; public function __construct( $settings ){ $this->settings = $settings; $this->errors = new WP_Error(); // Redirect to License if we detect a version other then the free version. $trp = TRP_Translate_Press::get_trp_instance(); if(!in_array( 'TranslatePress', $trp->tp_product_name )){ wp_redirect(add_query_arg(['step' => 'license'])); } } public function handle($data) { // Handle individual plugin activation/deactivation if (isset($data['plugin_action']) && isset($data['plugin_path'])) { $this->handle_plugin_action($data); return; } // Handle plugin upload and installation $nonce = isset($data['_wpnonce_trp_onboarding_install']) ? $data['_wpnonce_trp_onboarding_install'] : ''; if (!wp_verify_nonce($nonce, 'trp_onboarding_install')) { $this->errors->add('nonce_fail_languages', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); return; } elseif (empty($_FILES['plugin_zip_file']) && !is_array($_FILES['plugin_zip_file'])) { $this->errors->add('error_plugin_zip_empty', __('Please upload a TranslatePress Pro plugin file.', 'translatepress-multilingual')); return; } include_once ABSPATH . 'wp-admin/includes/file.php'; include_once ABSPATH . 'wp-admin/includes/misc.php'; include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; include_once ABSPATH . 'wp-admin/includes/plugin.php'; // Upload file to temp location $overrides = ['test_form' => false]; $upload = wp_handle_upload($_FILES['plugin_zip_file'], $overrides); if (!empty($upload['error'])) { $this->errors->add('error_plugin_zip_file', __('Upload error: ', 'translatepress-multilingual') . esc_html($upload['error'])); return; } $zip_path = $upload['file']; // Full server path to ZIP // Install plugin from local file $skin = new Automatic_Upgrader_Skin(); $upgrader = new Plugin_Upgrader($skin); $result = $upgrader->install($zip_path, array('clear_update_cache' => true, 'overwrite_package' => true)); // Delete uploaded temp file unlink($zip_path); if (is_wp_error($result)) { $this->errors->add('error_plugin_zip_file', __('Install failed: ', 'translatepress-multilingual') . esc_html($result->get_error_message())); return; } $plugin_file = $upgrader->plugin_info(); if (!$plugin_file) { $this->errors->add('error_plugin_zip_file', __('Plugin installed, but entry file not found. ', 'translatepress-multilingual')); return; } $activation_result = activate_plugin($plugin_file); if (is_wp_error($activation_result)) { $this->errors->add('error_plugin_activation', __('Activation error: ', 'translatepress-multilingual') . esc_html($activation_result->get_error_message())); return; } if (!$this->errors->has_errors()) { // If no errors, we save our data and redirect to next step wp_redirect(add_query_arg(['step' => 'license'])); exit; } } private function handle_plugin_action($data) { $plugin_path = sanitize_text_field($data['plugin_path']); $plugin_action = sanitize_text_field($data['plugin_action']); // Validate plugin path is from our allowed pro slugs $slug = explode('/', $plugin_path)[0]; if (!in_array($slug, $this->pro_slugs)) { $this->errors->add('error_invalid_plugin', __('Invalid plugin specified.', 'translatepress-multilingual')); return; } // Verify nonce - use the same pattern as in render() $nonce_action = $plugin_action . '-plugin_' . $plugin_path; $slug_in_nonce = str_replace('-', '_', $slug); $nonce_field = '_wpnonce_' . $slug_in_nonce; $nonce = isset($data[$nonce_field]) ? $data[$nonce_field] : ''; if (!wp_verify_nonce($nonce, $nonce_action)) { $this->errors->add('nonce_fail_plugin', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); return; } include_once ABSPATH . 'wp-admin/includes/plugin.php'; if ($plugin_action === 'activate') { $result = activate_plugin($plugin_path); if (is_wp_error($result)) { $this->errors->add('error_plugin_activation', __('Plugin activation failed: ', 'translatepress-multilingual') . esc_html($result->get_error_message())); return; } } elseif ($plugin_action === 'deactivate') { deactivate_plugins($plugin_path); if (is_plugin_active($plugin_path)) { $this->errors->add('error_plugin_deactivation', __('Plugin deactivation failed.', 'translatepress-multilingual')); return; } } else { $this->errors->add('error_invalid_action', __('Invalid action specified.', 'translatepress-multilingual')); return; } // Reload the page to reflect changes if (!$this->errors->has_errors() && $plugin_action == 'activate') { wp_redirect(add_query_arg(['step' => 'install'])); exit; } } public function render() { ?> <h1><?php esc_html_e('First, install and activate TranslatePress Pro', 'translatepress-multilingual'); ?></h1> <h3> <?php esc_html_e('Please upload the TranslatePress PRO zip archive from your', 'translatepress-multilingual'); ?><br/> <a href="https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=install-pro" target="_blank"> <?php esc_html_e('TranslatePress Account', 'translatepress-multilingual'); ?></a> </h3> <?php foreach ($this->errors->get_error_messages() as $message) { echo '<div class="ob-notice ob-notice-error">' . esc_html($message) . '</div>'; } ?> <form method="post" enctype="multipart/form-data"> <?php wp_nonce_field('trp_onboarding_install', '_wpnonce_trp_onboarding_install'); ?> <div class="trp-onboarding-install"> <input type="file" name="plugin_zip_file" accept=".zip" required /><button class="trp-submit-btn" type="submit"><?php esc_html_e('Install and Activate', 'translatepress-multilingual');?></button> </div> </form> <?php $all_plugins = get_plugins(); if ($this->is_pro_installed()){ echo '<h3>' . esc_html__('Installed Pro versions', 'translatepress-multilingual') . '</h3>'; echo '<ul class="trp-plugins">'; foreach ( $all_plugins as $plugin_path => $plugin_data ) { $slug = explode( '/', $plugin_path )[0]; if ( in_array( $slug, $this->pro_slugs ) ) { $is_active = is_plugin_active( $plugin_path ); $action = $is_active ? 'deactivate' : 'activate'; $button_label = ucfirst( $action ); $nonce_action = $action . '-plugin_' . $plugin_path; $slug_in_nonce = str_replace('-', '_', $slug); ?> <li> <form method="post"> <?php wp_nonce_field($nonce_action, '_wpnonce_' . $slug_in_nonce); ?> <label><?php echo esc_html($plugin_data['Name']); ?></label> <input type="hidden" name="plugin_path" value="<?php echo esc_attr( $plugin_path ); ?>"/> <input type="hidden" name="plugin_action" value="<?php echo esc_attr( $action ); ?>"/> <button type="submit" name="submit" class="trp-button-secondary"><?php echo esc_html($button_label); ?></button> </form> </li> <?php } } echo '</ul>'; } ?> <div class="trp-ob-center"> <?php // Check transient to determine which step to go back to $previous_step = get_transient('trp_onboarding_previous_step'); $back_step = ($previous_step) ? $previous_step : 'languages'; ?> <a href="<?php echo esc_url(add_query_arg(['step' => $back_step])); ?>"><?php esc_html_e('« Go back', 'translatepress-multilingual'); ?></a> <?php if ($this->is_pro_installed()) :?> <a href="<?php echo esc_url(add_query_arg(['step' => 'license'])); ?>" style="margin-left: 2rem;"><?php esc_html_e('Activate License »', 'translatepress-multilingual'); ?></a> <?php endif; ?> </div> <?php } private function is_pro_installed() { $all_plugins = get_plugins(); foreach ( $all_plugins as $plugin_path => $plugin_data ) { $slug = explode( '/', $plugin_path )[0]; if ( in_array( $slug, $this->pro_slugs ) ) { return true; } } return false; } } includes/onboarding/class-license.php 0000777 00000016635 15251156640 0013761 0 ustar 00 <?php class TRP_Step_License implements TRP_Onboarding_Step_Interface { protected array $settings; protected WP_Error $errors; public function __construct( $settings ){ $this->settings = $settings; $this->errors = new WP_Error(); } public function handle($data) { // Handle license activation $nonce = isset($data['_wpnonce_trp_onboarding_license']) ? $data['_wpnonce_trp_onboarding_license'] : ''; $license = isset($data['trp_license']) ? $data['trp_license'] : ''; if(!empty($license)){ update_option('trp_license_key', sanitize_text_field($license)); /* * We save the license and trigger a license check * The license details and status are then saved in the options: * * trp_license_details * * trp_license_status * We'll use these options to show different error messages. */ $trp = TRP_Translate_Press::get_trp_instance(); $trp->get_component('plugin_updater')->force_check_license('true'); } if (!wp_verify_nonce($nonce, 'trp_onboarding_license')) { $this->errors->add('nonce_fail_license', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); } elseif(empty($license)) { $this->errors->add('empty_license', __('Your TranslatePress license key is invalid or missing.', 'translatepress-multilingual')); } else { // Check license validation results after activation attempt $this->check_license_validation_results(); } if (!$this->errors->has_errors()) { //synchronize EDD license with MTAPI trp_mtapi_sync_license_call(sanitize_text_field($license)); // If no errors, we save our data and redirect to next step $previous_step = get_transient('trp_onboarding_previous_step'); $previous_step = ($previous_step) ? $previous_step : 'languages'; wp_redirect(add_query_arg(['step' => $previous_step])); exit; } } private function check_license_validation_results() { $license_details = get_option('trp_license_details'); // Check for invalid license details if (!empty($license_details) && !empty($license_details['invalid'])) { $license_detail = $license_details['invalid'][0]; switch($license_detail->error) { case 'expired': $this->errors->add('expired', sprintf( __('Your license key expired on %s.', 'translatepress-multilingual'), date_i18n(get_option('date_format'), strtotime($license_detail->expires, current_time('timestamp'))) )); break; case 'revoked': $this->errors->add('revoked', __('Your license key has been disabled.', 'translatepress-multilingual')); break; case 'missing': $this->errors->add('missing', __('Your TranslatePress license key is invalid or missing.', 'translatepress-multilingual')); break; case 'invalid': case 'site_inactive': $this->errors->add('site_inactive', __('Your license key is disabled for this URL. Re-enable it from <a target="_blank" href="https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=activate-license">https://translatepress.com/account</a> -> Manage Sites.', 'translatepress-multilingual')); break; case 'item_name_mismatch': $this->errors->add('item_name_mismatch', __('<p><strong>License key mismatch.</strong> The license you entered doesn\'t match the TranslatePress version you have installed.</p><p>Please check that you\'ve installed the correct version for your license from your TranslatePress account.</p>', 'translatepress-multilingual')); break; case 'no_activations_left': $this->errors->add('no_activations_left', __('Your license key has reached its activation limit.', 'translatepress-multilingual')); break; case 'website_already_on_free_license': $this->errors->add('website_already_on_free_license', __('This website is already activated under a free license. Each website can only use one free license.', 'translatepress-multilingual')); break; default: $this->errors->add('license_error', __('An error occurred, please try again.', 'translatepress-multilingual')); break; } } } public function render() { $trp = TRP_Translate_Press::get_trp_instance(); if(in_array( 'TranslatePress', $trp->tp_product_name )){ $back_link = add_query_arg(['step' => 'install']); // we have a free version } else { $back_link = add_query_arg(['step' => 'languages']); // we have a pro version } ?> <h1><?php esc_html_e('Add your License Key', 'translatepress-multilingual'); ?></h1> <h3> <?php esc_html_e('Add your License Key to unlock all premium features. Find the License Key in your', 'translatepress-multilingual'); ?> <a href="https://translatepress.com/account/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=activate-license" target="_blank"> <?php esc_html_e('TranslatePress Account', 'translatepress-multilingual'); ?></a> </h3> <?php if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST') { // Check license status first time we access the page. // We first do a force license check, or we might get cached results otherwise. $trp = TRP_Translate_Press::get_trp_instance(); $trp->get_component('plugin_updater')->force_check_license('true'); $this->check_license_validation_results(); } $license_status = get_option('trp_license_status', ''); if ($license_status === 'valid') { echo '<div class="ob-notice ob-notice-success">' . esc_html__('Your license is valid and active.', 'translatepress-multilingual') . '</div>'; } foreach ($this->errors->get_error_messages() as $message) { echo '<div class="ob-notice ob-notice-error">' . wp_kses_post($message) . '</div>'; } ?> <form method="post" enctype="multipart/form-data"> <?php wp_nonce_field('trp_onboarding_license', '_wpnonce_trp_onboarding_license'); ?> <div class="trp-onboarding-license"> <label for="license-field">License Key</label> <div class="license-field-wrap"> <input id="license-field" type="password" name="trp_license" value="<?php echo esc_attr(get_option('trp_license_key', '')); ?>" required /> <button class="trp-submit-btn" type="submit"><?php esc_html_e('Activate License', 'translatepress-multilingual');?></button> </div> </div> <div class="trp-go-back"> <a href="<?php echo esc_url($back_link); ?>"> <?php esc_html_e('« Go Back', 'translatepress-multilingual'); ?></a> </div> </form> <?php } } includes/onboarding/class-switcher.php 0000777 00000036177 15251156640 0014172 0 ustar 00 <?php class TRP_Step_Switcher implements TRP_Onboarding_Step_Interface { protected $settings; protected $config; protected WP_Error $errors; public function __construct( $settings ){ $this->settings = $settings; // Get the language switcher tab component to access config $trp = TRP_Translate_Press::get_trp_instance(); $language_switcher_tab = $trp->get_component('language_switcher_tab'); $this->config = $language_switcher_tab->get_initial_config(); // Ensure config is an array if (!is_array($this->config)) { $this->config = []; } $this->errors = new WP_Error(); } public function handle($data) { // Validation $nonce = isset($data['_wpnonce_trp_onboarding_switcher']) ? $data['_wpnonce_trp_onboarding_switcher'] : ''; if (!wp_verify_nonce($nonce, 'trp_onboarding_switcher')) { $this->errors->add('nonce_fail_switcher', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); return; } if ($this->errors->has_errors()) { set_transient('trp_onboarding_errors', $this->errors, 30); wp_redirect(add_query_arg(['step' => 'switcher'])); exit; } // Handle floating switcher enable/disable if (isset($data['trp_language_switcher'])) { $this->config['floater']['enabled'] = ($data['trp_language_switcher'] === 'yes'); } else { $this->config['floater']['enabled'] = false; } // Handle switcher location if (isset($data['switcher_location']) && !empty($data['switcher_location'])) { $location = sanitize_text_field($data['switcher_location']); $valid_locations = ['bottom-right', 'bottom-left', 'top-right', 'top-left']; if (in_array($location, $valid_locations)) { $this->config['floater']['layoutCustomizer']['desktop']['position'] = $location; $this->config['floater']['layoutCustomizer']['mobile']['position'] = $location; } } // Handle template selection if (isset($data['switcher_template']) && !empty($data['switcher_template'])) { $template = sanitize_text_field($data['switcher_template']); $this->apply_template($template); } // Apply position-based border radius after template selection if (isset($data['switcher_location']) && !empty($data['switcher_location'])) { $position = sanitize_text_field($data['switcher_location']); $this->apply_position_based_border_radius($position); } // Save the updated config update_option('trp_language_switcher_settings', $this->config); wp_redirect(add_query_arg(['step' => 'autotranslation'])); exit; } /** * Apply template color settings to the current config * * @param string $template Template name (default, dark, border, transparent) */ private function apply_template($template) { $templates = $this->get_template_settings(); if (!isset($templates[$template])) { return; // Invalid template, skip } $template_settings = $templates[$template]; // Apply floater settings if (isset($template_settings['floater'])) { foreach ($template_settings['floater'] as $key => $value) { $this->config['floater'][$key] = $value; } } // Apply shortcode settings if (isset($template_settings['shortcode'])) { foreach ($template_settings['shortcode'] as $key => $value) { $this->config['shortcode'][$key] = $value; } } } /** * Get template color configurations based on Vue.js preset settings * * @return array Template configurations */ private function get_template_settings() { return [ 'default' => [ 'floater' => [ 'bgColor' => '#ffffff', 'bgHoverColor' => '#0000000d', 'textColor' => '#143852', 'textHoverColor' => '#1d2327', 'borderColor' => '#1438521a' ], 'shortcode' => [ 'bgColor' => '#ffffff', 'bgHoverColor' => '#0000000d', 'textColor' => '#143852', 'textHoverColor' => '#1d2327', 'borderColor' => '#1438521a' ] ], 'dark' => [ 'floater' => [ 'bgColor' => '#000000', 'bgHoverColor' => '#444444', 'textColor' => '#ffffff', 'textHoverColor' => '#eeeeee', 'borderColor' => 'transparent' ], 'shortcode' => [ 'bgColor' => '#000000', 'bgHoverColor' => '#444444', 'textColor' => '#ffffff', 'textHoverColor' => '#eeeeee', 'borderColor' => 'transparent' ] ], 'border' => [ 'floater' => [ 'bgColor' => '#FFFFFF', 'bgHoverColor' => '#000000', 'textColor' => '#143852', 'textHoverColor' => '#ffffff', 'borderColor' => '#143852' ], 'shortcode' => [ 'bgColor' => '#FFFFFF', 'bgHoverColor' => '#000000', 'textColor' => '#143852', 'textHoverColor' => '#ffffff', 'borderColor' => '#143852' ] ], 'transparent' => [ 'floater' => [ 'bgColor' => '#FFFFFFB2', 'bgHoverColor' => '#FFFFFFB2', 'textColor' => '#000000', 'textHoverColor' => '#000000', 'borderColor' => 'transparent' ], 'shortcode' => [ 'bgColor' => '#FFFFFFB2', 'bgHoverColor' => '#FFFFFFB2', 'textColor' => '#000000', 'textHoverColor' => '#000000', 'borderColor' => 'transparent' ] ] ]; } /** * Apply position-based border radius to the floater config * * @param string $position Position (bottom-right, bottom-left, top-right, top-left) */ private function apply_position_based_border_radius($position) { // Get existing border radius or use default $existing_radius = isset($this->config['floater']['borderRadius']) ? $this->config['floater']['borderRadius'] : null; $default_radius = 8; // Default radius value if none exists // Use existing radius values if available, otherwise use default $radius_value = $default_radius; if (is_array($existing_radius) && !empty($existing_radius)) { // Use the first non-zero value from existing radius, or default if all are zero foreach ($existing_radius as $r) { if ($r > 0) { $radius_value = $r; break; } } } // Calculate border radius based on position // borderRadius format: [top-left, top-right, bottom-right, bottom-left] switch ($position) { case 'bottom-left': case 'bottom-right': // Bottom positions: top corners have radius, bottom corners are 0 $border_radius = [$radius_value, $radius_value, 0, 0]; break; case 'top-left': case 'top-right': // Top positions: bottom corners have radius, top corners are 0 $border_radius = [0, 0, $radius_value, $radius_value]; break; default: // Fallback to existing radius or default $border_radius = is_array($existing_radius) ? $existing_radius : [$default_radius, $default_radius, 0, 0]; break; } // Apply the border radius to floater config $this->config['floater']['borderRadius'] = $border_radius; } public function render() { $tp_switcher_default = TRP_PLUGIN_URL . 'assets/images/onboarding-switcher-default.svg'; $tp_switcher_dark = TRP_PLUGIN_URL . 'assets/images/onboarding-switcher-dark.svg'; $tp_switcher_border = TRP_PLUGIN_URL . 'assets/images/onboarding-switcher-border.svg'; $tp_switcher_transparent = TRP_PLUGIN_URL . 'assets/images/onboarding-switcher-transparent.svg'; // Get current values for form defaults $floater_enabled = isset($this->config['floater']['enabled']) && !empty($this->config['floater']['enabled']); $current_position = isset($this->config['floater']['layoutCustomizer']['desktop']['position']) ? $this->config['floater']['layoutCustomizer']['desktop']['position'] : 'bottom-right'; ?> <?php foreach ($this->errors->get_error_messages() as $message) { echo '<div class="ob-notice ob-notice-error">' . esc_html($message) . '</div>'; } ?> <h1><?php esc_html_e('Set up Language Switcher', 'translatepress-multilingual'); ?></h1> <h3><?php esc_html_e('Select the style of the language switcher. You will find more ways to display it, in plugin settings.', 'translatepress-multilingual'); ?></h3> <form method="post" class="trp-switcher-wrap"> <?php wp_nonce_field('trp_onboarding_switcher', '_wpnonce_trp_onboarding_switcher'); ?> <!-- Enable Floating Switcher --> <div class="trp-settings-options-item"> <label for="trp-machine-translation-enabled">Enable Floating Switcher</label> <div class="trp-switch"> <input type="checkbox" id="trp-language-switcher-enabled" class="trp-switch-input" name="trp_language_switcher" value="yes" <?php checked($floater_enabled); ?>> <label for="trp-language-switcher-enabled" class="trp-switch-label"></label> </div> </div> <p class="trp-onboarding-description"><?php esc_html_e('Displays a small language drop-down across your website, in a corner of your choosing.', 'translatepress-multilingual'); ?></p> <!-- Switcher Location --> <div class="trp-settings-options-item"> <label for="trp-switcher-location"><?php esc_html_e('Switcher Location', 'translatepress-multilingual'); ?></label> <select id="trp-switcher-location" name="switcher_location"> <option value="bottom-right" <?php selected($current_position, 'bottom-right'); ?>><?php esc_html_e('Bottom Right', 'translatepress-multilingual'); ?></option> <option value="bottom-left" <?php selected($current_position, 'bottom-left'); ?>><?php esc_html_e('Bottom Left', 'translatepress-multilingual'); ?></option> <option value="top-right" <?php selected($current_position, 'top-right'); ?>><?php esc_html_e('Top Right', 'translatepress-multilingual'); ?></option> <option value="top-left" <?php selected($current_position, 'top-left'); ?>><?php esc_html_e('Top Left', 'translatepress-multilingual'); ?></option> </select> </div> <!-- Switcher Template --> <div> <label for=""><?php esc_html_e('Apply a Template', 'translatepress-multilingual'); ?></label> <p class="trp-onboarding-description"><?php esc_html_e('You can customize the design later', 'translatepress-multilingual'); ?></p> <div class="trp-switcher-templates"> <div class="trp-template-row"> <!-- Default Template --> <div class="trp-template-preview"> <div class="trp-switcher-img"> <img src="<?php echo esc_url( $tp_switcher_default ); ?>" alt="<?php esc_attr_e('Default Template', 'translatepress-multilingual'); ?>" /> </div> <label class="trp-template-option"> <input type="radio" name="switcher_template" value="default"> <?php esc_html_e('Default', 'translatepress-multilingual'); ?> </label> </div> <!-- Dark Template --> <div class="trp-template-preview"> <div class="trp-switcher-img"> <img src="<?php echo esc_url( $tp_switcher_dark ); ?>" alt="<?php esc_attr_e('Dark Template', 'translatepress-multilingual'); ?>" /> </div> <label class="trp-template-option"> <input type="radio" name="switcher_template" value="dark"> <?php esc_html_e('Dark', 'translatepress-multilingual'); ?> </label> </div> </div> <div class="trp-template-row"> <!-- Border Template --> <div class="trp-template-preview"> <div class="trp-switcher-img"> <img src="<?php echo esc_url( $tp_switcher_border ); ?>" alt="<?php esc_attr_e('Border Template', 'translatepress-multilingual'); ?>" /> </div> <label class="trp-template-option"> <input type="radio" name="switcher_template" value="border"> <?php esc_html_e('Border', 'translatepress-multilingual'); ?> </label> </div> <!-- Transparent Template --> <div class="trp-template-preview"> <div class="trp-switcher-img"> <img src="<?php echo esc_url( $tp_switcher_transparent ); ?>" alt="<?php esc_attr_e('Transparent Template', 'translatepress-multilingual'); ?>" /> </div> <label class="trp-template-option"> <input type="radio" name="switcher_template" value="transparent"> <?php esc_html_e('Transparent', 'translatepress-multilingual'); ?> </label> </div> </div> </div> </div> <div class="trp-continue-onboarding"> <button type="submit" class="trp-submit-btn" style="min-width: calc(50% - 0.5rem) !important; "><?php esc_html_e('Continue', 'translatepress-multilingual'); ?></button> </div> </form> <?php } } includes/onboarding/class-welcome.php 0000777 00000003705 15251156640 0013764 0 ustar 00 <?php class TRP_Step_Welcome implements TRP_Onboarding_Step_Interface { protected $settings; protected WP_Error $errors; public function __construct( $settings ){ $this->settings = $settings; $this->errors = new WP_Error(); } public function handle($data) { if (!wp_verify_nonce($data['_wpnonce_trp_onboarding_welcome'], 'trp_onboarding_welcome')) { $this->errors->add('nonce_fail_welcome', __('The link you followed has expired. Please reload the page and try again.', 'translatepress-multilingual')); } if (!$this->errors->has_errors()) { wp_redirect(add_query_arg(['step' => 'languages'])); exit; } } public function render() { $tp_logo = TRP_PLUGIN_URL . 'assets/images/tp-logo-square-light.svg'; foreach ($this->errors->get_error_messages() as $message) { echo '<div class="ob-notice ob-notice-error">' . esc_html($message) . '</div>'; } ?> <form method="post"> <?php wp_nonce_field('trp_onboarding_welcome', '_wpnonce_trp_onboarding_welcome'); ?> <div class="trp-welcome-onboarding-container"> <div class="trp-settings-logo"> <img src="<?php echo esc_url( $tp_logo ); ?>" alt="TranslatePress Logo"> </div> <h1><?php esc_html_e('Welcome to TranslatePress', 'translatepress-multilingual'); ?></h1> <h3 class="trp-welcome-text" ><?php esc_html_e('Quick guided setup to configure TranslatePress in no time!', 'translatepress-multilingual'); ?></h3> <h3 class="trp-welcome-text" ><?php esc_html_e('It takes less than a minute.', 'translatepress-multilingual'); ?></h3> <div class="trp-continue-onboarding"><button type="submit" class="trp-submit-btn"><?php esc_html_e('Continue', 'translatepress-multilingual');?></button></div> </div> </form> <?php } } includes/class-rewrite-rules.php 0000777 00000002174 15251156640 0013017 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Rewrite_Rules * * Filters the .htaccess file to prevent language slug in URL * */ class TRP_Rewrite_Rules{ protected $settings; public function __construct( $settings ){ $this->settings = $settings; } /** * Remove language parameter from .htaccess in certain cases. * * Hooked to 'mod_rewrite_rules' * * @param string $htaccess_string * * @return string */ public function trp_remove_language_param( $htaccess_string ) { $url_slugs = $this->settings['url-slugs']; foreach ( $url_slugs as $key => $value ) { if( $this->settings['add-subdirectory-to-default-language'] == 'no' && $key == $this->settings['default-language'] ){ continue; } foreach ( array( '', 'index.php' ) as $base ) { $htaccess_string = str_replace( '/' . $value . '/' . $base, '/' . $base, $htaccess_string ); } } return $htaccess_string; } } includes/gettext/class-process-gettext.php 0000777 00000052772 15251156640 0015043 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Gettext_Manager * * Handles 'gettext' hook, replaces default with translation */ class TRP_Process_Gettext { protected $settings; /** @var TRP_Query */ protected $trp_query; protected $machine_translator; protected $trp_languages; protected $gettext_manager; protected $plural_forms; protected $machine_translation_codes; protected $skip_gettext_querying; /** * TRP_Gettext_Manager constructor. * * @param array $settings Settings option. */ public function __construct( $settings, $plural_forms ) { $this->settings = $settings; $this->plural_forms = $plural_forms; } /** * Function that replaces the translations with the ones in the database if they are different, wraps the texts in the html and * builds a global for machine translation with the strings that are not translated * @param $translation * @param $text * @param $domain * @return string */ public function process_gettext_strings( $translation, $text, $domain, $context = 'trp_context', $number_of_items = null, $original_plural = null ) { global $trp_wpdb_prefix, $wpdb; if ( $trp_wpdb_prefix != $wpdb->get_blog_prefix() ){ return $translation; } // if we have nested gettexts strip previous ones, and consider only the outermost $text = TRP_Gettext_Manager::strip_gettext_tags( $text ); $translation = TRP_Gettext_Manager::strip_gettext_tags( $translation ); //try here to exclude some strings that do not require translation $excluded_gettext_strings = array( '', ' ', '…', ' ', '»' ); $trim_filter = " \t\n\r\0\x0B\xA0�.,/`~!@#\$€£%^&*():;-_=+[]{}\\|?/<>1234567890'\""; if ( in_array( trim( $text, $trim_filter ), $excluded_gettext_strings ) || empty( $text ) ) return $translation; global $TRP_LANGUAGE; if ( ( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'true' ) || $domain == 'translatepress-multilingual' ) return $translation; /* for our own actions don't do nothing */ if (isset($_REQUEST['action']) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_') === 0) return $translation; if( $this->skip_gettext_querying === null ) { // apply filters takes time. Only do this once. Parameters $translation, $text, $domain are irrelevant but can't be removed due to backwards compatibility // Use trp_skip_gettext_processing hook for not adding wrappings. $this->skip_gettext_querying = apply_filters( 'trp_skip_gettext_querying', false, $translation, $text, $domain ); } /* get_locale() returns WP Settings Language (WPLANG). It might not be a language in TP so it may not have a TP table. */ $current_locale = get_locale(); global $trp_translated_gettext_texts_language; if ( !$this->skip_gettext_querying && ( !in_array( $current_locale, $this->settings['translation-languages'] ) || empty( $trp_translated_gettext_texts_language ) || $trp_translated_gettext_texts_language !== $current_locale ) ) { return $translation; } $plural_form = $this->plural_forms->get_plural_form( $number_of_items, $current_locale ); //set a global so we remember the last string we processed and if it is the same with the current one return a result immediately for performance reasons ( this could happen in loops ) global $tp_last_gettext_processed; if ( isset( $tp_last_gettext_processed[ $context . '::' . $plural_form . '::' . $text . '::' . $domain ] ) ) return $tp_last_gettext_processed[ $context . '::' . $plural_form . '::' . $text . '::' . $domain ]; if ( apply_filters( 'trp_skip_gettext_processing', false, $translation, $text, $domain ) ) return $translation; //use a global for is_ajax_on_frontend() so we don't execute it multiple times global $tp_gettext_is_ajax_on_frontend; if ( !isset( $tp_gettext_is_ajax_on_frontend ) ) $tp_gettext_is_ajax_on_frontend = TRP_Gettext_Manager::is_ajax_on_frontend(); if ( !defined( 'DOING_AJAX' ) || $tp_gettext_is_ajax_on_frontend ) { $trp = TRP_Translate_Press::get_trp_instance(); if ( !$this->gettext_manager ) { $this->gettext_manager = $trp->get_component( 'gettext_manager' ); } if ( !$this->gettext_manager->is_domain_loaded_in_locale( $domain, $current_locale ) ) { $translation = $text; } $db_id = ''; if ( !$this->skip_gettext_querying ) { global $trp_translated_gettext_texts, $trp_all_gettext_texts; $found_in_db = false; /* initiate trp query object */ if (!$this->trp_query) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component('query'); } if ( !isset( $trp_all_gettext_texts ) ) { $trp_all_gettext_texts = array(); } if ( !empty( $trp_translated_gettext_texts ) ) { if ( isset( $trp_translated_gettext_texts[ $context . '::' . $plural_form . '::' . $domain . '::' . $text ] ) ) { $trp_translated_gettext_text = $trp_translated_gettext_texts[ $context . '::' . $plural_form . '::' . $domain . '::' . $text ]; if (!empty($trp_translated_gettext_text['translated']) && $translation != $trp_translated_gettext_text['translated'] && $this->is_sprintf_compatible( $trp_translated_gettext_text['translated'] ) ) { $translation = str_replace(trim($text), trp_sanitize_string($trp_translated_gettext_text['translated']), $text); } $db_id = $trp_translated_gettext_text['id']; $found_in_db = true; // update the db if a translation appeared in the po file later if ( empty( $trp_translated_gettext_text['translated'] ) && $translation != $text && $translation != $original_plural ) { $gettext_insert_update = $this->trp_query->get_query_component('gettext_insert_update'); $gettext_insert_update->update_gettext_strings( array( array( 'id' => $db_id, 'translated' => $translation, 'status' => $this->trp_query->get_constant_human_reviewed(), ) ), $current_locale, array('id', 'translated', 'status') ); } } } if ( !$found_in_db ) { if ( !in_array( array( 'original' => $text, 'translated' => $translation, 'domain' => $domain, 'context' => $context, 'plural_form' => $plural_form ), $trp_all_gettext_texts ) ) { $translation = $this->maybe_get_older_version_translation($translation, $text, $domain, $context , $original_plural, $plural_form ); $trp_all_gettext_texts[] = array( 'original' => $text, 'translated' => $translation, 'domain' => $domain, 'context' => $context, 'plural_form' => $plural_form ); $gettext_insert_update = $this->trp_query->get_query_component('gettext_insert_update'); $db_id = $gettext_insert_update->insert_gettext_strings( array( array( 'original' => $text, 'translated' => ( $translation != $text && $translation != $original_plural ) ? $translation : '', 'domain' => $domain, 'context' => $context, 'plural_form' => $plural_form, 'original_plural' => $original_plural ) ), $current_locale ); /* insert it in the global of translated because now it is in the database */ $trp_translated_gettext_texts[ $context . '::' . $plural_form . '::' . $domain . '::' . $text ] = array( 'id' => $db_id, 'original' => $text, 'translated' => ( $translation != $text && $translation != $original_plural ) ? $translation : '', 'domain' => $domain, 'context' => $context, 'plural_form' => $plural_form ); } } $trp = TRP_Translate_Press::get_trp_instance(); if ( !$this->machine_translator ) { $this->machine_translator = $trp->get_component( 'machine_translator' ); } if ( !$this->trp_languages ) { $this->trp_languages = $trp->get_component( 'languages' ); } if ( !$this->machine_translation_codes ) { $this->machine_translation_codes = $this->trp_languages->get_iso_codes( $this->settings['translation-languages'] ); } /* We assume Gettext strings are in English so don't automatically translate into English */ if ( $this->machine_translation_codes[ $TRP_LANGUAGE ] != 'en' && $this->machine_translator->is_available( array( $TRP_LANGUAGE ) ) ) { global $trp_gettext_strings_for_machine_translation; if ( $text == $translation || $original_plural == $translation ) { foreach ( $trp_translated_gettext_texts as $trp_translated_gettext_text ) { if ( $trp_translated_gettext_text['id'] == $db_id ) { if ( $trp_translated_gettext_text['translated'] == '' && !isset( $trp_gettext_strings_for_machine_translation[ $db_id ] ) ) { $trp_gettext_strings_for_machine_translation[ $db_id ] = array( 'id' => $db_id, 'original' => $text, 'translated' => '', 'domain' => $domain, 'status' => $this->trp_query->get_constant_machine_translated(), 'context' => $context, 'plural_form' => $plural_form, 'original_plural' => $original_plural ); } break; } } } } } $blacklist_functions = apply_filters( 'trp_gettext_blacklist_functions', array( 'wp_enqueue_script', 'wp_enqueue_scripts', 'wp_editor', 'wp_enqueue_media', 'wp_register_script', 'wp_print_scripts', 'wp_localize_script', 'wp_print_media_templates', 'get_bloginfo', 'wp_get_document_title', 'wp_title', 'wp_trim_words', 'sanitize_title', 'sanitize_title_with_dashes', 'esc_url', 'wc_get_permalink_structure' // make sure we don't touch the woocommerce permalink rewrite slugs that are translated ), $text, $translation, $domain ); if ( version_compare( PHP_VERSION, '5.4.0', '>=' ) ) { $callstack_functions = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 15 );//set a limit if it is supported to improve performance } else { $callstack_functions = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); } if ( !empty( $callstack_functions ) ) { foreach ( $callstack_functions as $callstack_function ) { if ( in_array( $callstack_function['function'], $blacklist_functions ) ) { $tp_last_gettext_processed = array( $context . '::' . $plural_form . '::' . $text . '::' . $domain => $translation ); return $translation; } /* make sure we don't touch the woocommerce process_payment function in WC_Gateway_Stripe. It does a wp_remote_post() call to stripe with localized parameters */ if ( $callstack_function['function'] == 'process_payment' && $callstack_function['class'] == 'WC_Gateway_Stripe' ) { $tp_last_gettext_processed = array( $context . '::' . $plural_form . '::' . $text . '::' . $domain => $translation ); return $translation; } } } unset( $callstack_functions );//maybe free up some memory global $trp_output_buffer_started; if ( did_action( 'init' ) && isset( $trp_output_buffer_started ) && $trp_output_buffer_started ) {//check here for our global $trp_output_buffer_started, don't wrap the gettexts if they are not processed by our cleanup callbacks for the buffers if ( ( !empty( $TRP_LANGUAGE ) && $this->settings["default-language"] != $TRP_LANGUAGE ) || ( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] == 'preview' ) ) { //add special start and end tags so that it does not influence html in any way. we will replace them with < and > at the start of the translate function /** * Compatibility with Woocomerce Payments * * In the file woocommerce-payments/includes/class-wc-payments-customer-service.php there is this line of code * $description = sprintf( __( 'Name: %1$s, Username: %2$s', 'woocommerce-payments' ), $name, $wc_customer->get_username() ); that should return admin or guest * but for some reason it returns our gettext string without the stripped gettext. */ if ( ($text != 'Name: %1$s, Username: %2$s' && $text != 'Name: %1$s, Guest' && $domain == 'woocommerce-payments') || $domain != 'woocommerce-payments') { $translation = apply_filters( 'trp_process_gettext_tags', '#!trpst#trp-gettext data-trpgettextoriginal=' . $db_id . '#!trpen#' . $translation . '#!trpst#/trp-gettext#!trpen#', $translation, $this->skip_gettext_querying, $text, $domain ); } } } } $tp_last_gettext_processed = array( $context . '::' . $plural_form . '::' . $text . '::' . $domain => $translation ); return $translation; } /** * caller for woocommerce domain texts * @param $translation * @param $text * @param $domain * @return string */ public function woocommerce_process_gettext_strings( $translation, $text, $domain ) { if ( $domain === 'woocommerce' ) { $translation = $this->process_gettext_strings( $translation, $text, $domain ); } return $translation; } /** * Function that filters gettext strings with context _x * @param $translation * @param $text * @param $context * @param $domain * @return string */ public function process_gettext_strings_with_context( $translation, $text, $context, $domain ) { $translation = $this->process_gettext_strings( $translation, $text, $domain, $context ); return $translation; } /** * caller for woocommerce domain texts with context */ public function woocommerce_process_gettext_strings_with_context( $translation, $text, $context, $domain ) { if ( $domain === 'woocommerce' ) { $translation = $this->process_gettext_strings_with_context( $translation, $text, $context, $domain ); } return $translation; } /** * function that filters the _n translations * @param $translation * @param $single * @param $plural * @param $number * @param $domain * @return string */ public function process_ngettext_strings( $translation, $single, $plural, $number, $domain ) { $translation = $this->process_gettext_strings( $translation, $single, $domain, 'trp_context', $number, $plural ); return $translation; } /** * caller for woocommerce domain numeric texts */ public function woocommerce_process_ngettext_strings( $translation, $single, $plural, $number, $domain ) { if ( $domain === 'woocommerce' ) { $translation = $this->process_ngettext_strings( $translation, $single, $plural, $number, $domain ); } return $translation; } /** * function that filters the _nx translations * @param $translation * @param $single * @param $plural * @param $number * @param $context * @param $domain * @return string */ public function process_ngettext_strings_with_context( $translation, $single, $plural, $number, $context, $domain ) { $translation = $this->process_gettext_strings( $translation, $single, $domain, $context, $number, $plural ); return $translation; } /** * caller for woocommerce domain numeric texts with context */ public function woocommerce_process_ngettext_strings_with_context( $translation, $single, $plural, $number, $context, $domain ) { if ( $domain === 'woocommerce' ) { $translation = $this->process_ngettext_strings_with_context( $translation, $single, $plural, $number, $context, $domain ); } return $translation; } /** Caller for gettext with no context and no plural. * Can't call process_gettext_strings directly due to incorrect parameter number * * @param $translation * @param $text * @param $domain * @return string */ public function process_gettext_strings_no_context( $translation, $text, $domain ){ $translation = $this->process_gettext_strings( $translation, $text, $domain ); return $translation; } /** * Caller for woocommerce domain with no context and no plural * Can't call process_gettext_strings directly due to incorrect parameter number */ public function woocommerce_process_gettext_strings_no_context( $translation, $text, $domain ) { if ($domain === 'woocommerce') { $translation = $this->process_gettext_strings($translation, $text, $domain); } return $translation; } /** * If we have a translation without context and without plural form then return that translation * * @param $translation * @param $text * @param $domain * @param $context * @param $original_plural * @param $plural_form * * @return string */ public function maybe_get_older_version_translation($translation, $text, $domain, $context , $original_plural, $plural_form){ global $trp_translated_gettext_texts; if ( $context == 'trp_context' && $original_plural === null ){ return $translation; } if ( $original_plural !== null && $plural_form != 0 ){ $text = $original_plural; } if ( isset( $trp_translated_gettext_texts[ 'trp_context' . '::' . 0 . '::' . $domain . '::' . $text ] ) && !empty($trp_translated_gettext_texts[ 'trp_context' . '::' . 0 . '::' . $domain . '::' . $text ]['translated']) && $this->is_sprintf_compatible( $trp_translated_gettext_texts[ 'trp_context' . '::' . 0 . '::' . $domain . '::' . $text ]['translated'] ) ){ $translation = str_replace(trim($text), trp_sanitize_string($trp_translated_gettext_texts[ 'trp_context' . '::' . 0 . '::' . $domain . '::' . $text ]['translated']), $text); } return $translation; } public function is_sprintf_compatible($string){ if (! apply_filters('trp_check_sprintf_compatibility', true ) ){ return true; } // 200 arguments should be enough. If a string has more than 200 placeholders then it might cause "Warning: sprintf(): Too few arguments" on certain php versions $arr = array(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1); $is_compatible = true; try{ $test = sprintf($string, ...$arr); }catch(Throwable $e){ $is_compatible = false; } return $is_compatible; } } includes/gettext/class-plural-forms.php 0000777 00000012612 15251156640 0014313 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Plural_Form * * Helpful gettext plural form functions */ class TRP_Plural_Forms { protected $_gettext_select_plural_form; protected $settings; protected $_nplurals; protected $gettext_plural_forms_headers; protected $cached_language = null; /** * TRP_Plural_Form constructor. * * @param array $settings Settings option. */ public function __construct( $settings ) { $this->settings = $settings; $this->gettext_plural_forms_headers = $this->get_plural_forms_headers(); } /** * Returns plural form needed according to the actual number of items * * Dependent on language. * * @param $number * @param $language * @return int */ public function get_plural_form( $count, $language ){ if ( $count === null ){ return 0; } $header = $this->gettext_plural_forms_headers[$language]; return $this->gettext_select_plural_form( $count, $header, $language ); } public function get_number_of_plural_forms( $language ){ list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $this->gettext_plural_forms_headers[$language] ); return $nplurals; } public function get_plural_forms_headers(){ if ( !isset( $this->gettext_plural_forms_headers ) ){ $this->gettext_plural_forms_headers = $this->set_plural_forms_headers( $this->settings['translation-languages'] ); } return $this->gettext_plural_forms_headers; } /** * Gets plural form headers from trp_db_stored_data option. * * Auto-completes missing headers from default textdomain mo files * * @param $languages * @return array */ public function set_plural_forms_headers( $languages ){ global $l10n; $trp_db_stored_data = get_option( 'trp_db_stored_data', array() ); if ( !isset($trp_db_stored_data['gettext_plural_forms_header']) ){ $trp_db_stored_data['gettext_plural_forms_header'] = array(); } $changes = false; $current_locale = get_locale(); foreach( $languages as $language_code ){ if ( !isset( $trp_db_stored_data['gettext_plural_forms_header'][$language_code] ) ){ load_default_textdomain($language_code); if ( isset($l10n['default']->headers['Plural-Forms'] ) ) { $header = $l10n['default']->headers['Plural-Forms']; }else{ $header = 'nplurals=2; plural=n != 1;'; } $trp_db_stored_data['gettext_plural_forms_header'][$language_code] = $header; $changes = true; } } if ( $changes ) { update_option( 'trp_db_stored_data', $trp_db_stored_data ); // restore previous textdomain load_default_textdomain($current_locale); } return $trp_db_stored_data['gettext_plural_forms_header']; } private function gettext_select_plural_form( $count, $header, $language ) { if ( ! isset( $this->_gettext_select_plural_form ) || $this->cached_language != $language ) { list( $nplurals, $expression ) = $this->nplurals_and_expression_from_header( $header ); $this->_nplurals = $nplurals; $this->_gettext_select_plural_form = $this->make_plural_form_function( $nplurals, $expression ); $this->cached_language = $language; } return call_user_func( $this->_gettext_select_plural_form, $count ); } private function nplurals_and_expression_from_header( $header ) { if ( preg_match( '/^\s*nplurals\s*=\s*(\d+)\s*;\s+plural\s*=\s*(.+)$/', $header, $matches ) ) { $nplurals = (int) $matches[1]; $expression = trim( $matches[2] ); return array( $nplurals, $expression ); } else { return array( 2, 'n != 1' ); } } /** * Makes a function, which will return the right translation index, according to the * plural forms header * * @param int $nplurals * @param string $expression */ private function make_plural_form_function( $nplurals, $expression ) { try { $handler = new Plural_Forms( rtrim( $expression, ';' ) ); return array( $handler, 'get' ); } catch ( Exception $e ) { // Fall back to default plural-form function. return $this->make_plural_form_function( 2, 'n != 1' ); } } /** * Copied from wp-includes/pomo/translated and adapted to allow input for plural form ($index) instead of $count * * @param $singular * @param $plural * @param int $index Changed from $count to index * @param $context * * @return mixed */ public function translate_plural( $singular, $plural, $index, $context, $translations ) { $entry = new Translation_Entry( array( 'singular' => $singular, 'plural' => $plural, 'context' => $context, ) ); $translated = $translations->translate_entry( $entry ); $total_plural_forms = $translations->get_plural_forms_count(); if ( $translated && 0 <= $index && $index < $total_plural_forms && is_array( $translated->translations ) && isset( $translated->translations[ $index ] ) ) { return $translated->translations[ $index ]; } else { return 0 == $index ? $singular : $plural; } } } includes/gettext/class-gettext-manager.php 0000777 00000062300 15251156640 0014763 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Gettext_Manager * * Handles Gettext strings */ class TRP_Gettext_Manager { protected $settings; /** @var TRP_Query */ protected $trp_query; /** @var TRP_Process_Gettext */ protected $process_gettext; /** @var TRP_Plural_Forms */ protected $plural_forms; protected $machine_translator; protected $url_converter; protected $is_admin_request = null; /** * TRP_Gettext_Manager constructor. * * @param array $settings Settings option. */ public function __construct( $settings ) { $this->settings = $settings; $this->plural_forms = new TRP_Plural_Forms( $this->settings ); $this->process_gettext = new TRP_Process_Gettext( $this->settings, $this->plural_forms ); } public function get_gettext_component( $component ) { return $this->$component; } /** * Create a global with the gettext strings that exist in the database */ public function create_gettext_translated_global() { global $trp_translated_gettext_texts, $trp_translated_gettext_texts_language; // Create gettext translated global only if processing is needed if ( $this->processing_gettext_is_needed() ) { $language = get_locale(); if ( in_array( $language, $this->settings['translation-languages'] ) ) { $trp_translated_gettext_texts_language = $language; global $wpdb, $trp_wpdb_prefix; $trp_wpdb_prefix = $wpdb->get_blog_prefix(); $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } $strings = $this->trp_query->get_all_gettext_strings( $language ); if ( ! empty( $strings ) ) { $trp_translated_gettext_texts = $strings; $trp_strings = array(); foreach ( $trp_translated_gettext_texts as $key => $value ) { $context = ( $value['context'] ) ? $value['context'] : 'trp_context'; $plural_form = ( $value['plural_form'] ) ? $value['plural_form'] : 0; $domain = ( $value['domain'] ) ? $value['domain'] : $value['tt_domain']; $original = ( $value['original'] ) ? $value['original'] : $value['tt_original']; // trp_context::0::domain::original $trp_strings[ $context . '::' . $plural_form . '::' . $domain . '::' . $original ] = $value; } $trp_translated_gettext_texts = $trp_strings; } } } } /** * function that applies the gettext filter on frontend on different hooks depending on what we need */ public function initialize_gettext_processing() { $is_ajax_on_frontend = $this::is_ajax_on_frontend(); /* on ajax hooks from frontend that have the init hook ( we found WooCommerce has it ) apply it earlier */ if ( $is_ajax_on_frontend || apply_filters( 'trp_apply_gettext_early', false ) ) { add_action( 'wp_loaded', array( $this, 'apply_gettext_filter' ) ); } else if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ){ //if we have a block theme we need to start from template_redirect hook add_action( 'template_redirect', array( $this, 'apply_gettext_filter' ), 10 ); } else {//otherwise start from the wp_head hook add_action( 'wp_head', array( $this, 'apply_gettext_filter' ), 100 ); } //if we have woocommerce installed and it is not an ajax request add a gettext hook starting from wp_loaded and remove it on wp_head if ( class_exists( 'WooCommerce' ) && ! $is_ajax_on_frontend && ! apply_filters( 'trp_apply_gettext_early', false ) ) { // WooCommerce launches some ajax calls before wp_head, so we need to apply_gettext_filter earlier to catch them add_action( 'wp_loaded', array( $this, 'apply_woocommerce_gettext_filter' ), 19 ); } } /* apply the gettext filter here */ public function apply_gettext_filter() { //if we have wocommerce installed remove te hook that was added on wp_loaded if ( class_exists( 'WooCommerce' ) ) { // WooCommerce launches some ajax calls before wp_head, so we need to apply_gettext_filter earlier to catch them remove_action( 'wp_loaded', array( $this, 'apply_woocommerce_gettext_filter' ), 19 ); } $this->call_gettext_filters(); } public function apply_woocommerce_gettext_filter() { $this->call_gettext_filters( 'woocommerce_' ); } public function processing_gettext_is_needed() { global $pagenow; if ( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component( 'url_converter' ); } if ( $this->is_admin_request === null ) { $this->is_admin_request = $this->url_converter->is_admin_request(); } $should_process = ( ( $pagenow != 'wp-login.php' ) && ( ! is_admin() || $this::is_ajax_on_frontend() ) && ! $this->is_admin_request && $pagenow != 'xmlrpc.php' ); return apply_filters( 'trp_processing_gettext_is_needed', $should_process ); } public function call_gettext_filters( $prefix = '' ) { // Add gettext filters only if processing is needed if ( !$this->processing_gettext_is_needed() ) return; add_filter( 'gettext', array( $this->process_gettext, $prefix . 'process_gettext_strings_no_context' ), 100, 3 ); add_filter( 'gettext_with_context', array( $this->process_gettext, $prefix . 'process_gettext_strings_with_context' ), 100, 4 ); add_filter( 'ngettext', array( $this->process_gettext, $prefix . 'process_ngettext_strings' ), 100, 5 ); add_filter( 'ngettext_with_context', array( $this->process_gettext, $prefix . 'process_ngettext_strings_with_context' ), 100, 6 ); do_action( 'trp_call_gettext_filters' ); } public function is_domain_loaded_in_locale( $domain, $locale ) { $localemo = $locale . '.mo'; $length = strlen( $localemo ); global $l10n; if ( isset( $l10n[ $domain ] ) && is_object( $l10n[ $domain ] ) && method_exists( $l10n[ $domain ], 'get_filename' ) ) { $mo_filename = $l10n[ $domain ]->get_filename(); if ( is_string($mo_filename) ) { // $mo_filename does not end with string $locale if ( substr( strtolower( $mo_filename ), -$length ) == strtolower( $localemo ) ) { return true; } else { return false; } } return true; } // if something is not as expected, return true so that we do not interfere return true; } public function verify_locale_of_loaded_textdomain() { global $l10n; if ( ! empty( $l10n ) && is_array( $l10n ) ) { $reload_domains = array(); $locale = get_locale(); foreach ( $l10n as $domain => $item ) { if ( ! $this->is_domain_loaded_in_locale( $domain, $locale ) ) { $reload_domains[] = $domain; } } foreach ( $reload_domains as $domain ) { if ( isset( $l10n[ $domain ] ) && is_object( $l10n[ $domain ] ) ) { $path = $l10n[ $domain ]->get_filename(); $new_path = preg_replace( '/' . $domain . '-(.*).mo$/i', $domain . '-' . $locale . '.mo', $path ); if ( $new_path !== $path ) { unset( $l10n[ $domain ] ); load_textdomain( $domain, $new_path ); } } } } // do this function only once per execution. The init hook can be called more than once remove_action( 'trp_call_gettext_filters', array( $this, 'verify_locale_of_loaded_textdomain' ) ); } /** * Function that determines if an ajax request came from the frontend * @return bool */ static function is_ajax_on_frontend() { /* for our own actions return false */ if ( isset( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 ) { return false; } $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( "url_converter" ); //check here for wp ajax or woocommerce ajax if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'WC_DOING_AJAX' ) && WC_DOING_AJAX ) ) { $referer = ''; if (!empty( $_REQUEST['_wp_http_referer'])){ // USUALLY this one is actually REQUEST_URI from the previous page. It's set by the wp_nonce_field() and wp_referer_field() // wp_get_referer() returns $_SERVER['REQUEST_URI'] from the prev page (not a full URL) // HOWEVER, the _wp_http_referer can be manually set by a plugin, so it can be a FULL URL in some cases $referer = wp_unslash( esc_url_raw( $_REQUEST['_wp_http_referer'] ) ); } elseif (!empty($_SERVER['HTTP_REFERER'])) { // this one is an actual URL that the browser sets. $referer = wp_unslash( esc_url_raw( $_SERVER['HTTP_REFERER'] ) ); } //if the request did not come from the admin set proper variables for the request (being processed in ajax they got lost) and return true // Remove the absolute home prefix from the referer and admin URL $referer_uri = trp_remove_prefix($url_converter->get_abs_home(), $referer); $admin_uri = trp_remove_prefix($url_converter->get_abs_home(), admin_url()); if(!(strpos(trim($referer_uri, '/\\'), trim($admin_uri, '/\\')) === 0)) { TRP_Gettext_Manager::set_vars_in_frontend_ajax_request( $referer ); return true; } } return false; } /** * Function that sets the needed vars in the ajax request. Beeing ajax the globals got reset and also the REQUEST globals * * @param $referer */ static function set_vars_in_frontend_ajax_request( $referer ) { /* for our own actions don't do nothing */ if ( isset( $_REQUEST['action'] ) && strpos( sanitize_text_field( $_REQUEST['action'] ), 'trp_' ) === 0 ) { return; } /* if the request came from preview mode make sure to keep it */ if ( strpos( $referer, 'trp-edit-translation=preview' ) !== false && ! isset( $_REQUEST['trp-edit-translation'] ) ) { $_REQUEST['trp-edit-translation'] = 'preview'; } if ( strpos( $referer, 'trp-edit-translation=preview' ) !== false && strpos( $referer, 'trp-view-as=' ) !== false && strpos( $referer, 'trp-view-as-nonce=' ) !== false ) { $parts = parse_url( $referer ); parse_str( $parts['query'], $query ); $_REQUEST['trp-view-as'] = $query['trp-view-as']; $_REQUEST['trp-view-as-nonce'] = $query['trp-view-as-nonce']; } global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component( 'url_converter' ); $TRP_LANGUAGE = $url_converter->get_lang_from_url_string( $referer ); if ( empty( $TRP_LANGUAGE ) ) { $settings_obj = new TRP_Settings(); $settings = $settings_obj->get_settings(); $TRP_LANGUAGE = $settings["default-language"]; } } /** * function that machine translates gettext strings */ public function machine_translate_gettext() { /* @todo set the original language to detect and also decide if we automatically translate for the default language */ global $TRP_LANGUAGE, $trp_gettext_strings_for_machine_translation; if ( ! empty( $trp_gettext_strings_for_machine_translation ) ) { if ( ! $this->machine_translator ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->machine_translator = $trp->get_component( 'machine_translator' ); } // Gettext strings are considered by default to be in the English language $source_language = apply_filters( 'trp_gettext_source_language', 'en_US', $TRP_LANGUAGE, array(), $trp_gettext_strings_for_machine_translation ); // machine translate new strings if ( $this->machine_translator->is_available( array( $source_language, $TRP_LANGUAGE ) ) ) { /* Transform associative array into ordered numeric array. We need to keep keys numeric and ordered because $new_strings and $machine_strings depend on it. * Array was constructed as associative with db ids as keys to avoid duplication. */ $trp_gettext_strings_for_machine_translation = array_values( $trp_gettext_strings_for_machine_translation ); $new_strings = array(); foreach ( $trp_gettext_strings_for_machine_translation as $trp_gettext_string_for_machine_translation ) { $new_strings[] = ( $trp_gettext_string_for_machine_translation['original_plural'] && (int)$trp_gettext_string_for_machine_translation['plural_form'] > 0 ) ? $trp_gettext_string_for_machine_translation['original_plural'] : $trp_gettext_string_for_machine_translation['original']; } if ( apply_filters( 'trp_gettext_allow_machine_translation', true, $source_language, $TRP_LANGUAGE, $new_strings, $trp_gettext_strings_for_machine_translation ) ) { $machine_strings = $this->machine_translator->translate( $new_strings, $TRP_LANGUAGE, $source_language ); } else { $machine_strings = apply_filters( 'trp_gettext_machine_translate_strings', array(), $new_strings, $TRP_LANGUAGE, $trp_gettext_strings_for_machine_translation ); } if ( ! empty( $machine_strings ) ) { foreach ( $new_strings as $key => $new_string ) { if ( isset( $machine_strings[ $new_string ] ) ) { $trp_gettext_strings_for_machine_translation[ $key ]['translated'] = $machine_strings[ $new_string ]; } } if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component( 'query' ); } $gettext_insert_update = $this->trp_query->get_query_component( 'gettext_insert_update' ); $gettext_insert_update->update_gettext_strings( $trp_gettext_strings_for_machine_translation, $TRP_LANGUAGE ); } } } } /** * make sure we remove the trp-gettext wrap from the format the date_i18n receives * ideally if in the gettext filter we would know 100% that a string is a valid date format then we would not wrap it but it seems that it is not easy to determine that ( explore further in the future $d = DateTime::createFromFormat('Y', date('y a') method); ) */ public function handle_date_i18n_function_for_gettext( $j, $dateformatstring, $unixtimestamp, $gmt ) { /* remove trp-gettext wrap */ $dateformatstring = preg_replace( '/#!trpst#trp-gettext (.*?)#!trpen#/i', '', $dateformatstring ); $dateformatstring = preg_replace( '/#!trpst#(.?)\/trp-gettext#!trpen#/i', '', $dateformatstring ); global $wp_locale; $i = $unixtimestamp; if ( false === $i ) { $i = current_time( 'timestamp', $gmt ); } if ( ( ! empty( $wp_locale->month ) ) && ( ! empty( $wp_locale->weekday ) ) ) { $datemonth = $wp_locale->get_month( date( 'm', $i ) ); $datemonth_abbrev = $wp_locale->get_month_abbrev( $datemonth ); $dateweekday = $wp_locale->get_weekday( date( 'w', $i ) ); $dateweekday_abbrev = $wp_locale->get_weekday_abbrev( $dateweekday ); $datemeridiem = $wp_locale->get_meridiem( date( 'a', $i ) ); $datemeridiem_capital = $wp_locale->get_meridiem( date( 'A', $i ) ); $dateformatstring = ' ' . $dateformatstring; $dateformatstring = preg_replace( "/([^\\\])D/", "\\1" . backslashit( $dateweekday_abbrev ), $dateformatstring ); $dateformatstring = preg_replace( "/([^\\\])F/", "\\1" . backslashit( $datemonth ), $dateformatstring ); $dateformatstring = preg_replace( "/([^\\\])l/", "\\1" . backslashit( $dateweekday ), $dateformatstring ); $dateformatstring = preg_replace( "/([^\\\])M/", "\\1" . backslashit( $datemonth_abbrev ), $dateformatstring ); $dateformatstring = preg_replace( "/([^\\\])a/", "\\1" . backslashit( $datemeridiem ), $dateformatstring ); $dateformatstring = preg_replace( "/([^\\\])A/", "\\1" . backslashit( $datemeridiem_capital ), $dateformatstring ); $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) - 1 ); } $timezone_formats = array( 'P', 'I', 'O', 'T', 'Z', 'e' ); $timezone_formats_re = implode( '|', $timezone_formats ); if ( preg_match( "/$timezone_formats_re/", $dateformatstring ) ) { $timezone_string = get_option( 'timezone_string' ); if ( $timezone_string ) { $timezone_object = timezone_open( $timezone_string ); //date_create( null, $timezone_object ); //date_create() passing null to parameter #1 ($datetime) of type string is deprecated, from what I found online the null should be replaced with '' $date_object = date_create( '', $timezone_object ); foreach ( $timezone_formats as $timezone_format ) { if ( false !== strpos( $dateformatstring, $timezone_format ) ) { $formatted = date_format( $date_object, $timezone_format ); $dateformatstring = ' ' . $dateformatstring; $dateformatstring = preg_replace( "/([^\\\])$timezone_format/", "\\1" . backslashit( $formatted ), $dateformatstring ); $dateformatstring = substr( $dateformatstring, 1, strlen( $dateformatstring ) - 1 ); } } } } $j = @date( $dateformatstring, $i ); return $j; } /** * Strip gettext tags from urls that were parsed by esc_url * * Esc_url() replaces spaces with %20. This is why it is not automatically stripped like the rest of the urls. * * @param $good_protocol_url * @param $original_url * @param $_context * * @return mixed * @since 1.3.8 * */ public function trp_strip_gettext_tags_from_esc_url( $good_protocol_url, $original_url, $_context ) { if ( strpos( $good_protocol_url, '%20data-trpgettextoriginal=' ) !== false ) { // first replace %20 with space so that gettext tags can be stripped. $good_protocol_url = str_replace( '%20data-trpgettextoriginal=', ' data-trpgettextoriginal=', $good_protocol_url ); $good_protocol_url = TRP_Gettext_Manager::strip_gettext_tags( $good_protocol_url ); } return $good_protocol_url; } /** * Filter sanitize_title() to use our own remove_accents() function so it's based on the default language, not current locale. * * Also removes trp gettext tags before running the filter because it strip # and ! and / making it impossible to strip the #trpst later * * @param string $title * @param string $raw_title * @param string $context * * @return string * @since 1.3.1 * */ public function trp_sanitize_title( $title, $raw_title, $context ) { // remove trp_tags before sanitization, because otherwise some characters (#,!,/, spaces ) are stripped later, and it becomes impossible to strip trp-gettext later $raw_title = TRP_Gettext_Manager::strip_gettext_tags( $raw_title ); if ( 'save' == $context ) { $title = trp_remove_accents( $raw_title ); } remove_filter( 'sanitize_title', array( $this, 'trp_sanitize_title' ), 1 ); $title = apply_filters( 'sanitize_title', $title, $raw_title, $context ); add_filter( 'sanitize_title', array( $this, 'trp_sanitize_title' ), 1, 3 ); return $title; } /** * function that strips the gettext tags from a string * * @param $string * * @return mixed */ static function strip_gettext_tags( $string ) { if ( is_string( $string ) && strpos( $string, 'data-trpgettextoriginal=' ) !== false ) { // final 'i' is for case insensitive. same for the 'i' in str_ireplace $string = preg_replace( '/ data-trpgettextoriginal=\d+#!trpen#/i', '', $string ); $string = preg_replace( '/data-trpgettextoriginal=\d+#!trpen#/i', '', $string );//sometimes it can be without space $string = str_ireplace( '#!trpst#trp-gettext', '', $string ); $string = str_ireplace( '#!trpst#/trp-gettext', '', $string ); $string = str_ireplace( '#!trpst#\/trp-gettext', '', $string ); $string = str_ireplace( '#!trpen#', '', $string ); } return $string; } /** * Function that inserts in db translation from language files for specified original string ids for a specific language * This requests changes locale from the very beginning so all the active plugins/theme load their textdomain translations * * Also creates plural entries for all plural forms so we have an id * * @param $dictionary * @param $language * * @return void */ public function add_missing_language_file_translations( $dictionary, $language ) { // Ensure translation files are loaded with the correct locale $locale = determine_locale(); $switched = switch_to_locale( $language ); // This means that the language is not supported by WordPress. Either a custom language or a language that we support but WordPress does not. if ( !$switched && $language !== $locale ) return; $trp_plural_forms = $this->get_gettext_component( 'plural_forms' ); if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component( 'query' ); } $insert_gettext_strings = array(); $update_gettext_strings = array(); $number_of_plural_forms = $trp_plural_forms->get_number_of_plural_forms( $language ); if ( ! empty( $dictionary ) ) { foreach ( $dictionary as $current_key => $current_string ) { $translations = get_translations_for_domain( $current_string['domain'] ); $context = ( $current_string['context'] === 'trp_context' ) ? null : $current_string['context']; $translated = ''; if ( $current_string['original_plural'] ) { /* For some domains in some languages, $translations object is not of type Translations * (but of type WP_Translations) on WP version 6.5+. So it doesn't have this method. * Todo: find an alternative to access plural forms for these cases */ if ( !method_exists( $translations, 'translate_entry' ) ) { continue; } // Insert translation for all other plural forms than the current one for ( $plural_form_i = 0; $plural_form_i < $number_of_plural_forms; $plural_form_i ++ ) { if ( $plural_form_i == $current_string['plural_form'] ) { continue; } $translation_exists_for_plural_form = false; $plural_form_id_translation_table = null; foreach ( $dictionary as $secondary_key => $secondary_string ) { if ( $secondary_key == $current_key ) { continue; } if ( $current_string['ot_id'] === $secondary_string['ot_id'] && $secondary_string['plural_form'] == $plural_form_i ) { if ( $secondary_string['status'] == 0 ) { $plural_form_id_translation_table = $secondary_string['id']; } else { $translation_exists_for_plural_form = true; } break; } } if ( ! $translation_exists_for_plural_form ) { $translated = $trp_plural_forms->translate_plural( $current_string['original'], $current_string['original_plural'], $plural_form_i, $context, $translations ); if ( $translated && $translated != $current_string['original'] && $translated != $current_string['original_plural'] ) { $status = 2; }else { $translated = ''; $status = 0; } if ( $plural_form_id_translation_table ) { if ( $translated ) { $update_gettext_strings[] = array( 'id' => $plural_form_id_translation_table, 'translated' => $translated ); } } else { $insert_gettext_strings[] = array( 'original_id' => $current_string['ot_id'], 'original' => $current_string['original'], 'translated' => $translated, 'domain' => $current_string['domain'], 'plural_form' => $plural_form_i, 'status' => $status, 'context' => $current_string['context'], 'original_plural' => $current_string['original_plural'] ); } } } // Insert translation for this current string if ( $current_string['status'] == 0 ) { $translated = $trp_plural_forms->translate_plural( $current_string['original'], $current_string['original_plural'], (int) $current_string['plural_form'], $context, $translations ); } } else { if ( $current_string['status'] == 0 && empty( $current_string['translated'] ) ) { $translated = $translations->translate( $current_string['original'] ); } } if ( $current_string['status'] == 0 && empty( $current_string['translated'] ) ) { if ( $translated && $translated != $current_string['original'] && $translated != $current_string['original_plural'] ) { $status = 2; } else { $translated = ''; $status = 0; } if ( $current_string['id'] ) { if ( $translated ) { $update_gettext_strings[] = array( 'id' => $current_string['id'], 'translated' => $translated, 'status' => 2 ); } } else { $insert_gettext_strings[] = array( 'original_id' => $current_string['ot_id'], 'original' => $current_string['original'], 'translated' => $translated, 'domain' => $current_string['domain'], 'plural_form' => (int) $current_string['plural_form'], 'status' => $status, 'context' => $current_string['context'], 'original_plural' => $current_string['original_plural'] ); } } } $gettext_insert_update = $this->trp_query->get_query_component( 'gettext_insert_update' ); $gettext_insert_update->insert_gettext_strings($insert_gettext_strings, $language); $gettext_insert_update->update_gettext_strings($update_gettext_strings, $language, array('translated', 'id', 'status')); if ( $switched ) restore_previous_locale(); } } } includes/trp-ajax.php 0000777 00000015174 15251156640 0010635 0 ustar 00 <?php /** * Class TRP_Ajax * * Custom Ajax to get translation of dynamic elements. */ class TRP_Ajax{ protected $connection; protected $table_prefix; /** * TRP_Ajax constructor. * * Establishes db connection and triggers function to output translations. */ public function __construct( ){ if ( !isset( $_POST['action'] ) || $_POST['action'] !== 'trp_get_translations_regular' || empty( $_POST['originals'] ) || empty( $_POST['language'] ) || empty( $_POST['original_language'] ) ) { die(); } include './external-functions.php'; if ( !trp_is_valid_language_code( $_POST['language'] ) || !trp_is_valid_language_code( $_POST['original_language'] ) ) {//phpcs:ignore echo json_encode( 'TranslatePress Error: Invalid language code' ); exit; } if ( $this->connect_to_db() ){ $this->output_translations( $this->sanitize_strings( $_POST['originals'] ),//phpcs:ignore $this->sanitize_strings( $_POST['skip_machine_translation'] ),//phpcs:ignore mysqli_real_escape_string( $this->connection, $_POST['language'] ), /* phpcs:ignore */ /* validated with trp_is_valid_language_code on line 25 */ mysqli_real_escape_string( $this->connection, $_POST['original_language'] ) /* phpcs:ignore */ /* validated with trp_is_valid_language_code on line 25 */ ); //Successful connection to DB mysqli_close($this->connection); }else{ //Error connecting to DB $this->return_error(); } } /** * Sanitize posted strings. * * @param array $posted_strings Array of strings. * @return array Sanitized array of strings. */ protected function sanitize_strings( $posted_strings){ $numerals_option = ( isset( $_POST['translate_numerals_opt'] ) && $_POST['translate_numerals_opt'] === 'yes' ) ? 'yes' : 'no'; $strings = json_decode( $posted_strings ); if ( is_array( $strings ) ) { foreach ($strings as $key => $string) { $strings[$key] = mysqli_real_escape_string( $this->connection, trp_full_trim( $string, array( 'numerals'=> $numerals_option ) ) ); } } return $strings; } /** * Finds db credentials in wp-config file and tries to connect to db. * * @return bool Whether connection was succesful or not. */ protected function connect_to_db(){ $file = dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/wp-config.php'; try { $content = @file_get_contents($file); if ($content == false) { return false; } } catch (Exception $e) { return false; } // remove single line and multi-line /* Comments */ $content = preg_replace('!/\*.*?\*/!s', '', $content); $content = preg_replace('/\n\s*\n/', "\n", $content); // remove single line double slashes $content = preg_replace('#^\s*//.+$#m', "", $content); $credentials = array( 'db_name' => 'DB_NAME', 'db_user' => 'DB_USER', 'db_password' => 'DB_PASSWORD', 'db_host' => 'DB_HOST', 'db_charset' => 'DB_CHARSET' ); foreach ( $credentials as $credential => $constant_name ) { if ( preg_match_all( "/define\s*\(\s*['\"]" . $constant_name . "['\"]\s*,\s*['\"](.*?)['\"]\s*\)/", $content, $result ) ) { $credentials[ $credential ] = $result[1][0]; } else { return false; } } $this->connection = mysqli_connect( $credentials['db_host'], $credentials['db_user'], $credentials['db_password'], $credentials['db_name'] ); // Check connection if ( mysqli_connect_errno() ) { //Failed to connect to MySQL. return false; } mysqli_set_charset ( $this->connection , $credentials['db_charset'] ); if ( preg_match_all( '/\$table_prefix\s*=\s*[\'"](.*?)[\'"]/', $content, $results ) ) { $this->table_prefix = end( $results[1] ); }else{ $this->table_prefix = $this->sql_find_table_prefix(); if ( $this->table_prefix === false ){ return false; } } return true; } /** * Get WP table prefix. * * @return string Table prefix. */ protected function sql_find_table_prefix(){ $sql = "SELECT DISTINCT SUBSTRING(`TABLE_NAME` FROM 1 FOR ( LENGTH(`TABLE_NAME`)-8 ) ) as prefix FROM information_schema.TABLES WHERE `TABLE_NAME` LIKE '%postmeta'"; $result = mysqli_query( $this->connection, $sql ); if ( mysqli_num_rows( $result ) > 0 ) { $result_object = mysqli_fetch_assoc($result); return $result_object['prefix']; } else { return false; } } /** * Output translation for given strings. * * @param array $strings Array of string to translate. * @param string $language Language to translate into. * @param string $original_language Language to translate from. Default language. */ protected function output_translations( $strings, $skip_machine_translation, $language, $original_language ){ $sql = 'SELECT original, translated, status FROM ' . $this->table_prefix . 'trp_dictionary_' . strtolower( $original_language ) . '_' . strtolower( $language ) . ' WHERE original IN (\'' . implode( "','", $strings ) .'\') AND status != 0'; try { $result = mysqli_query( $this->connection, $sql ); }catch(Throwable $e){ $this->return_error(); } if ( $result === false ){ $this->return_error(); }else { $dictionaries[$language] = array(); while ($row = mysqli_fetch_object($result)) { // do not retrieve a row that should not be machine translated ( ex. src, href ) if ( $row->status == 1 && in_array( $row->original, $skip_machine_translation ) ) { continue; } $dictionaries[$language][] = $row; } $dictionary_by_original = trp_sort_dictionary_by_original( $dictionaries, 'regular', 'dynamicstrings', null, null ); echo json_encode($dictionary_by_original); } } /** * Return error in case of connection fail and other problems. */ protected function return_error(){ echo json_encode( 'error' ); exit; } } new TRP_Ajax; die(); includes/class-reviews.php 0000777 00000016071 15251156640 0011673 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Reviews */ class TRP_Reviews{ protected $settings; /* @var TRP_Settings */ protected $trp_settings; protected $date_of_install; public function __construct( $settings){ $this->settings = $settings; $this->maybe_set_date_of_install(); } /** * Marks timestamp TP install if not already set * * Started tracking timestamp of installation since version 1.9.8 */ public function maybe_set_date_of_install(){ $trp_db_stored_data = get_option( 'trp_db_stored_data', array() ); if ( !isset( $trp_db_stored_data['install_timestamp'] ) ){ $trp_db_stored_data['install_timestamp'] = time(); update_option('trp_db_stored_data', $trp_db_stored_data ); } $this->date_of_install = $trp_db_stored_data['install_timestamp']; } public function get_date_of_install(){ return $this->date_of_install; } public function should_it_show_review_notice(){ // conditions $time_to_wait_condition = WEEK_IN_SECONDS; $number_of_translations_condition = 25; $how_often_to_check = DAY_IN_SECONDS; $trp_db_stored_data = get_option( 'trp_db_stored_data', array() ); $notification_dismissed = isset( $trp_db_stored_data['trp_review_notification_dismiss_notification'] ) && $trp_db_stored_data['trp_review_notification_dismiss_notification'] === true; $site_meets_conditions_for_review = isset( $trp_db_stored_data['trp_site_meets_conditions_for_review'] ) && $trp_db_stored_data['trp_site_meets_conditions_for_review'] === true; if ( !$notification_dismissed && !$site_meets_conditions_for_review ) { $trp = TRP_Translate_Press::get_trp_instance(); $machine_translator = $trp->get_component( 'machine_translator' ); $trp_query = $trp->get_component( 'query' ); $transient = get_transient( 'trp_checked_if_site_meets_conditions_for_review' ); if ( $transient === false ) { // Do sql checks because transient has expired. Transient is used to ensure checking is not made on every page load. if ( time() - $this->get_date_of_install() > $time_to_wait_condition ) { foreach ( $this->settings['translation-languages'] as $language ) { if ( $language === $this->settings['default-language']){ continue; } if ( $trp_query->minimum_rows_with_status( $language, $number_of_translations_condition, 2 ) ) { $site_meets_conditions_for_review = true; break; } if ( $machine_translator->is_available( array() ) && $trp_query->minimum_rows_with_status( $language, $number_of_translations_condition, 1 ) ) { $site_meets_conditions_for_review = true; break; } } } set_transient( 'trp_checked_if_site_meets_conditions_for_review', 'yes', $how_often_to_check ); } } if ( !isset( $trp_db_stored_data['trp_site_meets_conditions_for_review'] ) && $site_meets_conditions_for_review ){ // once a site meets the conditions, remember so that we don't check anymore $trp_db_stored_data['trp_site_meets_conditions_for_review'] = true; update_option( 'trp_db_stored_data', $trp_db_stored_data ); } // actual logic for showing reviews or not $show_review_notice = ( !$notification_dismissed && $site_meets_conditions_for_review ); return apply_filters( 'trp_show_notification_about_review', $show_review_notice, $notification_dismissed, $site_meets_conditions_for_review ); } /** * Show an admin notice inviting the user to review TP * * hooked to admin_init */ public function display_review_notice(){ if ( !$this->should_it_show_review_notice() ){ return; } $notifications = TRP_Plugin_Notifications::get_instance(); /* this must be unique */ $notification_id = 'trp_review_notification'; $url = 'https://wordpress.org/support/plugin/translatepress-multilingual/reviews/?filter=5#new-post'; $message = '<p style="margin-top: 16px;font-size: 14px;padding-right:20px">'; $message .= wp_kses( __( "Hello! Seems like you've been using <strong>TranslatePress</strong> for a while now to translate your website. That's awesome! ", 'translatepress-multilingual' ), array('strong' => array() ) ); $message .= '</p>'; $message .= '<p style="font-size: 14px">'; $message .= esc_html__( "If you can spare a few moments to rate it on WordPress.org it would help us a lot (and boost my motivation).", 'translatepress-multilingual' ); $message .= '</p>'; $message .= '<p>'; $message .= esc_html__( "~ Razvan, developer of TranslatePress", 'translatepress-multilingual' ) ; $message .= '</p>'; // buttons for OK / No, thanks $message .= '<p>'; $message .= '<a href="' . esc_url( $url ) . '" title="' . esc_attr__( 'Rate TranslatePress on WordPress.org plugin page', 'translatepress-multilingual' ) . '" class="button-primary" style="margin-right: 20px">' . esc_html__( "Ok, I will gladly help!", 'translatepress-multilingual' ) . '</a>'; $message .= '<a href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" title="' . esc_attr__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '" class="button-secondary" >' . esc_html__( "No, thanks.", 'translatepress-multilingual' ) . '</a>'; $message .= '</p>'; //make sure to use the trp_dismiss_admin_notification arg $message .= '<a href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" style="text-decoration:none" type="button" class="notice-dismiss"><span class="screen-reader-text">' . __( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $notifications->add_notification( $notification_id, $message, 'trp-notice trp-narrow notice notice-info', true, array( 'translate-press' ), true ); } /** * Set option to not display notification * * Necessary because the plugin notification system is originally user meta based. * Change this behaviour so that dismissing the notification is known site-wide * * hooked to trp_dismiss_notification * * @param $notification_id * @param $current_user */ public function dismiss_notification($notification_id, $current_user){ if ( $notification_id === 'trp_review_notification' ) { $trp_db_stored_data = get_option( 'trp_db_stored_data', array() ); $trp_db_stored_data['trp_review_notification_dismiss_notification'] = true; update_option('trp_db_stored_data', $trp_db_stored_data ); } } } includes/mtapi/class-mtapi-customer.php 0000777 00000002403 15251156640 0014264 0 ustar 00 <?php // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) exit; /** * A thin client to make requests to MT API using WP HTTP API. */ class TRP_MTAPI_Customer { private $url; public function __construct( $url ) { $this->url = trailingslashit( $url ); } public function lookup_site( $key, $url ) { return $this->request( 'POST', 'sites/lookup', [ 'key' => $key, 'url' => trailingslashit($url) ] ); } public function lookup_license( $key ) { return $this->request( 'POST', 'licenses/lookup', [ 'key' => $key ] ); } private function request( $method, $path, $data = null ) { $request_args = [ 'method' => $method, 'headers' => [ 'Content-Type' => 'application/json' ], ]; if ( ! is_null( $data ) ) { $request_args['body'] = wp_json_encode( $data ); } $response = wp_remote_request( $this->url . $path, $request_args ); if ( is_wp_error( $response ) ) { $error_response = [ 'exception' => [] ]; foreach ( $response->get_error_messages() as $message ) { // Emulates structure of MT API exception response for simplicity/consistency. $error_response['exception'][]['message'] = $message; } return $error_response; } return json_decode( wp_remote_retrieve_body( $response ), true ); } } includes/mtapi/functions.php 0000777 00000040625 15251156640 0012230 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_machine_translation_engines', 'trp_mtapi_add_engine', 10 ); function trp_mtapi_add_engine( $engines ){ $engines[] = array( 'value' => 'mtapi', 'label' => __( 'TranslatePress AI', 'translatepress-multilingual' ) ); return $engines; } add_action( 'trp_machine_translation_extra_settings_middle', 'trp_mtapi_add_settings' ); function trp_mtapi_add_settings( $mt_settings ){ require_once("class-mtapi-customer.php"); $license = get_option('trp_license_key'); $status = get_option('trp_license_status'); $details = get_option('trp_license_details'); if (!isset($details['valid'][0])) $status = false; $translatepress_version_name = (defined('TRANSLATE_PRESS')) ? TRANSLATE_PRESS : 'TranslatePress'; if ($status === false) : ?> <div class="trp-get-free-license__container" <?php if ($translatepress_version_name !== 'TranslatePress') echo 'style="background: #F6F7F7"'; ?>> <div class="trp-engine trp-automatic-translation-engine__container" id="mtapi"> <span class="trp-primary-text-bold"> <img src="<?php echo esc_url(TRP_PLUGIN_URL.'assets/images/'); ?>ai-icon.svg" width="24" height="24"/> TranslatePress AI <?php //this is not localized by choice ?> </span> <div class="trp-automatic-translation-license-notice__wrapper"> <svg class="trp-no-license-automatic-translation__icon" width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M18 10C18 5.58 14.42 2 10 2C5.58 2 2 5.58 2 10C2 14.42 5.58 18 10 18C14.42 18 18 14.42 18 10ZM12 10L15 13L13 15L10 12L7 15L5 13L8 10L5 7L7 5L10 8L13 5L15 7L12 10Z" fill="#9CA1A8"/> </svg> <span id="trp-mtapi-key" class="trp-primary-text trp-settings-error-text"> <?php esc_html_e('No Active License Detected for this website.', 'translatepress-multilingual'); ?> </span> </div> <?php if ($translatepress_version_name == 'TranslatePress') : ?> <span class="trp-secondary-text trp-get-free-license-text"> <?php esc_html_e('In order to enable Automatic Translation using TranslatePress AI, you need a license key by creating a free account.', 'translatepress-multilingual'); ?> </span> <?php endif;?> <div class="trp-automatic-translation-get-license-buttons"> <?php if ( $translatepress_version_name == 'TranslatePress' ) : /* [utm42] */?> <a href="<?php echo esc_url( 'https://translatepress.com/ai-free/?utm_source=tp-automatic-translation&utm_medium=client-site&utm_campaign=tp-ai-free' ) ?>" class="trp-get-free-license-link trp-get-free-license-button button-primary" target="_blank" id="trp-enter-license-button"> <?php esc_html_e( 'Create your Free Account', 'translatepress-multilingual' ); ?> </a> <span class="trp-secondary-text trp-text-auto"><?php esc_html_e(' or ', 'translatepress-multilingual'); ?></span> <?php endif;?> <a href="<?php echo esc_url( admin_url('admin.php?page=trp_license_key') ) ?>" class="trp-enter-license-link trp-get-free-license-button trp-button-secondary" id="trp-enter-license-button"> <?php esc_html_e( 'Enter your license key', 'translatepress-multilingual' ); ?> </a> <?php if ( $translatepress_version_name != 'TranslatePress' ) : /* [utm43] */ ?> <span class="trp-secondary-text trp-text-auto"><?php printf( esc_html__(' Or %1$spurchase one here%2$s', 'translatepress-multilingual'), '<a href="https://translatepress.com/pricing/?utm_source=tp-automatic-translation&utm_medium=client-site&utm_campaign=activate-license" target="_blank">', '</a>' ); ?></span> <?php endif;?> </div> </div> <?php if ( $translatepress_version_name == 'TranslatePress' ) : ?> <div class="trp-automatic-translation-engine__upsale" id="tpai-upsale"> <span class="trp-primary-text-bold"> <?php esc_html_e('Your free account includes: ', 'translatepress-multilingual'); ?> </span> <span class="trp-secondary-text trp-check-text"> <img src="<?php echo esc_url(TRP_PLUGIN_URL.'assets/images/'); ?>green-circle-check.png" width="20px" height="20px"/> <?php esc_html_e('Access to TranslatePress AI for instant automatic translations', 'translatepress-multilingual'); ?> </span> <span class="trp-secondary-text trp-check-text"> <img src="<?php echo esc_url(TRP_PLUGIN_URL.'assets/images/'); ?>green-circle-check.png" width="20px" height="20px"/> <?php esc_html_e('2000 AI words to translate automatically', 'translatepress-multilingual'); ?> </span> <div class="trp-upsale-fill" id="<?php echo esc_html( $translatepress_version_name ) ?>" style="display: none;"> <span class="trp-primary-text trp-upsale-text-red"> <?php esc_html_e("Get more AI Tokens and unlock all AI features with TranslatePress Pro.", "translatepress-multilingual"); ?> <a href="https://translatepress.com/pricing/?utm_source=wpbackend&utm_medium=clientsite&utm_content=tpsettingsAT&utm_campaign=tpaifree" id="trp-upgrade-link" target="_blank"> <span class="trp-upsale-text-link"> <span><?php esc_html_e("Upgrade now", "translatepress-multilingual"); ?></span> <svg width="12" height="12" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17 7.3252L7 17.3252M17 7.3252H8M17 7.3252V16.3252" stroke="#354052" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </span> </a> </span> </div> </div> <?php endif; ?> </div> <?php endif; if ($status === 'valid') : $product_name = '<strong>' . str_replace('+', ' ', $details['valid'][0]->item_name) . '</strong>'; // MTAPI_URL needs to be defined in wp-config.php for local host development $mtapi_url = (defined('MTAPI_URL') ? MTAPI_URL : 'https://mtapi.translatepress.com' ); $mtapi_server = new TRP_MTAPI_Customer($mtapi_url); $site_status = $mtapi_server->lookup_site($license, home_url()); $site_status['quota'] = isset ( $site_status['quota'] ) ? $site_status['quota'] : 0; set_transient("trp_mtapi_cached_quota", $site_status['quota'], 5*60); $quota = ($site_status['quota'] < 500) ? 0 : ceil($site_status['quota'] / 5 ); // this $total_quota is not correct due to quota_used should account for ALL websites added to this license. // however, in case the site does have a user defined limit, the quota_used is correct. // without further changes to mtapi we don't have a proper way of knowing what's the quota_used. // will hide total_quota and let progress bar in place as the approximation is good enough if ( !isset( $site_status['quota_used'])){ $site_status['quota_used'] = 0; } $total_quota = ceil( ( $site_status['quota'] + $site_status['quota_used'] ) / 5 ); $formatted_quota = number_format( $quota ); $formatted_total_quota = number_format( $total_quota ); $usage_percentage = ($total_quota > 0) ? ($quota / $total_quota) * 100 : 0; ?> <div class="trp-engine trp-automatic-translation-engine__container" id="mtapi"> <span class="trp-primary-text-bold"> <img src="<?php echo esc_url(TRP_PLUGIN_URL.'assets/images/'); ?>ai-icon.svg" width="24" height="24"/> TranslatePress AI <?php //this is not localized by choice ?> </span> <div class="trp-automatic-translation-license-notice__wrapper"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17 3.33989C18.5083 4.21075 19.7629 5.46042 20.6398 6.96519C21.5167 8.46997 21.9854 10.1777 21.9994 11.9192C22.0135 13.6608 21.5725 15.3758 20.72 16.8946C19.8676 18.4133 18.6332 19.6831 17.1392 20.5782C15.6452 21.4733 13.9434 21.9627 12.2021 21.998C10.4608 22.0332 8.74055 21.6131 7.21155 20.7791C5.68256 19.9452 4.39787 18.7264 3.48467 17.2434C2.57146 15.7604 2.06141 14.0646 2.005 12.3239L2 11.9999L2.005 11.6759C2.061 9.94888 2.56355 8.26585 3.46364 6.79089C4.36373 5.31592 5.63065 4.09934 7.14089 3.25977C8.65113 2.42021 10.3531 1.98629 12.081 2.00033C13.8089 2.01437 15.5036 2.47589 17 3.33989ZM15.707 9.29289C15.5348 9.12072 15.3057 9.01729 15.0627 9.002C14.8197 8.98672 14.5794 9.06064 14.387 9.20989L14.293 9.29289L11 12.5849L9.707 11.2929L9.613 11.2099C9.42058 11.0607 9.18037 10.9869 8.9374 11.0022C8.69444 11.0176 8.46541 11.121 8.29326 11.2932C8.12112 11.4653 8.01768 11.6943 8.00235 11.9373C7.98702 12.1803 8.06086 12.4205 8.21 12.6129L8.293 12.7069L10.293 14.7069L10.387 14.7899C10.5624 14.926 10.778 14.9998 11 14.9998C11.222 14.9998 11.4376 14.926 11.613 14.7899L11.707 14.7069L15.707 10.7069L15.79 10.6129C15.9393 10.4205 16.0132 10.1802 15.9979 9.93721C15.9826 9.69419 15.8792 9.46509 15.707 9.29289Z" fill="#4AB067"/> </svg> <span id="trp-mtapi-key" class="trp-primary-text"><?php printf(wp_kses(__('You have a valid %s <strong>license</strong>.', 'translatepress-multilingual'), array( 'strong' => array() ) ), wp_kses( $product_name, array( 'strong' => array() ) ) ); ?> </span> </div> <span class="trp-secondary-text"> <?php echo "<strong>" . esc_html( $formatted_quota ) . "</strong>" . esc_html__( ' words remaining. ', 'translatepress-multilingual' ); ?> <?php if ( isset( $site_status['exception'][0]['message'] ) && $site_status['exception'][0]['message'] == "Site not found." ) : ?> <span id="trp-refresh-tpai"> <span id="trp-refresh-tpai-dashicon" class="dashicons dashicons-controls-repeat"></span> <span id="trp-refresh-tpai-text-recheck" class="trp-primary-text"> <?php esc_html_e( 'Recheck', 'translatepress-multilingual' ); ?> </span> </span> <span id="trp-refresh-tpai-text-rechecking " class="trp-primary-text" style="display:none"> <?php esc_html_e( 'Rechecking...', 'translatepress-multilingual' ); ?> </span> <span id="trp-refresh-tpai-text-done" class="trp-primary-text" style="display:none"> <?php esc_html_e( 'Done.', 'translatepress-multilingual' ); ?> </span> <?php endif; ?> </span> <div class="trp-quota-bar"> <div class="trp-quota-progress" style="width: <?php echo esc_attr( $usage_percentage ); ?>%;"></div> </div> <span class="trp-secondary-text"> <?php printf( esc_html__( 'Manage your license & quota on the %s', 'translatepress-multilingual' ), //[utm44] '<a href="' . esc_url( 'https://translatepress.com/account/?utm_source=tp-automatic-translation&utm_medium=client-site&utm_campaign=manage-quota') . '" target="_blank" class="trp-settings-link"> '. esc_html__('TranslatePress.com Account Page', 'translatepress-multilingual') . '</a>' ); ?> </span> </div> <div class="trp-upsale-fill trp-upsale-fill-active-license" id="<?php echo esc_html( $translatepress_version_name )?>" style=" display: none " > <span class="trp-primary-text trp-upsale-text-red"> <?php esc_html_e("Get more AI Tokens and unlock all AI features with TranslatePress Pro.", "translatepress-multilingual"); /* [utm45] */?> <a href="https://translatepress.com/pricing/?utm_source=tp-automatic-translation&utm_medium=client-site&utm_campaign=tp-ai" id="trp-upgrade-link" target="_blank"> <span class="trp-upsale-text-link"> <span><?php esc_html_e("Upgrade now", "translatepress-multilingual"); ?></span> <svg width="20" height="20" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M17 7.3252L7 17.3252M17 7.3252H8M17 7.3252V16.3252" stroke="#354052" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </span> </a> </span> </div> <?php endif; } /** * Store url * * In order of priority MTAPI_STORE_URL, tpcom.ddev.site, translatepress.com * * @return string */ function trp_mtapi_get_store_url() { $store_url = ( !isset( $store_url ) ) ? ( ( defined( 'MTAPI_STORE_URL' ) ) ? MTAPI_STORE_URL : null ) : $store_url; $store_url = ( !isset( $store_url ) ) ? ( ( defined( 'MTAPI_URL' ) && MTAPI_URL == 'https://mtapi.ddev.site' ) ? 'https://tpcom.ddev.site' : null ) : $store_url; return ( !isset( $store_url ) ) ? "https://translatepress.com" : $store_url; } /** * Make sure translatepress.com syncs with MTAPI for this license and this site * * Performed when saving Automatic Translation tab settings */ add_filter( 'trp_machine_translation_sanitize_settings', 'trp_mtapi_sync_license', 10, 2 ); function trp_mtapi_sync_license( $settings, $mt_settings ) { if ( isset ( $_POST['option_page'] ) && $_POST['option_page'] === 'trp_machine_translation_settings' && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) && $settings['translation-engine'] === 'mtapi' ) { $license = get_option( 'trp_license_key' ); $status = get_option( 'trp_license_status' ); if ( $status === 'valid' ) { trp_mtapi_sync_license_call( $license ); } } return $settings; } /** * Make translatepress.com sync with MTAPI for this license and this site */ function trp_mtapi_sync_license_call( $license_key ) { $trp = TRP_Translate_Press::get_trp_instance(); if ( !empty( $trp->tp_product_name ) ) { // data to send in our API request $api_params = array( 'edd_action' => 'sync_mtapi_license', 'license' => $license_key, 'url' => home_url(), 'version' => TRP_PLUGIN_VERSION ); $store_url = trp_mtapi_get_store_url(); // Call the custom API. $response = wp_remote_post( $store_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) ); return $response; } return false; } add_action('wp_ajax_trp_ai_recheck_quota','trp_ai_recheck_quota'); function trp_ai_recheck_quota(){ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_ai_recheck_quota' ) { $nonce_okay = check_ajax_referer( 'trp-tpai-recheck', 'nonce' ); if ( $nonce_okay ){ $license = get_option( 'trp_license_key' ); $status = get_option( 'trp_license_status' ); if ( $status === 'valid' ) { $response = trp_mtapi_sync_license_call( $license ); if ( is_array( $response ) && ! is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code']) && $response['response']['code'] == 200 ) { $mtapi_url = (defined('MTAPI_URL') ? MTAPI_URL : 'https://mtapi.translatepress.com' ); require_once("class-mtapi-customer.php"); $mtapi_server = new TRP_MTAPI_Customer($mtapi_url); $site_status = $mtapi_server->lookup_site($license, home_url()); $site_status['quota'] = isset ( $site_status['quota'] ) ? $site_status['quota'] : 0; $quota = intval(ceil($site_status['quota'] / 5)); echo trp_safe_json_encode( ['quota' => $quota ] ); //phpcs:ignore } } } } } wp_die(); } includes/mtapi/class-mtapi-machine-translator.php 0000777 00000044425 15251156640 0016230 0 ustar 00 <?php // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) exit; class TRP_MTAPI_Machine_Translator extends TRP_Machine_Translator { private $license_key = null; public function __construct( $settings ) { parent::__construct( $settings ); add_filter( 'trp_mtapi_source_language', array( $this, 'configure_api_source_language' ), 10, 3 ); add_filter( 'trp_mtapi_target_language', array( $this, 'configure_api_target_language' ), 10, 3 ); } /** * Send request to MTAPI * * @param string $source_language Translate from language * @param string $language_code Translate to language * @param array $strings_array Array of string to translate * @param string $formality The formality of the language * * @return array|WP_Error Response */ public function send_request( $source_language, $language_code, $strings_array, $formality = "default" ){ /* build our translation request */ $translation_request = []; $translation_request['key'] = get_option('trp_license_key', ''); $translation_request['url'] = trailingslashit( $this->get_referer() ); $translation_request['source'] = $source_language; $translation_request['target'] = $language_code; $translation_request['formality'] = $formality; $translation_request['texts'] = []; foreach ( $strings_array as $new_string ) { $translation_request['texts'][] = html_entity_decode( $new_string, ENT_QUOTES ); } $response = wp_remote_post( "{$this->get_api_url()}/translations", array( 'method' => 'POST', 'timeout' => 45, 'headers' => [ 'Content-Type' => 'application/json' ], 'body' => wp_json_encode($translation_request), ) ); return $response; } public function get_api_url(){ return (defined('MTAPI_URL') ? MTAPI_URL : 'https://mtapi.translatepress.com' ); } /** * Returns an array with the API provided translations of the $new_strings array. * * @param array $new_strings array with the strings that need translation. The keys are the node number in the DOM so we need to preserve the m * @param string $target_language_code language code of the language that we will be translating to. Not equal to the google language code * @param string $source_language_code language code of the language that we will be translating from. Not equal to the google language code * @return array array with the translation strings and the preserved keys or an empty array if something went wrong */ public function translate_array($new_strings, $target_language_code, $source_language_code = null ){ if ( $source_language_code == null ){ $source_language_code = $this->settings['default-language']; } if( empty( $new_strings ) || !$this->verify_request_parameters( $target_language_code, $source_language_code ) ) return array(); // Check if translations are permanently disabled for free license if ( $this->is_free_license_translation_disabled() ) { return array(); } $source_language = apply_filters( 'trp_mtapi_source_language', $this->machine_translation_codes[$source_language_code], $source_language_code, $target_language_code ); $target_language = apply_filters( 'trp_mtapi_target_language', $this->machine_translation_codes[$target_language_code], $source_language_code, $target_language_code ); $formality = $this->get_request_formality_for_language($target_language_code); $translated_strings = array(); /* apply filter to allow modification of chunk size, default is 50 */ $chunk_size = apply_filters( 'trp_mtapi_chunk_size', 50 ); /* split our strings that need translation in chunks of maximum $chunk_size strings due to limit of TranslatePress AI*/ $new_strings_chunks = array_chunk( $new_strings, $chunk_size, true ); foreach( $new_strings_chunks as $new_strings_chunk ){ // exist early if quota for this website is = 0 character // we exit here as well because we're doing the translation in chunks in a foreach. $quota = get_transient('trp_mtapi_cached_quota'); if ( !$quota && is_numeric($quota) && $quota < 500) { return array(); } $response = $this->send_request( $source_language, $target_language, $new_strings_chunk, $formality ); if ( is_array( $response ) && ! is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code']) && $response['response']['code'] == 200 ) { $translation_response = json_decode( $response['body'] ); // Only certain errors will stop MTAPI for 5 minutes trying out for new translations from the API. $exception_message = (isset($translation_response->exception[0]->message)) ? $translation_response->exception[0]->message : ''; if ($exception_message == 'Site not found.' || $exception_message == 'Insufficient quota.' || $exception_message == 'Site is not active.' || $exception_message == 'Out of valid license dates.') { set_transient("trp_mtapi_cached_quota", 0, 5*60); } if (isset($translation_response->quota) && is_numeric($translation_response->quota)) { set_transient("trp_mtapi_cached_quota", $translation_response->quota, 5*60); // Check if this is a free license and mark translation as permanently disabled if ($translation_response->quota < 500 && $this->is_free_license()) { $this->set_free_license_translation_disabled(true); } } } // this is run only if "Log machine translation queries." is set to Yes. $this->machine_translator_logger->log(array( 'strings' => serialize( $new_strings_chunk), 'response' => serialize( $response ), 'lang_source' => $source_language, 'lang_target' => $target_language, )); /* analyze the response */ if ( is_array( $response ) && ! is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code']) && $response['response']['code'] == 200 ) { $translation_response = json_decode( $response['body'] ); if ( empty( $translation_response->exception ) ) { $this->machine_translator_logger->count_towards_quota( $new_strings_chunk ); /* if we have strings build the translation strings array and make sure we keep the original keys from $new_string */ $translations = ( empty( $translation_response->translations ) ) ? array() : $translation_response->translations; $i = 0; foreach ( $new_strings_chunk as $key => $old_string ) { if ( isset( $translations[ $i ] ) && !empty( $translations[ $i ]->translation ) ) { $translated_strings[ $key ] = $translations[ $i ]->translation; } else { /* In some cases when API doesn't have a translation for a particular string, translation is returned empty instead of same string. Setting original string as translation prevents TP from keep trying to submit same string for translation endlessly. */ $translated_strings[ $key ] = $old_string; } $i++; } } if( $this->machine_translator_logger->quota_exceeded() ) break; } } // will have the same indexes as $new_string or it will be an empty array if something went wrong return $translated_strings; } /** * Send a test request to verify if the functionality is working */ public function test_request(){ return $this->send_request( 'en', 'es', array( 'about' ) ); } public function get_api_key(){ if ( $this->license_key === null ){ $this->license_key = get_option( 'trp_license_key' ); $this->license_key = ( empty( $this->license_key ) ) ? false : $this->license_key; } return $this->license_key; } public function get_supported_languages(){ $response = wp_remote_post( "{$this->get_api_url()}/languages", array( 'method' => 'GET', 'timeout' => 45, 'headers' => [ 'Content-Type' => 'application/json' ], ) ); if ( is_array( $response ) && ! is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code']) && $response['response']['code'] == 200 ) { $data = json_decode( $response['body'] ); $supported_languages = array(); foreach( $data as $language ){ $supported_languages[] = $language->language; } return apply_filters( 'trp_add_translatepress_ai_supported_languages_to_the_array', $supported_languages ); } return array(); } public function get_engine_specific_language_codes($languages){ $iso_translation_codes = $this->trp_languages->get_iso_codes($languages); $engine_specific_languages = array(); foreach( $languages as $language ) { /* All combinations of source and target languages are supported. Target language code can be country specific. Source language code is not. So the source language code is used here. */ $engine_specific_languages[] = apply_filters( 'trp_mtapi_source_language', $iso_translation_codes[ $language ], $language, null ); } return $engine_specific_languages; } public function check_api_key_validity() { $machine_translator = $this; $translation_engine = $this->settings['trp_machine_translation_settings']['translation-engine']; $is_error = false; $return_message = ''; if ( 'mtapi' === $translation_engine && $this->settings['trp_machine_translation_settings']['machine-translation'] === 'yes') { if ( isset( $this->correct_api_key ) && $this->correct_api_key != null ) { return $this->correct_api_key; } $is_error = true; $return_message = __( 'Please check your TranslatePress license key.', 'translatepress-multilingual' ); $license = $this->get_api_key(); $status = get_option( 'trp_license_status' ); if ( $status === 'valid' ) { require_once("class-mtapi-customer.php"); $mtapi_server = new TRP_MTAPI_Customer( $this->get_api_url() ); $site_status = $mtapi_server->lookup_site( $license, home_url() ); if ( !empty( $site_status ) && !empty( $site_status['status'] ) && $site_status['status'] === "active" ) { $is_error = false; $return_message = ''; // If API key is valid and site is active, allow re-enabling translations for paid licenses if ( !$this->is_free_license() ) { $this->set_free_license_translation_disabled(false); } } } $this->correct_api_key = array( 'message' => $return_message, 'error' => $is_error, ); } return array( 'message' => $return_message, 'error' => $is_error, ); } /** * Particularities for source language in TranslatePress API * * PT_BR is not treated in the same way as for the target language * * @param $source_language * @param $source_language_code * @param $target_language_code * @return string */ public function configure_api_source_language($source_language, $source_language_code, $target_language_code ){ $exceptions_source_mapping_codes = array( 'zh_HK' => 'zh', 'zh_TW' => 'zh', 'zh_CN' => 'zh', 'de_DE_formal' => 'de', 'nb_NO' => 'nb', 'ckb' => 'ckb' // Kurdish (Sorani) ); if ( isset( $exceptions_source_mapping_codes[$source_language_code] ) ){ $source_language = $exceptions_source_mapping_codes[$source_language_code]; } return $source_language; } /** * Particularities for target language in TranslatePress API * * @param $target_language * @param $source_language_code * @param $target_language_code * @return string */ public function configure_api_target_language($target_language, $source_language_code, $target_language_code ){ $exceptions_target_mapping_codes = array( 'zh_HK' => 'zh-hant', 'zh_TW' => 'zh-hant', 'zh_CN' => 'zh-hans', 'pt_BR' => 'pt-br', 'pt_PT' => 'pt-pt', 'pt_AO' => 'pt-pt', 'pt_PT_ao90' => 'pt-pt', 'de_DE_formal' => 'de', 'en_GB' => 'en-gb', 'en_US' => 'en-us', 'en_CA' => 'en-us', 'en_ZA' => 'en-gb', 'en_NZ' => 'en-gb', 'en_AU' => 'en-gb', 'nb_NO' => 'nb', 'ckb' => 'ckb', // Kurdish (Sorani) 'es_AR' => 'es-419', 'es_CL' => 'es-419', 'es_CO' => 'es-419', 'es_CR' => 'es-419', 'es_DO' => 'es-419', 'es_EC' => 'es-419', 'es_GT' => 'es-419', 'es_MX' => 'es-419', 'es_PE' => 'es-419', 'es_PR' => 'es-419', 'es_UY' => 'es-419', 'es_VE' => 'es-419' ); if ( isset( $exceptions_target_mapping_codes[$target_language_code] ) ){ $target_language = $exceptions_target_mapping_codes[$target_language_code]; } return $target_language; } public function get_formality_setting_for_language($target_language_code){ $formality = "default"; if(isset($this->settings["translation-languages-formality-parameter"][ $target_language_code ])) { if ( $this->settings["translation-languages-formality-parameter"][ $target_language_code ] == 'informal'){ $formality = "less"; }else{ if($this->settings["translation-languages-formality-parameter"][ $target_language_code ] == 'formal'){ $formality = "more"; } } } return $formality; } public function get_languages_that_support_formality(){ $data = get_option('trp_db_stored_data', array() ); if(!isset($data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['formality-supported-languages'])) { $this->check_languages_availability($this->settings['translation-languages'], true); $data = get_option('trp_db_stored_data', array()); } $formality_supported_languages = isset( $data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['formality-supported-languages'] ) ? $data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['formality-supported-languages'] : []; return $formality_supported_languages; } public function get_request_formality_for_language($target_language_code){ $formality = $this->get_formality_setting_for_language($target_language_code); $formality_supported_languages = $this->get_languages_that_support_formality(); if(isset($formality_supported_languages[$target_language_code]) && $formality_supported_languages[$target_language_code] == "true"){ $formality = ( $formality == "less" ) ? "informal" : $formality; $formality = ( $formality == "more" ) ? "formal" : $formality; return $formality; }else{ return 'default'; } } public function check_formality() { $formality_supported_languages = []; $language_iso_codes = []; $response = wp_remote_post( "{$this->get_api_url()}/languages", array( 'method' => 'GET', 'timeout' => 45, 'headers' => [ 'Content-Type' => 'application/json' ], ) ); if ( is_array( $response ) && !is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) { $response_data = json_decode( $response['body'] ); $all_languages = $this->trp_languages->get_wp_languages(); foreach ( $all_languages as $language ) { $language_iso_codes[ $language['language'] ] = $this->configure_api_target_language( reset( $language['iso'] ), '', $language['language'] ); } foreach ( $response_data as $supported_language ) { $matched_languages = array_keys( $language_iso_codes, strtolower( $supported_language->language ) ); if ( $matched_languages ) { foreach ( $matched_languages as $matched_language ) { $formality_supported_languages[ $matched_language ] = $supported_language->formality ? 'true' : 'false'; } } } } return apply_filters( 'trp_mtapi_formality_languages', $formality_supported_languages ); } /** * Check if translations are permanently disabled for free license * * @return bool */ private function is_free_license_translation_disabled() { $data = get_option('trp_db_stored_data', array()); return isset($data['mtapi_free_license_disabled']) && $data['mtapi_free_license_disabled'] === true; } /** * Set permanent translation disable status for free license * * @param bool $disabled */ private function set_free_license_translation_disabled($disabled) { $data = get_option('trp_db_stored_data', array()); $data['mtapi_free_license_disabled'] = $disabled; update_option('trp_db_stored_data', $data); } /** * Check if current license is a free license * * @return bool */ private function is_free_license() { $license_details = get_option('trp_license_details'); if (isset($license_details['valid'][0]->item_name) && $license_details['valid'][0]->item_name === 'TranslatePress') { return true; } return false; } } includes/class-plugin-notices.php 0000777 00000123716 15251156640 0013154 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class that adds a misc notice * * @since v.2.0 * * @return void */ class TRP_Add_General_Notices{ public $notificationId = ''; public $notificationMessage = ''; public $notificationClass = ''; public $startDate = ''; public $endDate = ''; public $force_show = false;//this attribute ignores the dismiss notification function __construct( $notificationId, $notificationMessage, $notificationClass = 'updated' , $startDate = '', $endDate = '', $force_show = false ){ $this->notificationId = $notificationId; $this->notificationMessage = $notificationMessage; $this->notificationClass = $notificationClass; $this->force_show = $force_show; if( !empty( $startDate ) && time() < strtotime( $startDate ) ) return; if( !empty( $endDate ) && time() > strtotime( $endDate ) ) return; add_action( 'admin_notices', array( $this, 'add_admin_notice' ) ); add_action( 'admin_init', array( $this, 'dismiss_notification' ) ); } // Display a notice that can be dismissed in case the license key is inactive function add_admin_notice() { global $current_user; global $pagenow; $user_id = $current_user->ID; do_action( $this->notificationId.'_before_notification_displayed', $current_user, $pagenow ); if ( current_user_can( 'manage_options' ) ){ // Check that the user hasn't already clicked to ignore the message if ( ! get_user_meta($user_id, $this->notificationId.'_dismiss_notification' ) || $this->force_show ) {//ignore the dismissal if we have force_show add_filter('safe_style_css', array( $this, 'allow_z_index_in_wp_kses')); echo wp_kses( apply_filters($this->notificationId.'_notification_message','<div class="'. $this->notificationClass .'" style="position:relative;' . ((strpos($this->notificationClass, 'trp-narrow')!==false ) ? 'max-width: 825px;' : '') . '" >'.$this->notificationMessage.'</div>', $this->notificationMessage), [ 'div' => [ 'class' => [],'style' => [] ], 'p' => ['style' => [], 'class' => []], 'a' => ['href' => [], 'type'=> [], 'class'=> [], 'style'=>[], 'title'=>[],'target'=>[]], 'span' => ['class'=> []], 'strong' => [], 'img' => [ 'src' => [], 'style' => [] ], 'br' => [] ] ); remove_filter('safe_style_css', array( $this, 'allow_z_index_in_wp_kses')); } do_action( $this->notificationId.'_notification_displayed', $current_user, $pagenow ); } do_action( $this->notificationId.'_after_notification_displayed', $current_user, $pagenow ); } function allow_z_index_in_wp_kses( $styles ) { $styles[] = 'z-index'; $styles[] = 'position'; return $styles; } function dismiss_notification() { global $current_user; $user_id = $current_user->ID; do_action( $this->notificationId.'_before_notification_dismissed', $current_user ); // If user clicks to ignore the notice, add that to their user meta if ( isset( $_GET[$this->notificationId.'_dismiss_notification']) && '0' == $_GET[$this->notificationId.'_dismiss_notification'] ) add_user_meta( $user_id, $this->notificationId.'_dismiss_notification', 'true', true ); do_action( $this->notificationId.'_after_notification_dismissed', $current_user ); } } Class TRP_Plugin_Notifications { public $notifications = array(); private static $_instance = null; private $prefix = 'trp'; private $menu_slug = 'options-general.php'; public $pluginPages = array( 'translate-press', 'trp_addons_page', 'trp_license_key', 'trp_advanced_page', 'trp_machine_translation', 'trp_test_machine_api', 'trp_language_switcher' ); protected function __construct() { add_action( 'admin_init', array( $this, 'dismiss_admin_notifications' ), 200 ); add_action( 'admin_init', array( $this, 'add_admin_menu_notification_counts' ), 1000 ); add_action( 'admin_init', array( $this, 'remove_other_plugin_notices' ), 1001 ); } function dismiss_admin_notifications() { if( ! empty( $_GET[$this->prefix.'_dismiss_admin_notification'] ) ) { $notifications = self::get_instance(); $notifications->dismiss_notification( sanitize_text_field( $_GET[$this->prefix.'_dismiss_admin_notification'] ) ); } } function add_admin_menu_notification_counts() { global $menu, $submenu; $notifications = TRP_Plugin_Notifications::get_instance(); if( ! empty( $menu ) ) { foreach( $menu as $menu_position => $menu_data ) { if( ! empty( $menu_data[2] ) && $menu_data[2] == $this->menu_slug ) { $menu_count = $notifications->get_count_in_menu(); if( ! empty( $menu_count ) ) $menu[$menu_position][0] .= '<span class="update-plugins '.$this->prefix.'-update-plugins"><span class="plugin-count">' . $menu_count . '</span></span>'; } } } if( ! empty( $submenu[$this->menu_slug] ) ) { foreach( $submenu[$this->menu_slug] as $menu_position => $menu_data ) { $menu_count = $notifications->get_count_in_submenu( $menu_data[2] ); if( ! empty( $menu_count ) ) $submenu[$this->menu_slug][$menu_position][0] .= '<span class="update-plugins '.$this->prefix.'-update-plugins"><span class="plugin-count">' . $menu_count . '</span></span>'; } } } /* handle other plugin notifications on our plugin pages */ function remove_other_plugin_notices(){ /* remove all other plugin notifications except our own from the rest of the PB pages */ if( $this->is_plugin_page() ) { global $wp_filter; if (!empty($wp_filter['admin_notices'])) { if (!empty($wp_filter['admin_notices']->callbacks)) { foreach ($wp_filter['admin_notices']->callbacks as $priority => $callbacks_level) { if (!empty($callbacks_level)) { foreach ($callbacks_level as $key => $callback) { if( is_array( $callback['function'] ) ){ if( is_object($callback['function'][0])) {//object here if (strpos(get_class($callback['function'][0]), 'PMS_') !== 0 && strpos(get_class($callback['function'][0]), 'WPPB_') !== 0 && strpos(get_class($callback['function'][0]), 'TRP_') !== 0 && strpos(get_class($callback['function'][0]), 'WCK_') !== 0) { unset($wp_filter['admin_notices']->callbacks[$priority][$key]);//unset everything that doesn't come from our plugins } } } else if( is_string( $callback['function'] ) ){//it should be a function name if (strpos($callback['function'], 'pms_') !== 0 && strpos($callback['function'], 'wppb_') !== 0 && strpos($callback['function'], 'trp_') !== 0 && strpos($callback['function'], 'wck_') !== 0) { unset($wp_filter['admin_notices']->callbacks[$priority][$key]);//unset everything that doesn't come from our plugins } } } } } } } } } /** * * */ public static function get_instance() { if( is_null( self::$_instance ) ) self::$_instance = new TRP_Plugin_Notifications(); return self::$_instance; } /** * * */ public function add_notification( $notification_id = '', $notification_message = '', $notification_class = 'update-nag', $count_in_menu = true, $count_in_submenu = array(), $show_in_all_backend = false, $force_show = false ) { if( empty( $notification_id ) ) return; if( empty( $notification_message ) ) return; /** * added a $show_in_all_backend argument in version 1.4.6 that allows some notifications to be displayed on all the pages not just the plugin pages * we needed it for license notifications * * if you want a notification that is non-dismissable on is_plugin_page() dismissable on the rest of the pages, simply do the verification where * TRP_Plugin_Notifications->add_notification() is called * */ $this->notifications[$notification_id] = array( 'id' => $notification_id, 'message' => $notification_message, 'class' => $notification_class, 'count_in_menu' => $count_in_menu, 'count_in_submenu' => $count_in_submenu ); if( $this->is_plugin_page() || $show_in_all_backend ) { new TRP_Add_General_Notices( $notification_id, $notification_message, $notification_class, '', '', $force_show ); } } /** * * */ public function get_notifications() { return $this->notifications; } /** * * */ public function get_notification( $notification_id = '' ) { if( empty( $notification_id ) ) return null; $notifications = $this->get_notifications(); if( ! empty( $notifications[$notification_id] ) ) return $notifications[$notification_id]; else return null; } /** * * */ public function dismiss_notification( $notification_id = '' ) { global $current_user; add_user_meta( $current_user->ID, $notification_id . '_dismiss_notification', 'true', true ); do_action('trp_dismiss_notification', $notification_id, $current_user); } /** * * */ public function get_count_in_menu() { $count = 0; foreach( $this->notifications as $notification ) { if( ! empty( $notification['count_in_menu'] ) ) $count++; } return $count; } /** * * */ public function get_count_in_submenu( $submenu = '' ) { if( empty( $submenu ) ) return 0; $count = 0; foreach( $this->notifications as $notification ) { if( empty( $notification['count_in_submenu'] ) ) continue; if( ! is_array( $notification['count_in_submenu'] ) ) continue; if( ! in_array( $submenu, $notification['count_in_submenu'] ) ) continue; $count++; } return $count; } /** * Test if we are an a page that belong to our plugin * */ public function is_plugin_page() { if( !empty( $this->pluginPages ) ){ foreach ( $this->pluginPages as $pluginPage ){ if( ! empty( $_GET['page'] ) && false !== strpos( sanitize_text_field( $_GET['page'] ), $pluginPage ) ) return true; if( ! empty( $_GET['post_type'] ) && false !== strpos( sanitize_text_field( $_GET['post_type'] ), $pluginPage ) ) return true; if( ! empty( $_GET['post'] ) && false !== strpos( get_post_type( (int)$_GET['post'] ), $pluginPage ) ) return true; } } return false; } } class TRP_Trigger_Plugin_Notifications{ private $settings; private $settings_obj; private $machine_translator_logger; function __construct($settings) { $this->settings = $settings; add_action( 'admin_init', array( $this, 'add_plugin_notifications' ) ); } function add_plugin_notifications() { $notifications = TRP_Plugin_Notifications::get_instance(); /* License Notifications */ $license_details = get_option( 'trp_license_details' ); $license_status = get_option( 'trp_license_status' ); $is_demosite = ( strpos(site_url(), 'https://demo.translatepress.com' ) !== false ); $trp = TRP_Translate_Press::get_trp_instance(); $tp_product_name = reset($trp->tp_product_name); $free_version = $tp_product_name == 'TranslatePress'; if ( empty($license_details) && !$is_demosite && !$free_version ){ /* this must be unique */ $notification_id = 'trp_invalid_license'; $message = '<p style="padding-right:30px;">'; // [utm10] $message .= sprintf( __('Your <strong>TranslatePress</strong> license is missing or invalid. <br/>Please %1$sregister your copy%2$s to enable automatic website translation via TranslatePress AI, premium addons, automatic updates and support. Need a license key? %3$sPurchase one now%4$s' , 'translatepress-multilingual' ), "<a href='". admin_url('/admin.php?page=trp_license_key') ."'>", "</a>", "<a href='https://translatepress.com/pricing/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=pro-no-active-license' target='_blank' class='button-primary'>", "</a>" ); if ( !$notifications->is_plugin_page() ) { //make sure to use the trp_dismiss_admin_notification arg $message .= '<a style="text-decoration: none;z-index:100;" href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $force_show = false; } else { $force_show = true; //ignore dismissal on own plugin pages } $message .= '</p>'; $notifications->add_notification( $notification_id, $message, 'trp-notice notice error', true, array('translate-press'), true, $force_show ); } if( !empty($license_details) && !$is_demosite && !$free_version){ /* if we have any invalid response for any of the addon show just the error notification and ignore any valid responses */ if( !empty( $license_details['invalid'] ) ){ //take the first addon details (it should be the same for the rest of the invalid ones) $license_detail = $license_details['invalid'][0]; /* this must be unique */ $notification_id = 'trp_invalid_license'; $message = '<p style="padding-right:30px;">'; // https://easydigitaldownloads.com/docs/software-licensing-api/#activate_license if( $license_detail->error == 'missing' || $license_detail->error == 'disabled' || $license_detail->error == 'key_mismatch' ) //[utm11] $message .= sprintf( __('Your <strong>TranslatePress</strong> license is missing or invalid. <br/>Please %1$sregister your copy%2$s to enable automatic website translation via TranslatePress AI, premium addons, automatic updates and support. Need a license key? %3$sPurchase one now%4$s' , 'translatepress-multilingual' ), "<a href='". admin_url('/admin.php?page=trp_license_key') ."'>", "</a>", "<a href='https://translatepress.com/pricing/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=pro-no-active-license' target='_blank' class='button-primary'>", "</a>" ); elseif( $license_detail->error == 'site_inactive' ) //[utm12] $message .= __( 'Your license is disabled for this URL. Re-enable it from <a target="_blank" href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-deactivated">https://translatepress.com/account</a> -> Manage Sites.', 'translatepress-multilingual' ); elseif( $license_detail->error == 'no_activations_left' ) //[utm13] $message .= sprintf( __('You have reached the activation limit for your <strong>%1$s</strong> license. <br/>Manage your active sites from %2$s your account %3$s.' , 'translatepress-multilingual' ), $tp_product_name, "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=activation-limit' target='_blank' >", "</a>" ); elseif( $license_detail->error == 'item_name_mismatch' ){ //[utm14] $message .= sprintf( __('License key mismatch. The license you entered doesn’t match the <strong>%1$s</strong> version you have installed. <br/>Please check that you’ve installed the correct version for your license from your %2$sTranslatePress account%3$s.' , 'translatepress-multilingual' ), $tp_product_name, "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-mismatch' target='_blank' >", "</a>" ); if( !empty( $license_detail->item_name ) && urldecode( $license_detail->item_name ) === 'TranslatePress' ) { $message .= __( '<br/>If you have only the free plugin installed but added a paid license, please install the paid plugin from your TranslatePress account.' , 'translatepress-multilingual' ); } } elseif( $license_detail->error == 'expired' ) //[utm15] $message .= sprintf( __('Your <strong>TranslatePress</strong> license has expired. <br/>Please %1$sRenew Your Licence%2$s to continue receiving access to automatic translations via TranslatePress AI, premium addons, product downloads, and automatic updates. %3$sRenew now %4$s' , 'translatepress-multilingual' ), "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expired-license' target='_blank'>", "</a>", "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expired-license' target='_blank' class='button-primary'>", "</a>" ); else { $license_error = __("Error: ", "translatepress-multilingual"); if (!empty($license_detail->error)){ $license_error .= $license_detail->error; } $message .= __('Something went wrong, please try again.', 'translatepress-multilingual') . $license_error ; } if ( !$notifications->is_plugin_page() ) { //make sure to use the trp_dismiss_admin_notification arg $message .= '<a style="text-decoration: none;z-index:100;" href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $force_show = false; } else { $force_show = true; //ignore dismissal on own plugin pages } $message .= '</p>'; if (!isset($_GET['trp_sl_activation'])) { $notifications->add_notification($notification_id, $message, 'trp-notice notice error', true, array('translate-press'), true, $force_show); } } elseif( !empty( $license_details['valid'] ) ){ //take the first addon details (it should be the same for the rest of the valid ones) $license_detail = $license_details['valid'][0]; if( isset( $license_detail->auto_billing ) && !$license_detail->auto_billing ) {//auto_billing was added by us in a filter on translatepress.com if ( ( strtotime($license_detail->expires ) - time() ) / (60 * 60 * 24) < 30 ) { /* this must be unique */ $notification_id = 'trp_will_expire_license'; //[utm16] $message = '<p style="padding-right:30px;">' . sprintf( __( 'Your <strong>TranslatePress</strong> license will expire on %1$s. Please %2$sRenew Your Licence%3$s to continue receiving access to automatic translations via TP AI, premium addons, product downloads and automatic updates. %4$sRenew Now%5$s', 'translatepress-multilingual'), date_i18n( get_option( 'date_format' ), strtotime( $license_detail->expires, current_time( 'timestamp' ) ) ), '<a href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expire-soon" target="_blank">', '</a>', "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expire-soon' target='_blank' class='button-primary'>", "</a>"). '</p>'; if ( !$notifications->is_plugin_page() ) { //make sure to use the trp_dismiss_admin_notification arg $message .= '<a style="text-decoration: none;z-index:100;" href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__('Dismiss this notice.', 'translatepress-multilingual') . '</span></a>'; $force_show = false; } else { $force_show = true; //ignore dismissal on own plugin pages } if (!isset($_GET['trp_sl_activation'])) { $notifications->add_notification($notification_id, $message, 'trp-notice notice notice-info is-dismissible', true, array('translate-press'), false, $force_show); } } } } } // If the license is invalid and the translation engine is DeepL or TP AI, show a notification only on the paid versions if( !in_array( 'TranslatePress', $trp->tp_product_name ) ) { if (isset($this->settings['trp_machine_translation_settings']['machine-translation']) && $this->settings['trp_machine_translation_settings']['machine-translation'] === 'yes') { if (isset($this->settings['trp_machine_translation_settings']['translation-engine']) && ($this->settings['trp_machine_translation_settings']['translation-engine'] === 'deepl' || $this->settings['trp_machine_translation_settings']['translation-engine'] === 'mtapi')) { $message = ''; $force_show = true; if ($this->settings['trp_machine_translation_settings']['translation-engine'] === 'deepl') $engine_name = 'DeepL'; else $engine_name = 'TranslatePress AI'; if (empty($license_status)) { $notification_id = 'trp_' . $this->settings['trp_machine_translation_settings']['translation-engine'] . '_missing_license'; $message = '<p style="padding-right:30px;">'; $message .= sprintf( __('Please %1$senter%2$s your license key to enable %3$s automatic translation.', 'translatepress-multilingual'), '<a href="' . admin_url('/admin.php?page=trp_license_key') . '">', '</a>', $engine_name ); if (!$notifications->is_plugin_page()) { //make sure to use the trp_dismiss_admin_notification arg $message .= '<a style="text-decoration: none;z-index:100;" href="' . add_query_arg(array('trp_dismiss_admin_notification' => $notification_id)) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__('Dismiss this notice.', 'translatepress-multilingual') . '</span></a>'; $force_show = false; } else { $force_show = true; //ignore dismissal on own plugin pages } $message .= '</p>'; } elseif ($license_status !== 'valid') { $notification_id = 'trp_' . $this->settings['trp_machine_translation_settings']['translation-engine'] . '_invalid_license'; $message = '<p style="padding-right:30px;">'; //[utm17] $message .= sprintf( __('%1$s automatic translation requires an active license. Please %2$srenew%3$s your license or purchase a new one %4$shere%5$s.', 'translatepress-multilingual'), $engine_name, '<a href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expired-license-with-at">', '</a>', '<a href="https://translatepress.com/pricing/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expired-license-with-at" target="_blank">', '</a>' ); if (!$notifications->is_plugin_page()) { //make sure to use the trp_dismiss_admin_notification arg $message .= '<a style="text-decoration: none;z-index:100;" href="' . add_query_arg(array('trp_dismiss_admin_notification' => $notification_id)) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__('Dismiss this notice.', 'translatepress-multilingual') . '</span></a>'; $force_show = false; } else { $force_show = true; //ignore dismissal on own plugin pages } $message .= '</p>'; } if (!empty($message)) $notifications->add_notification($notification_id, $message, 'trp-notice notice error', true, array('translate-press'), true, $force_show); } } } /* * Non-free license low quota notification */ if ( !empty($license_details) && !$is_demosite && !$free_version && $license_status === 'valid' ) { // Use cached quota that's updated during translation operations $cached_quota = get_transient('trp_mtapi_cached_quota'); if ( $cached_quota !== false && is_numeric($cached_quota) && $cached_quota > 0 && $cached_quota < 25000 ) { $notification_id = 'trp_low_quota_warning'; $message = '<p style="padding-right:30px;">'; //[utm18] $message .= sprintf( __('You have less than 5,000 TranslatePress AI words remaining. To continue automatically translating your website, please %spurchase additional AI words at a discount from your account%s.', 'translatepress-multilingual'), '<a href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=tp-ai-words-upsell" target="_blank">', '</a>' ); $message .= '<a style="text-decoration: none;z-index:100;" href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $message .= '</p>'; $notifications->add_notification( $notification_id, $message, 'trp-notice notice notice-warning', true, array('translate-press'), true, false ); } } /* * Free Licenses Notifications */ if( !empty($license_details) && !$is_demosite && $free_version){ if( !empty( $license_details['invalid'] ) ){ //take the first addon details (it should be the same for the rest of the invalid ones) $license_detail = $license_details['invalid'][0]; /* this must be unique */ $notification_id = 'trp_invalid_license'; $message = '<p style="padding-right:30px;">'; // https://easydigitaldownloads.com/docs/software-licensing-api/#activate_license if( $license_detail->error == 'missing' || $license_detail->error == 'disabled' || $license_detail->error == 'key_mismatch' ) //[utm19] $message .= sprintf( __('You do not have a valid license for <strong>TranslatePress</strong>. %1$sGet one for free%2$s to get access to TranslatePress AI.' , 'translatepress-multilingual' ), "<a href='https://translatepress.com/ai-free/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=tp-ai-free' target='_blank'>", "</a>" ); elseif( $license_detail->error == 'site_inactive' ) //[utm20] $message .= __( 'Your license is disabled for this URL. Re-enable it from <a target="_blank" href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-deactivated">https://translatepress.com/account</a> -> Manage Sites.', 'translatepress-multilingual' ); elseif( $license_detail->error == 'no_activations_left' ) //[utm21] $message .= sprintf( __('You have reached the activation limit for your <strong>%1$s</strong> license. <br/>Manage your active sites from %2$s your account %3$s.' , 'translatepress-multilingual' ), $tp_product_name, "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=activation-limit' target='_blank' >", "</a>" ); elseif( $license_detail->error == 'item_name_mismatch' ){ //[utm22] $message .= sprintf( __('License key mismatch. The license you entered doesn’t match the <strong>%1$s</strong> version you have installed. <br/>Please check that you’ve installed the correct version for your license from your %2$sTranslatePress account%3$s.' , 'translatepress-multilingual' ), $tp_product_name, "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=license-mismatch' target='_blank' >", "</a>" ); if( !empty( $license_detail->item_name ) && urldecode( $license_detail->item_name ) === 'TranslatePress' ) { $message .= __( '<br/>If you have only the free plugin installed but added a paid license, please install the paid plugin from your TranslatePress account.' , 'translatepress-multilingual' ); } } elseif( $license_detail->error == 'website_already_on_free_license' ) //[utm23] $message .= sprintf( __('This website is already activated under a free license. Each website can only use one free license. Please upgrade to a premium plan for more TranslatePress AI words from %1$s your account %2$s.' , 'translatepress-multilingual' ), "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=tp-ai-free-used-key' target='_blank' class='button-primary' >", "</a>" ); elseif( $license_detail->error == 'expired' ) //[utm24] $message .= sprintf( __('Your <strong>TranslatePress</strong> license has expired. <br/>Please %1$sRenew Your Licence%2$s to continue receiving access to automatic translations via TranslatePress AI, premium addons, product downloads, and automatic updates. %3$sRenew now %4$s' , 'translatepress-multilingual' ), "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expired-license' target='_blank'>", "</a>", "<a href='https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=expired-license' target='_blank' class='button-primary'>", "</a>" ); else { $license_error = __(" Error: ", "translatepress-multilingual"); if (!empty($license_detail->error)){ $license_error .= $license_detail->error; } $message .= __('Something went wrong, please try again.', 'translatepress-multilingual') . $license_error ; } if ( !$notifications->is_plugin_page() ) { //make sure to use the trp_dismiss_admin_notification arg $message .= '<a style="text-decoration: none;z-index:100;" href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $force_show = false; } else { $force_show = true; //ignore dismissal on own plugin pages } $message .= '</p>'; if ($license_detail->error != 'missing'){ // only show notification if we haven't clicked the activate license button. Otherwise we'll end up with duplicated messages. if (!isset($_GET['trp_sl_activation'])) { $notifications->add_notification( $notification_id, $message, 'trp-notice notice error', true, array('translate-press'), true, $force_show ); } } } } /* this must be unique */ // $notification_id = 'trp_new_feature_image_translation'; // // $message = '<p style="padding-right:30px;">' . __('NEW: Display different images based on language. Find out <a href="https://translatepress.com/docs/image-translation/" >how to translate images, sliders and more</a> from the TranslatePress editor.' , 'translatepress-multilingual' ) . '</p>'; // //make sure to use the trp_dismiss_admin_notification arg // $message .= '<a href="' . add_query_arg(array('trp_dismiss_admin_notification' => $notification_id)) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . __('Dismiss this notice.', 'translatepress-multilingual') . '</span></a>'; // // $notifications->add_notification($notification_id, $message, 'trp-notice trp-narrow notice notice-info', true, array('translate-press')); /* String translation */ // $notification_id = 'trp_new_feature_string_translation'; // $message = '<p style="padding-right:30px;">' . __('NEW: Translate Emails and other plugin texts using String Translation. Find out <a href="https://translatepress.com/docs/translation-editor/string-translation/?utm_source=wpbackend&utm_medium=clientsite&utm_content=tpsettings&utm_campaign=TRP" >how to search for a specific text to translate</a>.' , 'translatepress-multilingual' ) . '</p>'; // //make sure to use the trp_dismiss_admin_notification arg // $message .= '<a href="' . add_query_arg(array('trp_dismiss_admin_notification' => $notification_id)) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . __('Dismiss this notice.', 'translatepress-multilingual') . '</span></a>'; // $notifications->add_notification($notification_id, $message, 'trp-notice trp-narrow notice notice-info', true, array('translate-press')); /* * Machine translation enabled and quota are met. */ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->settings_obj ) $this->settings_obj = $trp->get_component( 'settings' ); if ( ! $this->machine_translator_logger ) $this->machine_translator_logger = $trp->get_component( 'machine_translator_logger' ); if( 'yes' === $this->settings['trp_machine_translation_settings']['machine-translation'] && $this->machine_translator_logger->quota_exceeded() ) { /* this must be unique */ $notification_id = 'trp_machine_translation_quota_exceeded_'. date('Ymd'); $message = ''; $message .= '<p style="margin-top: 16px;padding-right:30px;">'; $message .= sprintf( __( 'The daily quota for machine translation characters exceeded. Please check the <strong>TranslatePress -> <a href="%s">Automatic Translation</a></strong> page for more information.', 'translatepress-multilingual' ), admin_url( 'admin.php?page=trp_machine_translation' ) ); $message .= '</p>'; //make sure to use the trp_dismiss_admin_notification arg $message .= '<a href="' . add_query_arg(array('trp_dismiss_admin_notification' => $notification_id)) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $notifications->add_notification($notification_id, $message, 'trp-notice trp-narrow notice notice-info', true, array('translate-press')); } /** * Black Friday * * Showing this to: * free users or * users that have expired or disabled licenses */ if( trp_bf_show_promotion() ){ $free_version = !class_exists( 'TRP_Handle_Included_Addons' ); $license_status = trp_get_license_status(); // Plugin pages if( $notifications->is_plugin_page() ){ $notification_id = 'trp_bf_2025'; $message = '<img style="max-width: 60px;" src="' . TRP_PLUGIN_URL . 'assets/images/tp-logo.png" />'; if ( !$free_version && $license_status == 'expired' ){ $message .= '<div><p style="font-size: 110%;margin-top:0px;margin-bottom:4px;padding:0px;">' . '<strong>Get PRO back at a fraction of the cost!</strong>' . '</p>'; //[utm25] $message .= '<p style="font-size: 110%;margin-top:0px;margin-bottom: 0px;padding:0px;">Get our <strong>Black Friday</strong> deal and renew your TranslatePress license with our <strong>biggest sale of the year</strong>. <a class="button-primary" style="margin-top:6px;" href="https://translatepress.com/account/?utm_source=tp-settings&utm_medium=client-site&utm_campaign=bf-2025-renewal" target="_blank">Get discount</a></p></div>'; } else { //[utm26] $message .= '<div><p style="font-size: 110%;margin-top:0px;margin-bottom:4px;padding:0px;">' . '<strong>Go PRO at a fraction of the cost!</strong>' . '</p>'; $message .= '<p style="font-size: 110%;margin-top:0px;margin-bottom: 0px;padding:0px;">Get our <strong>Black Friday</strong> deal and switch to a premium license of TranslatePress with our <strong>biggest sale of the year</strong>. <a class="button-primary" style="margin-top:6px;" href="https://translatepress.com/black-friday/?utm_source=tp-settings&utm_medium=client-site&utm_campaign=bf-2025" target="_blank">Get discount</a></p></div>'; } $message .= '<a href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $notifications->add_notification( $notification_id, $message, 'trp-notice trp-narrow notice notice-info trp-bf-notice-container', true, array( 'translate-press' ) ); } else { $notification_id = 'trp_bf_2025'; $message = '<img style="float: left; margin: 10px 8px 10px 0px; max-width: 20px;" src="' . TRP_PLUGIN_URL . 'assets/images/tp-logo-2d.png" />'; if ( !$free_version && $license_status == 'expired' ) //[utm27] $message .= '<p style="padding-right:30px;font-size: 110%;"><strong>TranslatePress Black Friday is here!</strong> Renew your <strong>PRO</strong> license with our biggest discount of the year. <a href="https://translatepress.com/account/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=bf-2025-renewal" target="_blank">Learn more</a></p>'; else //[utm28] $message .= '<p style="padding-right:30px;font-size: 110%;"><strong>TranslatePress Black Friday is here!</strong> Go <strong>PRO</strong> with our biggest discount of the year. <a href="https://translatepress.com/black-friday/?utm_source=wp-dashboard&utm_medium=client-site&utm_campaign=bf-2025" target="_blank">Learn more</a></p>'; $message .= '<a href="' . add_query_arg( array( 'trp_dismiss_admin_notification' => $notification_id ) ) . '" type="button" class="notice-dismiss"><span class="screen-reader-text">' . esc_html__( 'Dismiss this notice.', 'translatepress-multilingual' ) . '</span></a>'; $notifications->add_notification( $notification_id, $message, 'trp-notice trp-narrow notice notice-info', true, array('translate-press'), true ); } } } } function trp_bf_show_promotion(){ if( !trp_bf_promotion_is_active() ) return false; $license_details = get_option( 'trp_license_details' ); if( !empty( $license_details ) ){ foreach( $license_details as $row ){ if( !empty( $row ) ){ foreach( $row as $details ){ // show message for expired and disabled licenses if( isset( $details->error ) && in_array( $details->error, [ 'expired', 'disabled', 'revoked', 'missing', 'no_activations_left' ] ) ) return true; } } } } if( !trp_is_paid_version() ) return true; return false; } function trp_bf_promotion_is_active(){ $black_friday = array( 'start_date' => '11/24/2025 00:00', 'end_date' => '12/02/2025 23:59', ); $current_date = time(); if( $current_date > strtotime( $black_friday['start_date'] ) && $current_date < strtotime( $black_friday['end_date'] ) ) return true; return false; } includes/class-editor-api-regular-strings.php 0000777 00000040251 15251156640 0015367 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Editor_Api_Regular_Strings { /* @var TRP_Query */ protected $trp_query; /* @var TRP_Translation_Render */ protected $translation_render; /* @var TRP_Translation_Manager */ protected $translation_manager; /* @var TRP_Url_Converter */ protected $url_converter; /* @var TRP_Settings */ protected $settings; /** * TRP_Translation_Manager constructor. * * @param array $settings Settings option. */ public function __construct( $settings ){ $this->settings = $settings; } /** * Returns translations based on original strings and ids. * * Hooked to wp_ajax_trp_get_translations_regular * and wp_ajax_nopriv_trp_get_translations_regular. */ public function get_translations() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { check_ajax_referer( 'get_translations', 'security' ); if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_get_translations_regular' && !empty( $_POST['language'] ) && in_array( $_POST['language'], $this->settings['translation-languages'] ) ) { $originals = (empty($_POST['originals']) )? array() : json_decode(stripslashes($_POST['originals'])); /* phpcs:ignore */ /* sanitized downstream */ $skip_machine_translation = (empty($_POST['skip_machine_translation']) )? array() : json_decode(stripslashes($_POST['skip_machine_translation'])); /* phpcs:ignore */ /* sanitized downstream */ $ids = (empty($_POST['string_ids']) )? array() : json_decode(stripslashes($_POST['string_ids'])); /* phpcs:ignore */ /* sanitized downstream */ if ( is_array( $skip_machine_translation ) ) { if ( is_array( $ids ) || is_array( $originals ) ) { $trp = TRP_Translate_Press::get_trp_instance(); if ( !$this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } if ( !$this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $block_type = $this->trp_query->get_constant_block_type_regular_string(); $dictionaries = $this->get_translation_for_strings( $ids, $originals, $block_type, $skip_machine_translation ); $localized_text = $this->translation_manager->string_groups(); $string_group = __( 'Others', 'translatepress-multilingual' ); // this type is not registered in the string types because it will be overwritten by the content in data-trp-node-type if ( isset( $_POST['dynamic_strings'] ) && $_POST['dynamic_strings'] === 'true' ) { $string_group = $localized_text['dynamicstrings']; } $dictionary_by_original = trp_sort_dictionary_by_original( $dictionaries, 'regular', $string_group, sanitize_text_field( $_POST['language'] ) ); echo trp_safe_json_encode( $dictionary_by_original );//phpcs:ignore } } } } wp_die(); } /** * Return dictionary with translated strings. * * @param $strings * @param null $block_type * * @return array */ protected function get_translation_for_strings( $ids, $originals, $block_type = null, $skip_machine_translation = array() ){ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } if ( ! $this->translation_render ) { $this->translation_render = $trp->get_component('translation_render'); } if ( ! $this->url_converter ) { $this->url_converter = $trp->get_component('url_converter'); } $home_url = home_url(); $id_array = array(); $original_array = array(); $dictionaries = array(); foreach ( $ids as $id ) { if ( isset( $id ) && is_numeric( $id ) ) { $id_array[] = (int) $id; } } foreach( $originals as $original ){ if ( isset( $original ) ) { $trimmed_string = trp_full_trim( trp_sanitize_string( $original, false ) ); if ( ( filter_var($trimmed_string, FILTER_VALIDATE_URL) === false) ){ // not url $original_array[] = $trimmed_string; }else{ // is url if ( $this->translation_render->is_external_link( $trimmed_string, $home_url ) || $this->url_converter->url_is_file( $trimmed_string ) ) { // allow only external url or file urls $original_array[] = remove_query_arg( 'trp-edit-translation', $trimmed_string ); } } } } $current_language = isset( $_POST['language'] ) && in_array( $_POST['language'], $this->settings['translation-languages'] ) ? $_POST['language'] : ''; /* phpcs:ignore */ /* sanitized by checking against existing languages */ // necessary in order to obtain all the original strings if ( $this->settings['default-language'] != $current_language ) { if ( !empty ( $original_array ) && current_user_can ( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { $this->translation_render->process_strings($original_array, $current_language, $block_type, $skip_machine_translation); } $dictionaries[$current_language] = $this->trp_query->get_string_rows( $id_array, $original_array, $current_language ); }else{ $dictionaries[$current_language] = array(); } if ( isset( $_POST['all_languages'] ) && $_POST['all_languages'] === 'true' ) { foreach ($this->settings['translation-languages'] as $language) { if ($language == $this->settings['default-language']) { $dictionaries[$language]['default-language'] = true; continue; } if ($language == $current_language) { continue; } if (empty($original_strings)) { $original_strings = $this->extract_original_strings($dictionaries[$current_language], $original_array, $id_array); } if (current_user_can(apply_filters( 'trp_translating_capability', 'manage_options' ))) { $this->translation_render->process_strings($original_strings, $language, $block_type, $skip_machine_translation); } $dictionaries[$language] = $this->trp_query->get_string_rows(array(), $original_strings, $language); } } if ( count( $skip_machine_translation ) > 0 ) { foreach ( $dictionaries as $language => $dictionary ) { if ( $language === $this->settings['default-language'] ) { continue; } foreach ( $dictionary as $key => $string ) { if ( $string->status == 1 && in_array( $string->original, $skip_machine_translation ) ) { // do not return translation for href and src $dictionaries[ $language ][ $key ]->translated = ''; $dictionaries[ $language ][ $key ]->status = 0; } } } } return $dictionaries; } /** * Return array of original strings given their db ids. * * @param array $strings Strings object to extract original * @param array $original_array Original strings array to append to. * @param array $id_array Id array to extract. * @return array Original strings array + Extracted strings from ids. */ protected function extract_original_strings( $strings, $original_array, $id_array ){ if ( count( $strings ) > 0 ) { foreach ($id_array as $id) { if ( isset($strings[$id]) && is_object( $strings[$id] ) ){ $original_array[] = $strings[ $id ]->original; } } } return array_values( $original_array ); } /** * Save translations from ajax post. * * Hooked to wp_ajax_trp_save_translations_regular. */ public function save_translations(){ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { check_ajax_referer( 'save_translations', 'security' ); if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_save_translations_regular' && !empty( $_POST['strings'] ) ) { $strings = json_decode(stripslashes($_POST['strings'])); /* phpcs:ignore */ /* sanitized downstream */ $update_strings = $this->save_translations_of_strings( $strings ); } } echo trp_safe_json_encode( $update_strings ); // phpcs:ignore die(); } /** * Save translations in DB for the strings * * @param $strings * @param null $block_type */ protected function save_translations_of_strings( $strings, $block_type = null ){ if ( !$block_type ){ if (!$this->trp_query) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component('query'); } $block_type = $this->trp_query->get_constant_block_type_regular_string(); } $update_strings = array(); foreach ( $strings as $language => $language_strings ) { if ( in_array( $language, $this->settings['translation-languages'] ) && $language != $this->settings['default-language'] ) { $update_strings[ $language ] = array(); foreach( $language_strings as $string ) { if ( isset( $string->id ) && is_numeric( $string->id ) ) { if ( ! isset( $string->block_type ) ){ $string->block_type = $block_type; } // Use URL-safe sanitization for href translations to preserve percent-encoding $translated = $string->translated; if ( filter_var($translated, FILTER_VALIDATE_URL) ) { $translated = esc_url_raw( $translated ); } else { $translated = trp_sanitize_string( $translated ); } array_push($update_strings[ $language ], array( 'id' => (int)$string->id, 'original' => trp_sanitize_string( $string->original, false ), 'translated' => $translated, 'status' => (int)$string->status, 'block_type' => (int)$string->block_type )); } } } } if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component( 'query' ); } foreach( $update_strings as $language => $update_string_array ) { $this->trp_query->update_strings( $update_string_array, $language, array('id','translated', 'status', 'block_type')); $this->trp_query->remove_possible_duplicates($update_string_array, $language, 'regular'); } do_action('trp_save_editor_translations_regular_strings', $update_strings, $this->settings); return $update_strings; } /** * Set translation block to active. * * Creates TB is not exists. Adds auto translation if one is not provided. * Supports handling multiple translation blocks */ public function create_translation_block(){ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { check_ajax_referer( 'merge_translation_block', 'security' ); if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_create_translation_block' && !empty( $_POST['strings'] ) && !empty( $_POST['language'] ) && in_array( $_POST['language'], $this->settings['translation-languages'] ) && !empty( $_POST['original'] ) ) { $strings = json_decode( stripslashes( $_POST['strings'] ) ); /* phpcs:ignore */ /* sanitized downstream */ if ( isset ( $this->settings['translation-languages']) ){ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } if ( ! $this->translation_render ) { $this->translation_render = $trp->get_component( 'translation_render' ); } $active_block_type = $this->trp_query->get_constant_block_type_active(); foreach( $this->settings['translation-languages'] as $language ){ if ( $language != $this->settings['default-language'] ){ $dictionaries = $this->get_translation_for_strings( array(), array( stripslashes( $_POST['original'] ) ), $active_block_type, array() );/* phpcs:ignore */ /* sanitized downstream */ break; } } /* * Merging the dictionary received from get_translation_for_strings (which contains ID and possibly automatic translations) with * ajax translated (which can contain manual translations) */ $originals_array_constructed = false; $originals = array(); if ( isset( $dictionaries ) ){ foreach ( $dictionaries as $language => $dictionary ){ if ( $language == $this->settings['default-language'] ) continue; foreach( $dictionary as $dictionary_string_key => $dictionary_string ){ if ( !isset ($strings->$language) ){ continue; } $ajax_translated_string_list = $strings->$language; foreach( $ajax_translated_string_list as $ajax_key => $ajax_string ) { if ( trp_full_trim( trp_sanitize_string( $ajax_string->original, false ) ) == $dictionary_string->original ) { if ( $ajax_string->translated != '' ) { $dictionaries[ $language ][ $dictionary_string_key ]->translated = trp_sanitize_string( $ajax_string->translated ); $dictionaries[ $language ][ $dictionary_string_key ]->status = (int) $ajax_string->status; } $dictionaries[ $language ][ $dictionary_string_key ]->block_type = (int) $ajax_string->block_type; } $dictionaries[ $language ][ $dictionary_string_key ]->new_translation_block = true; } if( !$originals_array_constructed ){ $originals[] = $dictionary_string->original; } } $originals_array_constructed = true; } $this->save_translations_of_strings( $dictionaries, $active_block_type ); // update deactivated languages $copy_of_originals = $originals; if ( $originals_array_constructed ){ $table_names = $this->trp_query->get_all_table_names( $this->settings['default-language'], $this->settings['translation-languages'] ); if ( count( $table_names ) > 0 ){ foreach( $table_names as $table_name ) { $originals = $copy_of_originals; $language = $this->trp_query->get_language_code_from_table_name( $table_name ); $existing_dictionary = $this->trp_query->get_string_rows( array(), $originals, $language, ARRAY_A ); foreach ( $existing_dictionary as $string_key => $string ){ foreach ( $originals as $original_key => $original ){ if ( $string['original'] == $original ){ unset( $originals[$original_key] ); } } $existing_dictionary[$string_key]['block_type'] = $active_block_type; $originals = array_values( $originals ); } $this->trp_query->insert_strings( $originals, $language, $active_block_type ); $this->trp_query->update_strings( $existing_dictionary, $language ); } } } echo trp_safe_json_encode( $dictionaries );//phpcs:ignore } } } } die(); } /** * Set translation block to deprecated * * Can handle splitting multiple blocks. * * @return mixed|string|void */ public function split_translation_block() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { check_ajax_referer( 'split_translation_block', 'security' ); if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_split_translation_block' && ! empty( $_POST['strings'] ) ) { $raw_original_array = json_decode( stripslashes( $_POST['strings'] ) ); /* phpcs:ignore */ /* sanitized downstream */ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } $deprecated_block_type = $this->trp_query->get_constant_block_type_deprecated(); $originals = array(); foreach( $raw_original_array as $original ){ $originals[] = trp_sanitize_string( $original, false ); } // even inactive languages ( not in $this->settings['translation-languages'] array ) will be updated $all_languages_table_names = $this->trp_query->get_all_table_names( $this->settings['default-language'], array() ); $rows_affected = $this->trp_query->update_translation_blocks_by_original( $all_languages_table_names, $originals, $deprecated_block_type ); if ( $rows_affected == 0 ){ // do updates individually if it fails foreach ( $all_languages_table_names as $table_name ){ $this->trp_query->update_translation_blocks_by_original( array( $table_name ), $originals, $deprecated_block_type ); } } } } die(); } } includes/custom-language.php 0000777 00000020551 15251156640 0012175 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_image_size( 'trp-custom-language-flag', 18, 12 ); // Register country flag size for use in Add Media modal add_filter( 'image_size_names_choose', 'trp_add_flag_sizes' ); function trp_add_flag_sizes( $sizes ) { return array_merge( $sizes, array( 'trp-custom-language-flag' => __( 'Custom Language Flag', 'translatepress-multilingual' ) ) ); } add_filter( 'trp_wp_languages', 'trpc_add_custom_language', 10, 2 ); function trpc_add_custom_language( $languages ) { $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['custom_language'] ) ) { //print_r($option['custom_language']; foreach ( $option['custom_language']['cuslangname'] as $key => $value ) { if(isset($option["custom_language"]["cuslangcode"][ $key ])) { $lang = $option["custom_language"]["cuslangcode"][$key]; }else{ $lang = $option["custom_language"]["cuslangiso"][ $key ]; } $custom_language_iso = $option["custom_language"]["cuslangiso"][ $key ]; $custom_language_name = $option["custom_language"]["cuslangname"][ $key ]; $custom_language_native = $option["custom_language"]["cuslangnative"][ $key ]; if ( array_key_exists( $lang, $languages ) ) { if(empty( $custom_language_name )){ $custom_language_name = $languages[$lang]['english_name']; } if(empty( $custom_language_native )){ $custom_language_native = $languages[$lang]['native_name']; } if(empty( $custom_language_iso )){ $custom_language_iso = reset($languages[$lang]['iso']); } }else{ if( empty($custom_language_iso) && isset($option["custom_language"]["cuslangcode"][ $key ])){ $custom_language_iso = $option["custom_language"]["cuslangcode"][$key]; } } $languages[ $lang ] = array( 'language' => $lang, 'english_name' => $custom_language_name, 'native_name' => $custom_language_native, 'iso' => array( $custom_language_iso ), 'is_custom_language' => true ); global $TRP_LANGUAGE; if ( isset( $option["cuslangisrtl"] ) && $option["cuslangisrtl"] === 'yes' && $TRP_LANGUAGE === $custom_language_iso ) { $GLOBALS['text_direction'] = 'rtl'; } } } return $languages; } add_filter('gettext_with_context', 'trpc_language_rtl', 10, 4); function trpc_language_rtl($translated, $text, $context, $domain){ $option = get_option( 'trp_advanced_settings', true ); global $TRP_LANGUAGE; if ( isset( $option['custom_language'] ) ) { foreach ( $option['custom_language']['cuslangname'] as $key => $value ) { $custom_language_code = $option["custom_language"]["cuslangcode"][$key]; if($text == 'ltr' && $context == "text direction" && isset($option["custom_language"]["cuslangisrtl"][$key]) && $option["custom_language"]["cuslangisrtl"][$key] === 'yes' && $TRP_LANGUAGE === $custom_language_code){ $translated = 'rtl'; } } } return $translated; } add_filter( 'trp_flags_path', 'trpc_flags_path_custom', 10, 2 ); /** * @param $original_flags_path * @param $language_code * * @return mixed * * Returns the original flags path for original languages * Or the custom flag path for flags uploaded into the media library * The image is returned resized to the custom size dictated bu trp-custom-language-flag * */ function trpc_flags_path_custom( $original_flags_path, $language_code ) { // only change the folder path for the custom languages: $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['custom_language'] ) ) { foreach ( $option['custom_language']['cuslangname'] as $key => $value ) { if ($language_code === $option["custom_language"]["cuslangcode"][$key] && !empty($option["custom_language"]["cuslangflag"][$key]) ) { $attachment_array = wp_get_attachment_image_src(attachment_url_to_postid($option["custom_language"]["cuslangflag"][ $key ]), 'trp-custom-language-flag'); return isset($attachment_array) && $attachment_array ? $attachment_array[0] : $option["custom_language"]["cuslangflag"][ $key ]; } } } return $original_flags_path; } add_filter( 'trp_flag_file_name', 'trpc_flag_name_custom', 10, 2 ); /** * @param $original_flags_path * @param $language_code * * @return string * * For the custom languages the flag name is contained into the flag path * it does not follow the naming pattern language.png * So no need to return anything in that case */ function trpc_flag_name_custom ( $original_flags_path, $language_code ){ // only change flag name for the custom languages: $option = get_option( 'trp_advanced_settings', true ); if ( isset( $option['custom_language'] ) ) { foreach ( $option['custom_language']['cuslangname'] as $key => $value ) { if ($language_code === $option["custom_language"]["cuslangcode"][$key] && !empty($option["custom_language"]["cuslangflag"][$key])) { return ''; } } } return $original_flags_path; } add_filter('trp_saving_advanced_settings_is_successful', 'trp_add_messages_custom_language_codes', 10, 3); /** * The function verifies if the language codes and ISO codes written by the user contain only the allowed characters, A-Z a-z 0-9 _ - and if the language code is unique among other custom languages and existing languages. * * @param bool $is_correct_code retains if the language code and the ISO code are valid or not * @param $settings * @param $submitted_settings */ function trp_verify_custom_language_codes($is_correct_code, $settings){ if(isset($settings['custom_language']['cuslangcode'])) { foreach ($settings['custom_language']['cuslangcode'] as $key => $item) { if (!empty($settings['custom_language']['cuslangcode'][$key])) { if (!trp_is_valid_language_code($item)) { $is_correct_code = false; return array( 'message' => esc_html__('The Language code of the added custom language is invalid.','translatepress-multilingual'), 'correct_code' => $is_correct_code ); } }else{ $is_correct_code = false; return array( 'message' => esc_html__('The Language code of the added custom language cannot be empty.', 'translatepress-multilingual'), 'correct_code' => $is_correct_code ); } } } if(isset($settings['custom_language']['cuslangiso'])) { foreach ($settings['custom_language']['cuslangiso'] as $key => $item) { if(!empty($settings['custom_language']['cuslangiso'][$key])){ if (!trp_is_valid_language_code($item)) { $is_correct_code = false; return array( 'message' => esc_html__('The Automatic Translation Code of the added custom language is invalid.', 'translatepress-multilingual'), 'correct_code' => $is_correct_code ); } } } } return array( 'message' => '', 'correct_code' => $is_correct_code ); } function trp_add_messages_custom_language_codes($correct_code, $settings, $submitted_settings){ $correct_code_custom_language = trp_verify_custom_language_codes(true, $settings); if($correct_code_custom_language['correct_code'] === false){ /* phpcs:ignore */ add_settings_error( 'trp_advanced_settings', 'settings_error', esc_html($correct_code_custom_language['message']), 'error' ); $correct_code = false; return $correct_code; } return $correct_code; } add_filter('trp_extra_sanitize_advanced_settings', 'trp_save_settings_language', 10, 3); /** * The custom language is saved only if the codes are correct. * @param $settings * @param $submitted_settings * @param $prev_settings * @return mixed */ function trp_save_settings_language($settings, $submitted_settings, $prev_settings){ $correct_custom_languagea_code = trp_verify_custom_language_codes(true, $settings); if($correct_custom_languagea_code['correct_code'] === false) { $settings['custom_language'] = $prev_settings['custom_language']; } return $settings; } includes/class-advanced-tab.php 0000777 00000131200 15251156640 0012510 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Advanced_Tab { private $settings; public function __construct($settings) { $this->settings = $settings; } /* * Add new tab to TP settings * * Hooked to trp_settings_tabs */ public function add_advanced_tab_to_settings( $tab_array ){ $tab_array[] = array( 'name' => __( 'Advanced', 'translatepress-multilingual' ), 'url' => admin_url( 'admin.php?page=trp_advanced_page' ), 'page' => 'trp_advanced_page' ); return $tab_array; } /* * Add submenu for advanced page tab * * Hooked to admin_menu */ public function add_submenu_page_advanced() { add_submenu_page( 'TRPHidden', 'TranslatePress Advanced Settings', 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_advanced_page', array( $this, 'advanced_page_content' ) ); } /** * Register setting * * Hooked to admin_init */ public function register_setting(){ register_setting( 'trp_advanced_settings', 'trp_advanced_settings', array( $this, 'sanitize_settings' ) ); } /** * Output admin notices after saving settings. */ public function admin_notices(){ settings_errors( 'trp_advanced_settings' ); } /** * Sanitize settings */ public function sanitize_settings( $submitted_settings ){ $array_possible_settings_for_tab = apply_filters('trp_possible_values_for_tab', array('ald_settings', 'troubleshooting', 'exclude_strings', 'debug', 'miscellaneous_options', 'custom_language')); if (isset($_REQUEST['tab']) && in_array($_REQUEST['tab'], $array_possible_settings_for_tab)){ $_REQUEST['_wp_http_referer'] = add_query_arg( 'tab', $_REQUEST['tab'], $_REQUEST['_wp_http_referer'] );//phpcs:ignore } $registered_settings = $this->get_registered_advanced_settings(); $prev_settings = get_option('trp_advanced_settings', array()); $settings = array(); foreach ( $registered_settings as $registered_setting ){ /* All advanced options are set to false and then maybe set to a default value below if a particular * advanced option is not set in array $submitted_settings * Form submitted checkboxes are never set, so this is especially useful */ if( !isset( $submitted_settings[$registered_setting['name']] ) ){ $submitted_settings[$registered_setting['name']] = false; } if ( isset( $submitted_settings[$registered_setting['name']] ) ){ switch ($registered_setting['type'] ) { case 'checkbox': { $settings[ $registered_setting['name'] ] = ( $submitted_settings[ $registered_setting['name'] ] === 'yes' ) ? 'yes' : 'no'; break; } case 'select': { if ( isset( $registered_setting['options'] ) && isset( $registered_setting['options'][ $submitted_settings[ $registered_setting['name'] ] ] ) ) { $settings[ $registered_setting['name'] ] = $submitted_settings[ $registered_setting['name'] ]; } else { $settings[ $registered_setting['name'] ] = ( empty( $registered_setting['default'] ) ) ? false : $registered_setting['default']; } break; } case 'input': { $settings[ $registered_setting['name'] ] = sanitize_text_field($submitted_settings[ $registered_setting['name'] ]); break; } case 'radio': { if ( isset( $registered_setting['options'] ) && in_array( $submitted_settings[ $registered_setting['name'] ], $registered_setting['options'] ) ){ $settings[ $registered_setting['name'] ] = $submitted_settings[ $registered_setting['name'] ]; }else{ $settings[ $registered_setting['name'] ] = ( empty($registered_setting['default'] ) )? false : $registered_setting['default']; } break; } case 'custom': { if ( isset( $registered_setting['rows'] ) ) { foreach ( $registered_setting['rows'] as $row_label => $row_type ) { if ( isset( $submitted_settings[ $registered_setting['name'] ][ $row_label ] ) ) { if ( $row_type != 'textarea' ) $value = sanitize_text_field( $submitted_settings[ $registered_setting['name'] ][ $row_label ] ); else $value = sanitize_textarea_field( $submitted_settings[ $registered_setting['name'] ][ $row_label ] ); $settings[ $registered_setting['name'] ][ $row_label ] = $value; } } } if ( $registered_setting['name'] === 'enable_hreflang_xdefault' ){ $select_key = $registered_setting['name']; $checkbox_key = $registered_setting['name'] . '-checkbox'; $is_checkbox_disabled = $submitted_settings[$select_key] === false ; $select_value = $is_checkbox_disabled ? 'disabled' : $submitted_settings[$select_key]; $checkbox_value = $is_checkbox_disabled ? 'no' : $submitted_settings[$checkbox_key]; $settings[ $select_key ] = sanitize_text_field( $select_value ); $settings[ $checkbox_key ] = sanitize_text_field( $checkbox_value ); } break; } case 'input_array': { $formats_array_key = $registered_setting['name']; $checkbox_key = $registered_setting['name'] . '-checkbox'; foreach ( $registered_setting['rows'] as $row_label => $row_name ) { if (isset($submitted_settings[$formats_array_key][$row_label])) { $settings[$formats_array_key][$row_label] = sanitize_text_field( $submitted_settings[$formats_array_key][$row_label] ); } } $checkbox_value = isset( $submitted_settings[$checkbox_key] ) && $submitted_settings[$checkbox_key] !== false ? $submitted_settings[$checkbox_key] : 'no'; $settings[$checkbox_key] = sanitize_text_field( $checkbox_value ); break; } case 'number': { $settings[ $registered_setting['name'] ] = sanitize_text_field(intval($submitted_settings[ $registered_setting['name'] ] ) ); break; } case 'list': case 'list_input': case 'mixed': /* We use the same parsing and saving mechanism for list and mixed advanced types. */ { $settings[ $registered_setting['name'] ] = array(); $one_column = ''; foreach ( $registered_setting['columns'] as $column => $column_name ) { $one_column = ( empty ( $one_column ) && !(is_array($column_name) && $column_name ['type'] === 'checkbox') ) ? $column : $one_column; $settings[ $registered_setting['name'] ][ $column ] = array(); if ( isset($submitted_settings[ $registered_setting['name'] ][ $column ] ) ) { foreach ($submitted_settings[$registered_setting['name']][$column] as $key => $value) { $settings[$registered_setting['name']][$column][] = sanitize_text_field($value); } } } /* If the setting is a type "checkbox" we remove one empty value from the sub-array if it comes after a 'yes' value In this case we properly save an empty value for an unchecked checkbox and also control the display checked/unchecked on the frontend */ foreach ( $registered_setting['columns'] as $column => $column_name ) { if (is_array($column_name) && $column_name ['type'] === 'checkbox'){ foreach ($settings[ $registered_setting['name'] ] [$column] as $submitted_key => $submitted_value) { if ( $submitted_value === 'yes' ) { unset ( $settings[ $registered_setting['name'] ] [ $column ] [ $submitted_key + 1 ] ); } // Check for illegal values at checkbox side if ( !$submitted_value === 'yes' || !$submitted_value === '' ) { $settings[ $registered_setting['name'] ] [ $column ] [$submitted_key] = ''; } } } } // remove empty rows except checkboxes foreach ( $settings[ $registered_setting['name'] ][ $one_column ] as $key => $value ) { $is_empty = true; foreach ( $registered_setting['columns'] as $column => $column_name ) { if ( $settings[ $registered_setting['name'] ][$column][$key] != "" || ( is_array($column_name) && $column_name ['type'] === 'checkbox') ) { $is_empty = false; break; } } if ( $is_empty ){ foreach ( $registered_setting['columns'] as $column => $column_name ) { unset( $settings[ $registered_setting['name'] ][$column][$key] ); } } } foreach ( $settings[ $registered_setting['name'] ] as $column => $value ) { $settings[ $registered_setting['name'] ][ $column ] = array_values( $settings[ $registered_setting['name'] ][ $column ] ); } break; } } } //endif // not all settings are updated by the user. Some are modified by the program and used as storage. // This is somewhat bad from a data model kind of way, but it's easy to pass the $settings variable around between classes. if( isset($registered_setting['data_model']) && $registered_setting['data_model'] == 'not_updatable_by_user' && isset($prev_settings[$registered_setting['name']]) ) { $settings[ $registered_setting['name'] ] = $prev_settings[$registered_setting['name']]; } } //end foreach of parsing all the registered settings array if ( apply_filters( 'trp_saving_advanced_settings_is_successful', true, $settings, $submitted_settings ) ) { add_settings_error( 'trp_advanced_settings', 'settings_updated', esc_html__( 'Settings saved.', 'translatepress-multilingual' ), 'updated' ); } return apply_filters( 'trp_extra_sanitize_advanced_settings', $settings, $submitted_settings, $prev_settings ); } /* * Advanced page content */ public function get_registered_advanced_settings() { // Pull everything registered by add-ons / filters $settings = apply_filters( 'trp_register_advanced_settings', array() ); $trp = TRP_Translate_Press::get_trp_instance(); $ls_tab = is_object( $trp ) ? $trp->get_component( 'language_switcher_tab' ) : null; $legacy_enabled = $ls_tab->is_legacy_enabled(); // Filter out all settings that belong to the "language_switcher" container if legacy language switcher is disabled if ( !$legacy_enabled ) { $settings = array_values( array_filter( $settings, static function ( $item ) { if ( !is_array( $item ) || !isset( $item['container'] ) ) { return true; } // Drop anything grouped under the language_switcher container // (container titles, elements, separators, etc.). return ( $item['container'] !== 'language_switcher' ); } ) ); } return $settings; } /* * Require the custom codes from the specified folder */ public function advanced_page_content(){ require_once TRP_PLUGIN_DIR . 'partials/advanced-settings-page.php'; } /* * Get array of registered options from custom code to display in Advanced Settings page */ public function include_custom_codes(){ include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/disable-dynamic-translation.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/force-slash-at-end-of-links.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/enable-numerals-translation.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/custom-date-format.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/custom-language.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/exclude-dynamic-selectors.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/exclude-gettext-strings.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/exclude-selectors.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/exclude-selectors-automatic-translation.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/fix-broken-html.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/show-dynamic-content-before-translation.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/enable-hreflang-xdefault.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/strip-gettext-post-content.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/strip-gettext-post-meta.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/exclude-words-from-auto-translate.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/disable-post-container-tags.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/separators.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/disable-languages-sitemap.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/remove-duplicates-from-db.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/do-not-translate-certain-paths.php'); include_once (TRP_PLUGIN_DIR . 'includes/advanced-settings/opposite-flag-shortcode.php'); include_once (TRP_PLUGIN_DIR . 'includes/advanced-settings/open-language-switcher-shortcode-on-click.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/hreflang-remove-locale.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/html-lang-remove-locale.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/serve-similar-translation.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/disable-gettext-strings.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/manual-translation-only.php'); //we can remove this at some point include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/load-legacy-seo-pack.php'); include_once(TRP_PLUGIN_DIR . 'includes/advanced-settings/load-legacy-language-switcher.php'); } /* * Hooked to trp_before_output_advanced_settings_options */ function trp_advanced_settings_content_table(){ $advanced_settings_array = $this->get_registered_advanced_settings(); $html = '<div class="trp_advanced_tab_content_table__wrapper"><div id="trp_advanced_tab_content_table">'; $advanced_settings_array = apply_filters( 'trp_advanced_tab_add_element', $advanced_settings_array ); $advanced_settings_array = apply_filters('trp_advanced_tab_add_element', $advanced_settings_array); $first_item = ''; $other_items = ''; foreach ($advanced_settings_array as $setting) { if ($setting['type'] === 'separator') { $tab_html = '<span class="trp_advanced_tab_content_table_item"> <a href="#' . esc_html($setting['id']) . '" class="' . esc_html($setting['id']) . '"> ' . esc_html($setting['label']) . ' </a> </span>'; if ($setting['name'] === 'automatic_user_language_detection') { $first_item = $tab_html; // Store this to add it first } else { $other_items .= $tab_html; // Collect other separators } } } $html .= $first_item . $other_items; $html .= '</div></div>'; echo $html;//phpcs:ignore } /* * Hooked to trp_settings_navigation_tabs */ public function output_advanced_options() { echo "<input type='hidden' name='tab' id='trp_advanced_settings_referer'>"; // phpcs:ignore $advanced_settings_array = $this->get_registered_advanced_settings(); $grouped_settings = []; // Step 1: Group settings by ID foreach ( $advanced_settings_array as $setting ) { if ( !isset( $setting['container'] ) ) continue; $array_key = $setting['type'] === 'container_title' ? 'container_title' : 'container_elements'; $grouped_settings[$setting['container']][$array_key][] = $setting; } // Step 2: Loop through each group and output settings within a container foreach ( $grouped_settings as $id => $settings ) { $container_id = $settings['container_elements'][0]['id']; echo "<div class='trp-settings-container trp-settings-container-" . esc_attr($container_id) . "'>"; echo $this->container_title_setting( $settings['container_title'][0] ); //phpcs:ignore echo "<div class='trp-settings-options__wrapper'>"; foreach ( $settings['container_elements'] as $setting ) { switch ( $setting['type'] ) { case 'checkbox': echo $this->checkbox_setting($setting); // phpcs:ignore break; case 'radio': echo $this->radio_setting($setting); // phpcs:ignore break; case 'input': echo $this->input_setting($setting); // phpcs:ignore break; case 'number': echo $this->input_setting($setting, 'number'); // phpcs:ignore break; case 'input_array': echo $this->input_array_setting($setting); // phpcs:ignore break; case 'select': echo $this->select_setting($setting); // phpcs:ignore break; case 'list': echo $this->add_to_list_setting($setting); // phpcs:ignore break; case 'list_input': echo $this->add_to_list_input_setting($setting); // phpcs:ignore break; case 'text': echo $this->text_setting($setting); // phpcs:ignore break; case 'mixed': echo $this->mixed_setting($setting); // phpcs:ignore break; case 'custom': echo $this->custom_setting($setting); // phpcs:ignore break; } } echo "</div>"; // Close options wrapper echo "</div>"; // Close container for this group } } /** * Return HTML of a checkbox type setting * * @param $setting * * @return 'string' */ public function checkbox_setting( $setting ) { $adv_option = $this->settings['trp_advanced_settings']; $checked = ( isset( $adv_option[ $setting['name'] ] ) && $adv_option[ $setting['name'] ] === 'yes' ) ? 'checked' : ''; $html = "<div class='trp-settings-checkbox trp-settings-options-item'> <input type='checkbox' id='" . esc_attr( $setting['name'] ) . "' name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "]' value='yes' " . $checked . " /> <label for='" . esc_attr( $setting['name'] ) . "' class='trp-checkbox-label'> <div class='trp-checkbox-content'> <span class='trp-primary-text-bold'>" . esc_html( $setting['label'] ) . "</span> <span class='trp-description-text'>" . wp_kses_post( $setting['description'] ) . "</span> </div> </label> </div>"; return apply_filters( 'trp_advanced_setting_checkbox', $html ); } /** * Return HTML of a radio button type setting * * @param $setting * * @return 'string' */ public function radio_setting( $setting ){ $adv_option = $this->settings['trp_advanced_settings']; $html = "<div class='trp-radio__wrapper trp-settings-options-item'> <span class='trp-primary-text-bold'>" . esc_html($setting['label'] ) . "</span> <div class='trp-adst-radio trp-radio__wrapper'>"; foreach($setting[ 'options' ] as $key => $option ){ if( isset( $adv_option[ $setting['name'] ] ) && !empty( $adv_option[ $setting['name'] ] ) ){ if( $adv_option[ $setting['name'] ] === $option ){ $checked = 'checked="checked"'; } else{ $checked = ''; } } else{ if( $setting['default'] === $option ){ $checked = 'checked="checked"'; } else{ $checked = ''; } } $setting_name = $setting['name']; $label = $setting[ 'labels' ][$key]; $html .= "<label class='trp-primary-text'> <input type='radio' id='". esc_attr( $setting_name ) . "' name='trp_advanced_settings[". esc_attr( $setting_name ) ."]' value='". esc_attr( $option ) ."' $checked > ". esc_html( $label ) ." </label>"; } $html .= "</div> <span class='trp-description-text'> " . wp_kses_post( $setting['description'] ). " </span> </div>"; return apply_filters('trp_advanced_setting_radio', $html ); } /** * Return HTML of a input type setting * * @param array $setting * @param string $type * * @return 'string' */ public function input_setting( $setting, $type = 'text'){ $adv_option = $this->settings['trp_advanced_settings']; $default = ( isset( $setting['default'] )) ? $setting['default'] : ''; $value = ( isset( $adv_option[ $setting['name'] ] ) ) ? $adv_option[ $setting['name'] ] : $default; $html = " <div class='trp_advanced_flex_box'> <div class='trp_advanced_option_name'>" . esc_html( $setting['label'] ). "</div> <div class='trp_advanced_settings_align'> <label> <input type='" . esc_attr( $type ) ."' id='" . esc_attr( $setting['name'] ) ."' name='trp_advanced_settings[" .esc_attr( $setting['name'] )."]' value='" . esc_attr( $value ) ."'> </label> <p class='description'> ". wp_kses_post( $setting['description'] ) . " </p> </div> </div>"; return apply_filters('trp_advanced_setting_input', $html ); } /** * Return HTML of an array type setting * * @param $setting * @param string $type * * @return 'string' */ public function input_array_setting ($setting, $type = 'text'){ $adv_option = $this->settings['trp_advanced_settings']; $default = ( isset( $setting['default'] )) ? $setting['default'] : ''; $checked = ( isset( $adv_option[ $setting['name'] . '-checkbox' ] ) && $adv_option[ $setting['name'] . '-checkbox' ] === 'yes' ) || !empty( $adv_option[ $setting['name'] ] ) ? 'checked' : ''; $input_rows = '<div class="trp-input-array-rows__wrapper">'; foreach ($setting['rows'] as $row_label=>$row_name ){ $value = ( isset( $adv_option[ $setting['name'] ][$row_label] ) ) ? $adv_option[ $setting['name'] ][$row_label] : $default; $input_rows.= "<div class='trp-input-array-setting-row'> <label class='trp-primary-text' for='". esc_attr( $setting['name'] ) ."-".esc_attr( $row_label ) ."'> ".esc_attr( $row_name )." </label> <input type='text' id='". esc_attr( $setting['name'] ) ."-". esc_attr( $row_label ) ."' name='trp_advanced_settings[". esc_attr( $setting['name'] )."][". esc_attr( $row_label )."]' value='".esc_attr( $value )."'> </div>"; } $input_rows.= "</div>"; $html = "<div class='trp-settings-custom-checkbox__wrapper'> <div class='trp-settings-checkbox'> <input type='checkbox' id='" . esc_attr( $setting['name'] ) . "' name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "-checkbox]' value='yes' " . $checked . " /> <label for='" . esc_attr( $setting['name'] ) . "' class='trp-checkbox-label'> <div class='trp-checkbox-content'> <span class='trp-primary-text-bold'>" . esc_html( $setting['label'] ) . "</span> <span class='trp-description-text'>" . wp_kses_post( $setting['description'] ) . "</span> </div> </label> </div> $input_rows </div>"; return apply_filters('trp_advanced_setting_input_array', $html ); } /** * Return HTML of an input type setting * * @param array $setting * @param string $type * * @return 'string' */ public function select_setting( $setting ){ $option = get_option('trp_advanced_settings', true ); $default = ( isset( $setting['default'] )) ? $setting['default'] : ''; $value = ( isset( $option[ $setting['name'] ] ) ) ? $option[ $setting['name'] ] : $default; $options = ''; foreach ($setting['options'] as $lang => $label) { ($value == $lang) ? $selected = 'selected' : $selected = '' ; $options .= "<option value='". esc_attr( $lang ) ."' $selected>". esc_html( $label )."</option>"; } $html = " <div class='trp_advanced_flex_box'> <div class='trp_advanced_option_name'>" . esc_html( $setting['label'] ) ."</div> <div class='trp_advanced_settings_align'> <label> <select id='".esc_attr( $setting['name'] ) ."' name='trp_advanced_settings[". esc_attr( $setting['name'] ) ."]' style='width: 225px;'> ". $options ." </select> </label> <p class='description'> ". wp_kses_post( $setting['description'] ) ." </p> </div> </div>"; return apply_filters('trp_advanced_setting_select', $html ); } /** * Return HTML of a container title type setting * * @param $setting * * @return 'string' */ public function container_title_setting( $setting ){ $html = "<div class='trp-settings-container-title__wrapper'> <h2 class='trp-settings-primary-heading'>" . esc_html( $setting['label'] ) . "</h2> <div class='trp-settings-separator'></div> </div>"; return apply_filters('trp_advanced_setting_separator', $html ); } /** * Return HTML of a checkbox type setting * * @param $setting * * @return 'string' */ public function add_to_list_setting( $setting ) { $adv_option = $this->settings['trp_advanced_settings']; $remove_element = "<div class='trp-remove-language__container trp-adst-remove-element'> <span class='trp-adst-remove-element-text' data-confirm-message='" . esc_html__('Are you sure you want to remove this item?', 'translatepress-multilingual') . "'>" . esc_html__( 'Remove', 'translatepress-multilingual' ) . "</span> <svg width='20' height='21' viewBox='0 0 20 21' fill='none' xmlns='http://www.w3.org/2000/svg'> <path fill-rule='evenodd' clip-rule='evenodd' d='M12 4.5H15C15.6 4.5 16 4.9 16 5.5V6.5H3V5.5C3 4.9 3.5 4.5 4 4.5H7C7.2 3.4 8.3 2.5 9.5 2.5C10.7 2.5 11.8 3.4 12 4.5ZM11 4.5C10.8 3.9 10.1 3.5 9.5 3.5C8.9 3.5 8.2 3.9 8 4.5H11ZM14.1 17.6L15 7.5H4L4.9 17.6C5 18.1 5.4 18.5 5.9 18.5H13.1C13.6 18.5 14.1 18.1 14.1 17.6Z' fill='#757575'/> </svg> </div>"; $html = " <span class='trp-description-text'>" . wp_kses_post( $setting['description'] ) . "</span> <table class='trp-adst-list-option'> <thead class='trp-add-to-input-setting-columns'> <tr>"; foreach( $setting['columns'] as $key => $value ){ $html .= '<th><span class="trp-primary-text-bold">' . esc_html( $value ) . '</span></th>'; } $html .= "</tr> </thead>"; $first_column = key($setting['columns']); $html .= "<tbody>"; // Existing Entries if ( isset( $adv_option[ $setting['name'] ] ) && is_array( $adv_option[ $setting['name'] ] ) ) { foreach ( $adv_option[ $setting['name'] ][ $first_column ] as $index => $value ) { $html .= "<tr class='trp-list-entry'>"; foreach ( $setting['columns'] as $column => $column_name ) { $column_value = isset($adv_option[ $setting['name'] ][ $column ][ $index ]) ? esc_attr($adv_option[ $setting['name'] ][ $column ][ $index ]) : ''; $html .= "<td><input type='text' name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "][" . esc_attr( $column ) . "][]' value='" . $column_value . "'></td>"; } $html .= "<td>$remove_element</td>"; $html .= "</tr>"; } } // Add New Entry Row $html .= "<tr class='trp-add-list-entry trp-list-entry'>"; foreach( $setting['columns'] as $column => $column_name ) { $html .= "<td class='trp-add-list-entry-input-col'><input type='text' id='new_entry_" . esc_attr( $setting['name'] ) . "_" . esc_attr( $column ) . "' data-name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "][" . esc_attr( $column ) . "][]' data-setting-name='" . esc_attr( $setting['name'] ) . "' data-column-name='" . esc_attr( $column ) . "'></td>"; } $html .= "<td class='trp-add-list-entry-btn-col'> <input type='button' class='trp-button-secondary trp-adst-button-add-new-item' value='" . esc_html__( 'Add', 'translatepress-multilingual' ) . "'> <div style='display: none;'>$remove_element</div> </td>"; $html .= "</tr></tbody></table>"; return apply_filters( 'trp_advanced_setting_list', $html ); } /** * Return HTML of input type list * * @param $setting * * @return 'string' */ public function add_to_list_input_setting( $setting ){ $adv_option = $this->settings['trp_advanced_settings']; $remove_element = "<div class='trp-remove-language__container trp-adst-remove-element'> <span class='trp-adst-remove-element-text' data-confirm-message='" . esc_html__('Are you sure you want to remove this item?', 'translatepress-multilingual') . "'>" . esc_html__( 'Remove', 'translatepress-multilingual' ) . "</span> <svg width='20' height='21' viewBox='0 0 20 21' fill='none' xmlns='http://www.w3.org/2000/svg'> <path fill-rule='evenodd' clip-rule='evenodd' d='M12 4.5H15C15.6 4.5 16 4.9 16 5.5V6.5H3V5.5C3 4.9 3.5 4.5 4 4.5H7C7.2 3.4 8.3 2.5 9.5 2.5C10.7 2.5 11.8 3.4 12 4.5ZM11 4.5C10.8 3.9 10.1 3.5 9.5 3.5C8.9 3.5 8.2 3.9 8 4.5H11ZM14.1 17.6L15 7.5H4L4.9 17.6C5 18.1 5.4 18.5 5.9 18.5H13.1C13.6 18.5 14.1 18.1 14.1 17.6Z' fill='#757575'/> </svg> </div>"; $html = " <span class='trp-description-text'> " . wp_kses_post( $setting['description'] ) . " </span> <table class='trp-adst-list-option'> <thead class='trp-add-to-input-setting-columns'><tr>"; foreach( $setting['columns'] as $key => $value ){ $html .= '<th><span class="trp-primary-text-bold">' . esc_html( $value ) . '</span></th>'; } $html .= "</tr></thead>"; $first_column = ''; foreach( $setting['columns'] as $column => $column_name ) { $first_column = $column; break; } $html .= '<tbody>'; if ( isset( $adv_option[ $setting['name'] ] ) && is_array( $adv_option[ $setting['name'] ] ) ) { foreach ( $adv_option[ $setting['name'] ][ $first_column ] as $index => $value ) { $html .= "<tr class='trp-list-entry' id='trp-add-to-input-setting-div-entry'>"; foreach ( $setting['columns'] as $column => $column_name ) { $html .= "<td><input type='text' name='trp_advanced_settings[" . esc_attr( $setting['name'] ). "][" . esc_attr( $column ) . "][]' value='". htmlspecialchars($adv_option[ $setting['name'] ][ $column ][ $index ], ENT_QUOTES) ."'></td>"; } $html .= "<td>$remove_element</td>"; $html .= "</tr>"; } } // add new entry to list $html .= "<tr class='trp-add-list-entry trp-list-entry'>"; foreach( $setting['columns'] as $column => $column_name ) { $html .= "<td class='trp-add-list-entry-input-col'><input type='text' id='new_entry_" . esc_attr( $setting['name'] ) . "_" . esc_attr( $column ) . "' data-name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "][" . esc_attr( $column ) . "][]' data-setting-name='" . esc_attr( $setting['name'] ) . "' data-column-name='" . esc_attr( $column ) . "'></td>"; } $html .= "<td class='trp-add-list-entry-btn-col'><input type='button' class='trp-button-secondary trp-adst-button-add-new-item' value='" . esc_html__( 'Add', 'translatepress-multilingual' ) . "'> <div style='display: none;'>$remove_element</div> </td>"; $html .= "</tr></tbody></table>"; return apply_filters( 'trp_advanced_setting_list', $html ); } /** * Return HTML of a text type setting * * @param $setting * * @return 'string' */ public function text_setting( $setting ){ $html = "<div class='trp-settings-options-item trp-settings-options-item__column trp-settings-options-item__nocheckbox'> <div class='trp-primary-text-bold'>" . esc_html( $setting['label'] ) . "</div> <span class='trp-description-text'> " . wp_kses_post( $setting['description'] ) . " </span> </div>"; return apply_filters('trp_advanced_setting_text', $html ); } public function mixed_setting($setting) { $adv_option = $this->settings['trp_advanced_settings']; $remove_element = "<div class='trp-remove-language__container trp-adst-remove-element'> <span class='trp-adst-remove-element-text' data-confirm-message='" . esc_html__('Are you sure you want to remove this item?', 'translatepress-multilingual') . "'>" . esc_html__( 'Remove', 'translatepress-multilingual' ) . "</span> <svg width='20' height='21' viewBox='0 0 20 21' fill='none' xmlns='http://www.w3.org/2000/svg'> <path fill-rule='evenodd' clip-rule='evenodd' d='M12 4.5H15C15.6 4.5 16 4.9 16 5.5V6.5H3V5.5C3 4.9 3.5 4.5 4 4.5H7C7.2 3.4 8.3 2.5 9.5 2.5C10.7 2.5 11.8 3.4 12 4.5ZM11 4.5C10.8 3.9 10.1 3.5 9.5 3.5C8.9 3.5 8.2 3.9 8 4.5H11ZM14.1 17.6L15 7.5H4L4.9 17.6C5 18.1 5.4 18.5 5.9 18.5H13.1C13.6 18.5 14.1 18.1 14.1 17.6Z' fill='#757575'/> </svg> </div>"; $html = "<span class='trp-description-text'>" . wp_kses_post($setting['first_description']) . "</span>"; $html .= "<table id='trp-cuslang-table' class='trp-adst-list-option'> <thead class='trp-add-to-input-setting-columns'>"; // Column headers foreach ( $setting['columns'] as $option_name => $option_details ) { if ( !empty($option_details['required'] ) ) { $html .= "<th class='trp_lang_code'><span class='trp-primary-text-bold'>" . esc_html($option_details['label']) . " <span title='Required'>*</span></span></th>"; } else { $html .= "<th><span class='trp-primary-text-bold'>" . esc_html($option_details['label']) . "</span></th>"; } } $html .= "<th></th></thead>"; $first_column = key($setting['columns']); // Existing entries if ( !empty( $adv_option[$setting['name']] ) && is_array( $adv_option[$setting['name']] ) ) { foreach ( $adv_option[$setting['name']][$first_column] as $index => $value ) { $html .= "<tr class='trp-list-entry'>"; foreach ( $setting['columns'] as $option_name => $option_details ) { $option_value = $adv_option[$setting['name']][$option_name][$index] ?? ''; switch ($option_details['type']) { case 'text': $html .= "<td class='trp-col-" . esc_attr($option_name) . "'> <input class='trp_narrow_input' type='text' name='trp_advanced_settings[" . esc_attr($setting['name']) . "][" . esc_attr($option_name) . "][]' value='" . esc_attr($option_value) . "'> </td>"; break; case 'textarea': $html .= "<td> <textarea class='trp_narrow_input' name='trp_advanced_settings[" . esc_attr($setting['name']) . "][" . esc_attr($option_name) . "][]'>" . esc_textarea($option_value) . "</textarea> </td>"; break; case 'select': $html .= "<td> <select class='trp-select-advanced' name='trp_advanced_settings[" . esc_attr($setting['name']) . "][" . esc_attr($option_name) . "][]'> <option value=''>" . esc_html__('Select...', 'translatepress-multilingual') . "</option>"; foreach ($option_details["values"] as $select_value) { $selected = ($option_value === $select_value) ? "selected='selected'" : ''; $html .= "<option value='" . esc_attr($select_value) . "' $selected>" . esc_html($select_value) . "</option>"; } $html .= "</select></td>"; break; case 'checkbox': $checked = ($option_value === 'yes') ? "checked='checked'" : ''; $html .= "<td> <div class='trp-settings-checkbox trp-settings-options-item'> <input type='checkbox' id='" . esc_attr($setting['name']) . "_" . esc_attr($option_name) . "_$index' name='trp_advanced_settings[" . esc_attr($setting['name']) . "][" . esc_attr($option_name) . "][]' value='yes' $checked /> </div> </td>"; break; } } $html .= "<td>$remove_element</td>"; $html .= "</tr>"; } } // Add new entry to list; renders the last row which is initially empty. $html .= "<tr class='trp-add-list-entry trp-list-entry'>"; foreach ( $setting['columns'] as $option_name => $option_details ) { switch ($option_details['type']) { case 'text': $html .= "<td class='trp-col-" . esc_attr($option_name) . "'> <input type='text' class='trp_narrow_input' id='new_entry_" . esc_attr($setting['name']) . "_" . esc_attr($option_name) . "' placeholder='" . esc_attr($option_details['placeholder'] ?? '') . "' data-name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "][" . esc_attr( $option_name ) . "][]' data-setting-name='" . esc_attr( $setting['name'] ) . "' data-column-name='" . esc_attr( $option_name ) . "'> </td>"; break; case 'textarea': $html .= "<td><textarea class='trp_narrow_input' id='new_entry_" . esc_attr($setting['name']) . "_" . esc_attr($option_name) . "' data-name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "][" . esc_attr( $option_name ) . "][]' data-setting-name='" . esc_attr( $setting['name'] ) . "' data-column-name='" . esc_attr( $option_name ) . "'></textarea></td>"; break; case 'select': $html .= "<td> <select id='new_entry_" . esc_attr($setting['name']) . "_" . esc_attr($option_name) . "' data-name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "][" . esc_attr( $option_name ) . "][]' data-setting-name='" . esc_attr( $setting['name'] ) . "' data-column-name='" . esc_attr( $option_name ) . "'> <option value=''>" . esc_html__('Select...', 'translatepress-multilingual') . "</option>"; foreach ($option_details["values"] as $select_value) { $html .= "<option value='" . esc_attr($select_value) . "'>" . esc_html($select_value) . "</option>"; } $html .= "</select></td>"; break; case 'checkbox': $html .= "<td> <div class='trp-settings-checkbox trp-settings-options-item'> <input type='checkbox' id='new_entry_" . esc_attr($setting['name']) . "_" . esc_attr($option_name) . "' value='yes' data-name='trp_advanced_settings[" . esc_attr( $setting['name'] ) . "][" . esc_attr( $option_name ) . "][]' data-setting-name='" . esc_attr( $setting['name'] ) . "' data-column-name='" . esc_attr( $option_name ) . "'> </div> </td>"; break; } } $html .= "<td class='trp-col-add-new'> <input type='button' class='trp-button-secondary trp-adst-button-add-new-item' value='" . esc_html__('Add', 'translatepress-multilingual') . "'> <div style='display: none;'>$remove_element</div> </td>"; $html .= "</tr></table>"; $html .= "<span class='trp-description-text'>" . wp_kses_post($setting['second_description']) . "</span>"; return apply_filters('trp_advanced_setting_list', $html); } /** * Can be used to output content outside the very static methods from above * Hook to the provided filter * */ public function custom_setting( $setting ){ if( empty( $setting['name'] ) ) return; return apply_filters( 'trp_advanced_setting_custom_' . $setting['name'], $setting ); } } includes/class-uri.php 0000777 00000021077 15251156640 0011010 0 ustar 00 <?php namespace TranslatePress; if ( !defined('ABSPATH' ) ) exit(); class Uri { const SCHEMES_WITH_AUTHORITY = ';http;https;ftp'; /** @var string */ private $scheme; /** @var string */ private $host; /** @var string */ private $user; /** @var string */ private $pass; /** @var string */ private $path; /** @var string */ private $query; /** @var string */ private $fragment; /** @var int */ private $port; /** @var bool */ private $absolute = true; /** * If $uri is set, we'll hydrate this object with it * * @param string $uri {optional} */ public function __construct($uri = null) { if ($uri !== null) { $this->fromString($uri); } } /** * Alias for getUri. * @return string */ public function __toString() { return $this->getUri(); } /** * Hydrate this object with values from a string * @param $uri * @return self */ public function fromString($uri) { if (is_numeric($uri)) { //Could be a valid url $uri = '' . $uri; } if (!is_string($uri)) { $uri = ''; } $this->setRelative(); if (0 === strpos($uri, '//')) { $this->setAbsolute(); } $parsed_url = parse_url($uri); if (!$parsed_url) { return $this; } if (array_key_exists('scheme', $parsed_url)) { $this->setAbsolute(); } foreach ($parsed_url as $urlPart => $value) { $method = 'set' . ucfirst($urlPart); if (method_exists($this, $method)) { $this->$method($value); } } return $this; } /** * Get the URI from the set parameters * @return string */ public function getUri() { $userPart = ''; if ($this->getUser() !== null && $this->getPass() !== null) { $userPart = $this->getUser() . ':' . $this->getPass() . '@'; } else if ($this->getUser() !== null) { $userPart = $this->getUser() . '@'; } $schemePart = ($this->getScheme() ? $this->getScheme() . '://' : '//'); if (!in_array($this->getScheme(), self::getSchemesWithAuthority())) { $schemePart = $this->getScheme() . ':'; } $portPart = ($this->getPort() ? ':' . $this->getPort() : ''); $queryPart = ($this->getQuery() ? '?' . $this->getQuery() : ''); $fragmentPart = ($this->getFragment() ? '#' . $this->getFragment() : ''); if ($this->isRelative()) { return $this->getPath() . $queryPart . $fragmentPart; } $path = $this->getPath(); if (0 !== strlen($path) && '/' !== $path[0]) { $path = '/' . $path; } return $schemePart . $userPart . $this->getHost() . $portPart . $path . $queryPart . $fragmentPart; } /** * @param string $fragment * @return self */ public function setFragment($fragment) { $this->fragment = $fragment; return $this; } /** * @return string */ public function getFragment() { return $this->fragment; } /** * @param string $host * @return self */ public function setHost($host) { $this->host = $host; $this->setAbsolute(); return $this; } /** * @return string */ public function getHost() { return $this->host; } /** * @param string $pass * @return self */ public function setPass($pass) { $this->pass = $pass; return $this; } /** * @return string */ public function getPass() { return $this->pass; } /** * @param string $path * @return self */ public function setPath($path) { $this->path = $path; return $this; } /** * @return string */ public function getPath() { return $this->path; } /** * Set the query. Must be a string, and the prepending "?" will be trimmed. * Example: ?a=b&c[]=123 -> "a=b&c[]=123" * @see Sensimity_Helper_UriTest::provideSetQuery * * @param string $query * @return self */ public function setQuery($query) { $this->query = null; if (is_string($query)) { $this->query = ltrim($query, '?'); } return $this; } /** * @return string */ public function getQuery() { return $this->query; } /** * Set the scheme. If its empty, it will be set to null. * * Must be a string. Can only contain "a-z A-Z 0-9 . : -". * Will be forced to lowercase. * Appended : or // will be removed. * @see Sensimity_Helper_UriTest::provideSetScheme * * @param string $scheme * @return self */ public function setScheme($scheme) { $this->scheme = null; if (empty($scheme) || null === $scheme) { return $this; } $scheme = preg_replace('/[^a-zA-Z0-9\.\:\-]/', '', $scheme); $scheme = strtolower($scheme); $scheme = rtrim($scheme, ':/'); $scheme = trim($scheme, ':/'); $scheme = str_replace('::', ':', $scheme); if (strlen($scheme) != 0) { if ($this->isRelative()) { /* Explained: */ /* @see Sensimity_Helper_UriTest::testRelativeAbsoluteUrls */ $exp = explode('/', ltrim($this->getPath(), '/')); $this->setHost($exp[0]); unset($exp[0]); $this->setPath(null); $path = implode('/', $exp); if (strlen($path) > 0) { //Only create the "/" if theres a path $this->setPath('/' . $path); } $this->setAbsolute(); } $this->scheme = $scheme; } return $this; } /** * @return string */ public function getScheme() { return $this->scheme; } /** * @param string $user * @return self */ public function setUser($user) { $this->user = $user; return $this; } /** * @return string */ public function getUser() { return $this->user; } /** * Port must be a valid number. Otherwise it will be set to NULL. (default scheme port) * @see Sensimity_Helper_UriTest::provideSetPort * * @param int|string $port * @return self */ public function setPort($port) { $this->port = null; if ((is_string($port) || is_numeric($port)) && ctype_digit(strval($port))) { $this->port = (int) $port; } return $this; } /** * @return int */ public function getPort() { return $this->port; } /** * @return bool */ public function isRelative() { return (!$this->absolute); } /** * @return bool */ public function isAbsolute() { return ($this->absolute); } /** * @return $this */ public function setAbsolute() { $this->absolute = true; return $this; } /** * @return $this */ public function setRelative() { $this->absolute = false; return $this; } /** Some helpful static functions */ /** * @param $uri * @param null $scheme * @return string */ public static function changeScheme($uri, $scheme = null) { if ($scheme == null) { //null for scheme = just no change at all - only in this static function, for BC! return $uri; } $class = get_called_class(); $uri = new $class($uri); $uri->setScheme($scheme); return $uri->getUri(); } /** * @see http://tools.ietf.org/html/rfc3986#section-3 * @return array */ public static function getSchemesWithAuthority() { return explode(';', self::SCHEMES_WITH_AUTHORITY); } /** * @return bool */ public function isSchemeless() { $scheme = $this->getScheme(); return (bool) ($this->isRelative() || ($this->isAbsolute() && empty($scheme))); } public function hasAnchor(){ return (bool) isset( $this->fragment ); } public function hasQueryParam(){ return (bool) isset( $this->query ); } } includes/shortcodes.php 0000777 00000010645 15251156640 0011262 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); // add conditional language shortcode /** * Old shortcode that displays different content in a particular language * * @deprecated 2.9.21 Use the shortcode language_include or language_exclude instead. * @see trp_include_content_in_language(), trp_exclude_content_in_language()) */ add_shortcode( 'trp_language', 'trp_language_content'); /* --------------------------------------------------------------------------- * Shortcode [trp_language language="en_US"] [/trp_language] * --------------------------------------------------------------------------- */ function trp_language_content( $attr, $content = null ){ global $TRP_LANGUAGE_SHORTCODE; if (!isset($TRP_LANGUAGE_SHORTCODE)){ $TRP_LANGUAGE_SHORTCODE = array(); } $TRP_LANGUAGE_SHORTCODE[] = $content; $attr = shortcode_atts(array( 'language' => '', ), $attr); $current_language = get_locale(); if( $current_language == $attr['language'] ){ $output = do_shortcode($content); }else{ $output = ""; } return $output; } // add conditional languages shortcode add_shortcode( 'language-include', 'trp_include_content_in_language'); /* --------------------------------------------------------------------------- * Shortcode [language-include lang="en_us,fr_fr,ro_RO" enable_translation="yes" (default value)] [/language-include] * * Displays content in the chosen languages (attribute lang="") and allows to decide if the content should be translatable or not * ( enable_translation="yes"/"no" - default value is "yes" ) * --------------------------------------------------------------------------- */ function trp_include_content_in_language( $attr, $content = null ){ $attr = shortcode_atts([ 'lang' => '', 'enable_translation' => 'yes', // default is "yes" if not set ], $attr, 'language-include'); $output = trp_get_include_exclude_content( true, $attr['lang'], $attr['enable_translation'], $content ); return $output; } // add conditional language shortcode add_shortcode( 'language-exclude', 'trp_exclude_content_in_language'); /* --------------------------------------------------------------------------- * Shortcode [language-exclude lang="en_us,fr_fr,ro_RO" enable_translation="yes" (default value)] [/language-exclude] * * Restricts content in the chosen languages (attribute lang="") and allows to decide if the content should be translatable or not * ( enable_translation="yes"/"no" - default value is "yes" ) * --------------------------------------------------------------------------- */ function trp_exclude_content_in_language( $attr, $content = null ){ $attr = shortcode_atts([ 'lang' => '', 'enable_translation' => 'yes', // default is "yes" if not set ], $attr, 'language-exclude'); $output = trp_get_include_exclude_content( false, $attr['lang'], $attr['enable_translation'], $content ); return $output; } // function to be called inside the language_include/ exclude shortcodes // $include can be true or false function trp_get_include_exclude_content( $include, $allowed_languages, $enable_translation_var, $content = null ) { global $TRP_LANGUAGE; $allowed_languages = array_map('trim', explode(',', strtolower($allowed_languages))); $enable_translation = true; if (isset($enable_translation_var)) { $value = strtolower(trim($enable_translation_var)); if ($value === 'no') { $enable_translation = false; } } if ($include === in_array(strtolower($TRP_LANGUAGE), $allowed_languages)) { /* "include the content" and "current language is among specified languages" * OR * "exclude the content" and "current language is NOT among specified languages" */ $output = $enable_translation ? do_shortcode($content) : do_shortcode('<trp-tag data-no-translation>' . $content . '</trp-tag>'); }else{ $output = ""; } return $output; } add_filter('trp_exclude_words_from_automatic_translation', 'trp_add_shortcode_content_to_excluded_words_from_auto_translation'); function trp_add_shortcode_content_to_excluded_words_from_auto_translation($excluded_words){ global $TRP_LANGUAGE_SHORTCODE; if (!isset($TRP_LANGUAGE_SHORTCODE)){ $TRP_LANGUAGE_SHORTCODE = array(); } $excluded_words = array_merge($excluded_words, $TRP_LANGUAGE_SHORTCODE); return $excluded_words; } includes/class-search.php 0000777 00000015277 15251156640 0011463 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Search * * Queries for translations in custom trp tables. * */ class TRP_Search extends WP_Query{ protected $settings; protected $db; /** * TRP_Search constructor. * @param $settings */ public function __construct( $settings ){ parent::__construct(''); global $wpdb; $this->db = $wpdb; $this->settings = $settings; } /** * Filter function to replace the search results on other languages. Basically we destroy the search query by unseting the s query var and give it post__in argument with the * results from our own query * @param $query * @return mixed */ public function trp_search_filter( $query ) { global $TRP_LANGUAGE; // Detect if this is specifically a REST "search" request $is_search_rest_request = false; if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { $rest_route = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field($_SERVER['REQUEST_URI']) : ''; if ( strpos( $rest_route, '/wp-json/wp/v2/search' ) !== false ) { $is_search_rest_request = true; } } if ( $TRP_LANGUAGE !== $this->settings['default-language'] ) { if ( ( !is_admin() && $query->is_main_query() && $query->is_search() ) || $is_search_rest_request || apply_filters( 'trp_force_search', false ) ) { // Get the "s" query arg from the initial search $search_query = get_query_var('s'); //in some cases for instance some ajax searches we might need to get it from the query if( empty($search_query) && !empty( $query->query['s'] ) ) $search_query = $query->query['s']; $search_result_ids = $this->get_post_ids_containing_search_term($search_query, $query); if( !empty($search_result_ids) ) { $query->set('s', ''); $query->set('post__in', $search_result_ids); } } } return $query; } public function get_post_ids_containing_search_term($search_query, $query = null ){ global $TRP_LANGUAGE; /* start adapted from parse_search() function from WP_Query */ // added slashes screw with quote grouping when done early, so done later $search_query = stripslashes( $search_query ); // there are no line breaks in <input /> fields $search_query = str_replace( array( "\r", "\n" ), '', $search_query ); $search_terms_count = 1; if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $search_query, $matches ) ) { $search_terms_count = count( $matches[0] ); $search_terms = $this->parse_search_terms( $matches[0] ); // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence if ( empty( $search_terms ) || count( $search_terms ) > 9 ) { $search_terms = array( $search_query ); $search_terms_count = 1; } } else { $search_terms = array( $search_query ); } /* end adapted from parse_search() function from WP_Query */ $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } $search_result_ids = array(); $trp_search_query = ''; $dictionary_name = $this->trp_query->get_table_name( apply_filters( 'trp_change_search_dictionary_language', $TRP_LANGUAGE, $this, $query ) ); $meta_table_name = $this->trp_query->get_table_name_for_original_meta(); if( $search_terms_count === 1 ){ /** * for one search term we can find it directly in the translated column */ $trp_search_query = $this->db->prepare( "SELECT meta_value FROM $dictionary_name INNER JOIN $meta_table_name ON $dictionary_name.original_id = $meta_table_name.original_id AND $meta_table_name.meta_key = '". $this->trp_query->get_meta_key_for_post_parent_id() ."' WHERE $dictionary_name.translated LIKE %s", '%' . $search_terms[0] . '%' ); } else{ $where_terms_or = array(); $where_terms_and = array(); foreach ( $search_terms as $search_term ){ $where_terms_or[] = $this->db->prepare("$dictionary_name.translated LIKE %s", '%' . $search_term . '%'); $where_terms_and[] = $this->db->prepare("t1.tra LIKE %s", '%' . $search_term . '%'); } $where_or = implode( ' OR ', $where_terms_or); $where_and = implode( ' AND ', $where_terms_and); /** * in the inner SELECT we search for the translated strings in dictionaries that have either of the search terms ( OR ) * and their original strings belong to the same post_id and we combine them in a virtual column with GROUP_CONCAT() * basically we recreate the translated post_content but in a random order (we don't care about the order of the strings) * in the outer SELECT we search in the result from inner SELECT the values that have all the search terms (AND) */ $trp_search_query = "SELECT meta_value FROM ( SELECT meta_value, GROUP_CONCAT( translated SEPARATOR ' ' ) AS tra FROM $dictionary_name INNER JOIN $meta_table_name ON $dictionary_name.original_id = $meta_table_name.original_id AND $meta_table_name.meta_key = '". $this->trp_query->get_meta_key_for_post_parent_id() ."' WHERE ( ". $where_or ." ) GROUP BY meta_value ) AS t1 WHERE ( ".$where_and." )"; } $search_result_ids = $this->db->get_results( $trp_search_query, OBJECT_K ); $search_result_ids = array_keys($search_result_ids); return $search_result_ids; } /** * In our search filter we unset the s variable from the query so we need to recreate it later from the $_GET * @param $s string the search query var * @return string */ public function trp_search_query( $s ){ global $TRP_LANGUAGE; if ( $TRP_LANGUAGE !== $this->settings['default-language'] ) { if ( !is_admin() && isset( $_GET['s'] ) && empty( $s ) ){ $s = sanitize_text_field( wp_unslash( $_GET['s'] ) ); } } return $s; } } includes/class-machine-translation-tab.php 0000777 00000026574 15251156640 0014724 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Machine_Translation_Tab { private $settings; public function __construct( $settings ) { $this->settings = $settings; add_action( 'plugins_loaded', array( $this, 'add_upsell_filter' ) ); add_filter( 'trp_machine_translate_slug', array( $this, 'add_enable_auto_translate_slug_filter' ) ); add_action( 'wp_ajax_test_api_key', array( $this, 'test_api_key' ) ); } /* * Add new tab to TP settings * Hooked to trp_settings_tabs */ public function add_tab_to_navigation( $tabs ){ $tab = array( 'name' => __( 'Automatic Translation', 'translatepress-multilingual' ), 'url' => admin_url( 'admin.php?page=trp_machine_translation' ), 'page' => 'trp_machine_translation' ); array_splice( $tabs, 2, 0, array( $tab ) ); return $tabs; } /* * Add submenu for advanced page tab * Hooked to admin_menu */ public function add_submenu_page() { add_submenu_page( 'TRPHidden', 'TranslatePress Automatic Translation', 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_machine_translation', array( $this, 'machine_translation_page_content' ) ); add_submenu_page( 'TRPHidden', 'TranslatePress Test Automatic Translation API', 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_test_machine_api', array( $this, 'test_api_page_content' ) ); } /** * Register setting * * Hooked to admin_init */ public function register_setting(){ register_setting( 'trp_machine_translation_settings', 'trp_machine_translation_settings', array( $this, 'sanitize_settings' ) ); } /** * Output admin notices after saving settings. */ public function admin_notices(){ if( isset( $_GET['page'] ) && $_GET['page'] == 'trp_machine_translation' ) settings_errors(); } /* * Sanitize settings */ public function sanitize_settings($mt_settings ){ $free_version = !class_exists( 'TRP_Handle_Included_Addons' ); $seo_pack_active = class_exists( 'TRP_IN_Seo_Pack'); $settings = array(); $machine_translation_keys = array( 'machine-translation', 'translation-engine', 'google-translate-key', 'deepl-api-type', 'deepl-api-key', 'block-crawlers', 'automatically-translate-slug', 'machine_translation_limit', 'machine_translation_log', 'machine_translation_limit_enabled' ); foreach( $machine_translation_keys as $key ){ if( isset( $mt_settings[$key] ) ){ $settings[$key] = $mt_settings[$key]; } } if( !empty( $settings['machine-translation'] ) ) { $settings['machine-translation'] = sanitize_text_field( $settings['machine-translation'] ); }else $settings['machine-translation'] = 'no'; if( !empty( $settings['translation-engine'] ) ) $settings['translation-engine'] = sanitize_text_field( $settings['translation-engine'] ); else $settings['translation-engine'] = 'google_translate_v2'; if($settings['translation-engine'] == 'deepl_upsell' && !class_exists( 'TRP_DeepL' ) && !class_exists( 'TRP_IN_DeepL' )){ $settings['translation-engine'] = 'google_translate_v2'; } if( !empty( $settings['block-crawlers'] ) ) $settings['block-crawlers'] = sanitize_text_field( $settings['block-crawlers'] ); else $settings['block-crawlers'] = 'no'; if( !empty( $settings['machine_translation_limit_enabled'] ) ) $settings['machine_translation_limit_enabled'] = sanitize_text_field( $settings['machine_translation_limit_enabled'] ); else $settings['machine_translation_limit_enabled'] = 'no'; if( $free_version || !$seo_pack_active ){ $mt_settings_option = get_option( 'trp_machine_translation_settings' ); if( isset( $mt_settings_option['automatically-translate-slug'] ) ){ $settings['automatically-translate-slug'] = $mt_settings_option['automatically-translate-slug']; } } else{ if( !empty( $settings['automatically-translate-slug'] ) ) $settings['automatically-translate-slug'] = sanitize_text_field( $settings['automatically-translate-slug'] ); else $settings['automatically-translate-slug'] = 'no'; } if ( isset ( $_POST['option_page'] ) && $_POST['option_page'] === 'trp_machine_translation_settings' && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { $db_stored_data = get_option( 'trp_db_stored_data', array() ); unset( $db_stored_data['trp_mt_supported_languages'][ $settings['translation-engine'] ]['last-checked'] ); update_option( 'trp_db_stored_data', $db_stored_data ); } return apply_filters( 'trp_machine_translation_sanitize_settings', $settings, $mt_settings ); } /* * Automatic Translation */ public function machine_translation_page_content(){ $trp = TRP_Translate_Press::get_trp_instance(); $machine_translator_logger = $trp->get_component( 'machine_translator_logger' ); $machine_translator_logger->maybe_reset_counter_date(); $machine_translator = $trp->get_component( 'machine_translator' ); require_once TRP_PLUGIN_DIR . 'partials/machine-translation-settings-page.php'; } /** * Test selected API functionality */ public function test_api_page_content(){ require_once TRP_PLUGIN_DIR . 'partials/test-api-settings-page.php'; } public function load_engines(){ include_once TRP_PLUGIN_DIR . 'includes/mtapi/functions.php'; include_once TRP_PLUGIN_DIR . 'includes/mtapi/class-mtapi-machine-translator.php'; include_once TRP_PLUGIN_DIR . 'includes/google-translate/functions.php'; include_once TRP_PLUGIN_DIR . 'includes/google-translate/class-google-translate-v2-machine-translator.php'; } public function get_active_engine( ){ // This $default is just a fail-safe. Should never be used. The real default is set in TRP_Settings->set_options function $default = 'TRP_MTAPI_Machine_Translator'; if( empty( $this->settings['trp_machine_translation_settings']['translation-engine'] ) ) $value = $default; else { $deepl_class_name = class_exists('TRP_IN_Deepl_Machine_Translator' ) ? 'TRP_IN_Deepl_Machine_Translator' : 'TRP_Deepl_Machine_Translator'; $existing_engines = apply_filters('trp_automatic_translation_engines_classes', array( 'mtapi' => 'TRP_MTAPI_Machine_Translator', 'google_translate_v2' => 'TRP_Google_Translate_V2_Machine_Translator', 'deepl' => $deepl_class_name )); $value = ( isset( $existing_engines[$this->settings['trp_machine_translation_settings']['translation-engine']] ) ) ? $existing_engines[$this->settings['trp_machine_translation_settings']['translation-engine']] : ''; if( !class_exists( $value ) ) { $value = $default; //something is wrong if it reaches this } } return new $value( $this->settings ); } public function add_upsell_filter(){ if( !class_exists( 'TRP_DeepL' ) && !class_exists( 'TRP_IN_DeepL' ) ) add_filter( 'trp_machine_translation_engines', [ $this, 'translation_engines_upsell' ], 20 ); } public function translation_engines_upsell( $engines ){ $engines[] = array( 'value' => 'deepl_upsell', 'label' => __( 'DeepL', 'translatepress-multilingual' ) ); return $engines; } public function add_enable_auto_translate_slug_filter( $allow ){ if( !empty( $this->settings['trp_machine_translation_settings']['machine-translation'] ) && $this->settings['trp_machine_translation_settings']['machine-translation'] == 'yes' && isset( $this->settings['trp_machine_translation_settings']['automatically-translate-slug'] ) && $this->settings['trp_machine_translation_settings']['automatically-translate-slug'] == 'yes' ){ $allow = true; } return $allow; } public function display_unsupported_languages(){ $trp = TRP_Translate_Press::get_trp_instance(); $machine_translator = $trp->get_component( 'machine_translator' ); $trp_languages = $trp->get_component( 'languages' ); $correct_key = $machine_translator->is_correct_api_key(); if ( 'yes' === $this->settings['trp_machine_translation_settings']['machine-translation'] && !empty( $machine_translator->get_api_key() ) && !$machine_translator->check_languages_availability($this->settings['translation-languages']) && $correct_key ){ $language_names = $trp_languages->get_language_names( $this->settings['translation-languages'], 'english_name' ); ?> <div class="trp-settings-container" id="trp_unsupported_languages"> <h3 class="trp-settings-primary-heading"><?php esc_html_e( 'Unsupported languages', 'translatepress-multilingual' ); ?></h3> <div class="trp-settings-separator"></div> <ul class="trp-unsupported-languages"> <?php foreach ( $this->settings['translation-languages'] as $language_code ) { if ( !$machine_translator->check_languages_availability( array( $language_code ) ) ) { echo '<li class="trp-primary-text-bold">' . esc_html( $language_names[$language_code] ) . '</li>'; echo '<div class="trp-settings-separator" style="width: 65%;"></div>'; } } ?> </ul> <p class="trp-primary-text"> <?php echo wp_kses( __( 'The selected automatic translation engine does not provide support for these languages.<br>You can still manually translate pages in these languages using the Translation Editor.', 'translatepress-multilingual' ), array( 'br' => array() ) ); ?> </p> </div> <?php } } public function test_api_key(){ check_ajax_referer( 'trp_test_api_nonce', 'security' ); if ( ! current_user_can( apply_filters( 'trp_settings_capability', 'manage_options' ) ) ) { wp_die( -1, 403 ); } $trp = TRP_Translate_Press::get_trp_instance(); $machine_translator = $trp->get_component( 'machine_translator' ); $response = $machine_translator->test_request(); if ( is_wp_error( $response ) ) { wp_send_json_error([ 'message' => esc_html__('API key validation failed.', 'translatepress-multilingual'), 'error' => $response->get_error_message() ]); } ob_start(); print_r( $response ); $full_response = ob_get_clean(); wp_send_json_success([ 'message' => esc_html__('API key verification was successful.', 'translatepress-multilingual'), 'response' => $response, 'raw_response' => $full_response ]); } } includes/queries/class-gettext-table-creation.php 0000777 00000011572 15251156640 0016240 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Gettext_Table_Creation * * Queries for creating gettext tables. * * To access this component use: * $trp = TRP_Translate_Press::get_trp_instance(); * $trp_query = $trp->get_component( 'query' ); * $gettext_table_creation = $trp_query->get_query_component('gettext_table_creation'); * */ class TRP_Gettext_Table_Creation extends TRP_Query{ public $db; protected $settings; /** * TRP_Gettext_Table_Creation constructor. * @param $settings */ public function __construct( $settings ){ global $wpdb; $this->db = $wpdb; $this->settings = $settings; } /** * Check if gettext table for specific language exists. * * If the table does not exists it is created. * * @param string $language_code */ public function check_gettext_table( $language_code ){ $table_name = sanitize_text_field( $this->get_gettext_table_name($language_code) ); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table not in database. Create new table $charset_collate = $this->db->get_charset_collate(); $sql = "CREATE TABLE `" . $table_name . "`( id bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, original longtext NOT NULL, translated longtext, domain longtext, status int(20), original_id bigint(20), plural_form int(20), UNIQUE KEY id (id) ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error creating gettext strings tables' ) ); $sql_index = "CREATE INDEX index_name ON `" . $table_name . "` (original(100));"; $this->db->query( $sql_index ); // full text index for original $sql_index = "CREATE FULLTEXT INDEX original_fulltext ON `" . $table_name . "`(original);"; $this->db->query( $sql_index ); } } /** * Check if the gettext original string table exists * * If the table does not exists it is created. * */ public function check_gettext_original_table(){ $table_name = $this->get_table_name_for_gettext_original_strings(); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table not in database. Create new table $charset_collate = $this->db->get_charset_collate(); $sql = "CREATE TABLE `" . $table_name . "`( id bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, original TEXT NOT NULL, domain TEXT NOT NULL, context TEXT DEFAULT NULL, original_plural TEXT DEFAULT NULL ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); $sql_index = "CREATE INDEX gettext_index_original ON `" . $table_name . "` (original(100));"; $this->db->query( $sql_index ); } } /** * Check if the gettext original meta table exists * * If the table does not exists it is created */ public function check_gettext_original_meta_table(){ $table_name = $this->get_table_name_for_gettext_original_meta(); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table not in database. Create new table $charset_collate = $this->db->get_charset_collate(); $sql = "CREATE TABLE `" . $table_name . "`( meta_id bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, original_id bigint(20) NOT NULL, meta_key varchar(255), meta_value longtext, UNIQUE KEY meta_id (meta_id) ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); //create indexes $sql_index = "CREATE INDEX gettext_index_original_id ON `" . $table_name . "` (original_id);"; $this->db->query( $sql_index ); $sql_index = "CREATE INDEX gettext_meta_key ON `" . $table_name . "`(meta_key);"; $this->db->query( $sql_index ); } } } includes/queries/class-query.php 0000777 00000212503 15251156640 0013027 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Query * * Queries for translations in custom trp tables. * */ class TRP_Query{ protected $table_name; public $db; protected $settings; protected $url_converter; protected $translation_render; protected $error_manager; protected $check_invalid_text; protected $tables_exist = array(); protected $db_sql_version = null; protected $gettext_normalized = null; /* gettext query components */ protected $gettext_table_creation; protected $gettext_normalization; protected $gettext_insert_update; const NOT_TRANSLATED = 0; const MACHINE_TRANSLATED = 1; const HUMAN_REVIEWED = 2; const SIMILAR_TRANSLATED = 3; const BLOCK_TYPE_REGULAR_STRING = 0; const BLOCK_TYPE_ACTIVE = 1; const BLOCK_TYPE_DEPRECATED = 2; /** * TRP_Query constructor. * @param $settings */ public function __construct( $settings ){ global $wpdb; $this->db = $wpdb; $this->settings = $settings; $this->gettext_normalization = new TRP_Gettext_Normalization($settings); $this->gettext_table_creation = new TRP_Gettext_Table_Creation($settings); $this->gettext_insert_update = new TRP_Gettext_Insert_Update($settings); } public function get_query_component( $component ){ return $this->$component; } /** * Return an array of all the active translation blocks * * @param $language_code * * @return array|null|object */ public function get_all_translation_blocks( $language_code ){ if ( apply_filters( 'trp_enable_translation_blocks_querying', true ) ) { $cache_key = 'get_all_translation_blocks_' . md5( $language_code ); $dictionary = wp_cache_get( $cache_key, 'trp' ); if ( false === $dictionary ) { $query = "SELECT original, id, block_type, status FROM `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` WHERE block_type = " . self::BLOCK_TYPE_ACTIVE . " OR block_type = " . self::BLOCK_TYPE_DEPRECATED; $dictionary = $this->db->get_results( $query, OBJECT_K ); wp_cache_set( $cache_key, $dictionary, 'trp' ); } }else{ $dictionary = array(); } return $dictionary; } /** * Returns the translations for the provided strings. * * Only returns results where there actually is a translation ( != NOT_TRANSLATED ) * * @param array $strings_array Array of original strings to search for. * @param string $language_code Language code to query for. * @return object Associative Array of objects with translations where key is original string. */ public function get_existing_translations( $strings_array, $language_code, $block_type = null ){ if ( !is_array( $strings_array ) || count ( $strings_array ) == 0 || !in_array( $language_code, $this->settings['translation-languages'] ) || $language_code === $this->settings['default-language'] ){ return array(); } if ( $block_type == null ){ $and_block_type = ""; }else { $and_block_type = " AND block_type = " . $block_type; } /* Do not add a condition for "translated <> '' because this would cause re-autotranslating. */ $query = "SELECT original,translated, status FROM `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` WHERE status != " . self::NOT_TRANSLATED . $and_block_type . " AND original IN "; $placeholders = array(); $values = array(); foreach( $strings_array as $string ){ $placeholders[] = '%s'; $values[] = $string; } $query .= "( " . implode ( ", ", $placeholders ) . " )"; $prepared_query = $this->db->prepare( $query, $values ); $results = $this->db->get_results( $prepared_query, OBJECT ); if ( !empty( $results ) && is_array( $results ) ) { // There are edge cases where we have 2 results for the same original: // one with translated as empty string, one with non-empty translation. Keep the one with translation. $dictionary = []; foreach ( $results as $row ) { $key = $row->original; // If this key has never been added, simply add it if ( !isset( $dictionary[ $key ] ) ) { $dictionary[ $key ] = $row; continue; } // If current stored entry has empty 'translated' // and this new row has non-empty 'translated', replace it if ( empty( $dictionary[ $key ]->translated ) && !empty( $row->translated ) ) { $dictionary[ $key ] = $row; } // Otherwise do nothing — we keep the existing "better" record } } else { $dictionary = $results; } if( !$this->check_invalid_text ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->check_invalid_text = $trp->get_component( 'check_invalid_text' ); } $dictionary = $this->check_invalid_text->get_existing_translations_without_invalid_text($dictionary, $prepared_query, $strings_array, $language_code, $block_type ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_existing_translations()' ) ); if ($this->db->last_error !== '' && !$this->check_invalid_text->is_invalid_data_error()) $dictionary = false; $dictionary = apply_filters( 'trp_get_existing_translations', $dictionary, $prepared_query, $strings_array, $language_code, $block_type ); if ( is_array( $dictionary ) && count( $dictionary ) === 0 && !$this->table_exists($this->get_table_name( $language_code )) && !$this->check_invalid_text->is_invalid_data_error()){ // if table is missing then last_error is empty for the select query $this->maybe_record_automatic_translation_error(array( 'details' => 'Missing table ' . $this->get_table_name( $language_code ) . ' . To regenerate tables, try going to Settings->TranslatePress->General tab and Save Settings.'), true ); } return $dictionary; } /** * Return constant used for entries without translations. * * @return int */ public function get_constant_not_translated(){ return self::NOT_TRANSLATED; } /** * Return constant used for entries with machine translation. * * @return int */ public function get_constant_machine_translated(){ return self::MACHINE_TRANSLATED; } /** * Return constant used for entries edited by humans. * * @return int */ public function get_constant_human_reviewed(){ return self::HUMAN_REVIEWED; } /** * Return constant used for entries automatically filled by the original being a string similar to another string that has a translation. * * @return int */ public function get_constant_similar_translated(){ return self::SIMILAR_TRANSLATED; } /** * Return constant used for individual strings, not part of a translation block * * @return int */ public function get_constant_block_type_regular_string(){ return self::BLOCK_TYPE_REGULAR_STRING; } /** * Return constant used for a translation block * * @return int */ public function get_constant_block_type_active(){ return self::BLOCK_TYPE_ACTIVE; } /** * Return constant used for a translation block, no longer in use (i.e. after being split ) * * @return int */ public function get_constant_block_type_deprecated(){ return self::BLOCK_TYPE_DEPRECATED; } /** * Check if table for specific language exists. * * If the table does not exists it is created. * * @param string $language_code */ public function check_table( $default_language, $language_code ){ $table_name = sanitize_text_field( $this->get_table_name( $language_code, $default_language ) ); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table not in database. Create new table $charset_collate = $this->db->get_charset_collate(); $sql = "CREATE TABLE `" . $table_name . "`( id bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, original longtext NOT NULL, translated longtext, status int(20) DEFAULT " . $this::NOT_TRANSLATED .", block_type int(20) DEFAULT " . $this::BLOCK_TYPE_REGULAR_STRING .", original_id bigint(20) DEFAULT NULL, UNIQUE KEY id (id) ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); $sql_index = "CREATE INDEX index_name ON `" . $table_name . "` (original(100));"; $this->db->query( $sql_index ); // added index on block_type for performance improvement when creating a new dictionary table // for existing tables a function was added in class-upgrade on version 2.7.4 $sql_index_block_type = "CREATE INDEX block_type ON `" . $table_name . "` (block_type);"; $this->db->query( $sql_index_block_type ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error creating regular tables' ) ); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table still doesn't exist after creation $this->maybe_record_automatic_translation_error(array( 'details' => 'Error creating regular strings tables' ), true ); }else { // full text index for original $sql_index = "CREATE FULLTEXT INDEX original_fulltext ON `" . $table_name . "`(original);"; $this->db->query( $sql_index ); //syncronize all translation blocks. $this->copy_all_translation_blocks_into_table($default_language, $language_code); } }else{ $this->check_for_block_type_column( $language_code, $default_language ); $this->check_for_original_id_column( $language_code, $default_language ); } } /** * Check if table for machine translation logs exists. * * If the table does not exists it is created. * * @param string $language_code */ public function check_machine_translation_log_table(){ $table_name = $this->db->prefix . 'trp_machine_translation_log'; if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table not in database. Create new table $charset_collate = $this->db->get_charset_collate(); $sql = "CREATE TABLE `{$table_name}`( id bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, url text, timestamp datetime DEFAULT '0000-00-00 00:00:00', strings longtext, characters text, response longtext, lang_source text, lang_target text, UNIQUE KEY id (id) ) {$charset_collate};"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error creating machine translation log tables' ) ); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { $this->maybe_record_automatic_translation_error(array( 'details' => 'Error creating machine translation log tables' ), true ); // something failed. Table still doesn't exist. return false; } // table exists return true; } //table exists return true; } public function copy_all_translation_blocks_into_table( $default_language, $language_code ){ $all_table_names = $this->get_all_table_names( $default_language, array( $language_code ) ); if ( count( $all_table_names ) > 0 ){ $source_table_name = $all_table_names[0]; // copy translation blocks from table name of this language $source_language = apply_filters( 'trp_source_language_translation_blocks', '', $default_language, $language_code ); if ( $source_language != '' ){ $source_table_name = $this->get_table_name( $source_language, $default_language ); } $destination_table_name = $this->get_table_name( $language_code, $default_language ); // get all tb from $source_table_name and copy to $destination_table_name $sql = 'INSERT INTO `' . $destination_table_name . '` (id, original, translated, status, block_type) SELECT NULL, original, "", ' . $this::NOT_TRANSLATED . ', block_type FROM `' . $source_table_name . '` WHERE block_type = ' . self::BLOCK_TYPE_ACTIVE . ' OR block_type = ' . self::BLOCK_TYPE_DEPRECATED; $this->db->query( $sql ); } } /** * Check if the original string table exists * * If the table does not exists it is created. * * @since 1.6.6 */ public function check_original_table(){ $table_name = $this->get_table_name_for_original_strings(); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table not in database. Create new table $charset_collate = $this->db->get_charset_collate(); $sql = "CREATE TABLE `" . $table_name . "`( id bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, original TEXT NOT NULL ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); $sql_index = "CREATE INDEX index_original ON `" . $table_name . "` (original(100));"; $this->db->query( $sql_index ); } } /** * Function that takes care of inserting original strings from dictionary to original_strings table when updating to version 1.6.6 */ public function original_ids_insert( $language_code, $inferior_limit, $batch_size ){ //don't do anything for default language if ( $this->settings['default-language'] === $language_code ) return 0; if( !$this->error_manager ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $originals_table = $this->get_table_name_for_original_strings(); $table_name = sanitize_text_field( $this->get_table_name( $language_code, $this->settings['default-language'] ) ); /* * select all string that are in the dictionary table and are not in the original tables and insert them in the original */ $insert_records = $this->db->query( $this->db->prepare( "INSERT INTO `$originals_table` (original) SELECT DISTINCT ( BINARY t1.original ) FROM `$table_name` t1 LEFT JOIN `$originals_table` t2 ON ( t2.original = t1.original AND t2.original = BINARY t1.original ) WHERE t2.original IS NULL AND t1.id > %d AND t1.id <= %d AND LENGTH(t1.original) < 20000", $inferior_limit, ($inferior_limit + $batch_size) ) ); if (!empty($this->db->last_error)) { $this->error_manager->record_error(array('last_error_insert_original_strings' => $this->db->last_error)); } return $insert_records; } /** * Function that makes sure we don't have duplicates in original_strings table when updating to version 1.6.6 * It is executed after we inserted all the strings */ public function original_ids_cleanup(){ if( !$this->error_manager ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $originals_table = $this->get_table_name_for_original_strings(); $charset_collate = $this->db->get_charset_collate(); $charset = "utf8mb4"; if( strpos( 'latin1', $charset_collate ) === 0 ) $charset = "latin1"; $this->db->query( "DELETE t1 FROM `$originals_table` t1 INNER JOIN `$originals_table` t2 WHERE t1.id > t2.id AND t1.original COLLATE ".$charset."_bin = t2.original" ); if (!empty($this->db->last_error)) { $this->error_manager->record_error(array('last_error_cleaning_original_strings' => $this->db->last_error)); } } /** * Function that takes care of synchronizing the dictionaries with the original table by inserting the original ids in the original_id column */ public function original_ids_reindex( $language_code, $inferior_limit, $batch_size ){ //don't do anything for default language if ( $this->settings['default-language'] === $language_code ) return 0; if( !$this->error_manager ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $originals_table = $this->get_table_name_for_original_strings(); $table_name = sanitize_text_field( $this->get_table_name( $language_code, $this->settings['default-language'] ) ); $charset_collate = $this->db->get_charset_collate(); $charset = "utf8mb4"; if( strpos( 'latin1', $charset_collate ) === 0 ) $charset = "latin1"; /* * perform a UPDATE JOIN with the original table https://www.mysqltutorial.org/mysql-update-join/ */ $update_records = $this->db->query( $this->db->prepare( "UPDATE $table_name, $originals_table SET $table_name.original_id = $originals_table.id WHERE $table_name.original COLLATE ". $charset ."_bin = $originals_table.original AND $table_name.id > %d AND $table_name.id <= %d", $inferior_limit, ($inferior_limit + $batch_size) ) ); if (!empty($this->db->last_error)) { $this->error_manager->record_error(array('last_error_reindex_original_ids' => $this->db->last_error)); } return $update_records; } /** * Function that makes sure that when new strings are inserted in dictionaries they are also inserted in original_strings table if they don't exist * @param $language_code * @param $new_strings * @return array|object|null */ public function original_strings_sync( $language_code, $new_strings ){ if ( count($new_strings ) === 0 ){ return array(); } if ( $this->settings['default-language'] != $language_code ) { $originals_table = $this->get_table_name_for_original_strings(); $possible_new_strings = array(); foreach ( $new_strings as $string ) { $possible_new_strings[] = $this->db->prepare( "%s", $string ); } $existing_strings = $this->db->get_results( "SELECT original FROM `$originals_table` WHERE BINARY $originals_table.original IN (".implode( ',', $possible_new_strings ).")", OBJECT_K ); if( !empty( $existing_strings ) ){ $existing_strings = array_keys($existing_strings); $insert_strings = array_diff( $new_strings, $existing_strings ); } else{ $insert_strings = $new_strings; } foreach ( $insert_strings as $k => $string ) { $insert_strings[$k] = $this->db->prepare( "(%s)", $string ); } if( !empty( $insert_strings ) ) { //insert the strings that are missing $this->db->query("INSERT INTO `$originals_table` (original) VALUES " . implode(',', $insert_strings)); } //get the ids for all the new strings (new in dictionary) $new_strings_in_dictionary_with_original_id = $this->db->get_results( "SELECT original,id FROM `$originals_table` WHERE BINARY $originals_table.original IN (".implode( ',', $possible_new_strings ).")", OBJECT_K ); if( count( $new_strings_in_dictionary_with_original_id ) === count( $new_strings ) ){ return $new_strings_in_dictionary_with_original_id; } } return array(); } /** * Function that adds post_parent_id meta to original_meta table * @param $original_string_ids * @param $post_ids */ public function set_original_string_meta_post_id( $original_string_ids, $post_ids ){ //group the strings in a new array by post_id $strings_grouped = array(); if( !empty( $post_ids ) ){ foreach( $post_ids as $i => $post_id ){ $strings_grouped[ $post_id ][] = $original_string_ids[$i]; } } if( !empty($strings_grouped) ){ foreach ( $strings_grouped as $post_id => $original_ids ){ //remove all empty values from original_ids just in case $original_ids = array_filter($original_ids); if( !empty( $original_ids ) ) { /* * - select all id's that are in the meta already * - in php compare our $original_ids with the result and leave just the ones that are not in the db * - insert all the remaining ones */ $existing_entries = $this->db->get_results($this->db->prepare( "SELECT original_id FROM " . $this->get_table_name_for_original_meta() . " WHERE meta_key = '" . $this->get_meta_key_for_post_parent_id() . "' AND meta_value = '%1d' AND original_id IN ( %2s )", $post_id, implode(', ', $original_ids) ), OBJECT_K); $existing_entries = array_keys($existing_entries); $insert_this = array_unique(array_diff($original_ids, $existing_entries)); if (!empty($insert_this)) { $insert_values = array(); foreach ($insert_this as $missing_entry) { $insert_values[] = $this->db->prepare("( %d, %s, %d )", $missing_entry, $this->get_meta_key_for_post_parent_id(), $post_id); } $this->db->query("INSERT INTO " . $this->get_table_name_for_original_meta() . " ( original_id, meta_key, meta_value ) VALUES " . implode(', ', $insert_values)); } } } } } /** * Check if the original meta table exists * * If the table does not exists it is created. * * @since 1.6.6 */ public function check_original_meta_table(){ $table_name = $this->get_table_name_for_original_meta(); if ( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) { // table not in database. Create new table $charset_collate = $this->db->get_charset_collate(); $sql = "CREATE TABLE `" . $table_name . "`( meta_id bigint(20) AUTO_INCREMENT NOT NULL PRIMARY KEY, original_id bigint(20) NOT NULL, meta_key varchar(255), meta_value longtext, UNIQUE KEY meta_id (meta_id) ) $charset_collate;"; require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); dbDelta( $sql ); //create indexes $sql_index = "CREATE INDEX index_original_id ON `" . $table_name . "` (original_id);"; $this->db->query( $sql_index ); $sql_index = "CREATE INDEX meta_key ON `" . $table_name . "`(meta_key);"; $this->db->query( $sql_index ); } } /** * Add block_type column to dictionary tables, if it doesn't exist. * * Affects all existing tables, including deactivated languages * * @param null $language_code * @param null $default_language */ public function check_for_block_type_column( $language_code = null, $default_language = null ){ if ( $default_language == null ){ $default_language = $this->settings['default-language']; } if ( $language_code ){ // check only this language $array_of_table_names = array( $this->get_table_name( $language_code, $default_language ) ); }else { // check all languages, including deactivated ones $array_of_table_names = $this->get_all_table_names( $default_language, array() ); } foreach( $array_of_table_names as $table_name ){ if ( ! $this->table_column_exists( $table_name, 'block_type' ) ) { $this->db->query("ALTER TABLE " . $table_name . " ADD block_type INT(20) DEFAULT " . $this::BLOCK_TYPE_REGULAR_STRING ); } } } /** * Add original_id column to dictionary tables, if it doesn't exist. * * Affects all existing tables, including deactivated languages * * @param null $language_code * @param null $default_language */ public function check_for_original_id_column($language_code = null, $default_language = null ){ if ( $default_language == null ){ $default_language = $this->settings['default-language']; } if ( $language_code ){ // check only this language $array_of_table_names = array( $this->get_table_name( $language_code, $default_language ) ); }else { // check all languages, including deactivated ones $array_of_table_names = $this->get_all_table_names( $default_language, array() ); } foreach( $array_of_table_names as $table_name ){ if ( ! $this->table_column_exists( $table_name, 'original_id' ) ) { $this->db->query("ALTER TABLE " . $table_name . " ADD original_id BIGINT(20) DEFAULT NULL" ); } } } /** * Returns true if a database table column exists. Otherwise returns false. * * @link http://stackoverflow.com/a/5943905/2489248 * @global wpdb $wpdb * * @param string $table_name Name of table we will check for column existence. * @param string $column_name Name of column we are checking for. * * @return boolean True if column exists. Else returns false. */ public function table_column_exists( $table_name, $column_name ) { $column = $this->db->get_results( $this->db->prepare( "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s ", DB_NAME, $table_name, $column_name ) ); if ( ! empty( $column ) ) { return true; } return false; } /** * Update regular (non-gettext) strings in DB * * @param array $update_strings Array of strings to update * @param string $language_code Language code * @param array $columns_to_update Array with the name of columns to update id, original, translated, status, block_type, original_id */ public function update_strings( $update_strings, $language_code, $columns_to_update = array('id','original', 'translated', 'status', 'block_type', 'original_id') ) { if ( count( $update_strings ) == 0 ) { return; } $placeholder_array_mapping = array( 'id'=>'%d', 'original'=>'%s', 'translated' => '%s', 'status' => '%d', 'block_type'=>'%d', 'original_id'=>'%d' ); $columns_query_part = ''; foreach ( $columns_to_update as $column ) { $columns_query_part .= $column . ','; $placeholders[] = $placeholder_array_mapping[$column]; } $columns_query_part = rtrim( $columns_query_part, ',' ); $query = "INSERT INTO `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` ( " . $columns_query_part . " ) VALUES "; $values = array(); $place_holders = array(); $placeholders_query_part = '('; foreach ( $placeholders as $placeholder ) { $placeholders_query_part .= "'" . $placeholder . "',"; } $placeholders_query_part = rtrim( $placeholders_query_part, ',' ); $placeholders_query_part .= ')'; foreach ( $update_strings as $string ) { foreach( $columns_to_update as $column ) { array_push( $values, $string[$column] ); } $place_holders[] = $placeholders_query_part; } $on_duplicate = ' ON DUPLICATE KEY UPDATE '; $key_term_values = $this->is_values_accepted() ? 'VALUES' : 'VALUE'; foreach ( $columns_to_update as $column ) { if ( $column == 'id' ){ continue; } $on_duplicate .= $column . '=' . $key_term_values . '(' . $column . '),'; } $query .= implode( ', ', $place_holders ); $on_duplicate = rtrim( $on_duplicate, ',' ); $query .= $on_duplicate; // you cannot insert multiple rows at once using insert() method. // but by using prepare you cannot insert NULL values. $prepared_query = $this->db->prepare($query . ' ', $values); $this->db->query( $prepared_query ); if( !$this->check_invalid_text ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->check_invalid_text = $trp->get_component( 'check_invalid_text' ); } $this->check_invalid_text->update_translations_without_invalid_text( $update_strings, $language_code, $columns_to_update ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running update_strings()' ) ); } /** * Insert new regular strings in DB. * * @param array $new_strings Array of strings for which we do not have a translation. Only inserts original. * @param string $language_code Language code of table where it should be inserted. * @param int $block_type */ public function insert_strings( $new_strings, $language_code, $block_type = self::BLOCK_TYPE_REGULAR_STRING ) { if ( $block_type == null ) { $block_type = self::BLOCK_TYPE_REGULAR_STRING; } if ( count( $new_strings ) == 0 ) { return; } $query = "INSERT INTO `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` ( original, translated, status, block_type, original_id ) VALUES "; $values = array(); $place_holders = array(); $new_strings = array_unique( $new_strings ); //make sure we have the same strings in the original table as well $original_inserts = $this->original_strings_sync( $language_code, $new_strings ); foreach ( $new_strings as $string ) { array_push( $values, $string, NULL, self::NOT_TRANSLATED, $block_type, $original_inserts[$string]->id ); $place_holders[] = "('%s','%s','%d','%d', %d)"; } $query .= implode( ', ', $place_holders ); // you cannot insert multiple rows at once using insert() method. // but by using prepare you cannot insert NULL values. $this->db->query( $this->db->prepare($query . ' ', $values) ); if( !$this->check_invalid_text ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->check_invalid_text = $trp->get_component( 'check_invalid_text' ); } $this->check_invalid_text->insert_translations_without_invalid_text($new_strings, $language_code, $block_type); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running insert_strings()' ) ); } /** * Returns the DB ids of the provided original strings * * @param array $original_strings Array of original strings to search for. * @param string $language_code Language code to query for. * @return object Associative Array of objects with translations where key is original string. */ public function get_string_ids( $original_strings, $language_code, $output = OBJECT_K ){ if ( !is_array( $original_strings ) || count ( $original_strings ) == 0 ){ return array(); } $query = "SELECT original,id FROM `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` WHERE original IN "; $placeholders = array(); $values = array(); foreach( $original_strings as $string ){ $placeholders[] = '%s'; $values[] = $string; } $query .= "( " . implode ( ", ", $placeholders ) . " )"; $dictionary = $this->db->get_results( $this->db->prepare( $query, $values ), $output ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_string_ids()' ) ); return $dictionary; } /** * Returns the DB ids of the provided original strings * * @param array $original_strings Array of original strings to search for. * @return array Associative Array of objects with translations where key is original string. */ public function get_original_string_ids( $original_strings ){ if ( !is_array( $original_strings ) || count ( $original_strings ) == 0 ){ return array(); } $query = "SELECT original,id FROM `" . $this->get_table_name_for_original_strings() . "` WHERE BINARY original IN "; $placeholders = array(); $values = array(); foreach( $original_strings as $string ){ $placeholders[] = '%s'; $values[] = $string; } $query .= "( " . implode ( ", ", $placeholders ) . " )"; $results = $this->db->get_results( $this->db->prepare( $query, $values ), OBJECT_K ); $results_ids = array(); if( !empty( $results ) && !empty( $original_strings ) ){ foreach( $original_strings as $string ){ if( !empty( $results[$string] ) && !empty($results[$string]->id) ) $results_ids[] = $results[$string]->id; else $results_ids[] = null; //this should not happen but if it does we need to keep the same number of result ids as original_strings to have a correlation } } return $results_ids; } /** * Returns the entries for the provided strings. * * Only returns results where there is no translation ( == NOT_TRANSLATED ) * * @param array $strings_array Array of original strings to search for. * @param string $language_code Language code to query for. * @return object Associative Array of objects with translations where key is original string. */ public function get_untranslated_strings( $strings_array, $language_code ){ if ( !is_array( $strings_array ) || count ( $strings_array ) == 0 ){ return array(); } $query = "SELECT original,id FROM `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` WHERE status = " . self::NOT_TRANSLATED . " AND original IN "; $placeholders = array(); $values = array(); foreach( $strings_array as $string ){ $placeholders[] = '%s'; $values[] = $string; } $query .= "( " . implode ( ", ", $placeholders ) . " )"; $dictionary = $this->db->get_results( $this->db->prepare( $query, $values ), OBJECT_K ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_untranslated_strings()' ) ); return $dictionary; } public function get_language_code_from_table_name( $table_name, $default_language = null ){ if ( $default_language == null ) { $default_language = $this->settings['default-language']; } $language_code = str_replace($this->db->prefix . 'trp_dictionary_' . strtolower( $default_language ) . '_', '', $table_name ); return $language_code; } /** * Return table name for original strings table * * @return string Table name. */ public function get_table_name_for_original_strings(){ return apply_filters( 'trp_table_name_original_strings', sanitize_text_field( $this->db->prefix . 'trp_original_strings' ), $this->db->prefix ); } /** * Return table name for original meta table * * @return string Table name. */ public function get_table_name_for_original_meta(){ return apply_filters( 'trp_table_name_original_meta', sanitize_text_field( $this->db->prefix . 'trp_original_meta' ), $this->db->prefix ); } /** * Return table name for gettext original strings table * * @return string Table name. */ public function get_table_name_for_gettext_original_strings(){ return sanitize_text_field( $this->db->prefix . 'trp_gettext_original_strings' ); } /** * Return table name for gettext original meta table * * @return string Table name. */ public function get_table_name_for_gettext_original_meta(){ return sanitize_text_field( $this->db->prefix . 'trp_gettext_original_meta' ); } /** * Return meta_key for post parent id from meta table * * @return string key name. */ public function get_meta_key_for_post_parent_id(){ return 'post_parent_id'; } public function get_all_gettext_strings( $language_code, $inferior_limit = null, $batch_size = null ){ if ($inferior_limit == null && $batch_size ==null) { $dictionary = $this->db->get_results("SELECT tt.id, CASE WHEN ot.original is NULL THEN tt.original ELSE NULL END as tt_original, tt.translated, tt.domain AS tt_domain, tt.plural_form, tt.original_id AS tt_original_id, ot.original, ot.domain, ot.context FROM `" . sanitize_text_field($this->get_gettext_table_name($language_code)) . "` AS tt LEFT JOIN `" . sanitize_text_field($this->get_table_name_for_gettext_original_strings()) . "` AS ot ON tt.original_id = ot.id", ARRAY_A); }else{ $dictionary = $this->db->get_results("SELECT tt.id, CASE WHEN ot.original is NULL THEN tt.original ELSE NULL END as tt_original, tt.translated, tt.domain AS tt_domain, tt.plural_form, tt.original_id AS tt_original_id, ot.original, ot.domain, ot.context FROM `" . sanitize_text_field($this->get_gettext_table_name($language_code)) . "` AS tt LEFT JOIN `" . sanitize_text_field($this->get_table_name_for_gettext_original_strings()) . "` AS ot ON tt.original_id = ot.id LIMIT " . $inferior_limit . ", " . ($inferior_limit + $batch_size), ARRAY_A); } $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_all_gettext_strings()' ) ); if ( is_array( $dictionary ) && count( $dictionary ) === 0 && !$this->table_exists($this->get_gettext_table_name( $language_code )) ){ // if table is missing then last_error is empty $this->maybe_record_automatic_translation_error(array( 'details' => 'Missing table ' . $this->get_gettext_table_name( $language_code ). ' . To regenerate tables, try going to Settings->TranslatePress->General tab and Save Settings.'), true ); } return $dictionary; } public function get_all_gettext_translated_strings( $language_code ){ $dictionary = $this->db->get_results("SELECT id, original, translated, domain FROM `" . sanitize_text_field( $this->get_gettext_table_name( $language_code ) ) . "` WHERE translated <>'' AND status != " . self::NOT_TRANSLATED, ARRAY_A ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_all_gettext_translated_strings()' ) ); return $dictionary; } /** * Return custom table name for given language code. * * @param string $language_code Language code. * @param string $default_language Default language. Defaults to the one from settings. * @return string Table name. */ public function get_table_name( $language_code, $default_language = null, $only_prefix = false ){ if ( $default_language == null ) { $default_language = $this->settings['default-language']; } if ( (!trp_is_valid_language_code($language_code) && $only_prefix === false) || !trp_is_valid_language_code($default_language) ){ /* there's are other checks that display an admin notice for this kind of errors */ return 'trp_language_code_is_invalid_error'; } return apply_filters( 'trp_table_name_dictionary', $this->db->prefix . 'trp_dictionary_' . strtolower( $default_language ) . '_'. strtolower( $language_code ), $this->db->prefix, $language_code, $default_language ); } public function get_gettext_table_name( $language_code ){ if ( !trp_is_valid_language_code($language_code) ){ /* there's are other checks that display an admin notice for this kind of errors */ return 'trp_language_code_is_invalid_error'; } return apply_filters( 'trp_table_name_gettext', $this->db->prefix . 'trp_gettext_' . strtolower( $language_code ), $this->db->prefix, $language_code ); } /** * Return entire rows for given ids or original strings. * * @param array $id_array Int array of db ids. * @param array $original_array String array of originals. * @param string $language_code Language code of table. * @param string $output Return format * @param bool $is_original_id_array Whether the id array refers to the id or original_id column * @return object Associative Array of objects with translations where key is id. */ public function get_string_rows( $id_array, $original_array, $language_code, $output = OBJECT_K, $is_original_id_array = false ){ $original_id = ($is_original_id_array) ? ' original_id, ' : ''; $select_query = "SELECT " . $original_id . "id, original, translated, status, block_type, original_id FROM `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` WHERE "; $prepared_query1 = ''; if ( is_array( $original_array ) && count ( $original_array ) > 0 ) { $placeholders = array(); $values = array(); foreach ($original_array as $string) { $placeholders[] = '%s'; $values[] = $string; } $query1 = "original IN ( " . implode(", ", $placeholders) . " )"; $prepared_query1 = $this->db->prepare($query1, $values); } $prepared_query2 = ''; if ( is_array( $id_array ) && count ( $id_array ) > 0 ) { $placeholders = array(); $values = array(); foreach ($id_array as $id) { $placeholders[] = '%d'; $values[] = intval($id); } $original_or_not = ( $is_original_id_array ) ? 'original_' : ''; $query2 = $original_or_not . "id IN ( " . implode(", ", $placeholders) . " )"; $prepared_query2 = $this->db->prepare($query2, $values); } $query = ''; if ( empty ( $prepared_query1 ) && empty ( $prepared_query2 ) ){ return array(); } if ( empty( $prepared_query1 ) ){ $query = $select_query . $prepared_query2; } if ( empty( $prepared_query2 ) ){ $query = $select_query . $prepared_query1; } if ( !empty ( $prepared_query1 ) && !empty ( $prepared_query2 ) ){ $query = $select_query . $prepared_query1 . " OR " . $prepared_query2; } $dictionary = $this->db->get_results( $query, $output ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_string_rows()' ) ); return $dictionary; } public function get_gettext_string_rows_by_ids( $id_array, $language_code ){ if ( !is_array( $id_array ) || count ( $id_array ) == 0 ){ return array(); } $query = "SELECT ot.id as ot_id, tt.id, ot.original, tt.original as tt_original, tt.translated, tt.domain AS tt_domain, tt.plural_form, ot.original, ot.domain, ot.context, ot.original_plural FROM `" . sanitize_text_field( $this->get_gettext_table_name( $language_code ) ) . "` AS tt LEFT JOIN `" . sanitize_text_field( $this->get_table_name_for_gettext_original_strings() ) . "` AS ot ON tt.original_id = ot.id WHERE tt.id IN "; $placeholders = array(); $values = array(); foreach( $id_array as $id ){ $placeholders[] = '%d'; $values[] = intval( $id ); } $query .= "( " . implode ( ", ", $placeholders ) . " )"; $dictionary = $this->db->get_results( $this->db->prepare( $query, $values ), ARRAY_A ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_gettext_string_rows_by_ids()' ) ); return $dictionary; } public function get_gettext_string_rows_by_original_id( $original_id_array, $language_code ){ if ( !is_array( $original_id_array ) || count ( $original_id_array ) == 0 ){ return array(); } $query = "SELECT tt.id, tt.original AS tt_original, tt.translated, tt.status, tt.domain AS tt_domain, tt.plural_form, ot.id AS ot_id, ot.original, ot.domain, ot.context, ot.original_plural FROM `" . sanitize_text_field( $this->get_table_name_for_gettext_original_strings() ) . "` AS ot LEFT JOIN `" . sanitize_text_field( $this->get_gettext_table_name( $language_code ) ) . "` AS tt ON tt.original_id = ot.id WHERE ot.id IN "; $placeholders = array(); $values = array(); foreach( $original_id_array as $id ){ $placeholders[] = '%d'; $values[] = intval( $id ); } $query .= "( " . implode ( ", ", $placeholders ) . " )"; $dictionary = $this->db->get_results( $this->db->prepare( $query, $values ), ARRAY_A ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_gettext_string_rows_by_original_id()' ) ); return $dictionary; } public function get_gettext_string_rows_by_original( $original_array, $language_code ){ if ( !is_array( $original_array ) || count ( $original_array ) == 0 ){ return array(); } $query = "SELECT tt.id, tt.original as tt_original, tt.translated, tt.domain AS tt_domain, tt.plural_form, tt.status, ot.original, ot.domain, ot.context FROM `" . sanitize_text_field( $this->get_gettext_table_name( $language_code ) ) . "` AS tt LEFT JOIN `" . sanitize_text_field( $this->get_table_name_for_gettext_original_strings() ) . "` AS ot ON tt.original_id = ot.id WHERE ot.original IN "; $placeholders = array(); $values = array(); foreach( $original_array as $string ){ $placeholders[] = '%s'; $values[] = $string; } $query .= "( " . implode ( ", ", $placeholders ) . " )"; $dictionary = $this->db->get_results( $this->db->prepare( $query, $values ), ARRAY_A ); $this->maybe_record_automatic_translation_error(array( 'details' => 'Error running get_gettext_string_rows_by_original()' ) ); return $dictionary; } public function get_all_table_names ( $original_language, $exception_translation_languages = array() ){ foreach ( $exception_translation_languages as $key => $language ){ $exception_translation_languages[$key] = $this->get_table_name( $language, $original_language ); } $return_tables = array(); $table_name = $this->get_table_name( '', null, true ); $table_names = $this->db->get_results( "SHOW TABLES LIKE '$table_name%'", ARRAY_N ); foreach ( $table_names as $table_name ){ if ( isset( $table_name[0]) && ! in_array( $table_name[0], $exception_translation_languages ) ) { $return_tables[] = $table_name[0]; } } return $return_tables; } public function get_all_gettext_table_names(){ global $wpdb; $table_name = $wpdb->get_blog_prefix() . 'trp_gettext_'; $return_tables = array(); $table_names = $this->db->get_results( "SHOW TABLES LIKE '$table_name%'", ARRAY_N ); foreach ( $table_names as $table_name ){ if ( isset( $table_name[0]) && strpos($table_name[0], 'trp_gettext_original_meta') === false && strpos($table_name[0], 'trp_gettext_original_strings') === false ) { $return_tables[] = $table_name[0]; } } return $return_tables; } public function update_translation_blocks_by_original( $table_names, $original_array, $block_type ) { $values = array(); foreach( $table_names as $table_name ){ $placeholders = array(); foreach( $original_array as $string ){ $placeholders[] = '%s'; $values[] = trp_full_trim( $string ); } } $placeholders = "( " . implode ( ", ", $placeholders ) . " )"; $query = 'UPDATE `' . implode( '`, `', $table_names ) . '` SET `' . implode( '`.block_type=' . $block_type . ', `', $table_names ) . '`.block_type=' . $block_type . ' WHERE `' . implode( '`.original IN ' . $placeholders . ' AND `', $table_names ) . '`.original IN ' . $placeholders ; return $this->db->query( $this->db->prepare( $query, $values ) ); } /** * Removes duplicate rows of regular strings table * * (original, block_type) have to be identical. * Only the row with the lowest ID remains * * * @param $language_code * @param $inferior_limit * @param $batch_size * @param $extra_params */ public function remove_duplicate_rows_in_dictionary_table( $language_code, $inferior_limit, $batch_size, $extra_params ) { $table_name = $this->get_table_name( $language_code ); // encoding the original so we don't brake it with sanitize_text_field() inside class-upgrade.php $trp_last_original = isset($extra_params['trp_last_original']) ? $extra_params['trp_last_original'] : ''; $trp_last_id = isset($extra_params['trp_last_id']) ? $extra_params['trp_last_id'] : 0; if ($this->table_exists($table_name)) { $query = $this->db->prepare( "SELECT id, original, translated, block_type domain FROM $table_name WHERE (original > %s OR (original = %s AND id > %d)) ORDER BY original, id LIMIT %d", $trp_last_original, $trp_last_original, $trp_last_id, $batch_size ); $results = $this->db->get_results($query, ARRAY_A); if (!empty($results)){ $last_row = end($results); $trp_last_original = $last_row['original']; $trp_last_id = $last_row['id']; // Step 2: Use PHP to group IDs by 'original' + 'domain' $duplicates = []; foreach ($results as $row) { $block_type = !empty($row['block_type']) ? strval($row['block_type']) : ''; $key = $row['original'] . $row['domain'] . $block_type; $id = $row['id']; $translated = $row['translated']; if (!isset($duplicates[$key])) { $duplicates[$key] = []; } if (!empty($translated)){ // place translated records to the beginning of the array. array_unshift($duplicates[$key], $id); } else { $duplicates[$key][] = $id; } } // Step 3: Collect IDs to delete, keeping one per duplicate $ids_to_delete = []; foreach ($duplicates as $ids) { if (count($ids) > 1) { // Keep the first ID, delete the rest array_shift($ids); $ids_to_delete = array_merge($ids_to_delete, $ids); } } if (!empty($ids_to_delete)) { $ids_string = implode(',', array_map('intval', $ids_to_delete)); $delete_query = "DELETE FROM $table_name WHERE id IN ($ids_string)"; $this->db->query( $delete_query ); } } $extra_params = array( 'trp_last_original' => $trp_last_original, 'trp_last_id' => $trp_last_id ); if ( empty($results) ) { $finalize_with_language = true; } else { $finalize_with_language = false; } }else{ $finalize_with_language = true; } return array( 'finalize_with_language' => $finalize_with_language, 'extra_params' => $extra_params ); } /** * Removes duplicate rows of gettext strings table * * (original, domain) have to be identical. * Only the row with the lowest ID remains * * @param $language_code * @param $inferior_limit 1000, 2000 * @param $batch_size * @param $extra_params * @return array */ public function remove_duplicate_rows_in_gettext_table( $language_code, $inferior_limit, $batch_size, $extra_params = array() ){ $table_name = $this->get_gettext_table_name( $language_code ); // encoding the original so we don't brake it with sanitize_text_field() inside class-upgrade.php $trp_last_original = isset($extra_params['trp_last_original']) ? $extra_params['trp_last_original'] : ''; $trp_last_id = isset($extra_params['trp_last_id']) ? $extra_params['trp_last_id'] : 0; if ($this->table_exists($table_name)) { $query = $this->db->prepare( "SELECT id, original, translated, plural_form domain FROM $table_name WHERE (original > %s OR (original = %s AND id > %d)) ORDER BY original, id LIMIT %d", $trp_last_original, $trp_last_original, $trp_last_id, $batch_size ); $results = $this->db->get_results($query, ARRAY_A); if (!empty($results)){ $last_row = end($results); $trp_last_original = $last_row['original']; $trp_last_id = $last_row['id']; // Step 2: Use PHP to group IDs by 'original' + 'domain' $duplicates = []; foreach ($results as $row) { $plural = !empty($row['plural_form']) ? strval($row['plural_form']) : ''; $key = $row['original'] . $row['domain'] . $plural; $id = $row['id']; $translated = $row['translated']; if (!isset($duplicates[$key])) { $duplicates[$key] = []; } if (!empty($translated)){ // place translated records to the beginning of the array. array_unshift($duplicates[$key], $id); } else { $duplicates[$key][] = $id; } } // Step 3: Collect IDs to delete, keeping one per duplicate $ids_to_delete = []; foreach ($duplicates as $ids) { if (count($ids) > 1) { // Keep the first ID, delete the rest array_shift($ids); $ids_to_delete = array_merge($ids_to_delete, $ids); } } if (!empty($ids_to_delete)) { $ids_string = implode(',', array_map('intval', $ids_to_delete)); $delete_query = "DELETE FROM $table_name WHERE id IN ($ids_string)"; $this->db->query( $delete_query ); } } $extra_params = array( 'trp_last_original' => $trp_last_original, 'trp_last_id' => $trp_last_id ); if ( empty($results) ) { $finalize_with_language = true; } else { $finalize_with_language = false; } }else{ $finalize_with_language = true; } return array( 'finalize_with_language' => $finalize_with_language, 'extra_params' => $extra_params ); } /** * Removes CDATA from original and dictionary tables. * @param $language_code * @param $inferior_limit * @param $batch_size * @return bool */ public function remove_cdata_in_original_and_dictionary_tables($language_code, $inferior_limit, $batch_size){ if ($language_code == $this->settings['default-language']){ $table_name = $this->get_table_name_for_original_strings(); $query = $this->get_remove_cdata_query($table_name, $batch_size); $rows_affected = $this->db->query( $query ); if ( $rows_affected > 0 ) { return false; }else{ return true; } } $table_name = $this->get_table_name( $language_code ); if ($this->table_exists($table_name)) { $query = $this->get_remove_cdata_query($table_name, $batch_size); $rows_affected = $this->db->query( $query ); if ( $rows_affected > 0 ) { return false; }else{ return true; } }else{ return true; } } /** * @param $table_name * @param $batch_size * @return string */ private function get_remove_cdata_query( $table_name, $batch_size ){ $query = "DELETE FROM " . $table_name . " WHERE original LIKE '<![CDATA[%' LIMIT " . $batch_size; return $query; } /** * Removes untranslated links from the dictionary table * @param $language_code * @param $inferior_limit * @param $batch_size * @return bool */ public function remove_untranslated_links_in_dictionary_table($language_code, $inferior_limit, $batch_size){ $table_name = $this->get_table_name( $language_code ); if ($this->table_exists($table_name)) { $query = $this->get_remove_untranslated_links_query($table_name, $batch_size); $rows_affected = $this->db->query( $query ); if ( $rows_affected > 0 ) { return false; }else{ return true; } }else{ return true; } } /** * @param $table_name * @return $query */ private function get_remove_untranslated_links_query($table_name, $batch_size){ $query = "DELETE FROM " . $table_name . " WHERE original LIKE 'http%' AND (translated = '' OR translated IS NULL) LIMIT " . $batch_size; return $query; } /* * Get last inserted ID for this table * * Useful for optimizing database by removing duplicate rows */ public function get_last_id( $table_name ){ $last_id = $this->db->get_var("SELECT MAX(id) FROM " . $table_name ); return $last_id; } /** * Returns a selection of rows from a specific location. * Only id and original are selected. * * Ex. if $inferior_limit = 400 and $batch_size = 10 * You will get rows 401 to 411 * * @param $language_code * @param $inferior_limit * @param $batch_size * * @return array|null|object */ public function get_rows_from_location( $language_code, $inferior_limit, $batch_size, $columns_to_retrieve ) { $columns_query_part = ''; foreach ( $columns_to_retrieve as $column ) { $columns_query_part .= $column . ','; } $columns_query_part = rtrim( $columns_query_part, ',' ); $query = "SELECT " . $columns_query_part . " FROM `" . sanitize_text_field( $this->get_table_name( $language_code ) ) . "` WHERE status != " . self::NOT_TRANSLATED . " ORDER BY id LIMIT " . $inferior_limit . ", " . $batch_size; $dictionary = $this->db->get_results( $query, ARRAY_A ); return $dictionary; } /** * Used for updating database * * @param string $language_code Language code of the table * @param string $limit How many strings to affect at most * * @return bool|int */ public function delete_empty_gettext_strings( $language_code, $limit ){ $limit = (int) $limit; $sql = "DELETE FROM `" . sanitize_text_field( $this->get_gettext_table_name( $language_code ) ). "` WHERE (original IS NULL OR original = '') LIMIT " . $limit; return $this->db->query( $sql ); } public function maybe_record_automatic_translation_error($error_details = array(), $ignore_last_error = false ){ if( !$this->check_invalid_text ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->check_invalid_text = $trp->get_component( 'check_invalid_text' ); } if ( ( !empty( $this->db->last_error) && !$this->check_invalid_text->is_invalid_data_error() ) || $ignore_last_error ){ $trp = TRP_Translate_Press::get_trp_instance(); if( !$this->error_manager ){ $this->error_manager = $trp->get_component( 'error_manager' ); } if( !$this->url_converter ) { $this->url_converter = $trp->get_component( 'url_converter' ); } $default_error_details = array( 'last_error' => $this->db->last_error, 'disable_automatic_translations' => true, 'url' => $this->url_converter->cur_page_url(), ); $error_details = array_merge( $default_error_details, $error_details ); $this->error_manager->record_error( $error_details ); } } /** * Return true if table exists in db, return false otherwise * * @param $table_name * @param $ignore_cache * @return bool */ public function table_exists($table_name, $ignore_cache = false ){ if( !$ignore_cache && in_array( $table_name, $this->tables_exist ) ){ return true; } $table_name = sanitize_text_field($table_name); $table_found = strtolower( $this->db->get_var( "SHOW TABLES LIKE '$table_name'" ) ) == strtolower( $table_name ); if ( $table_found ) { $this->tables_exist[] = $table_name; } return $table_found; } /** * Removes any other strings from DB that have the same original with the provided array * * Keeps only the rows with the ids specified. Any other rows with the same original (and domain for gettext) are deleted from DB * * @param $update_string_array * @param $language * @param $string_type * @return bool|int|void */ public function remove_possible_duplicates( $update_string_array, $language, $string_type ){ if ( !is_array( $update_string_array ) || count ($update_string_array) < 1 ) { return; } $charset_collate = $this->db->get_charset_collate(); $charset = (strpos( 'latin1', $charset_collate ) === 0 ) ? "latin1" : "utf8mb4"; $table_name = ( $string_type === 'gettext' ) ? $this->get_gettext_table_name( $language ) : $this->get_table_name( $language ); $values = array(); $place_holders = array(); foreach( $update_string_array as $string ){ if ( $string_type === 'gettext' ) { array_push( $values, $string['original'], $string['domain'], (int)$string['plural_form'], $string['id'] ); }else{ array_push( $values, $string['original'], $string['id'] ); } $domain = ( $string_type === 'gettext') ? "AND domain COLLATE " . $charset . "_bin = '%s' AND plural_form = '%d' " : ""; $place_holders[] = "(original COLLATE " . $charset . "_bin = '%s' " . $domain . "AND id != '%d' )"; } $sql = "DELETE FROM `" . sanitize_text_field( $table_name ). "` WHERE " . implode( " OR ", $place_holders ); $query = $this->db->prepare( $sql, $values ); return $this->db->query( $query ); } public function rename_originals_table(){ $new_table_name = sanitize_text_field( $this->get_table_name_for_original_strings() . time() ); $this->db->query( "ALTER TABLE " . $this->get_table_name_for_original_strings() . " RENAME TO " . $new_table_name ); $table_to_use_for_recovery = get_option('trp_original_strings_table_for_recovery', ''); if ( $table_to_use_for_recovery == '' ) { // if a previous run of removing original strings duplicates failed, use the old table, not the one created during that failed time update_option( 'trp_original_strings_table_for_recovery', $new_table_name ); } } public function regenerate_original_meta_table($inferior_limit, $batch_size){ if( !$this->error_manager ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $originals_table = $this->get_table_name_for_original_strings(); $recovery_originals_table = sanitize_text_field( get_option( 'trp_original_strings_table_for_recovery' ) ); $originals_meta_table = $this->get_table_name_for_original_meta(); if ( empty( $recovery_originals_table ) ){ $this->error_manager->record_error(array('regenerate_original_meta_table' => 'Empty option trp_original_strings_table_for_recovery')); return; } $this->db->query( $this->db->prepare("UPDATE `$originals_meta_table` trp_meta INNER JOIN `$recovery_originals_table` trp_old ON trp_meta.original_id = trp_old.id LEFT JOIN `$originals_table` trp_new ON trp_new.original = trp_old.original set trp_meta.original_id = IF(trp_new.id IS NULL, 0, trp_new.id) WHERE trp_meta.meta_id > %d AND trp_meta.meta_id <= %d AND trp_new.id != trp_old.id", $inferior_limit, ($inferior_limit + $batch_size) ) ); /* UPDATE `wp_trp_original_meta` trp_meta INNER JOIN `wp_trp_original_strings1608214654` as trp_old ON trp_meta.original_id = trp_old.id * LEFT JOIN `wp_trp_original_strings` as trp_new on trp_new.original = trp_old.original set trp_meta.original_id = IF(trp_new.id IS NULL, 0, trp_new.id) * WHERE trp_meta.meta_id > 10 AND trp_meta.meta_id <= 33 AND trp_new.id != trp_old.id*/ if (!empty($this->db->last_error)) { $this->error_manager->record_error(array('last_error_regenerate_original_meta_table' => $this->db->last_error)); } } public function clean_original_meta( $limit ){ $limit = (int) $limit; $sql = "DELETE FROM `" . sanitize_text_field( $this->get_table_name_for_original_meta() ). "` WHERE original_id = 0 LIMIT " . $limit; return $this->db->query( $sql ); } public function drop_table($table_name){ $sql = "DROP TABLE `" . sanitize_text_field( $table_name ). "`"; return $this->db->query( $sql ); } /** * Return db sql version * * Using 'select version()' instead of wpdb->db_server_info because * db_server_info returns format 5.5.5-10.3.3-mariadb instead of 10.3.3-mariadb on some setups * https://www.php.net/manual/en/mysqli.get-server-info.php#118822 * * @return string|null */ public function get_db_sql_version(){ if ( $this->db_sql_version === null ){ $this->db_sql_version = $this->db->get_var( 'select version()' ); $this->db_sql_version = ( $this->db_sql_version === null ) ? '0' : $this->db_sql_version; } return $this->db_sql_version; } /** * Whether it is safe to use VALUES() instead of VALUE() * * Starting with 10.3.3 MariaDB recommends using VALUE() instead of VALUES() * Even though they say they still accept the term values for 'on duplicate key update' syntax, * some users still report syntax error. * https://mariadb.com/kb/en/values-value/ * * MySQL servers marked the use of VALUES deprecated starting with 8.0.20 but have not removed support for it. * We can't use the MariaDB approach, their alternative is different and not supported by earlier versions. * For now, there is no need to further complicate this SQL query based on DB version and make. * * * @return bool */ public function is_values_accepted(){ $return = true; $db_sql_version = strtolower( $this->get_db_sql_version() ); if ( strpos( $db_sql_version, 'mariadb' ) !== false ){ $db_server_array = explode('-', $db_sql_version); if( isset( $db_server_array[1] ) && $db_server_array[1] == 'mariadb' && version_compare($db_server_array[0], '10.3.3', '>=') ){ $return = false; } } return apply_filters('trp_is_sql_values_accepted', $return ); } /** * Return true if the dictionary table of $language has at least $minimum_rows with $status * * @param $language * @param $minimum_rows * @param $status * * @return bool */ public function minimum_rows_with_status( $language, $minimum_rows, $status ) { $minimum_rows = (int) $minimum_rows; $status = (int) $status; $sql = "SELECT (COUNT(*) > " . $minimum_rows . ") FROM `" . sanitize_text_field( $this->get_table_name( $language ) ). "` WHERE status = " . $status; return $this->db->get_var( $sql ); } public function is_gettext_normalized(){ if( $this->gettext_normalized === null ){ $this->gettext_normalized = !( get_option( 'trp_gettext_normalized', '' ) == 'no' ); } return $this->gettext_normalized; } } includes/queries/class-gettext-delete.php 0000777 00000004203 15251156640 0014602 0 ustar 00 <?php /** * Class TRP_Gettext_Normalization * * Queries for inserting and updating strings in gettext tables * * To access this component use: * $trp_regular_delete = new TRP_Regular_Delete( ); * */ class TRP_Gettext_Delete extends TRP_Query { public $db; protected $settings; protected $error_manager; /** * TRP_Query constructor. * * @param $settings */ public function __construct() { global $wpdb; $this->db = $wpdb; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $this->settings = $settings; } public function delete_strings( $original_ids ){ global $wpdb; // Ensure IDs are properly formatted as integers $original_ids = array_map('intval', $original_ids); $ids_placeholder = implode(',', array_fill(0, count($original_ids), '%d')); if ( empty( $original_ids ) ) { return false; } foreach ($this->settings['translation-languages'] as $language_code ){ $dictionary_table = $this->get_gettext_table_name( $language_code ); $wpdb->query( $wpdb->prepare( "DELETE FROM `" . $dictionary_table . "` WHERE original_id IN ($ids_placeholder)", ...$original_ids ) ); } // Delete from wp_trp_original_strings $items_deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM " . $this->get_table_name_for_gettext_original_strings() . " WHERE id IN ($ids_placeholder)", ...$original_ids ) ); // Delete from wp_trp_original_meta $wpdb->query( $wpdb->prepare( "DELETE FROM " . $this->get_table_name_for_gettext_original_meta() . " WHERE original_id IN ($ids_placeholder)", ...$original_ids ) ); return (int)$items_deleted; } } includes/queries/class-gettext-insert-update.php 0000777 00000030207 15251156640 0016127 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Gettext_Normalization * * Queries for inserting and updating strings in gettext tables * * To access this component use: * $trp = TRP_Translate_Press::get_trp_instance(); * $trp_query = $trp->get_component( 'query' ); * $gettext_insert_update = $trp_query->get_query_component('gettext_insert_update'); * */ class TRP_Gettext_Insert_Update extends TRP_Query { public $db; protected $settings; protected $error_manager; /** * TRP_Query constructor. * * @param $settings */ public function __construct( $settings ) { global $wpdb; $this->db = $wpdb; $this->settings = $settings; } /** * Inserts gettext strings in trp_gettext_{language_code} table and trp_gettext_original_strings table * * @param $new_strings * @param $language_code * * @return int|null */ public function insert_gettext_strings( $new_strings, $language_code ) { if ( count( $new_strings ) == 0 ) { return; } $query = "INSERT INTO `" . sanitize_text_field( $this->get_gettext_table_name( $language_code ) ) . "` ( original, translated, domain, status, plural_form, original_id ) VALUES "; $values = array(); $place_holders = array(); $original_ids = $this->gettext_original_strings_sync( $new_strings ); foreach ( $new_strings as $key => $string ) { //make sure we don't insert empty strings in db if ( empty( $string['original'] ) ) { continue; } if ( $string['original'] == $string['translated'] || $string['original_plural'] == $string['translated'] || $string['translated'] == '' ) { $translated = null; $status = self::NOT_TRANSLATED; } else { $translated = $string['translated']; $status = self::HUMAN_REVIEWED; } // Skip if original_id doesn't exist for this key if ( !isset( $original_ids[ $key ] ) ) { continue; } array_push( $values, $string['original'], $translated, $string['domain'], $status, $string['plural_form'], $original_ids[ $key ] ); $place_holders[] = "( '%s', '%s', '%s', '%d', '%d', '%d')"; } if ( empty( $values ) ) return null; $query .= implode( ', ', $place_holders ); $this->db->query( $this->db->prepare( $query . ' ', $values ) ); $this->maybe_record_automatic_translation_error( array( 'details' => 'Error running insert_gettext_strings()' ) ); if ( count( $new_strings ) == 1 ) { return $this->db->insert_id; } else { return null; } } /** * Returns originals table ids of $new_strings * * Also inserts in gettext_original_strings table if strings not found * * @param $language_code * @param $new_strings * * @return array|object|null * * * How to call this function? * You should create an array of arrays that have id, original, domain and possible context: * * $gettext_with_null_original_id_array[] = array( 'original' => $current_language_string['tt_original'], 'id' => $current_language_string['id'], 'domain' => $current_language_string['tt_domain'], 'context' => $current_language_string['tt_context'], //optional ); * * than: * * foreach ($gettext_with_null_original_id_array as $item) { $original_ids_null_context_false[] = $item; } * * if the context is null call the function like this: * $original_ids_null = $gettext_insert_update->gettext_original_strings_sync($original_ids_null_context_false, false); * * else call it without the second argument (or with the second argument true): * * $original_ids_null = $gettext_insert_update->gettext_original_strings_sync($original_ids_null_context_false); * * or: * * $original_ids_null = $gettext_insert_update->gettext_original_strings_sync($original_ids_null_context_false, true); */ public function gettext_original_strings_sync( $new_strings, $use_context = true ) { if ( count( $new_strings ) === 0 ) { return array(); } $new_strings_in_dictionary_with_original_id = array(); $insert_strings = array(); $originals_table = $this->get_table_name_for_gettext_original_strings(); $possible_new_strings = array(); foreach ( $new_strings as $string ) { $possible_new_strings[] = $this->db->prepare( "%s", $string['original'] ); } // query for originals disregarding domain. Later, only the ones matching the domain too get selected. $existing_strings = $this->db->get_results( "SELECT id, original, domain, context FROM `$originals_table` WHERE BINARY $originals_table.original IN (" . implode( ',', $possible_new_strings ) . ")", ARRAY_A ); // filtering queried strings to match exact domain and context. If not found in db, prepare for inserting. At the same time, prepare ids for return if ( ! empty( $existing_strings ) ) { foreach ( $new_strings as $key => $new_string ) { foreach ( $existing_strings as $existing_string ) { if ( $existing_string['original'] === $new_string['original'] && $existing_string['domain'] === $new_string['domain']){ if ($use_context) { if ($existing_string['context'] === $new_string['context']) { $new_strings_in_dictionary_with_original_id[$key] = $existing_string['id']; break; } }else { $new_strings_in_dictionary_with_original_id[$key] = $existing_string['id']; break; } } } if ( ! isset( $new_strings_in_dictionary_with_original_id[ $key ] ) ) { $insert_strings[] = $new_string; } } } else { $insert_strings = $new_strings; } if ( ! empty( $insert_strings ) ) { foreach ( $insert_strings as $k => $string ) { $insert_strings[ $k ] = $this->db->prepare( "( '%s', '%s', '%s', '%s')", $string['original'], $string['domain'], $string['context'], $string['original_plural'] ); } //insert the strings that are missing $this->db->query( "INSERT INTO `$originals_table` (original, domain, context, original_plural) VALUES " . implode( ',', $insert_strings ) ); //get the ids for inserted the new strings (new in dictionary) $new_strings_inserted = $this->db->get_results( "SELECT id, original, domain, context FROM `$originals_table` WHERE BINARY $originals_table.original IN (" . implode( ',', $possible_new_strings ) . ")", OBJECT_K ); // filtering queried strings to match exact domain and context foreach ( $new_strings as $key => $new_string ) { foreach ( $new_strings_inserted as $new_string_inserted ) { if ( $new_string_inserted->original === $new_string['original'] && $new_string_inserted->domain === $new_string['domain'] && $new_string_inserted->context === $new_string['context'] ) { $new_strings_in_dictionary_with_original_id[ $key ] = $new_string_inserted->id; break; } } } } return $new_strings_in_dictionary_with_original_id; } /** * Update gettext strings in trp_gettext_{language_code} table * * @param $updated_strings * @param $language_code * @param $columns_to_update array Only update specified columns * * @return void * * * How to call this functions? * The id and original need to be in the call as arguments, after them you can add any values you want to update: * $gettext_insert_update->update_gettext_strings( array( array( 'id' => $gettext_with_null_original_id_array[$key]['id'], //id 'original' => $gettext_with_null_original_id_array[$key]['original'], //original 'original_id' => $value, // any argument you want to update in the gettext table ) ), $current_language, //a var that contains the language of the gettext table you want to update * array('id', 'original', 'original_id') ); // an array where you write the values you passed as arguments, possible arguments: * 'id','original','translated','domain','status','original_id','plural_form' */ public function update_gettext_strings( $updated_strings, $language_code, $columns_to_update = array('id','original','translated','domain','status','plural_form')) { if ( count( $updated_strings ) == 0 ) { return; } $placeholder_array_mapping = array( 'id' => '%d', 'original' => '%s', 'translated' => '%s', 'domain' => '%s', 'status' => '%d', 'original_id' => '%d', 'plural_form' => '%d' ); $columns_query_part = ''; foreach ( $columns_to_update as $column ) { $columns_query_part .= $column . ','; $placeholders[] = $placeholder_array_mapping[ $column ]; } $columns_query_part = rtrim( $columns_query_part, ',' ); $query = "INSERT INTO `" . sanitize_text_field( $this->get_gettext_table_name( $language_code ) ) . "` ( " . $columns_query_part . " ) VALUES "; $values = array(); $place_holders = array(); $placeholders_query_part = '('; foreach ( $placeholders as $placeholder ) { $placeholders_query_part .= "'" . $placeholder . "',"; } $placeholders_query_part = rtrim( $placeholders_query_part, ',' ); $placeholders_query_part .= ')'; $update_id_and_original = in_array( 'id', $columns_to_update ) && in_array( 'original', $columns_to_update ); foreach ( $updated_strings as $string ) { if ( ! $update_id_and_original || ( ! empty( $string['id'] ) && is_numeric( $string['id'] ) && ! empty( $string['original'] ) ) ) { //we must have an ID and an original if columns to update include id and original $string['status'] = ! empty( $string['status'] ) ? $string['status'] : self::NOT_TRANSLATED; foreach ( $columns_to_update as $column ) { array_push( $values, $string[ $column ] ); } $place_holders[] = $placeholders_query_part; } } if ( empty( $place_holders ) || empty( $values ) ) { return; } $on_duplicate = ' ON DUPLICATE KEY UPDATE '; $key_term_values = $this->is_values_accepted() ? 'VALUES' : 'VALUE'; foreach ( $columns_to_update as $column ) { if ( $column == 'id' ) { continue; } $on_duplicate .= $column . '=' . $key_term_values . '(' . $column . '),'; } $query .= implode( ', ', $place_holders ); $on_duplicate = rtrim( $on_duplicate, ',' ); $query .= $on_duplicate; $this->db->query( $this->db->prepare( $query . ' ', $values ) ); $this->maybe_record_automatic_translation_error( array( 'details' => 'Error running update_gettext_strings()' ) ); } /** * Insert in the DB gettext_original_meta the pair meta_key = meta_value for all original ids * * If exact pair meta_key = meta_value exists then skip inserting * * @param $original_ids * @param $meta_key * @param $meta_value * * @return void */ public function bulk_insert_original_id_meta( $original_ids, $meta_key, $meta_value ) { $meta_key = sanitize_text_field( $meta_key ); $meta_value = sanitize_text_field( $meta_value ); if ( ! empty( $original_ids ) ) { $original_id_values = array(); foreach ( $original_ids as $key => $original_id ) { $original_ids[$key] = (int)$original_id; $original_id_values[] = $this->db->prepare( "%d", $original_id ); } // if an original_id exists with the same meta_key and meta_value then skip insert $existing_entries = $this->db->get_results( $this->db->prepare( "SELECT original_id FROM " . $this->get_table_name_for_gettext_original_meta() . " WHERE meta_key = '" . $meta_key . "' AND meta_value = '" . $meta_value . "' AND original_id IN ( %2s )", implode( ', ', $original_id_values ) ), OBJECT_K ); $existing_entries = array_keys($existing_entries); $insert_this = array_unique( array_diff( $original_ids, $existing_entries ) ); if ( ! empty( $insert_this ) ) { $insert_values = array(); foreach ( $insert_this as $missing_entry ) { $insert_values[] = $this->db->prepare( "( %d, %s, %s )", $missing_entry, $meta_key, $meta_value ); } $this->db->query( "INSERT INTO " . $this->get_table_name_for_gettext_original_meta() . " ( original_id, meta_key, meta_value ) VALUES " . implode( ', ', $insert_values ) ); } } } } includes/queries/class-gettext-normalization.php 0000777 00000013214 15251156640 0016230 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Gettext_Normalization * * Queries for transitioning to normalized gettext table structure * * To access this component use: * $trp = TRP_Translate_Press::get_trp_instance(); * $trp_query = $trp->get_component( 'query' ); * $gettext_normalization = $trp_query->get_query_component('gettext_normalization'); * */ class TRP_Gettext_Normalization extends TRP_Query { public $db; protected $settings; protected $error_manager; /** * TRP_Query constructor. * @param $settings */ public function __construct( $settings ){ global $wpdb; $this->db = $wpdb; $this->settings = $settings; } /** * Add original_id, plural_form column to gettext tables, if it doesn't exist. * * Affects all existing tables, including deactivated languages * * @param null $language_code */ public function check_for_gettext_original_id_column($language_code = null){ if ( $language_code ){ // check only this language $array_of_table_names = array( $this->get_gettext_table_name( $language_code ) ); }else { // check all languages, including deactivated ones $array_of_table_names = $this->get_all_gettext_table_names(); } foreach( $array_of_table_names as $table_name ){ if ( ! $this->table_column_exists( $table_name, 'original_id' ) ) { $this->db->query("ALTER TABLE " . $table_name . " ADD original_id BIGINT(20) DEFAULT NULL" ); } if ( ! $this->table_column_exists( $table_name, 'plural_form' ) ) { $this->db->query("ALTER TABLE " . $table_name . " ADD plural_form INT(20) DEFAULT NULL" ); } } } /** * Function that takes care of inserting original strings from gettext to gettext_original_strings table */ public function gettext_original_ids_insert( $language_code, $inferior_limit, $batch_size ){ if( !$this->error_manager ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $originals_table = $this->get_table_name_for_gettext_original_strings(); $table_name = sanitize_text_field( $this->get_gettext_table_name( $language_code ) ); /* * select all string that are in the dictionary table and are not in the original tables and insert them in the original */ $insert_records = $this->db->query( $this->db->prepare( "INSERT INTO `$originals_table` (original, domain) SELECT DISTINCT ( BINARY t1.original ), t1.domain FROM `$table_name` t1 LEFT JOIN `$originals_table` t2 ON ( t2.original = t1.original AND t2.original = BINARY t1.original AND t2.domain = t1.domain ) WHERE t2.original IS NULL AND t2.domain IS NULL AND t1.domain != '' AND t1.original != '' AND t1.id > %d AND t1.id <= %d AND LENGTH(t1.original) < 20000", $inferior_limit, ($inferior_limit + $batch_size) ) ); if (!empty($this->db->last_error)) { $this->error_manager->record_error(array('last_error_insert_gettext_original_strings' => $this->db->last_error)); } return $insert_records; } /** * Function that makes sure we don't have duplicates in gettext_original_strings table * It is executed after we have inserted all the strings */ public function gettext_original_ids_cleanup(){ if( !$this->error_manager ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $originals_table = $this->get_table_name_for_gettext_original_strings(); $charset_collate = $this->db->get_charset_collate(); $charset = "utf8mb4"; if( strpos( 'latin1', $charset_collate ) === 0 ) $charset = "latin1"; $this->db->query( "DELETE t1 FROM `$originals_table` t1 INNER JOIN `$originals_table` t2 WHERE t1.id > t2.id AND t1.domain = t2.domain AND t1.original COLLATE ".$charset."_bin = t2.original" ); if (!empty($this->db->last_error)) { $this->error_manager->record_error(array('last_error_cleaning_gettext_original_strings' => $this->db->last_error)); } } /** * Function that takes care of synchronizing the gettext with the gettext original table by inserting the original * ids in the original_id column */ public function gettext_original_ids_reindex( $language_code, $inferior_limit, $batch_size ){ if( !$this->error_manager ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->error_manager = $trp->get_component( 'error_manager' ); } $originals_table = $this->get_table_name_for_gettext_original_strings(); $table_name = sanitize_text_field( $this->get_gettext_table_name( $language_code ) ); $charset_collate = $this->db->get_charset_collate(); $charset = "utf8mb4"; if( strpos( 'latin1', $charset_collate ) === 0 ) $charset = "latin1"; /* * perform a UPDATE JOIN with the original table https://www.mysqltutorial.org/mysql-update-join/ */ $update_records = $this->db->query( $this->db->prepare( "UPDATE $table_name, $originals_table SET $table_name.original_id = $originals_table.id WHERE $originals_table.domain = $table_name.domain AND $table_name.original COLLATE ". $charset ."_bin = $originals_table.original AND $table_name.id > %d AND $table_name.id <= %d", $inferior_limit, ($inferior_limit + $batch_size) ) ); if (!empty($this->db->last_error)) { $this->error_manager->record_error(array('last_error_reindex_gettext_original_ids' => $this->db->last_error)); } return $update_records; } } includes/queries/class-regular-delete.php 0000777 00000004340 15251156640 0014561 0 ustar 00 <?php /** * Class TRP_Gettext_Normalization * * Queries for inserting and updating strings in gettext tables * * To access this component use: * $trp_regular_delete = new TRP_Regular_Delete( ); * */ class TRP_Regular_Delete extends TRP_Query { public $db; protected $settings; protected $error_manager; /** * TRP_Query constructor. * * @param $settings */ public function __construct() { global $wpdb; $this->db = $wpdb; $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $this->settings = $settings; } public function delete_strings( $original_ids ){ global $wpdb; // Ensure IDs are properly formatted as integers $original_ids = array_map('intval', $original_ids); $ids_placeholder = implode(',', array_fill(0, count($original_ids), '%d')); if ( empty( $original_ids ) ) { return false; } foreach ($this->settings['translation-languages'] as $language_code ){ if ( $this->settings['default-language'] == $language_code ){ continue; } $dictionary_table = $this->get_table_name( $language_code ); $wpdb->query( $wpdb->prepare( "DELETE FROM `" . $dictionary_table . "` WHERE original_id IN ($ids_placeholder)", ...$original_ids ) ); } // Delete from wp_trp_original_strings $items_deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM " . $this->get_table_name_for_original_strings() . " WHERE id IN ($ids_placeholder)", ...$original_ids ) ); // Delete from wp_trp_original_meta $wpdb->query( $wpdb->prepare( "DELETE FROM " . $this->get_table_name_for_original_meta() . " WHERE original_id IN ($ids_placeholder)", ...$original_ids ) ); return (int)$items_deleted; } } includes/class-translation-manager.php 0000777 00000171515 15251156640 0014162 0 ustar 00 <?php // Exit if accessed directly if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Translation_Manager * * Handles Front-end Translation Editor, including Ajax requests. */ class TRP_Translation_Manager { protected $settings; protected $url_converter; /** * TRP_Translation_Manager constructor. * * @param array $settings Settings option. */ public function __construct( $settings ) { $this->settings = $settings; } /** * Function that determines if an ajax request came from the frontend * * Moved to TRP_Gettext_Manager. Keeping it in case there is third pary code that uses this * @return bool */ static function is_ajax_on_frontend() { return TRP_Gettext_Manager::is_ajax_on_frontend(); } /** * function that strips the gettext tags from a string * * Moved to TRP_Gettext_Manager. Keeping it in case third party uses it. * @param $string * @return mixed */ static function strip_gettext_tags( $string ) { return TRP_Gettext_Manager::strip_gettext_tags($string); } /** * Returns boolean whether current page is part of the Translation Editor. * * @param string $mode 'true' | 'preview' * @return bool Whether current page is part of the Translation Editor. */ protected function conditions_met( $mode = 'true' ) { if ( isset( $_REQUEST['trp-edit-translation'] ) && sanitize_text_field( $_REQUEST['trp-edit-translation'] ) == $mode ) { if ( current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) && !is_admin() ) { return true; } elseif ( sanitize_text_field( $_REQUEST['trp-edit-translation'] ) == "preview" ) { return true; } else { wp_die( '<h1>' . esc_html__( 'Cheatin’ uh?' ) . '</h1>' . //phpcs:ignore WordPress.WP.I18n.MissingArgDomain '<p>' . esc_html__( 'Sorry, you are not allowed to access this page.' ) . '</p>', //phpcs:ignore WordPress.WP.I18n.MissingArgDomain 403 ); } } return false; } /** * Start Translation Editor. * * Hooked to template_include. * * @param string $page_template Current page template. * @return string Template for translation Editor. */ public function translation_editor( $page_template ) { if ( !$this->conditions_met() ) { return $page_template; } return TRP_PLUGIN_DIR . 'partials/translation-manager.php'; } public function get_merge_rules() { $localized_text = $this->string_groups(); $merge_rules = array( 'top_parents' => array( 'p', 'div', 'li', 'ol', 'ul', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7', 'body', 'footer', 'article', 'main', 'iframe', 'section', 'figure', 'figcaption', 'blockquote', 'cite', 'tr', 'td', 'th', 'table', 'tbody', 'thead', 'tfoot', 'form', 'label' ), 'self_object_type' => array( 'translate-press' ), 'incompatible_siblings' => array( '[data-trpgettextoriginal]', '[data-trp-node-group="' . $localized_text['dynamicstrings'] . '"]' ) ); return apply_filters( 'trp_merge_rules', $merge_rules ); } public function localized_text() { $update_seo_add_on = ( class_exists( 'TRP_Seo_Pack' ) && !defined( 'TRP_SP_PLUGIN_VERSION' ) ); return $this->string_groups() + array( // attribute names 'src' => esc_html__( 'Source', 'translatepress-multilingual' ), 'srcset' => esc_html__( 'Srcset', 'translatepress-multilingual' ), 'alt' => esc_html__( 'Alt attribute', 'translatepress-multilingual' ), 'title' => esc_html__( 'Title attribute', 'translatepress-multilingual' ), 'href' => esc_html__( 'Anchor link', 'translatepress-multilingual' ), 'placeholder' => esc_html__( 'Placeholder attribute', 'translatepress-multilingual' ), 'submit' => esc_html__( 'Submit attribute', 'translatepress-multilingual' ), 'text' => esc_html__( 'Text', 'translatepress-multilingual' ), 'poster' => esc_html__( 'Video Poster', 'translatepress-multilingual' ), // plural form name variants 'plural_form_text' => esc_html__( 'plural form', 'translatepress-multilingual' ), 'plural_form_one' => esc_html__( 'one', 'translatepress-multilingual' ), 'plural_form_few' => esc_html__( 'few', 'translatepress-multilingual' ), 'plural_form_many' => esc_html__( 'many', 'translatepress-multilingual' ), 'plural_form_other' => esc_html__( 'other', 'translatepress-multilingual' ), 'saved' => esc_html__( 'Saved', 'translatepress-multilingual' ), 'save_translation' => esc_html__( 'Save', 'translatepress-multilingual' ), 'saving_translation' => esc_html__( 'Saving translation...', 'translatepress-multilingual' ), 'unsaved_changes' => esc_html__( 'You have unsaved changes!', 'translatepress-multilingual' ), 'discard' => esc_html__( 'Discard changes', 'translatepress-multilingual' ), 'discard_all' => esc_html__( 'Discard All', 'translatepress-multilingual' ), 'strings_loading' => esc_attr__( 'Loading Strings...', 'translatepress-multilingual' ), 'select_string' => esc_attr__( 'Select string to translate...', 'translatepress-multilingual' ), 'close' => esc_attr__( 'Close Editor', 'translatepress-multilingual' ), 'from' => esc_html__( 'From', 'translatepress-multilingual' ), 'to' => esc_html__( 'To', 'translatepress-multilingual' ), 'add_media' => esc_html__( 'Add Media', 'translatepress-multilingual' ), 'other_lang' => esc_html__( 'Other languages', 'translatepress-multilingual' ), 'context' => esc_html__( 'Context', 'translatepress-multilingual' ), 'view_as' => esc_html__( 'View Website As', 'translatepress-multilingual' ), 'view_as_pro' => esc_html__( 'Available in our Pro Versions', 'translatepress-multilingual' ), //wp media upload 'select_or_upload' => esc_html__( 'Select or Upload Media', 'translatepress-multilingual' ), 'use_this_media' => esc_html__( 'Use this media', 'translatepress-multilingual' ), // title attributes 'edit' => esc_attr__( 'Translate', 'translatepress-multilingual' ), 'merge' => esc_attr__( 'Translate entire block element', 'translatepress-multilingual' ), 'split' => esc_attr__( 'Split block to translate strings individually', 'translatepress-multilingual' ), 'save_title_attr' => esc_attr__( 'Save changes to translation. Shortcut: CTRL(⌘) + S', 'translatepress-multilingual' ), 'next_title_attr' => esc_attr__( 'Navigate to next string in dropdown list. Shortcut: CTRL(⌘) + ALT + Right Arrow', 'translatepress-multilingual' ), 'previous_title_attr' => esc_attr__( 'Navigate to previous string in dropdown list. Shortcut: CTRL(⌘) + ALT + Left Arrow', 'translatepress-multilingual' ), 'discard_all_title_attr' => esc_attr__( 'Discard all changes. Shortcut: CTRL(⌘) + ALT + Z', 'translatepress-multilingual' ), 'discard_individual_changes_title_attribute' => esc_attr__( 'Discard changes to this text box. To discard changes to all text boxes use shortcut: CTRL(⌘) + ALT + Z', 'translatepress-multilingual' ), 'dismiss_tooltip_title_attribute' => esc_attr__( 'Dismiss tooltip', 'translatepress-multilingual' ), 'quick_intro_title_attribute' => esc_attr__( 'Quick Intro', 'translatepress-multilingual' ), 'split_confirmation' => esc_js( __( 'Are you sure you want to split this phrase into smaller parts?', 'translatepress-multilingual' ) ), 'translation_not_loaded_yet' => wp_kses( __( 'This string is not ready for translation yet. <br>Try again in a moment...', 'translatepress-multilingual' ), array( 'br' => array() ) ), 'bor_update_notice' => esc_js( __( 'For this option to work, please update the Browse as other role add-on to the latest version.', 'translatepress-multilingual' ) ), 'seo_update_notice' => ( $update_seo_add_on ) ? esc_js( __( 'To translate slugs, please update the SEO Pack add-on to the latest version.', 'translatepress-multilingual' ) ) : 'seo_pack_update_not_needed', //Notice when the user has not defined a secondary language 'extra_lang_row1' => wp_kses( sprintf( __( 'You can add a new language from <a href="%s">Settings->TranslatePress</a>', 'translatepress-multilingual' ), esc_url( admin_url( 'options-general.php?page=translate-press' ) ) ), array( 'a' => [ 'href' => [] ] ) ), 'extra_lang_row2' => wp_kses( __( 'However, you can still use TranslatePress to <strong style="background: #f5fb9d;">modify gettext strings</strong> available in your page.', 'translatepress-multilingual' ), array( 'strong' => [ 'style' => [] ] ) ), 'extra_lang_row3' => esc_html__( 'Strings that are user-created cannot be modified, only those from themes and plugins.', 'translatepress-multilingual' ), //Pro version upselling 'extra_upsell_title' => esc_html__( 'Extra Translation Features', 'translatepress-multilingual' ), 'extra_upsell_row1' => esc_html__( 'Support for 130+ Extra Languages', 'translatepress-multilingual' ), 'extra_upsell_row2' => esc_html__( 'Access to TranslatePress AI', 'translatepress-multilingual' ), 'extra_upsell_row3' => esc_html__( 'Translate SEO Title, Description, Slug', 'translatepress-multilingual' ), 'extra_upsell_row4' => esc_html__( 'Publish only when translation is complete', 'translatepress-multilingual' ), 'extra_upsell_row5' => esc_html__( 'Translate by Browsing as User Role', 'translatepress-multilingual' ), 'extra_upsell_row6' => esc_html__( 'Different Menu Items for each Language', 'translatepress-multilingual' ), 'extra_upsell_row7' => esc_html__( 'Automatic User Language Detection', 'translatepress-multilingual' ), //[utm30] 'extra_upsell_button' => wp_kses( sprintf( '<a class="button-primary" target="_blank" href="%s">%s</a>', esc_url( trp_add_affiliate_id_to_link( 'https://translatepress.com/pricing/?utm_source=tp-editor&utm_medium=client-site&utm_campaign=tp-editor-upsell' ) ), __( 'Upgrade to PRO', 'translatepress-multilingual' ) ), array( 'a' => [ 'class' => [], 'target' => [], 'href' => [] ] ) ), // Black Friday 'extra_upsell_bf_row1' => esc_html__( 'Upgrade to PRO with our biggest discount of the year!', 'translatepress-multilingual' ), 'extra_upsell_bf_row2' => esc_html__( 'This Black Friday, get access to these features and more at a fraction of the costs:', 'translatepress-multilingual' ), //[utm31] 'extra_upsell_bf_button' => wp_kses( sprintf( '<a class="button-primary" target="_blank" href="%s">%s</a>', esc_url( trp_add_affiliate_id_to_link( 'https://translatepress.com/black-friday/?utm_source=tp-editor&utm_medium=client-site&utm_campaign=bf-2025' ) ), __( 'Upgrade to PRO', 'translatepress-multilingual' ) ), array( 'a' => [ 'class' => [], 'target' => [], 'href' => [] ] ) ), // Translation Memory 'translation_memory_no_suggestions' => esc_html__( 'No available suggestions', 'translatepress-multilingual' ), 'translation_memory_suggestions' => esc_html__( 'Suggestions from translation memory', 'translatepress-multilingual' ), 'translation_memory_click_to_copy' => esc_html__( 'Click to Copy', 'translatepress-multilingual' ), //human or machine translation tooltips 'human_translation' => esc_html__('Human Translation', 'translatepress-multilingual'), 'machine_translation' => esc_html__('Machine Translation', 'translatepress-multilingual'), 'percentage_bar' => array( 'tooltip_text_default' => esc_html__( 'Text on this page is %s% translated into all languages.', 'translatepress-multilingual'), 'tooltip_text_general' => esc_html__( '%1$s% of text on this page is translated into %2$s.', 'translatepress-multilingual'), 'minibar_text' => esc_html__('This page is %1$s% translated into %2$s.', 'translatepress-multilingual') ), 'multiple_types_alert' => esc_html__( "The slug that you are trying to edit is present in other slug types:%s%. Editing it will replace each occurrence, regardless of the current type.", 'translatepress-multilingual') ); } public function get_help_panel_content() { $edit_icon = '<svg class="trp-edit-icon-inline" xmlns="http://www.w3.org/2000/svg" visibility="hidden" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><path d="M20.1 5.1L16.9 2 6.2 12.7l-1.3 4.4 4.5-1.3L20.1 5.1zM4 20.8h8v-1.5H4v1.5z"></path></svg>'; return apply_filters( 'trp_help_panel_content', array( array( 'title' => esc_html__( 'Quick Intro', 'translatepress-multilingual' ), 'content' => wp_kses(sprintf( __( 'Hover any text on the page, click %s,<br> then modify the translation in the sidebar.', 'translatepress-multilingual' ), $edit_icon), array( 'svg' => array( 'class' => array(),'xmlns' => array(),'visibility'=>array(), 'viewbox' => array(), 'aria-hidden' => array(), 'focusable' => array() ), 'path' => array('d'=>array() ), "br" => array() ) ), 'event' => 'trp_hover_text_help_panel' ), array( 'title' => esc_html__( 'Quick Intro', 'translatepress-multilingual' ), 'content' => wp_kses( __( 'Don\'t forget to Save Translation. Use keyboard shortcut CTRL(⌘) + S', 'translatepress-multilingual' ), array() ), 'event' => 'trp_save_translation_help_panel' ), array( 'title' => esc_html__( 'Quick Intro', 'translatepress-multilingual' ), 'content' => wp_kses( __( 'Switch language to see the translation changes directly on the page.', 'translatepress-multilingual' ), array() ), 'event' => 'trp_switch_language_help_panel' ), array( 'title' => esc_html__( 'Quick Intro', 'translatepress-multilingual' ), 'content' => wp_kses( __( 'Search for any text in this page in the dropdown.', 'translatepress-multilingual' ), array() ), 'event' => 'trp_search_string_help_panel' ) ) ); } public function get_license_notice_content(){ $license_notice_content = false; // false will hide the license notice panel // paid version plugin (business/developer/personal) is active $free_version = !class_exists( 'TRP_Handle_Included_Addons' ); if ( !$free_version ){ $license_status = trp_get_license_status(); if ( $license_status != 'valid' && $license_status != 'free-version' ) { $translatepress_product = ( defined( 'TRANSLATE_PRESS' ) ) ? TRANSLATE_PRESS : "TranslatePress"; $purchase_text = ''; switch ( $license_status ) { case 'expired': { $status_text = wp_kses( sprintf( __( 'Your %s license has <span class="trp-license-status-emphasized">expired</span>.', 'translatepress-multilingual' ), '<strong>' . $translatepress_product . '</strong>' ), array( 'strong' => array(),'span' => array( 'class' => array() ) ) ); if( trp_bf_show_promotion() ){ $instructions = esc_html__( '<strong>This Black Friday, renew your license at a special price</strong> to continue receiving access to product downloads, automatic updates, and support.', 'translatepress-multilingual' ); $button = esc_html__( 'Get Deal', 'translatepress-multilingual' ); //[utm32] $link = 'https://translatepress.com/account/?utm_source=tp-editor&utm_medium=client-site&utm_campaign=bf-2025-renewal'; } else { $instructions = esc_html__( 'Please renew your license to continue receiving access to TranslatePress AI, premium addons, automatic updates and support.', 'translatepress-multilingual' ); $button = esc_html__( 'Renew Now', 'translatepress-multilingual' ); //[utm33] $link = 'https://translatepress.com/account/?utm_source=tp-editor&utm_medium=client-site&utm_campaign=expired-license'; } break; } case 'revoked': { $status_text = wp_kses( sprintf( __( 'Your %s license was <span class="trp-license-status-emphasized">refunded</span>.', 'translatepress-multilingual' ), '<strong>' . $translatepress_product . '</strong>' ), array( 'strong' => array(),'span' => array( 'class' => array() ) ) ); $instructions = esc_html__( 'Please purchase a new license to continue receiving access to TranslatePress AI, premium addons, automatic updates and support.', 'translatepress-multilingual' ); $button = esc_html__( 'Purchase a new license', 'translatepress-multilingual' ); //[utm34] $link = 'https://translatepress.com/pricing/?utm_source=tp-editor&utm_medium=client-site&utm_campaign=refunded-license'; break; } // case 'missing' : // case 'invalid' : // case 'site_inactive' : // case 'item_name_mismatch' : // case 'no_activations_left': default: { $status_text = wp_kses( sprintf( __( 'Your %s license is <span class="trp-license-status-emphasized">missing or invalid</span>.', 'translatepress-multilingual' ), '<strong>' . $translatepress_product . '</strong>' ), array( 'strong' => array(),'span' => array( 'class' => array() ) ) ); //[utm35] $instructions = sprintf( esc_html__( 'Please enter a valid license to get access to TranslatePress AI, premium addons, automatic updates and support. Need a license key? %1$sPurchase one now%2$s', 'translatepress-multilingual' ), '<a href="https://translatepress.com/pricing/?utm_source=tp-editor&utm_medium=client-site&utm_campaign=pro-no-active-license" target="_blank">', '</a>' ); $button = esc_html__( 'Enter a valid license', 'translatepress-multilingual' ); $link = admin_url( 'admin.php?page=trp_license_key' ); break; } } $button_class = 'trp-license-notice-button'; if( trp_bf_show_promotion() ) $button_class = 'trp-license-notice-button-red'; $license_notice_content = '<p>' . $status_text . '</p><p>' . $instructions . '</p><p><a href="' . esc_url($link) . '" class="button-primary '. esc_attr( $button_class ) .'" target="_blank">' . $button . '</a></p>'; } } return $license_notice_content; } public function get_default_editor_user_meta() { return apply_filters( 'trp_default_editor_user_meta', array( 'helpPanelOpened' => false, 'dismissTooltipSave' => false, 'dismissTooltipNext' => false, 'dismissTooltipPrevious' => false, 'dismissTooltipDismissAll' => false, 'dismissTooltipHumanorMachineTranslation' => false, 'dismissPreviousTabTooltip' => false, ) ); } public function get_editor_user_meta() { $user_meta = get_user_meta( get_current_user_id(), 'trp_editor_user_meta', true ); $user_meta = wp_parse_args( $user_meta, $this->get_default_editor_user_meta() ); return apply_filters( 'trp_editor_user_meta', $user_meta ); } public function save_editor_user_meta() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { check_ajax_referer( 'trp_editor_user_meta', 'security' ); if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_save_editor_user_meta' && !empty( $_POST['user_meta'] ) ) { $submitted_user_meta = json_decode( stripslashes( $_POST['user_meta'] ), true ); /* phpcs:ignore */ /* sanitized bellow */ $existing_user_meta = $this->get_editor_user_meta(); foreach ( $existing_user_meta as $key => $existing ) { if ( isset( $submitted_user_meta[ $key ] ) ) { $existing_user_meta[ $key ] = (bool)$submitted_user_meta[ $key ]; } } update_user_meta( get_current_user_id(), 'trp_editor_user_meta', $existing_user_meta ); } } echo trp_safe_json_encode( array() );//phpcs:ignore die(); } public function string_groups() { $string_groups = array( 'slugs' => esc_html__( 'Slugs', 'translatepress-multilingual' ), 'metainformation' => esc_html__( 'Meta Information', 'translatepress-multilingual' ), 'stringlist' => esc_html__( 'String List', 'translatepress-multilingual' ), 'gettextstrings' => esc_html__( 'Gettext Strings', 'translatepress-multilingual' ), 'images' => esc_html__( 'Images', 'translatepress-multilingual' ), 'videos' => esc_html__( 'Videos', 'translatepress-multilingual' ), 'audios' => esc_html__( 'Audios', 'translatepress-multilingual' ), 'dynamicstrings' => esc_html__( 'Dynamically Added Strings', 'translatepress-multilingual' ), ); return apply_filters( 'trp_string_groups', $string_groups ); } public function editor_nonces() { $nonces = array( 'gettranslationsnonceregular' => wp_create_nonce( 'get_translations' ), 'savetranslationsnonceregular' => wp_create_nonce( 'save_translations' ), 'gettranslationsnoncegettext' => wp_create_nonce( 'gettext_get_translations' ), 'savetranslationsnoncegettext' => wp_create_nonce( 'gettext_save_translations' ), 'gettranslationsnoncepostslug' => wp_create_nonce( 'postslug_get_translations' ), 'savetranslationsnoncepostslug' => wp_create_nonce( 'postslug_save_translations' ), 'splittbnonce' => wp_create_nonce( 'split_translation_block' ), 'mergetbnonce' => wp_create_nonce( 'merge_translation_block' ), 'logged_out' => wp_create_nonce( 'trp_view_aslogged_out' . get_current_user_id() ), 'getsimilarstring' => wp_create_nonce( 'getsimilarstring' ), 'trp_editor_user_meta' => wp_create_nonce( 'trp_editor_user_meta' ), 'scangettextnonce' => wp_create_nonce( 'scangettextnonce' ), 'get_missing_strings' => wp_create_nonce( 'string_translation_get_missing_strings_gettext' ), 'get_strings_by_original_id' => wp_create_nonce( 'string_translation_get_strings_by_original_ids_gettext' ) ); return apply_filters( 'trp_editor_nonces', $nonces ); } /** * Navigation tabs for Website editing, Url Slugs, String Translation * * @return array */ public function get_editors_navigation() { return apply_filters( 'trp_editors_navigation', array( 'show' => true, 'tabs' => array( array( 'handle' => 'visualeditor', 'label' => __( 'Translation Editor', 'translatepress-multilingual' ), 'path' => add_query_arg( 'trp-edit-translation', 'true', home_url() ), 'tooltip' => esc_html__('Edit translations by visually selecting them on each site page', 'translatepress-multilingual') ), array( 'handle' => 'stringtranslation', 'label' => __( 'String Translation', 'translatepress-multilingual' ), 'path' => add_query_arg( 'trp-string-translation', 'true', home_url() ) . '#/slugs/', 'tooltip' => esc_html__('Edit url slug translations, plugins and theme translation (emails, forms etc.)', 'translatepress-multilingual') ) ) ) ); } /** * Enqueue scripts and styles for translation Editor parent window. * * hooked to trp_translation_manager_footer */ public function enqueue_scripts_and_styles() { wp_enqueue_style( 'trp-editor-style', TRP_PLUGIN_URL . 'assets/css/trp-editor.css', array( 'dashicons', 'buttons' ), TRP_PLUGIN_VERSION ); wp_enqueue_script( 'trp-editor', TRP_PLUGIN_URL . 'assets/js/trp-editor.js', array(), TRP_PLUGIN_VERSION ); wp_localize_script( 'trp-editor', 'trp_editor_data', $this->get_trp_editor_data() ); // Show upload media dialog in default language switch_to_locale( $this->settings['default-language'] ); // Necessary for add media button wp_enqueue_media(); // Necessary for add media button wp_print_media_templates(); restore_current_locale(); // Necessary for translate-dom-changes to have a nonce as the same user as the Editor. // The Preview iframe (which loads translate-dom-changes script) can load as logged out which sets an different nonce $nonces = $this->editor_nonces(); wp_add_inline_script( 'trp-editor', 'var trp_dynamic_nonce = "' . $nonces['gettranslationsnonceregular'] . '";' ); $scripts_to_print = apply_filters( 'trp-scripts-for-editor', array( 'jquery', 'jquery-ui-core', 'jquery-effects-core', 'jquery-ui-resizable', 'trp-editor' ) ); $styles_to_print = apply_filters( 'trp-styles-for-editor', array( 'dashicons', 'trp-editor-style', 'media-views', 'imgareaselect', 'buttons' /*'wp-admin', 'common', 'site-icon', 'buttons'*/ ) ); wp_print_scripts( $scripts_to_print ); wp_print_styles( $styles_to_print ); // Necessary for add media button print_footer_scripts(); } /** * Localize all the data needed by the translation editor * * @return array */ public function get_trp_editor_data() { global $TRP_LANGUAGE; $trp = TRP_Translate_Press::get_trp_instance(); $trp_languages = $trp->get_component( 'languages' ); $translation_render = $trp->get_component( 'translation_render' ); $url_converter = $trp->get_component( 'url_converter' ); $language_names = $trp_languages->get_language_names( $this->settings['translation-languages'] ); // move the current language to the beginning of the array $translation_languages = $this->settings['translation-languages']; if ( $TRP_LANGUAGE != $this->settings['default-language'] ) { $current_language_key = array_search( $TRP_LANGUAGE, $this->settings['translation-languages'] ); unset( $translation_languages[ $current_language_key ] ); $translation_languages = array_merge( array( $TRP_LANGUAGE ), array_values( $translation_languages ) ); } $default_language_key = array_search( $this->settings['default-language'], $translation_languages ); unset( $translation_languages[ $default_language_key ] ); $ordered_secondary_languages = array_values( $translation_languages ); $current_language_published = ( in_array( $TRP_LANGUAGE, $this->settings['publish-languages'] ) ); $current_url = $url_converter->cur_page_url(); $selectors = $translation_render->get_accessors_array( '-' ); // suffix selectors such as array( '-alt', '-src', '-title', '-content', '-value', '-placeholder', '-href', '-outertext', '-innertext' ) $selectors[] = ''; // empty string suffix added for using just the base attribute data-trp-translate-id (instead of data-trp-translate-id-alt) $data_attributes = $translation_render->get_base_attribute_selectors(); //setup view_as roles $view_as_roles = array( __( 'Current User', 'translatepress-multilingual' ) => 'current_user', __( 'Logged Out', 'translatepress-multilingual' ) => 'logged_out' ); $all_roles = wp_roles()->roles; if ( !empty( $all_roles ) ) { foreach ( $all_roles as $role ) $view_as_roles[ $role['name'] ] = ''; } $view_as_roles = apply_filters( 'trp_view_as_values', $view_as_roles ); $string_groups = apply_filters( 'trp_string_group_order', array_values( $this->string_groups() ) ); $flags_path = array(); $flags_file_name = array(); foreach ( $this->settings['translation-languages'] as $language_code ) { $default_path = TRP_PLUGIN_URL . 'assets/images/flags/'; $flags_path[ $language_code ] = apply_filters( 'trp_flags_path', $default_path, $language_code ); $default_flag_file_name = $language_code . '.png'; $flags_file_name[ $language_code ] = apply_filters( 'trp_flag_file_name', $default_flag_file_name, $language_code ); } $editors_navigation = $this->get_editors_navigation(); $string_types = array( 'regular', 'gettext', 'postslug' ); $trp_editor_data = array( 'trp_localized_strings' => $this->localized_text(), 'trp_settings' => $this->settings, 'language_names' => $language_names, 'ordered_secondary_languages' => $ordered_secondary_languages, 'current_language' => $TRP_LANGUAGE, 'on_screen_language' => ( isset( $ordered_secondary_languages[0] ) ) ? $ordered_secondary_languages[0] : '', 'view_as_roles' => $view_as_roles, 'url_to_load' => add_query_arg( 'trp-edit-translation', 'preview', $current_url ), 'string_selectors' => $selectors, 'data_attributes' => $data_attributes, 'editor_nonces' => $this->editor_nonces(), 'ajax_url' => apply_filters( 'trp_wp_ajax_url', admin_url( 'admin-ajax.php' ) ), 'string_types' => apply_filters( 'trp_string_types', $string_types ), 'string_group_order' => $string_groups, 'merge_rules' => $this->get_merge_rules(), 'paid_version' => trp_is_paid_version() ? 'true' : 'false', 'black_friday' => trp_bf_show_promotion() ? 'true' : 'false', 'trp_license_status' => trp_get_license_status(), 'flags_path' => $flags_path, 'flags_file_name' => $flags_file_name, 'editors_navigation' => $editors_navigation, 'help_panel_content' => $this->get_help_panel_content(), 'user_meta' => $this->get_editor_user_meta(), 'upgraded_gettext' => ! ( ( get_option( 'trp_updated_database_gettext_original_id_update', 'yes' ) == 'no' ) ), 'notice_upgrade_gettext' => $this->display_notice_to_upgrade_gettext_in_editor(''), 'notice_upgrade_slugs' => $this->display_notice_to_upgrade_slugs_in_editor(''), 'upsale_slugs' => $this->is_seo_pack_active(), 'upsale_slugs_text' => $this->upsale_slugs_text(), 'license_notice_content' => $this->get_license_notice_content() ); // Remove API keys from the array due to it being exposed in JS unset( $trp_editor_data['trp_settings']['trp_machine_translation_settings']['deepl-api-key'] ); unset( $trp_editor_data['trp_settings']['trp_machine_translation_settings']['google-translate-key'] ); return apply_filters( 'trp_editor_data', $trp_editor_data ); } /** * Enqueue scripts and styles for translation Editor preview window. */ public function enqueue_preview_scripts_and_styles() { if ( $this->conditions_met( 'preview' ) ) { wp_enqueue_script( 'trp-translation-manager-preview-script', TRP_PLUGIN_URL . 'assets/js/trp-iframe-preview-script.js', array( 'jquery' ), TRP_PLUGIN_VERSION ); wp_enqueue_style( 'trp-preview-iframe-style', TRP_PLUGIN_URL . 'assets/css/trp-preview-iframe-style.css', array( 'dashicons' ), TRP_PLUGIN_VERSION ); } } /** * Display button to enter translation Editor in admin bar * * Hooked to admin_bar_menu. * * @param $wp_admin_bar */ public function add_shortcut_to_translation_editor( $wp_admin_bar ) { if ( !current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { return; } if ( is_admin() ) { $url = add_query_arg( 'trp-edit-translation', 'true', trailingslashit( home_url() ) ); $title = __( 'Translate Site', 'translatepress-multilingual' ); $url_target = '_blank'; } else { if ( !$this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component( 'url_converter' ); } $url = $this->url_converter->cur_page_url(); $url = apply_filters( 'trp_edit_translation_url', add_query_arg( 'trp-edit-translation', 'true', $url ) ); $title = __( 'Translate Page', 'translatepress-multilingual' ); $url_target = ''; } // Check if no secondary languages are configured $notif_no_language = !isset( $this->settings['translation-languages'] ) || count( $this->settings['translation-languages'] ) <= 1; // Check if user is on the free version $trp = TRP_Translate_Press::get_trp_instance(); $tp_product_name = reset( $trp->tp_product_name ); $is_free_version = ( $tp_product_name === 'TranslatePress' ); // Check license status $license_status = get_option( 'trp_license_status', '' ); // Check if free user does not have a valid license $notif_no_free_license = $is_free_version && $license_status !== 'valid'; // Check if paid user has expired license $notif_invalid_paid_license = !$is_free_version && $license_status !== 'valid'; // Check if TranslatePress AI is configured and has low quota (less than 100 words = 500 characters) $notif_low_ai_quota = false; $ai_words_remaining = 0; $mt_settings = isset( $this->settings['trp_machine_translation_settings'] ) ? $this->settings['trp_machine_translation_settings'] : array(); // Check if TranslatePress AI is the selected engine (regardless of whether auto-translate is enabled) $is_ai_configured = isset( $mt_settings['translation-engine'] ) && $mt_settings['translation-engine'] === 'mtapi'; if ( $is_ai_configured ) { $cached_quota = get_transient( 'trp_mtapi_cached_quota' ); // Quota is stored in characters, 500 characters = ~100 words if ( $cached_quota !== false && is_numeric( $cached_quota ) && $cached_quota < 500 ) { $notif_low_ai_quota = true; $ai_words_remaining = max( 0, floor( $cached_quota / 5 ) ); } } // Get dismissed notifications from user meta $dismissed = get_user_meta( get_current_user_id(), 'trp_dismissed_admin_bar_notifications', true ); if ( !is_array( $dismissed ) ) { $dismissed = array(); } // Reset low_ai_quota dismissal when quota is no longer low (user refilled) if ( !$notif_low_ai_quota && isset( $dismissed['low_ai_quota'] ) ) { unset( $dismissed['low_ai_quota'] ); update_user_meta( get_current_user_id(), 'trp_dismissed_admin_bar_notifications', $dismissed ); } // Count only non-dismissed notifications for the badge $notification_count = 0; if ( $notif_no_language && empty( $dismissed['no_language'] ) ) $notification_count++; if ( $notif_no_free_license && empty( $dismissed['no_free_license'] ) ) $notification_count++; if ( $notif_invalid_paid_license ) $notification_count++; if ( $notif_low_ai_quota && empty( $dismissed['low_ai_quota'] ) ) $notification_count++; // Add notification badge if there are any notifications $notification_badge = ''; $has_notifications = $notification_count > 0; if ( $has_notifications ) { $notification_badge = '<span class="trp-notification-badge">' . $notification_count . '</span>'; } $wp_admin_bar->add_node( array( 'id' => 'trp_edit_translation', 'title' => '<span class="ab-icon"></span><span class="ab-label">' . $title . $notification_badge . '</span>', 'href' => $url, 'meta' => array( 'class' => 'trp-edit-translation' . ( $has_notifications ? ' trp-needs-setup' : '' ), 'target' => $url_target ) ) ); // Add setup item if no secondary language is configured if ( $notif_no_language ) { $no_language_badge = empty( $dismissed['no_language'] ) ? '<span class="trp-notification-badge">1</span>' : ''; $wp_admin_bar->add_node( array( 'id' => 'trp_add_language', 'title' => __( 'Add a New Language', 'translatepress-multilingual' ) . $no_language_badge, 'href' => add_query_arg( 'trp-dismiss-notif', 'no_language', admin_url( 'options-general.php?page=translate-press' ) ), 'parent' => 'trp_edit_translation', 'meta' => array( 'class' => 'trp-add-language' ) ) ); } // Add free AI license item for free users without a valid license if ( $notif_no_free_license ) { $no_free_license_badge = empty( $dismissed['no_free_license'] ) ? '<span class="trp-notification-badge">1</span>' : ''; $wp_admin_bar->add_node( array( 'id' => 'trp_auto_translate_setup', 'title' => __( 'Get a Free AI License', 'translatepress-multilingual' ) . $no_free_license_badge, 'href' => add_query_arg( 'trp-dismiss-notif', 'no_free_license', admin_url( 'admin.php?page=trp_machine_translation' ) ), 'parent' => 'trp_edit_translation', 'meta' => array( 'class' => 'trp-auto-translate-setup' ) ) ); } // Add license renewal item for paid users with expired license if ( $notif_invalid_paid_license ) { $wp_admin_bar->add_node( array( 'id' => 'trp_renew_license', 'title' => __( 'Your License is Invalid', 'translatepress-multilingual' ) . '<span class="trp-notification-badge">1</span>', 'href' => admin_url( 'admin.php?page=trp_license_key' ), 'parent' => 'trp_edit_translation', 'meta' => array( 'class' => 'trp-renew-license' ) ) ); } // Add low AI quota warning if ( $notif_low_ai_quota ) { $low_ai_quota_badge = empty( $dismissed['low_ai_quota'] ) ? '<span class="trp-notification-badge">1</span>' : ''; /* translators: %d is the number of AI words remaining */ $low_quota_text = sprintf( __( 'Get More AI Words (%d left)', 'translatepress-multilingual' ), $ai_words_remaining ); $wp_admin_bar->add_node( array( 'id' => 'trp_low_ai_quota', 'title' => $low_quota_text . $low_ai_quota_badge, 'href' => add_query_arg( 'trp-dismiss-notif', 'low_ai_quota', admin_url( 'admin.php?page=trp_machine_translation' ) ), 'parent' => 'trp_edit_translation', 'meta' => array( 'class' => 'trp-low-ai-quota' ) ) ); } $wp_admin_bar->add_node( array( 'id' => 'trp_settings_page', 'title' => __( 'Settings', 'translatepress-multilingual' ), 'href' => admin_url( 'options-general.php?page=translate-press' ), 'parent' => 'trp_edit_translation', 'meta' => array( 'class' => 'trp-settings-page' ) ) ); } /** * Dismiss an admin bar notification when the user clicks through via trp-dismiss-notif param. * * Hooked to admin_init. */ public function maybe_dismiss_admin_bar_notification() { if ( !isset( $_GET['trp-dismiss-notif'] ) || !current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { return; } $notif_key = sanitize_text_field( $_GET['trp-dismiss-notif'] ); $valid_keys = array( 'no_language', 'no_free_license', 'low_ai_quota' ); if ( !in_array( $notif_key, $valid_keys, true ) ) { return; } $dismissed = get_user_meta( get_current_user_id(), 'trp_dismissed_admin_bar_notifications', true ); if ( !is_array( $dismissed ) ) { $dismissed = array(); } $dismissed[ $notif_key ] = true; update_user_meta( get_current_user_id(), 'trp_dismissed_admin_bar_notifications', $dismissed ); } /** * adds shortcut to trp editor in gutenberg editor */ function trp_add_shortcut_to_trp_editor_gutenberg(){ wp_enqueue_script( 'custom-link-in-toolbar', TRP_PLUGIN_URL. '/assets/js/trp-gutenberg-editor-shortcut.js', array("jquery"), TRP_PLUGIN_VERSION, true ); wp_localize_script( 'custom-link-in-toolbar', 'trp_localized', array( 'dont_adjust_width' => apply_filters( 'trp_dont_adjust_width_of_ls_in_gutenberg', false ) ) ); $trp = TRP_Translate_Press::get_trp_instance(); $url_converter = $trp->get_component('url_converter'); //$settings = $trp->get_component('settings'); global $post; global $TRP_LANGUAGE; $url_translation_editor = array(); add_filter('trp_add_language_to_home_url_check_for_admin', '__return_false'); if ($post) { $trp_permalink_post = $url_converter->get_url_for_language( $TRP_LANGUAGE, get_permalink( $post->ID ) ); if ( $post->post_status !== "publish" ) { $trp_permalink_post = $url_converter->get_url_for_language( $TRP_LANGUAGE, get_preview_post_link( $post->ID ) ); } }else{ $trp_permalink_post = $url_converter->get_url_for_language( $TRP_LANGUAGE, home_url() ); } $url_translation_editor = apply_filters('trp_edit_translation_url', add_query_arg('trp-edit-translation', 'true', $trp_permalink_post)); $title = esc_attr__('Opens post in the translation editor. Post must be saved as draft or published beforehand.', 'translatepress-multilingual'); $trp_editor_button[0] = "<a id='trp-link-id' class='components-button' href='" . esc_url($url_translation_editor) ."' title='" . $title ."' ><button class='button-primary' style='height: 33px'>" . esc_html__('Translate Page', 'translatepress-multilingual') ."</button></a>"; wp_localize_script('custom-link-in-toolbar', 'trp_url_tp_editor', $trp_editor_button); remove_filter('trp_add_language_to_home_url_check_for_admin', '__return_false'); } /** * Add the glyph icon for Translate Site button in admin bar * * hooked to admin_head and wp_head actions */ public function add_styling_to_admin_bar_button() { if ( ! current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { return; } echo "<style> #wpadminbar #wp-admin-bar-trp_edit_translation .ab-icon:before { content: '\\f326'; top: 3px;} #wpadminbar #wp-admin-bar-trp_edit_translation > .ab-item { text-indent: 0; display: flex; justify-content: space-between; align-items: center; } #wpadminbar li#wp-admin-bar-trp_edit_translation { display: block; } #wpadminbar .trp-notification-badge { background: #d63638; color: #fff; border-radius: 50%; padding: 0 6px; margin: -3px 0 0 6px; font-size: 11px; line-height: 18px; display: inline-block; vertical-align: middle; min-width: 18px; text-align: center; box-sizing: border-box; } @keyframes trp-greyscale-pulse { 0%, 100% { color: #fff; } 50% { color: #626265; } } #wpadminbar .trp-needs-setup .ab-icon:before { animation: trp-greyscale-pulse 3s ease-in-out infinite; } #wpadminbar .trp-settings-page { /*border-top: 1px solid #4b4b4b;*/ } </style>"; } /** * Function to hide admin bar when in editor preview mode. * * Hooked to show_admin_bar. * * @param bool $show_admin_bar TRUE | FALSE * @return bool */ public function hide_admin_bar_when_in_editor( $show_admin_bar ) { if ( $this->conditions_met( 'preview' ) ) { return false; } return $show_admin_bar; } /** * Function that determines if a request is a rest api request based on the URL. * @return bool */ static function is_rest_api_request() { if ( empty( $_SERVER['REQUEST_URI'] ) ) { // Probably a CLI request return false; } $rest_prefix = trailingslashit( rest_get_url_prefix() ); $is_rest_api_request = strpos( $_SERVER['REQUEST_URI'], $rest_prefix ) !== false; /* phpcs:ignore */ return apply_filters( 'trp_is_rest_api_request', $is_rest_api_request ); } /** * Filter sanitize_title() to use our own remove_accents() function so it's based on the default language, not current locale. * * Also removes trp gettext tags before running the filter because it strip # and ! and / making it impossible to strip the #trpst later * * @param string $title * @param string $raw_title * @param string $context * @return string * @since 1.3.1 * */ public function trp_sanitize_title( $title, $raw_title, $context ) { // remove trp_tags before sanitization, because otherwise some characters (#,!,/, spaces ) are stripped later, and it becomes impossible to strip trp-gettext later $raw_title = TRP_Gettext_Manager::strip_gettext_tags( $raw_title ); if ( 'save' == $context ) $title = trp_remove_accents( $raw_title ); remove_filter( 'sanitize_title', array( $this, 'trp_sanitize_title' ), 1 ); $title = apply_filters( 'sanitize_title', $title, $raw_title, $context ); add_filter( 'sanitize_title', array( $this, 'trp_sanitize_title' ), 1, 3 ); return $title; } /** * Add the current language as a class to the body * @param $classes * @return array */ public function add_language_to_body_class( $classes ) { global $TRP_LANGUAGE; if ( !empty( $TRP_LANGUAGE ) ) { $classes[] = 'translatepress-' . $TRP_LANGUAGE; } return $classes; } /** * Function that switches the view of the user to other roles */ public function trp_view_as_user() { if ( !is_admin() || TRP_Gettext_Manager::is_ajax_on_frontend() ) { if ( isset( $_REQUEST['trp-edit-translation'] ) && $_REQUEST['trp-edit-translation'] === 'preview' && isset( $_REQUEST['trp-view-as'] ) && isset( $_REQUEST['trp-view-as-nonce'] ) ) { if ( apply_filters( 'trp_allow_translator_role_to_view_page_as_other_roles', true ) ) { $current_user_can_change_roles = current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) || current_user_can( 'manage_options' ); } else { $current_user_can_change_roles = current_user_can( 'manage_options' ); } if ($current_user_can_change_roles) { if (!wp_verify_nonce( sanitize_text_field($_REQUEST['trp-view-as-nonce'] ), 'trp_view_as' . sanitize_text_field($_REQUEST['trp-view-as']) . get_current_user_id())) { wp_die(esc_html__('Security check', 'translatepress-multilingual')); } else { global $current_user; $view_as = sanitize_text_field( $_REQUEST['trp-view-as'] ); if ( $view_as === 'current_user' ) { return; } elseif ( $view_as === 'logged_out' ) { $current_user = new WP_User( 0, 'trp_logged_out' ); } else { $current_user = apply_filters( 'trp_temporary_change_current_user_role', $current_user, $view_as ); } } } } } } /** * Return true if the string contains characters which are not allowed in the query * * Only valid for utf8. * Function is an extract of strip_invalid_text() function from wp-includes/wp-db.php * * @param $string * * @return bool */ public function has_bad_characters( $string ) { $regex = '/ ( (?: [\x00-\x7F] # single-byte sequences 0xxxxxxx | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2 | [\xE1-\xEC][\x80-\xBF]{2} | \xED[\x80-\x9F][\x80-\xBF] | [\xEE-\xEF][\x80-\xBF]{2}'; $regex .= ' | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3 | [\xF1-\xF3][\x80-\xBF]{3} | \xF4[\x80-\x8F][\x80-\xBF]{2} '; $regex .= '){1,40} # ...one or more times ) | . # anything else /x'; $stripped_string = preg_replace( $regex, '$1', $string ); if ( $stripped_string === $string ) { return false; } else { return true; } } /** * Records a series of strings which may have encoding issues * * Does not alter dictionary. * * @param $dictionary * @param $prepared_query * @param $strings_array * * @return mixed */ public function display_possible_db_errors( $dictionary, $prepared_query, $strings_array ) { global $trp_editor_notices; if ( trp_is_translation_editor( 'preview' ) && is_array( $dictionary ) && count( $dictionary ) === 0 ) { if ( $this->has_bad_characters( $prepared_query ) ) { $html = "<div class='trp-notice trp-notice-warning'><p class='trp-bad-encoded-strings'>" . __( '<strong>Warning:</strong> Some strings have possibly incorrectly encoded characters. This may result in breaking the queries, rendering the page untranslated in live mode. Consider revising the following strings or their method of outputting.', 'translatepress-multilingual' ) . "</p>"; $html .= "<ul class='trp-bad-encoded-strings-list'>"; foreach ( $strings_array as $string ) { if ( $this->has_bad_characters( $string ) ) { $html .= "<li>" . $string . "</li>"; } } $html .= "</ul></div>"; $trp_editor_notices .= $html; } } // no modifications to the dictionary return $dictionary; } public function display_notice_to_upgrade_gettext_in_editor( $trp_editor_notices ) { if ( ( get_option( 'trp_updated_database_gettext_original_id_update', 'yes' ) == 'no' ) ){ $url = add_query_arg( array( 'page' => 'trp_update_database', ), site_url('wp-admin/admin.php') ); // maybe change notice color to blue #28B1FF $html = "<div class='trp-notice trp-notice-warning'>"; $html .= '<p><strong>' . esc_html__( 'TranslatePress data update', 'translatepress-multilingual' ) . '</strong> – ' . esc_html__( 'We need to update your translations database to the latest version.', 'translatepress-multilingual' ) . '</p>'; $html .= '<p>' . esc_html__( 'Updating will allow editing translations of localized text from plugins and theme. Existing translation will still work as expected.', 'translatepress-multilingual' ) . '</p>'; $html .= '<p><a class="trp-button-primary" target="_blank" href="' . esc_url( $url ) . '" onclick="return confirm( \'' . __( 'IMPORTANT: It is strongly recommended to first backup the database!\nAre you sure you want to continue?', 'translatepress-multilingual' ) . '\');" class="button-primary">' . esc_html__( 'Run the updater', 'translatepress-multilingual' ) . '</a></p>'; $html .= '</div>'; $trp_editor_notices = $html; } return $trp_editor_notices; } public function display_notice_to_upgrade_slugs_in_editor( $trp_editor_notices ) { if ( ( get_option( 'trp_migrate_old_slug_to_new_parent_and_translate_slug_table_term_meta_284', 'not_set' ) == 'no' ) ){ $url = add_query_arg( array( 'page' => 'trp_update_database', ), site_url('wp-admin/admin.php') ); $html = "<div class='trp-notice trp-notice-warning'>"; $html .= '<p><strong>' . esc_html__( 'TranslatePress data update', 'translatepress-multilingual' ) . '</strong> – ' . esc_html__( 'We need to update your translations database to the latest version.', 'translatepress-multilingual' ) . '</p>'; $html .= '<p>' . esc_html__( 'Updating will allow editing translations of slugs. Existing translation will still work as expected.', 'translatepress-multilingual' ) . '</p>'; $html .= '<p><a class="trp-button-primary" target="_blank" href="' . esc_url( $url ) . '" onclick="return confirm( \'' . __( 'IMPORTANT: It is strongly recommended to first backup the database!\nAre you sure you want to continue?', 'translatepress-multilingual' ) . '\');" class="button-primary">' . esc_html__( 'Run the updater', 'translatepress-multilingual' ) . '</a></p>'; $html .= '</div>'; $trp_editor_notices = $html; } return $trp_editor_notices; } /** * Receives and returns the date format in which a date (eg publish date) is presented on the frontend * The format is saved in the advanced settings tab for each language except the default one * * @param $date_format * * @return mixed */ public function filter_the_date( $date_format ) { global $TRP_LANGUAGE; if ( !empty( $TRP_LANGUAGE ) && $this->settings["default-language"] === $TRP_LANGUAGE ) { return $date_format; } else { if ( isset ( $this->settings["trp_advanced_settings"]["language_date_format"][ $TRP_LANGUAGE ] ) && !empty ( $this->settings["trp_advanced_settings"]["language_date_format"][ $TRP_LANGUAGE ] ) ) { return $this->settings["trp_advanced_settings"]["language_date_format"][ $TRP_LANGUAGE ]; } else { return $date_format; } } } /** * Prevent indexing edit translation preview pages. * * Hooked to trp_head, wp_head * */ public function output_noindex_tag() { if( $this->conditions_met( 'true' ) || $this->conditions_met( 'preview' ) ){ echo '<meta name="robots" content="noindex, nofollow">'; } } public function upsale_slugs_text(){ // Check if SEO Pack is inactive if ($this->is_seo_pack_active() === false) { // Check if Pro version (Personal, Business or Developer) is active if (trp_is_paid_version()) { // Display activation message instead of upsale for Pro users $html = '<div class="trp-text-and-image-upsale-slugs">'; $html .= '<div class="trp-text-upsale-slugs">'; $html .= '<p>'; $html .= esc_html__('Please activate the SEO Addon from <br/>WordPress -> Settings -> TranslatePress -> Addons section', 'translatepress-multilingual' ); $html .= '</p>'; $html .= '<a target="_blank" href="' . esc_url(admin_url('admin.php?page=trp_addons_page')) . '" class="trp-learn-more-upsale button-primary">'; $html .= esc_html__('Go to Addons', 'translatepress-multilingual' ); $html .= '</a>'; $html .= '</div>'; $html .= '</div>'; return $html; } } // Default upsale text for free version //[utm36] $upsale_url = 'https://translatepress.com/pricing/?utm_source=tp-editor&utm_medium=client-site&utm_campaign=tp-editor-upsell'; $html = '<div class="trp-text-and-image-upsale-slugs">'; $html .= '<div class="trp-text-upsale-slugs">'; $html .= '<p>'; $html .= esc_html__('The SEO Pack add-on allows translation of all the URL slugs:', 'translatepress-multilingual' ); $html .= '<ul class="trp-url-slugs-list">'; $html .= '<li>'; $html .= esc_html__('Taxonomy slugs', 'translatepress-multilingual' ); $html .= '</li>'; $html .= '<li>'; $html .= esc_html__('Term slugs', 'translatepress-multilingual' ); $html .= '</li>'; $html .= '<li>'; $html .= esc_html__('Post slugs (this includes pages and custom post types)', 'translatepress-multilingual' ); $html .= '</li>'; $html .= '<li>'; $html .= esc_html__('Post type base slugs', 'translatepress-multilingual' ); $html .= '</li>'; $html .= '<li>'; $html .= esc_html__('WooCommerce slugs', 'translatepress-multilingual' ); $html .= '</li>'; $html .= '</ul>'; $html .= '</p>'; $html .= '<p>'; $html .= esc_html__('The SEO Pack add-on is available with ALL premium versions of the plugin.', 'translatepress-multilingual' ); $html .= '</p>'; $html .= '<a target="_blank" href="' . esc_url($upsale_url) . '" class="trp-learn-more-upsale button-primary">'; $html .= esc_html__('Upgrade to Pro', 'translatepress-multilingual' ); $html .= '</a>'; $html .= '</div>'; $html .= '<div class="trp-image-upsale-slugs">'; $html .= '<div class="trp-image-container">'; $html .= '<img src="' . esc_url(TRP_PLUGIN_URL.'assets/images/slug-upsale-new-editor-new.png') . '" class="trp-image-zoom" alt="SEO Pack Add-on">'; $html .= '</div>'; $html .= '</div>'; $html .= '</div>'; return $html; } public function is_seo_pack_active(){ return class_exists( 'TRP_IN_Seo_Pack'); } } includes/class-editor-api-gettext-strings.php 0000777 00000010520 15251156640 0015406 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Editor_Api_Gettext_Strings { /* @var TRP_Query */ protected $trp_query; /* @var TRP_SP_Slug_Manager*/ protected $slug_manager; /* @var TRP_Translation_Render */ protected $translation_render; /* @var TRP_Translation_Manager */ protected $translation_manager; /* @var TRP_Settings */ protected $settings; /** * TRP_Translation_Manager constructor. * * @param array $settings Settings option. */ public function __construct( $settings ){ $this->settings = $settings; } /** * Hooked to wp_ajax_trp_get_translations_gettext */ public function gettext_get_translations() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_get_translations_gettext' && ! empty( $_POST['string_ids'] ) && ! empty( $_POST['language'] ) && in_array( $_POST['language'], $this->settings['translation-languages'] ) ) { check_ajax_referer( 'gettext_get_translations', 'security' ); if ( ! empty( $_POST['string_ids'] ) ) { $gettext_string_ids = json_decode( stripslashes( $_POST['string_ids'] ) ); /* phpcs:ignore */ /* sanitized when inserting in db */ } else { $gettext_string_ids = array(); } $current_language = sanitize_text_field( $_POST['language'] ); $dictionaries = array(); if ( is_array( $gettext_string_ids ) ) { $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } if ( ! $this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } $dictionaries[ $current_language ] = $this->trp_query->get_gettext_string_rows_by_ids( $gettext_string_ids, $current_language ); /* build the original id array */ $original_ids = array(); if ( ! empty( $dictionaries[ $current_language ] ) ) { foreach ( $dictionaries[ $current_language ] as $current_language_string ) { /* searching by original id */ $original_ids[] = (int)$current_language_string['ot_id']; } } echo trp_safe_json_encode( array( // phpcs:ignore 'originalIds' => $original_ids, ) ); } } } wp_die(); } /* * Save gettext translations */ public function gettext_save_translations(){ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { if (isset($_POST['action']) && $_POST['action'] === 'trp_save_translations_gettext' && !empty($_POST['strings'])) { check_ajax_referer( 'gettext_save_translations', 'security' ); $strings = json_decode(stripslashes($_POST['strings']));/* phpcs:ignore */ /* properly sanitized bellow */ $update_strings = array(); foreach ( $strings as $language => $language_strings ) { if ( in_array( $language, $this->settings['translation-languages'] ) ) { $update_strings[ $language ] = array(); foreach( $language_strings as $string ) { if ( isset( $string->id ) && is_numeric( $string->id ) ) { array_push($update_strings[ $language ], array( 'id' => (int)$string->id, 'original' => trp_sanitize_string( $string->original, false ), 'translated' => trp_sanitize_string( $string->translated ), 'domain' => sanitize_text_field( $string->domain ), 'status' => (int)$string->status, 'plural_form' => (int)$string->plural_form, 'context' => $string->context )); } } } } if ( ! $this->trp_query ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_query = $trp->get_component( 'query' ); } foreach( $update_strings as $language => $update_string_array ) { $gettext_insert_update = $this->trp_query->get_query_component('gettext_insert_update'); $gettext_insert_update->update_gettext_strings( $update_string_array, $language, array('id','translated', 'status') ); $this->trp_query->remove_possible_duplicates($update_string_array, $language, 'gettext'); } do_action('trp_save_editor_translations_gettext_strings', $update_strings, $this->settings); } } echo trp_safe_json_encode( $update_strings );//phpcs:ignore wp_die(); } } includes/google-translate/class-google-translate-v2-machine-translator.php 0000777 00000023124 15251156640 0023040 0 ustar 00 <?php // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) exit; class TRP_Google_Translate_V2_Machine_Translator extends TRP_Machine_Translator { public function __construct( $settings ) { parent::__construct( $settings ); add_filter( 'trp_add_google_v2_supported_languages_to_the_array', array( $this, 'add_google_v2_supported_languages_that_are_not_returned_by_the_post_response' ), 10, 1 ); } /** * Send request to Google Translation API * * @param string $source_language Translate from language * @param string $language_code Translate to language * @param array $strings_array Array of string to translate * * @return array|WP_Error Response */ public function send_request( $source_language, $language_code, $strings_array ){ /* build our translation request */ $translation_request = 'key=' . $this->get_api_key(); $translation_request .= '&source='.$source_language; $translation_request .= '&target='.$language_code; foreach( $strings_array as $new_string ){ $translation_request .= '&q='.rawurlencode(html_entity_decode( $new_string, ENT_QUOTES )); } $referer = $this->get_referer(); /* Due to url length restrictions we need so send a POST request faked as a GET request and send the strings in the body of the request and not in the URL */ $response = wp_remote_post( "https://translation.googleapis.com/language/translate/v2", array( 'headers' => array( 'X-HTTP-Method-Override' => 'GET', //this fakes a GET request 'timeout' => 45, 'Referer' => $referer ), 'body' => $translation_request, ) ); return $response; } /** * Returns an array with the API provided translations of the $new_strings array. * * @param array $new_strings array with the strings that need translation. The keys are the node number in the DOM so we need to preserve the m * @param string $target_language_code language code of the language that we will be translating to. Not equal to the google language code * @param string $source_language_code language code of the language that we will be translating from. Not equal to the google language code * @return array array with the translation strings and the preserved keys or an empty array if something went wrong */ public function translate_array($new_strings, $target_language_code, $source_language_code = null ){ if ( $source_language_code == null ){ $source_language_code = $this->settings['default-language']; } if( empty( $new_strings ) || !$this->verify_request_parameters( $target_language_code, $source_language_code ) ) return array(); $source_language = $this->machine_translation_codes[$source_language_code]; $target_language = $this->machine_translation_codes[$target_language_code]; $translated_strings = array(); /* split our strings that need translation in chunks of maximum 128 strings because Google Translate has a limit of 128 strings */ $new_strings_chunks = array_chunk( $new_strings, 128, true ); /* if there are more than 128 strings we make multiple requests */ foreach( $new_strings_chunks as $new_strings_chunk ){ $response = $this->send_request( $source_language, $target_language, $new_strings_chunk ); // this is run only if "Log machine translation queries." is set to Yes. $this->machine_translator_logger->log(array( 'strings' => serialize( $new_strings_chunk), 'response' => serialize( $response ), 'lang_source' => $source_language, 'lang_target' => $target_language, )); /* analyze the response */ if ( is_array( $response ) && ! is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code']) && $response['response']['code'] == 200 ) { $translation_response = json_decode( $response['body'] ); if ( empty( $translation_response->error ) ) { $this->machine_translator_logger->count_towards_quota( $new_strings_chunk ); /* if we have strings build the translation strings array and make sure we keep the original keys from $new_string */ $translations = ( empty( $translation_response->data->translations ) ) ? array() : $translation_response->data->translations; $i = 0; foreach ( $new_strings_chunk as $key => $old_string ) { if ( isset( $translations[ $i ] ) && !empty( $translations[ $i ]->translatedText ) ) { $translated_strings[ $key ] = $translations[ $i ]->translatedText; } else { /* In some cases when API doesn't have a translation for a particular string, translation is returned empty instead of same string. Setting original string as translation prevents TP from keep trying to submit same string for translation endlessly. */ $translated_strings[ $key ] = $old_string; } $i++; } } if( $this->machine_translator_logger->quota_exceeded() ) break; } } // will have the same indexes as $new_string or it will be an empty array if something went wrong return $translated_strings; } /** * Send a test request to verify if the functionality is working */ public function test_request(){ return $this->send_request( 'en', 'es', array( 'about' ) ); } public function get_api_key(){ return isset( $this->settings['trp_machine_translation_settings'], $this->settings['trp_machine_translation_settings']['google-translate-key'] ) ? $this->settings['trp_machine_translation_settings']['google-translate-key'] : false; } public function get_supported_languages(){ if ( $this->get_api_key() ) { $response = wp_remote_post( "https://translation.googleapis.com/language/translate/v2/languages", array( 'headers' => array( 'timeout' => 45, 'Referer' => $this->get_referer() ), 'body' => 'key=' . $this->get_api_key(), ) ); if ( is_array( $response ) && !is_wp_error( $response ) && isset( $response['response'] ) && isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) { $data = json_decode( $response['body'] ); $supported_languages = array(); foreach ( $data->data->languages as $language ) { $supported_languages[] = $language->language; } return apply_filters( 'trp_add_google_v2_supported_languages_to_the_array', $supported_languages ); } } return array(); } public function add_google_v2_supported_languages_that_are_not_returned_by_the_post_response($supported_language){ $supported_language[] = 'fil'; return $supported_language; } public function get_engine_specific_language_codes($languages){ return $this->trp_languages->get_iso_codes($languages); } /* * Google does not support formality yet, but we need this for the machine translation tab to show the unsupported languages for formality */ public function check_formality(){ $formality_supported_languages = array(); return $formality_supported_languages; } public function check_api_key_validity() { $machine_translator = $this; $translation_engine = $this->settings['trp_machine_translation_settings']['translation-engine']; $api_key = $machine_translator->get_api_key(); $is_error = false; $return_message = ''; if ( 'google_translate_v2' === $translation_engine && $this->settings['trp_machine_translation_settings']['machine-translation'] === 'yes') { if ( isset( $this->correct_api_key ) && $this->correct_api_key != null ) { return $this->correct_api_key; } if ( empty( $api_key ) ) { $is_error = true; $return_message = __( 'Please enter your Google Translate key.', 'translatepress-multilingual' ); } else { // Perform test. $response = $machine_translator->test_request(); $code = wp_remote_retrieve_response_code( $response ); if ( 200 !== $code ) { $is_error = true; $translate_response = trp_gt_response_codes( $code ); $return_message = $translate_response['message']; } } $this->correct_api_key = array( 'message' => $return_message, 'error' => $is_error, ); } return array( 'message' => $return_message, 'error' => $is_error, ); } } includes/google-translate/functions.php 0000777 00000011156 15251156640 0014362 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); add_filter( 'trp_machine_translation_engines', 'trp_gt_add_engine', 10 ); function trp_gt_add_engine( $engines ){ $engines[] = array( 'value' => 'google_translate_v2', 'label' => __( 'Google Translate v2', 'translatepress-multilingual' ) ); return $engines; } add_action( 'trp_machine_translation_extra_settings_middle', 'trp_gt_add_settings' ); function trp_gt_add_settings( $mt_settings ){ $trp = TRP_Translate_Press::get_trp_instance(); $machine_translator = $trp->get_component( 'machine_translator' ); $translation_engine = isset( $mt_settings['translation-engine'] ) ? $mt_settings['translation-engine'] : ''; $api_key = isset( $mt_settings['google-translate-key'] ) ? $mt_settings['google-translate-key'] : ''; // Check for API errors only if $translation_engine is Google. if ( 'google_translate_v2' === $translation_engine ) { $api_check = $machine_translator->check_api_key_validity(); } // Check for errors. $error_message = ''; $show_errors = false; if ( isset( $api_check ) && true === $api_check['error'] ) { $error_message = $api_check['message']; $show_errors = true; } $text_input_classes = array( 'trp-text-input', ); if ( $show_errors && 'google_translate_v2' === $translation_engine ) { $text_input_classes[] = 'trp-text-input-error'; } ?> <div class="trp-engine trp-automatic-translation-engine__container" id="google_translate_v2"> <span class="trp-primary-text-bold"><?php esc_html_e( 'Google Translate API Key', 'translatepress-multilingual' ); ?> </span> <div class="trp-automatic-translation-api-key-container"> <input type="text" id="trp-g-translate-key" placeholder="<?php esc_html_e( 'Add your API Key here...', 'translatepress-multilingual' ); ?>" class="<?php echo esc_html( implode( ' ', $text_input_classes ) ); ?>" name="trp_machine_translation_settings[google-translate-key]" value="<?php if( !empty( $mt_settings['google-translate-key'] ) ) echo esc_attr( $mt_settings['google-translate-key']);?>"/> <?php // Only show errors if Google Translate is active. if ( 'google_translate_v2' === $translation_engine && function_exists( 'trp_output_svg' ) ) { $machine_translator->automatic_translation_svg_output( $show_errors ); } ?> </div> <?php if ( $show_errors && 'google_translate_v2' === $translation_engine ) { ?> <span class="trp-error-inline trp-settings-error-text"> <?php echo wp_kses_post( $error_message ); ?> </span> <?php } ?> <span class="trp-description-text"> <?php echo wp_kses( __( 'Visit <a href="https://cloud.google.com/docs/authentication/api-keys" target="_blank">this link</a> to see how you can set up an API key, <strong>control API costs</strong> and set HTTP referrer restrictions.', 'translatepress-multilingual' ), [ 'a' => [ 'href' => [], 'title' => [], 'target' => [] ], 'strong' => [] ] ); ?> <br><?php echo esc_html( sprintf( __( 'Your HTTP referrer is: %s', 'translatepress-multilingual' ), $machine_translator->get_referer() ) ); ?> </span> </div> <?php } add_filter( 'trp_machine_translation_sanitize_settings', 'trp_gt_sanitize_settings' ); function trp_gt_sanitize_settings( $mt_settings ){ if( !empty( $mt_settings['google-translate-key'] ) ) $mt_settings['google-translate-key'] = sanitize_text_field( $mt_settings['google-translate-key'] ); return $mt_settings; } /** * Returns an appropriate error/success message for the Google Translate access. * * @param int $code The code returned by Google Translate access. * * @return array [ (string) $message, (bool) $error ]. */ function trp_gt_response_codes( $code ) { $is_error = false; $code = intval( $code ); $return_message = ''; /** * Determine if we have a 4xx or 5xx error. * * @see https://cloud.google.com/apis/design/errors */ if ( preg_match( '/4\d\d/', $code ) ) { $is_error = true; $return_message = esc_html__( 'There was an error with your Google Translate key.', 'translatepress-multilingual' ); } elseif ( preg_match( '/5\d\d/', $code ) ) { $is_error = true; $return_message = esc_html__( 'There was an error on the server processing your Google Translate key.', 'translatepress-multilingual' ); } return array( 'message' => $return_message, 'error' => $is_error, ); } includes/class-language-switcher.php 0000777 00000062267 15251156640 0013630 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Language_Switcher * * Generates all types of language switchers. */ class TRP_Language_Switcher{ protected $settings; /** @var TRP_Url_Converter */ protected $url_converter; protected $trp_settings_object; /** @var TRP_Languages */ protected $trp_languages; /** @var TRP_Translate_Press */ protected $trp; /** * TRP_Language_Switcher constructor. * * @param array $settings Settings option. * @param $trp TRP_Translate_Press Trp object */ public function __construct( $settings, $trp ){ $this->settings = $settings; $this->trp = $trp; $this->url_converter = $this->trp->get_component( 'url_converter' ); $language = $this->get_current_language($trp); global $TRP_LANGUAGE; $TRP_LANGUAGE = $language; add_filter( 'get_user_option_metaboxhidden_nav-menus', array( $this, 'cpt_always_visible_in_menus' ), 10, 3 ); add_shortcode( 'language-switcher', [ $this, 'language_switcher' ] ); } /** * Returns a valid current language code. * * Adds filter for redirect if necessary * * @param $trp TRP_Translate_Press TRP singleton object * * @return string Language code */ private function get_current_language( $trp ){ $language_from_url = $this->url_converter->get_lang_from_url_string(); $needed_language = $this->determine_needed_language( $language_from_url, $trp ); $allow_redirect = apply_filters( 'trp_allow_language_redirect', true, $needed_language, $this->url_converter->cur_page_url() ); if ( $allow_redirect ) { if ( ( $language_from_url == null && isset( $this->settings['add-subdirectory-to-default-language'] ) && $this->settings['add-subdirectory-to-default-language'] == 'yes' ) || ( $language_from_url == null && $needed_language != $this->settings['default-language'] ) || ( $language_from_url != null && $needed_language != $language_from_url ) ) { global $TRP_NEEDED_LANGUAGE; $TRP_NEEDED_LANGUAGE = $needed_language; add_filter( 'template_redirect', array( $this, 'redirect_to_correct_language' ) ); } } return $needed_language; } /** * Determine the language needed. * * @param string $lang_from_url Language code from url * @param TRP_Translate_Press $trp TRP singleton object * * @return string Language code */ public function determine_needed_language( $lang_from_url, $trp ){ if ( $lang_from_url == null ){ if ( isset( $this->settings['add-subdirectory-to-default-language'] ) && $this->settings['add-subdirectory-to-default-language'] == 'yes' && isset( $this->settings['publish-languages'][0] ) ) { $needed_language = $this->settings['publish-languages'][0]; }else{ $needed_language = $this->settings['default-language']; } }else{ $needed_language = $lang_from_url; } return apply_filters( 'trp_needed_language', $needed_language, $lang_from_url, $this->settings, $trp ); } /** * Redirects to language stored in global $TRP_NEEDED_LANGUAGE */ public function redirect_to_correct_language(){ if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || is_customize_preview() ) return; global $TRP_NEEDED_LANGUAGE; if ( ! $this->url_converter ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component( 'url_converter' ); } if ( $this->url_converter->is_sitemap_path() ) return; $link_to_redirect = sanitize_url(apply_filters( 'trp_link_to_redirect_to', $this->url_converter->get_url_for_language( $TRP_NEEDED_LANGUAGE, null, '' ), $TRP_NEEDED_LANGUAGE )); if( isset( $this->settings['add-subdirectory-to-default-language'] ) && $this->settings['add-subdirectory-to-default-language'] === 'yes' && isset( $this->settings['default-language'] ) && $this->settings['default-language'] === $TRP_NEEDED_LANGUAGE ) { $status = apply_filters( 'trp_redirect_status', 301, 'redirect_to_add_subdirectory_to_default_language' ); wp_redirect( $link_to_redirect, $status ); }else { $status = apply_filters( 'trp_redirect_status', 302, 'redirect_to_a_different_language_according_to_url_slug' ); wp_redirect( $link_to_redirect, $status ); } exit; } /** * Returns HTML for shortcode language switcher. * * Only shows published languages. * Takes into account shortcode flags and name options. * Runs an output buffer on 'partials/language-switcher-shortcode.php'. * * @return string HTML for shortcode language switcher */ public function language_switcher( $atts ){ $loader = $this->trp->get_component( 'loader' ); if ( apply_filters( 'trp_allow_tp_to_run', true, $loader ) === false ) return ''; ob_start(); global $TRP_LANGUAGE; $shortcode_attributes = shortcode_atts( array( 'display' => 0, 'is_editor' => 0, ), $atts ); if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } if ( current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { $languages_to_display = $this->settings['translation-languages']; }else{ $languages_to_display = $this->settings['publish-languages']; } $published_languages = $this->trp_languages->get_language_names( $languages_to_display ); $current_language = array(); $other_languages = array(); foreach( $published_languages as $code => $name ) { if( $code == $TRP_LANGUAGE ) { $current_language['code'] = $code; $current_language['name'] = $name; } else { $other_languages[$code] = $name; } } $current_language = apply_filters('trp_ls_shortcode_current_language', $current_language, $published_languages, $TRP_LANGUAGE, $this->settings); $other_languages = apply_filters('trp_ls_shortcode_other_languages', $other_languages, $published_languages, $TRP_LANGUAGE, $this->settings); if( ! $this->trp_settings_object ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_settings_object = $trp->get_component( 'settings' ); } $ls_options = $this->trp_settings_object->get_language_switcher_options(); if ( isset( $shortcode_attributes['display'] ) && isset( $ls_options[$shortcode_attributes['display']] ) ){ $shortcode_settings = $ls_options[ $shortcode_attributes['display'] ]; }else { $shortcode_settings = $ls_options[ $this->settings['shortcode-options'] ]; } $is_editor = isset( $shortcode_attributes['is_editor'] ) && $shortcode_attributes['is_editor'] === 'true'; require TRP_PLUGIN_DIR . 'partials/language-switcher-shortcode.php'; return ob_get_clean(); } /** * Enqueue language switcher scripts and styles. * * Adds scripts for shortcode and floater. * * Hooked on wp_enqueue_scripts. */ public function enqueue_language_switcher_scripts( ) { if ( apply_filters( 'trp_enqueue_style_language_switcher_css', true ) ) { if ( isset( $this->settings['trp-ls-floater'] ) && $this->settings['trp-ls-floater'] == 'yes' ) { $floater_path = apply_filters( 'trp_old_css_styling_for_floater_ls', false ) ? TRP_PLUGIN_URL . 'assets/css/trp-floater-language-switcher-old.css' : TRP_PLUGIN_URL . 'assets/css/trp-floater-language-switcher.css'; wp_enqueue_style('trp-floater-language-switcher-style', $floater_path, array(), TRP_PLUGIN_VERSION); } $shortcode_path = apply_filters( 'trp_old_css_styling_for_shortcode_ls', false ) ? TRP_PLUGIN_URL . 'assets/css/trp-language-switcher-old.css' : TRP_PLUGIN_URL . 'assets/css/trp-language-switcher.css'; wp_enqueue_style( 'trp-language-switcher-style', $shortcode_path, array(), TRP_PLUGIN_VERSION ); } } /** * Adds the floater language switcher. * * Hooked on wp_footer. */ public function add_floater_language_switcher() { // Check if floater language switcher is active and return if not if( $this->settings['trp-ls-floater'] !== 'yes' ) { return; } if ( ! $this->trp_settings_object ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_settings_object = $trp->get_component( 'settings' ); } // Current language global $TRP_LANGUAGE; // All the published languages if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } if ( current_user_can(apply_filters( 'trp_translating_capability', 'manage_options' )) ){ $languages_to_display = $this->settings['translation-languages']; }else{ $languages_to_display = $this->settings['publish-languages']; } $published_languages = $this->trp_languages->get_language_names( $languages_to_display ); // Floater languages display defaults $floater_class = 'trp-floater-ls-names'; $floater_flags_class = ''; // Floater languages settings $ls_options = $this->trp_settings_object->get_language_switcher_options(); $floater_settings = $ls_options[$this->settings['floater-options']]; if( $floater_settings['full_names'] ) { $floater_class = 'trp-floater-ls-names'; } if( $floater_settings['short_names'] ) { $floater_class = 'trp-floater-ls-codes'; } if( $floater_settings['flags'] && ! $floater_settings['full_names'] && ! $floater_settings['short_names'] ) { $floater_class = 'trp-floater-ls-flags'; } if( $floater_settings['flags'] && ( $floater_settings['full_names'] || $floater_settings['short_names'] ) ) { $floater_flags_class = 'trp-with-flags'; } if( $this->settings['floater-position'] ) { $floater_class .= ' trp-' . esc_attr($this->settings['floater-position']); } if( $this->settings['floater-color'] ) { $floater_class .= ' trp-color-' . esc_attr($this->settings['floater-color']); } else { $floater_class .= ' trp-color-dark'; // default color. Good for backwards compatibility as well. } if( $this->settings['trp-ls-show-poweredby'] == 'yes' ) { $floater_class .= ' trp-poweredby'; } $floater_class .= ' ' . $this->settings['floater-options']; $current_language = array(); $other_languages = array(); foreach( $published_languages as $code => $name ) { if( $code == $TRP_LANGUAGE ) { $current_language['code'] = $code; $current_language['name'] = $name; } else { $other_languages[$code] = $name; } } $current_language = apply_filters('trp_ls_floating_current_language', $current_language, $published_languages, $TRP_LANGUAGE, $this->settings); $other_languages = apply_filters('trp_ls_floating_other_languages', $other_languages, $published_languages, $TRP_LANGUAGE, $this->settings); $current_language_label = ''; if( $floater_settings['full_names'] ) { $current_language_label = ucfirst( $current_language['name'] ); } if( $floater_settings['short_names'] ) { $current_language_label = strtoupper( $this->url_converter->get_url_slug( $current_language['code'], false ) ); } ob_start(); ?> <div id="trp-floater-ls" onclick="" data-no-translation class="trp-language-switcher-container <?php echo esc_attr( $floater_class ); ?>" <?php echo ( isset( $_GET['trp-edit-translation'] ) && $_GET['trp-edit-translation'] == 'preview' ) ? 'data-trp-unpreviewable="trp-unpreviewable"' : '' ?>> <div id="trp-floater-ls-current-language" class="<?php echo esc_attr( $floater_flags_class ); ?>"> <a href="#" class="trp-floater-ls-disabled-language trp-ls-disabled-language" onclick="event.preventDefault()"> <?php echo ( $floater_settings['flags'] ? $this->add_flag( $current_language['code'], $current_language['name'] ) : '' ); // phpcs:ignore echo esc_html( $current_language_label ); ?> </a> </div> <div id="trp-floater-ls-language-list" class="<?php echo esc_attr( $floater_flags_class );?>" <?php echo ( isset( $_GET['trp-edit-translation'] ) && $_GET['trp-edit-translation'] == 'preview' ) ? 'data-trp-unpreviewable="trp-unpreviewable"' : ''?>> <?php if( $this->settings['trp-ls-show-poweredby'] == 'yes' ){ //[utm9] $powered_by = '<div id="trp-floater-poweredby">Powered by <a href="https://translatepress.com/?utm_source=frontend-ls&utm_medium=client-site&utm_campaign=powered-by-tp" rel="nofollow" target="_blank" title="WordPress Translation Plugin">TranslatePress</a></div>'; } else { $powered_by = ''; } if ( apply_filters('trp_ls_floater_show_disabled_language', true, $current_language, $this->settings ) ) { $disabled_language = '<a href="#" class="trp-floater-ls-disabled-language trp-ls-disabled-language" onclick="event.preventDefault()">'; $disabled_language .= ( $floater_settings['flags'] ? $this->add_flag( $current_language['code'], $current_language['name'] ) : '' ); // WPCS: ok. $disabled_language .= esc_html( $current_language_label ); $disabled_language .= '</a>'; } $floater_position = 'bottom'; if ( !empty( $this->settings['floater-position'] ) && strpos( $this->settings['floater-position'], 'top' ) !== false ){ echo $powered_by; // phpcs:ignore echo '<div class="trp-language-wrap trp-language-wrap-top">'; if ( !empty( $disabled_language ) ){ echo $disabled_language; // phpcs:ignore } $floater_position = 'top'; } if ( $floater_position == 'bottom' ){ echo '<div class="trp-language-wrap trp-language-wrap-bottom">'; } foreach( $other_languages as $code => $name ) { $language_label = ''; if( $floater_settings['full_names'] ) { $language_label = ucfirst( $name ); } if( $floater_settings['short_names'] ) { $language_label = strtoupper( $this->url_converter->get_url_slug( $code, false ) ); } ?> <a href="<?php echo esc_url( $this->url_converter->get_url_for_language($code, false) ); ?>" <?php echo ( isset( $_GET['trp-edit-translation'] ) && $_GET['trp-edit-translation'] == 'preview' ) ? 'data-trp-unpreviewable="trp-unpreviewable"' : '' ?> title="<?php echo esc_attr( $name ); ?>"> <?php echo ( $floater_settings['flags'] ? $this->add_flag( $code, $name ) : '' ); // phpcs:ignore echo esc_html( $language_label ); ?> </a> <?php } if ( $floater_position == 'top' ){ echo '</div>'; } if ( $floater_position == 'bottom' ){ if ( apply_filters('trp_ls_floater_show_disabled_language', true, $current_language, $this->settings ) ) { echo $disabled_language; // phpcs:ignore } echo '</div>'; echo $powered_by; // phpcs:ignore } ?> </div> </div> <?php $floating_ls_html = ob_get_clean(); echo apply_filters( 'trp_floating_ls_html', $floating_ls_html ); // phpcs:ignore } /** * Return flag html. * * @important This function is used in WP Rocket plugin. Please don't remove it or change its signature. * * @param string $language_code Language code. * @param string $language_name Language full name or shortname. * @param string $location NULL | ls_shortcode * @return string Returns flag html. */ public function add_flag( $language_code, $language_name, $location = NULL ) { // Path to folder with flags images $flags_path = TRP_PLUGIN_URL .'assets/images/flags/'; $flags_path = apply_filters( 'trp_flags_path', $flags_path, $language_code ); // File name for specific flag $flag_file_name = $language_code .'.png'; $flag_file_name = apply_filters( 'trp_flag_file_name', $flag_file_name, $language_code ); // HTML code to display flag image $flag_html = '<img class="trp-flag-image" src="'. esc_url( $flags_path . $flag_file_name ) .'" width="18" height="12" alt="' . esc_attr( $language_code ) . '" title="' . esc_attr( $language_name ) . '">'; if( $location == 'ls_shortcode' ) { $flag_url = $flags_path . $flag_file_name; return esc_url( $flag_url ); } return $flag_html; } /** * Return full or short name, with or without flag * * @param string $language_code Language code. * @param string $language_name Language full name or shortname. * @param array $settings NULL | ls_shortcode * @return string Returns html with flags short or long names, depending on settings. */ public function add_shortcode_preferences( $settings, $language_code, $language_name ) { if ( $settings['flags'] ){ $flag = $this->add_flag($language_code, $language_name); } else { $flag = ''; } if ( $settings['full_names'] ){ $full_name = $language_name; } else { $full_name = ''; } if ( $settings['short_names'] ){ $short_name = strtoupper( $this->url_converter->get_url_slug( $language_code, false ) ); } else { $short_name = ''; } return $flag . ' ' . esc_html( $short_name . $full_name ); } /** * Register language switcher post type. * */ public function register_ls_menu_switcher( ){ $args = array( 'exclude_from_search' => true, 'publicly_queryable' => false, 'show_ui' => true, 'show_in_nav_menus' => true, 'show_in_menu' => false, 'show_in_admin_bar' => false, 'can_export' => false, 'public' => false, 'label' => 'Language Switcher' ); register_post_type( 'language_switcher', $args ); } /** * Makes the Language Switcher CPT always visible in Menus interface. * */ function cpt_always_visible_in_menus( $result, $option, $user ) { if( is_array($result) && in_array( 'add-post-type-language_switcher', $result ) ) $result = array_diff( $result, array( 'add-post-type-language_switcher' ) ); return $result; } /** * Prepare language switcher menu items. * * Sets the current page permalinks to menu items. * Inserts flags and full name if necessary * Removes menu item of current language if Current Language item is present. * * Hooked on wp_get_nav_menu_items * * @param array $items Menu items. * @param string $menu Menu name. * @param array $args Menu arguments. * @return array Menu items with */ public function ls_menu_permalinks( $items, $menu, $args ){ global $TRP_LANGUAGE; if ( ! $this->trp_settings_object ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_settings_object = $trp->get_component( 'settings' ); } if ( ! $this->trp_languages ){ $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_languages = $trp->get_component( 'languages' ); } $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $published_languages = $this->trp_languages->get_language_names( $trp_settings->get_settings()['publish-languages'] ); $item_key_to_unset = false; $current_language_set = false; $language_labels = []; // Get all user defined labels for language names before building the language switcher. // Useful for keeping user labels for Current language and Opposite language foreach ( $items as $key => $item) { if ( $item->object == 'language_switcher' ){ $ls_id = get_post_meta( $item->ID, '_menu_item_object_id', true ); $ls_post = get_post( $ls_id ); if ( $ls_post == null || $ls_post->post_type != 'language_switcher' ) { continue; } $language_code = $ls_post->post_content; // Prefer item title if it exists, otherwise use the corresponding value from $published_languages $language_name = !empty($item->post_title) ? $item->post_title : (isset($published_languages[$language_code]) ? $published_languages[$language_code] : $item->title); $language_labels[$language_code] = $language_name; } } foreach ( $items as $key => $item ){ if ( $item->object == 'language_switcher' ){ $ls_id = get_post_meta( $item->ID, '_menu_item_object_id', true ); $ls_post = get_post( $ls_id ); if ( $ls_post == null || $ls_post->post_type != 'language_switcher' ) { continue; } $ls_options = $this->trp_settings_object->get_language_switcher_options(); $menu_settings = $ls_options[$this->settings['menu-options']]; $language_code = $ls_post->post_content; if ( $language_code == $TRP_LANGUAGE && ! is_admin() ){ $item_key_to_unset = $key; } if ( $language_code == 'current_language' ) { $language_code = $TRP_LANGUAGE; $current_language_set = true; } if ( $language_code == 'opposite_language' ) { foreach ( $published_languages as $value => $value_item ) { if ( $value != $TRP_LANGUAGE ) { $language_code = $value; } } } if ( !isset( $language_labels[ $language_code ] ) ) { // use language name as defined by WP if user did not add this specific language in the menu $language_names = $this->trp_languages->get_language_names( array( $language_code ) ); $language_name = $language_names[ $language_code ]; } else { // use the language label defined by the user in Appearance -> Menu // applicable for all languages defined in the menu and for Current Language and Opposite Language $language_name = $language_labels[ $language_code ]; } $items[ $key ]->url = esc_url( $this->url_converter->get_url_for_language( $language_code ) ); // Output of simple text only menu, for compatibility with certain themes/plugins if ($menu_settings["no_html"] ){ $items[$key]->classes[] = ''; $items[$key]->title = $language_name; } else { $items[$key]->classes[] = 'trp-language-switcher-container'; $items[$key]->title = '<span data-no-translation>'; if ( $menu_settings['flags'] ) { $items[$key]->title .= $this->add_flag( $language_code, $language_name ); } if ( $menu_settings['short_names'] ) { $items[$key]->title .= '<span class="trp-ls-language-name">' . strtoupper( $this->url_converter->get_url_slug( $language_code, false ) ) . '</span>'; } if ( $menu_settings['full_names'] ) { $items[$key]->title .= '<span class="trp-ls-language-name">' . wp_kses_post( $language_name ) . '</span>'; } $items[$key]->title .= '</span>'; } $items[$key]->title = apply_filters( 'trp_menu_language_switcher', $items[$key]->title, $language_name, $language_code, $menu_settings ); } } // Removes menu item of current language if "Current Language" language switcher item is present. if ( $current_language_set && $item_key_to_unset !== false ){ unset($items[$item_key_to_unset]); $items = array_values( $items ); } return $items; } } includes/class-error-manager.php 0000777 00000031004 15251156640 0012741 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Error_Manager */ class TRP_Error_Manager{ protected $settings; /* @var TRP_Settings */ protected $trp_settings; public function __construct( $settings){ $this->settings = $settings; } public function is_error_manager_disabled(){ return apply_filters( 'trp_disable_error_manager', false ); } /** * Record specified error in trp_db_errors option * * @param $error_details array Suggested fields: 'last_error' => $this->db->last_error, 'details' => 'Insert general description', 'disable_automatic_translations' => bool */ public function record_error( $error_details ){ global $wpdb; if ( $this->is_error_manager_disabled() ){ return; } $option = get_option('trp_db_errors', array( 'notifications' => array(), 'errors' => array() )); if ( !isset( $option ) || !is_array( $option['errors'] ) ){ $option['errors'] = []; } if ( count( $option['errors'] ) >= 5 ){ // only record the last few errors to avoid huge db options array_shift($option['errors'] ); } $error_details['last_query'] = $wpdb->last_query; $error_details['date_time'] = date('Y-m-d H:i:s'); $error_details['timestamp'] = time(); $error_message = wp_kses( sprintf( __('<strong>TranslatePress</strong> encountered SQL errors. <a href="%s" title="View TranslatePress SQL Errors">Check out the errors</a>.', 'translatepress-multilingual'), admin_url( 'admin.php?page=trp_error_manager' ) ), array('a' => array('href' => array(), 'title' => array()), 'strong' => array())); // specific actions for this error: add notification message and disable machine translation if ( isset( $error_details['disable_automatic_translations'] ) && $error_details['disable_automatic_translations'] === true ){ if ( ! $this->trp_settings ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->trp_settings = $trp->get_component( 'settings' ); } $mt_settings_option = get_option('trp_machine_translation_settings', $this->trp_settings->get_default_trp_machine_translation_settings() ); if ( $mt_settings_option['machine-translation'] != 'no' ) { $mt_settings_option['machine-translation'] = 'no'; update_option('trp_machine_translation_settings', $mt_settings_option ); // filter is needed to block automatic translation in this execution. The settings don't update throughout the plugin for this request. Only the next request will have machine translation turned off. add_filter( 'trp_disable_automatic_translations_due_to_error', '__return_true' ); $error_message = wp_kses( __('Automatic translation has been disabled.','translatepress-multilingual'), array('strong' => array() ) ) . ' ' . $error_message ; } if ( !isset( $option['notifications']['disable_automatic_translations'] ) ) { $option['notifications']['disable_automatic_translations' ] = array( // we need a unique ID so that after the notice is dismissed and this type of error appears again, it's not already marked as dismissed for that user 'notification_id' => 'disable_automatic_translations' . time(), 'message' => $error_message ); } } if ( isset( $error_details['notification_id'] ) && isset( $error_details['message'] ) ) { $option['notifications'][$error_details['notification_id']] = array( 'notification_id' => $error_details['notification_id'], 'message' => $error_details['message'] .' ' . $error_message ); } // this action allows you to trigger an action like send an email with the error details do_action( 'trp_error_details', $error_details ); $option['errors'][] = $error_details; update_option( 'trp_db_errors', $option ); } /** * Remove notification from trp_db_errors too (not only user_meta) when dismissed by user * * Necessary in order to allow logging of this error in the future. Basically allow creation of new notifications about this error. * * Hooked to trp_dismiss_notification * * @param $notification_id * @param $current_user */ public function clear_notification_from_db($notification_id, $current_user ){ $option = get_option( 'trp_db_errors', false ); if ( isset( $option['notifications'] ) ) { foreach ($option['notifications'] as $key => $logged_notification ){ if ( $notification_id == '' || $logged_notification['notification_id'] === $notification_id || $key === $notification_id ) { unset( $option['notifications'][$key] ); update_option('trp_db_errors', $option ); break; } } } } /** * When enabling machine translation, clear the Automatic translation has been disabled message * * @param $mt_settings * @return string $mt_settings */ public function clear_disable_machine_translation_notification_from_db( $mt_settings ){ if ( $mt_settings['machine-translation'] === 'yes' ){ $this->clear_notification_from_db('disable_automatic_translations', null); } return $mt_settings; } /** * Disable the notification after the link is clicked. */ public function disable_error_after_click_link(){ $link = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : ''; if($link === 'trp_error_manager') { $this->clear_notification_from_db('', null); } } /** * * Hooked to admin_init */ public function show_notification_about_errors(){ if ( $this->is_error_manager_disabled() ){ return; } $option = get_option( 'trp_db_errors', false ); if ( $option !== false && isset($option['notifications'])) { foreach( $option['notifications'] as $logged_notification ) { $notifications = TRP_Plugin_Notifications::get_instance(); $notification_id = $logged_notification['notification_id']; $message = '<p style="padding-right:30px;">' . $logged_notification['message'] . '</p>'; //make sure to use the trp_dismiss_admin_notification arg $message .= '<a href="' . add_query_arg(array('trp_dismiss_admin_notification' => $notification_id)) . '" type="button" class="notice-dismiss" style="text-decoration: none;z-index:100;"><span class="screen-reader-text">' . esc_html__('Dismiss this notice.', 'translatepress-multilingual') . '</span></a>'; $notifications->add_notification($notification_id, $message, 'trp-notice trp-narrow notice error is-dismissible', true, array('translate-press'), true); } } } public function register_submenu_errors_page(){ add_submenu_page( 'TRPHidden', 'TranslatePress Error Manager', 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_error_manager', array( $this, 'error_manager_page_content' ) ); } public function error_manager_page_content(){ require_once TRP_PLUGIN_DIR . 'partials/error-manager-page.php'; } public function output_db_errors( $html_content ){ $option = get_option( 'trp_db_errors', false ); if ( $option !== false && isset($option['errors']) ) { $html_content .= '<h2>' . esc_html__('Logged errors', 'translatepress-multilingual') . '</h2>'; $html_content .= '<p>' . esc_html__('These are the most recent 5 errors logged by TranslatePress:', 'translatepress-multilingual' ) . '</p>'; $html_content .= '<table>'; $option['errors'] = array_reverse($option['errors']); foreach ($option['errors'] as $count => $error) { $count = ( is_int( $count) ) ? $count + 1 : $count; $html_content .= '<tr><td>' . esc_html($count) . '</td></tr>'; foreach( $error as $key => $error_detail ){ $error_detail = ($error_detail === true ) ? esc_html__('Yes', 'translatepress-multilingual') : $error_detail; $html_content .= '<tr><td><strong>' . esc_html($key ) . '</strong></td>' . '<td>' .esc_html( $error_detail ) . '</td></tr>'; } } $html_content .= '</table>'; } return $html_content; } /** * Hooked to trp_error_manager_page_output * * @param $html_content * @return string */ public function show_instructions_on_how_to_fix( $html_content ){ $html_content .= '<h2>' . esc_html__('Why are these errors occuring', 'translatepress-multilingual') . '</h2>'; $html_content .= '<p>' . esc_html__('If TranslatePress detects something wrong when executing queries on your database, it may disable the Automatic Translation feature in order to avoid any extra charging by Google/DeepL. Automatic Translation needs to be manually turned on, after you solve the issues.', 'translatepress-multilingual') . '</p>'; $html_content .= '<p>' . esc_html__('The SQL errors detected can occur for various reasons including missing tables, missing permissions for the SQL user to create tables or perform other operations, problems after site migration or changes to SQL server configuration.', 'translatepress-multilingual') . '</p>'; $html_content .= '<h2>' . esc_html__('What you can do in this situation', 'translatepress-multilingual') . '</h2>'; $html_content .= '<h4>' . esc_html__('Plan A.', 'translatepress-multilingual') . '</h4>'; $html_content .= '<p>' . esc_html__('Go to Settings -> TranslatePress -> General tab and Save Settings. This will regenerate the tables using your current SQL settings. Check if no more errors occur while browsing your website in a translated language. Look at the timestamps of the errors to make sure you are not seeing the old errors. Only the most recent 5 errors are displayed.', 'translatepress-multilingual') . '</p>'; $html_content .= '<h4>' . esc_html__('Plan B.', 'translatepress-multilingual') . '</h4>'; $html_content .= '<p>' . esc_html__('If your problem isn\'t solved, try the following steps:', 'translatepress-multilingual') . '</p>'; $html_content .= '<ol>'; $html_content .= '<li>' . esc_html__('Create a backup of your database', 'translatepress-multilingual') . '</li>'; $html_content .= '<li>' . esc_html__('Create a copy of each translation table where you encounter errors. You can copy the table within the same database (trp_dictionary_en_us_es_es_COPY for example) -- perform this step only if you want to keep the current translations', 'translatepress-multilingual') . '</li>'; $html_content .= '<li>' . esc_html__('Remove the trouble tables by executing the DROP function on them', 'translatepress-multilingual') . '</li>'; $html_content .= '<li>' . esc_html__('Go to Settings -> TranslatePress -> General tab and Save Settings. This will regenerate the tables using your current SQL server.', 'translatepress-multilingual') . '</li>'; $html_content .= '<li>' . esc_html__('Copy the relevant content from the duplicated tables (trp_dictionary_en_us_es_es_COPY for example) in the newly generated table (trp_dictionary_en_us_es_es) -- perform this step only if you want to keep the current translations', 'translatepress-multilingual') . '</li>'; $html_content .= '<li>' . esc_html__('Test it to see if everything is working. If something went wrong, you can restore the backup that you\'ve made at the first step. Check if no more errors occur while browsing your website in a translated language. Look at the timestamps of the errors to make sure you are not seeing the old errors. Only the most recent 5 errors are displayed.', 'translatepress-multilingual') . '</li>'; $html_content .= '</ol>'; $html_content .= '<h4>' . esc_html__('Plan C.', 'translatepress-multilingual') . '</h4>'; $html_content .= '<p>' . esc_html__('If your problem still isn\'t solved, try asking your hosting about your errors. The most common issue is missing permissions for the SQL user, such as the Create Tables permission.', 'translatepress-multilingual') . '</p>'; return $html_content; } } includes/class-hooks-loader.php 0000777 00000011632 15251156640 0012574 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Hooks_Loader * * Buffer class for action and filters * * Collects all the actions and filters then registers them all at once in WP system. */ class TRP_Hooks_Loader{ protected $actions; protected $filters; /** * TRP_Hooks_Loader constructor. */ public function __construct() { $this->actions = array(); $this->filters = array(); } /** * Add action to array. * * @param string $hook Action hook. * @param string $component Object containing the method. Leave null for functions. * @param string $callback Method name. * @param int $priority WP priority. * @param int $accepted_args Number of accepted args. */ public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 0 ) { $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); } /** * Add filter to array. * * @param string $hook Filter hook. * @param string $component Object containing the method. Leave null for functions. * @param string $callback Method name. * @param int $priority WP priority. * @param int $accepted_args Number of accepted args. */ public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); } /** * Remove $hook from action or filter array * * @param array $array Action or filters array. * @param string $hook Hook to remove. * @param string $callback Function callback to remove (optional). If not set, it will remove all callbacks hooked to $hook. * @param string $component Component to remove (optional). If not set, it will remove all components with the callbacks function name $callback. * @return array Action or filters without the hook. */ private function unset_hook_from_array( $array, $hook, $callback, $component ) { foreach ( $array as $key => $filter ){ if ( $filter['hook'] == $hook ){ if ( !$callback || ( $callback && $filter['callback'] == $callback ) ) { if ( !$component || ( $component && $filter['component'] == $component ) ) { unset( $array[ $key ] ); } } } } return array_values( $array ); } /** * Remove actions or filters registered functions for this hook. * * @param string $hook Hook name. * @param string $callback Function callback to remove (optional). If not set, it will remove all callbacks hooked to $hook. * @param string $component Component to remove (optional). If not set, it will remove all components with the callbacks function name $callback. */ public function remove_hook( $hook, $callback = null, $component = null ){ $this->filters = $this->unset_hook_from_array( $this->filters, $hook, $callback, $component ); $this->actions = $this->unset_hook_from_array( $this->actions, $hook, $callback, $component ); } /** * Add hook to action or filter arrays. * * @param array $hooks Action or filters array. * @param string $hook Hook name. * @param string $component Object name. * @param string $callback Method name. * @param int $priority Priority. * @param int $accepted_args Number of args. * @return array Action or filters array containing the new hook. */ private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { $hooks[] = array( 'hook' => $hook, 'component' => $component, 'callback' => $callback, 'priority' => $priority, 'accepted_args' => $accepted_args ); return $hooks; } /** * Registers hooks with WordPress. * * Hooked on plugins_loaded filter, priority 15 */ public function run() { do_action( 'trp_before_running_hooks', $this ); foreach ( $this->filters as $hook ) { if ( $hook['component'] == null ){ add_filter( $hook['hook'], $hook['callback'], $hook['priority'], $hook['accepted_args'] ); }else{ add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); } } foreach ( $this->actions as $hook ) { if ( $hook['component'] == null ){ add_action( $hook['hook'], $hook['callback'], $hook['priority'], $hook['accepted_args'] ); }else { add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); } } } } includes/string-translation/class-string-translation-helper.php 0000777 00000041625 15251156640 0021173 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_String_Translation_Helper { /* @var TRP_Query */ protected $trp_query; /* @var TRP_String_Translation */ protected $string_translation; protected $settings; /** Functions used by regular, gettext and slugs from SEO Pack */ public function check_ajax( $type, $action ) { if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { if ( ! current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { wp_die( -1, 403 ); } $handle_suffix = ( $action !== 'delete' ) ? '_' . $type : ''; check_ajax_referer( 'string_translation_' . $action . '_strings' . $handle_suffix, 'security' ); $map = [ 'save' => 'trp_save_translations_', 'get' => 'trp_string_translation_get_strings_', 'delete' => 'trp_string_translation_delete_' ]; if ( isset( $_POST['action'] ) && $_POST['action'] === $map[ $action ] . $type ) { return true; } } wp_die(); } public function get_sanitized_query_args( $string_type ) { $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->string_translation ) { $this->string_translation = $trp->get_component( 'string_translation' ); } if ( ! $this->trp_query ) { $this->trp_query = $trp->get_component( 'query' ); } if ( ! $this->settings ) { $trp_settings = $trp->get_component( 'settings' ); $this->settings = $trp_settings->get_settings(); } $query_args = array(); $posted_query = ( empty( $_POST['query'] ) ) ? array() : json_decode( stripslashes( $_POST['query'] ), true ); /* phpcs:ignore */ /* sanitized below */ // translation status $translation_status_filters = $this->string_translation->get_translation_status_filters(); $query_args['status'] = array(); foreach ( $translation_status_filters['translation_status'] as $translation_status_key => $value ) { if ( ! empty( $posted_query[ $translation_status_key ] ) && ( $posted_query[ $translation_status_key ] === true || $posted_query[ $translation_status_key ] === 'true' ) ) { $constant_func_name = 'get_constant_' . $translation_status_key; $query_args['status'][] = $this->trp_query->$constant_func_name(); } } if ( count( $query_args['status'] ) === 3 ) { // if all 3 states are true then consider the query as if the no special translation status requirement was requested $query_args['status'] = array(); } // search string - sanitize but don't escape (escaping happens in wpdb->prepare) $query_args['s'] = ( empty( $posted_query['s'] ) ) ? '' : trim( sanitize_text_field( $posted_query['s'] ) ); // page $query_args['page'] = ( empty( $posted_query['page'] ) ? 1 : ( ( intval( $posted_query['page'] ) < 1 ) ? 1 : intval( $posted_query['page'] ) ) ); // language $query_args['language'] = ( ! empty( $posted_query['language'] ) && in_array( $posted_query['language'], $this->settings['translation-languages'] ) ) ? $posted_query['language'] : ''; // order $query_args['order'] = ( empty( $posted_query['order'] ) || ! in_array( $posted_query['order'], array( 'asc', 'desc' ) ) ) ? '' : sanitize_text_field( $posted_query['order'] ); $query_args['orderby'] = ( empty( $posted_query['orderby'] ) ) ? '' : sanitize_text_field( $posted_query['orderby'] ); // specific filters for each string type $string_types = $this->string_translation->get_string_types(); $specific_string_type_config = $string_types[ $string_type ]; foreach ( $specific_string_type_config['filters'] as $specific_filter_key => $specific_filter_values ) { //check if filter domain is selected and assign the domain value if ( $specific_filter_key=='domain' && !empty($posted_query['domain'])){ $specific_filter_values = $this->string_translation->get_gettext_domains(); } $query_args[ $specific_filter_key ] = ( ! empty( $posted_query[ $specific_filter_key ] ) && isset( $specific_filter_values[ $posted_query[ $specific_filter_key ] ] ) ) ? $posted_query[ $specific_filter_key ] : ''; } return apply_filters( 'trp_sanitized_query_args', $query_args, $string_type, $string_types ); } /** Functions used for regular and gettext */ public function add_where_clauses_to_query( $query, $where_clauses ) { if ( count( $where_clauses ) > 0 ) { $query .= 'WHERE '; foreach ( $where_clauses as $where_clause ) { $query .= $where_clause . ' AND '; } $query = rtrim( $query, ' AND' ) . ' '; } return $query; } public function get_language_table_column_based_query_for_filters( $filters, $translation_languages, $sanitized_args ) { $where_clauses = array(); foreach ( $filters as $column_name => $filter_name ) { if ( ! empty( $sanitized_args[ $filter_name ] ) ) { $column_query = '( '; foreach ( $translation_languages as $language ) { $column_query .= $this->get_column_query( $column_name, $sanitized_args[ $filter_name ], esc_sql( sanitize_text_field( $language ) ) ) . ' OR '; } $column_query = rtrim( $column_query, ' OR ' ) . ' ) '; $where_clauses[] = $column_query; } } return $where_clauses; } public function get_column_query( $column_name, $column_values, $language ) { $query = ''; if ( is_array( $column_values ) ) { foreach ( $column_values as $value ) { $query .= $language . '.' . $column_name . ' = ' . $value . ' OR '; } } else { $query .= $language . '.' . $column_name . ' = ' . $column_values . ' OR '; } $query = rtrim( $query, ' OR ' ); return $query; } public function get_join_language_table_sql( $table_name, $language ) { return 'LEFT JOIN ' . $table_name . ' AS ' . $language . ' ON ' . $language . '.original_id = original_strings.id '; } public function get_join_meta_table_sql( $table_name ) { return 'LEFT JOIN ' . $table_name . ' AS original_meta ON original_meta.original_id = original_strings.id '; } /** * Parse search input for exact match detection * * Detects if the search term is wrapped in quotes (plain or escaped) for exact matching. * Supports both "term" and \"term\" formats. * * @param string $search_input The raw search input from user * @return array { * Array containing parsed search information * * @type bool $is_exact_match Whether this is an exact match search (quoted) * @type string $search_term The cleaned search term without quotes * } */ public function parse_search_input( $search_input ) { $is_exact_match = false; $search_term = $search_input; if ( strlen( $search_input ) >= 2 ) { // Check for escaped quotes \"...\" if ( substr( $search_input, 0, 2 ) === '\"' && substr( $search_input, -2 ) === '\"' ) { $is_exact_match = true; $search_term = substr( $search_input, 2, -2 ); } // Check for plain quotes "..." elseif ( isset( $search_input[0] ) && $search_input[0] === '"' && $search_input[ strlen( $search_input ) - 1 ] === '"' ) { $is_exact_match = true; $search_term = substr( $search_input, 1, -1 ); } } return array( 'is_exact_match' => $is_exact_match, 'search_term' => $search_term ); } /** * Used by regular and gettext strings for returning original ids matching filters * * @param $type * @param $original_table * @param $original_meta_table * @param $get_table_name_func * @param $filters * * @return array array( 'original_ids' => $original_ids, 'total_item_count' => $total_item_count ); */ public function get_originals_results( $type, $original_table, $original_meta_table, $get_table_name_func, $filters ) { $this->check_ajax( $type, 'get' ); global $wpdb; $trp = TRP_Translate_Press::get_trp_instance(); $string_translation = $trp->get_component( 'string_translation' ); $trp_query = $trp->get_component( 'query' ); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $config = $string_translation->get_configuration_options(); $sanitized_args = $this->get_sanitized_query_args( $type ); $where_clauses = array(); if ( ! empty( $sanitized_args['translation-block-type'] ) ) { $mapping_array = array( 'individual_string' => 0, 'translation_block' => 1 ); $sanitized_args['translation-block-type'] = $mapping_array[ $sanitized_args['translation-block-type'] ]; } // language filter if ( empty( $sanitized_args['language'] ) ) { // all language tables are needed for table joining $translation_languages = array(); foreach ( $settings['translation-languages'] as $language ) { // regular strings don't have default language table. English language does not react to "Not translated/Manually/Automatically" if no specific language is selected if ( $language === $settings['default-language'] && $type === 'regular' || ( $type === 'gettext' && $this->string_starts_with($language, 'en') ) ) { continue; } $translation_languages[] = $language; } } else { // only current language is needed for table joining $translation_languages = array( $sanitized_args['language'] ); } $counting_query = "SELECT COUNT(*) "; $results_query = "SELECT DISTINCT original_strings.id, original_strings.original "; $results_query .= ( $type === 'gettext' ) ? ', original_strings.domain, original_strings.context, original_strings.original_plural ' : ''; $query = "FROM `" . sanitize_text_field( $original_table ) . "` AS original_strings "; if ( ( ! empty( $sanitized_args['status'] ) || ! empty( $sanitized_args['translation-block-type'] ) ) && empty( $sanitized_args['s'] ) ) { // joining translation tables is needed only when we have filter for translation status or for translation block type foreach ( $translation_languages as $language ) { $query .= $this->get_join_language_table_sql( sanitize_text_field( $trp_query->$get_table_name_func( $language ) ), esc_sql( sanitize_text_field( $language ) ) ); } // translation status and block type $where_clauses = array_merge( $where_clauses, $this->get_language_table_column_based_query_for_filters( $filters, $translation_languages, $sanitized_args ) ); } // original_meta table only needed when filter by type is set if ( ! empty( $sanitized_args['type'] ) && $sanitized_args['type'] !== 'trp_default' ) { $query .= $this->get_join_meta_table_sql( $original_meta_table ); } // Filter by type ( email ) if ( ! empty( $sanitized_args['type'] ) && $sanitized_args['type'] !== 'trp_default' ) { if ( $sanitized_args['type'] === 'email' ) { $where_clauses[] = "original_meta.meta_key='in_email' and original_meta.meta_value = 'yes' "; } } // search if ( ! empty( $sanitized_args['s'] ) ) { // Use helper method to parse search input for exact match detection $search_data = $this->parse_search_input( $sanitized_args['s'] ); $is_exact_match = $search_data['is_exact_match']; $search_term = $search_data['search_term']; // Properly escape the search term for SQL $search_term_escaped = esc_sql( $search_term ); $search = [ 'queries' => [], 'clauses' => [] ]; foreach ( $translation_languages as $language ){ $table = $trp_query->$get_table_name_func( $language ); $search['queries'][ $language ] = $results_query . "FROM `" . sanitize_text_field( $original_table ) . "` AS original_strings " . "LEFT JOIN $table AS $language ON $language.original_id = original_strings.id "; if ( ! empty( $sanitized_args['type'] ) && $sanitized_args['type'] !== 'trp_default' ) { // Ensure we also join the meta table for language-specific search queries when filtering by type (e.g., 'email') $search['queries'][ $language ] .= $this->get_join_meta_table_sql( $original_meta_table ); } // Use exact match or partial match based on quotes if ( $is_exact_match ) { $language_clauses = ["$language.translated = '$search_term_escaped'"]; } else { // Use esc_like to escape special LIKE wildcards (%, _, \) $search_term_like = '%' . $wpdb->esc_like( $search_term_escaped ) . '%'; $language_clauses = ["$language.translated LIKE '$search_term_like'"]; } if ( ! empty( $sanitized_args['status'] ) ) { $status_array = array_map( function( $status ) use ( $language ){ return "$language.status = $status"; }, $sanitized_args['status'] ); $status_clause = implode( ' OR ', $status_array ); $language_clauses[] = "($status_clause)"; } if ( ! empty( $sanitized_args['translation-block-type'] ) ) { $block_type = $sanitized_args['translation-block-type']; $language_clauses[] = "$language.block_type = $block_type"; } $search['clauses'][$language] = array_merge( $where_clauses, $language_clauses ); } // Use exact match or partial match for originals based on quotes if ( $is_exact_match ) { $where_clauses[] = "(original_strings.original = '$search_term_escaped' )"; } else { // Use esc_like to escape special LIKE wildcards (%, _, \) $search_term_like = '%' . $wpdb->esc_like( $search_term_escaped ) . '%'; $where_clauses[] = "(original_strings.original LIKE '$search_term_like' )"; } } if ( ! empty( $sanitized_args['domain'] ) ) { $domain_escaped = esc_sql( $sanitized_args['domain'] ); $domain_like = '%' . $wpdb->esc_like( $domain_escaped ) . '%'; $where_clauses[] = "(original_strings.domain LIKE '$domain_like' )"; } $query = $this->add_where_clauses_to_query( $query, $where_clauses ); if ( isset( $search ) ) { foreach ( $search['queries'] as $language => &$search_query ) { $search_query = $this->add_where_clauses_to_query( $search_query, $search['clauses'][$language] ); } $search['queries'] = array_merge( $search['queries'], [$results_query . $query] ); $query = implode( ' UNION ', $search['queries'] ); } $counting_query .= isset( $search ) ? "FROM ($query) as union_query" : $query; // order by if ( ! empty( $sanitized_args['orderby'] ) ) { if ( $sanitized_args['orderby'] === 'original' ) { // When using UNION (search), can't use table-qualified column names in ORDER BY $column_name = isset( $search ) ? $sanitized_args['orderby'] : 'original_strings.' . $sanitized_args['orderby']; $order_clause = 'ORDER BY ' . $column_name . ' ' . $sanitized_args['order'] . ' '; $query .= $order_clause; } } // pagination $query .= 'LIMIT ' . ( $sanitized_args['page'] - 1 ) * $config['items_per_page'] . ', ' . $config['items_per_page']; $total_item_count = $wpdb->get_var( $counting_query ); $original_ids = array(); $originals = array(); if ( $total_item_count > 0 ) { // query search to retrieve IDs of original strings needed $results_query = isset( $search ) ? $query : $results_query . $query; $originals = $wpdb->get_results( $results_query, OBJECT_K ); $original_ids = array_keys( $originals ); } return array( 'original_ids' => $original_ids, 'originals' => $originals, 'total_item_count' => $total_item_count ); } private function string_starts_with($haystack, $needle){ return (string)$needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0; } public function get_original_ids_from_post_request() { $trp = TRP_Translate_Press::get_trp_instance(); if ( !$this->settings ) { $trp_settings = $trp->get_component( 'settings' ); $this->settings = $trp_settings->get_settings(); } $all_strings = json_decode( stripslashes( $_POST['strings'] ), true ); //phpcs:ignore $ids = []; foreach ( $all_strings as $string ) { if ( !empty( $string['originalId'] ) ) { $ids[] = (int)$string['originalId']; } else { foreach ( $this->settings['translation-languages'] as $language ) { if ( $this->settings['default-language'] == $language ) { continue; } if ( isset( $string['translationsArray'][ $language ]['original_id'] ) && (int)$string['translationsArray'][ $language ]['original_id'] > 0 ) { $ids[] = (int)$string['translationsArray'][ $language ]['original_id']; // all languages have identical original table id break; }; } } } return $ids; } } includes/string-translation/class-string-translation.php 0000777 00000056073 15251156640 0017721 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_String_Translation { protected $settings; protected $loader; /* @var TRP_Translation_Manager */ protected $translation_manager; // flat structure of string_types_config protected $string_types = array(); // actual classes that may get retrieved from elsewhere through get_string_type_API() protected $string_type_apis = array(); /** * @var array */ protected $gettext_domains; public function __construct( $settings, $loader ) { $this->settings = $settings; $this->loader = $loader; } public function register_ajax_hooks() { // Build a flat structure of string types $string_types_config = $this->string_types_config(false); foreach ( $string_types_config as $string_type_key => $string_type_value ) { if ( $string_type_value['category_based'] ) { foreach ( $string_type_value['categories'] as $substring_type_key => $substring_type_value ) { $this->string_types[ $substring_type_key ] = $substring_type_value; } } else { $this->string_types[ $string_type_key ] = $string_type_value; } } // Include all classes and hooks needed for Visual Editor foreach ( $this->string_types as $string_type_key => $string_type_value ) { if ( $string_type_key == 'emails' || (isset($string_type_value['type']) && $string_type_value['type'] == 'upsale-slugs' ) ) { // it's just gettext. We are using it to create an extra tab with this filter continue; } require_once $string_type_value['plugin_path'] . 'includes/string-translation/class-string-translation-api-' . $string_type_key . '.php'; $class_name = 'TRP_String_Translation_API_' . $string_type_value['class_name_suffix']; $this->string_type_apis[ $string_type_key ] = new $class_name( $this->settings ); // Different hook for String Translation compared to Visual Editor add_action( 'wp_ajax_trp_string_translation_get_strings_' . $string_type_key, array( $this->string_type_apis[ $string_type_key ], 'get_strings' ) ); if ( $string_type_key == 'gettext' ) { add_action( 'wp_ajax_trp_string_translation_get_missing_gettext_strings', array( $this->string_type_apis[ 'gettext' ], 'get_missing_gettext_strings' ) ); add_action( 'wp_ajax_trp_string_translation_get_strings_by_original_ids_gettext', array( $this->string_type_apis[ 'gettext' ], 'get_strings_by_original_ids' ) ); } // Same hook as for Visual Editor save translations add_action( 'wp_ajax_trp_save_translations_' . $string_type_key, array( $this->string_type_apis[ $string_type_key ], 'save_strings' ) ); add_action( 'wp_ajax_trp_string_translation_delete_' . $string_type_key, array( $this->string_type_apis[ $string_type_key ], 'delete_strings' ) ); } } public function get_string_types() { return $this->string_types; } public function get_string_type_API( $string_type ) { return $this->string_type_apis[ $string_type ]; } /** * Start String Translation Editor. * * Hooked to template_include. * * @param string $page_template Current page template. * @return string Template for translation Editor. */ public function string_translation_editor( $page_template ) { if ( !$this->is_string_translation_editor() ) { return $page_template; } return TRP_PLUGIN_DIR . 'includes/string-translation/string-translation-editor.php'; } /** * Return true if we are on String translation page. * * Also wp_die and show 'Cheating' message if we are on translation page but user does not have capabilities to view it * * @return bool */ public function is_string_translation_editor() { if ( isset( $_REQUEST['trp-string-translation'] ) && sanitize_text_field( $_REQUEST['trp-string-translation'] ) === 'true' ) { if ( current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) && !is_admin() ) { return true; } else { wp_die( '<h1>' . esc_html__( 'Cheatin’ uh?' ) . '</h1>' . //phpcs:ignore '<p>' . esc_html__( 'Sorry, you are not allowed to access this page.' ) . '</p>', //phpcs:ignore 403 ); } } return false; } /** * Enqueue script and styles for String Translation Editor page * * Hooked to trp_string_translation_editor_footer */ public function enqueue_scripts_and_styles() { $trp = TRP_Translate_Press::get_trp_instance(); if ( !$this->translation_manager ) { $this->translation_manager = $trp->get_component( 'translation_manager' ); } wp_enqueue_style( 'trp-editor-style', TRP_PLUGIN_URL . 'assets/css/trp-editor.css', array( 'dashicons', 'buttons' ), TRP_PLUGIN_VERSION ); wp_enqueue_script( 'trp-string-translation-editor', TRP_PLUGIN_URL . 'assets/js/trp-string-translation-editor.js', array(), TRP_PLUGIN_VERSION ); wp_localize_script( 'trp-string-translation-editor', 'trp_editor_data', $this->translation_manager->get_trp_editor_data() ); wp_localize_script( 'trp-string-translation-editor', 'trp_string_translation_data', $this->get_string_translation_data() ); // Show upload media dialog in default language switch_to_locale( $this->settings['default-language'] ); // Necessary for add media button wp_enqueue_media(); // Necessary for add media button wp_print_media_templates(); restore_current_locale(); // Necessary for translate-dom-changes to have a nonce as the same user as the Editor. // The Preview iframe (which loads translate-dom-changes script) can load as logged out which sets an different nonce $scripts_to_print = apply_filters( 'trp-scripts-for-editor', array( 'jquery', 'jquery-ui-core', 'jquery-effects-core', 'jquery-ui-resizable', 'trp-string-translation-editor') ); $styles_to_print = apply_filters( 'trp-styles-for-editor', array( 'dashicons', 'trp-editor-style', 'media-views', 'imgareaselect', 'common', 'forms', 'list-tables', 'buttons' /*'wp-admin', 'common', 'site-icon', 'buttons'*/ ) ); wp_print_scripts( $scripts_to_print ); wp_print_styles( $styles_to_print ); // Necessary for add media button print_footer_scripts(); } public function get_string_translation_data() { $string_translation_data = array( 'string_types_config' => $this->string_types_config(true), 'st_editor_strings' => $this->get_st_editor_strings(), 'translation_status_filters' => $this->get_translation_status_filters(), 'default_actions' => $this->get_default_actions(), 'config' => $this->get_configuration_options() ); return apply_filters( 'trp_string_translation_data', $string_translation_data ); } public function get_translation_status_filters() { $filters = array( 'translation_status' => array( 'human_reviewed' => esc_html__( 'Manually translated', 'translatepress-multilingual' ), 'machine_translated' => esc_html__( 'Automatically translated', 'translatepress-multilingual' ), 'not_translated' => esc_html__( 'Not translated', 'translatepress-multilingual' ) ) ); return apply_filters( 'trp_st_default_filters', $filters ); } public function get_default_actions() { $actions = array( 'bulk_actions' => array( 'trp_default' => array( 'name' => esc_html__( 'Bulk Actions', 'translatepress-multilingual' ) ), 'delete' => array( 'name' => esc_html__( 'Delete entries', 'translatepress-multilingual' ), 'nonce' => wp_create_nonce( 'string_translation_delete_strings' ) ), ), 'actions' => array( 'edit' => esc_html__( 'Edit', 'translatepress-multilingual' ), 'delete' => esc_html__( 'Delete', 'translatepress-multilingual' ) ) ); return apply_filters( 'trp_st_default_actions', $actions ); } public function get_gettext_domains() { if ( !$this->gettext_domains ) { $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); global $wpdb; $query = 'SELECT DISTINCT domain FROM `' . $trp_query->get_table_name_for_gettext_original_strings() . '` ORDER BY domain ASC'; $this->gettext_domains = $wpdb->get_results( $query, OBJECT_K ); foreach ( $this->gettext_domains as $domain => $value ) { $this->gettext_domains[ $domain ] = $domain; } } return $this->gettext_domains; } public function get_st_editor_strings() { $st_editor_strings = array( 'filter' => esc_html__( 'Filter', 'translatepress-multilingual' ), 'clear_filter' => esc_html__( 'Clear filters', 'translatepress-multilingual' ), 'filter_by_language' => esc_html__( 'Language', 'translatepress-multilingual' ), 'add_new' => esc_html__( 'Add New', 'translatepress-multilingual' ), 'rescan_gettext' => esc_html__( 'Rescan plugins and theme for strings', 'translatepress-multilingual' ), 'scanning_gettext' => esc_html__( 'Scanning plugins and theme for strings...', 'translatepress-multilingual' ), 'gettext_scan_completed' => esc_html__( 'Plugins and theme scan is complete', 'translatepress-multilingual' ), 'gettext_scan_error' => esc_html__( 'Plugins and theme scan did not finish due to an error', 'translatepress-multilingual' ), 'importexport' => esc_html__( 'Import / Export', 'translatepress-multilingual' ), 'items' => esc_html__( 'items', 'translatepress-multilingual' ), 'of' => esc_html_x( 'of', 'page 1 of 3', 'translatepress-multilingual' ), 'see_more' => esc_html__( 'See More', 'translatepress-multilingual' ), 'see_less' => esc_html__( 'See Less', 'translatepress-multilingual' ), 'apply' => esc_html__( 'Apply', 'translatepress-multilingual' ), 'no_strings_match_query' => esc_html__( 'No strings match your query.', 'translatepress-multilingual' ), 'no_strings_match_rescan'=> esc_html__( 'Try to rescan plugins and theme for strings.', 'translatepress-multilingual' ), 'request_error' => esc_html__( 'An error occurred while loading results. Most likely you were logged out. Reload page?', 'translatepress-multilingual' ), 'found_in_translation' => esc_html__( 'found in translation', 'translatepress-multilingual' ), 'select_all' => esc_html__( 'Select All', 'translatepress-multilingual' ), 'select_visible' => esc_html__( 'Select Visible', 'translatepress-multilingual' ), 'select_all_warning' => esc_html__( 'You are about to perform this action on all the strings matching your filter, not just the visibly checked. To perform the action only to the visible strings click "Select Visible" from the table header dropdown.', 'translatepress-multilingual' ), 'select_visible_warning' => esc_html__( 'You are about to perform this action only on the visible strings. To perform the action on all the strings matching the filter click "Select All" from the table header dropdown.', 'translatepress-multilingual' ), 'type_a_word_for_security' => esc_html__( 'To continue please type the word:', 'translatepress-multilingual' ), 'incorect_word_typed' => esc_html__( 'The word typed was incorrect. Action was cancelled.', 'translatepress-multilingual' ), 'in' => esc_html_x( 'in', 'Untranslated in this language', 'translatepress-multilingual' ), // specific bulk actions 'delete_warning' => esc_html__( 'Warning: This action cannot be undone. Deleting a string will remove its current translation. The original string will appear again in this interface after TranslatePress detects it. This action is NOT equivalent to excluding the string from being translated again.', 'translatepress-multilingual' ), 'entries_deleted' => esc_html__( '%d original entries and their translations were deleted.', 'translatepress-multilingual' ), // tooltips 'next_page' => esc_html__( 'Navigate to next page', 'translatepress-multilingual' ), 'previous_page' => esc_html__( 'Navigate to previous page', 'translatepress-multilingual' ), 'first_page' => esc_html__( 'Navigate to first page', 'translatepress-multilingual' ), 'last_page' => esc_html__( 'Navigate to last page', 'translatepress-multilingual' ), 'navigate_to_page' => esc_html__( 'Type a page number to navigate to', 'translatepress-multilingual' ), 'wrong_page' => esc_html__( 'Incorrect page number. Type a page number between 1 and total number of pages', 'translatepress-multilingual' ), 'search_tooltip' => html_entity_decode(esc_html__( 'Search original and translated strings containing typed keywords while also matching selected filters. Place string in quotes for exact match: "string"', 'translatepress-multilingual' )), 'filter_tooltip' => esc_html__( 'Filter strings according to selected translation status, filters and keywords and selected filters', 'translatepress-multilingual' ), 'clear_filter_tooltip' => esc_html__( 'Removes selected filters', 'translatepress-multilingual' ), 'select_all_tooltip' => esc_html__( 'See options for selecting all strings', 'translatepress-multilingual' ), 'sort_by_column' => esc_html__( 'Click to sort strings by this column', 'translatepress-multilingual' ), 'filter_by_language_tooltip' => esc_html__( 'Language in which the translation status filter applies. Leave unselected for the translation status to apply to ANY language', 'translatepress-multilingual' ), 'search_placeholder' => esc_html__('Search', 'translatepress-multilingual'), 'other_slugs_tooltip' => esc_html__( 'Slugs that are not found in either one of the other categories.', 'translatepress-multilingual') ); return apply_filters( 'trp_st_editor_strings', $st_editor_strings ); } /** * @return mixed */ public function string_types_config($needs_gettext = false) { $string_types_config = array( 'gettext' => array( 'type' => 'gettext', 'name' => esc_html__( 'Plugins and Theme String Translation', 'translatepress-multilingual' ), 'tab_name' => esc_html__( 'Gettext', 'translatepress-multilingual' ), 'search_name' => esc_html__( 'Search Gettext Strings', 'translatepress-multilingual' ), 'class_name_suffix' => 'Gettext', // 'add_new' => true, 'scan_gettext' => true, 'plugin_path' => TRP_PLUGIN_DIR, 'nonces' => $this->get_nonces_for_type( 'gettext' ), 'table_columns' => array( 'id' => esc_html__( 'ID', 'translatepress-multilingual' ), 'original' => esc_html__( 'Original String', 'translatepress-multilingual' ), 'translated' => esc_html__( 'Translation', 'translatepress-multilingual' ), 'domain' => esc_html__( 'Domain', 'translatepress-multilingual' ), ), 'show_original_language' => true, 'category_based' => false, 'filters' => array( 'domain' => array_merge( array( 'trp_default' => esc_html__( 'Filter by domain', 'translatepress-multilingual' ) ), $needs_gettext ? $this->get_gettext_domains() : array() ), 'type' => array( 'trp_default' => esc_html__( 'Filter by type', 'translatepress-multilingual' ), 'email' => esc_html__( 'Email text', 'translatepress-multilingual' ) ), ) ), 'emails' => array( 'type' => 'gettext', 'name' => esc_html__( 'Emails String Translation', 'translatepress-multilingual' ), 'tab_name' => esc_html__( 'Emails', 'translatepress-multilingual' ), 'search_name' => esc_html__( 'Search Email Strings', 'translatepress-multilingual' ), 'class_name_suffix' => 'Gettext', // 'add_new' => true, 'scan_gettext' => true, 'plugin_path' => TRP_PLUGIN_DIR, 'nonces' => $this->get_nonces_for_type( 'gettext' ), 'table_columns' => array( 'id' => esc_html__( 'ID', 'translatepress-multilingual' ), 'original' => esc_html__( 'Original String', 'translatepress-multilingual' ), 'translated' => esc_html__( 'Translation', 'translatepress-multilingual' ), 'domain' => esc_html__( 'Domain', 'translatepress-multilingual' ), ), 'show_original_language' => true, 'category_based' => false, 'filters' => array( 'domain' => array_merge( array( 'trp_default' => esc_html__( 'Filter by domain', 'translatepress-multilingual' ) ), $needs_gettext ? $this->get_gettext_domains() : array() ), ) ), 'regular' => array( 'type' => 'regular', 'name' => esc_html__( 'User Inputted String Translation', 'translatepress-multilingual' ), 'tab_name' => esc_html__( 'Regular', 'translatepress-multilingual' ), 'search_name' => esc_html__( 'Search Regular Strings', 'translatepress-multilingual' ), 'class_name_suffix' => 'Regular', // 'add_new' => true, 'plugin_path' => TRP_PLUGIN_DIR, 'nonces' => $this->get_nonces_for_type( 'regular' ), 'table_columns' => array( 'id' => esc_html__( 'ID', 'translatepress-multilingual' ), 'original' => esc_html__( 'Original String', 'translatepress-multilingual' ), 'translated' => esc_html__( 'Translation', 'translatepress-multilingual' ) ), 'show_original_language' => false, 'category_based' => false, 'filters' => array( 'translation-block-type' => array( 'trp_default' => esc_html__( 'Filter by Translation Block', 'translatepress-multilingual' ), 'individual_string' => 'Individual string', 'translation_block' => 'Translation Block' ) ) ) ); if ( !apply_filters('trp_show_regular_strings_string_translation', true ) ){ unset($string_types_config['regular']); } $seo_pack_active = class_exists( 'TRP_IN_Seo_Pack'); if( !$seo_pack_active ){ $upsale_slugs_string_type = array( 'slugs' => array( 'type' => 'upsale-slugs', 'name' => __( 'URL Slugs Translation', 'translatepress-multilingual' ), 'tab_name' => __( 'Slugs', 'translatepress-multilingual' ), 'class_name_suffix' => 'Regular', 'plugin_path' => TRP_PLUGIN_DIR, 'category_based' => false, 'nonces' => $this->get_nonces_for_type( 'regular' ), ) ); $string_types_config = $upsale_slugs_string_type + $string_types_config; } return apply_filters( 'trp_st_string_types_config', $string_types_config, $this ); } public function get_nonces_for_type( $type ) { $nonces = array( 'get_strings' => wp_create_nonce( 'string_translation_get_strings_' . $type ), 'get_missing_strings' => wp_create_nonce( 'string_translation_get_missing_strings_' . $type ), 'get_strings_by_original_id' => wp_create_nonce( 'string_translation_get_strings_by_original_ids_' . $type ), 'save_strings' => wp_create_nonce( 'string_translation_save_strings_' . $type ) ); return apply_filters( 'trp_string_translation_nonces', $nonces, $type ); } public function get_configuration_options() { $config = array( 'items_per_page' => 20, 'see_more_max_length' => 5000 ); return apply_filters( 'trp_string_translation_config', $config ); } public function register_string_types( $registered_string_types ) { foreach ( $this->string_types as $string_type => $value ) { if ( !in_array( $string_type, $registered_string_types ) ) { $registered_string_types[] = $string_type; } } return $registered_string_types; } /* * hooked to trp_editor_nonces */ public function add_nonces_for_saving_translation( $nonces ) { foreach ( $this->string_types as $string_type => $string_config ) { if ( !isset( $nonces[ 'savetranslationsnonce' . $string_type ] ) ) { $nonces[ 'savetranslationsnonce' . $string_type ] = $string_config['nonces']['save_strings']; } } return $nonces; } } includes/string-translation/class-gettext-scan.php 0000777 00000013556 15251156640 0016464 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_Gettext_Scan { protected $settings; public function __construct( $settings ) { $this->settings = $settings; } public function scan_gettext() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX && current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { if ( isset( $_POST['action'] ) && $_POST['action'] === 'trp_scan_gettext' ) { check_ajax_referer( 'scangettextnonce', 'security' ); $status = $this->scan(); echo trp_safe_json_encode( $status ); //phpcs:ignore } } wp_die(); } public function scan() { global $trp_gettext_strings_discovered; require_once TRP_PLUGIN_DIR . 'assets/lib/potx/potx.php'; $start_time = microtime( true ); $scan_paths_completed = get_option( 'trp_gettext_scan_paths_completed', array( 'paths_completed' => 0, 'current_filename' => null ) ); $paths_to_scan = apply_filters( 'trp_paths_to_scan_for_gettext', array_merge( $this->get_active_plugins_paths(), $this->get_active_theme_paths() ) ); $filename = ''; $trp_gettext_strings_discovered = array(); $path_key = 0; foreach ( $paths_to_scan as $path_key => $path ) { if ( $path_key < $scan_paths_completed['paths_completed'] ) { continue; } $interrupted_in_the_recursive_scan = false; if ( is_file( $path ) ) { trp_potx_process_file( realpath( $path ), 0, 'trp_save_gettext_string' ); } elseif (is_dir($path)) { $iterator = new RecursiveDirectoryIterator( $path ); // loop through directory and get _e(), __() etc. function calls foreach ( new RecursiveIteratorIterator( $iterator ) as $filename => $current_file ) { if( $scan_paths_completed['current_filename'] ){ if( $filename == $scan_paths_completed['current_filename'] ) { $scan_paths_completed['current_filename'] = null; } continue; } if ( isset( $current_file ) ) { $current_file_pathinfo = pathinfo( $current_file ); if ( ! empty( $current_file_pathinfo['extension'] ) && $current_file_pathinfo['extension'] == "php" ) { if ( file_exists( $current_file ) ) { trp_potx_process_file( realpath( $current_file ), 0, 'trp_save_gettext_string' ); if ( ( microtime( true ) - $start_time ) > 2 ) { $path_key--; $interrupted_in_the_recursive_scan = true; break; } } } } } } if ( ( microtime( true ) - $start_time ) > 2 ) { $filename = ($interrupted_in_the_recursive_scan) ? $filename : ''; break; } } $this->insert_gettext_in_db(); $paths_completed = $path_key + 1; $total_paths_to_scan = count( $paths_to_scan ); $return_array = array( 'completed' => false, 'progress_message' => sprintf( esc_html__( 'Scanning item %1$d of %2$d...', 'translatepress-multilingual' ), $paths_completed, $total_paths_to_scan ) ); if ( $paths_completed >= $total_paths_to_scan ) { delete_option( 'trp_gettext_scan_paths_completed' ); $return_array['completed'] = true; } else { update_option( 'trp_gettext_scan_paths_completed', array( 'paths_completed' => $paths_completed, 'current_filename' => $filename ) ) ; } return $return_array; } public function get_active_plugins_paths() { $the_plugins = get_option( 'active_plugins' ); $folders = array(); foreach ( $the_plugins as $value ) { $string = explode( '/', $value ); if ( isset( $string[0] ) ) { $folders[] = trailingslashit( WP_PLUGIN_DIR ) . $string[0]; } } return $folders; } public function get_active_theme_paths() { $folders = array(); // current theme. child theme if present $child_theme_dir = get_stylesheet_directory(); $folders[] = $child_theme_dir; // parent theme $parent_theme_dir = get_template_directory(); if ( $parent_theme_dir !== $child_theme_dir ) { $folders[] = $parent_theme_dir; } return $folders; } public function insert_gettext_in_db() { global $trp_gettext_strings_discovered; $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $gettext_insert_update = $trp_query->get_query_component( 'gettext_insert_update' ); $inserted_original_ids = $gettext_insert_update->gettext_original_strings_sync( $trp_gettext_strings_discovered ); $email_paths = apply_filters( 'trp_email_paths_', array( 'templates/emails/', 'includes/emails/', 'woocommerce/emails/' ) ); // Windows servers have paths with \ instead of / $reverse_paths = array(); foreach($email_paths as $path ){ $reverse_paths[] = str_replace('/','\\', $path ); } $email_paths = array_merge($email_paths, $reverse_paths ); $strings_in_emails = array(); foreach ( $trp_gettext_strings_discovered as $key => $string ) { foreach ( $email_paths as $email_path ) { if ( strpos( $string['file'], $email_path ) !== false ) { $strings_in_emails[] = $inserted_original_ids[$key]; break; } } } $gettext_insert_update->bulk_insert_original_id_meta( $strings_in_emails, 'in_email', 'yes' ); } } function trp_save_gettext_string( $original, $domain, $context, $file, $line, $string_mode, $text_plural = false ) { global $trp_gettext_strings_discovered; if ( !empty( $original ) ) { $domain = ( empty( $domain ) ) ? 'default' : $domain; $context = ( empty( $context ) ) ? 'trp_context' : $context; $text_plural = ( empty( $text_plural ) ) ? '' : $text_plural; if ( ! isset( $trp_gettext_strings_discovered[ $context . '::' . $domain . '::' . $original ] ) ) { $trp_gettext_strings_discovered[ $context . '::' . $domain . '::' . $original ] = array( 'original' => $original, 'domain' => $domain, 'context' => $context, 'original_plural' => $text_plural, 'file' => $file ); } } } includes/string-translation/class-string-translation-api-gettext.php 0000777 00000016373 15251156640 0022151 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_String_Translation_API_Gettext { protected $type = 'gettext'; protected $helper; /* @var TRP_Query */ public function __construct( $settings ) { $this->helper = new TRP_String_Translation_Helper(); } /** * Returns only original string ids * * @return void */ public function get_strings(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $originals_results = $this->helper->get_originals_results( $this->type, $trp_query->get_table_name_for_gettext_original_strings(), $trp_query->get_table_name_for_gettext_original_meta(), 'get_gettext_table_name', array( 'status' => 'status' ) ); $query_args = $this->helper->get_sanitized_query_args( $this->type ); // Used to display (found in translation) label next to the original string in case we found the search result in translations if ( !empty( $query_args['s'] ) ) set_transient( 'trp_gettext_search', $query_args['s'], 10 ); echo trp_safe_json_encode( array( //phpcs:ignore 'originalIds' => $originals_results['original_ids'], 'totalItems' => $originals_results['total_item_count'], ) ); wp_die(); } /** * Function that inserts in db translation from language files for specified original string ids for a specific language * This request changes locale from the very beginning so all the active plugins/theme load their textdomain translations * * @return void */ public function get_missing_gettext_strings() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { if ( ! current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { wp_die( -1, 403 ); } check_ajax_referer( 'string_translation_get_missing_strings_gettext', 'security' ); $action = 'trp_string_translation_get_missing_gettext_strings'; if ( isset( $_POST['action'] ) && $_POST['action'] === $action && isset( $_POST['original_ids'] ) && isset( $_POST['trp_ajax_language'] ) ) { $original_ids = json_decode( $_POST['original_ids'] ); /* phpcs:ignore */ /* sanitized downstream */ foreach ( $original_ids as $key => $id ) { $original_ids[ $key ] = (int) $id; } $trp_ajax_language = sanitize_text_field( $_POST['trp_ajax_language'] ); $trp = TRP_Translate_Press::get_trp_instance(); $trp_settings = $trp->get_component( 'settings' ); $trp_query = $trp->get_component( 'query' ); $settings = $trp_settings->get_settings(); if ( in_array( $trp_ajax_language, $settings['translation-languages'] ) ) { $language = $trp_ajax_language; } else { wp_die(); } $dictionary = $trp_query->get_gettext_string_rows_by_original_id( $original_ids, $language ); $gettext_manager = $trp->get_component( 'gettext_manager' ); $gettext_manager->add_missing_language_file_translations($dictionary, $language); } } echo trp_safe_json_encode(array()); //phpcs:ignore wp_die(); } /** * Based on original ids, returns all the translations from db for all the languages * * @return void */ public function get_strings_by_original_ids(){ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { if ( ! current_user_can( apply_filters( 'trp_translating_capability', 'manage_options' ) ) ) { wp_die( -1, 403 ); } check_ajax_referer( 'string_translation_get_strings_by_original_ids_gettext', 'security' ); $action = 'trp_string_translation_get_strings_by_original_ids_gettext'; if ( isset( $_POST['action'] ) && $_POST['action'] === $action && isset( $_POST['original_ids'] ) ) { $original_ids = json_decode( $_POST['original_ids'] ); /* phpcs:ignore */ /* sanitized downstream */ foreach ( $original_ids as $key => $id ) { $original_ids[ $key ] = (int) $id; } $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); // query each language table to retrieve translations $dictionaries = array(); foreach ( $settings['translation-languages'] as $language ) { $dictionaries[ $language ] = $trp_query->get_gettext_string_rows_by_original_id( $original_ids, $language ); } /* html entity decode the strings so we display them properly in the textareas */ foreach ($dictionaries as $lang => $dictionary) { foreach ($dictionary as $key => $string) { // Ensure $string is an array before applying array_map if (is_array($string)) { $string = array_map(function($value) { return $value !== null ? html_entity_decode($value) : ''; }, $string); } $dictionaries[$lang][$key] = (object) $string; } } $translation_manager = $trp->get_component('translation_manager'); $localized_text = $translation_manager->string_groups(); $post_language = ( isset( $_POST['language'] ) ) ? sanitize_text_field( $_POST['language'] ) : null; $dictionary_by_original = trp_sort_dictionary_by_original( $dictionaries, 'gettext', $localized_text['gettextstrings'], $post_language ); $search_query = get_transient('trp_gettext_search' ); if ( ! empty( $search_query ) ) { // Use helper method to parse search input for exact match detection $search_data = $this->helper->parse_search_input( $search_query ); $is_exact_match = $search_data['is_exact_match']; $search_term = $search_data['search_term']; foreach ( $dictionary_by_original as &$dictionary ) { foreach ( $dictionary['translationsArray'] as $translationArray ) { if ( $is_exact_match ) { if ( $translationArray->translated === $search_term ) { $dictionary['foundInTranslation'] = true; } } else { if ( strpos( $translationArray->translated, $search_term ) !== false ) { $dictionary['foundInTranslation'] = true; } } } } } echo trp_safe_json_encode( array('dictionary' => $dictionary_by_original ) ); //phpcs:ignore } wp_die(); } } /** Using editor api function hooked for saving. * Implementing save_strings function is not necessary * Leave this function empty, removing it will cause a thrown notice */ public function save_strings() { } public function delete_strings() { $this->helper->check_ajax( 'gettext', 'delete' ); $original_ids = $this->helper->get_original_ids_from_post_request(); $regular_delete = new TRP_Gettext_Delete(); $items_deleted = $regular_delete->delete_strings( $original_ids ); echo trp_safe_json_encode( $items_deleted );//phpcs:ignore wp_die(); } } includes/string-translation/string-translation-editor.php 0000777 00000001153 15251156640 0020067 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); ?> <!DOCTYPE html> <html <?php language_attributes(); ?> class="no-js"> <head> <?php do_action( 'trp_string_translation_editor_head' ); ?> <title>TranslatePress - <?php esc_html_e('String Translation Editor', 'translatepress-multilingual'); ?> </title> </head> <body class="trp-editor-body"> <div id="trp-editor-container"> <trp-string-translation ref="trp_string_translation_editor" > </trp-string-translation> </div> <?php do_action( 'trp_string_translation_editor_footer' ); ?> </body> </html> <?php includes/string-translation/class-string-translation-api-regular.php 0000777 00000010574 15251156640 0022123 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); class TRP_String_Translation_API_Regular { protected $type = 'regular'; protected $helper; protected $translation_render; /* @var TRP_Query */ public function __construct( $settings ) { $this->helper = new TRP_String_Translation_Helper(); $this->translation_render = new TRP_Translation_Render( $settings ); } public function get_strings(){ $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $trp_settings = $trp->get_component( 'settings' ); $settings = $trp_settings->get_settings(); $originals_results = $this->helper->get_originals_results( $this->type, $trp_query->get_table_name_for_original_strings(), $trp_query->get_table_name_for_original_meta(), 'get_table_name', array( 'status' => 'status', 'block_type' => 'translation-block-type' ) ); if ( $originals_results['total_item_count'] > 0 ){ // query each language table to retrieve translations $dictionaries = array(); foreach ( $settings['translation-languages'] as $language ) { if ( $language === $settings['default-language'] ) { continue; } $dictionaries[ $language ] = $trp_query->get_string_rows( $originals_results['original_ids'], array(), $language, 'OBJECT_K', true ); $missing_strings = array_diff_key( $originals_results['originals'], $dictionaries[ $language ] ); $missing_strings_array = array_map( function( $object ){ return $object->original; // convert to array of originals }, $missing_strings ); $current_dictionary_array = array_map( function( $object ){ return $object->original; // convert to array of originals }, $dictionaries[ $language ] ); $full_dictionary_array = array_merge( $missing_strings_array, $current_dictionary_array ); $this->translation_render->process_strings( $full_dictionary_array, $language ); $dictionaries[ $language ] = $trp_query->get_string_rows( array(), $full_dictionary_array, $language ); } $dictionary_by_original = trp_sort_dictionary_by_original( $dictionaries, $this->type, null, null ); $query_args = $this->helper->get_sanitized_query_args( $this->type ); // Used to display (found in translation) label next to the original string in case we found the search result in translations if ( ! empty( $query_args['s'] ) ) { // Use helper method to parse search input for exact match detection $search_data = $this->helper->parse_search_input( $query_args['s'] ); $is_exact_match = $search_data['is_exact_match']; $search_term = $search_data['search_term']; foreach ( $dictionary_by_original as &$dictionary ) { foreach ( $dictionary['translationsArray'] as $translationArray ) { if ( $is_exact_match ) { if ( $translationArray->translated === $search_term ) { $dictionary['foundInTranslation'] = true; } } else { if ( strpos( $translationArray->translated, $search_term ) !== false ) { $dictionary['foundInTranslation'] = true; } } } } } }else{ $dictionary_by_original = array(); } echo trp_safe_json_encode( array( // phpcs:ignore 'dictionary' => $dictionary_by_original, 'totalItems' => $originals_results['total_item_count'] ) ); wp_die(); } /** Using editor api function hooked for saving. * Implementing save_strings function is not necessary * Leave this function empty, removing it will cause a thrown notice */ public function save_strings() { } public function delete_strings() { $this->helper->check_ajax( 'regular', 'delete' ); $original_ids = $this->helper->get_original_ids_from_post_request(); $regular_delete = new TRP_Regular_Delete(); $items_deleted = $regular_delete->delete_strings( $original_ids ); echo trp_safe_json_encode( $items_deleted );//phpcs:ignore wp_die(); } } includes/class-check-invalid-text.php 0000777 00000010031 15251156640 0013660 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Check_Invalid_Text * * Used to exclude problematic strings triggering 'WordPress database error: Could not perform query because it contains invalid data.' from TP query functions. * * Divide et impera method used to minimise number of queries needed to detect needle in haystack. Applied for key functions: * get_existing_translations, insert_strings and update_strings. */ class TRP_Check_Invalid_Text{ protected $table_charset; protected $check_current_query; protected $col_meta; public function get_existing_translations_without_invalid_text( $dictionary, $prepared_query, $strings_array, $language_code, $block_type ){ if ( $this->is_invalid_data_error() ){ $count = count($strings_array); if ( $count <= 1 ){ // fake translated so it doesn't get auto translated or updated in DB later $entry = new stdClass(); $entry->translated = $strings_array[0]; $entry->original = $strings_array[0]; $entry->status = "1"; $entry->invalid_data = true; return array( $strings_array[0] => $entry); }else{ $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $half = floor( $count / 2 ); $array1 = $trp_query->get_existing_translations( array_slice( $strings_array, 0, $half ), $language_code, $block_type ); $array2 = $trp_query->get_existing_translations( array_slice( $strings_array, $half ), $language_code, $block_type ); return array_merge( $array1, $array2 ); } } return $dictionary; } public function insert_translations_without_invalid_text( $new_strings, $language_code, $block_type ){ if ( $this->is_invalid_data_error() ){ $count = count($new_strings); if ( $count <= 1 ){ return; }else{ $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $half = floor( $count / 2 ); $trp_query->insert_strings( array_slice( $new_strings, 0, $half ), $language_code, $block_type ); $trp_query->insert_strings( array_slice( $new_strings, $half ), $language_code, $block_type ); return; } } } public function update_translations_without_invalid_text( $update_strings, $language_code, $block_type ){ if ( $this->is_invalid_data_error() ){ $count = count($update_strings); if ( $count <= 1 ){ return; }else{ $trp = TRP_Translate_Press::get_trp_instance(); $trp_query = $trp->get_component( 'query' ); $half = floor( $count / 2 ); $trp_query->update_strings( array_slice( $update_strings, 0, $half ), $language_code, $block_type ); $trp_query->update_strings( array_slice( $update_strings, $half ), $language_code, $block_type ); return; } } } public function is_invalid_data_error(){ // Using $trp_disable_invalid_data_detection as a sort of apply_filters to turn off this feature. // Not using proper WP filter to reduce page load time. This function is executed many times. global $wpdb, $trp_disable_invalid_data_detection; if ( !empty($wpdb->last_error) && !isset( $trp_disable_invalid_data_detection) ) { $invalid_data_error = __( 'WordPress database error: Could not perform query because it contains invalid data.' ); /* phpcs:ignore */ /* $domain arg is purposely omitted because we want to identify the exact wpdb last_error message. Only used for comparison reasons, it's not actually displayed. */ if ( $wpdb->last_error == $invalid_data_error ) { return true; } } return false; } } includes/class-language-switcher-tab.php 0000777 00000045341 15251156640 0014366 0 ustar 00 <?php if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class TRP_Language_Switcher_Tab * * Extracts language-switcher settings (with backwards-compat migration), * then renders the single Vue mount point carrying the serialized config. */ class TRP_Language_Switcher_Tab { private array $settings; /** * * @param array $settings */ public function __construct( array $settings ) { $this->settings = $settings; add_filter( 'trp_settings_tabs', [$this, 'add_tab_to_navigation'] ); add_action( 'admin_menu', [$this, 'add_submenu_page'] ); add_action( 'admin_enqueue_scripts', [$this, 'enqueue_assets'] ); add_action( 'wp_ajax_trp_language_switcher_save', [ $this, 'ajax_save_language_switcher' ] ); add_action( 'wp_ajax_trp_disable_legacy_language_switcher', [ $this, 'ajax_disable_legacy_language_switcher' ] ); } /** * Retrieve the nested language_switcher config. * - If option is missing/empty: seed with defaults. * - If option exists but misses keys: complete only the missing keys (deep), then update option. * * @return array */ public function get_initial_config(): array { $saved = get_option( 'trp_language_switcher_settings', null ); $defaults = self::default_switcher_config(); $changed = false; // Option is not set or is invalid - use defaults if ( !is_array( $saved ) || empty( $saved ) ) { update_option( 'trp_language_switcher_settings', $defaults ); return $defaults; } // Recursive merge that only fills missing keys $merge_missing = static function ( array $have, array $defaults ) use ( &$changed, &$merge_missing ): array { foreach ( $defaults as $key => $def_val ) { if ( !array_key_exists( $key, $have ) ) { $have[ $key ] = $def_val; $changed = true; continue; } if ( is_array( $def_val ) && is_array( $have[ $key ] ) ) { $have[ $key ] = $merge_missing( $have[ $key ], $def_val ); } } return $have; }; $completed = $merge_missing( $saved, $defaults ); if ( $changed ) update_option( 'trp_language_switcher_settings', $completed ); return $completed; } /** * Return the complete default Language Switcher config (all scopes). * * @return array */ public static function default_switcher_config(): array { $layout_customizer_default_map = [ 'floater' => [ 'position' => 'bottom-right', 'width' => 'default', 'customWidth' => 216, 'padding' => 'default', 'customPadding' => 0, 'flagIconPosition' => 'before', 'languageNames' => 'full', ], 'shortcode' => [ 'flagIconPosition' => 'before', 'languageNames' => 'full' ], 'menu' => [ 'flagIconPosition' => 'before', 'languageNames' => 'full', 'flagShape' => 'rect' ] ]; $layoutCustomizerDefault = [ 'floater' => [ 'desktop' => $layout_customizer_default_map['floater'], 'mobile' => $layout_customizer_default_map['floater'] ], 'shortcode' => [ 'desktop' => $layout_customizer_default_map['shortcode'], 'mobile' => $layout_customizer_default_map['shortcode'] ], 'menu' => [ 'desktop' => $layout_customizer_default_map['menu'], 'mobile' => $layout_customizer_default_map['menu'] ] ]; return [ 'floater' => [ 'enabled' => true, 'type' => 'dropdown', 'bgColor' => '#ffffff', 'bgHoverColor' => '#0000000d', 'textColor' => '#143852', 'textHoverColor' => '#1d2327', 'borderColor' => '#1438521a', 'borderWidth' => 1, 'borderRadius' => [8, 8, 0, 0], 'size' => 'normal', 'flagShape' => 'rect', 'flagRadius' => 2, 'enableCustomCss' => false, 'customCss' => '', 'oppositeLanguage' => false, 'showPoweredBy' => false, 'layoutCustomizer' => $layoutCustomizerDefault['floater'], 'enableTransitions' => true, ], 'shortcode' => [ 'bgColor' => '#ffffff', 'bgHoverColor' => '#0000000d', 'textColor' => '#143852', 'textHoverColor' => '#1d2327', 'borderColor' => '#1438521a', 'borderWidth' => 1, 'borderRadius' => 5, 'size' => 'normal', 'flagShape' => 'rect', 'flagRadius' => 2, 'enableCustomCss' => false, 'customCss' => '', 'clickLanguage' => false, 'layoutCustomizer' => $layoutCustomizerDefault['shortcode'], 'enableTransitions' => true, 'oppositeLanguage' => false ], 'menu' => [ 'layoutCustomizer' => $layoutCustomizerDefault['menu'], ], ]; } /** * AJAX: Save one language-switcher scope. * Action: trp_language_switcher_save */ public function ajax_save_language_switcher(): void { if ( ! current_user_can( apply_filters( 'trp_settings_capability', 'manage_options' ) ) ) wp_send_json_error( __( 'Permission denied.', 'translatepress-multilingual' ), 403 ); $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'trp_language_switcher_save' ) ) wp_send_json_error( __( 'Invalid nonce.', 'translatepress-multilingual' ), 403 ); $scope = sanitize_key( wp_unslash( $_POST['scope'] ?? '' ) ); $config_raw = isset( $_POST['config'] ) ? wp_unslash( $_POST['config'] ) : '{}'; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $config_str = is_string( $config_raw ) ? $config_raw : '{}'; $config = json_decode( $config_str, true ); $allowed_scopes = [ 'floater', 'shortcode', 'menu' ]; if ( ! in_array( $scope, $allowed_scopes, true ) || ! is_array( $config ) ) wp_send_json_error( __( 'Settings scope unknown.', 'translatepress-multilingual' ), 400 ); $sanitised = $this->sanitize_scope_config( $scope, $config ); $options = get_option( 'trp_language_switcher_settings', [] ); $options[ $scope ] = $sanitised; update_option( 'trp_language_switcher_settings', $options ); wp_send_json_success( __( 'Settings saved.', 'translatepress-multilingual' ) ); } /** * AJAX: disable the legacy Language Switcher. * * Validates capability and nonce, sets * trp_advanced_settings['load_legacy_language_switcher'] = 'no', * and returns a JSON response. * * Expects POST: 'nonce' for action 'trp_disable_legacy'. * * @return void */ public function ajax_disable_legacy_language_switcher(): void { if ( ! current_user_can( apply_filters( 'trp_settings_capability', 'manage_options' ) ) ) { wp_send_json_error( __( 'Permission denied.', 'translatepress-multilingual' ), 403 ); } $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'trp_disable_legacy' ) ) { wp_send_json_error( __( 'Invalid nonce.', 'translatepress-multilingual' ), 403 ); } $adv = get_option( 'trp_advanced_settings', [] ); if ( ! is_array( $adv ) ) { $adv = []; } // Flip legacy OFF $adv['load_legacy_language_switcher'] = 'no'; update_option( 'trp_advanced_settings', $adv ); TRP_Plugin_Notifications::get_instance()->dismiss_notification( 'trp_ls_v2_intro' ); wp_send_json_success( __( 'Legacy disabled.', 'translatepress-multilingual' ) ); } /** * Sanitise a single-scope config using allow-listed rules. * * @param string $scope floater|shortcode|menu * @param array $data Incoming config * @return array */ private function sanitize_scope_config( string $scope, array $data ): array { /* Rule map */ $rules = [ 'floater' => [ 'enabled' => 'bool', 'type' => 'text', 'bgColor' => 'color', 'bgHoverColor' => 'color', 'textColor' => 'color', 'textHoverColor' => 'color', 'borderColor' => 'color', 'borderWidth' => 'int', 'borderRadius' => 'int_array', 'size' => 'text', 'flagShape' => 'text', 'flagRadius' => 'int', 'enableCustomCss' => 'bool', 'customCss' => 'css', 'oppositeLanguage' => 'bool', 'layoutCustomizer' => 'layoutCustomizer', 'showPoweredBy' => 'bool', 'enableTransitions' => 'bool' ], 'shortcode' => [ 'bgColor' => 'color', 'bgHoverColor' => 'color', 'textColor' => 'color', 'textHoverColor' => 'color', 'borderColor' => 'color', 'borderWidth' => 'int', 'borderRadius' => 'int', 'size' => 'text', 'flagShape' => 'text', 'flagRadius' => 'int', 'enableCustomCss' => 'bool', 'customCss' => 'css', 'layoutCustomizer' => 'layoutCustomizer', 'clickLanguage' => 'bool', 'enableTransitions' => 'bool', 'oppositeLanguage' => 'bool' ], 'menu' => [ 'flagShape' => 'text', 'flagIconPosition' => 'text', 'languageNames' => 'text', 'layoutCustomizer' => 'layoutCustomizer', ], ]; /* sanitiser callbacks */ $filters = [ 'bool' => static fn ( $v ) => (bool) $v, 'int' => 'intval', 'text' => 'sanitize_text_field', 'color' => static function ( $v ) { $v = trim( strtolower( $v ) ); if ( $v === 'transparent' ) return $v; // Allow 4, 5, 7 or 9-character hex codes (with #) if ( preg_match( '/^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i', $v ) ) return $v; return ''; }, 'int_array' => static fn ( $v ) => array_map( 'intval', (array) $v ), 'css' => [ $this, 'sanitize_custom_css' ], 'layoutCustomizer' => [ $this, 'sanitize_layout_customizer' ], ]; /* iterate */ $out = []; foreach ( $rules[ $scope ] as $key => $rule ) { if ( array_key_exists( $key, $data ) ) { $out[ $key ] = $filters[ $rule ]( $data[ $key ] ); } } return $out; } /** * Sanitize a raw CSS string for storage. * * - Rejects any HTML markup. * - Removes dangerous CSS constructs. * * @param string $css Raw user input. * @return string Sanitized CSS (or empty string on fatal error). */ public function sanitize_custom_css( string $css ): string { // Reject any HTML tags if ( preg_match( '#</?\w+#', $css ) ) return ''; // Neutralize CSS expressions & javascript: URLs $css = preg_replace( '/expression\s*\([^)]*\)/i', '', $css ); $css = preg_replace( '/url\s*\(\s*[\'"]?\s*javascript:[^)]*\)/i', 'url("about:blank")', $css ); $css = preg_replace( '/@import\s+[^;]+;/i', '', $css ); return trim( $css ); } /** * Sanitises the layoutCustomizer object for both desktop and mobile. * * @param mixed $value * @return array */ private function sanitize_layout_customizer( array $value ): array { $out = [ 'desktop' => [], 'mobile' => [] ]; $int_fields = [ 'customWidth', 'customPadding' ]; $text_fields = [ 'position', 'width', 'padding', 'flagIconPosition', 'languageNames', 'flagShape' ]; foreach ( [ 'desktop', 'mobile' ] as $device ) { foreach ( array_merge( $int_fields, $text_fields ) as $key ) { if ( ! isset( $value[ $device ][ $key ] ) ) continue; $raw = $value[ $device ][ $key ]; if ( in_array( $key, $int_fields, true ) ) { $out[ $device ][ $key ] = (int) $raw; } else { $out[ $device ][ $key ] = sanitize_text_field( $raw ); } } } return $out; } /** * Adds the Language Switcher tab * * Hooked: trp_settings_tabs * * @param array $tabs * @return array */ public function add_tab_to_navigation( array $tabs ): array { $tab = [ 'name' => __( 'Language Switcher', 'translatepress-multilingual' ), 'url' => admin_url( 'admin.php?page=trp_language_switcher' ), 'page' => 'trp_language_switcher' ]; array_splice( $tabs, 1, 0, [$tab] ); return $tabs; } /** * Adds a hidden submenu page for TranslatePress language switcher tab * * Hooked: admin_menu */ public function add_submenu_page(): void { add_submenu_page( 'TRPHidden', __( 'Language Switcher', 'translatepress-multilingual' ), 'TRPHidden', apply_filters( 'trp_settings_capability', 'manage_options' ), 'trp_language_switcher', [$this, 'language_switcher_page_content'] ); } /** * Echoes the Vue mount point <div>. * * @return void */ public function language_switcher_page_content(): void { require_once TRP_PLUGIN_DIR . 'partials/language-switcher-configurator-page.php'; } /** * Build the data array sent to the Vue app via wp_localize_script(). * * @return array */ private function get_localize_payload(): array { $config = $this->get_initial_config(); $trp = TRP_Translate_Press::get_trp_instance(); $languages_component = $trp->get_component( 'languages' ); $published_codes = $this->settings['publish-languages'] ?? []; $short_language_names = $this->settings['url-slugs'] ?? []; $published = []; $all_languages = $languages_component->get_language_names( $published_codes ); foreach ( $published_codes as $code ) { if ( ! isset( $all_languages[ $code ] ) ) continue; /** Custom language flag support */ $flag_path = apply_filters( 'trp_flags_path', '', $code ); $lang_data = [ 'name' => $all_languages[ $code ], 'shortName' => strtoupper( $short_language_names[ $code ] ), ]; // Only include flagPath if it’s non-empty, works only for custom languages if ( ! empty( $flag_path ) ) $lang_data['flagPath'] = $flag_path; $published[ $code ] = $lang_data; } $default_code = $this->settings['default-language']; $default_name = $all_languages[ $default_code ]; return [ 'lsConfig' => $config, 'languages' => [ 'published' => $published, 'default' => [ 'code' => $default_code, 'name' => $default_name, ], ], 'misc' => [ 'pluginUrl' => TRP_PLUGIN_URL ], 'nonce' => wp_create_nonce( 'trp_language_switcher_save' ), ]; } /** * Enqueue the Vue app script & CSS. * @param ?string $hook */ public function enqueue_assets( $hook = null ): void { if ( !is_string( $hook ) && function_exists( 'get_current_screen' ) ) { $screen = get_current_screen(); $hook = $screen ? $screen->id : ''; } if ( 'admin_page_trp_language_switcher' !== $hook ) return; $script_url = TRP_PLUGIN_URL . 'assets/js/trp-lang-switcher-configurator.js'; $style_url = TRP_PLUGIN_URL . 'assets/css/trp-lang-switcher-configurator.css'; $version = TRP_PLUGIN_VERSION; $script_handle = 'tp-lang-switcher-configurator'; wp_enqueue_style( 'tp-lang-switcher-configurator-style', $style_url, [], $version ); wp_register_script( $script_handle, $script_url, [ 'wp-i18n', 'wp-element'], $version, true ); wp_set_script_translations( $script_handle, 'translatepress-multilingual' ); wp_localize_script( $script_handle, 'tpLangSwitcherData', $this->get_localize_payload() ); wp_enqueue_script( $script_handle ); } public function is_legacy_enabled(): bool { return ( $this->settings['trp_advanced_settings']['load_legacy_language_switcher'] ?? 'no' ) === 'yes'; } } includes/class-onboarding.php 0000777 00000021176 15251156640 0012333 0 ustar 00 <?php if ( ! defined( 'ABSPATH' ) ) exit; /** * Class TRP_Onboarding * * Loads required files regarding the TP onboarding, initializes components and hooks methods for the onboarding TP. * */ class TRP_Onboarding { protected $settings; protected $steps = [ 'welcome' => TRP_Step_Welcome::class, 'install' => TRP_Step_Install::class, 'license' => TRP_Step_License::class, 'languages' => TRP_Step_Languages::class, 'switcher' => TRP_Step_Switcher::class, 'autotranslation' => TRP_Step_AutoTranslation::class, 'addons' => TRP_Step_Addons::class, 'finish' => TRP_Step_Finish::class, ]; /** * The current onboarding step or an error. * * @var TRP_Onboarding_Step_Interface|WP_Error */ protected $step; public function __construct( $settings ){ $this->settings = $settings; add_action( 'admin_init', array( $this, 'run_onboarding_admin' ) ); // Render both menu & admin page. add_action('admin_menu', array($this, 'register_onboarding')); } public function run_onboarding_admin(){ if (current_user_can('manage_options') && $this->is_onboarding()) { add_action('admin_head', array($this, 'remove_admin_notices')); add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts_and_styles')); // Process form submissions on admin_init to prevent headers already sent issues. $this->step = $this->init_step(); $this->step_handle(); } } private function init_step(){ if(!current_user_can('manage_options') || !$this->is_onboarding()){ return new WP_Error('not_onboarding', __( 'Not TranslatePress onboarding page.', 'translatepress-multilingual' )); } if(file_exists(TRP_PLUGIN_DIR . 'includes/onboarding/interface-onboarding-step.php')){ require_once TRP_PLUGIN_DIR . 'includes/onboarding/interface-onboarding-step.php'; } $step = sanitize_text_field(isset($_GET['step']) ? $_GET['step'] : 'welcome'); $step_class = (isset($this->steps[$step])) ? $this->steps[$step] : null; if($step_class){ $file = TRP_PLUGIN_DIR . 'includes/onboarding/class-' . $step . '.php'; if (file_exists($file)) { include_once($file); } } if (!$step_class || !class_exists($step_class)) { return new WP_Error('invalid_step', sprintf( __( 'Step %s does not exist', 'translatepress-multilingual' ), $step)); } else { return new $step_class($this->settings); } } private function is_onboarding(): bool { if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) { return false; } if ( empty( $_GET['page'] ) || $_GET['page'] !== 'trp-onboarding' ) { return false; } return true; } public function register_onboarding(){ add_submenu_page( 'translate-press', 'Setup Wizard', 'Setup Wizard', 'manage_options', 'trp-onboarding', array( $this, 'render_template' ) ); } public function render_template(){ $full_logo = TRP_PLUGIN_URL . 'assets/images/tp-logo-with-text-dark.svg'; $small_logo = TRP_PLUGIN_URL . 'assets/images/tp-logo.png'; ob_start(); ?> <div id="trp-settings-page" class="wrap trp-onboarding"> <div id="trp-settings-header"> <div class="trp-settings-logo"> <img src="<?php echo esc_url( $full_logo ); ?>" srcset="<?php echo esc_url( $small_logo ); ?> 128w, <?php echo esc_url( $full_logo ); ?> 177w" sizes="(max-width: 520px) 40px, 177px" alt="TranslatePress Logo"> </div> <nav class="trp-onboarding-nav-menu"> <ul class="trp-onboarding-nav-list"> <li><a href="<?php echo esc_url( admin_url('admin.php?page=trp-onboarding&step=welcome') ); ?>" class="trp-nav-onboarding-dot" aria-label="<?php echo esc_attr__( 'Welcome', 'translatepress-multilingual' ); ?>" title="<?php echo esc_attr__( 'Welcome', 'translatepress-multilingual' ); ?>"></a></li> <li><a href="<?php echo esc_url( admin_url('admin.php?page=trp-onboarding&step=languages') ); ?>" class="trp-nav-onboarding-dot" aria-label="<?php echo esc_attr__( 'Add Languages', 'translatepress-multilingual' ); ?>" title="<?php echo esc_attr__( 'Add Languages', 'translatepress-multilingual' ); ?>"></a></li> <li><a href="<?php echo esc_url( admin_url('admin.php?page=trp-onboarding&step=switcher') ); ?>" class="trp-nav-onboarding-dot" aria-label="<?php echo esc_attr__( 'Language Switcher', 'translatepress-multilingual' ); ?>" title="<?php echo esc_attr__( 'Language Switcher', 'translatepress-multilingual' ); ?>"></a></li> <li><a href="<?php echo esc_url( admin_url('admin.php?page=trp-onboarding&step=autotranslation') ); ?>" class="trp-nav-onboarding-dot" aria-label="<?php echo esc_attr__( 'Automatic Translation', 'translatepress-multilingual' ); ?>" title="<?php echo esc_attr__( 'Automatic Translation', 'translatepress-multilingual' ); ?>"></a></li> <li><a href="<?php echo esc_url( admin_url('admin.php?page=trp-onboarding&step=addons') ); ?>" class="trp-nav-onboarding-dot" aria-label="<?php echo esc_attr__( 'Enable Addons', 'translatepress-multilingual' ); ?>" title="<?php echo esc_attr__( 'Enable Addons', 'translatepress-multilingual' ); ?>"></a></li> <li><a href="<?php echo esc_url( admin_url('admin.php?page=trp-onboarding&step=finish') ); ?>" class="trp-nav-onboarding-dot" aria-label="<?php echo esc_attr__( 'Finalize', 'translatepress-multilingual' ); ?>" title="<?php echo esc_attr__( 'Finalize', 'translatepress-multilingual' ); ?>"></a></li> </ul> </nav> <div id="trp-header-items-wrapper"> <a class="trp-header-link" href="<?php echo esc_url( admin_url( 'options-general.php?page=translate-press' ) ); ?>"><span class="trp-header-item-text trp-primary-text"><?php esc_html_e( 'Exit Setup', 'translatepress-multilingual' ); ?></span></a> <a id="trp-upgrade-now-button" class="trp-header-link" href="https://translatepress.com/pricing/?utm_source=tp-onboarding&utm_medium=client-site&utm_campaign=header-upsell"><?php esc_html_e( 'Upgrade', 'translatepress-multilingual' ); ?></a> </div> </div> <div class="trp-onboarding-content"> <?php $this->step_render(); ?> </div> </div> <?php echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } private function step_handle(){ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST') { // all nonce verification happens inside each step class if ($this->step instanceof TRP_Onboarding_Step_Interface){ $this->step->handle($_POST); } } } public function step_render(){ if ($this->step instanceof TRP_Onboarding_Step_Interface){ $this->step->render(); } else { esc_html_e('Nothing here', 'translatepress-multilingual'); } } public function remove_admin_notices(){ remove_all_actions( 'admin_notices' ); remove_all_actions( 'all_admin_notices' ); } public function enqueue_scripts_and_styles(){ wp_enqueue_style('trp-onboarding-style', TRP_PLUGIN_URL . 'assets/css/trp-onboarding-style.css', array(), TRP_PLUGIN_VERSION); wp_enqueue_script( 'trp-select2-lib-js', TRP_PLUGIN_URL . 'assets/lib/select2-lib/dist/js/select2.min.js', array( 'jquery' ), TRP_PLUGIN_VERSION ); wp_enqueue_style( 'trp-select2-lib-css', TRP_PLUGIN_URL . 'assets/lib/select2-lib/dist/css/select2.min.css', array(), TRP_PLUGIN_VERSION ); // Register and enqueue your script wp_enqueue_script('trp-onboarding-js', TRP_PLUGIN_URL . 'assets/js/trp-onboarding-script.js', array('jquery', 'trp-select2-lib-js'), TRP_PLUGIN_VERSION,true); // Localize the script with a variable $translation_array = array( 'trp_secondary_languages' => apply_filters('trp_secondary_languages', 1), ); wp_localize_script('trp-onboarding-js', 'trp_onboarding_vars', $translation_array); } } includes/class-machine-translator.php 0000777 00000070370 15251156640 0014004 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); /** * Class TRP_Machine_Translator * * Facilitates Machine Translation calls. */ class TRP_Machine_Translator { protected $settings; protected $referer; protected $url_converter; protected $machine_translator_logger; protected $machine_translation_codes; protected $trp_languages; protected $correct_api_key = null; /** * TRP_Machine_Translator constructor. * * @param array $settings Settings option. */ public function __construct( $settings ){ $this->settings = $settings; $trp = TRP_Translate_Press::get_trp_instance(); if ( ! $this->machine_translator_logger ) { $this->machine_translator_logger = $trp->get_component('machine_translator_logger'); } if ( ! $this->trp_languages ) { $this->trp_languages = $trp->get_component('languages'); } $this->machine_translation_codes = $this->trp_languages->get_iso_codes($this->settings['translation-languages']); add_filter( 'trp_exclude_words_from_automatic_translation', array( $this, 'sort_exclude_words_from_automatic_translation_array' ), 99999, 1 ); add_filter( 'trp_exclude_words_from_automatic_translation', array( $this, 'exclude_special_symbol_from_translation' ), 9999, 2 ); } /** * Whether automatic translation is available. * * @param array $languages * @return bool */ public function is_available( $languages = array() ) { /** * Return false in case it was directly called from parent and not from a derived class (DeepL / Google / TPAI) * Calling this method on the parent class means it was called too early and machine translation is not available at this point */ if ( get_class( $this ) === __CLASS__ ) return false; $settings = $this->settings['trp_machine_translation_settings'] ?? array(); $enabled = ( $settings['machine-translation'] ?? '' ) === 'yes'; $is_available = false; if ( $enabled ) { $engine = $settings['translation-engine'] ?? null; if ( $engine === 'deepl' && get_option( 'trp_license_status' ) !== 'valid' ) { $is_available = false; } elseif ( empty( $languages ) ) { $is_available = true; } else { $is_available = $this->check_languages_availability( $languages ); } } return apply_filters( 'trp_machine_translator_is_available', $is_available, $languages, $settings ); } public function check_languages_availability( $languages, $force_recheck = false ){ if ( !method_exists( $this, 'get_supported_languages' ) || !method_exists( $this, 'get_engine_specific_language_codes' )){ return true; } $force_recheck = ( current_user_can('manage_options') && !empty( $_GET['trp_recheck_supported_languages']) && $_GET['trp_recheck_supported_languages'] === '1' && wp_verify_nonce( sanitize_text_field( $_GET['trp_recheck_supported_languages_nonce'] ), 'trp_recheck_supported_languages' ) ) ? true : $force_recheck; //phpcs:ignore $data = get_option('trp_db_stored_data', array() ); if ( isset( $_GET['trp_recheck_supported_languages'] )) { unset($_GET['trp_recheck_supported_languages'] ); } // if supported languages are not stored, fetch them and update option if ( empty( $data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['last-checked'] ) || $force_recheck || ( method_exists($this,'check_formality') && !isset($data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['formality-supported-languages']))){ if ( empty( $data['trp_mt_supported_languages'] ) ) { $data['trp_mt_supported_languages'] = array(); } if ( empty( $data['trp_mt_supported_languages'][ $this->settings['trp_machine_translation_settings']['translation-engine'] ] ) ) { $data['trp_mt_supported_languages'][ $this->settings['trp_machine_translation_settings']['translation-engine'] ] = array( 'languages' => array() ); } $data['trp_mt_supported_languages'][ $this->settings['trp_machine_translation_settings']['translation-engine'] ]['languages'] = $this->get_supported_languages(); if (method_exists($this, 'check_formality')) { $data['trp_mt_supported_languages'][ $this->settings['trp_machine_translation_settings']['translation-engine'] ]['formality-supported-languages'] = $this->check_formality(); } $data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['last-checked'] = date("Y-m-d H:i:s" ); update_option('trp_db_stored_data', $data ); } $languages_iso_to_check = $this->get_engine_specific_language_codes( $languages ); $all_are_available = !array_diff($languages_iso_to_check, $data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['languages']); return apply_filters('trp_mt_available_supported_languages', $all_are_available, $languages, $this->settings ); } public function get_last_checked_supported_languages(){ $data = get_option('trp_db_stored_data', array() ); if ( empty( $data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['last-checked'] ) ){ $this->check_languages_availability( $this->settings['translation-languages'], true); } return $data['trp_mt_supported_languages'][$this->settings['trp_machine_translation_settings']['translation-engine']]['last-checked']; } /** * Output an SVG based on translation engine and error flag. * * @param bool $show_errors true to show an error SVG, false if not. */ public function automatic_translation_svg_output( $show_errors ) { if ( method_exists( $this, 'automatic_translate_error_check' ) ) { if ( $show_errors ) { trp_output_svg( 'error' ); } else { trp_output_svg( 'check' ); } } } /** * * @deprecated * Check the automatic translation API keys for Google Translate and DeepL * * @param TRP_Translate_Press $machine_translator Machine translator instance. * @param string $translation_engine The translation engine (can be google_translate_v2 and deepl). * @param string $api_key The API key to check. * * @return array [ (string) $message, (bool) $error ]. */ public function automatic_translate_error_check( $machine_translator, $translation_engine, $api_key ) { $is_error = false; $return_message = ''; switch ( $translation_engine ) { case 'google_translate_v2': if ( empty( $api_key ) ) { $is_error = true; $return_message = __( 'Please enter your Google Translate key.', 'translatepress-multilingual' ); } else { // Perform test. $response = $machine_translator->test_request(); $code = wp_remote_retrieve_response_code( $response ); if ( 200 !== $code ) { $is_error = true; $translate_response = trp_gt_response_codes( $code ); $return_message = $translate_response['message']; } } break; case 'deepl': if ( empty( $api_key ) ) { $is_error = true; $return_message = __( 'Please enter your DeepL API key.', 'translatepress-multilingual' ); } else { // Perform test. $is_error= false; $response = $machine_translator->test_request(); $code = wp_remote_retrieve_response_code( $response ); if ( 200 !== $code && ( method_exists( 'TRP_DeepL', 'deepl_response_codes' ) || method_exists( 'TRP_IN_DeepL', 'deepl_response_codes' ) ) ) { // Test whether the old deepL add-on or the new repackaging model is used if ( method_exists( 'TRP_DeepL', 'deepl_response_codes' ) ) { $translate_response = TRP_DeepL::deepl_response_codes( $code ); } else { $translate_response = TRP_IN_DeepL::deepl_response_codes( $code ); } $is_error = true; $return_message = $translate_response['message']; } } break; default: break; } $this->correct_api_key=array( 'message' => $return_message, 'error' => $is_error, ); return $this->correct_api_key; } // checking if the api_key is correct in order to display unsupported languages public function is_correct_api_key(){ if(method_exists($this, 'check_api_key_validity')){ $verification = $this->check_api_key_validity(); }else { //we only need this values for automatic translate error check function for backwards compatibility $machine_translator = $this; $translation_engine = $this->settings['trp_machine_translation_settings']['translation-engine']; $api_key = $this->get_api_key(); $verification = $this->automatic_translate_error_check( $machine_translator, $translation_engine, $api_key ); } if($verification['error']== false) { return true; } return false; } /** * Return site referer * * @return string */ public function get_referer(){ if( ! $this->referer ) { if( ! $this->url_converter ) { $trp = TRP_Translate_Press::get_trp_instance(); $this->url_converter = $trp->get_component( 'url_converter' ); } $this->referer = $this->url_converter->get_abs_home(); } return apply_filters( 'trp_machine_translator_referer', $this->referer ); } /** * Verifies that the machine translation request is valid * @deprecated since TP 1.6.0 (only here to support Deepl Add-on version 1.0.0) * * @param string $to_language language we're looking to translate to * @return bool */ public function verify_request( $to_language ){ if( empty( $this->get_api_key() ) || empty( $to_language ) || $to_language == $this->settings['default-language'] || empty( $this->machine_translation_codes[$this->settings['default-language']] ) ) return false; // Method that can be extended in the child class to add extra validation if( !$this->extra_request_validations( $to_language ) ) return false; // Check if crawlers are blocked if( !empty( $this->settings['trp_machine_translation_settings']['block-crawlers'] ) && $this->settings['trp_machine_translation_settings']['block-crawlers'] == 'yes' && $this->is_crawler() ) return false; // Check if daily quota is met if( $this->machine_translator_logger->quota_exceeded() ) return false; return true; } /** * Verifies that the machine translation request is valid * * @param string $target_language_code language we're looking to translate to * @param string $source_language_code language we're looking to translate from * @return bool */ public function verify_request_parameters($target_language_code, $source_language_code){ if( empty( $this->get_api_key() ) || empty( $target_language_code ) || empty( $source_language_code ) || empty( $this->machine_translation_codes[$target_language_code] ) || empty( $this->machine_translation_codes[$source_language_code] ) || $this->machine_translation_codes[$target_language_code] == $this->machine_translation_codes[$source_language_code] ) return false; // Method that can be extended in the child class to add extra validation if( !$this->extra_request_validations( $target_language_code ) ) return false; // Check if crawlers are blocked if( !empty( $this->settings['trp_machine_translation_settings']['block-crawlers'] ) && $this->settings['trp_machine_translation_settings']['block-crawlers'] == 'yes' && $this->is_crawler() ) return false; // Check if daily quota is met if( $this->machine_translator_logger->quota_exceeded() ) return false; return true; } /** * Verifies user agent to check if the request is being made by a crawler * * @return boolean */ private function is_crawler(){ if( !isset( $_SERVER['HTTP_USER_AGENT'] ) ) return false; $crawlers = apply_filters( 'trp_machine_translator_crawlers', 'rambler|abacho|acoi|accona|aspseek|altavista|estyle|scrubby|lycos|geona|ia_archiver|alexa|sogou|skype|facebook|twitter|pinterest|linkedin|naver|bing|google|yahoo|duckduckgo|yandex|baidu|teoma|xing|java\/1.7.0_45|bot|crawl|slurp|spider|mediapartners|\sask\s|\saol\s' ); return preg_match( '/'. $crawlers .'/i', sanitize_text_field ( $_SERVER['HTTP_USER_AGENT'] ) ); } private function get_placeholders( $count ){ $placeholders = array(); for( $i = 1 ; $i <= $count; $i++ ){ $placeholders[] = '1TP' . $i . 'T'; } return $placeholders; } /** * Check if a string should be sent for translation based on minimum length and content criteria * * @param string $string The string to check * @return bool True if the string should be translated, false otherwise */ private function should_translate_string( $string ) { // Trim whitespace for accurate length check $trimmed = trim( $string ); // Check if string is empty after trimming if ( empty( $trimmed ) ) { return false; } // Get minimum length (default: 2 characters) // Allow customization via filter $min_length = apply_filters( 'trp_minimum_translation_length', 2 ); // Check minimum length if ( mb_strlen( $trimmed, 'UTF-8' ) < $min_length ) { return false; } // Check if string is only punctuation/special characters (optional, can be disabled via filter) $skip_punctuation_only = apply_filters( 'trp_skip_punctuation_only_strings', true ); if ( $skip_punctuation_only && preg_match( '/^[[:punct:][:space:]]+$/u', $trimmed ) ) { return false; } return true; } /** * Function to be used externally * * @param $strings * @param $target_language_code * @param $source_language_code * @return array */ public function translate($strings, $target_language_code, $source_language_code = null ){ if ( !empty($strings) && is_array($strings) && method_exists( $this, 'translate_array' ) && apply_filters( 'trp_disable_automatic_translations_due_to_error', false ) === false ) { /* google has a problem translating this characters ( '%', '$', '#' )...for some reasons it puts spaces after them so we need to 'encode' them and decode them back. hopefully it won't break anything important */ /* we put '%s' before '%' because google seems to transform %s into % in strings for some languages which causes a 500 Fatal Error in PHP 8*/ $imploded_strings = implode(" ", $strings); $trp_exclude_words_from_automatic_translation = apply_filters('trp_exclude_words_from_automatic_translation', array('%s', '%d', '%', '$', '#'), $imploded_strings); $placeholders = $this->get_placeholders(count($trp_exclude_words_from_automatic_translation)); $shortcode_tags_to_execute = apply_filters( 'trp_do_these_shortcodes_before_automatic_translation', array('trp_language', 'language-include', 'language-exclude') ); $strings = array_unique($strings); $original_strings = $strings; // Filter out strings that are too short to translate $strings_to_skip = array(); $strings_to_translate = array(); foreach ($strings as $key => $string) { if ( !$this->should_translate_string($string) ) { $strings_to_skip[$key] = $string; } else { $strings_to_translate[$key] = $string; } } // If all strings are too short, return them as-is if ( empty($strings_to_translate) ) { return $original_strings; } // Continue with only the strings that meet the minimum length $strings = $strings_to_translate; foreach ($strings as $key => $string) { /* html_entity_decode is needed before replacing the character "#" from the list because characters like “ (8220 utf8) * will get an extra space after '&' which will break the character, rendering it like this: & #8220; */ $strings[$key] = str_replace($trp_exclude_words_from_automatic_translation, $placeholders, html_entity_decode( $string )); $strings[$key] = trp_do_these_shortcodes( $strings[$key], $shortcode_tags_to_execute ); } if ( $this->settings['trp_machine_translation_settings']['translation-engine'] === 'deepl' ) { // if we don't have a valid license, return an empty array $license_status = get_option( 'trp_license_status' ); if( $license_status !== 'valid' ){ return array(); } } $machine_strings = $this->translate_array($strings, $target_language_code, $source_language_code); $machine_strings_return_array = array(); if (!empty($machine_strings)) { foreach ($machine_strings as $key => $machine_string) { // Restore placeholders to original excluded words $processed_string = str_ireplace( $placeholders, $trp_exclude_words_from_automatic_translation, $machine_string ); // Restore quote patterns (use $strings which is decoded, not $original_strings with HTML entities) $processed_string = $this->restore_translation_quotes($strings[$key], $processed_string); // Restore punctuation and spacing patterns (use $strings which is decoded, not $original_strings with HTML entities) $processed_string = $this->restore_punctuation_patterns($strings[$key], $processed_string); $machine_strings_return_array[$original_strings[$key]] = $processed_string; } } // Add skipped strings back to the return array with their original values foreach ($strings_to_skip as $key => $skipped_string) { $machine_strings_return_array[$original_strings[$key]] = $original_strings[$key]; } return $machine_strings_return_array; }else { return array(); } } /** * @param $trp_exclude_words_from_automatic_translation * @return mixed * * We need to sort the $trp_exclude_words_from_automatic_translation array descending because we risk to not translate excluded multiple words when one * is repeated ( example: Facebook, Facebook Store, Facebook View, because Facebook was the first one in the array it was replaced with a code and the * other words group ( Store, View) were translated) */ public function sort_exclude_words_from_automatic_translation_array($trp_exclude_words_from_automatic_translation){ usort($trp_exclude_words_from_automatic_translation, array($this,"sort_array")); return $trp_exclude_words_from_automatic_translation; } public function sort_array($a, $b){ return strlen($b)-strlen($a); } public function test_request(){} public function get_api_key(){ return false; } public function extra_request_validations( $to_language ){ return true; } public function exclude_special_symbol_from_translation($array, $strings){ $float_array_symbols = array('d', 's', 'e', 'E', 'f', 'F', 'g', 'G', 'h', 'H'); foreach ($float_array_symbols as $float_array_symbol){ for($i= 1; $i<=10; $i++) { $symbol = '%'.$i .'$'.$float_array_symbol; if ( strpos( $strings, $symbol ) !== false ) { $array[] = '%' . $i . '$' . $float_array_symbol; } } } return $array; } /** * Restore and normalize quotes in translated strings to match the original * * @param string $original_string The original untranslated string * @param string $translated_string The translated string from the API * @return string The translated string with quotes restored and normalized */ public function restore_translation_quotes($original_string, $translated_string) { // Allow disabling this functionality via filter if ( apply_filters( 'trp_disable_restore_translation_quotes', false ) ) { return $translated_string; } // Check if original string is empty or translated string is empty if ( empty($original_string) || empty($translated_string) ) { return $translated_string; } // Define all quote characters to check for $quote_chars = [ // Straight quotes "'", // U+0027 Apostrophe / single quote '"', // U+0022 Double quote // Curly / typographic quotes '‘', // U+2018 Left single curly quote '’', // U+2019 Right single curly quote (also apostrophe) '“', // U+201C Left double curly quote '”', // U+201D Right double curly quote // Common international quotes '„', // U+201E Low double quote (German, Polish) '‚', // U+201A Low single quote '«', // U+00AB Left double angle quote (French, Italian, etc.) '»', // U+00BB Right double angle quote ]; // Step 1: Restore boundary quotes if they were stripped $original_first_char = mb_substr($original_string, 0, 1, 'UTF-8'); $original_last_char = mb_substr($original_string, -1, 1, 'UTF-8'); $translated_first_char = mb_substr($translated_string, 0, 1, 'UTF-8'); $translated_last_char = mb_substr($translated_string, -1, 1, 'UTF-8'); // Check if original starts with a quote character and translated doesn't if ( in_array($original_first_char, $quote_chars, true) && !in_array($translated_first_char, $quote_chars, true) ) { $translated_string = $original_first_char . $translated_string; } // Check if original ends with a quote character and translated doesn't // Need to recalculate last char if we added a quote at the beginning if ( in_array($original_last_char, $quote_chars, true) && !in_array($translated_last_char, $quote_chars, true) ) { $translated_string = $translated_string . $original_last_char; } // Step 2: Normalize ALL quotes - collect all quotes from original (excluding apostrophes) $original_quotes = array(); $original_len = mb_strlen($original_string, 'UTF-8'); for ($i = 0; $i < $original_len; $i++) { $char = mb_substr($original_string, $i, 1, 'UTF-8'); if ( in_array($char, $quote_chars, true) ) { // Check if this is an apostrophe (letter-apostrophe-letter pattern) $prev_char = ($i > 0) ? mb_substr($original_string, $i - 1, 1, 'UTF-8') : ''; $next_char = ($i < $original_len - 1) ? mb_substr($original_string, $i + 1, 1, 'UTF-8') : ''; $is_apostrophe = ($char === "'" || $char === '’') && preg_match('/^\p{L}$/u', $prev_char) && preg_match('/^\p{L}$/u', $next_char); if ( !$is_apostrophe ) { $original_quotes[] = $char; } } } // If we found quotes in the original, replace them in order in the translation if ( !empty($original_quotes) ) { $quote_index = 0; $translated_len = mb_strlen($translated_string, 'UTF-8'); $translated_chars = array(); // Convert translation to array of characters for ($i = 0; $i < $translated_len; $i++) { $translated_chars[] = mb_substr($translated_string, $i, 1, 'UTF-8'); } // Replace all quotes in translation in order (excluding apostrophes) for ($i = 0; $i < $translated_len; $i++) { $char = $translated_chars[$i]; if ( in_array($char, $quote_chars, true) ) { // Check if this is an apostrophe (letter-apostrophe-letter pattern) $prev_char = ($i > 0) ? $translated_chars[$i - 1] : ''; $next_char = ($i < $translated_len - 1) ? $translated_chars[$i + 1] : ''; $is_apostrophe = ($char === "'" || $char === '’') && preg_match('/^\p{L}$/u', $prev_char) && preg_match('/^\p{L}$/u', $next_char); if ( !$is_apostrophe && $quote_index < count($original_quotes) ) { $translated_chars[$i] = $original_quotes[$quote_index]; $quote_index++; } } } $translated_string = implode('', $translated_chars); } return $translated_string; } /** * Restore punctuation and spacing patterns at string boundaries * * Handles patterns at the beginning or end of strings: * - Two-character: ", " (comma+space), ". " (period+space), "; " (semicolon+space) * - Single character: "," (comma), "." (period), ";" (semicolon), " " (space) * * @param string $original_string The original untranslated string * @param string $translated_string The translated string from the API * @return string The translated string with punctuation patterns restored */ public function restore_punctuation_patterns($original_string, $translated_string) { // Allow disabling this functionality via filter if ( apply_filters( 'trp_disable_restore_punctuation_patterns', false ) ) { return $translated_string; } // Check if original string is empty or translated string is empty if ( empty($original_string) || empty($translated_string) ) { return $translated_string; } // Define patterns to check (longer patterns first to avoid partial matches) // leading or trailing spaces are trimmed by trp_full_trim() inside translate_page but we still check here as "space+comma" at the end is valid for example $patterns = [', ', '. ', '; ', ' ,', ' .', ' ;', ',', '.', ';', ' ']; // Check all patterns and restore at both leading and trailing positions foreach ($patterns as $pattern) { $pattern_len = mb_strlen($pattern, 'UTF-8'); // Check and restore leading pattern $original_start = mb_substr($original_string, 0, $pattern_len, 'UTF-8'); $translated_start = mb_substr($translated_string, 0, $pattern_len, 'UTF-8'); if ($original_start === $pattern && $translated_start !== $pattern) { $translated_string = $pattern . $translated_string; } // Check and restore trailing pattern $original_end = mb_substr($original_string, -$pattern_len, $pattern_len, 'UTF-8'); $translated_end = mb_substr($translated_string, -$pattern_len, $pattern_len, 'UTF-8'); if ($original_end === $pattern && $translated_end !== $pattern) { $translated_string = $translated_string . $pattern; } } return $translated_string; } } partials/advanced-settings-page.php 0000777 00000001476 15251156640 0013435 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); ?> <div id="trp-settings-page" class="wrap"> <?php require_once TRP_PLUGIN_DIR . 'partials/settings-header.php'; ?> <form method="post" action="options.php"> <?php settings_fields( 'trp_advanced_settings' ); ?> <?php do_action ( 'trp_settings_navigation_tabs' ); ?> <div class="advanced_setting_tab_class"> <div id="trp-settings__wrap"> <?php do_action('trp_before_output_advanced_settings_options' ); ?> <?php do_action('trp_output_advanced_settings_options' ); ?> <button type="submit" class="trp-submit-btn"> <?php esc_html_e( 'Save Changes', 'translatepress-multilingual' ); ?> </button> </div> </div> </form> </div> partials/settings-navigation-tabs.php 0000777 00000000751 15251156640 0014037 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); ?> <h2 class="nav-tab-wrapper"> <?php foreach( $tabs as $tb ) { echo '<a href="' . esc_url( $tb['url'] ) . '" '. ( $tb['page'] == 'trp_translation_editor' ? 'target="_blank"' : '' ) .' class="nav-tab ' . ( ( $active_tab == $tb['page'] ) ? 'nav-tab-active' : '' ) . ( ( $tb['page'] == 'trp_translation_editor' ) ? 'trp-translation-editor' : '' ) . '">' . esc_html( $tb['name'] ) . '</a>'; } ?> </h2> partials/language-switcher-shortcode.php 0000777 00000006011 15251156640 0014507 0 ustar 00 <?php if ( !defined('ABSPATH' ) ) exit(); $current_language_preference = $this->add_shortcode_preferences($shortcode_settings, $current_language['code'], $current_language['name']); ?> <div class="trp_language_switcher_shortcode"> <div class="trp-language-switcher trp-language-switcher-container" data-no-translation <?php echo ( isset( $_GET['trp-edit-translation'] ) && $_GET['trp-edit-translation'] == 'preview' ) ? 'data-trp-unpreviewable="trp-unpreviewable"' : '' ?>> <div class="trp-ls-shortcode-current-language"> <a href="#" class="trp-ls-shortcode-disabled-language trp-ls-disabled-language" title="<?php echo esc_attr( $current_language['name'] ); ?>" onclick="event.preventDefault()"> <?php echo $current_language_preference; /* phpcs:ignore */ /* escaped inside the function that generates the output */ ?> </a> </div> <div class="trp-ls-shortcode-language"> <?php if ( apply_filters('trp_ls_shortcode_show_disabled_language', true, $current_language, $current_language_preference, $this->settings ) ){ ?> <a href="#" class="trp-ls-shortcode-disabled-language trp-ls-disabled-language" title="<?php echo esc_attr( $current_language['name'] ); ?>" onclick="event.preventDefault()"> <?php echo $current_language_preference; /* phpcs:ignore */ /* escaped inside the function that generates the output */ ?> </a> <?php } ?> <?php foreach ( $other_languages as $code => $name ){ $language_preference = $this->add_shortcode_preferences($shortcode_settings, $code, $name); ?> <a href="<?php echo (isset($is_editor) && $is_editor) ? '#' : esc_url( $this->url_converter->get_url_for_language($code, false) ); /* phpcs:ignore */ /* $is_editor is not outputted */ ?>" title="<?php echo esc_attr( $name ); ?>"> <?php echo $language_preference; /* phpcs:ignore */ /* escaped inside the function that generates the output */ ?> </a> <?php } ?> </div> <script type="application/javascript"> // need to have the same with set from JS on both divs. Otherwise it can push stuff around in HTML var trp_ls_shortcodes = document.querySelectorAll('.trp_language_switcher_shortcode .trp-language-switcher'); if ( trp_ls_shortcodes.length > 0) { // get the last language switcher added var trp_el = trp_ls_shortcodes[trp_ls_shortcodes.length - 1]; var trp_shortcode_language_item = trp_el.querySelector( '.trp-ls-shortcode-language' ) // set width var trp_ls_shortcode_width = trp_shortcode_language_item.offsetWidth + 16; trp_shortcode_language_item.style.width = trp_ls_shortcode_width + 'px'; trp_el.querySelector( '.trp-ls-shortcode-current-language' ).style.width = trp_ls_shortcode_width + 'px'; // We're putting this on display: none after we have its width. trp_shortcode_language_item.style.display = 'none'; } </script> </div> </div>