Просмотр файла vendor/symfony/string/Slugger/AsciiSlugger.php

Размер файла: 5.99Kb
  1. <?php
  2.  
  3. /*
  4. * This file is part of the Symfony package.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11.  
  12. namespace Symfony\Component\String\Slugger;
  13.  
  14. use Symfony\Component\String\AbstractUnicodeString;
  15. use Symfony\Component\String\UnicodeString;
  16. use Symfony\Contracts\Translation\LocaleAwareInterface;
  17.  
  18. if (!interface_exists(LocaleAwareInterface::class)) {
  19. throw new \LogicException('You cannot use the "Symfony\Component\String\Slugger\AsciiSlugger" as the "symfony/translation-contracts" package is not installed. Try running "composer require symfony/translation-contracts".');
  20. }
  21.  
  22. /**
  23. * @author Titouan Galopin <galopintitouan@gmail.com>
  24. */
  25. class AsciiSlugger implements SluggerInterface, LocaleAwareInterface
  26. {
  27. private const LOCALE_TO_TRANSLITERATOR_ID = [
  28. 'am' => 'Amharic-Latin',
  29. 'ar' => 'Arabic-Latin',
  30. 'az' => 'Azerbaijani-Latin',
  31. 'be' => 'Belarusian-Latin',
  32. 'bg' => 'Bulgarian-Latin',
  33. 'bn' => 'Bengali-Latin',
  34. 'de' => 'de-ASCII',
  35. 'el' => 'Greek-Latin',
  36. 'fa' => 'Persian-Latin',
  37. 'he' => 'Hebrew-Latin',
  38. 'hy' => 'Armenian-Latin',
  39. 'ka' => 'Georgian-Latin',
  40. 'kk' => 'Kazakh-Latin',
  41. 'ky' => 'Kirghiz-Latin',
  42. 'ko' => 'Korean-Latin',
  43. 'mk' => 'Macedonian-Latin',
  44. 'mn' => 'Mongolian-Latin',
  45. 'or' => 'Oriya-Latin',
  46. 'ps' => 'Pashto-Latin',
  47. 'ru' => 'Russian-Latin',
  48. 'sr' => 'Serbian-Latin',
  49. 'sr_Cyrl' => 'Serbian-Latin',
  50. 'th' => 'Thai-Latin',
  51. 'tk' => 'Turkmen-Latin',
  52. 'uk' => 'Ukrainian-Latin',
  53. 'uz' => 'Uzbek-Latin',
  54. 'zh' => 'Han-Latin',
  55. ];
  56.  
  57. private $defaultLocale;
  58. private $symbolsMap = [
  59. 'en' => ['@' => 'at', '&' => 'and'],
  60. ];
  61.  
  62. /**
  63. * Cache of transliterators per locale.
  64. *
  65. * @var \Transliterator[]
  66. */
  67. private $transliterators = [];
  68.  
  69. /**
  70. * @param array|\Closure|null $symbolsMap
  71. */
  72. public function __construct(string $defaultLocale = null, $symbolsMap = null)
  73. {
  74. if (null !== $symbolsMap && !\is_array($symbolsMap) && !$symbolsMap instanceof \Closure) {
  75. throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be array, Closure or null, "%s" given.', __METHOD__, \gettype($symbolsMap)));
  76. }
  77.  
  78. $this->defaultLocale = $defaultLocale;
  79. $this->symbolsMap = $symbolsMap ?? $this->symbolsMap;
  80. }
  81.  
  82. /**
  83. * {@inheritdoc}
  84. */
  85. public function setLocale($locale)
  86. {
  87. $this->defaultLocale = $locale;
  88. }
  89.  
  90. /**
  91. * {@inheritdoc}
  92. */
  93. public function getLocale()
  94. {
  95. return $this->defaultLocale;
  96. }
  97.  
  98. /**
  99. * {@inheritdoc}
  100. */
  101. public function slug(string $string, string $separator = '-', string $locale = null): AbstractUnicodeString
  102. {
  103. $locale = $locale ?? $this->defaultLocale;
  104.  
  105. $transliterator = [];
  106. if ($locale && ('de' === $locale || 0 === strpos($locale, 'de_'))) {
  107. // Use the shortcut for German in UnicodeString::ascii() if possible (faster and no requirement on intl)
  108. $transliterator = ['de-ASCII'];
  109. } elseif (\function_exists('transliterator_transliterate') && $locale) {
  110. $transliterator = (array) $this->createTransliterator($locale);
  111. }
  112.  
  113. if ($this->symbolsMap instanceof \Closure) {
  114. // If the symbols map is passed as a closure, there is no need to fallback to the parent locale
  115. // as the closure can just provide substitutions for all locales of interest.
  116. $symbolsMap = $this->symbolsMap;
  117. array_unshift($transliterator, static function ($s) use ($symbolsMap, $locale) {
  118. return $symbolsMap($s, $locale);
  119. });
  120. }
  121.  
  122. $unicodeString = (new UnicodeString($string))->ascii($transliterator);
  123.  
  124. if (\is_array($this->symbolsMap)) {
  125. $map = null;
  126. if (isset($this->symbolsMap[$locale])) {
  127. $map = $this->symbolsMap[$locale];
  128. } else {
  129. $parent = self::getParentLocale($locale);
  130. if ($parent && isset($this->symbolsMap[$parent])) {
  131. $map = $this->symbolsMap[$parent];
  132. }
  133. }
  134. if ($map) {
  135. foreach ($map as $char => $replace) {
  136. $unicodeString = $unicodeString->replace($char, ' '.$replace.' ');
  137. }
  138. }
  139. }
  140.  
  141. return $unicodeString
  142. ->replaceMatches('/[^A-Za-z0-9]++/', $separator)
  143. ->trim($separator)
  144. ;
  145. }
  146.  
  147. private function createTransliterator(string $locale): ?\Transliterator
  148. {
  149. if (\array_key_exists($locale, $this->transliterators)) {
  150. return $this->transliterators[$locale];
  151. }
  152.  
  153. // Exact locale supported, cache and return
  154. if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$locale] ?? null) {
  155. return $this->transliterators[$locale] = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id);
  156. }
  157.  
  158. // Locale not supported and no parent, fallback to any-latin
  159. if (!$parent = self::getParentLocale($locale)) {
  160. return $this->transliterators[$locale] = null;
  161. }
  162.  
  163. // Try to use the parent locale (ie. try "de" for "de_AT") and cache both locales
  164. if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) {
  165. $transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id);
  166. }
  167.  
  168. return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null;
  169. }
  170.  
  171. private static function getParentLocale(?string $locale): ?string
  172. {
  173. if (!$locale) {
  174. return null;
  175. }
  176. if (false === $str = strrchr($locale, '_')) {
  177. // no parent locale
  178. return null;
  179. }
  180.  
  181. return substr($locale, 0, -\strlen($str));
  182. }
  183. }