I18nTest.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 1.2.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\I18n;
  16. use Aura\Intl\Package;
  17. use Cake\Cache\Cache;
  18. use Cake\Core\Plugin;
  19. use Cake\I18n\I18n;
  20. use Cake\TestSuite\TestCase;
  21. use Locale;
  22. /**
  23. * I18nTest class
  24. */
  25. class I18nTest extends TestCase
  26. {
  27. /**
  28. * Used to restore the internal locale after tests
  29. *
  30. * @var string
  31. */
  32. public $locale;
  33. /**
  34. * Set Up
  35. *
  36. * @return void
  37. */
  38. public function setUp()
  39. {
  40. parent::setUp();
  41. $this->locale = Locale::getDefault() ?: I18n::DEFAULT_LOCALE;
  42. }
  43. /**
  44. * Tear down method
  45. *
  46. * @return void
  47. */
  48. public function tearDown()
  49. {
  50. parent::tearDown();
  51. I18n::clear();
  52. I18n::setDefaultFormatter('default');
  53. I18n::setLocale($this->locale);
  54. $this->clearPlugins();
  55. Cache::clear(false, '_cake_core_');
  56. }
  57. /**
  58. * Tests that the default locale is set correctly
  59. *
  60. * @group deprecated
  61. * @return void
  62. */
  63. public function testDefaultLocale()
  64. {
  65. $this->deprecated(function () {
  66. $newLocale = 'de_DE';
  67. I18n::setLocale($newLocale);
  68. $this->assertEquals($newLocale, I18n::getLocale());
  69. $this->assertEquals($this->locale, I18n::getDefaultLocale());
  70. });
  71. }
  72. /**
  73. * Tests that a default translator is created and messages are parsed
  74. * correctly
  75. *
  76. * @return void
  77. */
  78. public function testGetDefaultTranslator()
  79. {
  80. $translator = I18n::getTranslator();
  81. $this->assertInstanceOf('Aura\Intl\TranslatorInterface', $translator);
  82. $this->assertEquals('%d is 1 (po translated)', $translator->translate('%d = 1'));
  83. $this->assertSame($translator, I18n::getTranslator(), 'backwards compat works');
  84. }
  85. /**
  86. * Tests that the translator can automatically load messages from a .mo file
  87. *
  88. * @return void
  89. */
  90. public function testGetTranslatorLoadMoFile()
  91. {
  92. $translator = I18n::getTranslator('default', 'es_ES');
  93. $this->assertEquals('Plural Rule 6 (translated)', $translator->translate('Plural Rule 1'));
  94. }
  95. /**
  96. * Tests that plural rules are correctly used for the English language
  97. * using the sprintf formatter
  98. *
  99. * @return void
  100. */
  101. public function testPluralSelectionSprintfFormatter()
  102. {
  103. I18n::setDefaultFormatter('sprintf');
  104. $translator = I18n::getTranslator(); // en_US
  105. $result = $translator->translate('%d = 0 or > 1', ['_count' => 1, 1]);
  106. $this->assertEquals('1 is 1 (po translated)', $result);
  107. $result = $translator->translate('%d = 0 or > 1', ['_count' => 2, 2]);
  108. $this->assertEquals('2 is 2-4 (po translated)', $result);
  109. }
  110. /**
  111. * Tests that plural rules are correctly used for the English language
  112. * using the basic formatter
  113. *
  114. * @return void
  115. */
  116. public function testPluralSelectionBasicFormatter()
  117. {
  118. $translator = I18n::getTranslator('special');
  119. $result = $translator->translate('There are {0} things', ['_count' => 2, 'plenty']);
  120. $this->assertEquals('There are plenty things', $result);
  121. $result = $translator->translate('There are {0} things', ['_count' => 1]);
  122. $this->assertEquals('There is only one', $result);
  123. }
  124. /**
  125. * Test plural rules are used for non-english languages
  126. *
  127. * @return void
  128. */
  129. public function testPluralSelectionRussian()
  130. {
  131. $translator = I18n::getTranslator('default', 'ru');
  132. $result = $translator->translate('{0} months', ['_count' => 1, 1]);
  133. $this->assertEquals('1 months ends in 1, not 11', $result);
  134. $result = $translator->translate('{0} months', ['_count' => 2, 2]);
  135. $this->assertEquals('2 months ends in 2-4, not 12-14', $result);
  136. $result = $translator->translate('{0} months', ['_count' => 7, 7]);
  137. $this->assertEquals('7 months everything else', $result);
  138. }
  139. /**
  140. * Tests that custom translation packages can be created on the fly and used later on
  141. *
  142. * @return void
  143. */
  144. public function testCreateCustomTranslationPackage()
  145. {
  146. I18n::setTranslator('custom', function () {
  147. $package = new Package('default');
  148. $package->setMessages([
  149. 'Cow' => 'Le moo',
  150. ]);
  151. return $package;
  152. }, 'fr_FR');
  153. $translator = I18n::getTranslator('custom', 'fr_FR');
  154. $this->assertEquals('Le moo', $translator->translate('Cow'));
  155. }
  156. /**
  157. * Tests that messages can also be loaded from plugins by using the
  158. * domain = plugin_name convention
  159. *
  160. * @return void
  161. */
  162. public function testPluginMesagesLoad()
  163. {
  164. $this->loadPlugins([
  165. 'TestPlugin',
  166. 'Company/TestPluginThree',
  167. ]);
  168. $translator = I18n::getTranslator('test_plugin');
  169. $this->assertEquals(
  170. 'Plural Rule 1 (from plugin)',
  171. $translator->translate('Plural Rule 1')
  172. );
  173. $translator = I18n::getTranslator('company/test_plugin_three');
  174. $this->assertEquals(
  175. 'String 1 (from plugin three)',
  176. $translator->translate('String 1')
  177. );
  178. }
  179. /**
  180. * Tests that messages messages from a plugin can be automatically
  181. * overridden by messages in app
  182. *
  183. * @return void
  184. */
  185. public function testPluginOverride()
  186. {
  187. $this->loadPlugins(['TestTheme']);
  188. $translator = I18n::getTranslator('test_theme');
  189. $this->assertEquals(
  190. 'translated',
  191. $translator->translate('A Message')
  192. );
  193. }
  194. /**
  195. * Tests the locale method
  196. *
  197. * @return void
  198. */
  199. public function testGetDefaultLocale()
  200. {
  201. $this->assertEquals('en_US', I18n::getLocale());
  202. $this->assertEquals('en_US', ini_get('intl.default_locale'));
  203. I18n::setLocale('fr_FR');
  204. $this->assertEquals('fr_FR', I18n::getLocale());
  205. $this->assertEquals('fr_FR', ini_get('intl.default_locale'));
  206. }
  207. /**
  208. * Tests that changing the default locale also changes the way translators
  209. * are fetched
  210. *
  211. * @return void
  212. */
  213. public function testGetTranslatorByDefaultLocale()
  214. {
  215. I18n::setTranslator('custom', function () {
  216. $package = new Package('default');
  217. $package->setMessages([
  218. 'Cow' => 'Le moo',
  219. ]);
  220. return $package;
  221. }, 'fr_FR');
  222. I18n::setLocale('fr_FR');
  223. $translator = I18n::getTranslator('custom');
  224. $this->assertEquals('Le moo', $translator->translate('Cow'));
  225. }
  226. /**
  227. * Tests the __() function
  228. *
  229. * @return void
  230. */
  231. public function testBasicTranslateFunction()
  232. {
  233. I18n::setDefaultFormatter('sprintf');
  234. $this->assertEquals('%d is 1 (po translated)', __('%d = 1'));
  235. $this->assertEquals('1 is 1 (po translated)', __('%d = 1', 1));
  236. $this->assertEquals('1 is 1 (po translated)', __('%d = 1', [1]));
  237. $this->assertEquals('The red dog, and blue cat', __('The %s dog, and %s cat', ['red', 'blue']));
  238. $this->assertEquals('The red dog, and blue cat', __('The %s dog, and %s cat', 'red', 'blue'));
  239. }
  240. /**
  241. * Tests the __() functions with explicit null params
  242. *
  243. * @return void
  244. */
  245. public function testBasicTranslateFunctionsWithNullParam()
  246. {
  247. $this->assertEquals('text {0}', __('text {0}'));
  248. $this->assertEquals('text ', __('text {0}', null));
  249. $this->assertEquals('text {0}', __n('text {0}', 'texts {0}', 1));
  250. $this->assertEquals('text ', __n('text {0}', 'texts {0}', 1, null));
  251. $this->assertEquals('text {0}', __d('default', 'text {0}'));
  252. $this->assertEquals('text ', __d('default', 'text {0}', null));
  253. $this->assertEquals('text {0}', __dn('default', 'text {0}', 'texts {0}', 1));
  254. $this->assertEquals('text ', __dn('default', 'text {0}', 'texts {0}', 1, null));
  255. $this->assertEquals('text {0}', __x('default', 'text {0}'));
  256. $this->assertEquals('text ', __x('default', 'text {0}', null));
  257. $this->assertEquals('text {0}', __xn('default', 'text {0}', 'texts {0}', 1));
  258. $this->assertEquals('text ', __xn('default', 'text {0}', 'texts {0}', 1, null));
  259. $this->assertEquals('text {0}', __dx('default', 'words', 'text {0}'));
  260. $this->assertEquals('text ', __dx('default', 'words', 'text {0}', null));
  261. $this->assertEquals('text {0}', __dxn('default', 'words', 'text {0}', 'texts {0}', 1));
  262. $this->assertEquals('text ', __dxn('default', 'words', 'text {0}', 'texts {0}', 1, null));
  263. }
  264. /**
  265. * Tests the __() function on a plural key works
  266. *
  267. * @return void
  268. */
  269. public function testBasicTranslateFunctionPluralData()
  270. {
  271. I18n::setDefaultFormatter('sprintf');
  272. $this->assertEquals('%d is 1 (po translated)', __('%d = 0 or > 1'));
  273. }
  274. /**
  275. * Tests the __n() function
  276. *
  277. * @return void
  278. */
  279. public function testBasicTranslatePluralFunction()
  280. {
  281. I18n::setDefaultFormatter('sprintf');
  282. $result = __n('singular msg', '%d = 0 or > 1', 1, 1);
  283. $this->assertEquals('1 is 1 (po translated)', $result);
  284. $result = __n('singular msg', '%d = 0 or > 1', 2, 2);
  285. $this->assertEquals('2 is 2-4 (po translated)', $result);
  286. $result = __n('%s, %s, and %s are good', '%s, %s, and %s are best', 1, ['red', 'blue', 'green']);
  287. $this->assertSame('red, blue, and green are good', $result);
  288. $result = __n('%s, %s, and %s are good', '%s, %s, and %s are best', 1, 'red', 'blue', 'green');
  289. $this->assertSame('red, blue, and green are good', $result);
  290. }
  291. /**
  292. * Tests the __n() function on singular keys
  293. *
  294. * @return void
  295. */
  296. public function testBasicTranslatePluralFunctionSingularMessage()
  297. {
  298. I18n::setDefaultFormatter('sprintf');
  299. $result = __n('No translation needed', 'not used', 1);
  300. $this->assertEquals('No translation needed', $result);
  301. }
  302. /**
  303. * Tests the __d() function
  304. *
  305. * @return void
  306. */
  307. public function testBasicDomainFunction()
  308. {
  309. I18n::setTranslator('custom', function () {
  310. $package = new Package('default');
  311. $package->setMessages([
  312. 'Cow' => 'Le moo',
  313. 'The {0} is tasty' => 'The {0} is delicious',
  314. 'Average price {0}' => 'Price Average {0}',
  315. 'Unknown' => '',
  316. ]);
  317. return $package;
  318. }, 'en_US');
  319. $this->assertEquals('Le moo', __d('custom', 'Cow'));
  320. $this->assertEquals('Unknown', __d('custom', 'Unknown'));
  321. $result = __d('custom', 'The {0} is tasty', ['fruit']);
  322. $this->assertEquals('The fruit is delicious', $result);
  323. $result = __d('custom', 'The {0} is tasty', 'fruit');
  324. $this->assertEquals('The fruit is delicious', $result);
  325. $result = __d('custom', 'Average price {0}', ['9.99']);
  326. $this->assertEquals('Price Average 9.99', $result);
  327. }
  328. /**
  329. * Tests the __dn() function
  330. *
  331. * @return void
  332. */
  333. public function testBasicDomainPluralFunction()
  334. {
  335. I18n::setTranslator('custom', function () {
  336. $package = new Package('default');
  337. $package->setMessages([
  338. 'Cow' => 'Le Moo',
  339. 'Cows' => [
  340. 'Le Moo',
  341. 'Les Moos',
  342. ],
  343. '{0} years' => [
  344. '',
  345. '',
  346. ],
  347. ]);
  348. return $package;
  349. }, 'en_US');
  350. $this->assertEquals('Le Moo', __dn('custom', 'Cow', 'Cows', 1));
  351. $this->assertEquals('Les Moos', __dn('custom', 'Cow', 'Cows', 2));
  352. $this->assertEquals('{0} years', __dn('custom', '{0} year', '{0} years', 1));
  353. $this->assertEquals('{0} years', __dn('custom', '{0} year', '{0} years', 2));
  354. }
  355. /**
  356. * Tests the __x() function
  357. *
  358. * @return void
  359. */
  360. public function testBasicContextFunction()
  361. {
  362. I18n::setTranslator('default', function () {
  363. $package = new Package('default');
  364. $package->setMessages([
  365. 'letter' => [
  366. '_context' => [
  367. 'character' => 'The letter {0}',
  368. 'communication' => 'She wrote a letter to {0}',
  369. ],
  370. ],
  371. 'letters' => [
  372. '_context' => [
  373. 'character' => [
  374. 'The letter {0}',
  375. 'The letters {0} and {1}',
  376. ],
  377. 'communication' => [
  378. 'She wrote a letter to {0}',
  379. 'She wrote a letter to {0} and {1}',
  380. ],
  381. ],
  382. ],
  383. ]);
  384. return $package;
  385. }, 'en_US');
  386. $this->assertEquals('The letters A and B', __x('character', 'letters', ['A', 'B']));
  387. $this->assertEquals('The letter A', __x('character', 'letter', ['A']));
  388. $this->assertEquals('The letters A and B', __x('character', 'letters', 'A', 'B'));
  389. $this->assertEquals('The letter A', __x('character', 'letter', 'A'));
  390. $this->assertEquals(
  391. 'She wrote a letter to Thomas and Sara',
  392. __x('communication', 'letters', ['Thomas', 'Sara'])
  393. );
  394. $this->assertEquals(
  395. 'She wrote a letter to Thomas',
  396. __x('communication', 'letter', ['Thomas'])
  397. );
  398. $this->assertEquals(
  399. 'She wrote a letter to Thomas and Sara',
  400. __x('communication', 'letters', 'Thomas', 'Sara')
  401. );
  402. $this->assertEquals(
  403. 'She wrote a letter to Thomas',
  404. __x('communication', 'letter', 'Thomas')
  405. );
  406. }
  407. /**
  408. * Tests the __x() function with no msgstr
  409. *
  410. * @return void
  411. */
  412. public function testBasicContextFunctionNoString()
  413. {
  414. I18n::setTranslator('default', function () {
  415. $package = new Package('default');
  416. $package->setMessages([
  417. 'letter' => [
  418. '_context' => [
  419. 'character' => '',
  420. ],
  421. ],
  422. ]);
  423. return $package;
  424. }, 'en_US');
  425. $this->assertEquals('letter', __x('character', 'letter'));
  426. $this->assertEquals('letter', __x('unknown', 'letter'));
  427. }
  428. /**
  429. * Tests the __x() function with an invalid context
  430. *
  431. * @return void
  432. */
  433. public function testBasicContextFunctionInvalidContext()
  434. {
  435. I18n::setTranslator('default', function () {
  436. $package = new Package('default');
  437. $package->setMessages([
  438. 'letter' => [
  439. '_context' => [
  440. 'noun' => 'a paper letter',
  441. ],
  442. ],
  443. ]);
  444. return $package;
  445. }, 'en_US');
  446. $this->assertEquals('letter', __x('garbage', 'letter'));
  447. $this->assertEquals('a paper letter', __('letter'));
  448. }
  449. /**
  450. * Tests the __xn() function
  451. *
  452. * @return void
  453. */
  454. public function testPluralContextFunction()
  455. {
  456. I18n::setTranslator('default', function () {
  457. $package = new Package('default');
  458. $package->setMessages([
  459. 'letter' => [
  460. '_context' => [
  461. 'character' => 'The letter {0}',
  462. 'communication' => 'She wrote a letter to {0}',
  463. ],
  464. ],
  465. 'letters' => [
  466. '_context' => [
  467. 'character' => [
  468. 'The letter {0}',
  469. 'The letters {0} and {1}',
  470. ],
  471. 'communication' => [
  472. 'She wrote a letter to {0}',
  473. 'She wrote a letter to {0} and {1}',
  474. ],
  475. ],
  476. ],
  477. ]);
  478. return $package;
  479. }, 'en_US');
  480. $this->assertEquals('The letters A and B', __xn('character', 'letter', 'letters', 2, ['A', 'B']));
  481. $this->assertEquals('The letter A', __xn('character', 'letter', 'letters', 1, ['A']));
  482. $this->assertEquals(
  483. 'She wrote a letter to Thomas and Sara',
  484. __xn('communication', 'letter', 'letters', 2, ['Thomas', 'Sara'])
  485. );
  486. $this->assertEquals(
  487. 'She wrote a letter to Thomas',
  488. __xn('communication', 'letter', 'letters', 1, ['Thomas'])
  489. );
  490. $this->assertEquals(
  491. 'She wrote a letter to Thomas and Sara',
  492. __xn('communication', 'letter', 'letters', 2, 'Thomas', 'Sara')
  493. );
  494. $this->assertEquals(
  495. 'She wrote a letter to Thomas',
  496. __xn('communication', 'letter', 'letters', 1, 'Thomas')
  497. );
  498. }
  499. /**
  500. * Tests the __dx() function
  501. *
  502. * @return void
  503. */
  504. public function testDomainContextFunction()
  505. {
  506. I18n::setTranslator('custom', function () {
  507. $package = new Package('default');
  508. $package->setMessages([
  509. 'letter' => [
  510. '_context' => [
  511. 'character' => 'The letter {0}',
  512. 'communication' => 'She wrote a letter to {0}',
  513. ],
  514. ],
  515. 'letters' => [
  516. '_context' => [
  517. 'character' => [
  518. 'The letter {0}',
  519. 'The letters {0} and {1}',
  520. ],
  521. 'communication' => [
  522. 'She wrote a letter to {0}',
  523. 'She wrote a letter to {0} and {1}',
  524. ],
  525. ],
  526. ],
  527. ]);
  528. return $package;
  529. }, 'en_US');
  530. $this->assertEquals('The letters A and B', __dx('custom', 'character', 'letters', ['A', 'B']));
  531. $this->assertEquals('The letter A', __dx('custom', 'character', 'letter', ['A']));
  532. $this->assertEquals(
  533. 'She wrote a letter to Thomas and Sara',
  534. __dx('custom', 'communication', 'letters', ['Thomas', 'Sara'])
  535. );
  536. $this->assertEquals(
  537. 'She wrote a letter to Thomas',
  538. __dx('custom', 'communication', 'letter', ['Thomas'])
  539. );
  540. $this->assertEquals(
  541. 'She wrote a letter to Thomas and Sara',
  542. __dx('custom', 'communication', 'letters', 'Thomas', 'Sara')
  543. );
  544. $this->assertEquals(
  545. 'She wrote a letter to Thomas',
  546. __dx('custom', 'communication', 'letter', 'Thomas')
  547. );
  548. }
  549. /**
  550. * Tests the __dxn() function
  551. *
  552. * @return void
  553. */
  554. public function testDomainPluralContextFunction()
  555. {
  556. I18n::setTranslator('custom', function () {
  557. $package = new Package('default');
  558. $package->setMessages([
  559. 'letter' => [
  560. '_context' => [
  561. 'character' => 'The letter {0}',
  562. 'communication' => 'She wrote a letter to {0}',
  563. ],
  564. ],
  565. 'letters' => [
  566. '_context' => [
  567. 'character' => [
  568. 'The letter {0}',
  569. 'The letters {0} and {1}',
  570. ],
  571. 'communication' => [
  572. 'She wrote a letter to {0}',
  573. 'She wrote a letter to {0} and {1}',
  574. ],
  575. ],
  576. ],
  577. ]);
  578. return $package;
  579. }, 'en_US');
  580. $this->assertEquals(
  581. 'The letters A and B',
  582. __dxn('custom', 'character', 'letter', 'letters', 2, ['A', 'B'])
  583. );
  584. $this->assertEquals(
  585. 'The letter A',
  586. __dxn('custom', 'character', 'letter', 'letters', 1, ['A'])
  587. );
  588. $this->assertEquals(
  589. 'She wrote a letter to Thomas and Sara',
  590. __dxn('custom', 'communication', 'letter', 'letters', 2, ['Thomas', 'Sara'])
  591. );
  592. $this->assertEquals(
  593. 'She wrote a letter to Thomas',
  594. __dxn('custom', 'communication', 'letter', 'letters', 1, ['Thomas'])
  595. );
  596. $this->assertEquals(
  597. 'She wrote a letter to Thomas and Sara',
  598. __dxn('custom', 'communication', 'letter', 'letters', 2, 'Thomas', 'Sara')
  599. );
  600. $this->assertEquals(
  601. 'She wrote a letter to Thomas',
  602. __dxn('custom', 'communication', 'letter', 'letters', 1, 'Thomas')
  603. );
  604. }
  605. /**
  606. * Tests that translators are cached for performance
  607. *
  608. * @return void
  609. */
  610. public function testTranslatorCache()
  611. {
  612. $english = I18n::getTranslator();
  613. $spanish = I18n::getTranslator('default', 'es_ES');
  614. $cached = Cache::read('translations.default.en_US', '_cake_core_');
  615. $this->assertEquals($english, $cached);
  616. $cached = Cache::read('translations.default.es_ES', '_cake_core_');
  617. $this->assertEquals($spanish, $cached);
  618. $this->assertSame($english, I18n::getTranslator());
  619. $this->assertSame($spanish, I18n::getTranslator('default', 'es_ES'));
  620. $this->assertSame($english, I18n::getTranslator());
  621. }
  622. /**
  623. * Tests that it is possible to register a generic translators factory for a domain
  624. * instead of having to create them manually
  625. *
  626. * @return void
  627. */
  628. public function testLoaderFactory()
  629. {
  630. I18n::config('custom', function ($name, $locale) {
  631. $this->assertEquals('custom', $name);
  632. $package = new Package('default');
  633. if ($locale == 'fr_FR') {
  634. $package->setMessages([
  635. 'Cow' => 'Le Moo',
  636. 'Cows' => [
  637. 'Le Moo',
  638. 'Les Moos',
  639. ],
  640. ]);
  641. }
  642. if ($locale === 'es_ES') {
  643. $package->setMessages([
  644. 'Cow' => 'El Moo',
  645. 'Cows' => [
  646. 'El Moo',
  647. 'Los Moos',
  648. ],
  649. ]);
  650. }
  651. return $package;
  652. });
  653. $translator = I18n::getTranslator('custom', 'fr_FR');
  654. $this->assertEquals('Le Moo', $translator->translate('Cow'));
  655. $this->assertEquals('Les Moos', $translator->translate('Cows', ['_count' => 2]));
  656. $translator = I18n::getTranslator('custom', 'es_ES');
  657. $this->assertEquals('El Moo', $translator->translate('Cow'));
  658. $this->assertEquals('Los Moos', $translator->translate('Cows', ['_count' => 2]));
  659. $translator = I18n::getTranslator();
  660. $this->assertEquals('%d is 1 (po translated)', $translator->translate('%d = 1'));
  661. }
  662. /**
  663. * Tests that it is possible to register a fallback translators factory
  664. *
  665. * @return void
  666. */
  667. public function testFallbackLoaderFactory()
  668. {
  669. I18n::config('_fallback', function ($name) {
  670. $package = new Package('default');
  671. if ($name == 'custom') {
  672. $package->setMessages([
  673. 'Cow' => 'Le Moo custom',
  674. ]);
  675. } else {
  676. $package->setMessages([
  677. 'Cow' => 'Le Moo default',
  678. ]);
  679. }
  680. return $package;
  681. });
  682. $translator = I18n::getTranslator('custom');
  683. $this->assertEquals('Le Moo custom', $translator->translate('Cow'));
  684. $translator = I18n::getTranslator();
  685. $this->assertEquals('Le Moo default', $translator->translate('Cow'));
  686. }
  687. /**
  688. * Tests that missing translations will get fallbacked to the default translator
  689. *
  690. * @return void
  691. */
  692. public function testFallbackTranslator()
  693. {
  694. I18n::setTranslator('default', function () {
  695. $package = new Package('default');
  696. $package->setMessages([
  697. 'Dog' => 'Le bark',
  698. ]);
  699. return $package;
  700. }, 'fr_FR');
  701. I18n::setTranslator('custom', function () {
  702. $package = new Package('default');
  703. $package->setMessages([
  704. 'Cow' => 'Le moo',
  705. ]);
  706. return $package;
  707. }, 'fr_FR');
  708. $translator = I18n::getTranslator('custom', 'fr_FR');
  709. $this->assertEquals('Le moo', $translator->translate('Cow'));
  710. $this->assertEquals('Le bark', $translator->translate('Dog'));
  711. }
  712. /**
  713. * Test that the translation fallback can be disabled
  714. *
  715. * @return void
  716. */
  717. public function testFallbackTranslatorDisabled()
  718. {
  719. I18n::useFallback(false);
  720. I18n::setTranslator('default', function () {
  721. $package = new Package('default');
  722. $package->setMessages(['Dog' => 'Le bark']);
  723. return $package;
  724. }, 'fr_FR');
  725. I18n::setTranslator('custom', function () {
  726. $package = new Package('default');
  727. $package->setMessages(['Cow' => 'Le moo']);
  728. return $package;
  729. }, 'fr_FR');
  730. $translator = I18n::getTranslator('custom', 'fr_FR');
  731. $this->assertEquals('Le moo', $translator->translate('Cow'));
  732. $this->assertEquals('Dog', $translator->translate('Dog'));
  733. }
  734. /**
  735. * Tests that it is possible to register a generic translators factory for a domain
  736. * instead of having to create them manually
  737. *
  738. * @return void
  739. */
  740. public function testFallbackTranslatorWithFactory()
  741. {
  742. I18n::setTranslator('default', function () {
  743. $package = new Package('default');
  744. $package->setMessages([
  745. 'Dog' => 'Le bark',
  746. ]);
  747. return $package;
  748. }, 'fr_FR');
  749. I18n::config('custom', function ($name, $locale) {
  750. $this->assertEquals('custom', $name);
  751. $package = new Package('default');
  752. $package->setMessages([
  753. 'Cow' => 'Le moo',
  754. ]);
  755. return $package;
  756. });
  757. $translator = I18n::getTranslator('custom', 'fr_FR');
  758. $this->assertEquals('Le moo', $translator->translate('Cow'));
  759. $this->assertEquals('Le bark', $translator->translate('Dog'));
  760. }
  761. /**
  762. * Tests the __() function on empty translations
  763. *
  764. * @return void
  765. */
  766. public function testEmptyTranslationString()
  767. {
  768. I18n::setDefaultFormatter('sprintf');
  769. $result = __('No translation needed');
  770. $this->assertEquals('No translation needed', $result);
  771. }
  772. /**
  773. * Tests that a plurals from a domain get translated correctly
  774. *
  775. * @return void
  776. */
  777. public function testPluralTranslationsFromDomain()
  778. {
  779. I18n::setLocale('de');
  780. $this->assertEquals('Standorte', __dn('wa', 'Location', 'Locations', 0));
  781. $this->assertEquals('Standort', __dn('wa', 'Location', 'Locations', 1));
  782. $this->assertEquals('Standorte', __dn('wa', 'Location', 'Locations', 2));
  783. }
  784. }