diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 55a0884619..ab3be7a76c 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -24,19 +24,21 @@ * @uses $wp_cockneyreplace Array of formatted entities for certain common phrases * * @param string $text The text to be formatted + * @param bool $reset Set to true for unit testing. Translated patterns will reset. * @return string The string replaced with html entities */ -function wptexturize($text) { +function wptexturize($text, $reset = false) { global $wp_cockneyreplace; static $static_characters, $static_replacements, $dynamic_characters, $dynamic_replacements, $default_no_texturize_tags, $default_no_texturize_shortcodes, $run_texturize = true; - if ( false === $run_texturize ) { + // If there's nothing to do, just stop. + if ( empty( $text ) || false === $run_texturize ) { return $text; } - // No need to set up these static variables more than once - if ( ! isset( $static_characters ) ) { + // Set up static variables. Run once only. + if ( $reset || ! isset( $static_characters ) ) { /** * Filter whether to skip running `wptexturize()`. * @@ -123,9 +125,9 @@ function wptexturize($text) { $dynamic[ '/(?<=\A|[([{<"\-]|' . $spaces . ')\'/' ] = $opening_single_quote; } - // Apostrophe in a word. No spaces or double apostrophes. + // Apostrophe in a word. No spaces, double apostrophes, or other punctuation. if ( "'" != $apos ) { - $dynamic[ '/(?[\]\-]|' . $spaces . ')/' ] = $apos; } // 9" (double prime) @@ -148,9 +150,9 @@ function wptexturize($text) { $dynamic[ '/"/' ] = $closing_quote; } - // Single quotes followed by spaces or a period. - if ( "'" !== $closing_single_quote ) { - $dynamic[ '/\'(?=\Z|\.|' . $spaces . ')/' ] = $closing_single_quote; + // Single quotes followed by spaces or ending punctuation. + if ( "'" != $closing_single_quote ) { + $dynamic[ '/\'(?=\Z|[.,)}>\-\]]|' . $spaces . ')/' ] = $closing_single_quote; } // Dashes and spaces @@ -163,11 +165,6 @@ function wptexturize($text) { $dynamic_replacements = array_values( $dynamic ); } - // If there's nothing to do, just stop. - if ( empty( $text ) ) { - return $text; - } - // Transform into regexp sub-expression used in _wptexturize_pushpop_element // Must do this every time in case plugins use these filters in a context sensitive manner /** diff --git a/tests/phpunit/tests/formatting/WPTexturize.php b/tests/phpunit/tests/formatting/WPTexturize.php index f518a3d264..305751001f 100644 --- a/tests/phpunit/tests/formatting/WPTexturize.php +++ b/tests/phpunit/tests/formatting/WPTexturize.php @@ -189,7 +189,7 @@ class Tests_Formatting_WPTexturize extends WP_UnitTestCase { function test_spaces_around_hyphens() { $nbsp = "\xC2\xA0"; - $this->assertEquals( ' – ', wptexturize( ' - ' ) ); + $this->assertEquals( ' – ', wptexturize( ' - ' ) ); $this->assertEquals( ' – ', wptexturize( ' - ' ) ); $this->assertEquals( ' – ', wptexturize( ' - ' ) ); $this->assertEquals( ' – ', wptexturize( ' - ') ); @@ -197,7 +197,7 @@ class Tests_Formatting_WPTexturize extends WP_UnitTestCase { $this->assertEquals( " –$nbsp", wptexturize( " -$nbsp" ) ); $this->assertEquals( "$nbsp– ", wptexturize( "$nbsp- ") ); - $this->assertEquals( ' — ', wptexturize( ' -- ' ) ); + $this->assertEquals( ' — ', wptexturize( ' -- ' ) ); $this->assertEquals( ' — ', wptexturize( ' -- ' ) ); $this->assertEquals( ' — ', wptexturize( ' -- ' ) ); $this->assertEquals( ' — ', wptexturize( ' -- ') ); @@ -723,6 +723,10 @@ class Tests_Formatting_WPTexturize extends WP_UnitTestCase { "word word'.word", "word word’.word", ), + array( + "word word', she said", + "word word’, she said", + ), array( "word word'", "word word’", @@ -1362,4 +1366,223 @@ class Tests_Formatting_WPTexturize extends WP_UnitTestCase { ), ); } + + /** + * Make sure translation actually works. + * + * Also make sure apostrophes and closing quotes aren't being confused by default. + * + * @ticket 27426 + * @dataProvider data_tag_avoidance + */ + function test_translate( $input, $output ) { + add_filter( 'gettext_with_context', array( $this, 'filter_translate' ), 10, 4 ); + + $result = wptexturize( $input, true ); + + remove_filter( 'gettext_with_context', array( $this, 'filter_translate' ), 10, 4 ); + wptexturize( 'reset', true ); + + return $this->assertEquals( $output, $result ); + } + + function filter_translate( $translations, $text, $context, $domain ) { + switch ($text) { + case '–' : return '!endash!'; + case '—' : return '!emdash!'; + case '‘' : return '!openq1!'; + case '’' : + if ( 'apostrophe' == $context ) { + return '!apos!'; + } else { + return '!closeq1!'; + } + case '“' : return '!openq2!'; + case '”' : return '!closeq2!'; + case '′' : return '!prime1!'; + case '″' : return '!prime2!'; + default : return $translations; + } + } + + function data_translate() { + return array( + array( + "word '99 word", + "word !apos!99 word", + ), + array( + "word'99 word", + "word!apos!99 word", + ), + array( + "word 'test sentence' word", + "word !openq1!test sentence!closeq1! word", + ), + array( + "'test sentence'", + "!openq1!test sentence!closeq1!", + ), + array( + 'word "test sentence" word', + 'word !openq2!test sentence!closeq2! word', + ), + array( + '"test sentence"', + '!openq2!test sentence!closeq2!', + ), + array( + "word 'word word", + "word !openq1!word word", + ), + array( + "word ('word word", + "word (!openq1!word word", + ), + array( + "word ['word word", + "word [!openq1!word word", + ), + array( + 'word 99" word', + 'word 99!prime2! word', + ), + array( + 'word 99"word', + 'word 99!prime2!word', + ), + array( + 'word99" word', + 'word99!prime2! word', + ), + array( + 'word99"word', + 'word99!prime2!word', + ), + array( + "word 99' word", + "word 99!prime1! word", + ), + array( + "word99' word", + "word99!prime1! word", + ), + array( + "word word's word", + "word word!apos!s word", + ), + array( + "word word'. word", + "word word!closeq1!. word", + ), + array( + "word ]'. word", + "word ]!closeq1!. word", + ), + array( + 'word "word word', + 'word !openq2!word word', + ), + array( + 'word ("word word', + 'word (!openq2!word word', + ), + array( + 'word ["word word', + 'word [!openq2!word word', + ), + array( + 'word word" word', + 'word word!closeq2! word', + ), + array( + 'word word") word', + 'word word!closeq2!) word', + ), + array( + 'word word"] word', + 'word word!closeq2!] word', + ), + array( + 'word word"', + 'word word!closeq2!', + ), + array( + 'word word"word', + 'word word!closeq2!word', + ), + array( + 'test sentence".', + 'test sentence!closeq2!.', + ), + array( + 'test sentence."', + 'test sentence.!closeq2!', + ), + array( + 'test sentence." word', + 'test sentence.!closeq2! word', + ), + array( + "word word' word", + "word word!closeq1! word", + ), + array( + "word word'. word", + "word word!closeq1!. word", + ), + array( + "word word'.word", + "word word!closeq1!.word", + ), + array( + "word word'", + "word word!closeq1!", + ), + array( + "test sentence'.", + "test sentence!closeq1!.", + ), + array( + "test sentence.'", + "test sentence.!closeq1!", + ), + array( + "test sentence'. word", + "test sentence!closeq1!. word", + ), + array( + "test sentence.' word", + "test sentence.!closeq1! word", + ), + array( + "word 'tain't word", + "word !apos!tain!apos!t word", + ), + array( + "word 'twere word", + "word !apos!twere word", + ), + array( + 'word "42.00" word', + 'word !openq2!42.00!closeq2! word', + ), + array( + "word '42.00' word", + "word !openq1!42.00!closeq1! word", + ), + array( + "word word'. word", + "word word!closeq1!. word", + ), + array( + "word word'.word", + "word word!closeq1!.word", + ), + array( + "word word', she said", + "word word!closeq1!, she said", + ), + ); + } } \ No newline at end of file