TranslateBehaviorTest.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Model\Behavior;
  16. use Cake\Collection\Collection;
  17. use Cake\Event\Event;
  18. use Cake\Model\Behavior\TranslateBehavior;
  19. use Cake\ORM\Entity;
  20. use Cake\ORM\TableRegistry;
  21. use Cake\TestSuite\TestCase;
  22. /**
  23. * Translate behavior test case
  24. */
  25. class TranslateBehaviorTest extends TestCase {
  26. /**
  27. * fixtures
  28. *
  29. * @var array
  30. */
  31. public $fixtures = [
  32. 'core.translate',
  33. 'core.article',
  34. 'core.comment',
  35. 'core.author'
  36. ];
  37. public function tearDown() {
  38. parent::tearDown();
  39. TableRegistry::clear();
  40. }
  41. /**
  42. * Returns an array with all the translations found for a set of records
  43. *
  44. * @param array|\Traversable $data
  45. * @return Collection
  46. */
  47. protected function _extractTranslations($data) {
  48. return (new Collection($data))->map(function($row) {
  49. $translations = $row->get('_translations');
  50. if (!$translations) {
  51. return [];
  52. }
  53. return array_map(function($t) {
  54. return $t->toArray();
  55. }, $translations);
  56. });
  57. }
  58. /**
  59. * Tests that fields from a translated model are overriden
  60. *
  61. * @return void
  62. */
  63. public function testFindSingleLocale() {
  64. $table = TableRegistry::get('Articles');
  65. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  66. $table->locale('eng');
  67. $results = $table->find()->combine('title', 'body', 'id')->toArray();
  68. $expected = [
  69. 1 => ['Title #1' => 'Content #1'],
  70. 2 => ['Title #2' => 'Content #2'],
  71. 3 => ['Title #3' => 'Content #3'],
  72. ];
  73. $this->assertSame($expected, $results);
  74. }
  75. /**
  76. * Tests that fields from a translated model are not overriden if translation
  77. * is null
  78. *
  79. * @return void
  80. */
  81. public function testFindSingleLocaleWithNullTranslation() {
  82. $table = TableRegistry::get('Comments');
  83. $table->addBehavior('Translate', ['fields' => ['comment']]);
  84. $table->locale('spa');
  85. $results = $table->find()
  86. ->where(['Comments.id' => 6])
  87. ->combine('id', 'comment')->toArray();
  88. $expected = [6 => 'Second Comment for Second Article'];
  89. $this->assertSame($expected, $results);
  90. }
  91. /**
  92. * Tests that overriding fields with the translate behavior works when
  93. * using conditions and that all other columns are preserved
  94. *
  95. * @return void
  96. */
  97. public function testFindSingleLocaleWithConditions() {
  98. $table = TableRegistry::get('Articles');
  99. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  100. $table->locale('eng');
  101. $results = $table->find()
  102. ->where(['Articles.id' => 2])
  103. ->all();
  104. $this->assertCount(1, $results);
  105. $row = $results->first();
  106. $expected = [
  107. 'id' => 2,
  108. 'title' => 'Title #2',
  109. 'body' => 'Content #2',
  110. 'author_id' => 3,
  111. 'published' => 'Y',
  112. '_locale' => 'eng'
  113. ];
  114. $this->assertEquals($expected, $row->toArray());
  115. }
  116. /**
  117. * Tests that translating fields work when other formatters are used
  118. *
  119. * @return void
  120. */
  121. public function testFindList() {
  122. $table = TableRegistry::get('Articles');
  123. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  124. $table->locale('eng');
  125. $results = $table->find('list')->toArray();
  126. $expected = [1 => 'Title #1', 2 => 'Title #2', 3 => 'Title #3'];
  127. $this->assertSame($expected, $results);
  128. }
  129. /**
  130. * Tests that the query count return the correct results
  131. *
  132. * @return void
  133. */
  134. public function testFindCount() {
  135. $table = TableRegistry::get('Articles');
  136. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  137. $table->locale('eng');
  138. $this->assertEquals(3, $table->find()->count());
  139. }
  140. /**
  141. * Tests that it is possible to get all translated fields at once
  142. *
  143. * @return void
  144. */
  145. public function testFindTranslations() {
  146. $table = TableRegistry::get('Articles');
  147. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  148. $results = $table->find('translations');
  149. $expected = [
  150. [
  151. 'eng' => ['title' => 'Title #1', 'body' => 'Content #1', 'locale' => 'eng'],
  152. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  153. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze']
  154. ],
  155. [
  156. 'eng' => ['title' => 'Title #2', 'body' => 'Content #2', 'locale' => 'eng'],
  157. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  158. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  159. ],
  160. [
  161. 'eng' => ['title' => 'Title #3', 'body' => 'Content #3', 'locale' => 'eng'],
  162. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  163. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  164. ]
  165. ];
  166. $translations = $this->_extractTranslations($results);
  167. $this->assertEquals($expected, $translations->toArray());
  168. $expected = [
  169. 1 => ['First Article' => 'First Article Body'],
  170. 2 => ['Second Article' => 'Second Article Body'],
  171. 3 => ['Third Article' => 'Third Article Body']
  172. ];
  173. $grouped = $results->combine('title', 'body', 'id');
  174. $this->assertEquals($expected, $grouped->toArray());
  175. }
  176. /**
  177. * Tests that it is possible to request just a few translations
  178. *
  179. * @return void
  180. */
  181. public function testFindFilteredTranslations() {
  182. $table = TableRegistry::get('Articles');
  183. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  184. $results = $table->find('translations', ['locales' => ['deu', 'cze']]);
  185. $expected = [
  186. [
  187. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  188. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze']
  189. ],
  190. [
  191. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  192. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  193. ],
  194. [
  195. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  196. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  197. ]
  198. ];
  199. $translations = $this->_extractTranslations($results);
  200. $this->assertEquals($expected, $translations->toArray());
  201. $expected = [
  202. 1 => ['First Article' => 'First Article Body'],
  203. 2 => ['Second Article' => 'Second Article Body'],
  204. 3 => ['Third Article' => 'Third Article Body']
  205. ];
  206. $grouped = $results->combine('title', 'body', 'id');
  207. $this->assertEquals($expected, $grouped->toArray());
  208. }
  209. /**
  210. * Tests that it is possible to combine find('list') and find('translations')
  211. *
  212. * @return void
  213. */
  214. public function testFindTranslationsList() {
  215. $table = TableRegistry::get('Articles');
  216. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  217. $results = $table
  218. ->find('list', [
  219. 'idField' => 'title',
  220. 'valueField' => '_translations.deu.title',
  221. 'groupField' => 'id'
  222. ])
  223. ->find('translations', ['locales' => ['deu']]);
  224. $expected = [
  225. 1 => ['First Article' => 'Titel #1'],
  226. 2 => ['Second Article' => 'Titel #2'],
  227. 3 => ['Third Article' => 'Titel #3']
  228. ];
  229. $this->assertEquals($expected, $results->toArray());
  230. }
  231. /**
  232. * Tests that you can both override fields and find all translations
  233. *
  234. * @return void
  235. */
  236. public function testFindTranslationsWithFieldOverriding() {
  237. $table = TableRegistry::get('Articles');
  238. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  239. $table->locale('cze');
  240. $results = $table->find('translations', ['locales' => ['deu', 'cze']]);
  241. $expected = [
  242. [
  243. 'deu' => ['title' => 'Titel #1', 'body' => 'Inhalt #1', 'locale' => 'deu'],
  244. 'cze' => ['title' => 'Titulek #1', 'body' => 'Obsah #1', 'locale' => 'cze']
  245. ],
  246. [
  247. 'deu' => ['title' => 'Titel #2', 'body' => 'Inhalt #2', 'locale' => 'deu'],
  248. 'cze' => ['title' => 'Titulek #2', 'body' => 'Obsah #2', 'locale' => 'cze']
  249. ],
  250. [
  251. 'deu' => ['title' => 'Titel #3', 'body' => 'Inhalt #3', 'locale' => 'deu'],
  252. 'cze' => ['title' => 'Titulek #3', 'body' => 'Obsah #3', 'locale' => 'cze']
  253. ]
  254. ];
  255. $translations = $this->_extractTranslations($results);
  256. $this->assertEquals($expected, $translations->toArray());
  257. $expected = [
  258. 1 => ['Titulek #1' => 'Obsah #1'],
  259. 2 => ['Titulek #2' => 'Obsah #2'],
  260. 3 => ['Titulek #3' => 'Obsah #3']
  261. ];
  262. $grouped = $results->combine('title', 'body', 'id');
  263. $this->assertEquals($expected, $grouped->toArray());
  264. }
  265. /**
  266. * Tests that fields can be overriden in a hasMany association
  267. *
  268. * @return void
  269. */
  270. public function testFindSingleLocaleHasMany() {
  271. $table = TableRegistry::get('Articles');
  272. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  273. $table->hasMany('Comments');
  274. $comments = $table->hasMany('Comments')->target();
  275. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  276. $table->locale('eng');
  277. $comments->locale('eng');
  278. $results = $table->find()->contain(['Comments' => function($q) {
  279. return $q->select(['id', 'comment', 'article_id']);
  280. }]);
  281. $list = new Collection($results->first()->comments);
  282. $expected = [
  283. 1 => 'Comment #1',
  284. 2 => 'Comment #2',
  285. 3 => 'Comment #3',
  286. 4 => 'Comment #4'
  287. ];
  288. $this->assertEquals($expected, $list->combine('id', 'comment')->toArray());
  289. }
  290. /**
  291. * Test that it is possible to bring translations from hasMany relations
  292. *
  293. * @return void
  294. */
  295. public function testTranslationsHasMany() {
  296. $table = TableRegistry::get('Articles');
  297. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  298. $table->hasMany('Comments');
  299. $comments = $table->hasMany('Comments')->target();
  300. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  301. $results = $table->find('translations')->contain([
  302. 'Comments' => function($q) {
  303. return $q->find('translations')->select(['id', 'comment', 'article_id']);
  304. }
  305. ]);
  306. $comments = $results->first()->comments;
  307. $expected = [
  308. [
  309. 'eng' => ['comment' => 'Comment #1', 'locale' => 'eng']
  310. ],
  311. [
  312. 'eng' => ['comment' => 'Comment #2', 'locale' => 'eng']
  313. ],
  314. [
  315. 'eng' => ['comment' => 'Comment #3', 'locale' => 'eng']
  316. ],
  317. [
  318. 'eng' => ['comment' => 'Comment #4', 'locale' => 'eng'],
  319. 'spa' => ['comment' => 'Comentario #4', 'locale' => 'spa']
  320. ]
  321. ];
  322. $translations = $this->_extractTranslations($comments);
  323. $this->assertEquals($expected, $translations->toArray());
  324. }
  325. /**
  326. * Tests that it is possible to both override fields with a translation and
  327. * also find separately other translations
  328. *
  329. * @return void
  330. */
  331. public function testTranslationsHasManyWithOverride() {
  332. $table = TableRegistry::get('Articles');
  333. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  334. $table->hasMany('Comments');
  335. $comments = $table->hasMany('Comments')->target();
  336. $comments->addBehavior('Translate', ['fields' => ['comment']]);
  337. $table->locale('cze');
  338. $comments->locale('eng');
  339. $results = $table->find('translations')->contain([
  340. 'Comments' => function($q) {
  341. return $q->find('translations')->select(['id', 'comment', 'article_id']);
  342. }
  343. ]);
  344. $comments = $results->first()->comments;
  345. $expected = [
  346. 1 => 'Comment #1',
  347. 2 => 'Comment #2',
  348. 3 => 'Comment #3',
  349. 4 => 'Comment #4'
  350. ];
  351. $list = new Collection($comments);
  352. $this->assertEquals($expected, $list->combine('id', 'comment')->toArray());
  353. $expected = [
  354. [
  355. 'eng' => ['comment' => 'Comment #1', 'locale' => 'eng']
  356. ],
  357. [
  358. 'eng' => ['comment' => 'Comment #2', 'locale' => 'eng']
  359. ],
  360. [
  361. 'eng' => ['comment' => 'Comment #3', 'locale' => 'eng']
  362. ],
  363. [
  364. 'eng' => ['comment' => 'Comment #4', 'locale' => 'eng'],
  365. 'spa' => ['comment' => 'Comentario #4', 'locale' => 'spa']
  366. ]
  367. ];
  368. $translations = $this->_extractTranslations($comments);
  369. $this->assertEquals($expected, $translations->toArray());
  370. $this->assertEquals('Titulek #1', $results->first()->title);
  371. $this->assertEquals('Obsah #1', $results->first()->body);
  372. }
  373. /**
  374. * Tests that it is possible to translate belongsTo associations
  375. *
  376. * @return void
  377. */
  378. public function testFindSingleLocaleBelongsto() {
  379. $table = TableRegistry::get('Articles');
  380. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  381. $authors = $table->belongsTo('Authors')->target();
  382. $authors->addBehavior('Translate', ['fields' => ['name']]);
  383. $table->locale('eng');
  384. $authors->locale('eng');
  385. $results = $table->find()
  386. ->select(['title', 'body'])
  387. ->order(['title' => 'asc'])
  388. ->contain(['Authors' => function($q) {
  389. return $q->select(['id', 'name']);
  390. }]);
  391. $expected = [
  392. [
  393. 'title' => 'Title #1',
  394. 'body' => 'Content #1',
  395. 'author' => ['id' => 1, 'name' => 'May-rianoh', '_locale' => 'eng'],
  396. '_locale' => 'eng'
  397. ],
  398. [
  399. 'title' => 'Title #2',
  400. 'body' => 'Content #2',
  401. 'author' => ['id' => 3, 'name' => 'larry', '_locale' => 'eng'],
  402. '_locale' => 'eng'
  403. ],
  404. [
  405. 'title' => 'Title #3',
  406. 'body' => 'Content #3',
  407. 'author' => ['id' => 1, 'name' => 'May-rianoh', '_locale' => 'eng'],
  408. '_locale' => 'eng'
  409. ]
  410. ];
  411. $results = array_map(function($r) {
  412. return $r->toArray();
  413. }, $results->toArray());
  414. $this->assertEquals($expected, $results);
  415. }
  416. /**
  417. * Tests that updating an existing record translations work
  418. *
  419. * @return void
  420. */
  421. public function testUpdateSingleLocale() {
  422. $table = TableRegistry::get('Articles');
  423. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  424. $table->locale('eng');
  425. $article = $table->find()->first();
  426. $this->assertEquals(1, $article->get('id'));
  427. $article->set('title', 'New translated article');
  428. $table->save($article);
  429. $this->assertNull($article->get('_i18n'));
  430. $article = $table->find()->first();
  431. $this->assertEquals(1, $article->get('id'));
  432. $this->assertEquals('New translated article', $article->get('title'));
  433. $this->assertEquals('Content #1', $article->get('body'));
  434. $table->locale(false);
  435. $article = $table->find()->first();
  436. $this->assertEquals(1, $article->get('id'));
  437. $this->assertEquals('First Article', $article->get('title'));
  438. $table->locale('eng');
  439. $article->set('title', 'Wow, such translated article');
  440. $article->set('body', 'A translated body');
  441. $table->save($article);
  442. $this->assertNull($article->get('_i18n'));
  443. $article = $table->find()->first();
  444. $this->assertEquals(1, $article->get('id'));
  445. $this->assertEquals('Wow, such translated article', $article->get('title'));
  446. $this->assertEquals('A translated body', $article->get('body'));
  447. }
  448. /**
  449. * Tests adding new translation to a record
  450. *
  451. * @return void
  452. */
  453. public function testInsertNewTranslations() {
  454. $table = TableRegistry::get('Articles');
  455. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  456. $table->locale('fra');
  457. $article = $table->find()->first();
  458. $this->assertEquals(1, $article->get('id'));
  459. $article->set('title', 'Le titre');
  460. $table->save($article);
  461. $this->assertEquals('fra', $article->get('_locale'));
  462. $article = $table->find()->first();
  463. $this->assertEquals(1, $article->get('id'));
  464. $this->assertEquals('Le titre', $article->get('title'));
  465. $this->assertEquals('First Article Body', $article->get('body'));
  466. $article->set('title', 'Un autre titre');
  467. $article->set('body', 'Le contenu');
  468. $table->save($article);
  469. $this->assertNull($article->get('_i18n'));
  470. $article = $table->find()->first();
  471. $this->assertEquals('Un autre titre', $article->get('title'));
  472. $this->assertEquals('Le contenu', $article->get('body'));
  473. }
  474. /**
  475. * Tests that it is possible to use the _locale property to specify the language
  476. * to use for saving an entity
  477. *
  478. * @return void
  479. */
  480. public function testUpdateTranslationWithLocaleInEntity() {
  481. $table = TableRegistry::get('Articles');
  482. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  483. $article = $table->find()->first();
  484. $this->assertEquals(1, $article->get('id'));
  485. $article->set('_locale', 'fra');
  486. $article->set('title', 'Le titre');
  487. $table->save($article);
  488. $this->assertNull($article->get('_i18n'));
  489. $article = $table->find()->first();
  490. $this->assertEquals(1, $article->get('id'));
  491. $this->assertEquals('First Article', $article->get('title'));
  492. $this->assertEquals('First Article Body', $article->get('body'));
  493. $table->locale('fra');
  494. $article = $table->find()->first();
  495. $this->assertEquals(1, $article->get('id'));
  496. $this->assertEquals('Le titre', $article->get('title'));
  497. $this->assertEquals('First Article Body', $article->get('body'));
  498. }
  499. /**
  500. * Tests that translations are added to the whitelist of associations to be
  501. * saved
  502. *
  503. * @return void
  504. */
  505. public function testSaveTranslationWithAssociationWhitelist() {
  506. $table = TableRegistry::get('Articles');
  507. $table->hasMany('Comments');
  508. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  509. $table->locale('fra');
  510. $article = $table->find()->first();
  511. $this->assertEquals(1, $article->get('id'));
  512. $article->set('title', 'Le titre');
  513. $table->save($article, ['associated' => ['Comments']]);
  514. $this->assertNull($article->get('_i18n'));
  515. $article = $table->find()->first();
  516. $this->assertEquals('Le titre', $article->get('title'));
  517. }
  518. /**
  519. * Tests that after deleting a translated entity, all translations are also removed
  520. *
  521. * @return void
  522. */
  523. public function testDelete() {
  524. $table = TableRegistry::get('Articles');
  525. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  526. $article = $table->find()->first();
  527. $this->assertTrue($table->delete($article));
  528. $translations = TableRegistry::get('I18n')->find()
  529. ->where(['model' => 'Articles', 'foreign_key' => $article->id])
  530. ->count();
  531. $this->assertEquals(0, $translations);
  532. }
  533. /**
  534. * Tests saving multiple translations at once when the translations already
  535. * exist in the database
  536. *
  537. * @return void
  538. */
  539. public function testSaveMultipleTranslations() {
  540. $table = TableRegistry::get('Articles');
  541. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  542. $article = $results = $table->find('translations')->first();
  543. $translations = $article->get('_translations');
  544. $translations['deu']->set('title', 'Another title');
  545. $translations['eng']->set('body', 'Another body');
  546. $article->set('_translations', $translations);
  547. $table->save($article);
  548. $this->assertNull($article->get('_i18n'));
  549. $article = $results = $table->find('translations')->first();
  550. $translations = $article->get('_translations');
  551. $this->assertEquals('Another title', $translations['deu']->get('title'));
  552. $this->assertEquals('Another body', $translations['eng']->get('body'));
  553. }
  554. /**
  555. * Tests saving multiple existing translations and adding new ones
  556. *
  557. * @return void
  558. */
  559. public function testSaveMultipleNewTranslations() {
  560. $table = TableRegistry::get('Articles');
  561. $table->addBehavior('Translate', ['fields' => ['title', 'body']]);
  562. $article = $results = $table->find('translations')->first();
  563. $translations = $article->get('_translations');
  564. $translations['deu']->set('title', 'Another title');
  565. $translations['eng']->set('body', 'Another body');
  566. $translations['spa'] = new Entity(['title' => 'Titulo']);
  567. $translations['fre'] = new Entity(['title' => 'Titre']);
  568. $article->set('_translations', $translations);
  569. $table->save($article);
  570. $this->assertNull($article->get('_i18n'));
  571. $article = $results = $table->find('translations')->first();
  572. $translations = $article->get('_translations');
  573. $this->assertEquals('Another title', $translations['deu']->get('title'));
  574. $this->assertEquals('Another body', $translations['eng']->get('body'));
  575. $this->assertEquals('Titulo', $translations['spa']->get('title'));
  576. $this->assertEquals('Titre', $translations['fre']->get('title'));
  577. }
  578. }