diff --git a/src/Symfony/Component/String/CHANGELOG.md b/src/Symfony/Component/String/CHANGELOG.md index 988671514a..700d214832 100644 --- a/src/Symfony/Component/String/CHANGELOG.md +++ b/src/Symfony/Component/String/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.3 +--- + + * Made `AsciiSlugger` fallback to parent locale's symbolsMap + 5.2.0 ----- diff --git a/src/Symfony/Component/String/Slugger/AsciiSlugger.php b/src/Symfony/Component/String/Slugger/AsciiSlugger.php index 55b441aca2..f45af71421 100644 --- a/src/Symfony/Component/String/Slugger/AsciiSlugger.php +++ b/src/Symfony/Component/String/Slugger/AsciiSlugger.php @@ -111,6 +111,8 @@ class AsciiSlugger implements SluggerInterface, LocaleAwareInterface } if ($this->symbolsMap instanceof \Closure) { + // If the symbols map is passed as a closure, there is no need to fallback to the parent locale + // as the closure can just provide substitutions for all locales of interest. $symbolsMap = $this->symbolsMap; array_unshift($transliterator, static function ($s) use ($symbolsMap, $locale) { return $symbolsMap($s, $locale); @@ -119,9 +121,20 @@ class AsciiSlugger implements SluggerInterface, LocaleAwareInterface $unicodeString = (new UnicodeString($string))->ascii($transliterator); - if (\is_array($this->symbolsMap) && isset($this->symbolsMap[$locale])) { - foreach ($this->symbolsMap[$locale] as $char => $replace) { - $unicodeString = $unicodeString->replace($char, ' '.$replace.' '); + if (\is_array($this->symbolsMap)) { + $map = null; + if (isset($this->symbolsMap[$locale])) { + $map = $this->symbolsMap[$locale]; + } else { + $parent = self::getParentLocale($locale); + if ($parent && isset($this->symbolsMap[$parent])) { + $map = $this->symbolsMap[$parent]; + } + } + if ($map) { + foreach ($map as $char => $replace) { + $unicodeString = $unicodeString->replace($char, ' '.$replace.' '); + } } } @@ -143,17 +156,28 @@ class AsciiSlugger implements SluggerInterface, LocaleAwareInterface } // Locale not supported and no parent, fallback to any-latin - if (false === $str = strrchr($locale, '_')) { + if (!$parent = self::getParentLocale($locale)) { return $this->transliterators[$locale] = null; } // Try to use the parent locale (ie. try "de" for "de_AT") and cache both locales - $parent = substr($locale, 0, -\strlen($str)); - if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) { $transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); } return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null; } + + private static function getParentLocale(?string $locale): ?string + { + if (!$locale) { + return null; + } + if (false === $str = strrchr($locale, '_')) { + // no parent locale + return null; + } + + return substr($locale, 0, -\strlen($str)); + } } diff --git a/src/Symfony/Component/String/Tests/SluggerTest.php b/src/Symfony/Component/String/Tests/SluggerTest.php index e838da6afb..4066867e1a 100644 --- a/src/Symfony/Component/String/Tests/SluggerTest.php +++ b/src/Symfony/Component/String/Tests/SluggerTest.php @@ -65,6 +65,37 @@ class SluggerTest extends TestCase $this->assertSame('yo_y_tu_a_esta_direccion_slug_en_senal_test_es', $slug); } + public function testSlugCharReplacementLocaleConstructWithoutSymbolsMap() + { + $slugger = new AsciiSlugger('en'); + $slug = (string) $slugger->slug('you & me with this address slug@test.uk', '_'); + + $this->assertSame('you_and_me_with_this_address_slug_at_test_uk', $slug); + } + + public function testSlugCharReplacementParentLocaleConstructWithoutSymbolsMap() + { + $slugger = new AsciiSlugger('en_GB'); + $slug = (string) $slugger->slug('you & me with this address slug@test.uk', '_'); + + $this->assertSame('you_and_me_with_this_address_slug_at_test_uk', $slug); + } + + public function testSlugCharReplacementParentLocaleConstruct() + { + $slugger = new AsciiSlugger('fr_FR', ['fr' => ['&' => 'et', '@' => 'chez']]); + $slug = (string) $slugger->slug('toi & moi avec cette adresse slug@test.fr', '_'); + + $this->assertSame('toi_et_moi_avec_cette_adresse_slug_chez_test_fr', $slug); + } + + public function testSlugCharReplacementParentLocaleMethod() + { + $slugger = new AsciiSlugger(null, ['es' => ['&' => 'y', '@' => 'en senal']]); + $slug = (string) $slugger->slug('yo & tu a esta dirección slug@test.es', '_', 'es_ES'); + $this->assertSame('yo_y_tu_a_esta_direccion_slug_en_senal_test_es', $slug); + } + public function testSlugClosure() { $slugger = new AsciiSlugger(null, function ($s, $locale) {