Skip to content
Open
10 changes: 9 additions & 1 deletion .distignore
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
.DS_Store
.github
.wordpress-org
__build
assets/src
dev-tools
.distignore
.gitignore
composer.json
composer.lock
node_modules
package.json
package-lock.json
pot.js
postcss.config.js
README.md
webpack.config.js
webpack-entry-list.js
webpack.common.js
webpack.dev.js
webpack.prod.js
webpack.prod.js
yarn.lock
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ Official WPML integration extension for [Directorist](https://directorist.com) t

- Syncs Directorist listings, directory types, categories, locations and tags across WPML languages.
- Makes Directorist settings, search forms, widgets/blocks and email templates translatable via WPML String Translation.
- Automatically syncs category `_directory_type` meta and directory `_default` flags across translations (as of 2.2.1).
- Tested with WordPress 6.9, PHP 7.4+, and the latest WPML core and addons.
- Automatically syncs category and listing `_directory_type` meta plus directory `_default` flags across translations.
- Tested with WordPress 6.9, PHP 7.4+, Directorist 8.7.1, WPML 4.9.2.1, and WP-CLI.
- Verified with a 138-check WP-CLI compatibility suite across English, French, German, Romanian, and Bengali.

## Changelog (short)

- **2.2.0 (2026‑02‑05)**
- Added automatic syncing of Directorist category directory assignments across WPML languages.
- Added WPML config for copying the default directory type flag across translations.
- **2.2.3 (2026-05-06)**
- Fixed translated listing visibility for all-listing and search-result queries.
- Fixed directory type meta synchronization across WPML translation groups.
- Added WP-CLI compatibility verification across `en`, `fr`, `de`, `ro`, and `bn`.

- **2.2.1 (2026-02-05)**
- Added automatic syncing of Directorist category directory assignments across WPML languages.
- Added WPML config for copying the default directory type flag across translations.
- Improved overall compatibility with WordPress 6.8 and current WPML releases.

For full details, see `readme.txt` or the [plugin page](https://github.com/sovware/directorist-wpml-integration).
15 changes: 7 additions & 8 deletions app/Controller/Ajax/Get_Directory_Type_Translations.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function create_directory_type_translation() {
wp_send_json( $response->toArray() );
}

$directory_type_id = ( isset( $_REQUEST['directory_type_id'] ) ) ? sanitize_text_field( $_REQUEST['directory_type_id'] ) : 0;
$directory_type_id = isset( $_REQUEST['directory_type_id'] ) ? absint( $_REQUEST['directory_type_id'] ) : 0;
$taranslation_language_code = ( isset( $_REQUEST['taranslation_language_code'] ) ) ? sanitize_text_field( $_REQUEST['taranslation_language_code'] ) : '';

if ( empty( $directory_type_id ) ) {
Expand Down Expand Up @@ -134,8 +134,7 @@ public function get_wpml_active_languages() {
* @return array
*/
public function get_directory_type_translations() {
$taxonomy = ATBDP_DIRECTORY_TYPE;
$element_type = apply_filters( 'wpml_element_type', $taxonomy );
$taxonomy = ATBDP_DIRECTORY_TYPE;

$directory_types = get_terms([
'taxonomy' => $taxonomy,
Expand All @@ -149,13 +148,13 @@ public function get_directory_type_translations() {
$directory_type_translations = [];

foreach( $directory_types as $directory_type ) {
$translation_id = apply_filters( 'wpml_element_trid', NULL, $directory_type->term_id, $element_type );
$translation = apply_filters( 'wpml_get_element_translations', NULL, $translation_id, $element_type );

$directory_type_translations[ $directory_type->term_id ] = $translation;
$directory_type_translations[ $directory_type->term_id ] = WPML_Helper::get_element_translations(
$directory_type->term_id,
$taxonomy
);
}

return $directory_type_translations;
}

}
}
75 changes: 61 additions & 14 deletions app/Controller/Hook/Add_Listing_Form_Translation.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Directorist_WPML_Integration\Controller\Hook;

use Directorist_WPML_Integration\Helper\WPML_Helper;

class Add_Listing_Form_Translation {

/**
Expand Down Expand Up @@ -124,6 +126,27 @@ private function safe_slug( $text ) {
return sanitize_key( sanitize_title( $text ) );
}

/**
* Get a stable WPML context ID for a directory type.
*
* Using the translation group ID keeps one set of strings shared across all
* language variants of the same directory type.
*
* @param int $directory_id Directory term ID.
* @return int
*/
private function get_directory_context_id( $directory_id ) {
$directory_id = (int) $directory_id;

if ( $directory_id <= 0 ) {
return 0;
}

$translation_group_id = WPML_Helper::get_element_trid( $directory_id, ATBDP_DIRECTORY_TYPE );

return $translation_group_id > 0 ? $translation_group_id : $directory_id;
}

/**
* Register string with WPML
*
Expand Down Expand Up @@ -152,9 +175,10 @@ private function register_wpml_string( $name, $value ) {
// This happens when WPML processes Gutenberg block configurations during string registration
ob_start();

// Suppress warnings during registration
// Suppress WPML internals during registration. Some WPML versions emit
// PHP 8.x deprecations while sanitizing package/string metadata.
$error_level = error_reporting();
error_reporting( $error_level & ~E_WARNING );
error_reporting( $error_level & ~E_WARNING & ~E_DEPRECATED );

try {
// Use WPML String Translation function
Expand Down Expand Up @@ -189,8 +213,16 @@ private function translate_wpml_string( $value, $name ) {
return $value;
}

// Use WPML String Translation filter hook
$translated = apply_filters( 'wpml_translate_single_string', $value, self::WPML_DOMAIN, $name );
// Use WPML String Translation filter hook. Some WPML versions emit PHP
// 8.x deprecations while resolving untranslated strings.
$error_level = error_reporting();
error_reporting( $error_level & ~E_WARNING & ~E_DEPRECATED );

try {
$translated = apply_filters( 'wpml_translate_single_string', $value, self::WPML_DOMAIN, $name );
} finally {
error_reporting( $error_level );
}

// If translation is empty or same as original, return original
// This ensures we never lose the label even if translation is empty
Expand Down Expand Up @@ -232,6 +264,11 @@ public function translate_field_data( $field_data ) {
return $field_data;
}

$directory_context_id = $this->get_directory_context_id( $directory_id );
if ( empty( $directory_context_id ) ) {
return $field_data;
}

// Get field key
$field_key = ! empty( $field_data['field_key'] ) ? $field_data['field_key'] : '';
if ( empty( $field_key ) ) {
Expand All @@ -242,7 +279,7 @@ public function translate_field_data( $field_data ) {

// Translate field label
if ( ! empty( $field_data['label'] ) && is_string( $field_data['label'] ) ) {
$string_name = sprintf( 'add_listing_dir_%d_field_%s_label', $directory_id, $field_key_slug );
$string_name = sprintf( 'add_listing_dir_%d_field_%s_label', $directory_context_id, $field_key_slug );

$this->register_wpml_string( $string_name, $field_data['label'] );
$translated = $this->translate_wpml_string( $field_data['label'], $string_name );
Expand All @@ -254,7 +291,7 @@ public function translate_field_data( $field_data ) {

// Translate field placeholder
if ( ! empty( $field_data['placeholder'] ) && is_string( $field_data['placeholder'] ) ) {
$string_name = sprintf( 'add_listing_dir_%d_field_%s_placeholder', $directory_id, $field_key_slug );
$string_name = sprintf( 'add_listing_dir_%d_field_%s_placeholder', $directory_context_id, $field_key_slug );

$this->register_wpml_string( $string_name, $field_data['placeholder'] );
$translated = $this->translate_wpml_string( $field_data['placeholder'], $string_name );
Expand All @@ -266,7 +303,7 @@ public function translate_field_data( $field_data ) {

// Translate field description
if ( ! empty( $field_data['description'] ) && is_string( $field_data['description'] ) ) {
$string_name = sprintf( 'add_listing_dir_%d_field_%s_description', $directory_id, $field_key_slug );
$string_name = sprintf( 'add_listing_dir_%d_field_%s_description', $directory_context_id, $field_key_slug );

$this->register_wpml_string( $string_name, $field_data['description'] );
$translated = $this->translate_wpml_string( $field_data['description'], $string_name );
Expand Down Expand Up @@ -310,7 +347,7 @@ public function translate_field_data( $field_data ) {
];

if ( $is_translatable || in_array( $property_key, $known_custom_properties ) ) {
$string_name = sprintf( 'add_listing_dir_%d_field_%s_%s', $directory_id, $field_key_slug, $this->safe_slug( $property_key ) );
$string_name = sprintf( 'add_listing_dir_%d_field_%s_%s', $directory_context_id, $field_key_slug, $this->safe_slug( $property_key ) );

$this->register_wpml_string( $string_name, $property_value );
$translated = $this->translate_wpml_string( $property_value, $string_name );
Expand All @@ -332,7 +369,7 @@ public function translate_field_data( $field_data ) {

$string_name = sprintf(
'add_listing_dir_%d_field_%s_option_%s',
$directory_id,
$directory_context_id,
$field_key_slug,
$option_value_slug
);
Expand All @@ -350,7 +387,7 @@ public function translate_field_data( $field_data ) {

$string_name = sprintf(
'add_listing_dir_%d_field_%s_option_%s',
$directory_id,
$directory_context_id,
$field_key_slug,
$option_slug
);
Expand Down Expand Up @@ -402,6 +439,11 @@ public function translate_section_label( $load_section, $args ) {
return $load_section;
}

$directory_context_id = $this->get_directory_context_id( $directory_id );
if ( empty( $directory_context_id ) ) {
return $load_section;
}

// Translate section label
if ( ! empty( $args['section_data']['label'] ) && is_string( $args['section_data']['label'] ) ) {
$original_label = $args['section_data']['label'];
Expand All @@ -411,15 +453,15 @@ public function translate_section_label( $load_section, $args ) {
? $this->safe_slug( $args['section_data']['key'] )
: $this->safe_slug( $original_label );

$string_name = sprintf( 'add_listing_dir_%d_section_%s_label', $directory_id, $section_slug );
$string_name = sprintf( 'add_listing_dir_%d_section_%s_label', $directory_context_id, $section_slug );

// Register and translate
$this->register_wpml_string( $string_name, $original_label );
$translated = $this->translate_wpml_string( $original_label, $string_name );

// Store translation for output replacement (since $args is passed by value)
// Always store even if same, so output replacement can use it
$translation_key = sprintf( '%d_%s', $directory_id, $section_slug );
$translation_key = sprintf( '%d_%s', $directory_context_id, $section_slug );
self::$section_translations[ $translation_key ] = [
'original' => $original_label,
'translated' => $translated,
Expand Down Expand Up @@ -514,6 +556,11 @@ public function translate_section_labels_in_output( $template_output, $args ) {
return $template_output;
}

$directory_context_id = $this->get_directory_context_id( $directory_id );
if ( empty( $directory_context_id ) ) {
return $template_output;
}

// Translate all section labels in output
// If form_data is empty, we'll still try to translate common section names
$sections_to_translate = [];
Expand Down Expand Up @@ -547,8 +594,8 @@ public function translate_section_labels_in_output( $template_output, $args ) {
? $this->safe_slug( $section['key'] )
: $this->safe_slug( $original_label );

$string_name = sprintf( 'add_listing_dir_%d_section_%s_label', $directory_id, $section_slug );
$translation_key = sprintf( '%d_%s', $directory_id, $section_slug );
$string_name = sprintf( 'add_listing_dir_%d_section_%s_label', $directory_context_id, $section_slug );
$translation_key = sprintf( '%d_%s', $directory_context_id, $section_slug );

// Check if we already have translation stored from translate_section_label()
$translated = null;
Expand Down
21 changes: 7 additions & 14 deletions app/Controller/Hook/Directory_Builder_Actions.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,7 @@ public function before_update_directory_type( $directory_type_id = 0 ) {

set_transient( 'directorist_wpml_integration:current_language', $current_language );

$element_type = ATBDP_DIRECTORY_TYPE;
$wpml_element_type = apply_filters( 'wpml_element_type', $element_type );

// get the language info of the original post
$get_language_args = [
'element_id' => $directory_type_id,
'element_type' => $wpml_element_type
];

$original_post_language_info = apply_filters( 'wpml_element_language_details', null, $get_language_args );
$original_post_language_info = WPML_Helper::get_language_info( $directory_type_id, ATBDP_DIRECTORY_TYPE );

if ( empty( $original_post_language_info ) ) {
return;
Expand Down Expand Up @@ -105,16 +96,18 @@ public function after_update_default_directory_type( $directory_type_id = 0 ) {

delete_transient( 'directorist_wpml_integration:current_language' );

$element_type = apply_filters( 'wpml_element_type', ATBDP_DIRECTORY_TYPE );
$translation_id = apply_filters( 'wpml_element_trid', NULL, $directory_type_id, $element_type );
$translations = apply_filters( 'wpml_get_element_translations', NULL, $translation_id, $element_type );
$translations = WPML_Helper::get_element_translations( $directory_type_id, ATBDP_DIRECTORY_TYPE );

if ( empty( $translations ) ) {
return;
}

foreach( $translations as $translation ) {
update_term_meta( $translation->term_id, '_default', true );
if ( empty( $translation->term_id ) ) {
continue;
}

update_term_meta( (int) $translation->term_id, '_default', true );
}
}
}
Loading