├── .gitignore ├── vendor ├── composer │ ├── installed.json │ ├── autoload_psr4.php │ ├── autoload_namespaces.php │ ├── installed.php │ ├── LICENSE │ └── autoload_real.php └── autoload.php ├── js └── build │ ├── media.min.js │ ├── filter-path-middleware.min.js │ ├── user.min.js │ ├── admin.min.js │ ├── media.js │ ├── metabox-button.min.js │ ├── index.min.js │ ├── filter-path-middleware.js │ ├── metabox-autocomplete.min.js │ ├── user.js │ ├── bulk-translate.min.js │ ├── confirmation-modal.min.js │ ├── metabox-button.js │ ├── widgets.min.js │ ├── admin.js │ ├── integrations │ └── acf.min.js │ ├── post.min.js │ ├── index.js │ ├── metabox-autocomplete.js │ └── nav-menu.min.js ├── modules ├── duplicate │ ├── uninstall.php │ ├── load.php │ ├── duplicate.php │ └── duplicate-rest.php ├── Machine_Translation │ ├── uninstall.php │ ├── Views │ │ ├── progress-bar.php │ │ ├── characters-consumption-row.php │ │ ├── inner-notice.php │ │ ├── inner-notices-row.php │ │ └── string-form.php │ ├── Clients │ │ └── Client_Interface.php │ ├── Settings │ │ └── Settings_Interface.php │ ├── load.php │ ├── Posts │ │ ├── Button.php │ │ └── Button_REST.php │ ├── Strings │ │ └── Metabox.php │ └── css │ │ └── machine-translation-settings.css ├── translate-slugs │ ├── uninstall.php │ ├── load.php │ └── settings-translate-slugs.php ├── active-languages │ └── load.php ├── rest │ ├── load.php │ └── rest-comment.php ├── locale-fallback │ ├── load.php │ └── view-locale-fallback.php ├── sync │ └── load.php ├── Site_Health │ ├── load.php │ └── Info.php ├── wizard │ ├── load.php │ └── wizard-pro.php ├── bulk-translate │ ├── load.php │ ├── view-bulk-translate-option.php │ ├── css │ │ └── bulk-translate.css │ └── view-bulk-translate.php ├── xdata │ ├── load.php │ └── xdata-session-manager.php ├── import-export │ ├── xliff │ │ ├── export │ │ │ └── xliff-export-20.php │ │ └── xliff-format.php │ ├── load.php │ ├── export │ │ ├── view-export-file-format.php │ │ ├── export-file.php │ │ ├── strings-form-trait.php │ │ └── view-tab-export-strings.php │ ├── file-format │ │ └── file-format.php │ ├── po │ │ └── po-format.php │ └── import │ │ ├── view-tab-import-translations.php │ │ ├── import-file-interface.php │ │ └── import-object-interface.php ├── blocks │ ├── load.php │ └── language-switcher │ │ └── language-switcher-block.php ├── module-interface.php ├── Widget_Blocks │ ├── load.php │ └── Frontend_Filters.php ├── frontend-filter-template │ ├── load.php │ ├── filter-template-single.php │ ├── filter-template-custom-taxonomy.php │ ├── filter-templates.php │ ├── filter-template-page.php │ ├── filter-template-core-taxonomy.php │ └── abstract-filter-template.php ├── share-slug │ ├── load.php │ └── settings-share-slug.php ├── sync-post │ ├── load.php │ └── sync-post-bulk-option.php ├── Editors │ ├── load.php │ └── Screens │ │ ├── Post.php │ │ ├── Widget.php │ │ └── Site.php ├── media │ ├── media-bulk-option.php │ ├── load.php │ └── settings-advanced-media.php └── full-site-editing │ ├── fse-abstract-module.php │ ├── load.php │ ├── fse-post-types.php │ ├── fse-post-deletion.php │ ├── fse-default-language-change.php │ ├── fse-language-slug-change.php │ └── fse-language.php ├── css └── build │ ├── metabox-button.min.css │ ├── metabox-button.css │ ├── bulk-translate.min.css │ ├── translations-dashboard.min.css │ ├── bulk-translate.css │ ├── dialog.min.css │ ├── machine-translation-settings.min.css │ ├── translations-dashboard.css │ ├── machine-translation-settings.css │ └── dialog.css ├── integrations ├── beaver-builder │ ├── load.php │ └── flbuilder.php ├── cptui │ └── load.php ├── divi │ ├── load.php │ └── divi-builder.php ├── events-calendar │ └── load.php ├── admin-columns │ ├── load.php │ └── cpac.php ├── content-blocks │ ├── load.php │ └── content-blocks.php └── acf │ ├── Strategy │ ├── Copy_All.php │ ├── Collect_Term_Ids.php │ └── Collect_Post_Ids.php │ ├── Entity │ ├── Translatable_Entity_Interface.php │ ├── Term.php │ └── Media.php │ ├── load.php │ ├── Labels │ ├── Taxonomy.php │ └── Post_Type.php │ ├── README.md │ └── js │ └── index.js ├── services ├── exporter │ ├── export-term-metas.php │ ├── export-post-metas.php │ ├── export-data-from-strings.php │ └── export-container.php ├── metabox-button │ ├── css │ │ └── metabox-button.css │ ├── metabox-user-button.php │ ├── js │ │ └── metabox-button.js │ └── toggle-user-meta.php ├── translation │ ├── translation-walker-interface.php │ ├── translation-post-metas.php │ ├── translation-term-metas.php │ ├── translation-object-model-trait.php │ ├── translation-walker-factory.php │ ├── translation-data-model-interface.php │ └── translation-content.php └── manage-user-capabilities.php ├── include ├── Options │ ├── Registry.php │ └── Business │ │ ├── Machine_Translation_Enabled.php │ │ ├── Media.php │ │ └── Machine_Translation_Services.php ├── functions.php └── upgrade.php ├── uninstall.php └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | *.log -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [], 3 | "dev": true, 4 | "dev-package-names": [] 5 | } 6 | -------------------------------------------------------------------------------- /js/build/media.min.js: -------------------------------------------------------------------------------- 1 | jQuery((function(a){a.ajaxPrefilter((function(t,d,f){"string"==typeof t.data&&-1!==t.data.indexOf("action=find_posts")&&(t.data="pll_post_id="+a("#affected").val()+"&"+t.data)}))})); -------------------------------------------------------------------------------- /modules/duplicate/uninstall.php: -------------------------------------------------------------------------------- 1 | {const r=t.path.split("?")[0].replace(/^\/+|\/+$/g,"");return Object.values(e).find((t=>r===t))?l(t):t};var __WEBPACK_DEFAULT_EXPORT__=null; -------------------------------------------------------------------------------- /vendor/composer/autoload_namespaces.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | $polylang->active_languages = new PLL_Active_Languages( $polylang ); 12 | } 13 | -------------------------------------------------------------------------------- /modules/rest/load.php: -------------------------------------------------------------------------------- 1 | rest_api = new PLL_REST_API( $polylang ); 14 | } 15 | ); 16 | -------------------------------------------------------------------------------- /modules/locale-fallback/load.php: -------------------------------------------------------------------------------- 1 | locale_fallback = new PLL_Locale_Fallback(); 11 | add_action( 'pll_init', array( $polylang->locale_fallback, 'init' ) ); 12 | -------------------------------------------------------------------------------- /css/build/metabox-button.min.css: -------------------------------------------------------------------------------- 1 | .pll-button{background:none;border:none;cursor:pointer;font-size:20px;height:20px;padding:0}.pll-button:not(.wp-ui-text-highlight){color:#ddd}.pll-button svg{fill:currentColor}.pll-before-post-translations-button{float:right;margin:13px 7px}.rtl .pll-before-post-translations-button{float:left} -------------------------------------------------------------------------------- /modules/sync/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | $polylang->sync_content = new PLL_Sync_Content( $polylang ); 12 | $polylang->navigation = new PLL_Sync_Navigation( $polylang ); 13 | } 14 | -------------------------------------------------------------------------------- /modules/Site_Health/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 13 | $polylang->site_health_pro = new Info(); 14 | } 15 | -------------------------------------------------------------------------------- /integrations/beaver-builder/load.php: -------------------------------------------------------------------------------- 1 | flbuilder = new PLL_FLBuilder(); 15 | } 16 | } 17 | ); 18 | -------------------------------------------------------------------------------- /integrations/cptui/load.php: -------------------------------------------------------------------------------- 1 | cptui = new PLL_CPTUI(), 'init' ) ); 15 | } 16 | }, 17 | 0 18 | ); 19 | -------------------------------------------------------------------------------- /integrations/divi/load.php: -------------------------------------------------------------------------------- 1 | divi_builder = new PLL_Divi_Builder(); 15 | } 16 | } 17 | ); 18 | -------------------------------------------------------------------------------- /modules/wizard/load.php: -------------------------------------------------------------------------------- 1 | wizard_pro = new PLL_Wizard_Pro( $polylang ); 15 | }, 16 | 30 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /integrations/events-calendar/load.php: -------------------------------------------------------------------------------- 1 | tec = new PLL_TEC(), 'init' ) ); 15 | } 16 | }, 17 | 0 18 | ); 19 | -------------------------------------------------------------------------------- /modules/bulk-translate/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | $polylang->bulk_translate = new PLL_Bulk_Translate( $polylang->model ); 12 | add_action( 'current_screen', array( $polylang->bulk_translate, 'init' ) ); 13 | } 14 | -------------------------------------------------------------------------------- /js/build/user.min.js: -------------------------------------------------------------------------------- 1 | jQuery((function(e){var n=e("#description").parent(),i=e("#description").clone(),t=n.children(".description").clone();n.children().remove(),e(".biography").each((function(){lang=e(this).attr("name").split("___"),desc=i.clone(),desc.attr("name","description_"+lang[0]),desc.attr("id","description_"+lang[0]),desc.html(e(this).val()),n.append(e("
").text(lang[1])),n.append(desc)})),n.append("
"),n.append(t)})); -------------------------------------------------------------------------------- /integrations/admin-columns/load.php: -------------------------------------------------------------------------------- 1 | cpac = new PLL_CPAC(), 'init' ) ); 15 | } 16 | } 17 | ); 18 | -------------------------------------------------------------------------------- /integrations/content-blocks/load.php: -------------------------------------------------------------------------------- 1 | content_blocks = new PLL_Content_Blocks(), 'init' ) ); 15 | } 16 | }, 17 | 0 18 | ); 19 | -------------------------------------------------------------------------------- /services/exporter/export-term-metas.php: -------------------------------------------------------------------------------- 1 | meta_type = 'term'; 20 | $this->import_export_meta_type = PLL_Import_Export::TERM_META; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /css/build/metabox-button.css: -------------------------------------------------------------------------------- 1 | .pll-button { 2 | padding: 0; 3 | height: 20px; 4 | background: none; 5 | border: none; 6 | font-size: 20px; 7 | cursor: pointer; 8 | } 9 | 10 | .pll-button:not(.wp-ui-text-highlight) { 11 | color: #DDDDDD; 12 | } 13 | 14 | .pll-button svg { 15 | fill: currentColor; 16 | } 17 | 18 | .pll-before-post-translations-button { 19 | float: right; 20 | margin: 13px 7px; 21 | } 22 | 23 | .rtl .pll-before-post-translations-button { 24 | float: left; 25 | } 26 | -------------------------------------------------------------------------------- /modules/xdata/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | $class = array( 2 => 'PLL_Xdata_Subdomain', 3 => 'PLL_Xdata_Domain' ); 12 | if ( isset( $class[ $polylang->options['force_lang'] ] ) ) { 13 | $polylang->xdata = new $class[ $polylang->options['force_lang'] ]( $polylang ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /services/metabox-button/css/metabox-button.css: -------------------------------------------------------------------------------- 1 | .pll-button { 2 | padding: 0; 3 | height: 20px; 4 | background: none; 5 | border: none; 6 | font-size: 20px; 7 | cursor: pointer; 8 | } 9 | 10 | .pll-button:not(.wp-ui-text-highlight) { 11 | color: #DDDDDD; 12 | } 13 | 14 | .pll-button svg { 15 | fill: currentColor; 16 | } 17 | 18 | .pll-before-post-translations-button { 19 | float: right; 20 | margin: 13px 7px; 21 | } 22 | 23 | .rtl .pll-before-post-translations-button { 24 | float: left; 25 | } 26 | -------------------------------------------------------------------------------- /js/build/admin.min.js: -------------------------------------------------------------------------------- 1 | "use strict";function ajaxFilter(a){if("undefined"==typeof jQuery||!a)return;const t=jQuery.param(a);jQuery.ajaxPrefilter((function(e){if(-1!==e.url.indexOf(ajaxurl)||-1!==ajaxurl.indexOf(e.url))if(void 0===e.data||null===e.data||"string"==typeof e.data&&""===e.data.trim())e.data=t;else if("string"==typeof e.data)try{e.data=JSON.stringify(Object.assign(JSON.parse(e.data),a))}catch(a){e.data=`${e.data}&${t}`}else jQuery.isPlainObject(e.data)&&(e.data=Object.assign(e.data,a))}))}ajaxFilter(pll_admin?.ajax_filter); -------------------------------------------------------------------------------- /modules/import-export/xliff/export/xliff-export-20.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | if ( $polylang instanceof PLL_Admin ) { 12 | new PLL_Admin_Loader( $polylang, 'duplicate' ); 13 | $polylang->duplicate_action = new PLL_Duplicate_Action( $polylang ); 14 | } elseif ( $polylang instanceof PLL_REST_Request ) { 15 | $polylang->duplicate = new PLL_Duplicate_REST( $polylang ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /js/build/media.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @package Polylang 3 | */ 4 | 5 | /** 6 | * Media list table 7 | * When clicking on attach link, filters find post list per media language 8 | */ 9 | jQuery( 10 | function ( $ ) { 11 | $.ajaxPrefilter( 12 | function ( options, originalOptions, jqXHR ) { 13 | if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) { 14 | options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data; 15 | } 16 | } 17 | ); 18 | } 19 | ); 20 | 21 | -------------------------------------------------------------------------------- /modules/Machine_Translation/Views/progress-bar.php: -------------------------------------------------------------------------------- 1 |
', 14 | esc_attr( $atts['ajax_action'] ), 15 | esc_attr( wp_create_nonce( $atts['ajax_action'] ) ) 16 | ); 17 | -------------------------------------------------------------------------------- /modules/blocks/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() && pll_use_block_editor_plugin() ) { 14 | $polylang->switcher_block = ( new PLL_Language_Switcher_Block( $polylang ) )->init(); 15 | $polylang->navigation_block = ( new PLL_Navigation_Language_Switcher_Block( $polylang ) )->init(); 16 | } 17 | } 18 | ); 19 | -------------------------------------------------------------------------------- /modules/module-interface.php: -------------------------------------------------------------------------------- 1 | get_method(), array( 'PATCH', 'POST', 'PUT' ), true ) ) { 16 | return true; 17 | } 18 | 19 | return 'GET' === $request->get_method() && 'edit' === $request->get_param( 'context' ); 20 | } 21 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | if ( $polylang instanceof PLL_Admin ) { 12 | require_once POLYLANG_PRO_DIR . '/modules/bulk-translate/load.php'; 13 | } 14 | require_once POLYLANG_PRO_DIR . '/modules/sync/load.php'; 15 | require_once POLYLANG_DIR . '/modules/sync/load.php'; 16 | 17 | $polylang->import_export = new PLL_Import_Export( $polylang ); 18 | } 19 | -------------------------------------------------------------------------------- /services/translation/translation-walker-interface.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 20 |

 

21 | 22 | 23 | -------------------------------------------------------------------------------- /js/build/metabox-button.min.js: -------------------------------------------------------------------------------- 1 | jQuery((function(t){t("#ml_box").on("click",".pll-button",(function(){var a=t(this).hasClass("wp-ui-text-highlight"),l=t(this).attr("id"),e=t("#htr_lang_"+l.replace("pll_sync_post[","").replace("]","")).val();if(void 0===e||0==e||a||confirm(pll_sync_post.confirm_text)){var n={action:"toggle_"+l,value:a,post_type:t("#post_type").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,n,(function(a){var e=wpAjax.parseAjaxResponse(a,"pll-ajax-response");t.each(e.responses,(function(){l=l.replace("[","\\[").replace("]","\\]"),t("#"+l).toggleClass("wp-ui-text-highlight").attr("title",this.data).children("span").text(this.data),t('input[name="'+l+'"]').val(!n.value)}))}))}}))})); -------------------------------------------------------------------------------- /modules/Widget_Blocks/load.php: -------------------------------------------------------------------------------- 1 | model->languages->has() && pll_use_block_editor_plugin() ) { 17 | if ( $polylang instanceof PLL_Frontend ) { 18 | $polylang->filters_widgets_blocks = new Frontend_Filters( $polylang ); 19 | } 20 | 21 | $polylang->widget_editor = ( new Language_Attribute() )->init(); 22 | } 23 | } 24 | ); 25 | -------------------------------------------------------------------------------- /js/build/index.min.js: -------------------------------------------------------------------------------- 1 | "use strict";var __webpack_require__={d:(a,e)=>{for(var t in e)__webpack_require__.o(e,t)&&!__webpack_require__.o(a,t)&&Object.defineProperty(a,t,{enumerable:!0,get:e[t]})},o:(a,e)=>Object.prototype.hasOwnProperty.call(a,e)},__webpack_exports__={};function ajaxFilter(a){if("undefined"==typeof jQuery||!a)return;const e=jQuery.param(a);jQuery.ajaxPrefilter((function(t){if(-1!==t.url.indexOf(ajaxurl)||-1!==ajaxurl.indexOf(t.url))if(void 0===t.data||null===t.data||"string"==typeof t.data&&""===t.data.trim())t.data=e;else if("string"==typeof t.data)try{t.data=JSON.stringify(Object.assign(JSON.parse(t.data),a))}catch(a){t.data=`${t.data}&${e}`}else jQuery.isPlainObject(t.data)&&(t.data=Object.assign(t.data,a))}))} -------------------------------------------------------------------------------- /modules/locale-fallback/view-locale-fallback.php: -------------------------------------------------------------------------------- 1 | 12 |
13 | 14 | 15 |

16 |
17 | -------------------------------------------------------------------------------- /modules/frontend-filter-template/load.php: -------------------------------------------------------------------------------- 1 | filtered_templates = ( new PLL_Filter_Templates( $polylang ) )->init(); 23 | } 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wordpress-premium/polylang-pro", 3 | "type": "wordpress-plugin", 4 | "description": "With over 500,000 installs, Polylang is the most popular multilingual plugin available on the WordPress directory. You write your posts, pages and create categories and post tags as usual, and assign a language to each of them.", 5 | "homepage": "https://polylang.pro", 6 | "license": "GPL-3.0-or-later", 7 | "keywords": [ 8 | "multilingual", 9 | "bilingual", 10 | "translate", 11 | "translation", 12 | "language", 13 | "multilanguage", 14 | "international", 15 | "localization" 16 | ], 17 | "autoload": { 18 | "classmap": ["include/", "integrations/", "modules/", "services/", "../polylang"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /modules/share-slug/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | add_filter( 12 | 'pll_settings_modules', 13 | function ( $modules ) { 14 | $k = array_search( 'PLL_Settings_Preview_Share_Slug', $modules ); 15 | if ( $k ) { 16 | $modules[ $k ] = 'PLL_Settings_Share_Slug'; 17 | } 18 | return $modules; 19 | }, 20 | 20 // After Polylang. 21 | ); 22 | 23 | if ( get_option( 'permalink_structure' ) && $polylang->options['force_lang'] ) { 24 | $polylang->share_post_slug = new PLL_Share_Post_Slug( $polylang ); 25 | $polylang->share_term_slug = new PLL_Share_Term_Slug( $polylang ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/frontend-filter-template/filter-template-single.php: -------------------------------------------------------------------------------- 1 | post_type}-{$post->post_name}.php" ); 23 | 24 | return $templates; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /services/translation/translation-post-metas.php: -------------------------------------------------------------------------------- 1 | get_name(); 14 | ?> 15 | 19 | -------------------------------------------------------------------------------- /integrations/acf/Strategy/Copy_All.php: -------------------------------------------------------------------------------- 1 | taxonomy}-{$term->slug}.php" ); 23 | 24 | return $templates; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /services/translation/translation-object-model-trait.php: -------------------------------------------------------------------------------- 1 | assign_parents( $ids, $target_language ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /integrations/acf/Entity/Translatable_Entity_Interface.php: -------------------------------------------------------------------------------- 1 | array( 3 | 'name' => 'wordpress-premium/polylang-pro', 4 | 'pretty_version' => '1.0.0+no-version-set', 5 | 'version' => '1.0.0.0', 6 | 'reference' => null, 7 | 'type' => 'wordpress-plugin', 8 | 'install_path' => __DIR__ . '/../../', 9 | 'aliases' => array(), 10 | 'dev' => true, 11 | ), 12 | 'versions' => array( 13 | 'wordpress-premium/polylang-pro' => array( 14 | 'pretty_version' => '1.0.0+no-version-set', 15 | 'version' => '1.0.0.0', 16 | 'reference' => null, 17 | 'type' => 'wordpress-plugin', 18 | 'install_path' => __DIR__ . '/../../', 19 | 'aliases' => array(), 20 | 'dev_requirement' => false, 21 | ), 22 | ), 23 | ); 24 | -------------------------------------------------------------------------------- /modules/sync-post/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | $polylang->sync_post_model = new PLL_Sync_Post_Model( $polylang ); 12 | 13 | if ( $polylang instanceof PLL_Admin ) { 14 | require_once POLYLANG_PRO_DIR . '/modules/bulk-translate/load.php'; 15 | } 16 | 17 | if ( wp_doing_cron() || ( defined( 'WP_CLI' ) && WP_CLI ) ) { 18 | $polylang->sync_post = new PLL_Sync_Post( $polylang ); 19 | } elseif ( $polylang instanceof PLL_Admin ) { 20 | new PLL_Admin_Loader( $polylang, 'sync_post' ); 21 | } elseif ( $polylang instanceof PLL_REST_Request ) { 22 | $polylang->sync_post = new PLL_Sync_Post_REST( $polylang ); 23 | } else { 24 | $polylang->sync_post = new PLL_Sync_Post( $polylang ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /services/translation/translation-walker-factory.php: -------------------------------------------------------------------------------- 1 | { 18 | const cleanPath = options.path.split( '?' )[0].replace(/^\/+|\/+$/g, ''); // Get path without query parameters and trim '/'. 19 | 20 | return Object.values( filteredRoutes ).find( ( path ) => cleanPath === path ) ? filter( options ) : options; 21 | } 22 | 23 | /* unused harmony default export */ var __WEBPACK_DEFAULT_EXPORT__ = ((/* unused pure expression or super */ null && (filterPathMiddleware))); 24 | 25 | -------------------------------------------------------------------------------- /js/build/metabox-autocomplete.min.js: -------------------------------------------------------------------------------- 1 | "use strict";var __webpack_require__={d:(e,t)=>{for(var r in t)__webpack_require__.o(t,r)&&!__webpack_require__.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},__webpack_exports__={};function initMetaboxAutoComplete(){jQuery(".tr_lang").each((function(){var e=jQuery(this).attr("id").substring(8),t=jQuery(this).parent().parent().siblings(".pll-edit-column");jQuery(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+jQuery(".post_lang_choice").val()+"&translation_language="+e+"&post_type="+jQuery("#post_type").val()+"&_pll_nonce="+jQuery("#_pll_nonce").val(),select:function(r,n){jQuery("#htr_lang_"+e).val(n.item.id),t.html(n.item.link)}}),jQuery(this).on("blur",(function(){jQuery(this).val()||(jQuery("#htr_lang_"+e).val(0),t.html(t.siblings(".hidden").children().clone()))}))}))} -------------------------------------------------------------------------------- /integrations/acf/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 26 | // Run only if Polylang (and its API) is loaded, and if there is at least one language. 27 | return; 28 | } 29 | 30 | PLL_Integrations::instance()->acf = new Main(); 31 | 32 | add_action( 'acf/init', array( PLL_Integrations::instance()->acf, 'on_acf_init' ) ); 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /modules/Editors/load.php: -------------------------------------------------------------------------------- 1 | model->languages->has() 20 | && $polylang instanceof PLL_Admin 21 | && pll_use_block_editor_plugin() 22 | ) { 23 | $polylang->site_editor = ( new Site( $polylang ) )->init(); 24 | $polylang->post_editor = ( new Post( $polylang ) )->init(); 25 | $polylang->widget_editor = ( new Widget( $polylang ) )->init(); 26 | $polylang->filter_path = ( new Filter_Preload_Paths( $polylang ) )->init(); 27 | } 28 | } 29 | ); 30 | -------------------------------------------------------------------------------- /modules/Machine_Translation/Views/inner-notice.php: -------------------------------------------------------------------------------- 1 | ` and `` tags. 7 | * @type string $slug Slug of the machine translation service. 8 | * @type string $type Optional. Possible values are `success`, `warning`, `error`, and `info`. Default is `error`. 9 | * } 10 | */ 11 | 12 | defined( 'ABSPATH' ) || exit; 13 | 14 | $tags = array( 15 | 'br' => array(), 16 | 'code' => array(), 17 | ); 18 | 19 | $atts['type'] = ! empty( $atts['type'] ) && in_array( $atts['type'], array( 'success', 'warning', 'info' ), true ) ? $atts['type'] : 'error'; 20 | ?> 21 |
22 |

23 |
24 | -------------------------------------------------------------------------------- /modules/import-export/export/view-export-file-format.php: -------------------------------------------------------------------------------- 1 | 16 | 30 | -------------------------------------------------------------------------------- /modules/media/media-bulk-option.php: -------------------------------------------------------------------------------- 1 | base && current_user_can( 'upload_files' ); 22 | } 23 | 24 | 25 | /** 26 | * Duplicates a media object 27 | * 28 | * @since 2.7 29 | * 30 | * @param int $object_id The media id. 31 | * @param string $lang A language locale. 32 | * @return void 33 | */ 34 | public function translate( $object_id, $lang ) { 35 | $this->model->post->create_media_translation( $object_id, $lang ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /css/build/bulk-translate.min.css: -------------------------------------------------------------------------------- 1 | .bulk-translate-save .button{margin-right:20px}#wpbody-content #pll-translate .pll-bulk-translate-fields-wrapper{display:flex}#wpbody-content #pll-translate fieldset{flex-basis:fit-content;margin-right:10%}#pll-translate .pll-translation-flag{margin:auto 10px auto 7px}.rtl #pll-translate .pll-translation-flag{margin:auto 7px auto 10px}#pll-translate .title{display:block;line-height:2.5}#pll-translate label{align-items:center;display:flex}#pll-translate label span.description{line-height:normal}#pll-translate label[for=pll-select-format]{padding-left:18px}#pll-translate #pll-select-format{margin-left:.5rem}@supports(selector(:has(*))){#pll-translate label[for=pll-select-format]{display:none}#pll-translate label:has([name=translate][value=pll_export_post]:checked)~[for=pll-select-format]{display:flex}}@media screen and (max-width:782px){#wpbody-content #pll-translate .pll-bulk-translate-fields-wrapper{flex-direction:column}} -------------------------------------------------------------------------------- /modules/media/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | add_filter( 12 | 'pll_settings_modules', 13 | function ( $modules ) { 14 | $k = array_search( 'PLL_Settings_Media', $modules ); 15 | if ( $k ) { 16 | $modules[ $k ] = 'PLL_Settings_Advanced_Media'; 17 | } 18 | return $modules; 19 | }, 20 | 0 21 | ); 22 | 23 | add_action( 24 | 'pll_init', 25 | function ( $polylang ) { 26 | if ( $polylang->options['media_support'] ) { 27 | if ( $polylang instanceof PLL_Admin ) { 28 | require_once POLYLANG_PRO_DIR . '/modules/bulk-translate/load.php'; 29 | } 30 | 31 | if ( $polylang instanceof PLL_Admin || $polylang instanceof PLL_REST_Request ) { 32 | $polylang->advanced_media = new PLL_Admin_Advanced_Media( $polylang ); 33 | } 34 | } 35 | } 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /include/Options/Business/Machine_Translation_Enabled.php: -------------------------------------------------------------------------------- 1 | 'libxml', 34 | 'required' => true, 35 | ); 36 | $modules['zip'] = array( 37 | 'class' => 'ZipArchive', 38 | 'required' => true, 39 | ); 40 | return $modules; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /modules/translate-slugs/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 11 | add_filter( 12 | 'pll_settings_modules', 13 | function ( $modules ) { 14 | $k = array_search( 'PLL_Settings_Preview_Translate_Slugs', $modules ); 15 | if ( $k ) { 16 | $modules[ $k ] = 'PLL_Settings_Translate_Slugs'; 17 | } 18 | return $modules; 19 | }, 20 | 20 // After Polylang. 21 | ); 22 | 23 | if ( get_option( 'permalink_structure' ) ) { 24 | $slugs_model = new PLL_Translate_Slugs_Model( $polylang ); 25 | 26 | if ( $polylang instanceof PLL_Frontend ) { 27 | $polylang->translate_slugs = new PLL_Frontend_Translate_Slugs( $slugs_model, $polylang->curlang ); 28 | } else { 29 | $curlang = isset( $polylang->curlang ) ? $polylang->curlang : null; 30 | $polylang->translate_slugs = new PLL_Translate_Slugs( $slugs_model, $curlang ); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /services/metabox-button/metabox-user-button.php: -------------------------------------------------------------------------------- 1 | user_meta->is_active(); 26 | } 27 | 28 | /** 29 | * Saves the button state. 30 | * 31 | * @since 3.6 32 | * 33 | * @param string $post_type Current post type. 34 | * @param bool $active New requested button state. 35 | * @return bool Whether the new button state is accepted or not. 36 | */ 37 | protected function toggle_option( $post_type, $active ) { 38 | return $this->user_meta->toggle_option( $post_type, $active ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /modules/duplicate/duplicate.php: -------------------------------------------------------------------------------- 1 | user_meta = new PLL_Toggle_User_Meta( PLL_Duplicate_Action::META_NAME ); 29 | 30 | $args = array( 31 | 'position' => 'before_post_translations', 32 | 'activate' => __( 'Activate content duplication', 'polylang-pro' ), 33 | 'deactivate' => __( 'Deactivate content duplication', 'polylang-pro' ), 34 | 'class' => 'dashicons-before dashicons-admin-page', 35 | ); 36 | 37 | parent::__construct( 'pll-duplicate', $args ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /integrations/beaver-builder/flbuilder.php: -------------------------------------------------------------------------------- 1 | 'none' ) ); 21 | } 22 | 23 | /** 24 | * Returns the module description. 25 | * 26 | * @since 3.1 27 | * 28 | * @return string 29 | */ 30 | protected function get_description() { 31 | return parent::get_description() . ' ' . __( 'The module is automatically deactivated when using plain permalinks.', 'polylang-pro' ); 32 | } 33 | 34 | /** 35 | * Tells if the module is active. 36 | * 37 | * @since 1.9 38 | * 39 | * @return bool 40 | */ 41 | public function is_active() { 42 | return get_option( 'permalink_structure' ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /integrations/acf/Strategy/Collect_Term_Ids.php: -------------------------------------------------------------------------------- 1 | model = &$polylang->model; 39 | $this->options = &$polylang->options; 40 | } 41 | 42 | /** 43 | * Returns the list of the slugs of enabled languages. 44 | * 45 | * @since 1.0 46 | * 47 | * @return string[] 48 | */ 49 | protected function get_languages_slugs() { 50 | return $this->model->get_languages_list( array( 'fields' => 'slug' ) ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /modules/rest/rest-comment.php: -------------------------------------------------------------------------------- 1 | array() ) ); 22 | 23 | $this->type = 'comment'; 24 | 25 | add_action( 'parse_comment_query', array( $this, 'parse_comment_query' ), 5 ); 26 | } 27 | 28 | /** 29 | * Filters the query per language according to the 'lang' parameter. 30 | * 31 | * @since 2.6.9 32 | * 33 | * @param WP_Comment_Query $query Comment query. 34 | * @return void 35 | */ 36 | public function parse_comment_query( $query ) { 37 | if ( isset( $this->request['lang'] ) && in_array( $this->request['lang'], $this->model->get_languages_list( array( 'fields' => 'slug' ) ) ) ) { 38 | $query->query_vars['lang'] = $this->request['lang']; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /integrations/acf/Strategy/Collect_Post_Ids.php: -------------------------------------------------------------------------------- 1 | options['media_support'] && is_numeric( $value ) ) { 31 | return $value; 32 | } 33 | break; 34 | case 'gallery': 35 | if ( PLL()->options['media_support'] && is_array( $value ) ) { 36 | return $value; 37 | } 38 | break; 39 | } 40 | 41 | return array(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /modules/import-export/file-format/file-format.php: -------------------------------------------------------------------------------- 1 | 51 | */ 52 | abstract public function get_export_class( $version = '' ): string; 53 | } 54 | -------------------------------------------------------------------------------- /modules/import-export/export/export-file.php: -------------------------------------------------------------------------------- 1 | get_source_language()->get_locale( 'display' ); 22 | $target_language = $this->get_target_language()->get_locale( 'display' ); 23 | $datenow = gmdate( 'Y-m-d_G-i-s' ); 24 | $extension = $this->get_extension(); 25 | 26 | return "{$source_language}_{$target_language}_{$datenow}.{$extension}"; 27 | } 28 | 29 | /** 30 | * Returns exported data. 31 | * 32 | * @since 3.6 33 | * 34 | * @return string 35 | */ 36 | abstract public function get(): string; 37 | 38 | /** 39 | * Returns the current file extension. 40 | * 41 | * @since 3.1 42 | * 43 | * @return string The file extension. 44 | */ 45 | abstract protected function get_extension(): string; 46 | } 47 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | register(true); 33 | 34 | return $loader; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /css/build/translations-dashboard.min.css: -------------------------------------------------------------------------------- 1 | .languages_page_mlang_strings .metabox-holder>div{display:flex;flex-wrap:wrap}.languages_page_mlang_strings .metabox-holder>div>div{display:flex;flex-basis:0;flex-direction:column;flex-grow:1}.languages_page_mlang_strings .metabox-holder>div>div:not(:first-child){margin-left:1rem}.languages_page_mlang_strings .metabox-holder>div>div.closed{background:none;border:0}.languages_page_mlang_strings .metabox-holder>div>div.closed .postbox-header{background:#fff;border:1px solid #ccd0d4}#pll-export-strings-box.postbox .submit,#pll-import-translations-box.postbox .submit,#pll-machine-strings-translations.postbox .submit{float:none;padding:0}.postbox .inside{margin:0}#export-string-translation .pll-translation-flag,#pll-machine-strings-translations .pll-translation-flag{margin:auto 10px auto 7px}#export-string-translation label,#export-string-translation label[for=pll-select-format] span,#import-translation label,#pll-machine-strings-translations label,.pll-legend{display:block;margin:.35em 0 .5em}.pll-legend{color:#1d2327;font-weight:400;padding:2px 0;text-shadow:none}@media screen and (max-width:1024px){.languages_page_mlang_strings .metabox-holder>div{flex-direction:column}.languages_page_mlang_strings .metabox-holder>div>div:not(:first-child){margin-left:0}} -------------------------------------------------------------------------------- /modules/Machine_Translation/Clients/Client_Interface.php: -------------------------------------------------------------------------------- 1 | 'text/x-po' ); 23 | 24 | /** 25 | * Po format is always supported. 26 | * 27 | * @since 3.1 28 | * 29 | * @return true 30 | */ 31 | public function is_supported() { 32 | return true; 33 | } 34 | 35 | /** 36 | * Returns the associated import class. 37 | * 38 | * @since 3.1 39 | * 40 | * @return PLL_PO_Import 41 | */ 42 | public function get_import() { 43 | return new PLL_PO_Import(); 44 | } 45 | 46 | /** 47 | * Returns the associated export class. 48 | * 49 | * @since 3.6 50 | * 51 | * @param string $version Optional file format version. Not used for PO. 52 | * @return string 53 | * 54 | * @phpstan-return class-string 55 | */ 56 | public function get_export_class( $version = '' ): string { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable 57 | return PLL_PO_Export::class; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /js/build/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds one biography input field per language in the user profile. 3 | * 4 | * @package Polylang 5 | */ 6 | 7 | jQuery( 8 | function ( $ ) { 9 | // biography 10 | // FIXME there is probably a more efficient way to do this 11 | var td = $( '#description' ).parent(); 12 | var d = $( '#description' ).clone(); 13 | var span = td.children( '.description' ).clone(); 14 | td.children().remove(); 15 | 16 | $( '.biography' ).each( 17 | function () { 18 | lang = $( this ).attr( 'name' ).split( '___' ); 19 | desc = d.clone(); 20 | desc.attr( 'name', 'description_' + lang[0] ); 21 | desc.attr( 'id', 'description_' + lang[0] ); 22 | // Whitelist because description and lang value is already escaped by the side of PHP 23 | desc.html( $( this ).val() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html 24 | td.append( $( '
' ).text( lang[1] ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append 25 | td.append( desc ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append 26 | } 27 | ); 28 | 29 | td.append( '
' ); 30 | // Whitelist because description come from html code generated by WordPress 31 | td.append( span ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append 32 | } 33 | ); 34 | 35 | -------------------------------------------------------------------------------- /css/build/bulk-translate.css: -------------------------------------------------------------------------------- 1 | .bulk-translate-save .button { 2 | margin-right: 20px; 3 | } 4 | 5 | #wpbody-content #pll-translate .pll-bulk-translate-fields-wrapper { 6 | display: flex; 7 | } 8 | 9 | #wpbody-content #pll-translate fieldset { 10 | flex-basis: fit-content; 11 | margin-right: 10%; 12 | } 13 | 14 | #pll-translate .pll-translation-flag { 15 | margin: auto 10px auto 7px; 16 | } 17 | 18 | .rtl #pll-translate .pll-translation-flag { 19 | margin: auto 7px auto 10px; 20 | } 21 | 22 | #pll-translate .title { 23 | display: block; 24 | line-height: 2.5; 25 | } 26 | 27 | #pll-translate label { 28 | display: flex; 29 | align-items: center; 30 | } 31 | 32 | #pll-translate label span.description{ 33 | line-height: normal; 34 | } 35 | 36 | #pll-translate label[for="pll-select-format"] { 37 | padding-left: 18px; 38 | } 39 | 40 | #pll-translate #pll-select-format { 41 | margin-left: 0.5rem; 42 | } 43 | 44 | @supports(selector(:has(*))) { 45 | #pll-translate label[for="pll-select-format"] { 46 | display: none; 47 | } 48 | #pll-translate label:has([name="translate"][value="pll_export_post"]:checked) ~ [for="pll-select-format"] { 49 | display: flex; 50 | } 51 | } 52 | 53 | @media screen and ( max-width: 782px ) { 54 | #wpbody-content #pll-translate .pll-bulk-translate-fields-wrapper { 55 | flex-direction: column; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /integrations/divi/divi-builder.php: -------------------------------------------------------------------------------- 1 | '),t("#pll-translate .inline-edit-wrapper").attr("tabindex","-1").focus(),t("html, body").animate({scrollTop:0},"fast")):t("#pll-translate").find(".cancel").trigger("click")})),t("#pll-translate").on("click",".cancel",(function(){t("#pll-translate").siblings(".hidden").remove(),t("#pll-bulk-translate").append(t("#pll-translate")),t("#"+e.whichBulkButtonId).trigger("focus")})),t("#pll-translate").on("keydown",(function(e){"Enter"!==e.key||t(e.target).hasClass("cancel")||(e.preventDefault(),t(this).find("input[type=submit]").trigger("click")),"Escape"===e.key&&(e.preventDefault(),t(this).find(".cancel").trigger("click"))})),t("#posts-filter").on("submit",(function(){t(".settings-error").remove(),setTimeout((function(){t("input[type=checkbox]:checked").attr("checked",!1),t("#pll-translate").find(".cancel").trigger("click")}),500)}))})); -------------------------------------------------------------------------------- /modules/Machine_Translation/Views/inner-notices-row.php: -------------------------------------------------------------------------------- 1 | ` and `` tags. 7 | * @type string $type Optional type of notice. Possible values are `success`, `warning`, `error`, and `info`. Default is `error`. 8 | * @type string $name Name of the machine translation service. 9 | * @type string[] $languages List of language names with their locale. Can contain `` tags. 10 | * Example array( 'Afrikaans (af)' ). 11 | * } 12 | */ 13 | 14 | defined( 'ABSPATH' ) || exit; 15 | 16 | if ( ! empty( $atts['languages'] ) ) { 17 | $atts['type'] = 'warning'; 18 | if ( 1 === count( $atts['languages'] ) ) { 19 | /* translators: %1$s is a machine translation service's name, %2$s is a language name (and its locale). */ 20 | $atts['message'] = __( 'The following language is not available in %1$s: %2$s.', 'polylang-pro' ); 21 | } else { 22 | /* translators: %1$s is a machine translation service's name, %2$s is a list of language names (and their locale). */ 23 | $atts['message'] = __( 'The following languages are not available in %1$s: %2$s.', 'polylang-pro' ); 24 | } 25 | $atts['message'] = sprintf( $atts['message'], $atts['name'], wp_sprintf_l( '%l', $atts['languages'] ) ); 26 | 27 | include __DIR__ . '/inner-notice.php'; 28 | } 29 | -------------------------------------------------------------------------------- /js/build/confirmation-modal.min.js: -------------------------------------------------------------------------------- 1 | "use strict";var __webpack_require__={d:(e,a)=>{for(var t in a)__webpack_require__.o(a,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},o:(e,a)=>Object.prototype.hasOwnProperty.call(e,a)},__webpack_exports__={};const languagesList=jQuery(".post_lang_choice"),initializeConfirmationModal=()=>{const{__:e}=wp.i18n,a=jQuery("
",{id:"pll-dialog",style:"display:none;"}).text(e("Are you sure you want to change the language of the current content?","polylang"));languagesList.after(a);const t=new Promise(((t,l)=>{const n=e=>{switch(e){case"yes":languagesList.data("old-value",languagesList.children(":selected").first().val()),t();break;case"no":languagesList.val(languagesList.data("old-value")),l("Cancel")}a.dialog("close")},i={autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:e("Change language","polylang"),minWidth:600,maxWidth:"100%",open:function(e,a){jQuery("body").hasClass("rtl")&&jQuery(this).parent().css({right:jQuery(this).parent().css("left"),left:"auto"})},close:function(e,a){n("no")},buttons:[{text:e("OK","polylang"),click:function(e){n("yes")}},{text:e("Cancel","polylang"),click:function(e){n("no")}}]};jQuery.ui.version>="1.12.0"?Object.assign(i,{classes:{"ui-dialog":"pll-confirmation-modal"}}):Object.assign(i,{dialogClass:"pll-confirmation-modal"}),a.dialog(i)}));return{dialogContainer:a,dialogResult:t}},initializeLanguageOldValue=()=>{languagesList.attr("data-old-value",languagesList.children(":selected").first().val())}; -------------------------------------------------------------------------------- /js/build/metabox-button.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handle the response to a click on a Languages metabox button. 3 | * 4 | * @package Polylang-Pro 5 | */ 6 | 7 | jQuery( 8 | function ( $ ) { 9 | $( '#ml_box' ).on( 10 | 'click', 11 | '.pll-button', 12 | function () { 13 | var value = $( this ).hasClass( 'wp-ui-text-highlight' ); 14 | var id = $( this ).attr( 'id' ); 15 | var post_id = $( '#htr_lang_' + id.replace( 'pll_sync_post[', '' ).replace( ']', '' ) ).val(); 16 | 17 | if ( 'undefined' == typeof( post_id ) || 0 == post_id || value || confirm( pll_sync_post.confirm_text ) ) { 18 | var data = { 19 | action: 'toggle_' + id, 20 | value: value, 21 | post_type: $( '#post_type' ).val(), 22 | _pll_nonce: $( '#_pll_nonce' ).val() 23 | } 24 | 25 | $.post( 26 | ajaxurl, 27 | data, 28 | function ( response ) { 29 | // Target a non existing WP HTML id to avoid a conflict with WP ajax requests. 30 | var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' ); 31 | $.each( 32 | res.responses, 33 | function () { 34 | id = id.replace( '[', '\\[' ).replace( ']', '\\]' ); 35 | $( '#' + id ).toggleClass( 'wp-ui-text-highlight' ).attr( 'title', this.data ).children( 'span' ).text( this.data ); 36 | $( 'input[name="' + id + '"]' ).val( ! data['value'] ); 37 | } 38 | ); 39 | } 40 | ); 41 | } 42 | } 43 | ); 44 | } 45 | ); 46 | 47 | -------------------------------------------------------------------------------- /services/metabox-button/js/metabox-button.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handle the response to a click on a Languages metabox button. 3 | * 4 | * @package Polylang-Pro 5 | */ 6 | 7 | jQuery( 8 | function ( $ ) { 9 | $( '#ml_box' ).on( 10 | 'click', 11 | '.pll-button', 12 | function () { 13 | var value = $( this ).hasClass( 'wp-ui-text-highlight' ); 14 | var id = $( this ).attr( 'id' ); 15 | var post_id = $( '#htr_lang_' + id.replace( 'pll_sync_post[', '' ).replace( ']', '' ) ).val(); 16 | 17 | if ( 'undefined' == typeof( post_id ) || 0 == post_id || value || confirm( pll_sync_post.confirm_text ) ) { 18 | var data = { 19 | action: 'toggle_' + id, 20 | value: value, 21 | post_type: $( '#post_type' ).val(), 22 | _pll_nonce: $( '#_pll_nonce' ).val() 23 | } 24 | 25 | $.post( 26 | ajaxurl, 27 | data, 28 | function ( response ) { 29 | // Target a non existing WP HTML id to avoid a conflict with WP ajax requests. 30 | var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' ); 31 | $.each( 32 | res.responses, 33 | function () { 34 | id = id.replace( '[', '\\[' ).replace( ']', '\\]' ); 35 | $( '#' + id ).toggleClass( 'wp-ui-text-highlight' ).attr( 'title', this.data ).children( 'span' ).text( this.data ); 36 | $( 'input[name="' + id + '"]' ).val( ! data['value'] ); 37 | } 38 | ); 39 | } 40 | ); 41 | } 42 | } 43 | ); 44 | } 45 | ); 46 | -------------------------------------------------------------------------------- /integrations/acf/Entity/Term.php: -------------------------------------------------------------------------------- 1 | term_id; 27 | } 28 | 29 | /** 30 | * Transforms a term ID to the corresponding ACF post ID. 31 | * 32 | * @since 3.7 33 | * 34 | * @param int $id Term ID. 35 | * @return string ACF post ID. 36 | */ 37 | protected static function acf_id( $id ) { 38 | return 'term_' . $id; 39 | } 40 | 41 | /** 42 | * Returns source object ID passed in the main request if exists. 43 | * 44 | * @since 3.7 45 | * 46 | * @return int 47 | */ 48 | protected function get_from_id_in_request(): int { 49 | if ( isset( $_GET['taxonomy'], $_GET['from_tag'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 50 | return (int) $_GET['from_tag']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | /** 57 | * Returns current object type. 58 | * 59 | * @since 3.7 60 | * 61 | * @return string 62 | * @phpstan-return non-falsy-string 63 | */ 64 | public function get_type(): string { 65 | return 'term'; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /css/build/dialog.min.css: -------------------------------------------------------------------------------- 1 | .pll-confirmation-modal .ui-widget,.pll-confirmation-modal.ui-widget,.pll-confirmation-modal.ui-widget .ui-widget{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px}.pll-confirmation-modal.ui-dialog{background:#fff;border:0;border-radius:0;color:#444;padding:0;z-index:100102}.ui-dialog.pll-confirmation-modal .ui-dialog-titlebar{background:#fcfcfc;border:0;border-bottom:1px solid #dfdfdf;border-radius:0;color:#444;font-size:18px;font-weight:600;height:36px;line-height:2;padding:0 36px 0 16px;position:static}.ui-dialog.pll-confirmation-modal .ui-dialog-title{float:none;margin:0;width:auto}.pll-confirmation-modal .ui-widget-header .ui-icon{background:none;position:static}.pll-confirmation-modal .ui-button.ui-dialog-titlebar-close{background:none;border:0;height:36px;margin:0;padding:0;right:0;top:0;width:36px}.ui-dialog.pll-confirmation-modal .ui-dialog-content{border:0;box-sizing:border-box;color:#444;padding:16px;position:static}.ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane{background:#fcfcfc;border:0;border-top:1px solid #dfdfdf;margin:0;padding:16px}.ui-dialog.pll-confirmation-modal .ui-dialog-buttonpane .ui-button{background:#f7f7f7;border:1px solid #ccc;border-radius:3px;line-height:2;margin:0 0 0 16px;padding:0 10px 1px;position:static;vertical-align:top}.ui-dialog.pll-confirmation-modal .ui-button:focus,.ui-dialog.pll-confirmation-modal .ui-button:hover{background:#fafafa;border-color:#999;color:#23282d}.pll-confirmation-modal+.ui-widget-overlay{background:#000;opacity:.7;z-index:100101} -------------------------------------------------------------------------------- /modules/frontend-filter-template/filter-templates.php: -------------------------------------------------------------------------------- 1 | > 24 | */ 25 | private $template_types = array( 26 | 'category' => PLL_Filter_Template_Core_Taxonomy::class, 27 | 'tag' => PLL_Filter_Template_Core_Taxonomy::class, 28 | 'taxonomy' => PLL_Filter_Template_Custom_Taxonomy::class, 29 | 'page' => PLL_Filter_Template_Page::class, 30 | 'single' => PLL_Filter_Template_Single::class, 31 | ); 32 | 33 | /** 34 | * Constructor. 35 | * 36 | * @since 3.7 37 | * 38 | * @param PLL_Frontend $polylang Main Polylang object. 39 | */ 40 | public function __construct( PLL_Frontend $polylang ) { 41 | $this->model = $polylang->model; 42 | } 43 | 44 | /** 45 | * Hooks to the template hierarchy filters with the corresponding objects. 46 | * 47 | * @since 3.7 48 | * 49 | * @return self 50 | */ 51 | public function init(): self { 52 | foreach ( $this->template_types as $type => $class ) { 53 | $template_filter = new $class( $this->model ); 54 | add_filter( "{$type}_template_hierarchy", array( $template_filter, 'filter' ) ); 55 | } 56 | 57 | return $this; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /modules/import-export/import/view-tab-import-translations.php: -------------------------------------------------------------------------------- 1 | 15 |
16 |
17 |
18 | 19 | 20 | 23 | 24 |
25 |
26 | 27 | 31 |
32 |

33 | 34 |

35 |
36 |
37 | -------------------------------------------------------------------------------- /js/build/widgets.min.js: -------------------------------------------------------------------------------- 1 | jQuery((function(e){var t,i,n,o=void 0!==wp.blockEditor;function l(t){if(n){t=e(t);var i=o?t.prev("h3"):e(".widget-top .widget-title h3",t),l=e(".pll-lang-choice option:selected",t).val(),d=l&&n.hasOwnProperty(l)?n[l]:null;if(d){d+="   ";var s=e(".pll-lang",i);s.length?s.html(d):(flag=e("").addClass("pll-lang").html(d),i.prepend(flag))}else e(".pll-lang",i).remove()}}if("undefined"!=typeof pll_widgets&&pll_widgets.hasOwnProperty("flags")&&(n=pll_widgets.flags),o)i=".widget",(t=e(".edit-widgets-main-block-list")).on("click",".wp-block-legacy-widget",(function(){l(e(this).find(".widget"))}));else{if(void 0!==wp.customize){function d(e){e.extended(wp.customize.Widgets.WidgetControl)&&(e.embedWidgetContent(),l(e.container.find(".widget")))}t=e("#customize-controls"),i=".customize-control .widget",wp.customize.control.each(d),wp.customize.control.bind("add",d)}else t=e("#widgets-right"),i=".widget";e(i,t).each((function(){l(this)}))}t.on("change",".pll-lang-choice",(function(){l(e(this).parents(".widget"))})),e(".widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list").on("change",".pll-dropdown",(function(){var t,i=e(this).parent().parent().parent().children(".widget-id").attr("value");t=e(".no-dropdown-"+i),1!=e(this).prop("checked")?t.show():t.hide()}));var s=["-show_flags","-show_names"];e.each(s,(function(t,i){e(".widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list").on("change",".pll"+i,(function(){var i=e(this).parent().parent().parent().children(".widget-id").attr("value");1!=e(this).prop("checked")&&e("#widget-"+i+s[1-t]).prop("checked",!0)}))}))})); -------------------------------------------------------------------------------- /integrations/acf/Labels/Taxonomy.php: -------------------------------------------------------------------------------- 1 | 53 | */ 54 | protected function get_type_objects(): array { 55 | return $GLOBALS['wp_taxonomies']; 56 | } 57 | 58 | /** 59 | * Returns the label of the type. 60 | * 61 | * @since 3.7 62 | * 63 | * @return string 64 | * 65 | * @phpstan-return non-empty-string 66 | */ 67 | protected function get_type_label(): string { 68 | return 'Taxonomy'; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /js/build/admin.js: -------------------------------------------------------------------------------- 1 | /******/ "use strict"; 2 | 3 | ;// ./vendor/wpsyntex/polylang/js/src/lib/ajax-filter/index.js 4 | /** 5 | * @package Polylang 6 | */ 7 | 8 | /** 9 | * Adds data to all ajax requests made with jQuery. 10 | * 11 | * @since 3.7 12 | * 13 | * @param {Object} data The data to add. 14 | * @returns {void} 15 | */ 16 | function ajaxFilter( data ) { 17 | if ( 'undefined' === typeof jQuery || ! data ) { 18 | return; 19 | } 20 | 21 | const dataStr = jQuery.param( data ); 22 | 23 | jQuery.ajaxPrefilter( function ( options ) { 24 | if ( -1 === options.url.indexOf( ajaxurl ) && -1 === ajaxurl.indexOf( options.url ) ) { 25 | return; 26 | } 27 | 28 | if ( 29 | 'undefined' === typeof options.data || 30 | null === options.data || 31 | 'string' === typeof options.data && '' === options.data.trim() 32 | ) { 33 | // An empty string or null/undefined. 34 | options.data = dataStr; 35 | } else if ( 'string' === typeof options.data ) { 36 | // A non-empty string: can be a JSON string or a query string. 37 | try { 38 | options.data = JSON.stringify( Object.assign( JSON.parse( options.data ), data ) ); 39 | } catch ( exception ) { 40 | // A non-empty non-JSON string is considered a query string. 41 | options.data = `${ options.data }&${ dataStr }`; 42 | } 43 | } else if ( jQuery.isPlainObject( options.data ) ) { 44 | // An object. 45 | options.data = Object.assign( options.data, data ); 46 | } 47 | } ); 48 | } 49 | 50 | ;// ./vendor/wpsyntex/polylang/js/src/admin.js 51 | /** 52 | * @package Polylang 53 | */ 54 | 55 | 56 | 57 | ajaxFilter( pll_admin?.ajax_filter ); 58 | 59 | -------------------------------------------------------------------------------- /integrations/acf/Labels/Post_Type.php: -------------------------------------------------------------------------------- 1 | 53 | */ 54 | protected function get_type_objects(): array { 55 | return $GLOBALS['wp_post_types']; 56 | } 57 | 58 | /** 59 | * Returns the label of the type. 60 | * 61 | * @since 3.7 62 | * 63 | * @return string 64 | * 65 | * @phpstan-return non-empty-string 66 | */ 67 | protected function get_type_label(): string { 68 | return 'Post Type'; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /modules/full-site-editing/load.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 9 | return; 10 | } 11 | 12 | add_action( 13 | 'pll_init', 14 | function ( $polylang ) { 15 | $pll_fse_sub_modules = array( 16 | PLL_FSE_Default_Language_Change::class, 17 | PLL_FSE_Language::class, 18 | PLL_FSE_Language_Slug_Change::class, 19 | PLL_FSE_Filter_Block_Types::class, 20 | PLL_FSE_Post_Types::class, 21 | PLL_FSE_Query_Filters::class, 22 | PLL_FSE_REST_Duplicate_Template::class, 23 | PLL_FSE_REST_Enforce_Default_Template::class, 24 | PLL_FSE_Post_Deletion::class, 25 | PLL_FSE_Template_Model::class, 26 | PLL_FSE_Template_Slug_Sync::class, 27 | ); 28 | 29 | foreach ( $pll_fse_sub_modules as $pll_fse_class ) { 30 | $polylang->{$pll_fse_class::get_name()} = ( new $pll_fse_class( $polylang ) )->init(); 31 | } 32 | 33 | if ( $polylang->model instanceof PLL_Admin_Model ) { 34 | $polylang->{PLL_FSE_Recreate_Language::get_name()} = ( new PLL_FSE_Recreate_Language( $polylang ) )->init(); 35 | } 36 | 37 | // PLL_FSE_REST_Template is required only in a REST context. 38 | add_action( 39 | 'rest_api_init', 40 | function () use ( $polylang ) { 41 | $polylang->rest_api->template = ( new PLL_FSE_REST_Template( $polylang->rest_api, PLL_FSE_Tools::get_template_post_types() ) )->init(); 42 | }, 43 | 20 // Load the FSE modules after the PLL_REST_API. 44 | ); 45 | 46 | unset( $pll_fse_sub_modules, $pll_fse_class ); 47 | }, 48 | 20 // Load the FSE modules after the PLL_REST_API. 49 | ); 50 | -------------------------------------------------------------------------------- /css/build/machine-translation-settings.min.css: -------------------------------------------------------------------------------- 1 | .pll-settings .pll-inner-notice{background:#fff;border:1px solid #c3c4c7;border-left-width:4px;box-shadow:0 1px 1px rgba(0,0,0,.04);margin:5px 0 15px;padding:1px 12px}.form-table .pll-inner-notice{margin-bottom:5px}.pll-settings .notice-success{border-left-color:#00a32a}.pll-settings .notice-warning{border-left-color:#dba617}.pll-settings .notice-error{border-left-color:#d63638}.pll-settings .pll-inner-notice p{margin:.5em 0;padding:2px}.pll-settings .notice-error .pll-origin-message,.pll-settings .notice-success .pll-origin-message,.pll-settings .notice-warning .pll-origin-message,.pll-settings .pll-message:not(.pll-origin-message){display:none}.pll-settings .notice-error .pll-error-message.pll-message-shown,.pll-settings .notice-success .pll-success-message,.pll-settings .notice-warning .pll-warning-message.pll-message-shown{display:block}.pll-progress-bar-wrapper .spinner,.pll-settings [disabled]+.spinner{visibility:visible}.pll-success-message .pll-icon{color:#125b91}.pll-error-message .pll-icon,.pll-warning-message .pll-icon{color:#911e1f}.pll-icon{font-size:1.8em;margin-left:1px;margin-right:3px;vertical-align:-2px}.pll-progress-bar-wrapper{border:1px solid #404648;border-radius:4px;color:#1772b5;font-size:2em;height:2em;line-height:2em;max-width:500px;overflow:hidden;position:relative;text-align:left;text-indent:1em;vertical-align:middle;white-space:nowrap}.pll-progress-bar-wrapper div{background-color:#1772b5;color:#e8e6e3;height:100%;left:0;overflow:hidden;position:absolute;top:0}.pll-settings [type=password]+.button{margin-left:20px;margin-right:0}.pll-settings [type=password]+.button+.spinner{float:none}.pll-progress-bar-wrapper .spinner{float:none;margin:0} -------------------------------------------------------------------------------- /modules/Machine_Translation/Settings/Settings_Interface.php: -------------------------------------------------------------------------------- 1 | $key, 'meta_compare' => 'EXISTS' ) ); 47 | 48 | if ( ! empty( $users ) ) { 49 | $user = reset( $users ); 50 | $data = get_user_meta( $user->ID, $key, true ); 51 | delete_user_meta( $user->ID, wp_slash( $key ) ); // No replay. 52 | $data['user_id'] = $user->ID; 53 | return $data; 54 | } 55 | 56 | $data = get_option( $key, array() ); 57 | 58 | if ( ! empty( $data ) ) { 59 | delete_option( $key ); // No replay. 60 | return $data; 61 | } 62 | 63 | wp_die( esc_html__( 'An error has occurred.', 'polylang-pro' ) ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /modules/import-export/export/strings-form-trait.php: -------------------------------------------------------------------------------- 1 | |WP_Error 22 | */ 23 | private function get_sanitized_languages( array $languages ) { 24 | if ( empty( $languages ) ) { 25 | return new WP_Error( 'pll_export_no_target_languages', __( 'Error: Please select a target language.', 'polylang-pro' ) ); 26 | } 27 | 28 | $languages = array_filter( $languages, 'is_string' ); 29 | $languages = array_map( 'sanitize_key', $languages ); 30 | $languages = array_map( array( $this->model, 'get_language' ), $languages ); 31 | $languages = array_filter( $languages ); 32 | 33 | if ( empty( $languages ) ) { 34 | return new WP_Error( 'invalid-target-languages', __( 'Error: invalid target languages.', 'polylang-pro' ) ); 35 | } 36 | 37 | return $languages; 38 | } 39 | 40 | /** 41 | * Registers settings errors to be displayed to the user. 42 | * 43 | * @since 3.6 44 | * @since 3.7 Moved from PLL_Export_Strings_Action. 45 | * 46 | * @param WP_Error $error An error object. 47 | * @return never 48 | */ 49 | private function display_errors( WP_Error $error ) { 50 | pll_add_notice( $error ); 51 | 52 | PLL_Settings::redirect(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /js/build/integrations/acf.min.js: -------------------------------------------------------------------------------- 1 | (()=>{var e={631:e=>{e.exports=function(){return this.wp.apiFetch}()}},t={};function o(n){var r=t[n];if(void 0!==r)return r.exports;var a=t[n]={exports:{}};return e[n](a,a.exports,o),a.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},o.d=(e,t)=>{for(var n in t)o.o(t,n)&&!o.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var n={};(()=>{"use strict";o.r(n);var e=o(631),t=o.n(e);document.addEventListener("onPostLangChoice",(e=>{const o=[],n=document.querySelectorAll(".acf-field-relationship");n.forEach((function(e){const t=e.getAttribute("data-key");o.push(t)}));document.querySelectorAll(".acf-field-post-object").forEach((function(e){const t=e.getAttribute("data-key");o.push(t)}));if(document.querySelectorAll(".acf-field-taxonomy").forEach((function(e){const t=e.getAttribute("data-key");o.push(t)})),0{e.forEach((function(e){const t=document.querySelector(".acf-"+e.field_key);t.outerHTML=e.field_data,acf.do_action("ready_field/type="+t.getAttribute("data-type"),t)})),0meta_type = 'post'; 19 | $this->import_export_meta_type = PLL_Import_Export::POST_META; 20 | } 21 | 22 | /** 23 | * Get the meta names to export. 24 | * 25 | * @since 3.3 26 | * 27 | * @param int $from ID of the source object. 28 | * @param int $to ID of the target object. 29 | * @return string[] List of custom fields names. 30 | */ 31 | protected function get_meta_names_to_export( int $from, int $to ): array { 32 | $default_metas_to_export = array( 33 | '_wp_attachment_image_alt' => 1, 34 | 'footnotes' => array( 35 | '*' => array( 36 | 'content' => 1, 37 | ), 38 | ), 39 | ); 40 | 41 | /** This filter is documented in modules/import-export/export/export-metas.php */ 42 | return (array) apply_filters( "pll_{$this->meta_type}_metas_to_export", $default_metas_to_export, $from, $to ); 43 | } 44 | 45 | /** 46 | * Returns the meta formats. 47 | * 48 | * @since 3.6 49 | * 50 | * @param int $from ID of the source object. 51 | * @param int $to ID of the target object. 52 | * @return array List of custom fields formats. 53 | */ 54 | protected function get_meta_encodings( int $from, int $to ): array { 55 | $formats = array( 56 | 'footnotes' => 'json', 57 | ); 58 | 59 | /** This filter is documented in modules/import-export/export/export-metas.php */ 60 | return (array) apply_filters( "pll_{$this->meta_type}_meta_encodings", $formats, $from, $to ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /modules/import-export/import/import-file-interface.php: -------------------------------------------------------------------------------- 1 | div { 3 | display: flex; 4 | flex-wrap: wrap; 5 | } 6 | .languages_page_mlang_strings .metabox-holder > div > div { 7 | display: flex; 8 | flex-direction: column; 9 | flex-basis: 0; 10 | flex-grow: 1; 11 | } 12 | .languages_page_mlang_strings .metabox-holder > div > div:not(:first-child) { 13 | margin-left: 1rem; 14 | } 15 | .languages_page_mlang_strings .metabox-holder > div > div.closed { 16 | border:0; 17 | background: none; 18 | } 19 | .languages_page_mlang_strings .metabox-holder > div > div.closed .postbox-header{ 20 | border: 1px solid #ccd0d4; 21 | background: #fff; 22 | } 23 | 24 | #pll-export-strings-box.postbox .submit, /* Override WordPress styles */ 25 | #pll-import-translations-box.postbox .submit, 26 | #pll-machine-strings-translations.postbox .submit { 27 | float: none; 28 | padding: 0; 29 | } 30 | .postbox .inside { 31 | margin: 0; 32 | } 33 | 34 | #pll-machine-strings-translations .pll-translation-flag, #export-string-translation .pll-translation-flag { 35 | margin: auto 10px auto 7px; 36 | } 37 | 38 | #export-string-translation label, 39 | #export-string-translation label[for="pll-select-format"] span, 40 | #import-translation label, 41 | #pll-machine-strings-translations label { 42 | display: block; 43 | margin: 0.35em 0 0.5em; 44 | } 45 | 46 | .pll-legend { 47 | display: block; 48 | padding: 2px 0; 49 | color: #1d2327; 50 | font-weight: 400; 51 | text-shadow: none; 52 | margin: 0.35em 0 0.5em; 53 | } 54 | 55 | /* Narrow devices */ 56 | @media screen and ( max-width: 1024px ) { 57 | .languages_page_mlang_strings .metabox-holder > div { 58 | flex-direction: column; 59 | } 60 | .languages_page_mlang_strings .metabox-holder > div > div:not(:first-child) { 61 | margin-left: 0; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /js/build/post.min.js: -------------------------------------------------------------------------------- 1 | jQuery((function(n){n.ajaxPrefilter((function(e,t,a){"string"==typeof e.data&&-1!==e.data.indexOf("action=ajax-tag-search")&&(lang=n(':input[name="inline_lang_choice"]').val())&&(e.data="lang="+lang+"&"+e.data)}))})),jQuery((function(n){const e=document.getElementById("the-list");if(!e)return;new MutationObserver((e=>{for(const i of e){const o=Array.from(i.addedNodes).filter((n=>n.nodeType===Node.ELEMENT_NODE))[0];if(00){const l=o.querySelector('select[name="inline_lang_choice"]'),r=document.querySelector("#lang_"+String(s)).innerHTML;l.value=r,t(r),a(r),l.addEventListener("change",(function(n){const e=n.target.value;t(e),a(e)}))}}function t(e){"undefined"!=typeof pll_term_languages&&n.each(pll_term_languages,(function(t,a){n.each(a,(function(a,i){n.each(i,(function(i){id="#"+a+"-"+pll_term_languages[t][a][i],e==t?n(id).show():n(id).hide()}))}))}))}function a(e){"undefined"!=typeof pll_page_languages&&n.each(pll_page_languages,(function(t,a){n.each(a,(function(a){v=n('#post_parent option[value="'+pll_page_languages[t][a]+'"]'),e==t?v.show():v.hide()}))}))}}})).observe(e,{childList:!0,subtree:!0})})),jQuery((function(n){n(document).ajaxSuccess((function(e,t,a){if("string"==typeof a.data){var i=wpAjax.unserialize(a.data);void 0!==i.action&&"inline-save"==i.action&&function(e){var t=new Array;n(".translation_"+e).each((function(){t.push(n(this).parent().parent().attr("id").substring(5))}));var a={action:"pll_update_post_rows",post_id:e,translations:t.join(","),post_type:n("input[name='post_type']").val(),screen:n("input[name='screen']").val(),_pll_nonce:n("input[name='_inline_edit']").val()};n.post(ajaxurl,a,(function(e){if(e){var t=wpAjax.parseAjaxResponse(e,"pll-ajax-response");n.each(t.responses,(function(){"row"==this.what&&n("#post-"+this.supplemental.post_id).replaceWith(this.data)}))}}))}(i.post_ID)}}))})); -------------------------------------------------------------------------------- /modules/import-export/import/import-object-interface.php: -------------------------------------------------------------------------------- 1 | parse( $widget_data['settings'][ $widget_data['number'] ]['content'] ); 34 | if ( is_array( $parser->output ) ) { 35 | foreach ( $parser->output as $output ) { 36 | if ( isset( $output['attrs'] ) ) { 37 | if ( array_key_exists( 'pll_lang', $output['attrs'] ) ) { 38 | $lang_to_be_displayed = $output['attrs']['pll_lang']; 39 | 40 | if ( ! empty( $this->curlang ) && $this->curlang->slug !== $lang_to_be_displayed ) { 41 | unset( $sidebars_widgets[ $sidebar ][ $key ] ); 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | return $sidebars_widgets; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /services/manage-user-capabilities.php: -------------------------------------------------------------------------------- 1 | model->has_languages() ) { 14 | // Ensure dependencies are loaded. 15 | require_once POLYLANG_PRO_DIR . '/modules/sync/load.php'; 16 | require_once POLYLANG_PRO_DIR . '/modules/sync-post/load.php'; 17 | require_once POLYLANG_DIR . '/modules/sync/load.php'; 18 | 19 | $machine_translation_factory = new Machine_Translation\Factory( $polylang->model ); 20 | 21 | if ( $machine_translation_factory->is_enabled() ) { 22 | $active_service = $machine_translation_factory->get_active_service(); 23 | 24 | if ( $active_service && $polylang instanceof PLL_Admin ) { 25 | new PLL_Admin_Loader( $polylang, 'machine_translation', array( $active_service ) ); 26 | $polylang->machine_translation_action = new Post_Action( $polylang, $active_service ); 27 | } elseif ( $active_service && $polylang instanceof PLL_REST_Request ) { 28 | $polylang->machine_translation = new Button_REST( $polylang, $active_service ); 29 | } elseif ( $active_service && $polylang instanceof PLL_Settings ) { 30 | $polylang->machine_translation = ( new Strings_Metabox( $polylang, $active_service ) )->init(); 31 | } 32 | } 33 | 34 | if ( $polylang instanceof PLL_Settings ) { 35 | add_filter( 36 | 'pll_settings_modules', 37 | function ( $modules ) { 38 | $k = array_search( PLL_Settings_Preview_Machine_Translation::class, $modules ); 39 | if ( $k ) { 40 | unset( $modules[ $k ] ); 41 | $modules['machine_translation'] = Machine_Translation\Module_Settings::class; 42 | } 43 | return $modules; 44 | }, 45 | 100 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /integrations/acf/Entity/Media.php: -------------------------------------------------------------------------------- 1 | model->get_language( $target_language ); 29 | if ( empty( $target_language ) ) { 30 | return; 31 | } 32 | 33 | $this->maybe_reset_fields_store( $target_language ); 34 | 35 | $this->apply_to_all_fields( new Copy(), $to_id, array( 'target_language' => $target_language ) ); 36 | } 37 | 38 | /** 39 | * Transforms a post ID to the corresponding ACF post ID. 40 | * 41 | * @since 3.7 42 | * 43 | * @param int $id Post ID. 44 | * @return string ACF post ID. 45 | */ 46 | protected static function acf_id( $id ): string { 47 | return 'attachment_' . $id; 48 | } 49 | 50 | /** 51 | * Does nothing for media. `self::translate_fields()` does the job instead. 52 | * 53 | * @since 3.7 54 | * 55 | * @param array $field Custom field definition. 56 | * @return array Custom field of the target object. 57 | */ 58 | public function render_field( $field ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable 59 | 60 | /* 61 | * Does nothing, media translations are created in two times. 62 | * 1. A request is made to create the media translation. 63 | * 2. A redirect is made toward the edit page of the media, where ACF loads the fields. 64 | */ 65 | return $field; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /include/Options/Business/Media.php: -------------------------------------------------------------------------------- 1 | false ); 40 | } 41 | 42 | /** 43 | * Returns the JSON schema part specific to this option. 44 | * 45 | * @since 3.7 46 | * 47 | * @return array Partial schema. 48 | * 49 | * @phpstan-return array{ 50 | * type: 'object', 51 | * properties: array{ 52 | * duplicate: array{ 53 | * type: 'boolean', 54 | * required: true 55 | * } 56 | * }, 57 | * additionalProperties: false 58 | * } 59 | */ 60 | protected function get_data_structure(): array { 61 | return array( 62 | 'type' => 'object', // Correspond to associative array in PHP, @see{https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#primitive-types}. 63 | 'properties' => array( 64 | 'duplicate' => array( 65 | 'type' => 'boolean', 66 | 'required' => true, 67 | ), 68 | ), 69 | 'additionalProperties' => false, 70 | ); 71 | } 72 | 73 | /** 74 | * Returns the description used in the JSON schema. 75 | * 76 | * @since 3.7 77 | * 78 | * @return string 79 | */ 80 | protected function get_description(): string { 81 | return __( 'Automatically duplicate media in all languages when uploading a new file.', 'polylang-pro' ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /modules/Editors/Screens/Post.php: -------------------------------------------------------------------------------- 1 | posts = &$polylang->posts; 33 | } 34 | 35 | 36 | /** 37 | * Tells whether the given screen is the Post edtitor or not. 38 | * 39 | * @since 3.7 40 | * 41 | * @param WP_Screen $screen The current screen. 42 | * @return bool True if Post editor screen, false otherwise. 43 | */ 44 | protected function screen_matches( WP_Screen $screen ): bool { 45 | return ( 46 | 'post' === $screen->base 47 | && $this->model->post_types->is_translated( $screen->post_type ) 48 | && method_exists( $screen, 'is_block_editor' ) 49 | && $screen->is_block_editor() 50 | ); 51 | } 52 | 53 | /** 54 | * Returns the language to use in the Post editor. 55 | * 56 | * @since 3.7 57 | * 58 | * @return PLL_Language|null 59 | */ 60 | protected function get_language(): ?PLL_Language { 61 | global $post; 62 | 63 | if ( ! empty( $post ) && ! empty( $this->posts ) && $this->model->post_types->is_translated( $post->post_type ) ) { 64 | $this->posts->set_default_language( $post->ID ); 65 | $post_lang = $this->model->post->get_language( $post->ID ); 66 | return ! empty( $post_lang ) ? $post_lang : null; 67 | } 68 | 69 | return null; 70 | } 71 | 72 | /** 73 | * Returns the screen name for the Post editor to use across all process. 74 | * 75 | * @since 3.7 76 | * 77 | * @return string 78 | */ 79 | protected function get_screen_name(): string { 80 | return 'post'; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /modules/frontend-filter-template/filter-template-page.php: -------------------------------------------------------------------------------- 1 | model->post->get( $object->ID, $this->default_language ); 27 | $def_lang_object = get_post( $def_lang_id ); 28 | if ( ! $def_lang_object instanceof WP_Post ) { 29 | return $templates; 30 | } 31 | return $this->prepend_templates( $templates, $def_lang_object ); 32 | } 33 | 34 | /** 35 | * Returns the language of the given post. 36 | * 37 | * @since 3.7 38 | * 39 | * @param object $object The post object. 40 | * @return PLL_Language|false Post language, `false` if none found. 41 | */ 42 | protected function get_object_language( object $object ) { 43 | if ( ! $object instanceof WP_Post ) { 44 | return false; 45 | } 46 | 47 | return $this->model->post->get_language( $object->ID ); 48 | } 49 | 50 | /** 51 | * Prepends templates for the given post to the given array. 52 | * 53 | * @since 3.7 54 | * 55 | * @param array $templates Array of templates. 56 | * @param WP_Post $post Post object to use. 57 | * @return array Array of templates with prepended ones. 58 | */ 59 | protected function prepend_templates( array $templates, WP_Post $post ): array { 60 | array_unshift( $templates, "page-{$post->ID}.php" ); 61 | array_unshift( $templates, "page-{$post->post_name}.php" ); 62 | 63 | return $templates; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /modules/full-site-editing/fse-post-types.php: -------------------------------------------------------------------------------- 1 | model->term->get( $object->term_id, $this->default_language ); 27 | $def_lang_object = get_term( $def_lang_id ); 28 | if ( ! $def_lang_object instanceof WP_Term ) { 29 | return $templates; 30 | } 31 | 32 | return $this->prepend_templates( $templates, $def_lang_object ); 33 | } 34 | 35 | /** 36 | * Returns the language of the given term. 37 | * 38 | * @since 3.7 39 | * 40 | * @param object $object The term object. 41 | * @return PLL_Language|false Term language, `false` if none found. 42 | */ 43 | protected function get_object_language( object $object ) { 44 | if ( ! $object instanceof WP_Term ) { 45 | return false; 46 | } 47 | 48 | return $this->model->term->get_language( $object->term_id ); 49 | } 50 | 51 | /** 52 | * Prepends templates for the given term to the given array. 53 | * 54 | * @since 3.7 55 | * 56 | * @param array $templates Array of templates. 57 | * @param WP_Term $term Term object to use. 58 | * @return array Array of templates with prepended ones. 59 | */ 60 | protected function prepend_templates( array $templates, WP_Term $term ): array { 61 | $type = 'post_tag' === $term->taxonomy ? 'tag' : $term->taxonomy; 62 | array_unshift( $templates, "{$type}-{$term->term_id}.php" ); 63 | array_unshift( $templates, "{$type}-{$term->slug}.php" ); 64 | 65 | return $templates; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /services/exporter/export-data-from-strings.php: -------------------------------------------------------------------------------- 1 | model = $model; 28 | } 29 | 30 | /** 31 | * Prepares and exports the selected strings translations. 32 | * 33 | * @since 3.6 34 | * 35 | * @param PLL_Export_Container $container Export container. 36 | * @param array $sources Currated list of strings to export. 37 | * @param PLL_Language $target_language The target language. 38 | * @param bool $no_update Whether to remove already translated strings. Default to false. 39 | * @return WP_Error A `WP_Error` object. Note: an "empty" `WP_Error` object is returned on success. 40 | */ 41 | public function send_to_export( PLL_Export_Container $container, array $sources, PLL_Language $target_language, bool $no_update = false ): WP_Error { 42 | $source_language = $this->model->get_default_language(); 43 | 44 | if ( empty( $source_language ) ) { 45 | return new WP_Error( 'pll_export_no_source_language', __( 'Error: Default language not defined.', 'polylang-pro' ) ); 46 | } 47 | 48 | if ( $no_update ) { 49 | $mo = new PLL_MO(); 50 | $mo->import_from_db( $target_language ); 51 | $sources = array_filter( 52 | $sources, 53 | function ( $source ) use ( $mo ) { 54 | return empty( $mo->translate_if_any( $source['string'] ) ); 55 | } 56 | ); 57 | } 58 | 59 | if ( empty( $sources ) ) { 60 | return new WP_Error( 'pll_export_no_strings', __( 'Error: No strings found.', 'polylang-pro' ) ); 61 | } 62 | 63 | ( new PLL_Export_Strings( $this->model ) )->add_items( $container, $sources, $target_language ); 64 | 65 | return new WP_Error(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /modules/media/settings-advanced-media.php: -------------------------------------------------------------------------------- 1 | 'advanced_media', 32 | 'title' => __( 'Media', 'polylang-pro' ), 33 | 'description' => __( 'Activate languages and translations for media. Provides options for multilingual media management.', 'polylang-pro' ), 34 | 'active_option' => 'media_support', 35 | ) 36 | ); 37 | } 38 | 39 | /** 40 | * Displays the settings form 41 | * 42 | * @since 1.9 43 | * 44 | * @return void 45 | */ 46 | protected function form() { 47 | printf( 48 | '', 49 | checked( empty( $this->options['media']['duplicate'] ), false, false ), 50 | esc_html__( 'Automatically duplicate media in all languages when uploading a new file.', 'polylang-pro' ) 51 | ); 52 | } 53 | 54 | /** 55 | * Prepare the received data before saving. 56 | * 57 | * @since 3.7 58 | * 59 | * @param array $options Raw values to save. 60 | * @return array 61 | */ 62 | protected function prepare_raw_data( array $options ): array { 63 | $newoptions = array( 'media' => array( 'duplicate' => ! empty( $options['media']['duplicate'] ) ? 1 : 0 ) ); 64 | return $newoptions; // Take care to return only validated options. 65 | } 66 | 67 | /** 68 | * Get the row actions 69 | * 70 | * @since 1.9 71 | * 72 | * @return string[] 73 | */ 74 | protected function get_actions() { 75 | return empty( $this->options['media_support'] ) ? array( 'activate' ) : array( 'configure', 'deactivate' ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /modules/import-export/xliff/xliff-format.php: -------------------------------------------------------------------------------- 1 | 'text/xml' ); 23 | 24 | /** 25 | * PLL_Xliff_Format constructor. 26 | * 27 | * @since 3.1 28 | */ 29 | public function __construct() { 30 | // MIME type does not use the same string for PHP versions < 7.2. 31 | if ( version_compare( phpversion(), '7.2', '<' ) ) { 32 | $this->mime_type = array( 'xlf|xliff' => 'application/xml' ); 33 | } 34 | } 35 | 36 | /** 37 | * Whether the xliff format is supported or not by the current environment. 38 | * 39 | * @since 3.1 40 | * 41 | * @return true|WP_Error 42 | */ 43 | public function is_supported() { 44 | if ( ! extension_loaded( 'libxml' ) ) { 45 | return new WP_Error( 'pll_libxml_missing', __( 'Your PHP installation appears to be missing the libxml extension which is required by the importer.', 'polylang-pro' ) ); 46 | } 47 | 48 | return true; 49 | } 50 | 51 | /** 52 | * Returns the associated import class. 53 | * 54 | * @since 3.1 55 | * 56 | * @return PLL_Xliff_Import 57 | */ 58 | public function get_import() { 59 | return new PLL_Xliff_Import(); 60 | } 61 | 62 | /** 63 | * Returns the associated export class. 64 | * 65 | * @since 3.6 66 | * 67 | * @param string $version Optional file format version. 68 | * @return string 69 | * 70 | * @phpstan-return class-string|class-string|class-string 71 | */ 72 | public function get_export_class( $version = '' ): string { 73 | switch ( $version ) { 74 | case '20': 75 | $class_name = PLL_Xliff_Export_20::class; 76 | break; 77 | case '21': 78 | $class_name = PLL_Xliff_Export_21::class; 79 | break; 80 | default: // 1.2 81 | $class_name = PLL_Xliff_Export_12::class; 82 | break; 83 | } 84 | return $class_name; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /integrations/admin-columns/cpac.php: -------------------------------------------------------------------------------- 1 | model->get_translated_post_types() as $type ) { 23 | if ( isset( $_REQUEST['list_screen'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification 24 | $filter = 'manage_' . ( 'attachment' === $type ? 'upload' : 'edit-' . $type ) . '_columns'; 25 | add_filter( $filter, array( $this, 'remove_filter_lang' ), 90 ); // Before Polylang. 26 | } 27 | 28 | $filter = 'option_cpac_options_' . ( 'attachment' === $type ? 'wp-media' : $type ) . '__default'; 29 | add_filter( $filter, array( $this, 'filter_default_columns' ) ); 30 | } 31 | } 32 | 33 | /** 34 | * Deactivates the admin language filter on Admin Columns settings page. 35 | * 36 | * @since 2.4 37 | * 38 | * @param array $columns List of table columns. 39 | * @return array 40 | */ 41 | public function remove_filter_lang( $columns ) { 42 | PLL()->filters_columns->filter_lang = ''; 43 | return $columns; 44 | } 45 | 46 | /** 47 | * Fixes the Polylang columns in default columns. 48 | * 49 | * @since 2.4 50 | * 51 | * @param array $columns List of table columns. 52 | * @return array 53 | */ 54 | public function filter_default_columns( $columns ) { 55 | $screen = get_current_screen(); 56 | 57 | if ( isset( $screen->base ) ) { 58 | $is_post_type = 'edit' === $screen->base && has_filter( 'manage_edit-' . $screen->post_type . '_columns', array( PLL()->filters_columns, 'add_post_column' ) ); 59 | $is_media = 'upload' === $screen->base && has_filter( 'manage_upload_columns', array( PLL()->filters_columns, 'add_post_column' ) ); 60 | 61 | if ( $is_post_type || $is_media ) { 62 | foreach ( pll_languages_list() as $lang ) { 63 | unset( $columns[ 'language_' . $lang ] ); 64 | } 65 | 66 | $columns = PLL()->filters_columns->add_post_column( $columns ); 67 | } 68 | } 69 | 70 | return $columns; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /modules/duplicate/duplicate-rest.php: -------------------------------------------------------------------------------- 1 | links = new PLL_Admin_Links( $polylang ); 37 | $this->user_meta = new PLL_Toggle_User_Meta( PLL_Duplicate_Action::META_NAME ); 38 | 39 | register_rest_field( 40 | 'user', 41 | $this->user_meta->get_meta_name(), 42 | array( 43 | 'get_callback' => array( $this->user_meta, 'get' ), 44 | 'update_callback' => array( $this->user_meta, 'update' ), 45 | ) 46 | ); 47 | 48 | add_filter( 'block_editor_settings_all', array( $this, 'remove_template' ), 10, 2 ); 49 | } 50 | 51 | /** 52 | * Avoids that the post template overwrites our duplicated content. 53 | * 54 | * @since 3.2 55 | * 56 | * @param array $editor_settings Default editor settings. 57 | * @param WP_Block_Editor_Context $block_editor_context The current block editor context. 58 | * @return array 59 | */ 60 | public function remove_template( $editor_settings, $block_editor_context ) { 61 | if ( empty( $block_editor_context->post ) || ! $block_editor_context->post instanceof WP_Post || empty( $block_editor_context->post->post_content ) ) { 62 | return $editor_settings; 63 | } 64 | 65 | $data = $this->links->get_data_from_new_post_translation_request( $block_editor_context->post->post_type ); 66 | 67 | if ( empty( $data ) ) { 68 | return $editor_settings; 69 | } 70 | 71 | unset( $editor_settings['template'], $editor_settings['templateLock'] ); 72 | 73 | return $editor_settings; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /integrations/acf/README.md: -------------------------------------------------------------------------------- 1 | # ACF Integration 2 | 3 | Integration with Advanced Custom Fields Pro, bootstraped in `acf/load.php` with `Main` main object. 4 | All names take place in `WP_Syntex\Polylang_Pro\Integrations\ACF` namespace. 5 | 6 | ## How field translation works 7 | 8 | ### For standard custom fields 9 | 10 | `Dispatcher` applies strategies to custom fields of `Entity\Abstract_Object` objects (posts or terms) as well as ACF blocks. 11 | These strategies follow the Strategy Pattern to make fields benefit from Polylang Pro core features such as: 12 | 13 | | Feature | Strategy | 14 | | --- | --- | 15 | | New post translation creation | `Strategy\Copy` | 16 | | Synchronization on post/term update | `Strategy\Synchronize` | 17 | | Bulk translate | `Strategy\Copy` or `Strategy\Copy_All` | 18 | | Export, import and machine translation | `Strategy\Export`, `Strategy\Import` and `Strategy\Abstract_Collect_Ids` | 19 | 20 | ### For block fields 21 | 22 | Import, export, machine translation and smart synchronization for fields containing IDs are supported thanks to `Entity\Blocks` class. This class is able to apply any strategy to a list of blocks thanks to `Entity\Blocks::apply_on_blocks()`. 23 | 24 | ### Note on language location 25 | 26 | To provide end users a way to have field groups displayed only for specific language, a custom location is provided with `Location\Language`. 27 | 28 | ## Translated labels 29 | 30 | Now that field groups are not translatable anymore, the only way to have them displayed in several languages is using strings translations. 31 | This new feature lays in `Labels\Field_Groups`. 32 | Note that the integration offers the same feature for ACF post types and taxonomies (`Labels\Abstract_Object_Type`). 33 | 34 | ## Editor 35 | 36 | Integration with editors UI can be found in `Ajax_Lang_Choice` and `acf/js/acf.js`. 37 | 38 | ## Migration from Polylang Pro prior to 3.7 39 | 40 | Field groups are not translated at all now. For sites having translated field groups with Polylang Pro < 3.7, a migration will occur on update. 41 | This migration will update each field groups with a location corresponding to the formerly assigned language. 42 | The related code can be found in `WP_Syntex\Polylang_Pro\Upgrade::upgrade_3_7()`. 43 | -------------------------------------------------------------------------------- /modules/Machine_Translation/Posts/Button.php: -------------------------------------------------------------------------------- 1 | service = $service; 39 | $this->user_meta = new PLL_Toggle_User_Meta( sprintf( 'pll_machine_translation_%s', $this->service->get_slug() ) ); 40 | 41 | $args = array( 42 | 'position' => 'before_post_translations', 43 | 'activate' => sprintf( 44 | /* translators: %s is the name of the machine translation service. */ 45 | __( 'Activate %s machine translation', 'polylang-pro' ), 46 | $this->service->get_name() 47 | ), 48 | 'deactivate' => sprintf( 49 | /* translators: %s is the name of the machine translation service. */ 50 | __( 'Deactivate %s machine translation', 'polylang-pro' ), 51 | $this->service->get_name() 52 | ), 53 | 'icon' => $this->service->get_icon(), 54 | 'priority' => 20, 55 | ); 56 | 57 | parent::__construct( 'pll-machine-translation', $args ); 58 | 59 | add_action( 'admin_notices', array( $this, 'display_errors' ) ); 60 | } 61 | 62 | /** 63 | * Prints translation errors into the page. 64 | * 65 | * @since 3.6 66 | * 67 | * @return void 68 | */ 69 | public function display_errors() { 70 | settings_errors( 'polylang' ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /modules/full-site-editing/fse-post-deletion.php: -------------------------------------------------------------------------------- 1 | delete_post()` (prio 10), so the data about the translations still exist. 36 | return $this; 37 | } 38 | 39 | /** 40 | * When a template in the default language is deleted, also delete its translations. 41 | * 42 | * @since 3.2 43 | * 44 | * @param int $post_id Post ID. 45 | * @param WP_Post $post Post object. 46 | * @return void 47 | */ 48 | public function delete_translation_posts( $post_id, $post ) { 49 | if ( ! $post instanceof WP_Post || ! in_array( $post->post_type, PLL_FSE_Tools::get_translatable_post_types(), true ) ) { 50 | // Not a translated template post type. 51 | return; 52 | } 53 | 54 | $def_lang = $this->model->get_default_language(); 55 | $post_lang = $this->model->post->get_language( $post->ID ); 56 | 57 | if ( empty( $def_lang ) || empty( $post_lang ) || $def_lang->slug !== $post_lang->slug ) { 58 | // This one is not in the default language. 59 | return; 60 | } 61 | 62 | $translations = $this->model->post->get_translations( $post->ID ); 63 | $translations = array_diff( $translations, array( $post->ID ) ); // Let's not create an infinite loop. 64 | 65 | if ( empty( $translations ) ) { 66 | // Nothing to delete. 67 | return; 68 | } 69 | 70 | foreach ( $translations as $translation_id ) { 71 | // Send it to Sovngarde. 72 | wp_delete_post( $translation_id, true ); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /modules/full-site-editing/fse-default-language-change.php: -------------------------------------------------------------------------------- 1 | model->get_language( $new_def_lang_slug ); 53 | if ( empty( $new_def_lang ) ) { 54 | // Uh? 55 | return; 56 | } 57 | 58 | $old_def_lang = $this->model->get_language( $old_def_lang_slug ); 59 | if ( empty( $old_def_lang ) ) { 60 | // Uh? 61 | return; 62 | } 63 | 64 | // Add a language suffix to the slugs belonging to templates in the current default language. 65 | $this->update_language_suffix_in_post_names( $old_def_lang ); 66 | 67 | // Remove the language suffix from the slugs belonging to templates in the new default language. 68 | $this->remove_language_suffix_from_post_names( $new_def_lang ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /services/translation/translation-content.php: -------------------------------------------------------------------------------- 1 | translations = $translations; 36 | } 37 | 38 | /** 39 | * Translates the original's post title. 40 | * 41 | * @since 3.3 42 | * 43 | * @param string $from_post The post_content field of the original WP_Post. 44 | * @return string 45 | */ 46 | public function translate_title( $from_post ) { 47 | return $this->translations->translate( 48 | $from_post, 49 | Context::to_string( 50 | array( 51 | Context::FIELD => PLL_Import_Export::POST_TITLE, 52 | ) 53 | ) 54 | ); 55 | } 56 | 57 | /** 58 | * Uses a {@see PLL_Translation_Walker_Interface} subclass to iterate over each translatable part of the passed content, and applies a transformation callback to it. Then returns the transformed content. 59 | * 60 | * @since 3.3 61 | * 62 | * @param string $content The post_content field of the original WP_Post. 63 | * @return string 64 | */ 65 | public function translate_content( $content ) { 66 | $walker = PLL_Translation_Walker_Factory::create_from( $content ); 67 | 68 | return $walker->walk( array( $this->translations, 'translate_entry' ) ); 69 | } 70 | 71 | /** 72 | * Translates the original post's excerpt. 73 | * 74 | * @since 3.3 75 | * 76 | * @param string $post_excerpt The post_excerpt field of the original WP_Post. 77 | * @return string 78 | */ 79 | public function translate_excerpt( $post_excerpt ) { 80 | return $this->translations->translate( 81 | $post_excerpt, 82 | Context::to_string( 83 | array( 84 | Context::FIELD => PLL_Import_Export::POST_EXCERPT, 85 | ) 86 | ) 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /modules/Machine_Translation/Strings/Metabox.php: -------------------------------------------------------------------------------- 1 | model = $polylang->model; 39 | $this->action = new Action( $polylang, $service ); 40 | } 41 | 42 | /** 43 | * Initializes the machine translations metabox. 44 | * 45 | * @since 3.7 46 | * 47 | * @return self Instance of the current class. 48 | */ 49 | public function init(): self { 50 | add_action( 'load-languages_page_mlang_strings', array( $this, 'add' ) ); 51 | 52 | /* 53 | * See the hook `mlang_action_{$action}` in `PLL_Settings::handle_actions()`. 54 | */ 55 | add_action( 56 | 'mlang_action_machine-translations', 57 | array( $this->action, 'validate_form' ) 58 | ); 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * Adds the machine translation metabox. 65 | * 66 | * @since 3.7 67 | * 68 | * @return void 69 | */ 70 | public function add() { 71 | add_meta_box( 72 | 'pll-machine-strings-translations', 73 | __( 'Machine translations', 'polylang-pro' ), 74 | array( $this, 'render' ), 75 | 'languages_page_mlang_strings', 76 | 'normal' 77 | ); 78 | } 79 | 80 | /** 81 | * Renders the machine translation metabox. 82 | * 83 | * @since 3.7 84 | * 85 | * @return void 86 | */ 87 | public function render() { 88 | $model = $this->model; 89 | include POLYLANG_PRO_DIR . '/modules/Machine_Translation/Views/string-form.php'; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /modules/Machine_Translation/Views/string-form.php: -------------------------------------------------------------------------------- 1 | get_languages_list(); 15 | $strings = PLL_Admin_Strings::get_strings(); 16 | $groups = array_unique( wp_list_pluck( $strings, 'context' ) ); 17 | ?> 18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | is_default ) { 28 | printf( 29 | '', 30 | esc_attr( $language->slug ), 31 | esc_html( $language->name ), 32 | $language->flag // phpcs:ignore WordPress.Security.EscapeOutput 33 | ); 34 | } 35 | } 36 | ?> 37 |
38 |
39 |
40 | 41 | 55 |
56 |

57 | 58 |

59 |
60 |
61 | -------------------------------------------------------------------------------- /modules/sync-post/sync-post-bulk-option.php: -------------------------------------------------------------------------------- 1 | do_synchronize = $args['do_synchronize']; 42 | $this->sync_model = $sync_model; 43 | } 44 | 45 | /** 46 | * Checks whether the option should be selectable by the user. 47 | * 48 | * @since 2.7 49 | * 50 | * @return bool 51 | */ 52 | public function is_available() { 53 | $screen = get_current_screen(); 54 | 55 | if ( $screen && 'edit' === $screen->base ) { 56 | $post_type = get_post_type_object( $screen->post_type ); 57 | return $post_type && current_user_can( $post_type->cap->edit_posts ); 58 | } 59 | 60 | return false; 61 | } 62 | 63 | 64 | /** 65 | * Duplicates or Synchronize the given post, depending on the value of {@see PLL_Sync_Post_Bulk_Option::$do_synchronize} 66 | * 67 | * @since 2.7 68 | * 69 | * @param int $object_id Identifies a post to duplicate or synchronize. 70 | * @param string $lang A language slug to translate into. 71 | */ 72 | public function translate( $object_id, $lang ) { 73 | if ( false === $this->do_synchronize ) { 74 | $this->sync_model->save_group( $object_id, array() ); 75 | $strategy = PLL_Sync_Post_Model::COPY; 76 | } else { 77 | $strategy = PLL_Sync_Post_Model::SYNC; 78 | } 79 | 80 | $this->sync_model->copy( $object_id, $lang, $strategy, $this->do_synchronize ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /modules/bulk-translate/view-bulk-translate.php: -------------------------------------------------------------------------------- 1 | 16 |
17 | 18 | 59 | 60 |
19 |
20 | 21 |
22 |
23 |
24 | 25 | get_languages_list() as $language ) { 27 | printf( 28 | '', 29 | esc_attr( $language->slug ), 30 | esc_html( $language->name ), 31 | $language->flag // phpcs:ignore WordPress.Security.EscapeOutput 32 | ); 33 | } 34 | ?> 35 |
36 |
37 |
38 |
39 | 40 | display( $selected->get_name() ); 46 | } 47 | } 48 | ?> 49 |
50 |
51 |
52 |

53 | 54 | 55 | 56 |

57 |
58 |
61 | -------------------------------------------------------------------------------- /modules/blocks/language-switcher/language-switcher-block.php: -------------------------------------------------------------------------------- 1 | set_attributes_for_block( $attributes ); 45 | 46 | $attributes['raw'] = false; 47 | $switcher = new PLL_Switcher(); 48 | $switcher_output = $switcher->the_languages( $this->links, $attributes ); 49 | 50 | if ( empty( $switcher_output ) ) { 51 | return ''; 52 | } 53 | 54 | $aria_label = __( 'Choose a language', 'polylang-pro' ); 55 | if ( $attributes['dropdown'] ) { 56 | $switcher_output = '' . $switcher_output; 57 | 58 | $wrap_tag = '
%2$s
'; 59 | } else { 60 | $wrap_tag = ''; 61 | } 62 | 63 | $wrap_attributes = get_block_wrapper_attributes(); 64 | 65 | return sprintf( $wrap_tag, $wrap_attributes, $switcher_output ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /js/build/index.js: -------------------------------------------------------------------------------- 1 | /******/ "use strict"; 2 | /******/ // The require scope 3 | /******/ var __webpack_require__ = {}; 4 | /******/ 5 | /************************************************************************/ 6 | /******/ /* webpack/runtime/define property getters */ 7 | /******/ (() => { 8 | /******/ // define getter functions for harmony exports 9 | /******/ __webpack_require__.d = (exports, definition) => { 10 | /******/ for(var key in definition) { 11 | /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 12 | /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 13 | /******/ } 14 | /******/ } 15 | /******/ }; 16 | /******/ })(); 17 | /******/ 18 | /******/ /* webpack/runtime/hasOwnProperty shorthand */ 19 | /******/ (() => { 20 | /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 21 | /******/ })(); 22 | /******/ 23 | /************************************************************************/ 24 | var __webpack_exports__ = {}; 25 | /* unused harmony export ajaxFilter */ 26 | /** 27 | * @package Polylang 28 | */ 29 | 30 | /** 31 | * Adds data to all ajax requests made with jQuery. 32 | * 33 | * @since 3.7 34 | * 35 | * @param {Object} data The data to add. 36 | * @returns {void} 37 | */ 38 | function ajaxFilter( data ) { 39 | if ( 'undefined' === typeof jQuery || ! data ) { 40 | return; 41 | } 42 | 43 | const dataStr = jQuery.param( data ); 44 | 45 | jQuery.ajaxPrefilter( function ( options ) { 46 | if ( -1 === options.url.indexOf( ajaxurl ) && -1 === ajaxurl.indexOf( options.url ) ) { 47 | return; 48 | } 49 | 50 | if ( 51 | 'undefined' === typeof options.data || 52 | null === options.data || 53 | 'string' === typeof options.data && '' === options.data.trim() 54 | ) { 55 | // An empty string or null/undefined. 56 | options.data = dataStr; 57 | } else if ( 'string' === typeof options.data ) { 58 | // A non-empty string: can be a JSON string or a query string. 59 | try { 60 | options.data = JSON.stringify( Object.assign( JSON.parse( options.data ), data ) ); 61 | } catch ( exception ) { 62 | // A non-empty non-JSON string is considered a query string. 63 | options.data = `${ options.data }&${ dataStr }`; 64 | } 65 | } else if ( jQuery.isPlainObject( options.data ) ) { 66 | // An object. 67 | options.data = Object.assign( options.data, data ); 68 | } 69 | } ); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /modules/wizard/wizard-pro.php: -------------------------------------------------------------------------------- 1 | model = &$polylang->model; 32 | $this->sync_model = &$polylang->sync_post_model; 33 | 34 | // See pll_wizard_create_home_page_translations filter in PLL_Wizard class. 35 | add_filter( 'pll_wizard_create_home_page_translations', array( $this, 'replace_create_home_page_translations' ) ); 36 | } 37 | 38 | /** 39 | * Replace function to apply to process the home page translations creation. 40 | * 41 | * @since 2.7 42 | * 43 | * @return callable 44 | */ 45 | public function replace_create_home_page_translations() { 46 | return array( $this, 'create_home_page_translations' ); 47 | } 48 | 49 | /** 50 | * Create home page translations for each language defined with duplicating content. 51 | * 52 | * @since 2.7 53 | * 54 | * @param string $default_language slug of the default language; null if no default language is defined. 55 | * @param int $home_page post_id of the home page if it's defined, false otherwise. 56 | * @param string $home_page_title home page title if it's defined, 'Homepage' otherwise. 57 | * @param string $home_page_language slug of the home page if it's defined, false otherwise. 58 | * @param array $untranslated_languages array of languages which needs to have a home page translated. 59 | * @return void 60 | */ 61 | public function create_home_page_translations( $default_language, $home_page, $home_page_title, $home_page_language, $untranslated_languages ) { 62 | global $wpdb; 63 | 64 | foreach ( $untranslated_languages as $language ) { 65 | $translated_post_id = $this->sync_model->copy( $home_page, $language ); 66 | $language_properties = $this->model->get_language( $language ); 67 | $wpdb->update( 68 | $wpdb->posts, 69 | array( 'post_title' => $home_page_title . ' - ' . $language_properties->name ), 70 | array( 'ID' => $translated_post_id ) 71 | ); // Don't use wp_update_post to ensure not redo save post process. 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /modules/frontend-filter-template/abstract-filter-template.php: -------------------------------------------------------------------------------- 1 | model = $model; 35 | /** @phpstan-var PLL_Language $default_language */ 36 | $default_language = $this->model->get_default_language(); 37 | $this->default_language = $default_language; 38 | } 39 | 40 | /** 41 | * Filters templates according to the current queried object. 42 | * 43 | * @since 3.7 44 | * 45 | * @param array $templates Array of templates guessed from the hierarchy. 46 | * @return array Filtered array of templates. 47 | */ 48 | public function filter( $templates ) { 49 | if ( ! is_array( $templates ) ) { 50 | // Something bad happened. 51 | return $templates; 52 | } 53 | 54 | $object = get_queried_object(); 55 | if ( ! is_object( $object ) ) { 56 | return $templates; 57 | } 58 | 59 | $language = $this->get_object_language( $object ); 60 | if ( ! $language instanceof PLL_Language ) { 61 | return $templates; 62 | } 63 | 64 | if ( $language->slug === $this->default_language->slug ) { 65 | return $templates; 66 | } 67 | 68 | return $this->add_def_lang_templates_from_object( $templates, $object ); 69 | } 70 | 71 | /** 72 | * Adds templates of the default language from an object. 73 | * 74 | * @since 3.7 75 | * 76 | * @param array $templates Array of templates guessed from the hierarchy. 77 | * @param object $object Object to use to guess templates. 78 | * @return array Array of templates with added ones. 79 | */ 80 | abstract protected function add_def_lang_templates_from_object( array $templates, object $object ): array; 81 | 82 | /** 83 | * Returns the language of the given object. 84 | * 85 | * @since 3.7 86 | * 87 | * @param object $object The object (e.g. `WP_Post` or `WP_Term`). 88 | * @return PLL_Language|false Object language, `false` if none found. 89 | */ 90 | abstract protected function get_object_language( object $object ); 91 | } 92 | -------------------------------------------------------------------------------- /css/build/machine-translation-settings.css: -------------------------------------------------------------------------------- 1 | .pll-settings .pll-inner-notice { 2 | margin: 5px 0 15px; 3 | border: 1px solid #c3c4c7; 4 | border-left-width: 4px; 5 | padding: 1px 12px; 6 | background: #fff; 7 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 8 | } 9 | 10 | .form-table .pll-inner-notice { 11 | margin-bottom: 5px; 12 | } 13 | 14 | .pll-settings .notice-success { 15 | border-left-color: #00a32a; 16 | } 17 | 18 | .pll-settings .notice-warning { 19 | border-left-color: #dba617; 20 | } 21 | 22 | .pll-settings .notice-error { 23 | border-left-color: #d63638; 24 | } 25 | 26 | .pll-settings .pll-inner-notice p { 27 | margin: 0.5em 0; 28 | padding: 2px; 29 | } 30 | 31 | .pll-settings .pll-message:not(.pll-origin-message), 32 | .pll-settings .notice-success .pll-origin-message, 33 | .pll-settings .notice-warning .pll-origin-message, 34 | .pll-settings .notice-error .pll-origin-message { 35 | display: none; 36 | } 37 | 38 | .pll-settings .notice-success .pll-success-message, 39 | .pll-settings .notice-warning .pll-warning-message.pll-message-shown, 40 | .pll-settings .notice-error .pll-error-message.pll-message-shown { 41 | display: block; 42 | } 43 | 44 | .pll-settings [disabled] + .spinner, 45 | .pll-progress-bar-wrapper .spinner { 46 | visibility: visible; 47 | } 48 | 49 | .pll-success-message .pll-icon { 50 | color: rgb(18, 91, 145); 51 | } 52 | 53 | .pll-error-message .pll-icon, 54 | .pll-warning-message .pll-icon { 55 | color: rgb(145, 30, 31); 56 | } 57 | 58 | .pll-icon { 59 | font-size: 1.8em; 60 | margin-right: 3px; 61 | margin-left: 1px; 62 | vertical-align: -2px; 63 | } 64 | 65 | .pll-progress-bar-wrapper { 66 | position: relative; 67 | max-width: 500px; 68 | height: 2em; 69 | line-height: 2em; 70 | vertical-align: middle; 71 | text-align: left; 72 | font-size: 2em; 73 | border: 1px solid rgb(64, 70, 72); 74 | border-radius: 4px; 75 | overflow: hidden; 76 | white-space: nowrap; 77 | color: rgb(23, 114, 181); 78 | text-indent: 1em; 79 | } 80 | 81 | .pll-progress-bar-wrapper div { 82 | position: absolute; 83 | height: 100%; 84 | top: 0; 85 | left: 0; 86 | overflow: hidden; 87 | background-color: rgb(23, 114, 181); 88 | color: rgb(232, 230, 227); 89 | } 90 | 91 | .pll-settings [type="password"] + .button { 92 | margin-left: 20px; 93 | margin-right: 0; 94 | } 95 | 96 | .pll-settings [type="password"] + .button + .spinner { 97 | float: none; 98 | } 99 | 100 | .pll-progress-bar-wrapper .spinner { 101 | float: none; 102 | margin: 0; 103 | } 104 | -------------------------------------------------------------------------------- /js/build/metabox-autocomplete.js: -------------------------------------------------------------------------------- 1 | /******/ "use strict"; 2 | /******/ // The require scope 3 | /******/ var __webpack_require__ = {}; 4 | /******/ 5 | /************************************************************************/ 6 | /******/ /* webpack/runtime/define property getters */ 7 | /******/ (() => { 8 | /******/ // define getter functions for harmony exports 9 | /******/ __webpack_require__.d = (exports, definition) => { 10 | /******/ for(var key in definition) { 11 | /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 12 | /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 13 | /******/ } 14 | /******/ } 15 | /******/ }; 16 | /******/ })(); 17 | /******/ 18 | /******/ /* webpack/runtime/hasOwnProperty shorthand */ 19 | /******/ (() => { 20 | /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 21 | /******/ })(); 22 | /******/ 23 | /************************************************************************/ 24 | var __webpack_exports__ = {}; 25 | /* unused harmony export initMetaboxAutoComplete */ 26 | /** 27 | * @package Polylang 28 | */ 29 | 30 | // Translations autocomplete input box. 31 | function initMetaboxAutoComplete() { 32 | jQuery('.tr_lang').each( 33 | function () { 34 | var tr_lang = jQuery(this).attr('id').substring(8); 35 | var td = jQuery(this).parent().parent().siblings('.pll-edit-column'); 36 | 37 | jQuery(this).autocomplete( 38 | { 39 | minLength: 0, 40 | source: ajaxurl + '?action=pll_posts_not_translated' + 41 | '&post_language=' + jQuery('.post_lang_choice').val() + 42 | '&translation_language=' + tr_lang + 43 | '&post_type=' + jQuery('#post_type').val() + 44 | '&_pll_nonce=' + jQuery('#_pll_nonce').val(), 45 | select: function (event, ui) { 46 | jQuery('#htr_lang_' + tr_lang).val(ui.item.id); 47 | // ui.item.link is built and come from server side and is well escaped when necessary 48 | td.html(ui.item.link); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html 49 | }, 50 | } 51 | ); 52 | 53 | // when the input box is emptied 54 | jQuery(this).on( 55 | 'blur', 56 | function () { 57 | if ( ! jQuery(this).val() ) { 58 | jQuery('#htr_lang_' + tr_lang).val(0); 59 | // Value is retrieved from HTML already generated server side 60 | td.html(td.siblings('.hidden').children().clone()); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html 61 | } 62 | } 63 | ); 64 | } 65 | ); 66 | } 67 | 68 | -------------------------------------------------------------------------------- /include/upgrade.php: -------------------------------------------------------------------------------- 1 | options = $options; 33 | } 34 | 35 | /** 36 | * Runs upgrade process. 37 | * 38 | * @since 3.7 39 | * 40 | * @return void 41 | */ 42 | public function upgrade() { 43 | foreach ( array( '3.7' ) as $version ) { 44 | if ( version_compare( $this->options->get( 'version' ), $version, '<' ) ) { 45 | $method_to_call = array( $this, 'upgrade_' . str_replace( '.', '_', $version ) ); 46 | if ( is_callable( $method_to_call ) ) { 47 | call_user_func( $method_to_call ); 48 | } 49 | } 50 | } 51 | } 52 | 53 | /** 54 | * Migrates translated field groups to the new ACF integration. 55 | * Transforms language taxonomy to group location. 56 | * 57 | * @since 3.7 58 | * 59 | * @return void 60 | */ 61 | private function upgrade_3_7() { 62 | if ( ! ACF_Main::can_use() ) { 63 | return; 64 | } 65 | 66 | if ( ! in_array( 'acf-field-group', $this->options->get( 'post_types' ), true ) ) { 67 | return; 68 | } 69 | 70 | $this->options->set( 71 | 'post_types', 72 | array_diff( 73 | $this->options->get( 'post_types' ), 74 | array( 'acf-field-group' ) 75 | ) 76 | ); 77 | $this->options->save(); 78 | 79 | foreach ( acf_get_field_groups() as $group ) { 80 | if ( empty( $group['ID'] ) ) { 81 | continue; 82 | } 83 | 84 | $group_language = wp_get_object_terms( $group['ID'], 'language' ); 85 | 86 | if ( empty( $group_language ) || is_wp_error( $group_language ) || 1 !== count( $group_language ) ) { 87 | continue; 88 | } 89 | 90 | $group_language = $group_language[0]; 91 | $new_location = array( 92 | 'param' => 'language', 93 | 'operator' => '==', 94 | 'value' => $group_language->slug, 95 | ); 96 | 97 | foreach ( $group['location'] as &$location ) { 98 | $location[] = $new_location; 99 | } 100 | 101 | acf_update_field_group( $group ); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /js/build/nav-menu.min.js: -------------------------------------------------------------------------------- 1 | const pllNavMenu={wrapper:null,init:()=>{"loading"!==document.readyState?pllNavMenu.ready():document.addEventListener("DOMContentLoaded",pllNavMenu.ready)},ready:()=>{pllNavMenu.wrapper=document.getElementById("menu-to-edit"),pllNavMenu.wrapper&&(pllNavMenu.wrapper.addEventListener("click",pllNavMenu.printMetabox),pllNavMenu.wrapper.addEventListener("change",pllNavMenu.ensureContent),pllNavMenu.wrapper.addEventListener("change",pllNavMenu.showHideRows))},printMetabox:{handleEvent:e=>{if(!e.target.classList.contains("item-edit"))return;const t=e.target.closest(".menu-item").querySelector(".menu-item-settings");if(!t?.id)return;if(!t.querySelectorAll('input[value="#pll_switcher"][type=text]').length)return;[...t.children].forEach((e=>{"P"!==e.nodeName||e.classList.contains("field-move")||e.remove()}));const n=pllNavMenu.printMetabox,a=Number(t.id.replace("menu-item-settings-",""));t.append(n.createHiddenInput("title",a,pll_data.title)),t.append(n.createHiddenInput("url",a,"#pll_switcher")),t.append(n.createHiddenInput("pll-detect",a,1));const r=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names","dropdown"),d=void 0!==pll_data.val[a];r.forEach((e=>{const r=n.createElement("p",{class:"description"});"hide_current"===e&&d&&1===pll_data.val[a].dropdown&&r.classList.add("hidden"),t.prepend(r);const l=`edit-menu-item-${e}-${a}`,i=n.createElement("label",{for:l});i.innerText=` ${pll_data.strings[e]}`,r.append(i);const c=n.createElement("input",{type:"checkbox",id:l,name:`menu-item-${e}[${a}]`,value:1});(d&&1===pll_data.val[a][e]||!d&&"show_names"===e)&&(c.checked=!0),i.prepend(c)}))},createHiddenInput:(e,t,n)=>pllNavMenu.printMetabox.createElement("input",{type:"hidden",id:`edit-menu-item-${e}-${t}`,name:`menu-item-${e}[${t}]`,value:n}),createElement:(e,t)=>{const n=document.createElement(e);for(const[e,a]of Object.entries(t))n.setAttribute(e,a);return n}},ensureContent:{regExpr:new RegExp(/^edit-menu-item-show_(names|flags)-(\d+)$/),handleEvent:e=>{if(!e.target.id||e.target.checked)return;const t=e.target.id.match(pllNavMenu.ensureContent.regExpr);if(!t)return;const[,n,a]=t,r="names"===n?"flags":"names";document.getElementById(`edit-menu-item-show_${r}-${a}`).checked=!0}},showHideRows:{regExpr:new RegExp(/^edit-menu-item-dropdown-(\d+)$/),handleEvent:e=>{if(!e.target.id)return;const t=e.target.id.match(pllNavMenu.showHideRows.regExpr);if(!t)return;const n=document.getElementById(`edit-menu-item-hide_current-${t[1]}`);if(!n)return;n.closest(".description").classList.toggle("hidden",e.target.checked),e.target.checked&&(n.checked=!1,n.dispatchEvent(new Event("change")))}}};pllNavMenu.init(); -------------------------------------------------------------------------------- /modules/Machine_Translation/css/machine-translation-settings.css: -------------------------------------------------------------------------------- 1 | .pll-settings .pll-inner-notice { 2 | margin: 5px 0 15px; 3 | border: 1px solid #c3c4c7; 4 | border-left-width: 4px; 5 | padding: 1px 12px; 6 | background: #fff; 7 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); 8 | } 9 | 10 | .form-table .pll-inner-notice { 11 | margin-bottom: 5px; 12 | } 13 | 14 | .pll-settings .notice-success { 15 | border-left-color: #00a32a; 16 | } 17 | 18 | .pll-settings .notice-warning { 19 | border-left-color: #dba617; 20 | } 21 | 22 | .pll-settings .notice-error { 23 | border-left-color: #d63638; 24 | } 25 | 26 | .pll-settings .pll-inner-notice p { 27 | margin: 0.5em 0; 28 | padding: 2px; 29 | } 30 | 31 | .pll-settings .pll-message:not(.pll-origin-message), 32 | .pll-settings .notice-success .pll-origin-message, 33 | .pll-settings .notice-warning .pll-origin-message, 34 | .pll-settings .notice-error .pll-origin-message { 35 | display: none; 36 | } 37 | 38 | .pll-settings .notice-success .pll-success-message, 39 | .pll-settings .notice-warning .pll-warning-message.pll-message-shown, 40 | .pll-settings .notice-error .pll-error-message.pll-message-shown { 41 | display: block; 42 | } 43 | 44 | .pll-settings [disabled] + .spinner, 45 | .pll-progress-bar-wrapper .spinner { 46 | visibility: visible; 47 | } 48 | 49 | .pll-success-message .pll-icon { 50 | color: rgb(18, 91, 145); 51 | } 52 | 53 | .pll-error-message .pll-icon, 54 | .pll-warning-message .pll-icon { 55 | color: rgb(145, 30, 31); 56 | } 57 | 58 | .pll-icon { 59 | font-size: 1.8em; 60 | margin-right: 3px; 61 | margin-left: 1px; 62 | vertical-align: -2px; 63 | } 64 | 65 | .pll-progress-bar-wrapper { 66 | position: relative; 67 | max-width: 500px; 68 | height: 2em; 69 | line-height: 2em; 70 | vertical-align: middle; 71 | text-align: left; 72 | font-size: 2em; 73 | border: 1px solid rgb(64, 70, 72); 74 | border-radius: 4px; 75 | overflow: hidden; 76 | white-space: nowrap; 77 | color: rgb(23, 114, 181); 78 | text-indent: 1em; 79 | } 80 | 81 | .pll-progress-bar-wrapper div { 82 | position: absolute; 83 | height: 100%; 84 | top: 0; 85 | left: 0; 86 | overflow: hidden; 87 | background-color: rgb(23, 114, 181); 88 | color: rgb(232, 230, 227); 89 | } 90 | 91 | .pll-settings [type="password"] + .button { 92 | margin-left: 20px; 93 | margin-right: 0; 94 | } 95 | 96 | .pll-settings [type="password"] + .button + .spinner { 97 | float: none; 98 | } 99 | 100 | .pll-progress-bar-wrapper .spinner { 101 | float: none; 102 | margin: 0; 103 | } 104 | -------------------------------------------------------------------------------- /modules/share-slug/settings-share-slug.php: -------------------------------------------------------------------------------- 1 | 'none' ) ); 21 | 22 | if ( get_option( 'permalink_structure' ) ) { 23 | add_action( 'admin_print_footer_scripts', array( $this, 'print_js' ) ); 24 | } 25 | } 26 | 27 | /** 28 | * Returns the module description. 29 | * 30 | * @since 3.1 31 | * 32 | * @return string 33 | */ 34 | protected function get_description() { 35 | return parent::get_description() . ' ' . __( 'The module is automatically deactivated when using plain permalinks or when the language is set from the content in the URL modifications.', 'polylang-pro' ); 36 | } 37 | 38 | /** 39 | * Tells if the module is active. 40 | * 41 | * @since 1.9 42 | * 43 | * @return bool 44 | */ 45 | public function is_active() { 46 | return $this->options['force_lang'] && get_option( 'permalink_structure' ); 47 | } 48 | 49 | /** 50 | * Displays the javascript to handle dynamically the change in url modifications 51 | * as sharing slugs is not possible when the language is set from the content 52 | * 53 | * @since 1.9 54 | * 55 | * @return void 56 | */ 57 | public function print_js() { 58 | wp_enqueue_script( 'jquery' ); 59 | 60 | $activated = sprintf( '%s', $this->action_links['activated'] ); 61 | $deactivated = sprintf( '%s', $this->action_links['deactivated'] ); 62 | 63 | ?> 64 | 79 | 12 | */ 13 | class PLL_Export_Container implements IteratorAggregate, Countable { 14 | 15 | /** 16 | * Name of the class defining an individual export. 17 | * 18 | * @var string 19 | * 20 | * @phpstan-var class-string 21 | */ 22 | private $class_name; 23 | 24 | /** 25 | * Contains all the exports. 26 | * Each export is referenced with a key composed of its source and target languages. 27 | * 28 | * @var PLL_Export_Data[] 29 | * 30 | * @phpstan-var array 31 | */ 32 | private $exports = array(); 33 | 34 | /** 35 | * Constructor. 36 | * 37 | * @since 3.6 38 | * 39 | * @param string $class_name Name of the class that defines an individual export. The class must implement 40 | * the interface `PLL_Export_Data`. 41 | * 42 | * @phpstan-param class-string $class_name 43 | */ 44 | public function __construct( string $class_name ) { 45 | $this->class_name = $class_name; 46 | } 47 | 48 | /** 49 | * Returns an export object for the given source/target languages pair. 50 | * 51 | * @since 3.6 52 | * 53 | * @param PLL_Language $source_language The export's source language. 54 | * @param PLL_Language $target_language The export's target language. 55 | * @return PLL_Export_Data 56 | */ 57 | public function get( PLL_Language $source_language, PLL_Language $target_language ): PLL_Export_Data { 58 | $export_key = "{$source_language->slug}/{$target_language->slug}"; 59 | $class_name = $this->class_name; 60 | 61 | if ( ! array_key_exists( $export_key, $this->exports ) ) { 62 | $this->exports[ $export_key ] = new $class_name( $source_language, $target_language ); 63 | } 64 | 65 | return $this->exports[ $export_key ]; 66 | } 67 | 68 | /** 69 | * Returns an exports iterator. 70 | * Needed for the interface `IteratorAggregate`. 71 | * 72 | * @since 3.6 73 | * 74 | * @return ArrayIterator 75 | * 76 | * @phpstan-return ArrayIterator 77 | */ 78 | public function getIterator(): ArrayIterator { 79 | return new ArrayIterator( $this->exports ); 80 | } 81 | 82 | /** 83 | * Returns the number of exports. 84 | * Needed for the interface `Countable`. 85 | * 86 | * @since 3.6 87 | * 88 | * @return int 89 | */ 90 | public function count(): int { 91 | return count( $this->exports ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /services/metabox-button/toggle-user-meta.php: -------------------------------------------------------------------------------- 1 | meta_name = $meta_name; 30 | } 31 | 32 | /** 33 | * Returns the user meta name storing the enabled/disabled statuses of the action per post type. 34 | * 35 | * @since 3.6 36 | * 37 | * @return string The user meta name. 38 | */ 39 | public function get_meta_name(): string { 40 | return $this->meta_name; 41 | } 42 | 43 | /** 44 | * Tells whether the button is active or not. 45 | * 46 | * @since 2.1 47 | * 48 | * @global $post 49 | * 50 | * @return bool 51 | */ 52 | public function is_active() { 53 | global $post; 54 | $user_meta = $this->get(); 55 | return ! empty( $user_meta[ $post->post_type ] ); 56 | } 57 | 58 | /** 59 | * Returns the user meta value. 60 | * 61 | * @since 3.6 62 | * 63 | * @return bool[] 64 | */ 65 | public function get() { 66 | $user_meta = get_user_meta( (int) get_current_user_id(), $this->get_meta_name(), true ); 67 | return is_array( $user_meta ) ? $user_meta : array(); 68 | } 69 | 70 | /** 71 | * Updates the user meta. 72 | * 73 | * @since 3.6 74 | * 75 | * @param bool[] $user_meta An array with post type as key and boolean as value. 76 | * @param WP_User|null $user An instance of `WP_User`. 77 | * @return bool 78 | */ 79 | public function update( $user_meta, $user = null ) { 80 | if ( ! $user instanceof WP_User ) { 81 | $user = wp_get_current_user(); 82 | } 83 | 84 | return (bool) update_user_meta( (int) $user->ID, $this->get_meta_name(), $user_meta ); 85 | } 86 | 87 | /** 88 | * Saves the button state. 89 | * 90 | * @since 2.1 91 | * 92 | * @param string $post_type Current post type. 93 | * @param bool $active New requested button state. 94 | * @return bool Whether the new button state is accepted or not. 95 | */ 96 | public function toggle_option( $post_type, $active ) { 97 | $user_meta = $this->get(); 98 | $user_meta[ $post_type ] = (bool) $active; 99 | 100 | return $this->update( $user_meta ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /include/Options/Business/Machine_Translation_Services.php: -------------------------------------------------------------------------------- 1 | , 66 | * additionalProperties: false 67 | * } 68 | */ 69 | protected function get_data_structure(): array { 70 | $structure = array( 71 | 'type' => 'object', // Correspond to associative array in PHP, @see{https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#primitive-types}. 72 | 'properties' => array(), 73 | 'additionalProperties' => false, 74 | ); 75 | 76 | foreach ( Factory::get_classnames() as $service ) { 77 | $structure['properties'][ $service::get_slug() ] = array( 78 | 'type' => 'object', 79 | 'properties' => $service::get_option_schema(), 80 | 'additionalProperties' => false, 81 | ); 82 | } 83 | 84 | return $structure; 85 | } 86 | 87 | /** 88 | * Returns the description used in the JSON schema. 89 | * 90 | * @since 3.7 91 | * 92 | * @return string 93 | */ 94 | protected function get_description(): string { 95 | return __( 'Settings for machine translation services: DeepL\'s API key and formality for now.', 'polylang-pro' ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /modules/full-site-editing/fse-language-slug-change.php: -------------------------------------------------------------------------------- 1 | slug === $args['slug'] ) { 59 | // The slug hasn't changed. 60 | return; 61 | } 62 | 63 | /** 64 | * At this point: 65 | * - the language cache has been cleared: `PLL_Model->clean_languages_cache()` is hooked to 66 | * 'edited_term_taxonomy'. 67 | * - `$this->options['default_lang']` has been updated in `PLL_Admin_Model->update_language()`. 68 | */ 69 | $def_lang = $this->model->get_default_language(); 70 | 71 | if ( empty( $def_lang ) || $def_lang->slug === $args['slug'] ) { 72 | // Templates in the default language don't have the language suffix: nothing to update then. 73 | return; 74 | } 75 | 76 | // At this point, we're modifying the slug of a non-default language. 77 | $this->update_language_suffix_in_post_names( $lang, $args['slug'] ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /integrations/acf/js/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress dependencies. 3 | */ 4 | import apiFetch from '@wordpress/api-fetch'; 5 | 6 | document.addEventListener( 'onPostLangChoice', ( e ) => { 7 | const fields = []; 8 | 9 | // Adds relationship fields to the fields to be refreshed. 10 | const relationshipFields = document.querySelectorAll( 11 | '.acf-field-relationship' 12 | ); 13 | relationshipFields.forEach( function ( relationshipField ) { 14 | const field = relationshipField.getAttribute( 'data-key' ); 15 | fields.push( field ); 16 | } ); 17 | 18 | // Adds post object fields to the fields to be refreshed. 19 | const postObjectFields = document.querySelectorAll( 20 | '.acf-field-post-object' 21 | ); 22 | postObjectFields.forEach( function ( postObjectField ) { 23 | const field = postObjectField.getAttribute( 'data-key' ); 24 | fields.push( field ); 25 | } ); 26 | 27 | // Adds taxonomy fields to the fields to be refreshed. 28 | const taxonomyFields = document.querySelectorAll( '.acf-field-taxonomy' ); 29 | taxonomyFields.forEach( function ( taxonomyField ) { 30 | const field = taxonomyField.getAttribute( 'data-key' ); 31 | fields.push( field ); 32 | } ); 33 | 34 | if ( 0 < fields.length ) { 35 | const postId = document 36 | .getElementById( 'post_ID' ) 37 | .getAttribute( 'value' ); 38 | 39 | let nonce = document.querySelector( '#_pll_nonce' )?.value; // Classic editor. 40 | if ( undefined === nonce ) { 41 | // Block editor. 42 | nonce = pll_block_editor_plugin_settings.nonce; 43 | } 44 | const data = new FormData(); 45 | data.set( 'action', 'acf_post_lang_choice' ); 46 | data.set( 'lang', encodeURI( e.detail.lang.slug ) ); 47 | data.set( 'fields', fields ); 48 | data.set( 'post_id', postId ); 49 | data.set( '_pll_nonce', nonce ); 50 | 51 | apiFetch( { 52 | url: ajaxurl, 53 | method: 'POST', 54 | body: data, 55 | } ).then( ( response ) => { 56 | response.forEach( function ( res ) { 57 | // Data comes from ACF field and server side. 58 | const field = document.querySelector( '.acf-' + res.field_key ); 59 | 60 | field.outerHTML = res.field_data; 61 | acf.do_action( 62 | 'ready_field/type=' + field.getAttribute( 'data-type' ), 63 | field 64 | ); 65 | } ); 66 | 67 | if ( 0 < relationshipFields.length ) { 68 | // We need to reload the choices list for relationship fields (otherwise it remains empty). 69 | relationshipFields.forEach( function ( relationshipField ) { 70 | acf.getField( 71 | relationshipField.getAttribute( 'data-key' ) 72 | ).fetch(); 73 | } ); 74 | } 75 | 76 | // Reloads the list of posts in `post_object` fields. 77 | acf.getFields( { type: 'post_object' } ); 78 | } ); 79 | } 80 | } ); 81 | -------------------------------------------------------------------------------- /modules/import-export/export/view-tab-export-strings.php: -------------------------------------------------------------------------------- 1 | get_languages_list(); 15 | $default_language = $model->get_default_language(); 16 | $strings = PLL_Admin_Strings::get_strings(); 17 | $groups = array_unique( wp_list_pluck( $strings, 'context' ) ); 18 | $supported_formats = ( new PLL_File_Format_Factory() )->get_supported_formats( 'strings' ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable 19 | ?> 20 |
21 |
22 |
23 |
24 | 25 | 26 | 27 | %3$s%2$s', 32 | esc_attr( $language->slug ), 33 | esc_html( $language->name ), 34 | $language->flag // phpcs:ignore WordPress.Security.EscapeOutput 35 | ); 36 | } 37 | } 38 | ?> 39 |
40 |
41 |
42 | 43 | 57 |
58 |
59 | 63 |
64 |

65 | 66 |

67 |
68 |
69 | -------------------------------------------------------------------------------- /modules/full-site-editing/fse-language.php: -------------------------------------------------------------------------------- 1 | get_site_editor_language(); 53 | 54 | if ( empty( $editor_lang ) ) { 55 | return false; 56 | } 57 | 58 | return $editor_lang; 59 | } 60 | 61 | /** 62 | * Returns the language object to use in the site editor. 63 | * 64 | * @since 3.2 65 | * @since 3.5 Removed `$model` parameter. 66 | * 67 | * @return PLL_Language|false 68 | */ 69 | private function get_site_editor_language() { 70 | if ( empty( $_GET['postType'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 71 | return $this->model->get_default_language(); 72 | } 73 | 74 | $post = null; 75 | 76 | if ( PLL_FSE_Tools::is_template_post_type( sanitize_key( $_GET['postType'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 77 | $post = PLL_FSE_Tools::get_template_post(); 78 | } elseif ( ! empty( $_GET['postId'] ) && is_numeric( sanitize_key( $_GET['postId'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 79 | $post = get_post( (int) $_GET['postId'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended 80 | } 81 | 82 | if ( empty( $post ) ) { 83 | return $this->model->get_default_language(); 84 | } 85 | 86 | $post_lang = $this->model->post->get_language( $post->ID ); 87 | 88 | if ( empty( $post_lang ) ) { 89 | return $this->model->get_default_language(); 90 | } 91 | 92 | return $post_lang; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /modules/Editors/Screens/Widget.php: -------------------------------------------------------------------------------- 1 | base || parent::can_enqueue_style( $screen ); 38 | } 39 | 40 | /** 41 | * Method that allows legacy widgets in widget block editor previously removed by WP and hide legacy Polylang widget. 42 | * 43 | * @since 3.2 44 | * 45 | * @param array $widget_ids An array of hidden widget ids. 46 | * @return array 47 | */ 48 | public function filter_legacy_widgets( $widget_ids ) { 49 | $widgets_to_show = array( 'custom_html' ); 50 | $widget_ids = array_diff( $widget_ids, $widgets_to_show ); 51 | 52 | $widgets_to_hide = array( 'polylang' ); 53 | $widget_ids = array_merge( $widget_ids, $widgets_to_hide ); 54 | 55 | return $widget_ids; 56 | } 57 | 58 | 59 | /** 60 | * Tells whether the given screen is the Widget edtitor or not. 61 | * 62 | * @since 3.7 63 | * 64 | * @param WP_Screen $screen The current screen. 65 | * @return bool True if Widget editor screen, false otherwise. 66 | */ 67 | protected function screen_matches( WP_Screen $screen ): bool { 68 | return ( 69 | 'widgets' === $screen->base 70 | && function_exists( 'wp_use_widgets_block_editor' ) 71 | && wp_use_widgets_block_editor() 72 | && method_exists( $screen, 'is_block_editor' ) 73 | && $screen->is_block_editor() 74 | ); 75 | } 76 | 77 | /** 78 | * Returns the language to use in the Widget editor. 79 | * 80 | * @since 3.7 81 | * 82 | * @return PLL_Language|null 83 | */ 84 | protected function get_language(): ?PLL_Language { 85 | $language = $this->model->languages->get_default(); 86 | 87 | return $language instanceof PLL_Language ? $language : null; 88 | } 89 | 90 | /** 91 | * Returns the screen name for the Widget editor to use across all process. 92 | * 93 | * @since 3.7 94 | * 95 | * @return string 96 | */ 97 | protected function get_screen_name(): string { 98 | return 'widget'; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /modules/Machine_Translation/Posts/Button_REST.php: -------------------------------------------------------------------------------- 1 | service = $service; 46 | $this->user_meta = new PLL_Toggle_User_Meta( sprintf( 'pll_machine_translation_%s', $this->service->get_slug() ) ); 47 | 48 | register_rest_field( 49 | 'user', 50 | $this->user_meta->get_meta_name(), 51 | array( 52 | 'get_callback' => array( $this->user_meta, 'get' ), 53 | 'update_callback' => array( $this->user_meta, 'update' ), 54 | ) 55 | ); 56 | 57 | add_filter( 'pll_block_editor_plugin_settings', array( $this, 'get_service_settings' ) ); 58 | add_filter( 'pll_block_editor_plugin_settings', array( $this, 'get_settings_errors' ) ); 59 | } 60 | 61 | /** 62 | * Adds service properties in UI settings. 63 | * 64 | * @since 3.6 65 | * 66 | * @param array $settings UI settings. 67 | * @return array Updated UI settings. 68 | */ 69 | public function get_service_settings( $settings ) { 70 | $settings['machine_translation'] = array( 71 | 'slug' => $this->service::get_slug(), 72 | 'name' => $this->service->get_name(), 73 | 'icon' => $this->service->get_icon_properties(), 74 | 'isActive' => $this->service->is_active(), 75 | ); 76 | return $settings; 77 | } 78 | 79 | /** 80 | * Adds machine translation errors in UI settings. 81 | * 82 | * @since 3.6 83 | * 84 | * @param array $settings UI settings. 85 | * @return array Updated UI settings. 86 | */ 87 | public function get_settings_errors( $settings ) { 88 | $settings_errors = get_settings_errors( 'polylang' ); 89 | 90 | if ( empty( $settings_errors ) ) { 91 | return $settings; 92 | } 93 | 94 | $settings['machine_translation']['errors'] = $settings_errors; 95 | 96 | return $settings; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /modules/Editors/Screens/Site.php: -------------------------------------------------------------------------------- 1 | curlang = &$polylang->curlang; 34 | } 35 | 36 | /** 37 | * Adds required hooks. 38 | * 39 | * @since 3.7 40 | * 41 | * @return static 42 | */ 43 | public function init() { 44 | parent::init(); 45 | add_filter( 'pll_admin_ajax_params', array( $this, 'ajax_filter' ) ); 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * Adds the language to the data added to all AJAX requests. 52 | * 53 | * @since 3.7 54 | * 55 | * @param array $params List of parameters to add to the admin ajax request. 56 | * @return array 57 | */ 58 | public function ajax_filter( $params ) { 59 | $screen = get_current_screen(); 60 | 61 | if ( empty( $screen ) ) { 62 | return $params; 63 | } 64 | 65 | if ( ! $this->screen_matches( $screen ) ) { 66 | return $params; 67 | } 68 | 69 | $editor_lang = $this->get_language(); 70 | 71 | if ( empty( $editor_lang ) ) { 72 | return $params; 73 | } 74 | 75 | $params['lang'] = $editor_lang->slug; 76 | return $params; 77 | } 78 | 79 | 80 | /** 81 | * Tells whether the given screen is the Site edtitor or not. 82 | * 83 | * @since 3.7 84 | * 85 | * @param WP_Screen $screen The current screen. 86 | * @return bool True if Site editor screen, false otherwise. 87 | */ 88 | protected function screen_matches( WP_Screen $screen ): bool { 89 | return ( 90 | 'site-editor' === $screen->base 91 | && $this->model->post_types->is_translated( 'wp_template_part' ) 92 | && method_exists( $screen, 'is_block_editor' ) 93 | && $screen->is_block_editor() 94 | ); 95 | } 96 | 97 | /** 98 | * Returns the language to use in the Site editor. 99 | * 100 | * @since 3.7 101 | * 102 | * @return PLL_Language|null 103 | */ 104 | protected function get_language(): ?PLL_Language { 105 | if ( ! empty( $this->curlang ) && PLL_FSE_Tools::is_site_editor() ) { 106 | return $this->curlang; 107 | } 108 | 109 | return null; 110 | } 111 | 112 | /** 113 | * Returns the screen name for the Site editor to use across all process. 114 | * 115 | * @since 3.7 116 | * 117 | * @return string 118 | */ 119 | protected function get_screen_name(): string { 120 | return 'site'; 121 | } 122 | } 123 | --------------------------------------------------------------------------------