Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .distignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
.distignore
.gitattributes
.gitignore
Expand All @@ -13,6 +14,7 @@
/.phpunit.cache
/.vscode
/.wordpress-org
/artifacts
/bin
/build
/docs
Expand Down
7 changes: 7 additions & 0 deletions .phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
<ruleset name="CS">
<description>PHPCS MSLS configuration</description>
<config name="testVersion" value="7.4-"/>

<file>MultisiteLanguageSwitcher.php</file>
<file>config.php</file>
<file>index.php</file>
<file>includes</file>
<file>src</file>

<exclude-pattern>assets/*</exclude-pattern>
<exclude-pattern>bin/*</exclude-pattern>
<exclude-pattern>images/*</exclude-pattern>
Expand Down
5 changes: 3 additions & 2 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
* 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.
* Fix: broken links on the page for the latest posts.
* 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
Expand All @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions assets/css-flags/flags.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
6 changes: 3 additions & 3 deletions assets/flags/flags.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
Expand All @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
8 changes: 4 additions & 4 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
24 changes: 15 additions & 9 deletions docs/e2e-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 3 additions & 2 deletions docs/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 11 additions & 3 deletions docs/snippets.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions includes/Admin/Icon.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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(
Expand Down
7 changes: 4 additions & 3 deletions includes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Loading