diff --git a/includes/Abilities/Content_Classification/Content_Classification.php b/includes/Abilities/Content_Classification/Content_Classification.php index 4d066ab82..6fe38a958 100644 --- a/includes/Abilities/Content_Classification/Content_Classification.php +++ b/includes/Abilities/Content_Classification/Content_Classification.php @@ -196,11 +196,14 @@ protected function execute_callback( $input ) { ); } + $tax_label = $this->get_taxonomy_label( $args['taxonomy'] ); + // If we have no content, return an error. if ( empty( $context['content'] ) ) { return new WP_Error( 'content_not_provided', - esc_html__( 'Content is required to generate taxonomy suggestions.', 'ai' ) + /* translators: %s: Taxonomy label (e.g., "category" or "tag"). */ + sprintf( esc_html__( 'Content is required to generate %s suggestions.', 'ai' ), $tax_label ) ); } @@ -222,7 +225,8 @@ protected function execute_callback( $input ) { if ( empty( $result ) ) { return new WP_Error( 'no_results', - esc_html__( 'No taxonomy suggestions were generated.', 'ai' ) + /* translators: %s: Taxonomy label (e.g., "category" or "tag"). */ + sprintf( esc_html__( 'No %s suggestions were generated.', 'ai' ), $tax_label ) ); } @@ -240,7 +244,9 @@ protected function execute_callback( $input ) { * @return bool|\WP_Error True if the user has permission, WP_Error otherwise. */ protected function permission_callback( $args ) { - $post_id = isset( $args['post_id'] ) ? absint( $args['post_id'] ) : null; + $taxonomy = isset( $args['taxonomy'] ) && is_string( $args['taxonomy'] ) ? $args['taxonomy'] : 'post_tag'; + $tax_label = $this->get_taxonomy_label( $taxonomy ); + $post_id = isset( $args['post_id'] ) ? absint( $args['post_id'] ) : null; if ( $post_id ) { $post = get_post( $post_id ); @@ -258,7 +264,8 @@ protected function permission_callback( $args ) { if ( ! current_user_can( 'edit_post', $post_id ) ) { return new WP_Error( 'insufficient_capabilities', - esc_html__( 'You do not have permission to generate taxonomy suggestions for this post.', 'ai' ) + /* translators: %s: Taxonomy label (e.g., "category" or "tag"). */ + sprintf( esc_html__( 'You do not have permission to generate %s suggestions for this post.', 'ai' ), $tax_label ) ); } @@ -270,7 +277,8 @@ protected function permission_callback( $args ) { // Ensure the user has permission to edit posts in general. return new WP_Error( 'insufficient_capabilities', - esc_html__( 'You do not have permission to generate taxonomy suggestions.', 'ai' ) + /* translators: %s: Taxonomy label (e.g., "category" or "tag"). */ + sprintf( esc_html__( 'You do not have permission to generate %s suggestions.', 'ai' ), $tax_label ) ); } @@ -744,4 +752,34 @@ private function get_top_terms( string $taxonomy, int $limit = 100 ): array { return (array) $terms; } + + /** + * Gets the lowercase taxonomy label for use in error and status messages. + * + * Defaults to 'taxonomy' if taxonomy does not exist or has no label. + * + * @since x.x.x + * + * @param string $taxonomy The taxonomy slug. + * @return string Lowercase taxonomy label (e.g., 'category', 'tag', 'taxonomy'). + */ + private function get_taxonomy_label( string $taxonomy ): string { + $taxonomy = sanitize_key( $taxonomy ); + + if ( '' === $taxonomy || ! taxonomy_exists( $taxonomy ) ) { + return 'taxonomy'; + } + + $tax_object = get_taxonomy( $taxonomy ); + + if ( ! $tax_object instanceof WP_Taxonomy ) { + return 'taxonomy'; + } + + $label = ! empty( $tax_object->labels->singular_name ) + ? $tax_object->labels->singular_name + : ( ! empty( $tax_object->labels->name ) ? $tax_object->labels->name : $taxonomy ); + + return strtolower( trim( wp_strip_all_tags( (string) $label ) ) ); + } } diff --git a/tests/Integration/Includes/Abilities/Content_ClassificationTest.php b/tests/Integration/Includes/Abilities/Content_ClassificationTest.php index f79ca5699..5f8daba81 100644 --- a/tests/Integration/Includes/Abilities/Content_ClassificationTest.php +++ b/tests/Integration/Includes/Abilities/Content_ClassificationTest.php @@ -1446,4 +1446,24 @@ static function ( $prompt ) use ( &$captured_prompt ) { $this->assertStringNotContainsString( '', $captured_prompt, 'Prompt should not contain available terms for allow_new strategy' ); } + + /** + * Test get_taxonomy_label returns expected singular labels. + * + * @since x.x.x + */ + public function test_get_taxonomy_label_returns_correct_labels(): void { + $reflection = new \ReflectionClass( $this->ability ); + $method = $reflection->getMethod( 'get_taxonomy_label' ); + $method->setAccessible( true ); + + $category_label = $method->invoke( $this->ability, 'category' ); + $this->assertSame( 'category', $category_label, 'Label for category should be category' ); + + $tag_label = $method->invoke( $this->ability, 'post_tag' ); + $this->assertSame( 'tag', $tag_label, 'Label for post_tag should be tag' ); + + $unknown_label = $method->invoke( $this->ability, 'nonexistent_taxonomy' ); + $this->assertSame( 'taxonomy', $unknown_label, 'Label for unknown taxonomy should default to taxonomy' ); + } }