MessageSelector.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <?php
  2. namespace Illuminate\Translation;
  3. use Illuminate\Support\Str;
  4. class MessageSelector
  5. {
  6. /**
  7. * Select a proper translation string based on the given number.
  8. *
  9. * @param string $line
  10. * @param int $number
  11. * @param string $locale
  12. * @return mixed
  13. */
  14. public function choose($line, $number, $locale)
  15. {
  16. $segments = explode('|', $line);
  17. if (($value = $this->extract($segments, $number)) !== null) {
  18. return trim($value);
  19. }
  20. $segments = $this->stripConditions($segments);
  21. $pluralIndex = $this->getPluralIndex($locale, $number);
  22. if (count($segments) == 1 || ! isset($segments[$pluralIndex])) {
  23. return $segments[0];
  24. }
  25. return $segments[$pluralIndex];
  26. }
  27. /**
  28. * Extract a translation string using inline conditions.
  29. *
  30. * @param array $segments
  31. * @param int $number
  32. * @return mixed
  33. */
  34. private function extract($segments, $number)
  35. {
  36. foreach ($segments as $part) {
  37. if (! is_null($line = $this->extractFromString($part, $number))) {
  38. return $line;
  39. }
  40. }
  41. }
  42. /**
  43. * Get the translation string if the condition matches.
  44. *
  45. * @param string $part
  46. * @param int $number
  47. * @return mixed
  48. */
  49. private function extractFromString($part, $number)
  50. {
  51. preg_match('/^[\{\[]([^\[\]\{\}]*)[\}\]](.*)/s', $part, $matches);
  52. if (count($matches) !== 3) {
  53. return;
  54. }
  55. $condition = $matches[1];
  56. $value = $matches[2];
  57. if (Str::contains($condition, ',')) {
  58. list($from, $to) = explode(',', $condition, 2);
  59. if ($to == '*' && $number >= $from) {
  60. return $value;
  61. } elseif ($from == '*' && $number <= $to) {
  62. return $value;
  63. } elseif ($number >= $from && $number <= $to) {
  64. return $value;
  65. }
  66. }
  67. return $condition == $number ? $value : null;
  68. }
  69. /**
  70. * Strip the inline conditions from each segment, just leaving the text.
  71. *
  72. * @param array $segments
  73. * @return array
  74. */
  75. private function stripConditions($segments)
  76. {
  77. return collect($segments)->map(function ($part) {
  78. return preg_replace('/^[\{\[]([^\[\]\{\}]*)[\}\]]/', '', $part);
  79. })->all();
  80. }
  81. /**
  82. * Get the index to use for pluralization.
  83. *
  84. * The plural rules are derived from code of the Zend Framework (2010-09-25), which
  85. * is subject to the new BSD license (http://framework.zend.com/license/new-bsd)
  86. * Copyright (c) 2005-2010 - Zend Technologies USA Inc. (http://www.zend.com)
  87. *
  88. * @param string $locale
  89. * @param int $number
  90. * @return int
  91. */
  92. public function getPluralIndex($locale, $number)
  93. {
  94. switch ($locale) {
  95. case 'az':
  96. case 'az_AZ':
  97. case 'bo':
  98. case 'bo_CN':
  99. case 'bo_IN':
  100. case 'dz':
  101. case 'dz_BT':
  102. case 'id':
  103. case 'id_ID':
  104. case 'ja':
  105. case 'ja_JP':
  106. case 'jv':
  107. case 'ka':
  108. case 'ka_GE':
  109. case 'km':
  110. case 'km_KH':
  111. case 'kn':
  112. case 'kn_IN':
  113. case 'ko':
  114. case 'ko_KR':
  115. case 'ms':
  116. case 'ms_MY':
  117. case 'th':
  118. case 'th_TH':
  119. case 'tr':
  120. case 'tr_CY':
  121. case 'tr_TR':
  122. case 'vi':
  123. case 'vi_VN':
  124. case 'zh':
  125. case 'zh_CN':
  126. case 'zh_HK':
  127. case 'zh_SG':
  128. case 'zh_TW':
  129. return 0;
  130. case 'af':
  131. case 'af_ZA':
  132. case 'bn':
  133. case 'bn_BD':
  134. case 'bn_IN':
  135. case 'bg':
  136. case 'bg_BG':
  137. case 'ca':
  138. case 'ca_AD':
  139. case 'ca_ES':
  140. case 'ca_FR':
  141. case 'ca_IT':
  142. case 'da':
  143. case 'da_DK':
  144. case 'de':
  145. case 'de_AT':
  146. case 'de_BE':
  147. case 'de_CH':
  148. case 'de_DE':
  149. case 'de_LI':
  150. case 'de_LU':
  151. case 'el':
  152. case 'el_CY':
  153. case 'el_GR':
  154. case 'en':
  155. case 'en_AG':
  156. case 'en_AU':
  157. case 'en_BW':
  158. case 'en_CA':
  159. case 'en_DK':
  160. case 'en_GB':
  161. case 'en_HK':
  162. case 'en_IE':
  163. case 'en_IN':
  164. case 'en_NG':
  165. case 'en_NZ':
  166. case 'en_PH':
  167. case 'en_SG':
  168. case 'en_US':
  169. case 'en_ZA':
  170. case 'en_ZM':
  171. case 'en_ZW':
  172. case 'eo':
  173. case 'eo_US':
  174. case 'es':
  175. case 'es_AR':
  176. case 'es_BO':
  177. case 'es_CL':
  178. case 'es_CO':
  179. case 'es_CR':
  180. case 'es_CU':
  181. case 'es_DO':
  182. case 'es_EC':
  183. case 'es_ES':
  184. case 'es_GT':
  185. case 'es_HN':
  186. case 'es_MX':
  187. case 'es_NI':
  188. case 'es_PA':
  189. case 'es_PE':
  190. case 'es_PR':
  191. case 'es_PY':
  192. case 'es_SV':
  193. case 'es_US':
  194. case 'es_UY':
  195. case 'es_VE':
  196. case 'et':
  197. case 'et_EE':
  198. case 'eu':
  199. case 'eu_ES':
  200. case 'eu_FR':
  201. case 'fa':
  202. case 'fa_IR':
  203. case 'fi':
  204. case 'fi_FI':
  205. case 'fo':
  206. case 'fo_FO':
  207. case 'fur':
  208. case 'fur_IT':
  209. case 'fy':
  210. case 'fy_DE':
  211. case 'fy_NL':
  212. case 'gl':
  213. case 'gl_ES':
  214. case 'gu':
  215. case 'gu_IN':
  216. case 'ha':
  217. case 'ha_NG':
  218. case 'he':
  219. case 'he_IL':
  220. case 'hu':
  221. case 'hu_HU':
  222. case 'is':
  223. case 'is_IS':
  224. case 'it':
  225. case 'it_CH':
  226. case 'it_IT':
  227. case 'ku':
  228. case 'ku_TR':
  229. case 'lb':
  230. case 'lb_LU':
  231. case 'ml':
  232. case 'ml_IN':
  233. case 'mn':
  234. case 'mn_MN':
  235. case 'mr':
  236. case 'mr_IN':
  237. case 'nah':
  238. case 'nb':
  239. case 'nb_NO':
  240. case 'ne':
  241. case 'ne_NP':
  242. case 'nl':
  243. case 'nl_AW':
  244. case 'nl_BE':
  245. case 'nl_NL':
  246. case 'nn':
  247. case 'nn_NO':
  248. case 'no':
  249. case 'om':
  250. case 'om_ET':
  251. case 'om_KE':
  252. case 'or':
  253. case 'or_IN':
  254. case 'pa':
  255. case 'pa_IN':
  256. case 'pa_PK':
  257. case 'pap':
  258. case 'pap_AN':
  259. case 'pap_AW':
  260. case 'pap_CW':
  261. case 'ps':
  262. case 'ps_AF':
  263. case 'pt':
  264. case 'pt_BR':
  265. case 'pt_PT':
  266. case 'so':
  267. case 'so_DJ':
  268. case 'so_ET':
  269. case 'so_KE':
  270. case 'so_SO':
  271. case 'sq':
  272. case 'sq_AL':
  273. case 'sq_MK':
  274. case 'sv':
  275. case 'sv_FI':
  276. case 'sv_SE':
  277. case 'sw':
  278. case 'sw_KE':
  279. case 'sw_TZ':
  280. case 'ta':
  281. case 'ta_IN':
  282. case 'ta_LK':
  283. case 'te':
  284. case 'te_IN':
  285. case 'tk':
  286. case 'tk_TM':
  287. case 'ur':
  288. case 'ur_IN':
  289. case 'ur_PK':
  290. case 'zu':
  291. case 'zu_ZA':
  292. return ($number == 1) ? 0 : 1;
  293. case 'am':
  294. case 'am_ET':
  295. case 'bh':
  296. case 'fil':
  297. case 'fil_PH':
  298. case 'fr':
  299. case 'fr_BE':
  300. case 'fr_CA':
  301. case 'fr_CH':
  302. case 'fr_FR':
  303. case 'fr_LU':
  304. case 'gun':
  305. case 'hi':
  306. case 'hi_IN':
  307. case 'hy':
  308. case 'hy_AM':
  309. case 'ln':
  310. case 'ln_CD':
  311. case 'mg':
  312. case 'mg_MG':
  313. case 'nso':
  314. case 'nso_ZA':
  315. case 'ti':
  316. case 'ti_ER':
  317. case 'ti_ET':
  318. case 'wa':
  319. case 'wa_BE':
  320. case 'xbr':
  321. return (($number == 0) || ($number == 1)) ? 0 : 1;
  322. case 'be':
  323. case 'be_BY':
  324. case 'bs':
  325. case 'bs_BA':
  326. case 'hr':
  327. case 'hr_HR':
  328. case 'ru':
  329. case 'ru_RU':
  330. case 'ru_UA':
  331. case 'sr':
  332. case 'sr_ME':
  333. case 'sr_RS':
  334. case 'uk':
  335. case 'uk_UA':
  336. return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
  337. case 'cs':
  338. case 'cs_CZ':
  339. case 'sk':
  340. case 'sk_SK':
  341. return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
  342. case 'ga':
  343. case 'ga_IE':
  344. return ($number == 1) ? 0 : (($number == 2) ? 1 : 2);
  345. case 'lt':
  346. case 'lt_LT':
  347. return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
  348. case 'sl':
  349. case 'sl_SI':
  350. return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3));
  351. case 'mk':
  352. case 'mk_MK':
  353. return ($number % 10 == 1) ? 0 : 1;
  354. case 'mt':
  355. case 'mt_MT':
  356. return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
  357. case 'lv':
  358. case 'lv_LV':
  359. return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2);
  360. case 'pl':
  361. case 'pl_PL':
  362. return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
  363. case 'cy':
  364. case 'cy_GB':
  365. return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3));
  366. case 'ro':
  367. case 'ro_RO':
  368. return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
  369. case 'ar':
  370. case 'ar_AE':
  371. case 'ar_BH':
  372. case 'ar_DZ':
  373. case 'ar_EG':
  374. case 'ar_IN':
  375. case 'ar_IQ':
  376. case 'ar_JO':
  377. case 'ar_KW':
  378. case 'ar_LB':
  379. case 'ar_LY':
  380. case 'ar_MA':
  381. case 'ar_OM':
  382. case 'ar_QA':
  383. case 'ar_SA':
  384. case 'ar_SD':
  385. case 'ar_SS':
  386. case 'ar_SY':
  387. case 'ar_TN':
  388. case 'ar_YE':
  389. return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
  390. default:
  391. return 0;
  392. }
  393. }
  394. }