From 65d51a89bff8281bdf7b0793705eacc4ceba4c9c Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Fri, 17 Jul 2026 21:13:36 +0600 Subject: [PATCH 1/8] fix(locale): allow generating content in other languages (#166) - Add fakerpress_get_available_locales() helper with 10 curated locales - Thread locale through Abstract_Module::get_faker() to Faker Factory - Add locale param to all 5 REST endpoints (Posts, Users, Comments, Terms, Attachments) - Add locale dropdown to all 5 admin form templates - Update clean-faker-lang composer script to preserve locale directories Falls back to en_US for unknown or unavailable locales. Fixes #166 --- composer.json | 2 +- src/FakerPress/Module/Abstract_Module.php | 31 ++++++++++++++++- src/FakerPress/Module/Attachment.php | 2 ++ src/FakerPress/Module/Comment.php | 2 ++ src/FakerPress/Module/Post.php | 2 ++ src/FakerPress/Module/Term.php | 2 ++ src/FakerPress/Module/User.php | 2 ++ src/FakerPress/Plugin.php | 1 + src/FakerPress/REST/Abstract_Endpoint.php | 27 +++++++++++++++ src/FakerPress/REST/Endpoints/Attachments.php | 3 +- src/FakerPress/REST/Endpoints/Comments.php | 3 +- src/FakerPress/REST/Endpoints/Posts.php | 3 +- src/FakerPress/REST/Endpoints/Terms.php | 3 +- src/FakerPress/REST/Endpoints/Users.php | 3 +- src/FakerPress/functions/locale.php | 33 +++++++++++++++++++ src/templates/pages/attachments.php | 21 ++++++++++++ src/templates/pages/comments.php | 21 ++++++++++++ src/templates/pages/posts.php | 21 ++++++++++++ src/templates/pages/terms.php | 21 ++++++++++++ src/templates/pages/users.php | 21 ++++++++++++ 20 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 src/FakerPress/functions/locale.php diff --git a/composer.json b/composer.json index 882060a..f9ad761 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,7 @@ "test -f ./bin/strauss.phar || curl -o bin/strauss.phar -L -C - https://github.com/BrianHenryIE/strauss/releases/download/0.26.5/strauss.phar" ], "clean-faker-lang": [ - "[ -d './vendor/fakerphp/faker/src/Faker/Provider' ] && echo 'Removing folders:' && find ./vendor/fakerphp/faker/src/Faker/Provider -mindepth 1 -maxdepth 1 -type d ! -name 'en_US' -print -exec rm -r {} + || true" + "[ -d './vendor/fakerphp/faker/src/Faker/Provider' ] && echo 'Removing folders:' && find ./vendor/fakerphp/faker/src/Faker/Provider -mindepth 1 -maxdepth 1 -type d ! -name 'en_US' ! -name 'fr_FR' ! -name 'de_DE' ! -name 'es_ES' ! -name 'pt_BR' ! -name 'it_IT' ! -name 'nl_NL' ! -name 'ja_JP' ! -name 'zh_CN' ! -name 'ru_RU' -print -exec rm -r {} + || true" ], "strauss": [ "@strauss-install", diff --git a/src/FakerPress/Module/Abstract_Module.php b/src/FakerPress/Module/Abstract_Module.php index e100edf..2746a07 100644 --- a/src/FakerPress/Module/Abstract_Module.php +++ b/src/FakerPress/Module/Abstract_Module.php @@ -3,6 +3,7 @@ namespace FakerPress\Module; use FakerPress\ThirdParty\Faker\Generator; +use function FakerPress\fakerpress_get_available_locales; use function FakerPress\is_truthy; /** @@ -41,6 +42,15 @@ abstract class Abstract_Module implements Interface_Module { */ protected $provider_class; + /** + * Faker locale for generated content. + * + * @since 0.10.0 + * + * @var ?string + */ + protected ?string $locale = null; + /** * Stores the generated data. * @@ -108,7 +118,11 @@ public function get_amount_allowed(): int { */ public function get_faker(): Generator { if ( ! $this->faker ) { - $this->faker = \FakerPress\ThirdParty\Faker\Factory::create(); + $locale = $this->locale && isset( fakerpress_get_available_locales()[ $this->locale ] ) + ? $this->locale + : null; + + $this->faker = \FakerPress\ThirdParty\Faker\Factory::create( $locale ); // We need to merge the Provider to the Dependencies, so everything is loaded. $providers = array_merge( $this->get_dependencies(), (array) $this->get_provider_class() ); @@ -123,6 +137,21 @@ public function get_faker(): Generator { return $this->faker; } + /** + * Set the Faker locale for generated content. + * + * @since 0.10.0 + * + * @param ?string $locale Locale code (e.g. 'fr_FR'), or null for default. + * + * @return self + */ + public function set_locale( ?string $locale ): self { + $this->locale = '' !== $locale ? $locale : null; + + return $this; + } + /** * Filters the dependencies classes for this module. * diff --git a/src/FakerPress/Module/Attachment.php b/src/FakerPress/Module/Attachment.php index dc4e073..6fee7c2 100644 --- a/src/FakerPress/Module/Attachment.php +++ b/src/FakerPress/Module/Attachment.php @@ -306,6 +306,8 @@ public function parse_request( $qty, $request = [] ) { // The quantity is already calculated by the REST endpoint or passed directly // We should use the $qty parameter, not recalculate from request + $this->set_locale( get( $request, 'locale', null ) ); + // Process date range if ( isset( $request['interval_date'] ) ) { $date_range = [ diff --git a/src/FakerPress/Module/Comment.php b/src/FakerPress/Module/Comment.php index 77f781c..6f69213 100644 --- a/src/FakerPress/Module/Comment.php +++ b/src/FakerPress/Module/Comment.php @@ -133,6 +133,8 @@ public function parse_request( int $qty, array $request = [] ) { return new WP_Error( 'fakerpress_zero_comments', __( 'Zero is not a good number of comments to fake...', 'fakerpress' ) ); } + $this->set_locale( get( $request, 'locale', null ) ); + $comment_content_size = get( $request, 'content_size', [ 1, 5 ] ); $comment_content_use_html = is_truthy( get( $request, 'use_html', 'off' ) ); $comment_content_html_tags = array_map( 'trim', explode( ',', get( $request, 'html_tags' ) ) ); diff --git a/src/FakerPress/Module/Post.php b/src/FakerPress/Module/Post.php index 0860e2f..163c6d7 100644 --- a/src/FakerPress/Module/Post.php +++ b/src/FakerPress/Module/Post.php @@ -130,6 +130,8 @@ public function parse_request( int $qty, array $request = [] ) { return new WP_Error( 'fakerpress_zero_posts', __( 'Zero is not a good number of posts to fake...', 'fakerpress' ) ); } + $this->set_locale( get( $request, 'locale', null ) ); + // Fetch Comment Status $comment_status = get( $request, 'comment_status', '' ); $comment_status = array_map( 'trim', explode( ',', $comment_status ) ); diff --git a/src/FakerPress/Module/Term.php b/src/FakerPress/Module/Term.php index 7a5a8de..14ef9ba 100644 --- a/src/FakerPress/Module/Term.php +++ b/src/FakerPress/Module/Term.php @@ -109,6 +109,8 @@ public function parse_request( int $qty, array $request = [] ) { return new WP_Error( 'fakerpress_zero_terms', __( 'Zero is not a good number of terms to fake...', 'fakerpress' ) ); } + $this->set_locale( get( $request, 'locale', null ) ); + $name_size = get( $request, 'size' ); // Fetch taxonomies diff --git a/src/FakerPress/Module/User.php b/src/FakerPress/Module/User.php index eccf778..6f03112 100644 --- a/src/FakerPress/Module/User.php +++ b/src/FakerPress/Module/User.php @@ -136,6 +136,8 @@ public function parse_request( int $qty, array $request = [] ) { return new WP_Error( 'fakerpress_zero_users', __( 'Zero is not a good number of users to fake...', 'fakerpress' ) ); } + $this->set_locale( get( $request, 'locale', null ) ); + $description_size = get( $request, 'description_size', [ 1, 5 ] ); $description_use_html = get( $request, 'use_html', 'off' ) === 'on'; $description_html_tags = array_map( 'trim', explode( ',', get( $request, 'html_tags', '' ) ) ); diff --git a/src/FakerPress/Plugin.php b/src/FakerPress/Plugin.php index c04212c..ab9ecae 100644 --- a/src/FakerPress/Plugin.php +++ b/src/FakerPress/Plugin.php @@ -167,6 +167,7 @@ protected function static_load(): void { require_once $this->plugin_path . 'src' . DIRECTORY_SEPARATOR . 'functions' . DIRECTORY_SEPARATOR . 'conditionals.php'; require_once $this->plugin_path . 'src' . DIRECTORY_SEPARATOR . 'functions' . DIRECTORY_SEPARATOR . 'sorting.php'; require_once $this->plugin_path . 'src' . DIRECTORY_SEPARATOR . 'functions' . DIRECTORY_SEPARATOR . 'assets.php'; + require_once $this->plugin_path . 'src' . DIRECTORY_SEPARATOR . 'functions' . DIRECTORY_SEPARATOR . 'locale.php'; } /** diff --git a/src/FakerPress/REST/Abstract_Endpoint.php b/src/FakerPress/REST/Abstract_Endpoint.php index 7b6fb57..f502236 100644 --- a/src/FakerPress/REST/Abstract_Endpoint.php +++ b/src/FakerPress/REST/Abstract_Endpoint.php @@ -15,6 +15,8 @@ use WP_REST_Server; use WP_Error; +use function FakerPress\fakerpress_get_available_locales; + /** * Abstract class Abstract_Endpoint * @@ -388,6 +390,31 @@ protected function get_batching_args(): array { ]; } + /** + * Get the locale argument definition shared across all generation endpoints. + * + * @since 0.10.0 + * + * @return array + */ + protected function get_locale_args(): array { + return [ + 'locale' => [ + 'description' => __( 'Locale for generated content (e.g. fr_FR, de_DE). Falls back to en_US.', 'fakerpress' ), + 'type' => 'string', + 'default' => null, + 'sanitize_callback' => 'sanitize_text_field', + 'validate_callback' => function ( $value ) { + if ( $value === null || '' === $value ) { + return true; + } + + return isset( fakerpress_get_available_locales()[ $value ] ); + }, + ], + ]; + } + /** * Convert endpoint args to request schema format. * diff --git a/src/FakerPress/REST/Endpoints/Attachments.php b/src/FakerPress/REST/Endpoints/Attachments.php index c69bcf7..89a43f3 100644 --- a/src/FakerPress/REST/Endpoints/Attachments.php +++ b/src/FakerPress/REST/Endpoints/Attachments.php @@ -295,7 +295,8 @@ protected function get_endpoint_args(): array { 'type' => 'object', ], ], - $this->get_batching_args() + $this->get_batching_args(), + $this->get_locale_args() ); } diff --git a/src/FakerPress/REST/Endpoints/Comments.php b/src/FakerPress/REST/Endpoints/Comments.php index 90f22e2..755a24c 100644 --- a/src/FakerPress/REST/Endpoints/Comments.php +++ b/src/FakerPress/REST/Endpoints/Comments.php @@ -226,7 +226,8 @@ protected function get_endpoint_args(): array { 'type' => 'object', ], ], - $this->get_batching_args() + $this->get_batching_args(), + $this->get_locale_args() ); } diff --git a/src/FakerPress/REST/Endpoints/Posts.php b/src/FakerPress/REST/Endpoints/Posts.php index 6d88624..6baa158 100644 --- a/src/FakerPress/REST/Endpoints/Posts.php +++ b/src/FakerPress/REST/Endpoints/Posts.php @@ -245,7 +245,8 @@ protected function get_endpoint_args(): array { 'type' => 'object', ], ], - $this->get_batching_args() + $this->get_batching_args(), + $this->get_locale_args() ); } diff --git a/src/FakerPress/REST/Endpoints/Terms.php b/src/FakerPress/REST/Endpoints/Terms.php index 9e55092..67f5816 100644 --- a/src/FakerPress/REST/Endpoints/Terms.php +++ b/src/FakerPress/REST/Endpoints/Terms.php @@ -229,7 +229,8 @@ protected function get_endpoint_args(): array { 'type' => 'object', ], ], - $this->get_batching_args() + $this->get_batching_args(), + $this->get_locale_args() ); } diff --git a/src/FakerPress/REST/Endpoints/Users.php b/src/FakerPress/REST/Endpoints/Users.php index 75802e7..042f058 100644 --- a/src/FakerPress/REST/Endpoints/Users.php +++ b/src/FakerPress/REST/Endpoints/Users.php @@ -225,7 +225,8 @@ protected function get_endpoint_args(): array { 'type' => 'object', ], ], - $this->get_batching_args() + $this->get_batching_args(), + $this->get_locale_args() ); } diff --git a/src/FakerPress/functions/locale.php b/src/FakerPress/functions/locale.php new file mode 100644 index 0000000..cb5518b --- /dev/null +++ b/src/FakerPress/functions/locale.php @@ -0,0 +1,33 @@ + human-readable label. + */ +function fakerpress_get_available_locales(): array { + return [ + 'en_US' => 'English (United States)', + 'fr_FR' => 'French (France)', + 'de_DE' => 'German (Germany)', + 'es_ES' => 'Spanish (Spain)', + 'pt_BR' => 'Portuguese (Brazil)', + 'it_IT' => 'Italian (Italy)', + 'nl_NL' => 'Dutch (Netherlands)', + 'ja_JP' => 'Japanese (Japan)', + 'zh_CN' => 'Chinese (Simplified, China)', + 'ru_RU' => 'Russian (Russia)', + ]; +} diff --git a/src/templates/pages/attachments.php b/src/templates/pages/attachments.php index c16d5ad..5059224 100644 --- a/src/templates/pages/attachments.php +++ b/src/templates/pages/attachments.php @@ -197,6 +197,27 @@ ] ); +$_locale_options = []; +foreach ( fakerpress_get_available_locales() as $locale_code => $locale_label ) { + $_locale_options[] = [ + 'id' => $locale_code, + 'text' => $locale_label, + ]; +} + +$fields[] = new Field( + 'dropdown', + [ + 'id' => 'locale', + 'data-options' => $_locale_options, + 'value' => 'en_US', + ], + [ + 'label' => __( 'Locale', 'fakerpress' ), + 'description' => __( 'Language for generated content. Falls back to English if the locale data is unavailable.', 'fakerpress' ), + ] +); + $fields[] = new Field( 'meta', [ diff --git a/src/templates/pages/comments.php b/src/templates/pages/comments.php index 4f95cf1..5020e1e 100644 --- a/src/templates/pages/comments.php +++ b/src/templates/pages/comments.php @@ -146,6 +146,27 @@ ] ); +$_locale_options = []; +foreach ( fakerpress_get_available_locales() as $locale_code => $locale_label ) { + $_locale_options[] = [ + 'id' => $locale_code, + 'text' => $locale_label, + ]; +} + +$fields[] = new Field( + 'dropdown', + [ + 'id' => 'locale', + 'data-options' => $_locale_options, + 'value' => 'en_US', + ], + [ + 'label' => __( 'Locale', 'fakerpress' ), + 'description' => __( 'Language for generated content. Falls back to English if the locale data is unavailable.', 'fakerpress' ), + ] +); + $fields[] = new Field( 'meta', [ diff --git a/src/templates/pages/posts.php b/src/templates/pages/posts.php index e5ed42c..b15a68a 100644 --- a/src/templates/pages/posts.php +++ b/src/templates/pages/posts.php @@ -223,6 +223,27 @@ ] ); +$_locale_options = []; +foreach ( fakerpress_get_available_locales() as $locale_code => $locale_label ) { + $_locale_options[] = [ + 'id' => $locale_code, + 'text' => $locale_label, + ]; +} + +$fields[] = new Field( + 'dropdown', + [ + 'id' => 'locale', + 'data-options' => $_locale_options, + 'value' => 'en_US', + ], + [ + 'label' => __( 'Locale', 'fakerpress' ), + 'description' => __( 'Language for generated content. Falls back to English if the locale data is unavailable.', 'fakerpress' ), + ] +); + $fields[] = new Field( 'meta', [ diff --git a/src/templates/pages/terms.php b/src/templates/pages/terms.php index 720125f..7290e51 100644 --- a/src/templates/pages/terms.php +++ b/src/templates/pages/terms.php @@ -47,6 +47,27 @@ ] ); +$_locale_options = []; +foreach ( fakerpress_get_available_locales() as $locale_code => $locale_label ) { + $_locale_options[] = [ + 'id' => $locale_code, + 'text' => $locale_label, + ]; +} + +$fields[] = new Field( + 'dropdown', + [ + 'id' => 'locale', + 'data-options' => $_locale_options, + 'value' => 'en_US', + ], + [ + 'label' => __( 'Locale', 'fakerpress' ), + 'description' => __( 'Language for generated content. Falls back to English if the locale data is unavailable.', 'fakerpress' ), + ] +); + if ( version_compare( $GLOBALS['wp_version'], '4.4-beta', '>=' ) ) { $fields[] = new Field( 'meta', diff --git a/src/templates/pages/users.php b/src/templates/pages/users.php index 33d5564..37dad07 100644 --- a/src/templates/pages/users.php +++ b/src/templates/pages/users.php @@ -87,6 +87,27 @@ ] ); +$_locale_options = []; +foreach ( fakerpress_get_available_locales() as $locale_code => $locale_label ) { + $_locale_options[] = [ + 'id' => $locale_code, + 'text' => $locale_label, + ]; +} + +$fields[] = new Field( + 'dropdown', + [ + 'id' => 'locale', + 'data-options' => $_locale_options, + 'value' => 'en_US', + ], + [ + 'label' => __( 'Locale', 'fakerpress' ), + 'description' => __( 'Language for generated content. Falls back to English if the locale data is unavailable.', 'fakerpress' ), + ] +); + $fields[] = new Field( 'meta', [ From 3e79882069629bc42925eb3244e3ec46ecbdaf50 Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Fri, 17 Jul 2026 21:38:15 +0600 Subject: [PATCH 2/8] fix(locale): place locale.php in src/functions/ where require_once expects it The existing require_once in static_load() loads from src/functions/, not src/FakerPress/functions/. locale.php must live at src/functions/ to match. --- src/functions/locale.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/functions/locale.php diff --git a/src/functions/locale.php b/src/functions/locale.php new file mode 100644 index 0000000..cb5518b --- /dev/null +++ b/src/functions/locale.php @@ -0,0 +1,33 @@ + human-readable label. + */ +function fakerpress_get_available_locales(): array { + return [ + 'en_US' => 'English (United States)', + 'fr_FR' => 'French (France)', + 'de_DE' => 'German (Germany)', + 'es_ES' => 'Spanish (Spain)', + 'pt_BR' => 'Portuguese (Brazil)', + 'it_IT' => 'Italian (Italy)', + 'nl_NL' => 'Dutch (Netherlands)', + 'ja_JP' => 'Japanese (Japan)', + 'zh_CN' => 'Chinese (Simplified, China)', + 'ru_RU' => 'Russian (Russia)', + ]; +} From 3c095773df5521693f5a34b4992ef6fc36407978 Mon Sep 17 00:00:00 2001 From: Faisal Ahammad Date: Fri, 17 Jul 2026 21:50:40 +0600 Subject: [PATCH 3/8] fix(locale): use 'options' key for native select dropdown (#166) data-options is the Select2 attribute used for multiple-select fields. The locale field is a single-select (native