LanguageManager.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <?php
  2. namespace Drupal\Core\Language;
  3. use Drupal\Core\DependencyInjection\DependencySerializationTrait;
  4. use Drupal\Core\StringTranslation\TranslatableMarkup;
  5. use Drupal\Core\Url;
  6. /**
  7. * Class responsible for providing language support on language-unaware sites.
  8. */
  9. class LanguageManager implements LanguageManagerInterface {
  10. use DependencySerializationTrait;
  11. /**
  12. * A static cache of translated language lists.
  13. *
  14. * Array of arrays to cache the result of self::getLanguages() keyed by the
  15. * language the list is translated to (first level) and the flags provided to
  16. * the method (second level).
  17. *
  18. * @var \Drupal\Core\Language\LanguageInterface[]
  19. *
  20. * @see \Drupal\Core\Language\LanguageManager::getLanguages()
  21. */
  22. protected $languages = [];
  23. /**
  24. * The default language object.
  25. *
  26. * @var \Drupal\Core\Language\LanguageDefault
  27. */
  28. protected $defaultLanguage;
  29. /**
  30. * Constructs the language manager.
  31. *
  32. * @param \Drupal\Core\Language\LanguageDefault $default_language
  33. * The default language.
  34. */
  35. public function __construct(LanguageDefault $default_language) {
  36. $this->defaultLanguage = $default_language;
  37. }
  38. /**
  39. * {@inheritdoc}
  40. */
  41. public function isMultilingual() {
  42. return FALSE;
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function getLanguageTypes() {
  48. return [LanguageInterface::TYPE_INTERFACE, LanguageInterface::TYPE_CONTENT, LanguageInterface::TYPE_URL];
  49. }
  50. /**
  51. * Returns information about all defined language types.
  52. *
  53. * Defines the three core language types:
  54. * - Interface language is the only configurable language type in core. It is
  55. * used by t() as the default language if none is specified.
  56. * - Content language is by default non-configurable and inherits the
  57. * interface language negotiated value. It is used by the Field API to
  58. * determine the display language for fields if no explicit value is
  59. * specified.
  60. * - URL language is by default non-configurable and is determined through the
  61. * URL language negotiation method or the URL fallback language negotiation
  62. * method if no language can be detected. It is used by l() as the default
  63. * language if none is specified.
  64. *
  65. * @return array
  66. * An associative array of language type information arrays keyed by
  67. * language type machine name, in the format of
  68. * hook_language_types_info().
  69. */
  70. public function getDefinedLanguageTypesInfo() {
  71. $this->definedLanguageTypesInfo = [
  72. LanguageInterface::TYPE_INTERFACE => [
  73. 'name' => new TranslatableMarkup('Interface text'),
  74. 'description' => new TranslatableMarkup('Order of language detection methods for interface text. If a translation of interface text is available in the detected language, it will be displayed.'),
  75. 'locked' => TRUE,
  76. ],
  77. LanguageInterface::TYPE_CONTENT => [
  78. 'name' => new TranslatableMarkup('Content'),
  79. 'description' => new TranslatableMarkup('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'),
  80. 'locked' => TRUE,
  81. ],
  82. LanguageInterface::TYPE_URL => [
  83. 'locked' => TRUE,
  84. ],
  85. ];
  86. return $this->definedLanguageTypesInfo;
  87. }
  88. /**
  89. * {@inheritdoc}
  90. */
  91. public function getCurrentLanguage($type = LanguageInterface::TYPE_INTERFACE) {
  92. return $this->getDefaultLanguage();
  93. }
  94. /**
  95. * {@inheritdoc}
  96. */
  97. public function reset($type = NULL) {
  98. return $this;
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function getDefaultLanguage() {
  104. return $this->defaultLanguage->get();
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. public function getLanguages($flags = LanguageInterface::STATE_CONFIGURABLE) {
  110. $static_cache_id = $this->getCurrentLanguage()->getId();
  111. if (!isset($this->languages[$static_cache_id][$flags])) {
  112. // If this language manager is used, there are no configured languages.
  113. // The default language and locked languages comprise the full language
  114. // list.
  115. $default = $this->getDefaultLanguage();
  116. $languages = [$default->getId() => $default];
  117. $languages += $this->getDefaultLockedLanguages($default->getWeight());
  118. // Filter the full list of languages based on the value of $flags.
  119. $this->languages[$static_cache_id][$flags] = $this->filterLanguages($languages, $flags);
  120. }
  121. return $this->languages[$static_cache_id][$flags];
  122. }
  123. /**
  124. * {@inheritdoc}
  125. */
  126. public function getNativeLanguages() {
  127. // In a language unaware site we don't have translated languages.
  128. return $this->getLanguages();
  129. }
  130. /**
  131. * {@inheritdoc}
  132. */
  133. public function getLanguage($langcode) {
  134. $languages = $this->getLanguages(LanguageInterface::STATE_ALL);
  135. return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
  136. }
  137. /**
  138. * {@inheritdoc}
  139. */
  140. public function getLanguageName($langcode) {
  141. if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
  142. return new TranslatableMarkup('None');
  143. }
  144. if ($language = $this->getLanguage($langcode)) {
  145. return $language->getName();
  146. }
  147. if (empty($langcode)) {
  148. return new TranslatableMarkup('Unknown');
  149. }
  150. return new TranslatableMarkup('Unknown (@langcode)', ['@langcode' => $langcode]);
  151. }
  152. /**
  153. * {@inheritdoc}
  154. */
  155. public function getDefaultLockedLanguages($weight = 0) {
  156. $languages = [];
  157. $locked_language = [
  158. 'default' => FALSE,
  159. 'locked' => TRUE,
  160. 'direction' => LanguageInterface::DIRECTION_LTR,
  161. ];
  162. // This is called very early while initializing the language system. Prevent
  163. // early t() calls by using the TranslatableMarkup.
  164. $languages[LanguageInterface::LANGCODE_NOT_SPECIFIED] = new Language([
  165. 'id' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
  166. 'name' => new TranslatableMarkup('Not specified'),
  167. 'weight' => ++$weight,
  168. ] + $locked_language);
  169. $languages[LanguageInterface::LANGCODE_NOT_APPLICABLE] = new Language([
  170. 'id' => LanguageInterface::LANGCODE_NOT_APPLICABLE,
  171. 'name' => new TranslatableMarkup('Not applicable'),
  172. 'weight' => ++$weight,
  173. ] + $locked_language);
  174. return $languages;
  175. }
  176. /**
  177. * {@inheritdoc}
  178. */
  179. public function isLanguageLocked($langcode) {
  180. $language = $this->getLanguage($langcode);
  181. return ($language ? $language->isLocked() : FALSE);
  182. }
  183. /**
  184. * {@inheritdoc}
  185. */
  186. public function getFallbackCandidates(array $context = []) {
  187. return [LanguageInterface::LANGCODE_DEFAULT];
  188. }
  189. /**
  190. * {@inheritdoc}
  191. */
  192. public function getLanguageSwitchLinks($type, Url $url) {
  193. return [];
  194. }
  195. /**
  196. * {@inheritdoc}
  197. */
  198. public static function getStandardLanguageList() {
  199. // This list is based on languages available from localize.drupal.org. See
  200. // http://localize.drupal.org/issues for information on how to add languages
  201. // there.
  202. //
  203. // The "Left-to-right marker" comments and the enclosed UTF-8 markers are to
  204. // make otherwise strange looking PHP syntax natural (to not be displayed in
  205. // right to left). See https://www.drupal.org/node/128866#comment-528929.
  206. return [
  207. 'af' => ['Afrikaans', 'Afrikaans'],
  208. 'am' => ['Amharic', 'አማርኛ'],
  209. 'ar' => ['Arabic', /* Left-to-right marker "‭" */ 'العربية', LanguageInterface::DIRECTION_RTL],
  210. 'ast' => ['Asturian', 'Asturianu'],
  211. 'az' => ['Azerbaijani', 'Azərbaycanca'],
  212. 'be' => ['Belarusian', 'Беларуская'],
  213. 'bg' => ['Bulgarian', 'Български'],
  214. 'bn' => ['Bengali', 'বাংলা'],
  215. 'bo' => ['Tibetan', 'བོད་སྐད་'],
  216. 'bs' => ['Bosnian', 'Bosanski'],
  217. 'ca' => ['Catalan', 'Català'],
  218. 'cs' => ['Czech', 'Čeština'],
  219. 'cy' => ['Welsh', 'Cymraeg'],
  220. 'da' => ['Danish', 'Dansk'],
  221. 'de' => ['German', 'Deutsch'],
  222. 'dz' => ['Dzongkha', 'རྫོང་ཁ'],
  223. 'el' => ['Greek', 'Ελληνικά'],
  224. 'en' => ['English', 'English'],
  225. 'en-x-simple' => ['Simple English', 'Simple English'],
  226. 'eo' => ['Esperanto', 'Esperanto'],
  227. 'es' => ['Spanish', 'Español'],
  228. 'et' => ['Estonian', 'Eesti'],
  229. 'eu' => ['Basque', 'Euskera'],
  230. 'fa' => ['Persian, Farsi', /* Left-to-right marker "‭" */ 'فارسی', LanguageInterface::DIRECTION_RTL],
  231. 'fi' => ['Finnish', 'Suomi'],
  232. 'fil' => ['Filipino', 'Filipino'],
  233. 'fo' => ['Faeroese', 'Føroyskt'],
  234. 'fr' => ['French', 'Français'],
  235. 'fy' => ['Frisian, Western', 'Frysk'],
  236. 'ga' => ['Irish', 'Gaeilge'],
  237. 'gd' => ['Scots Gaelic', 'Gàidhlig'],
  238. 'gl' => ['Galician', 'Galego'],
  239. 'gsw-berne' => ['Swiss German', 'Schwyzerdütsch'],
  240. 'gu' => ['Gujarati', 'ગુજરાતી'],
  241. 'he' => ['Hebrew', /* Left-to-right marker "‭" */ 'עברית', LanguageInterface::DIRECTION_RTL],
  242. 'hi' => ['Hindi', 'हिन्दी'],
  243. 'hr' => ['Croatian', 'Hrvatski'],
  244. 'ht' => ['Haitian Creole', 'Kreyòl ayisyen'],
  245. 'hu' => ['Hungarian', 'Magyar'],
  246. 'hy' => ['Armenian', 'Հայերեն'],
  247. 'id' => ['Indonesian', 'Bahasa Indonesia'],
  248. 'is' => ['Icelandic', 'Íslenska'],
  249. 'it' => ['Italian', 'Italiano'],
  250. 'ja' => ['Japanese', '日本語'],
  251. 'jv' => ['Javanese', 'Basa Java'],
  252. 'ka' => ['Georgian', 'ქართული ენა'],
  253. 'kk' => ['Kazakh', 'Қазақ'],
  254. 'km' => ['Khmer', 'ភាសាខ្មែរ'],
  255. 'kn' => ['Kannada', 'ಕನ್ನಡ'],
  256. 'ko' => ['Korean', '한국어'],
  257. 'ku' => ['Kurdish', 'Kurdî'],
  258. 'ky' => ['Kyrgyz', 'Кыргызча'],
  259. 'lo' => ['Lao', 'ພາສາລາວ'],
  260. 'lt' => ['Lithuanian', 'Lietuvių'],
  261. 'lv' => ['Latvian', 'Latviešu'],
  262. 'mg' => ['Malagasy', 'Malagasy'],
  263. 'mk' => ['Macedonian', 'Македонски'],
  264. 'ml' => ['Malayalam', 'മലയാളം'],
  265. 'mn' => ['Mongolian', 'монгол'],
  266. 'mr' => ['Marathi', 'मराठी'],
  267. 'ms' => ['Bahasa Malaysia', 'بهاس ملايو'],
  268. 'my' => ['Burmese', 'ဗမာစကား'],
  269. 'ne' => ['Nepali', 'नेपाली'],
  270. 'nl' => ['Dutch', 'Nederlands'],
  271. 'nb' => ['Norwegian Bokmål', 'Norsk, bokmål'],
  272. 'nn' => ['Norwegian Nynorsk', 'Norsk, nynorsk'],
  273. 'oc' => ['Occitan', 'Occitan'],
  274. 'pa' => ['Punjabi', 'ਪੰਜਾਬੀ'],
  275. 'pl' => ['Polish', 'Polski'],
  276. 'pt-pt' => ['Portuguese, Portugal', 'Português, Portugal'],
  277. 'pt-br' => ['Portuguese, Brazil', 'Português, Brasil'],
  278. 'ro' => ['Romanian', 'Română'],
  279. 'ru' => ['Russian', 'Русский'],
  280. 'sco' => ['Scots', 'Scots'],
  281. 'se' => ['Northern Sami', 'Sámi'],
  282. 'si' => ['Sinhala', 'සිංහල'],
  283. 'sk' => ['Slovak', 'Slovenčina'],
  284. 'sl' => ['Slovenian', 'Slovenščina'],
  285. 'sq' => ['Albanian', 'Shqip'],
  286. 'sr' => ['Serbian', 'Српски'],
  287. 'sv' => ['Swedish', 'Svenska'],
  288. 'sw' => ['Swahili', 'Kiswahili'],
  289. 'ta' => ['Tamil', 'தமிழ்'],
  290. 'ta-lk' => ['Tamil, Sri Lanka', 'தமிழ், இலங்கை'],
  291. 'te' => ['Telugu', 'తెలుగు'],
  292. 'th' => ['Thai', 'ภาษาไทย'],
  293. 'tr' => ['Turkish', 'Türkçe'],
  294. 'tyv' => ['Tuvan', 'Тыва дыл'],
  295. 'ug' => ['Uyghur', /* Left-to-right marker "‭" */ 'ئۇيغۇرچە', LanguageInterface::DIRECTION_RTL],
  296. 'uk' => ['Ukrainian', 'Українська'],
  297. 'ur' => ['Urdu', /* Left-to-right marker "‭" */ 'اردو', LanguageInterface::DIRECTION_RTL],
  298. 'vi' => ['Vietnamese', 'Tiếng Việt'],
  299. 'xx-lolspeak' => ['Lolspeak', 'Lolspeak'],
  300. 'zh-hans' => ['Chinese, Simplified', '简体中文'],
  301. 'zh-hant' => ['Chinese, Traditional', '繁體中文'],
  302. ];
  303. }
  304. /**
  305. * The 6 official languages used at the United Nations.
  306. *
  307. * This list is based on
  308. * http://www.un.org/en/sections/about-un/official-languages/index.html and it
  309. * uses the same format as getStandardLanguageList().
  310. *
  311. * @return array
  312. * An array with language codes as keys, and English and native language
  313. * names as values.
  314. */
  315. public static function getUnitedNationsLanguageList() {
  316. return [
  317. 'ar' => ['Arabic', /* Left-to-right marker "‭" */ 'العربية', LanguageInterface::DIRECTION_RTL],
  318. 'zh-hans' => ['Chinese, Simplified', '简体中文'],
  319. 'en' => ['English', 'English'],
  320. 'fr' => ['French', 'Français'],
  321. 'ru' => ['Russian', 'Русский'],
  322. 'es' => ['Spanish', 'Español'],
  323. ];
  324. }
  325. /**
  326. * Sets the configuration override language.
  327. *
  328. * This function is a noop since the configuration cannot be overridden by
  329. * language unless the Language module is enabled. That replaces the default
  330. * language manager with a configurable language manager.
  331. *
  332. * @param \Drupal\Core\Language\LanguageInterface $language
  333. * The language to override configuration with.
  334. *
  335. * @return $this
  336. *
  337. * @see \Drupal\language\ConfigurableLanguageManager::setConfigOverrideLanguage()
  338. */
  339. public function setConfigOverrideLanguage(LanguageInterface $language = NULL) {
  340. return $this;
  341. }
  342. /**
  343. * {@inheritdoc}
  344. */
  345. public function getConfigOverrideLanguage() {
  346. return $this->getCurrentLanguage();
  347. }
  348. /**
  349. * Filters the full list of languages based on the value of the flag.
  350. *
  351. * The locked languages are removed by default.
  352. *
  353. * @param \Drupal\Core\Language\LanguageInterface[] $languages
  354. * Array with languages to be filtered.
  355. * @param int $flags
  356. * (optional) Specifies the state of the languages that have to be returned.
  357. * It can be: LanguageInterface::STATE_CONFIGURABLE,
  358. * LanguageInterface::STATE_LOCKED, or LanguageInterface::STATE_ALL.
  359. *
  360. * @return \Drupal\Core\Language\LanguageInterface[]
  361. * An associative array of languages, keyed by the language code.
  362. */
  363. protected function filterLanguages(array $languages, $flags = LanguageInterface::STATE_CONFIGURABLE) {
  364. // STATE_ALL means we don't actually filter, so skip the rest of the method.
  365. if ($flags == LanguageInterface::STATE_ALL) {
  366. return $languages;
  367. }
  368. $filtered_languages = [];
  369. // Add the site's default language if requested.
  370. if ($flags & LanguageInterface::STATE_SITE_DEFAULT) {
  371. // Setup a language to have the defaults with data appropriate of the
  372. // default language only for runtime.
  373. $defaultLanguage = $this->getDefaultLanguage();
  374. $default = new Language(
  375. [
  376. 'id' => $defaultLanguage->getId(),
  377. 'name' => new TranslatableMarkup("Site's default language (@lang_name)",
  378. ['@lang_name' => $defaultLanguage->getName()]),
  379. 'direction' => $defaultLanguage->getDirection(),
  380. 'weight' => $defaultLanguage->getWeight(),
  381. ]
  382. );
  383. $filtered_languages[LanguageInterface::LANGCODE_SITE_DEFAULT] = $default;
  384. }
  385. foreach ($languages as $id => $language) {
  386. if (($language->isLocked() && ($flags & LanguageInterface::STATE_LOCKED)) || (!$language->isLocked() && ($flags & LanguageInterface::STATE_CONFIGURABLE))) {
  387. $filtered_languages[$id] = $language;
  388. }
  389. }
  390. return $filtered_languages;
  391. }
  392. }