diff --git a/.changelogs/fix_i18n-dashboard-language-and-slugs-1.yml b/.changelogs/fix_i18n-dashboard-language-and-slugs-1.yml
new file mode 100644
index 0000000000..b1bbee63f7
--- /dev/null
+++ b/.changelogs/fix_i18n-dashboard-language-and-slugs-1.yml
@@ -0,0 +1,7 @@
+significance: minor
+type: added
+links:
+ - "#2429"
+ - "#2525"
+entry: Loads translation files later for compatibility with plugins like Loco
+ Translate.
diff --git a/.changelogs/fix_i18n-dashboard-language-and-slugs-2.yml b/.changelogs/fix_i18n-dashboard-language-and-slugs-2.yml
new file mode 100644
index 0000000000..a783b76c60
--- /dev/null
+++ b/.changelogs/fix_i18n-dashboard-language-and-slugs-2.yml
@@ -0,0 +1,5 @@
+significance: patch
+type: fixed
+links:
+ - "#2525"
+entry: LifterLMS block editor strings now appear in the user's language.
diff --git a/.changelogs/fix_i18n-dashboard-language-and-slugs-3.yml b/.changelogs/fix_i18n-dashboard-language-and-slugs-3.yml
new file mode 100644
index 0000000000..fa2f86673c
--- /dev/null
+++ b/.changelogs/fix_i18n-dashboard-language-and-slugs-3.yml
@@ -0,0 +1,4 @@
+significance: minor
+type: added
+entry: Adds settings in the Permalinks page to edit the custom post type and
+ taxonomy slugs. Slugs are saved in the site language on install on update.
diff --git a/.changelogs/fix_i18n-dashboard-language-and-slugs-4.yml b/.changelogs/fix_i18n-dashboard-language-and-slugs-4.yml
new file mode 100644
index 0000000000..8ba53bd7d1
--- /dev/null
+++ b/.changelogs/fix_i18n-dashboard-language-and-slugs-4.yml
@@ -0,0 +1,5 @@
+significance: patch
+type: added
+entry: Adds `llms_switch_to_site_locale` and `llms_restore_locale` to help
+ LifterLMS add-ons switch to the site language when getting translation
+ strings.
diff --git a/.changelogs/fix_i18n-dashboard-language-and-slugs.yml b/.changelogs/fix_i18n-dashboard-language-and-slugs.yml
new file mode 100644
index 0000000000..b0a7147b3a
--- /dev/null
+++ b/.changelogs/fix_i18n-dashboard-language-and-slugs.yml
@@ -0,0 +1,5 @@
+significance: patch
+type: fixed
+links:
+ - "#2324"
+entry: Fixed user's language setting not honored on backend.
diff --git a/class-lifterlms.php b/class-lifterlms.php
index 4cc9bf0b38..87b6229889 100644
--- a/class-lifterlms.php
+++ b/class-lifterlms.php
@@ -68,20 +68,12 @@ final class LifterLMS {
* @since 5.3.0 Move the loading of the LifterLMS autoloader to the main `lifterlms.php` file.
* @since 6.1.0 Automatically load payment gateways.
* @since 6.4.0 Moved registration of `LLMS_Shortcodes::init()` with the 'init' hook to `LLMS_Shortcodes::__construct()`.
+ * @since [version] Lood locale textdomain on `init` instead of immediately
*
* @return void
*/
private function __construct() {
- /**
- * Localize as early as possible.
- *
- * Since 4.6 the "just_in_time" l10n will load the default (not custom) file first
- * so we must localize before any l10n functions (like `__()`) are used
- * so that our custom "safe" location will always load first.
- */
- $this->localize();
-
$this->define_constants();
$this->init_assets();
@@ -89,9 +81,9 @@ private function __construct() {
$this->query = new LLMS_Query();
// Hooks.
- register_activation_hook( __FILE__, array( 'LLMS_Install', 'install' ) );
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'add_action_links' ), 10, 1 );
+ add_action( 'init', array( $this, 'localize' ), 0 );
add_action( 'init', array( $this, 'init' ), 0 );
add_action( 'init', array( $this, 'integrations' ), 1 );
add_action( 'init', array( $this, 'processors' ), 5 );
@@ -415,9 +407,10 @@ public function add_action_links( $links ) {
*/
public function localize() {
- require_once LLMS_PLUGIN_DIR . 'includes/functions/llms-functions-l10n.php';
llms_load_textdomain( 'lifterlms' );
}
}
+
+
diff --git a/includes/admin/class-llms-admin-permalinks.php b/includes/admin/class-llms-admin-permalinks.php
new file mode 100644
index 0000000000..1b9db173f9
--- /dev/null
+++ b/includes/admin/class-llms-admin-permalinks.php
@@ -0,0 +1,269 @@
+id ) {
+ $this->settings_init();
+ $this->settings_save();
+ }
+ }
+
+ /**
+ * Show the available permalink settings
+ */
+ public function settings_init() {
+ add_settings_section( 'lifterlms-permalink', __( 'LifterLMS Permalinks', 'lifterlms' ), array( $this, 'settings' ), 'permalink' );
+
+ $this->permalinks = llms_get_permalink_structure();
+ }
+
+ public function settings() {
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false,
'hierarchical' => false,
'rewrite' => array(
- 'slug' => _x( 'course', 'course url slug', 'lifterlms' ),
+ 'slug' => $permalinks['course_base'],
'with_front' => false,
'feeds' => true,
),
'query_var' => true,
'supports' => array( 'title', 'author', 'editor', 'thumbnail', 'comments', 'custom-fields', 'page-attributes', 'revisions', 'llms-clone-post', 'llms-export-post', 'llms-sales-page' ),
- 'has_archive' => ( $catalog_id && get_post( $catalog_id ) ) ? get_page_uri( $catalog_id ) : _x( 'courses', 'course archive url slug', 'lifterlms' ),
+ 'has_archive' => ( $catalog_id && get_post( $catalog_id ) ) ? get_page_uri( $catalog_id ) : $permalinks['courses_base'],
'show_in_nav_menus' => true,
'menu_position' => 52,
)
@@ -552,7 +553,7 @@ public static function register_post_types() {
'show_in_menu' => 'edit.php?post_type=course',
'hierarchical' => false,
'rewrite' => array(
- 'slug' => _x( 'lesson', 'lesson url slug', 'lifterlms' ),
+ 'slug' => $permalinks['lesson_base'],
'with_front' => false,
'feeds' => true,
),
@@ -591,7 +592,7 @@ public static function register_post_types() {
'show_in_menu' => 'edit.php?post_type=course',
'hierarchical' => false,
'rewrite' => array(
- 'slug' => _x( 'quiz', 'quiz url slug', 'lifterlms' ),
+ 'slug' => $permalinks['quiz_base'],
'with_front' => false,
'feeds' => true,
),
@@ -673,7 +674,7 @@ public static function register_post_types() {
),
'query_var' => true,
'supports' => array( 'title', 'editor', 'thumbnail', 'comments', 'custom-fields', 'page-attributes', 'revisions', 'llms-sales-page' ),
- 'has_archive' => ( $membership_page_id && get_post( $membership_page_id ) ) ? get_page_uri( $membership_page_id ) : _x( 'memberships', 'membership archive url slug', 'lifterlms' ),
+ 'has_archive' => ( $membership_page_id && get_post( $membership_page_id ) ) ? get_page_uri( $membership_page_id ) : $permalinks['memberships_base'],
'show_in_nav_menus' => true,
'menu_position' => 52,
)
@@ -902,7 +903,7 @@ public static function register_post_types() {
array(
'map_meta_cap' => true,
),
- _x( 'certificate-template', 'slug', 'lifterlms' ),
+ $permalinks['certificate_template_base'],
/**
* Filters the WordPress user capability required for a user to manage certificate templates on the admin panel.
*
@@ -935,7 +936,7 @@ public static function register_post_types() {
'capabilities' => self::get_post_type_caps( 'my_certificate' ),
'map_meta_cap' => false,
),
- _x( 'certificate', 'slug', 'lifterlms' ),
+ $permalinks['certificate_base'],
/**
* Filters the needed capability to generate and allow a UI for managing `llms_my_certificate` post type in the admin.
*
@@ -1275,6 +1276,8 @@ public static function register_taxonomy( $name, $object, $data ) {
*/
public static function register_taxonomies() {
+ $permalinks = llms_get_permalink_structure();
+
// Course cat.
self::register_taxonomy(
'course_cat',
@@ -1300,7 +1303,7 @@ public static function register_taxonomies() {
'show_admin_column' => true,
'show_ui' => true,
'rewrite' => array(
- 'slug' => _x( 'course-category', 'slug', 'lifterlms' ),
+ 'slug' => $permalinks['course_category_base'],
'with_front' => false,
'hierarchical' => true,
),
@@ -1333,7 +1336,7 @@ public static function register_taxonomies() {
'show_admin_column' => true,
'show_ui' => true,
'rewrite' => array(
- 'slug' => _x( 'course-difficulty', 'slug', 'lifterlms' ),
+ 'slug' => $permalinks['course_difficulty_base'],
'with_front' => false,
),
'show_in_llms_rest' => true,
@@ -1365,7 +1368,7 @@ public static function register_taxonomies() {
'show_admin_column' => true,
'show_ui' => true,
'rewrite' => array(
- 'slug' => _x( 'course-tag', 'slug', 'lifterlms' ),
+ 'slug' => $permalinks['course_tag_base'],
'with_front' => false,
),
'show_in_llms_rest' => true,
@@ -1397,7 +1400,7 @@ public static function register_taxonomies() {
'show_admin_column' => true,
'show_ui' => true,
'rewrite' => array(
- 'slug' => _x( 'course-track', 'slug', 'lifterlms' ),
+ 'slug' => $permalinks['course_track_base'],
'with_front' => false,
'hierarchical' => true,
),
@@ -1431,7 +1434,7 @@ public static function register_taxonomies() {
'query_var' => true,
'show_admin_column' => true,
'rewrite' => array(
- 'slug' => _x( 'membership-category', 'slug', 'lifterlms' ),
+ 'slug' => $permalinks['membership_category_base'],
'with_front' => false,
'hierarchical' => true,
),
@@ -1465,7 +1468,7 @@ public static function register_taxonomies() {
'query_var' => true,
'show_admin_column' => true,
'rewrite' => array(
- 'slug' => _x( 'membership-tag', 'slug', 'lifterlms' ),
+ 'slug' => $permalinks['membership_tag_base'],
'with_front' => false,
),
'show_in_llms_rest' => true,
diff --git a/includes/functions/llms-functions-l10n.php b/includes/functions/llms-functions-l10n.php
index 5ec7a2cb41..382e615baa 100644
--- a/includes/functions/llms-functions-l10n.php
+++ b/includes/functions/llms-functions-l10n.php
@@ -20,12 +20,7 @@
*/
function llms_get_locale( $domain = 'lifterlms' ) {
- if ( function_exists( 'determine_locale' ) ) {
- $locale = determine_locale();
- } else {
- // @TODO: This can be removed when minimum supported version is 5.0.
- $locale = is_admin() ? get_user_locale() : get_locale();
- }
+ $locale = determine_locale();
/**
* Filter the plugin's locale
@@ -96,6 +91,8 @@ function llms_load_textdomain( $domain, $plugin_dir = null, $language_dir = null
$plugin_dir = $plugin_dir ? $plugin_dir : LLMS_PLUGIN_DIR;
$language_dir = $language_dir ? $language_dir : 'languages';
+ unload_textdomain( $domain );
+
/**
* Load from the custom LifterLMS "safe" directory (if it exists).
*
@@ -112,3 +109,109 @@ function llms_load_textdomain( $domain, $plugin_dir = null, $language_dir = null
load_plugin_textdomain( $domain, false, sprintf( '%1$s/%2$s', basename( $plugin_dir ), $language_dir ) );
}
+
+/**
+ * Retrieve the current permalink structure. If no structure is set, the default structure is returned.
+ *
+ * Note: this should be called on install or update of LifterLMS at a time when the site language is known and set.
+ *
+ * @since [version]
+ *
+ * @return array
+ */
+function llms_get_permalink_structure() {
+ $saved_permalinks = (array) get_option( 'llms_permalinks', array() );
+
+ $permalinks = wp_parse_args(
+ // Remove false or empty entries so we can use the default values.
+ array_filter( $saved_permalinks ),
+ array(
+ 'course_base' => _x( 'course', 'course url slug', 'lifterlms' ),
+ 'courses_base' => _x( 'courses', 'course archive url slug', 'lifterlms' ),
+ 'memberships_base' => _x( 'memberships', 'membership archive url slug', 'lifterlms' ),
+ 'lesson_base' => _x( 'lesson', 'lesson url slug', 'lifterlms' ),
+ 'quiz_base' => _x( 'quiz', 'quiz url slug', 'lifterlms' ),
+ 'certificate_template_base' => _x( 'certificate-template', 'slug', 'lifterlms' ),
+ 'certificate_base' => _x( 'certificate', 'slug', 'lifterlms' ),
+ 'course_category_base' => _x( 'course-category', 'slug', 'lifterlms' ),
+ 'course_tag_base' => _x( 'course-tag', 'slug', 'lifterlms' ),
+ 'course_track_base' => _x( 'course-track', 'slug', 'lifterlms' ),
+ 'course_difficulty_base' => _x( 'course-difficulty', 'slug', 'lifterlms' ),
+ 'membership_category_base' => _x( 'membership-category', 'slug', 'lifterlms' ),
+ 'membership_tag_base' => _x( 'membership-tag', 'slug', 'lifterlms' ),
+ )
+ );
+
+ array_filter( $permalinks, 'untrailingslashit' );
+
+ if ( $saved_permalinks !== $permalinks ) {
+ update_option( 'llms_permalinks', $permalinks );
+ }
+
+ return $permalinks;
+};
+/**
+ * Set the permalink structure and only allow keys we know about.
+ *
+ * @since [version]
+ *
+ * @param array $permalinks
+ *
+ * @return void
+ */
+function llms_set_permalink_structure( $permalinks ) {
+ $defaults = llms_get_permalink_structure();
+
+ $permalinks = wp_parse_args(
+ // Only allow values whose keys are in the defaults array.
+ array_intersect_key( $permalinks, $defaults ),
+ $defaults
+ );
+
+ array_filter( $permalinks, 'untrailingslashit' );
+
+ update_option( 'llms_permalinks', $permalinks );
+}
+
+/**
+ * Switch LifterLMS language to site language.
+ *
+ * @param string $textdomain Text domain. Defaults to lifterlms.
+ * @param string $plugin_dir Plugin directory. Defaults to null.
+ * @param string $language_dir Language directory. Defaults to null.
+ *
+ * @since [version]
+ */
+function llms_switch_to_site_locale( $textdomain = 'lifterlms', $plugin_dir = null, $language_dir = null ) {
+ global $wp_locale_switcher;
+
+ if ( function_exists( 'switch_to_locale' ) && isset( $wp_locale_switcher ) ) {
+ switch_to_locale( get_locale() );
+
+ // Filter on plugin_locale so load_plugin_textdomain loads the correct locale.
+ add_filter( 'plugin_locale', 'get_locale' );
+
+ llms_load_textdomain( $textdomain, $plugin_dir, $language_dir );
+ }
+}
+
+/**
+ * Switch LifterLMS language to original.
+ *
+ * @param string $textdomain Text domain. Defaults to lifterlms.
+ * @param string $plugin_dir Plugin directory. Defaults to null.
+ * @param string $language_dir Language directory. Defaults to null.
+ *
+ * @since [version]
+ */
+function llms_restore_locale( $textdomain = 'lifterlms', $plugin_dir = null, $language_dir = null ) {
+ global $wp_locale_switcher;
+
+ if ( function_exists( 'restore_previous_locale' ) && isset( $wp_locale_switcher ) ) {
+ restore_previous_locale();
+
+ remove_filter( 'plugin_locale', 'get_locale' );
+
+ llms_load_textdomain( $textdomain, $plugin_dir, $language_dir );
+ }
+}
diff --git a/includes/llms.functions.core.php b/includes/llms.functions.core.php
index 5343de2f32..78561fa7f4 100644
--- a/includes/llms.functions.core.php
+++ b/includes/llms.functions.core.php
@@ -10,6 +10,8 @@
defined( 'ABSPATH' ) || exit;
+require_once 'functions/llms-functions-l10n.php';
+
require_once 'functions/llms-functions-access-plans.php';
require_once 'functions/llms-functions-deprecated.php';
require_once 'functions/llms-functions-forms.php';
diff --git a/lifterlms.php b/lifterlms.php
index 4840a763fd..139f6f3638 100644
--- a/lifterlms.php
+++ b/lifterlms.php
@@ -52,6 +52,8 @@
require_once LLMS_PLUGIN_DIR . 'class-lifterlms.php';
}
+register_activation_hook( __FILE__, array( 'LLMS_Install', 'install' ) );
+
/**
* Returns the main instance of LifterLMS
*
diff --git a/packages/llms-e2e-test-utils/src/visit-page.js b/packages/llms-e2e-test-utils/src/visit-page.js
index 7cd6b53c87..da90927fb9 100644
--- a/packages/llms-e2e-test-utils/src/visit-page.js
+++ b/packages/llms-e2e-test-utils/src/visit-page.js
@@ -9,5 +9,5 @@ const { createURL } = require( '@wordpress/e2e-test-utils' );
* @return {void}
*/
export async function visitPage( path, query ) {
- await page.goto( createURL( path, query ) );
+ return await page.goto( createURL( path, query ) );
}
diff --git a/tests/phpunit/unit-tests/class-llms-test-assets.php b/tests/phpunit/unit-tests/class-llms-test-assets.php
index 10417bdb15..1b78ea0f5b 100644
--- a/tests/phpunit/unit-tests/class-llms-test-assets.php
+++ b/tests/phpunit/unit-tests/class-llms-test-assets.php
@@ -723,64 +723,6 @@ public function test_register_script_undefined() {
}
- /**
- * Test register_script() with translations
- *
- * @since 5.5.0
- *
- * @return void
- */
- public function test_register_script_with_translations() {
-
- // LLMS_PLUGIN_URL gets messed up in the testing environment.
- $handler = function( $defaults ) {
- $defaults['base_url'] = plugins_url() . '/lifterlms';
- return $defaults;
- };
- add_filter( 'llms_get_script_asset_defaults', $handler );
-
-
- $handle = 'llms-test-messages';
- $file = 'assets/js/llms-test-messages.js';
- $md5 = md5( $file );
- $json = file_get_contents( LLMS_Unit_Test_Files::get_asset_path( sprintf( 'lifterlms-en_US-%s.json', $md5 ) ) );
-
- $scripts = array( $handle => array( 'translate' => true ) );
- $this->main->define( 'scripts', $scripts );
-
- $dirs = array(
- WP_LANG_DIR . '/lifterlms', // "Safe" directory.
- WP_LANG_DIR . '/plugins', // Default language directory.
- plugin_dir_path( LLMS_PLUGIN_FILE ) . 'languages', // Plugin language directory.
- );
-
- foreach ( $dirs as $dir ) {
-
- // Load a language file.
- $file = LLMS_Unit_Test_Files::copy_asset( sprintf( 'lifterlms-en_US-%s.json', $md5 ), $dir );
- $this->main->register_script( $handle );
-
- // The script's translation path should be the intended directory.
- $this->assertEquals( $dir, wp_scripts()->registered[ $handle ]->translations_path, $dir );
-
- // If we load the script's textdomain we'll see JSON matching the mock file.
- $this->assertEquals( $json, load_script_textdomain( $handle, 'lifterlms', $dir ), $dir );
-
- // Clean up.
- LLMS_Unit_Test_Files::remove( $file );
- wp_deregister_script( $handle );
-
- }
-
- // No files found.
- $this->main->register_script( $handle );
- $this->assertNull( wp_scripts()->registered[ $handle ]->translations_path );
- $this->assertFalse( load_script_textdomain( $handle, 'lifterlms' ) );
-
- remove_filter( 'llms_get_script_asset_defaults', $handler );
-
- }
-
/**
* Test register_style() for a custom asset (added via a filter)
*