diff --git a/.distignore b/.distignore index 1cf71d09d..73ad95a02 100644 --- a/.distignore +++ b/.distignore @@ -1,3 +1,4 @@ +.DS_Store .distignore .gitattributes .gitignore @@ -13,6 +14,7 @@ /.phpunit.cache /.vscode /.wordpress-org +/artifacts /bin /build /docs diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 7e2eda7e7..59577fe63 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -2,6 +2,13 @@ PHPCS MSLS configuration + + MultisiteLanguageSwitcher.php + config.php + index.php + includes + src + assets/* bin/* images/* diff --git a/Changelog.md b/Changelog.md index d328af753..98d40fe2f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,7 +6,7 @@ * Restructure `lloc\Msls\` into per-concern sub-namespaces (`Admin\`, `Blog\`, `ContentImport\`, `ContentTypes\`, `Frontend\`, `Link\`, `Options\`, `Registry\`, `RestApi\`). Every former flat `Msls*` class name keeps working through `lloc\Msls\Compat\Aliases`, registered from `includes/aliases.php`. * Move the public helper functions into `includes/api.php` and make the `$attr` argument of `msls_get_switcher()` optional. * Add a PHP-DI container for service construction, built on first use by `lloc\Msls\Container::get()`. -* Documentation: add a developer reference under `docs/` (public API, hooks, snippets, acknowledgements) and refresh the class and package diagrams. +* Documentation: add a developer reference under `docs/` (public API, hooks, snippets, end-to-end testing, acknowledgements) and refresh the class and package diagrams. * Fix: load the backwards-compatibility aliases and the `msls_*()` functions when the plugin file is included instead of on `plugins_loaded`, and keep the settings page slug handed to `msls_admin_register` at its pre-3.0 value. Add-ons such as MslsMenu load before the plugin and check `class_exists( 'lloc\Msls\MslsOptions' )` before registering anything, which silently disabled them — no add-on settings section, and no switcher in the nav menu. * Fix: check authorization on the destination post during content import, and correct the ContentImporter permission and post type checks. * Fix: do not fall back to `home_url()` for taxonomy and query archives. @@ -14,6 +14,7 @@ * Fix: several issues in the blog collection. * i18n: close gaps in the WP-CLI messages and the Quick Create button title. * Internal: strict typing throughout, PHPStan level 8 clean, `ABSPATH` guards, Plugin Check and PHPCS findings addressed, wp-env setup for local multisite development. +* Internal: add a Playwright end-to-end suite (admin, frontend and visual specs against wp-env, plus a read-only `live` smoke test) and run the local project on every pull request. * Maintenance: numerous dependency updates. ## 2.10.1 @@ -22,7 +23,7 @@ ## 2.10.0 -* Add prefixed public helper functions (msls_get_*, msls_the_msls) with deprecation shims for legacy names to satisfy WPCS while staying backward compatible. +* Add prefixed public helper functions (msls_get_*, msls_the_switcher) with deprecation shims for legacy names to satisfy WPCS while staying backward compatible. * Expose action hook names as constants for safer programmatic use. * Accessibility: add aria-current="page" on the active language link. * Fix: resolve path-related issue affecting asset/loader resolution. diff --git a/assets/css-flags/flags.php b/assets/css-flags/flags.php index 8be42bf79..5a0da669c 100644 --- a/assets/css-flags/flags.php +++ b/assets/css-flags/flags.php @@ -33,10 +33,10 @@ 'cy' => 'flag-icon-gb-wls', 'da_DK' => 'flag-icon-dk', 'de_AT' => 'flag-icon-at', - 'de_CH' => 'flag-icon-ch', 'de_CH_informal' => 'flag-icon-ch', 'de_DE' => 'flag-icon-de', 'de_DE_formal' => 'flag-icon-de', + 'de_CH' => 'flag-icon-ch', 'dzo' => 'flag-icon-bt', 'el' => 'flag-icon-gr', 'en_AU' => 'flag-icon-au', @@ -47,7 +47,6 @@ 'eo' => 'flag-icon-eu', 'es_AR' => 'flag-icon-ar', 'es_CR' => 'flag-icon-cr', - 'es_ES' => 'flag-icon-es', 'es_MX' => 'flag-icon-mx', 'es_CO' => 'flag-icon-co', 'es_PE' => 'flag-icon-pe', @@ -58,6 +57,7 @@ 'es_PR' => 'flag-icon-pr', 'es_CL' => 'flag-icon-cl', 'es_GT' => 'flag-icon-gt', + 'es_ES' => 'flag-icon-es', 'et' => 'flag-icon-ee', 'eu' => 'flag-icon-es', 'fa_IR' => 'flag-icon-ir', @@ -98,9 +98,9 @@ 'my_MM' => 'flag-icon-mm', 'nb_NO' => 'flag-icon-no', 'ne_NP' => 'flag-icon-np', + 'nl_BE' => 'flag-icon-be', 'nl_NL' => 'flag-icon-nl', 'nl_NL_formal' => 'flag-icon-nl', - 'nl_BE' => 'flag-icon-be', 'nn_NO' => 'flag-icon-no', 'oci' => 'flag-icon-es-ca', 'pa_IN' => 'flag-icon-in', diff --git a/assets/flags/flags.php b/assets/flags/flags.php index e25481ce1..fa58c36cc 100644 --- a/assets/flags/flags.php +++ b/assets/flags/flags.php @@ -33,10 +33,10 @@ 'cy' => 'wales.png', 'da_DK' => 'dk.png', 'de_AT' => 'at.png', - 'de_CH' => 'ch.png', 'de_CH_informal' => 'ch.png', 'de_DE' => 'de.png', 'de_DE_formal' => 'de.png', + 'de_CH' => 'ch.png', 'dzo' => 'bt.png', 'el' => 'gr.png', 'en_AU' => 'au.png', @@ -47,7 +47,6 @@ 'eo' => 'europeanunion.png', 'es_AR' => 'ar.png', 'es_CR' => 'cr.png', - 'es_ES' => 'es.png', 'es_MX' => 'mx.png', 'es_CO' => 'co.png', 'es_PE' => 'pe.png', @@ -58,6 +57,7 @@ 'es_PR' => 'pr.png', 'es_CL' => 'cl.png', 'es_GT' => 'gt.png', + 'es_ES' => 'es.png', 'et' => 'ee.png', 'eu' => 'es.png', 'fa_IR' => 'ir.png', @@ -98,9 +98,9 @@ 'my_MM' => 'mm.png', 'nb_NO' => 'no.png', 'ne_NP' => 'np.png', + 'nl_BE' => 'be.png', 'nl_NL' => 'nl.png', 'nl_NL_formal' => 'nl.png', - 'nl_BE' => 'be.png', 'nn_NO' => 'no.png', 'oci' => 'catalonia.png', 'pa_IN' => 'in.png', diff --git a/composer.lock b/composer.lock index e9cd885be..22264902e 100644 --- a/composer.lock +++ b/composer.lock @@ -1368,11 +1368,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.2.8", + "version": "2.2.9", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e285254e60f33c21902efef4a926ca0987c06804", - "reference": "e285254e60f33c21902efef4a926ca0987c06804", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/13d6b4f347bad222da436580c8304fa6f83e6bd0", + "reference": "13d6b4f347bad222da436580c8304fa6f83e6bd0", "shasum": "" }, "require": { @@ -1428,7 +1428,7 @@ "type": "github" } ], - "time": "2026-08-04T22:21:45+00:00" + "time": "2026-08-22T07:38:16+00:00" }, { "name": "phpstan/phpstan-mockery", diff --git a/docs/api.md b/docs/api.md index c5fcfc769..48089b152 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,9 +1,11 @@ # Public API Functions Multisite Language Switcher ships with a small set of global helper functions -that act as the plugin's public API. They live in `includes/api.php` and are -loaded on the `plugins_loaded` action, so they're available anywhere your -theme or another plugin runs after WordPress has bootstrapped its plugins. +that act as the plugin's public API. They live in `includes/api.php`, which +`MultisiteLanguageSwitcher.php` requires the moment the plugin file is +included — before `plugins_loaded` fires. They are therefore available to +add-ons and themes regardless of the order in which WordPress loads the +plugins. Use these functions in templates, shortcodes, blocks, or other plugins whenever you need to render the switcher, resolve a translation URL, or diff --git a/docs/e2e-testing.md b/docs/e2e-testing.md index 4cbaeb0fd..32f483783 100644 --- a/docs/e2e-testing.md +++ b/docs/e2e-testing.md @@ -63,16 +63,22 @@ msls.co will break the suite. ### Current status -As of 2026-08-21 the suite was **5 passed, 1 failed** against msls.co. The failing test was -`testing with .msls-menu de_DE en_GB`: `/testpage` no longer rendered an element with the +As of 2026-08-22 the suite is **5 passed, 1 failed** against msls.co. The failing test is +`testing with .msls-menu de_DE en_GB`: `/testpage` does not render an element with the `msls-menu` class, while the site's Custom CSS rule (`.msls-menu a { display: inline-block; }`) -was still there. - -That was **not** fixture drift — it was the plugin. Since commit `3afd781` the -backwards-compatibility aliases were loaded inside a `plugins_loaded` callback, which made -`class_exists( 'lloc\Msls\MslsOptions' )` return `false` for the MslsMenu add-on, so -MslsMenu registered neither its `wp_nav_menu_items` filter nor its settings section. This -spec is the standing regression test for that bug; keep it. +is still there. + +That is **not** fixture drift — it is the plugin. Between commit `3afd781` and the 3.0.0 +release the backwards-compatibility aliases were loaded inside a `plugins_loaded` callback, +which made `class_exists( 'lloc\Msls\MslsOptions' )` return `false` for the MslsMenu add-on, +so MslsMenu registered neither its `wp_nav_menu_items` filter nor its settings section. + +The cause is fixed on this branch (commit `7e80283`, PR #690): `includes/aliases.php`, +`includes/deprecated.php` and `includes/api.php` are required at file-load time again. The +spec keeps failing against msls.co only because the site still runs the released 2.10.1 +code. **Re-run it once 3.0.0 is deployed to msls.co — it is the release verification for +the add-on connector fix, and it should then be 6 passed.** Keep the spec either way: it is +the standing regression test for that bug. ### Pitfalls diff --git a/docs/hooks.md b/docs/hooks.md index 8bccc21e7..620e355d7 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -411,8 +411,9 @@ Filter on the post data array (title, content, status, post type, …) that MSLS passes to `wp_insert_post()` when creating a translation on a target blog via the REST Quick Create endpoint. Use it to prefix titles, set a different post status, or inject taxonomy/meta defaults before insertion. -The built-in `Msls::prefix_source_language()` callback is registered on -this hook by default and can be removed with `remove_filter()`. +The built-in `lloc\Msls\RestApi\RestApi::prefix_source_language()` callback is +registered on this hook by default (priority 10, four arguments) and can be +removed with `remove_filter()`. ### msls_quick_create_after_insert diff --git a/docs/snippets.md b/docs/snippets.md index 436c3b2a2..565f54c0d 100644 --- a/docs/snippets.md +++ b/docs/snippets.md @@ -184,9 +184,17 @@ add_filter( 'msls_quick_create_post_data', function ( array $post_data, \WP_Post ``` Remember that any custom callback runs alongside the built-in -`Msls::prefix_source_language()` filter, which prepends the source locale to -the new post's title. Remove it with `remove_filter()` if that behavior is -unwanted in your workflow. +`lloc\Msls\RestApi\RestApi::prefix_source_language()` filter, which prepends +the source locale to the new post's title. Remove it with `remove_filter()` if +that behavior is unwanted in your workflow: + +```php +remove_filter( + 'msls_quick_create_post_data', + array( \lloc\Msls\RestApi\RestApi::class, 'prefix_source_language' ), + 10 +); +``` ## See also diff --git a/includes/Admin/Icon.php b/includes/Admin/Icon.php index 4211b03a2..8737141ce 100644 --- a/includes/Admin/Icon.php +++ b/includes/Admin/Icon.php @@ -136,11 +136,11 @@ public function get_a(): string { return $this->get_quick_create_a(); } - /* translators: %s: blog name */ + /* translators: %s: language code */ $format = __( 'Create a new translation in the %s-blog', 'multisite-language-switcher' ); $href = $this->get_edit_new(); } else { - /* translators: %s: blog name */ + /* translators: %s: language code */ $format = __( 'Edit the translation in the %s-blog', 'multisite-language-switcher' ); $href = $this->href; } @@ -161,10 +161,10 @@ protected function get_quick_create_a(): string { $source_blog_id = $collection->get_blog_id( $this->origin_language ); $target_blog_id = get_current_blog_id(); - /* translators: %s: blog name */ + /* translators: %s: language code */ $title = sprintf( __( 'Create a new translation in the %s-blog', 'multisite-language-switcher' ), $this->language ); - /* translators: %s: blog name */ + /* translators: %s: language code */ $edit_title = sprintf( __( 'Edit the translation in the %s-blog', 'multisite-language-switcher' ), $this->language ); return sprintf( diff --git a/includes/api.php b/includes/api.php index 0a6b312fd..29f67abd7 100644 --- a/includes/api.php +++ b/includes/api.php @@ -26,13 +26,14 @@ function msls_get_switcher( $attr = array() ): string { * * You can call this function directly like that * - * if ( function_exists ( 'the_msls' ) ) - * the_msls(); + * if ( function_exists( 'msls_the_switcher' ) ) { + * msls_the_switcher(); + * } * * or just use it as shortcode [sc_msls] * * @package Msls - * @uses get_the_msls + * @uses msls_get_switcher * * @param string[] $arr */ diff --git a/languages/default.pot b/languages/default.pot index 089a8bb11..955eb438b 100644 --- a/languages/default.pot +++ b/languages/default.pot @@ -1,408 +1,596 @@ -#, fuzzy +# Copyright (C) 2026 Dennis Ploetner +# This file is distributed under the GPLv2 or later. msgid "" msgstr "" -"Project-Id-Version: Multisite Language Switcher 1.0\n" -"Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/multisite-language-" -"switcher\n" -"POT-Creation-Date: 2022-10-27 15:32+0200\n" -"PO-Revision-Date: 2014-09-01 16:52+0100\n" -"Last-Translator: \n" -"Language-Team: realloc's asylum \n" +"Project-Id-Version: Multisite Language Switcher 3.0.0\n" +"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/multisite-language-switcher\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 2.3\n" -"X-Poedit-SourceCharset: UTF-8\n" -"X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;" -"_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;" -"esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" -"X-Poedit-Basepath: ..\n" -"X-Poedit-Basepath: .\n" -"X-Poedit-SearchPath-0: languages\n" -"X-Poedit-SearchPath-1: .\n" -"X-Poedit-SearchPathExcluded-0: vendor\n" +"POT-Creation-Date: 2026-08-22T08:48:23+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.12.0\n" +"X-Domain: multisite-language-switcher\n" + +#. Plugin Name of the plugin +#: MultisiteLanguageSwitcher.php +#: includes/Admin/Admin.php:77 +#: includes/Admin/MetaBox.php:149 +#: includes/Admin/PostTag/PostTag.php:308 +#: includes/Frontend/Widget.php:21 +msgid "Multisite Language Switcher" +msgstr "" -#: includes/ContentImport/Importers/Attachments/Linking.php:26 -#: includes/ContentImport/Importers/PostThumbnail/Linking.php:27 -msgid "Linking" +#. Plugin URI of the plugin +#: MultisiteLanguageSwitcher.php +msgid "http://msls.co/" msgstr "" -#: includes/ContentImport/Importers/Attachments/Linking.php:27 -msgid "" -"Links the media attachments from the source post to the destination post; " -"media attachments are not duplicated." +#. Description of the plugin +#: MultisiteLanguageSwitcher.php +msgid "A simple but powerful plugin that will help you to manage the relations of your contents in a multilingual multisite-installation." msgstr "" -#: includes/ContentImport/Importers/Attachments/Linking.php:34 -msgid "" -"Post attachments were left in place in the source blog and linked in the " -"destination post." +#. Author of the plugin +#: MultisiteLanguageSwitcher.php +msgid "Dennis Ploetner" msgstr "" -#: includes/ContentImport/Importers/AttachmentsImporters.php:23 -msgid "Image Attachments" +#. Author URI of the plugin +#: MultisiteLanguageSwitcher.php +msgid "http://lloc.de/" msgstr "" -#: includes/ContentImport/Importers/PostFields/Duplicating.php:28 -#: includes/ContentImport/Importers/PostMeta/Duplicating.php:19 -msgid "Duplicating" +#: includes/Admin/Admin.php:122 +msgid "Activate experimental autocomplete inputs" msgstr "" -#: includes/ContentImport/Importers/PostFields/Duplicating.php:29 -msgid "Copies the source post fields to the destination." +#: includes/Admin/Admin.php:126 +msgid "Activate the content import functionality" msgstr "" -#: includes/ContentImport/Importers/PostFieldsImporters.php:23 -msgid "Post Fields" +#: includes/Admin/Admin.php:130 +msgid "Create draft translations with a single click" msgstr "" -#: includes/ContentImport/Importers/PostMeta/Duplicating.php:20 -msgid "Copies the source post meta to the destination." +#: includes/Admin/Admin.php:134 +msgid "Sort languages by description" msgstr "" -#: includes/ContentImport/Importers/PostMetaImporters.php:23 -msgid "Meta Fields" +#: includes/Admin/Admin.php:135 +msgid "Exclude this blog from output" msgstr "" -#: includes/ContentImport/Importers/PostThumbnail/Linking.php:28 -msgid "" -"Links the featured image from the source post to the destination post; the " -"image is not duplicated." +#: includes/Admin/Admin.php:136 +msgid "Show only links with a translation" msgstr "" -#: includes/ContentImport/Importers/PostThumbnailImporters.php:23 -msgid "Featured Image" +#: includes/Admin/Admin.php:137 +msgid "Display link to the current language" msgstr "" -#: includes/ContentImport/Importers/Terms/ShallowDuplicating.php:33 -msgid "Shallow Duplicating" +#: includes/Admin/Admin.php:138 +msgid "Add hint for available translations" msgstr "" -#: includes/ContentImport/Importers/Terms/ShallowDuplicating.php:34 -msgid "" -"Shallow (one level deep) duplication or assignment of the source post " -"taxonomy terms to the destnation post." +#. translators: %s: URL to the options page +#: includes/Admin/Admin.php:169 +#, php-format +msgid "Multisite Language Switcher is almost ready. You must complete the configuration process." msgstr "" -#: includes/ContentImport/Importers/TermsImporters.php:23 -msgid "Taxonomy Terms" +#. translators: %1$s: URL to a page at WordPress.orgs +#: includes/Admin/Admin.php:176 +#, php-format +msgid "No language files are currently installed. Learn how to install various languages in WordPress by reading more here." msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:18 -msgid "Multisite Language Switcher last import report" +#: includes/Admin/Admin.php:195 +msgid "Multisite Language Switcher Options" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:20 -#, php-format -msgid "From post %d on site %d to post %d on site %d" +#: includes/Admin/Admin.php:197 +msgid "To achieve maximum flexibility, you have to configure each blog separately." msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:27 -msgid "General information" +#: includes/Admin/Admin.php:206 +msgid "Configure" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:33 -msgid "Details" +#: includes/Admin/Admin.php:206 +msgid "Update" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:38 -msgid "The following post fields have been set: " +#: includes/Admin/Admin.php:244 +msgid "Language Settings" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:42 -msgid "The following post meta have been set: " +#: includes/Admin/Admin.php:245 +msgid "Main Settings" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:46 -msgid "Terms have been assigned to the post for the following taxonomies: " +#: includes/Admin/Admin.php:246 +msgid "Advanced Settings" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:50 -msgid "The post thumbnail has been set." +#: includes/Admin/Admin.php:251 +msgid "Rewrites Settings" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:57 -msgid "Errors:" +#: includes/Admin/Admin.php:276 +msgid "Blog Language" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:63 -msgid "" -"There were issues creating or assigning terms for the following taxonomies: " +#: includes/Admin/Admin.php:290 +msgid "Display" msgstr "" -#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:67 -msgid "The post thumbnail could not be created or set." +#: includes/Admin/Admin.php:291 +msgid "Admin Display" msgstr "" -#: includes/ContentImport/MetaBox.php:35 -#, php-format -msgid "Import content from %s" +#: includes/Admin/Admin.php:292 +msgid "Sort languages" msgstr "" -#: includes/ContentImport/MetaBox.php:38 -msgid "" -"Warning! This will override and replace all the post content with the " -"content from the source post!" +#: includes/Admin/Admin.php:293 +msgid "Current language link" msgstr "" -#: includes/ContentImport/MetaBox.php:63 -msgid "" -"No translated versions linked to this post: import content functionality is " -"disabled." +#: includes/Admin/Admin.php:294 +msgid "Translation links" msgstr "" -#: includes/ContentImport/MetaBox.php:98 -msgid "Select what should be imported and how" +#: includes/Admin/Admin.php:295 +msgid "Description" msgstr "" -#: includes/ContentImport/MetaBox.php:113 -msgid "No importers available for this type of content." +#: includes/Admin/Admin.php:296 +msgid "Text/HTML before the list" msgstr "" -#: includes/ContentImport/MetaBox.php:119 -msgid "Off - Do not import this type of content in the destination post." +#: includes/Admin/Admin.php:297 +msgid "Text/HTML after the list" msgstr "" -#: includes/ContentImport/MetaBox.php:141 -msgid "Import Content" +#: includes/Admin/Admin.php:298 +msgid "Text/HTML before each item" msgstr "" -#: includes/MslsAdmin.php:46 includes/MslsMetaBox.php:127 -#: includes/MslsPostTag.php:171 includes/MslsPostTagClassic.php:121 -#: includes/MslsWidget.php:26 -msgid "Multisite Language Switcher" +#: includes/Admin/Admin.php:299 +msgid "Text/HTML after each item" msgstr "" -#: includes/MslsAdmin.php:93 -msgid "Activate experimental autocomplete inputs" +#: includes/Admin/Admin.php:300 +msgid "Available translations hint" msgstr "" -#: includes/MslsAdmin.php:94 -msgid "Activate the content import functionality" +#: includes/Admin/Admin.php:301 +msgid "Hint priority" msgstr "" -#: includes/MslsAdmin.php:95 -msgid "Sort languages by description" +#: includes/Admin/Admin.php:316 +msgid "Autocomplete" msgstr "" -#: includes/MslsAdmin.php:96 -msgid "Exclude this blog from output" +#: includes/Admin/Admin.php:317 +msgid "Custom URL for flag-images" msgstr "" -#: includes/MslsAdmin.php:97 -msgid "Show only links with a translation" +#: includes/Admin/Admin.php:318 +msgid "Reference user" msgstr "" -#: includes/MslsAdmin.php:98 -msgid "Display link to the current language" +#: includes/Admin/Admin.php:319 +msgid "Exclude blog" msgstr "" -#: includes/MslsAdmin.php:99 -msgid "Add hint for available translations" +#: includes/Admin/Admin.php:320 +msgid "Content import" msgstr "" -#: includes/MslsAdmin.php:119 +#: includes/Admin/Admin.php:321 +msgid "Quick Create" +msgstr "" + +#. translators: %s: post type label +#: includes/Admin/Admin.php:338 #, php-format -msgid "" -"Multisite Language Switcher is almost ready. You must complete the configuration process." +msgid "%s Slug" +msgstr "" + +#: includes/Admin/Admin.php:400 +msgid "Flag" +msgstr "" + +#: includes/Admin/Admin.php:401 +msgid "Label" msgstr "" -#: includes/MslsAdmin.php:124 +#: includes/Admin/CustomFilter.php:53 +msgid "Show all posts" +msgstr "" + +#. translators: %s: blog name +#: includes/Admin/CustomFilter.php:56 #, php-format -msgid "" -"There are no language files installed. You can manually " -"install some language files or you could use a plugin " -"to download these files automatically." +msgid "Not translated in the %s-blog" msgstr "" -#: includes/MslsAdmin.php:139 -msgid "Multisite Language Switcher Options" +#. translators: %s: language code +#: includes/Admin/Icon.php:140 +#: includes/Admin/Icon.php:165 +#: includes/Admin/MetaBox.php:466 +#, php-format +msgid "Create a new translation in the %s-blog" msgstr "" -#: includes/MslsAdmin.php:141 -msgid "" -"To achieve maximum flexibility, you have to configure each blog separately." +#. translators: %s: language code +#: includes/Admin/Icon.php:144 +#: includes/Admin/Icon.php:168 +#: includes/Admin/MetaBox.php:434 +#, php-format +msgid "Edit the translation in the %s-blog" msgstr "" -#: includes/MslsAdmin.php:149 -msgid "Configure" +#: includes/Admin/MetaBox.php:169 +msgid "Multisite Language Switcher - Import content" msgstr "" -#: includes/MslsAdmin.php:149 -msgid "Update" +#: includes/Admin/MetaBox.php:278 +#: includes/Admin/MetaBox.php:403 +msgid "You should define at least another blog in a different language in order to have some benefit from this plugin!" msgstr "" -#: includes/MslsAdmin.php:180 -msgid "Language Settings" +#: includes/Admin/PostListActions.php:79 +#: includes/Admin/TranslationPicker/Page.php:100 +msgid "Add from Translation" msgstr "" -#: includes/MslsAdmin.php:181 -msgid "Main Settings" +#: includes/Admin/TranslationPicker/Page.php:99 +#: includes/Admin/TranslationPicker/Page.php:343 +msgid "Add Post from Translation" msgstr "" -#: includes/MslsAdmin.php:182 -msgid "Advanced Settings" +#: includes/Admin/TranslationPicker/Page.php:130 +msgid "Posts per page" msgstr "" -#: includes/MslsAdmin.php:187 -msgid "Rewrites Settings" +#: includes/Admin/TranslationPicker/Page.php:280 +msgid "Creating draft…" msgstr "" -#: includes/MslsAdmin.php:213 -msgid "Blog Language" +#. translators: 1: index of the current item being processed, 2: total number of selected items +#: includes/Admin/TranslationPicker/Page.php:282 +#, php-format +msgid "Creating drafts: %1$d of %2$d…" msgstr "" -#: includes/MslsAdmin.php:227 -msgid "Display" +#. translators: 1: number of drafts successfully created, 2: number of errors encountered +#: includes/Admin/TranslationPicker/Page.php:284 +#, php-format +msgid "%1$d drafts created, %2$d errors." msgstr "" -#: includes/MslsAdmin.php:228 -msgid "Sort languages" +#: includes/Admin/TranslationPicker/Page.php:285 +msgid "No posts selected." msgstr "" -#: includes/MslsAdmin.php:229 -msgid "Current language link" +#: includes/Admin/TranslationPicker/Page.php:286 +msgid "Something went wrong. Please try again." msgstr "" -#: includes/MslsAdmin.php:230 -msgid "Translation links" +#: includes/Admin/TranslationPicker/Page.php:299 +msgid "You do not have permission to access this page." msgstr "" -#: includes/MslsAdmin.php:231 -msgid "Description" +#: includes/Admin/TranslationPicker/Page.php:339 +msgid "← Back to all posts" msgstr "" -#: includes/MslsAdmin.php:232 -msgid "Text/HTML before the list" +#: includes/Admin/TranslationPicker/Page.php:350 +msgid "Creating drafts in:" msgstr "" -#: includes/MslsAdmin.php:233 -msgid "Text/HTML after the list" +#: includes/Admin/TranslationPicker/Page.php:363 +msgid "Choose a source blog to list untranslated posts." msgstr "" -#: includes/MslsAdmin.php:234 -msgid "Text/HTML before each item" +#: includes/Admin/TranslationPicker/Page.php:391 +msgid "Filter by title…" msgstr "" -#: includes/MslsAdmin.php:235 -msgid "Text/HTML after each item" +#: includes/Admin/TranslationPicker/Page.php:396 +msgid "Apply" msgstr "" -#: includes/MslsAdmin.php:236 -msgid "Available translations hint" +#: includes/Admin/TranslationPicker/Page.php:419 +msgid "Source blog" msgstr "" -#: includes/MslsAdmin.php:237 -msgid "Hint priority" +#: includes/Admin/TranslationPicker/Page.php:423 +msgid "Source blog:" msgstr "" -#: includes/MslsAdmin.php:252 -msgid "Autocomplete" +#: includes/Admin/TranslationPicker/Table.php:96 +#: includes/Frontend/Widget.php:94 +msgid "Title" msgstr "" -#: includes/MslsAdmin.php:253 -msgid "Custom URL for flag-images" +#: includes/Admin/TranslationPicker/Table.php:97 +msgid "Author" msgstr "" -#: includes/MslsAdmin.php:254 -msgid "Reference user" +#: includes/Admin/TranslationPicker/Table.php:104 +msgid "Status" msgstr "" -#: includes/MslsAdmin.php:255 -msgid "Exclude blog" +#: includes/Admin/TranslationPicker/Table.php:105 +msgid "Date" msgstr "" -#: includes/MslsAdmin.php:256 -msgid "Content import" +#: includes/Admin/TranslationPicker/Table.php:150 +msgid "Create drafts for selected" msgstr "" -#: includes/MslsAdmin.php:272 -#, php-format -msgid "%s Slug" +#: includes/Admin/TranslationPicker/Table.php:257 +msgid "View original" +msgstr "" + +#: includes/Admin/TranslationPicker/Table.php:263 +msgid "Create draft" msgstr "" -#: includes/MslsAdmin.php:334 +#: includes/Admin/TranslationPicker/Table.php:275 +msgid "Published" +msgstr "" + +#: includes/Admin/TranslationPicker/Table.php:276 +msgid "Draft" +msgstr "" + +#: includes/Admin/TranslationPicker/Table.php:277 +msgid "Pending" +msgstr "" + +#: includes/Admin/TranslationPicker/Table.php:278 +msgid "Scheduled" +msgstr "" + +#: includes/Admin/TranslationPicker/Table.php:325 +msgid "No posts match your search." +msgstr "" + +#: includes/Admin/TranslationPicker/Table.php:327 +msgid "All source posts are already translated." +msgstr "" + +#. translators: %s: maximum number of users +#: includes/Blog/Collection.php:326 #, php-format -msgid "" -"Multisite Language Switcher: Collection for reference user has been " -"truncated because it exceeded the maximum of %s users. Please, use the hook " -"\"msls_reference_users\" to filter the result before!" +msgid "Multisite Language Switcher: The user list has been limited to %d users." msgstr "" -#: includes/MslsAdminIcon.php:209 +#. translators: %s: locale code, e.g. de_DE. +#: includes/Cli/Cli.php:43 #, php-format -msgid "Create a new translation in the %s-blog" +msgid "No blog with locale %1$s found!" msgstr "" -#: includes/MslsAdminIcon.php:212 +#. translators: 1: blog ID, 2: locale code, e.g. de_DE. +#: includes/Cli/Cli.php:51 #, php-format -msgid "Edit the translation in the %s-blog" +msgid "Blog ID %1$d has locale %2$s!" +msgstr "" + +#: includes/ContentImport/Importers/Attachments/Linking.php:26 +#: includes/ContentImport/Importers/PostThumbnail/Linking.php:27 +msgid "Linking" +msgstr "" + +#: includes/ContentImport/Importers/Attachments/Linking.php:27 +msgid "Links the media attachments from the source post to the destination post; media attachments are not duplicated." +msgstr "" + +#: includes/ContentImport/Importers/Attachments/Linking.php:37 +msgid "Post attachments were left in place in the source blog and linked in the destination post." +msgstr "" + +#: includes/ContentImport/Importers/AttachmentsImporters.php:26 +msgid "Image Attachments" +msgstr "" + +#: includes/ContentImport/Importers/PostFields/Duplicating.php:29 +#: includes/ContentImport/Importers/PostMeta/Duplicating.php:20 +msgid "Duplicating" msgstr "" -#: includes/MslsCustomFilter.php:54 -msgid "Show all blogs" +#: includes/ContentImport/Importers/PostFields/Duplicating.php:30 +msgid "Copies the source post fields to the destination." msgstr "" -#: includes/MslsCustomFilter.php:60 +#: includes/ContentImport/Importers/PostFieldsImporters.php:26 +msgid "Post Fields" +msgstr "" + +#: includes/ContentImport/Importers/PostMeta/Duplicating.php:21 +msgid "Copies the source post meta to the destination." +msgstr "" + +#: includes/ContentImport/Importers/PostMetaImporters.php:26 +msgid "Meta Fields" +msgstr "" + +#: includes/ContentImport/Importers/PostThumbnail/Linking.php:28 +msgid "Links the featured image from the source post to the destination post; the image is not duplicated." +msgstr "" + +#: includes/ContentImport/Importers/PostThumbnailImporters.php:26 +msgid "Featured Image" +msgstr "" + +#: includes/ContentImport/Importers/Terms/ShallowDuplicating.php:34 +msgid "Shallow Duplicating" +msgstr "" + +#: includes/ContentImport/Importers/Terms/ShallowDuplicating.php:35 +msgid "Shallow (one level deep) duplication or assignment of the source post taxonomy terms to the destination post." +msgstr "" + +#: includes/ContentImport/Importers/TermsImporters.php:26 +msgid "Taxonomy Terms" +msgstr "" + +#. translators: %1$d: source post ID, %2$d: source blog ID, %3$d: destination post ID, %4$d: destination blog ID +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:30 #, php-format -msgid "Not translated in the %s-blog" +msgid "From post %1$d on site %2$d to post %3$d on site %4$d" msgstr "" -#: includes/MslsLink.php:47 -msgid "Flag and description" +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:32 +msgid "Multisite Language Switcher last import report" msgstr "" -#: includes/MslsLinkImageOnly.php:26 -msgid "Flag only" +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:41 +msgid "General information" msgstr "" -#: includes/MslsLinkTextImage.php:26 -msgid "Description and flag" +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:47 +msgid "Details" msgstr "" -#: includes/MslsLinkTextOnly.php:26 -msgid "Description only" +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:52 +msgid "The following post fields have been set: " msgstr "" -#: includes/MslsMetaBox.php:140 -msgid "Multisite Language Switcher - Import content" +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:62 +msgid "The following post meta have been set: " msgstr "" -#: includes/MslsMetaBox.php:233 includes/MslsMetaBox.php:338 -msgid "" -"You should define at least another blog in a different language in order to " -"have some benefit from this plugin!" +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:72 +msgid "Terms have been assigned to the post for the following taxonomies: " msgstr "" -#: includes/MslsOptions.php:358 -msgid "American English" +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:82 +msgid "The post thumbnail has been set." msgstr "" -#: includes/MslsPlugin.php:94 +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:89 +msgid "Errors:" +msgstr "" + +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:98 +msgid "There were issues creating or assigning terms for the following taxonomies: " +msgstr "" + +#: includes/ContentImport/LogWriters/AdminNoticeLogger.php:105 +msgid "The post thumbnail could not be created or set." +msgstr "" + +#. translators: %s: language name +#: includes/ContentImport/MetaBox.php:53 #, php-format -msgid "" -"The Multisite Language Switcher needs the activation of the multisite-" -"feature for working properly. Please read this post if you don't know the " -"meaning." +msgid "Import content from %s" +msgstr "" + +#: includes/ContentImport/MetaBox.php:55 +msgid "Warning! This will override and replace all the post content with the content from the source post!" +msgstr "" + +#: includes/ContentImport/MetaBox.php:89 +msgid "No translated versions linked to this post: import content functionality is disabled." +msgstr "" + +#: includes/ContentImport/MetaBox.php:142 +msgid "Select what should be imported and how" +msgstr "" + +#: includes/ContentImport/MetaBox.php:154 +msgid "No importers available for this type of content." +msgstr "" + +#: includes/ContentImport/MetaBox.php:166 +msgid "Off - Do not import this type of content in the destination post." +msgstr "" + +#: includes/ContentImport/MetaBox.php:185 +msgid "Import Content" msgstr "" -#: includes/MslsPlugin.php:171 +#. translators: %s: list of languages +#: includes/Frontend/ContentFilter.php:56 #, php-format msgid "This post is also available in %s." msgstr "" -#: includes/MslsPlugin.php:189 +#. translators: %1$s: list of languages separated by a comma, %2$s: last language +#: includes/Frontend/ContentFilter.php:91 #, php-format -msgid "%s and %s" +msgid "%1$s and %2$s" msgstr "" -#: includes/MslsWidget.php:60 +#: includes/Frontend/Widget.php:57 msgid "No available translations found" msgstr "" -#: includes/MslsWidget.php:96 -msgid "Title" +#: includes/Link/ImageOnly.php:25 +msgid "Flag only" +msgstr "" + +#: includes/Link/Link.php:41 +msgid "Flag and description" +msgstr "" + +#: includes/Link/TextImage.php:25 +msgid "Description and flag" +msgstr "" + +#: includes/Link/TextOnly.php:25 +msgid "Description only" +msgstr "" + +#: includes/Options/Options.php:379 +msgid "American English" +msgstr "" + +#. translators: %s: URL to the WordPress Codex. +#: includes/Plugin.php:112 +#, php-format +msgid "The Multisite Language Switcher needs the activation of the multisite-feature for working properly. Please read this post if you don't know the meaning." +msgstr "" + +#: includes/RestApi/RestApi.php:284 +msgid "Source post not found." +msgstr "" + +#: includes/RestApi/RestApi.php:313 +msgid "Post type does not exist on the target blog." +msgstr "" + +#: includes/RestApi/RestApi.php:398 +msgid "Post type does not exist on the source blog." +msgstr "" + +#. translators: 1: language code, 2: original post title +#: includes/RestApi/RestApi.php:489 +#, php-format +msgid "From %1$s: %2$s" +msgstr "" + +#: assets/js/msls-widget-block/block.json +#: src/msls-widget-block/block.json +msgctxt "block title" +msgid "Multisite Language Switcher" +msgstr "" + +#: assets/js/msls-widget-block/block.json +msgctxt "block description" +msgid "Review the settings for the Multisite Language Switcher plugin, as the block utilizes the API function `the_msls()` for its output." +msgstr "" + +#: src/msls-widget-block/block.json +msgctxt "block description" +msgid "Review the settings for the Multisite Language Switcher plugin, as the block utilizes the API function `msls_the_switcher()` for its output." msgstr "" diff --git a/readme.txt b/readme.txt index 695587f11..de46f3e54 100644 --- a/readme.txt +++ b/readme.txt @@ -32,11 +32,14 @@ Now you can: * Connect your translated pages and posts in `Posts` -> `Edit` or `Pages` -> `Edit` * Connect your translated categories and tags in `Posts` -> `Categories` or `Posts` -> `Tags` * connect your Custom Post Types and Custom Taxonomies across languages +* create a missing translation in one step - straight from the editor metabox, or from the `Add from Translation` submenu, which lists everything that is not translated yet and can create translations in bulk (Quick Create, new in 3.0) * use the widget, the Gutenberg block, the shortcode [sc_msls] and/or a content_filter which displays a hint to the user if a translation is available * you can find also a shortcode for the widget [sc_msls_widget] * optionally you can place the code `` directly in your theme files -Review the [Multisite Language Switcher Website](http://msls.co/) for more information. +Review the [Multisite Language Switcher Website](https://msls.co/) for more information. +Developers will find the API functions, every action and filter, and a set of integration +snippets in the [developer documentation](https://github.com/lloc/Multisite-Language-Switcher/tree/master/docs). == Frequently Asked Questions == @@ -70,11 +73,12 @@ Please check the add-on [MslsSelect](https://wordpress.org/plugins/mslsselect/) Yes, you should use the WordPress API function `get_locale()` but you could also use code like that -`use lloc\Msls\MslsBlogCollection; - -$blog = MslsBlogCollection::instance()->get_current_blog(); +`$blog = msls_blog_collection()->get_current_blog(); $language = $blog->get_language();` +The class behind it moved to `lloc\Msls\Blog\Collection` in version 3.0.0. The old name +`lloc\Msls\MslsBlogCollection` still resolves, so existing code keeps working. + = If I have another question, where can I ask? = Please visit the [MSLS website](https://msls.co/) or use the [WordPress support forum](https://wordpress.org/support/plugin/multisite-language-switcher) for more information. @@ -91,7 +95,42 @@ Please visit the [MSLS website](https://msls.co/) or use the [WordPress support == Changelog == -This project has a separate [Changelog](https://github.com/lloc/Multisite-Language-Switcher/blob/master/Changelog.md). += 3.0.0 = + +* New: Quick Create - create the translated post straight from the editor metabox, or pick a source post on the new `Add from Translation` submenu (single and bulk). Backed by a REST endpoint and switchable in the settings. +* New: `msls_quick_create_capability` lets integrations override the Quick Create permission checks. Additional filters cover the post data, the inserted post, the REST response, the untranslated-posts list and the mapped taxonomy terms. +* New: filter hooks for the AJAX suggest results of the post and term metaboxes. +* New: a developer reference in `docs/` - public API, every hook, integration snippets and the Playwright setup. +* Changed: the `lloc\Msls\` namespace is split into per-concern sub-namespaces. Every pre-3.0 class name (`MslsOptions`, `MslsLink`, `MslsOutput`, ...) keeps working through a backwards-compatibility alias, so add-ons and custom code do not need changes. +* Changed: the public helper functions moved to `includes/api.php`, and the `$attr` argument of `msls_get_switcher()` is now optional. +* Fixed: add-ons such as MslsMenu and MslsSelect could be silently disabled because the plugin registered its classes too late. Aliases and API functions are now available the moment the plugin file is loaded. +* Fixed: authorization is checked on the destination post during content import, and the ContentImporter permission and post type checks were corrected. +* Fixed: no `home_url()` fallback for taxonomy and query archives anymore. +* Fixed: broken links on the page for the latest posts. +* Fixed: several issues in the blog collection. +* Internal: strict typing throughout, PHPStan level 8 clean, `ABSPATH` guards, Plugin Check and PHPCS findings addressed, a wp-env based local multisite and a Playwright end-to-end suite. + += 2.10.1 = + +* Fixed: deprecated function warning pointed to a non-existent function. +* Documentation: README.md code snippets reflect the new function names. + += 2.10.0 = + +* New: prefixed public helper functions (`msls_get_*`, `msls_the_switcher`) with deprecation shims for the legacy names. +* New: action hook names are exposed as constants for safer programmatic use. +* Accessibility: `aria-current="page"` on the active language link. +* Fixed: path-related issue affecting asset/loader resolution. + +The full history is kept in the separate [Changelog](https://github.com/lloc/Multisite-Language-Switcher/blob/master/Changelog.md). + +== Upgrade Notice == + += 3.0.0 = + +Major release. The class structure moved into sub-namespaces, but every pre-3.0 class name +still resolves through a compatibility alias, so add-ons, themes and snippets keep working. +Requirements are unchanged (PHP 7.4, WordPress 6.1). Clear your opcode cache after updating. == Translators == diff --git a/src/msls-widget-block/block.json b/src/msls-widget-block/block.json index be0c4b78c..44895a5e5 100644 --- a/src/msls-widget-block/block.json +++ b/src/msls-widget-block/block.json @@ -5,8 +5,8 @@ "icon": "translation", "category": "widgets", "name": "lloc/msls-widget-block", - "version": "2.10.1", - "description": "Review the settings for the Multisite Language Switcher plugin, as the block utilizes the API function `the_msls()` for its output.", + "version": "3.0.0", + "description": "Review the settings for the Multisite Language Switcher plugin, as the block utilizes the API function `msls_the_switcher()` for its output.", "example": {}, "supports": { "html": false