1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\i18n\Tests; |
4
|
|
|
|
5
|
|
|
use PHPUnit_Framework_Error_Notice; |
6
|
|
|
use SilverStripe\Assets\Filesystem; |
7
|
|
|
use SilverStripe\Core\Manifest\ModuleLoader; |
8
|
|
|
use SilverStripe\Dev\SapphireTest; |
9
|
|
|
use SilverStripe\i18n\i18n; |
10
|
|
|
use SilverStripe\i18n\TextCollection\i18nTextCollector; |
11
|
|
|
use SilverStripe\i18n\Messages\YamlWriter; |
12
|
|
|
use SilverStripe\i18n\Tests\i18nTextCollectorTest\Collector; |
13
|
|
|
|
14
|
|
|
class i18nTextCollectorTest extends SapphireTest |
15
|
|
|
{ |
16
|
|
|
use i18nTestManifest; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var string |
20
|
|
|
*/ |
21
|
|
|
protected $alternateBaseSavePath = null; |
22
|
|
|
|
23
|
|
|
protected function setUp() |
24
|
|
|
{ |
25
|
|
|
parent::setUp(); |
26
|
|
|
$this->setupManifest(); |
27
|
|
|
|
28
|
|
|
$this->alternateBaseSavePath = TEMP_FOLDER . DIRECTORY_SEPARATOR . 'i18nTextCollectorTest_webroot'; |
29
|
|
|
Filesystem::makeFolder($this->alternateBaseSavePath); |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
protected function tearDown() |
33
|
|
|
{ |
34
|
|
|
if (is_dir($this->alternateBaseSavePath)) { |
35
|
|
|
Filesystem::removeFolder($this->alternateBaseSavePath); |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
$this->tearDownManifest(); |
39
|
|
|
parent::tearDown(); |
40
|
|
|
} |
41
|
|
|
|
42
|
|
View Code Duplication |
public function testConcatenationInEntityValues() |
|
|
|
|
43
|
|
|
{ |
44
|
|
|
$c = i18nTextCollector::create(); |
45
|
|
|
$module = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
46
|
|
|
|
47
|
|
|
$php = <<<PHP |
48
|
|
|
_t( |
49
|
|
|
'Test.CONCATENATED', |
50
|
|
|
'Line 1 and ' . |
51
|
|
|
'Line \'2\' and ' . |
52
|
|
|
'Line "3"', |
53
|
|
|
|
54
|
|
|
'Comment' |
55
|
|
|
); |
56
|
|
|
|
57
|
|
|
_t( |
58
|
|
|
'Test.CONCATENATED2', |
59
|
|
|
"Line \"4\" and " . |
60
|
|
|
"Line 5"); |
61
|
|
|
PHP; |
62
|
|
|
$this->assertEquals( |
63
|
|
|
array( |
64
|
|
|
'Test.CONCATENATED' => [ |
65
|
|
|
'default' => "Line 1 and Line '2' and Line \"3\"", |
66
|
|
|
'comment' => 'Comment' |
67
|
|
|
], |
68
|
|
|
'Test.CONCATENATED2' => "Line \"4\" and Line 5" |
69
|
|
|
), |
70
|
|
|
$c->collectFromCode($php, null, $module) |
|
|
|
|
71
|
|
|
); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
public function testCollectFromNewTemplateSyntaxUsingParserSubclass() |
75
|
|
|
{ |
76
|
|
|
$c = i18nTextCollector::create(); |
77
|
|
|
$c->setWarnOnEmptyDefault(false); |
78
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
79
|
|
|
|
80
|
|
|
$html = <<<SS |
81
|
|
|
<% _t('Test.SINGLEQUOTE','Single Quote'); %> |
82
|
|
|
<%t i18nTestModule.NEWMETHODSIG "New _t method signature test" %> |
83
|
|
|
<%t i18nTestModule.INJECTIONS_0 "Hello {name} {greeting}, and {goodbye}" name="Mark" greeting="welcome" goodbye="bye" %> |
84
|
|
|
<%t i18nTestModule.INJECTIONS_1 "Hello {name} {greeting}, and {goodbye}" name="Paul" greeting="welcome" goodbye="cya" %> |
85
|
|
|
<%t i18nTestModule.INJECTIONS_2 "Hello {name} {greeting}" is "context (ignored)" name="Steffen" greeting="Wilkommen" %> |
86
|
|
|
<%t i18nTestModule.INJECTIONS_3 name="Cat" greeting='meow' goodbye="meow" %> |
87
|
|
|
<%t i18nTestModule.INJECTIONS_4 name=\$absoluteBaseURL greeting=\$get_locale goodbye="global calls" %> |
88
|
|
|
<%t i18nTestModule.INJECTIONS_9 "An item|{count} items" is "Test Pluralisation" count=4 %> |
89
|
|
|
<%t SilverStripe\\TestModule\\i18nTestModule.INJECTIONS_10 "This string is namespaced" %> |
90
|
|
|
<%t SilverStripe\\\\TestModule\\\\i18nTestModule.INJECTIONS_11 "Escaped namespaced string" %> |
91
|
|
|
SS; |
92
|
|
|
$c->collectFromTemplate($html, null, $mymodule); |
|
|
|
|
93
|
|
|
|
94
|
|
|
$this->assertEquals( |
95
|
|
|
[ |
96
|
|
|
'Test.SINGLEQUOTE' => 'Single Quote', |
97
|
|
|
'i18nTestModule.NEWMETHODSIG' => "New _t method signature test", |
98
|
|
|
'i18nTestModule.INJECTIONS_0' => "Hello {name} {greeting}, and {goodbye}", |
99
|
|
|
'i18nTestModule.INJECTIONS_1' => "Hello {name} {greeting}, and {goodbye}", |
100
|
|
|
'i18nTestModule.INJECTIONS_2' => [ |
101
|
|
|
'default' => "Hello {name} {greeting}", |
102
|
|
|
'comment' => 'context (ignored)', |
103
|
|
|
], |
104
|
|
|
'i18nTestModule.INJECTIONS_9' => [ |
105
|
|
|
'one' => 'An item', |
106
|
|
|
'other' => '{count} items', |
107
|
|
|
'comment' => 'Test Pluralisation' |
108
|
|
|
], |
109
|
|
|
'SilverStripe\\TestModule\\i18nTestModule.INJECTIONS_10' => 'This string is namespaced', |
110
|
|
|
'SilverStripe\\TestModule\\i18nTestModule.INJECTIONS_11' => 'Escaped namespaced string' |
111
|
|
|
], |
112
|
|
|
$c->collectFromTemplate($html, null, $mymodule) |
|
|
|
|
113
|
|
|
); |
114
|
|
|
|
115
|
|
|
// Test warning is raised on empty default |
116
|
|
|
$c->setWarnOnEmptyDefault(true); |
117
|
|
|
$this->setExpectedException( |
118
|
|
|
PHPUnit_Framework_Error_Notice::class, |
119
|
|
|
'Missing localisation default for key i18nTestModule.INJECTIONS_3' |
120
|
|
|
); |
121
|
|
|
$c->collectFromTemplate($html, null, $mymodule); |
|
|
|
|
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
public function testCollectFromTemplateSimple() |
125
|
|
|
{ |
126
|
|
|
$c = i18nTextCollector::create(); |
127
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
128
|
|
|
|
129
|
|
|
$html = <<<SS |
130
|
|
|
<% _t('Test.SINGLEQUOTE','Single Quote'); %> |
131
|
|
|
SS; |
132
|
|
|
$this->assertEquals( |
133
|
|
|
[ 'Test.SINGLEQUOTE' => 'Single Quote' ], |
134
|
|
|
$c->collectFromTemplate($html, null, $mymodule) |
|
|
|
|
135
|
|
|
); |
136
|
|
|
|
137
|
|
|
$html = <<<SS |
138
|
|
|
<% _t( "Test.DOUBLEQUOTE", "Double Quote and Spaces" ); %> |
139
|
|
|
SS; |
140
|
|
|
$this->assertEquals( |
141
|
|
|
[ 'Test.DOUBLEQUOTE' => "Double Quote and Spaces" ], |
142
|
|
|
$c->collectFromTemplate($html, null, $mymodule) |
|
|
|
|
143
|
|
|
); |
144
|
|
|
|
145
|
|
|
$html = <<<SS |
146
|
|
|
<% _t("Test.NOSEMICOLON","No Semicolon") %> |
147
|
|
|
SS; |
148
|
|
|
$this->assertEquals( |
149
|
|
|
[ 'Test.NOSEMICOLON' => "No Semicolon" ], |
150
|
|
|
$c->collectFromTemplate($html, null, $mymodule) |
|
|
|
|
151
|
|
|
); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
public function testCollectFromTemplateAdvanced() |
155
|
|
|
{ |
156
|
|
|
$c = i18nTextCollector::create(); |
157
|
|
|
$c->setWarnOnEmptyDefault(false); |
158
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
159
|
|
|
|
160
|
|
|
$html = <<<SS |
161
|
|
|
<% _t( |
162
|
|
|
'NEWLINES', |
163
|
|
|
'New Lines' |
164
|
|
|
) %> |
165
|
|
|
SS; |
166
|
|
|
$this->assertEquals( |
167
|
|
|
[ 'Test.NEWLINES' => "New Lines" ], |
168
|
|
|
$c->collectFromTemplate($html, 'Test', $mymodule) |
|
|
|
|
169
|
|
|
); |
170
|
|
|
|
171
|
|
|
$html = <<<SS |
172
|
|
|
<% _t( |
173
|
|
|
'Test.PRIOANDCOMMENT', |
174
|
|
|
' Prio and Value with "Double Quotes"', |
175
|
|
|
'Comment with "Double Quotes"' |
176
|
|
|
) %> |
177
|
|
|
SS; |
178
|
|
|
$this->assertEquals( |
179
|
|
|
[ 'Test.PRIOANDCOMMENT' => [ |
180
|
|
|
'default' => ' Prio and Value with "Double Quotes"', |
181
|
|
|
'comment' => 'Comment with "Double Quotes"', |
182
|
|
|
]], |
183
|
|
|
$c->collectFromTemplate($html, 'Test', $mymodule) |
|
|
|
|
184
|
|
|
); |
185
|
|
|
|
186
|
|
|
$html = <<<SS |
187
|
|
|
<% _t( |
188
|
|
|
'Test.PRIOANDCOMMENT', |
189
|
|
|
" Prio and Value with 'Single Quotes'", |
190
|
|
|
|
191
|
|
|
"Comment with 'Single Quotes'" |
192
|
|
|
) %> |
193
|
|
|
SS; |
194
|
|
|
$this->assertEquals( |
195
|
|
|
[ 'Test.PRIOANDCOMMENT' => [ |
196
|
|
|
'default' => " Prio and Value with 'Single Quotes'", |
197
|
|
|
'comment' => "Comment with 'Single Quotes'", |
198
|
|
|
]], |
199
|
|
|
$c->collectFromTemplate($html, 'Test', $mymodule) |
|
|
|
|
200
|
|
|
); |
201
|
|
|
|
202
|
|
|
// Test empty |
203
|
|
|
$html = <<<SS |
204
|
|
|
<% _t('Test.PRIOANDCOMMENT') %> |
205
|
|
|
SS; |
206
|
|
|
$this->assertEquals( |
207
|
|
|
[], |
208
|
|
|
$c->collectFromTemplate($html, null, $mymodule) |
|
|
|
|
209
|
|
|
); |
210
|
|
|
|
211
|
|
|
// Test warning is raised on empty default |
212
|
|
|
$c->setWarnOnEmptyDefault(true); |
213
|
|
|
$this->setExpectedException( |
214
|
|
|
PHPUnit_Framework_Error_Notice::class, |
215
|
|
|
'Missing localisation default for key Test.PRIOANDCOMMENT' |
216
|
|
|
); |
217
|
|
|
$c->collectFromTemplate($html, 'Test', $mymodule); |
|
|
|
|
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
|
221
|
|
|
public function testCollectFromCodeSimple() |
222
|
|
|
{ |
223
|
|
|
$c = i18nTextCollector::create(); |
224
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
225
|
|
|
|
226
|
|
|
$php = <<<PHP |
227
|
|
|
_t('Test.SINGLEQUOTE','Single Quote'); |
228
|
|
|
PHP; |
229
|
|
|
$this->assertEquals( |
230
|
|
|
[ 'Test.SINGLEQUOTE' => 'Single Quote' ], |
231
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
232
|
|
|
); |
233
|
|
|
|
234
|
|
|
$php = <<<PHP |
235
|
|
|
_t( "Test.DOUBLEQUOTE", "Double Quote and Spaces" ); |
236
|
|
|
PHP; |
237
|
|
|
$this->assertEquals( |
238
|
|
|
[ 'Test.DOUBLEQUOTE' => "Double Quote and Spaces" ], |
239
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
240
|
|
|
); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
public function testCollectFromCodeAdvanced() |
244
|
|
|
{ |
245
|
|
|
$c = i18nTextCollector::create(); |
246
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
247
|
|
|
|
248
|
|
|
$php = <<<PHP |
249
|
|
|
_t( |
250
|
|
|
'Test.NEWLINES', |
251
|
|
|
'New Lines' |
252
|
|
|
); |
253
|
|
|
PHP; |
254
|
|
|
$this->assertEquals( |
255
|
|
|
[ 'Test.NEWLINES' => "New Lines" ], |
256
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
257
|
|
|
); |
258
|
|
|
|
259
|
|
|
$php = <<<PHP |
260
|
|
|
_t( |
261
|
|
|
'Test.PRIOANDCOMMENT', |
262
|
|
|
' Value with "Double Quotes"', |
263
|
|
|
|
264
|
|
|
'Comment with "Double Quotes"' |
265
|
|
|
); |
266
|
|
|
PHP; |
267
|
|
|
$this->assertEquals( |
268
|
|
|
[ |
269
|
|
|
'Test.PRIOANDCOMMENT' => [ |
270
|
|
|
'default' => ' Value with "Double Quotes"', |
271
|
|
|
'comment' => 'Comment with "Double Quotes"', |
272
|
|
|
] |
273
|
|
|
], |
274
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
275
|
|
|
); |
276
|
|
|
|
277
|
|
|
$php = <<<PHP |
278
|
|
|
_t( |
279
|
|
|
'Test.PRIOANDCOMMENT', |
280
|
|
|
" Value with 'Single Quotes'", |
281
|
|
|
|
282
|
|
|
"Comment with 'Single Quotes'" |
283
|
|
|
); |
284
|
|
|
PHP; |
285
|
|
|
$this->assertEquals( |
286
|
|
|
[ 'Test.PRIOANDCOMMENT' => [ |
287
|
|
|
'default' => " Value with 'Single Quotes'", |
288
|
|
|
'comment' => "Comment with 'Single Quotes'" |
289
|
|
|
] ], |
290
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
291
|
|
|
); |
292
|
|
|
|
293
|
|
|
$php = <<<PHP |
294
|
|
|
_t( |
295
|
|
|
'Test.PRIOANDCOMMENT', |
296
|
|
|
'Value with \'Escaped Single Quotes\'' |
297
|
|
|
); |
298
|
|
|
PHP; |
299
|
|
|
$this->assertEquals( |
300
|
|
|
[ 'Test.PRIOANDCOMMENT' => "Value with 'Escaped Single Quotes'" ], |
301
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
302
|
|
|
); |
303
|
|
|
|
304
|
|
|
$php = <<<PHP |
305
|
|
|
_t( |
306
|
|
|
'Test.PRIOANDCOMMENT', |
307
|
|
|
"Doublequoted Value with 'Unescaped Single Quotes'" |
308
|
|
|
|
309
|
|
|
|
310
|
|
|
); |
311
|
|
|
PHP; |
312
|
|
|
$this->assertEquals( |
313
|
|
|
[ 'Test.PRIOANDCOMMENT' => "Doublequoted Value with 'Unescaped Single Quotes'"], |
314
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
315
|
|
|
); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
View Code Duplication |
public function testCollectFromCodeNamespace() |
|
|
|
|
319
|
|
|
{ |
320
|
|
|
$c = i18nTextCollector::create(); |
321
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
322
|
|
|
$php = <<<PHP |
323
|
|
|
<?php |
324
|
|
|
namespace SilverStripe\Framework\Core; |
325
|
|
|
|
326
|
|
|
class MyClass extends Base implements SomeService { |
327
|
|
|
public function getNewLines() { |
328
|
|
|
return _t( |
329
|
|
|
__CLASS__.'.NEWLINES', |
330
|
|
|
'New Lines' |
331
|
|
|
); |
332
|
|
|
} |
333
|
|
|
public function getAnotherString() { |
334
|
|
|
return _t( |
335
|
|
|
'SilverStripe\\\\Framework\\\\MyClass.ANOTHER_STRING', |
336
|
|
|
'Slash=\\\\, Quote=\\'' |
337
|
|
|
); |
338
|
|
|
} |
339
|
|
|
public function getDoubleQuotedString() { |
340
|
|
|
return _t( |
341
|
|
|
"SilverStripe\\\\Framework\\\\MyClass.DOUBLE_STRING", |
342
|
|
|
"Slash=\\\\, Quote=\\"" |
343
|
|
|
); |
344
|
|
|
} |
345
|
|
|
} |
346
|
|
|
PHP; |
347
|
|
|
$this->assertEquals( |
348
|
|
|
[ |
349
|
|
|
'SilverStripe\\Framework\\Core\\MyClass.NEWLINES' => "New Lines", |
350
|
|
|
'SilverStripe\\Framework\\MyClass.ANOTHER_STRING' => 'Slash=\\, Quote=\'', |
351
|
|
|
'SilverStripe\\Framework\\MyClass.DOUBLE_STRING' => 'Slash=\\, Quote="' |
352
|
|
|
], |
353
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
354
|
|
|
); |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
|
358
|
|
|
public function testNewlinesInEntityValues() |
359
|
|
|
{ |
360
|
|
|
$c = i18nTextCollector::create(); |
361
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
362
|
|
|
|
363
|
|
|
$php = <<<PHP |
364
|
|
|
_t( |
365
|
|
|
'Test.NEWLINESINGLEQUOTE', |
366
|
|
|
'Line 1 |
367
|
|
|
Line 2' |
368
|
|
|
); |
369
|
|
|
PHP; |
370
|
|
|
|
371
|
|
|
$eol = PHP_EOL; |
372
|
|
|
$this->assertEquals( |
373
|
|
|
[ 'Test.NEWLINESINGLEQUOTE' => "Line 1{$eol}Line 2" ], |
374
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
375
|
|
|
); |
376
|
|
|
|
377
|
|
|
$php = <<<PHP |
378
|
|
|
_t( |
379
|
|
|
'Test.NEWLINEDOUBLEQUOTE', |
380
|
|
|
"Line 1 |
381
|
|
|
Line 2" |
382
|
|
|
); |
383
|
|
|
PHP; |
384
|
|
|
$this->assertEquals( |
385
|
|
|
[ 'Test.NEWLINEDOUBLEQUOTE' => "Line 1{$eol}Line 2" ], |
386
|
|
|
$c->collectFromCode($php, null, $mymodule) |
|
|
|
|
387
|
|
|
); |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* Test extracting entities from the new _t method signature |
392
|
|
|
*/ |
393
|
|
|
public function testCollectFromCodeNewSignature() |
394
|
|
|
{ |
395
|
|
|
$c = i18nTextCollector::create(); |
396
|
|
|
$c->setWarnOnEmptyDefault(false); // Disable warnings for tests |
397
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
398
|
|
|
|
399
|
|
|
$php = <<<PHP |
400
|
|
|
_t('i18nTestModule.NEWMETHODSIG',"New _t method signature test"); |
401
|
|
|
_t('i18nTestModule.INJECTIONS2', "Hello {name} {greeting}. But it is late, {goodbye}", |
402
|
|
|
array("name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you")); |
403
|
|
|
_t("i18nTestModule.INJECTIONS3", "Hello {name} {greeting}. But it is late, {goodbye}", |
404
|
|
|
"New context (this should be ignored)", |
405
|
|
|
array("name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen")); |
406
|
|
|
_t('i18nTestModule.INJECTIONS4', array("name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow")); |
407
|
|
|
_t('i18nTestModule.INJECTIONS6', "Hello {name} {greeting}. But it is late, {goodbye}", |
408
|
|
|
["name"=>"Paul", "greeting"=>"good you are here", "goodbye"=>"see you"]); |
409
|
|
|
_t("i18nTestModule.INJECTIONS7", "Hello {name} {greeting}. But it is late, {goodbye}", |
410
|
|
|
"New context (this should be ignored)", |
411
|
|
|
["name"=>"Steffen", "greeting"=>"willkommen", "goodbye"=>"wiedersehen"]); |
412
|
|
|
_t('i18nTestModule.INJECTIONS8', ["name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow"]); |
413
|
|
|
_t('i18nTestModule.INJECTIONS9', "An item|{count} items", ['count' => 4], "Test Pluralisation"); |
414
|
|
|
PHP; |
415
|
|
|
|
416
|
|
|
$collectedTranslatables = $c->collectFromCode($php, null, $mymodule); |
|
|
|
|
417
|
|
|
|
418
|
|
|
$expectedArray = [ |
419
|
|
|
'i18nTestModule.INJECTIONS2' => "Hello {name} {greeting}. But it is late, {goodbye}", |
420
|
|
|
'i18nTestModule.INJECTIONS3' => [ |
421
|
|
|
'default' => "Hello {name} {greeting}. But it is late, {goodbye}", |
422
|
|
|
'comment' => 'New context (this should be ignored)' |
423
|
|
|
], |
424
|
|
|
'i18nTestModule.INJECTIONS6' => "Hello {name} {greeting}. But it is late, {goodbye}", |
425
|
|
|
'i18nTestModule.INJECTIONS7' => [ |
426
|
|
|
'default' => "Hello {name} {greeting}. But it is late, {goodbye}", |
427
|
|
|
'comment' => "New context (this should be ignored)", |
428
|
|
|
], |
429
|
|
|
'i18nTestModule.INJECTIONS9' => [ |
430
|
|
|
'one' => 'An item', |
431
|
|
|
'other' => '{count} items', |
432
|
|
|
'comment' => 'Test Pluralisation', |
433
|
|
|
], |
434
|
|
|
'i18nTestModule.NEWMETHODSIG' => "New _t method signature test", |
435
|
|
|
]; |
436
|
|
|
$this->assertEquals($expectedArray, $collectedTranslatables); |
437
|
|
|
|
438
|
|
|
// Test warning is raised on empty default |
439
|
|
|
$this->setExpectedException( |
440
|
|
|
PHPUnit_Framework_Error_Notice::class, |
441
|
|
|
'Missing localisation default for key i18nTestModule.INJECTIONS4' |
442
|
|
|
); |
443
|
|
|
$php = <<<PHP |
444
|
|
|
_t('i18nTestModule.INJECTIONS4', array("name"=>"Cat", "greeting"=>"meow", "goodbye"=>"meow")); |
445
|
|
|
PHP; |
446
|
|
|
$c->setWarnOnEmptyDefault(true); |
447
|
|
|
$c->collectFromCode($php, null, $mymodule); |
|
|
|
|
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
public function testUncollectableCode() |
451
|
|
|
{ |
452
|
|
|
$c = i18nTextCollector::create(); |
453
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
454
|
|
|
|
455
|
|
|
$php = <<<PHP |
456
|
|
|
_t(static::class.'.KEY1', 'Default'); |
457
|
|
|
_t(self::class.'.KEY2', 'Default'); |
458
|
|
|
_t(__CLASS__.'.KEY3', 'Default'); |
459
|
|
|
_t('Collectable.KEY4', 'Default'); |
460
|
|
|
PHP; |
461
|
|
|
|
462
|
|
|
$collectedTranslatables = $c->collectFromCode($php, null, $mymodule); |
|
|
|
|
463
|
|
|
|
464
|
|
|
// Only one item is collectable |
465
|
|
|
$expectedArray = [ 'Collectable.KEY4' => 'Default' ]; |
466
|
|
|
$this->assertEquals($expectedArray, $collectedTranslatables); |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
public function testCollectFromIncludedTemplates() |
470
|
|
|
{ |
471
|
|
|
$c = i18nTextCollector::create(); |
472
|
|
|
$c->setWarnOnEmptyDefault(false); // Disable warnings for tests |
473
|
|
|
$mymodule = ModuleLoader::instance()->getManifest()->getModule('i18ntestmodule'); |
474
|
|
|
|
475
|
|
|
$templateFilePath = $this->alternateBasePath . '/i18ntestmodule/templates/Layout/i18nTestModule.ss'; |
476
|
|
|
$html = file_get_contents($templateFilePath); |
477
|
|
|
$matches = $c->collectFromTemplate($html, $templateFilePath, $mymodule); |
|
|
|
|
478
|
|
|
|
479
|
|
|
$this->assertArrayHasKey('i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE', $matches); |
480
|
|
|
$this->assertEquals( |
481
|
|
|
'Layout Template no namespace', |
482
|
|
|
$matches['i18nTestModule.ss.LAYOUTTEMPLATENONAMESPACE'] |
483
|
|
|
); |
484
|
|
|
$this->assertArrayHasKey('i18nTestModule.ss.SPRINTFNONAMESPACE', $matches); |
485
|
|
|
$this->assertEquals( |
486
|
|
|
'My replacement no namespace: %s', |
487
|
|
|
$matches['i18nTestModule.ss.SPRINTFNONAMESPACE'] |
488
|
|
|
); |
489
|
|
|
$this->assertArrayHasKey('i18nTestModule.LAYOUTTEMPLATE', $matches); |
490
|
|
|
$this->assertEquals( |
491
|
|
|
'Layout Template', |
492
|
|
|
$matches['i18nTestModule.LAYOUTTEMPLATE'] |
493
|
|
|
); |
494
|
|
|
$this->assertArrayHasKey('i18nTestModule.SPRINTFNAMESPACE', $matches); |
495
|
|
|
$this->assertEquals( |
496
|
|
|
'My replacement: %s', |
497
|
|
|
$matches['i18nTestModule.SPRINTFNAMESPACE'] |
498
|
|
|
); |
499
|
|
|
|
500
|
|
|
// Includes should not automatically inject translations into parent templates |
501
|
|
|
$this->assertArrayNotHasKey('i18nTestModule.WITHNAMESPACE', $matches); |
502
|
|
|
$this->assertArrayNotHasKey('i18nTestModuleInclude.ss.NONAMESPACE', $matches); |
503
|
|
|
$this->assertArrayNotHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENAMESPACE', $matches); |
504
|
|
|
$this->assertArrayNotHasKey('i18nTestModuleInclude.ss.SPRINTFINCLUDENONAMESPACE', $matches); |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
public function testCollectMergesWithExisting() |
508
|
|
|
{ |
509
|
|
|
$c = i18nTextCollector::create(); |
510
|
|
|
$c->setWarnOnEmptyDefault(false); |
511
|
|
|
$c->setWriter(new YamlWriter()); |
512
|
|
|
$c->basePath = $this->alternateBasePath; |
513
|
|
|
$c->baseSavePath = $this->alternateBaseSavePath; |
514
|
|
|
|
515
|
|
|
$entitiesByModule = $c->collect(null, true /* merge */); |
|
|
|
|
516
|
|
|
$this->assertArrayHasKey( |
517
|
|
|
'i18nTestModule.ENTITY', |
518
|
|
|
$entitiesByModule['i18ntestmodule'], |
519
|
|
|
'Retains existing entities' |
520
|
|
|
); |
521
|
|
|
$this->assertArrayHasKey( |
522
|
|
|
'i18nTestModule.NEWENTITY', |
523
|
|
|
$entitiesByModule['i18ntestmodule'], |
524
|
|
|
'Adds new entities' |
525
|
|
|
); |
526
|
|
|
|
527
|
|
|
// Test cross-module strings are set correctly |
528
|
|
|
$this->assertArrayHasKey( |
529
|
|
|
'i18nProviderClass.OTHER_MODULE', |
530
|
|
|
$entitiesByModule['i18ntestmodule'] |
531
|
|
|
); |
532
|
|
|
$this->assertEquals( |
533
|
|
|
[ |
534
|
|
|
'comment' => 'Test string in another module', |
535
|
|
|
'default' => 'i18ntestmodule string defined in i18nothermodule', |
536
|
|
|
], |
537
|
|
|
$entitiesByModule['i18ntestmodule']['i18nProviderClass.OTHER_MODULE'] |
538
|
|
|
); |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
public function testCollectFromFilesystemAndWriteMasterTables() |
542
|
|
|
{ |
543
|
|
|
i18n::set_locale('en_US'); //set the locale to the US locale expected in the asserts |
544
|
|
|
i18n::config()->update('default_locale', 'en_US'); |
545
|
|
|
i18n::config()->update('missing_default_warning', false); |
546
|
|
|
|
547
|
|
|
$c = i18nTextCollector::create(); |
548
|
|
|
$c->setWarnOnEmptyDefault(false); |
549
|
|
|
$c->setWriter(new YamlWriter()); |
550
|
|
|
$c->basePath = $this->alternateBasePath; |
551
|
|
|
$c->baseSavePath = $this->alternateBaseSavePath; |
552
|
|
|
|
553
|
|
|
$c->run(); |
554
|
|
|
|
555
|
|
|
// i18ntestmodule |
556
|
|
|
$moduleLangFile = "{$this->alternateBaseSavePath}/i18ntestmodule/lang/" . $c->getDefaultLocale() . '.yml'; |
557
|
|
|
$this->assertTrue( |
558
|
|
|
file_exists($moduleLangFile), |
559
|
|
|
'Master language file can be written to modules /lang folder' |
560
|
|
|
); |
561
|
|
|
|
562
|
|
|
$moduleLangFileContent = file_get_contents($moduleLangFile); |
563
|
|
|
$this->assertContains( |
564
|
|
|
" ADDITION: Addition\n", |
565
|
|
|
$moduleLangFileContent |
566
|
|
|
); |
567
|
|
|
$this->assertContains( |
568
|
|
|
" ENTITY: 'Entity with \"Double Quotes\"'\n", |
569
|
|
|
$moduleLangFileContent |
570
|
|
|
); |
571
|
|
|
$this->assertContains( |
572
|
|
|
" MAINTEMPLATE: 'Main Template'\n", |
573
|
|
|
$moduleLangFileContent |
574
|
|
|
); |
575
|
|
|
$this->assertContains( |
576
|
|
|
" OTHERENTITY: 'Other Entity'\n", |
577
|
|
|
$moduleLangFileContent |
578
|
|
|
); |
579
|
|
|
$this->assertContains( |
580
|
|
|
" WITHNAMESPACE: 'Include Entity with Namespace'\n", |
581
|
|
|
$moduleLangFileContent |
582
|
|
|
); |
583
|
|
|
$this->assertContains( |
584
|
|
|
" NONAMESPACE: 'Include Entity without Namespace'\n", |
585
|
|
|
$moduleLangFileContent |
586
|
|
|
); |
587
|
|
|
|
588
|
|
|
// i18nothermodule |
589
|
|
|
$otherModuleLangFile = "{$this->alternateBaseSavePath}/i18nothermodule/lang/" . $c->getDefaultLocale() . '.yml'; |
590
|
|
|
$this->assertTrue( |
591
|
|
|
file_exists($otherModuleLangFile), |
592
|
|
|
'Master language file can be written to modules /lang folder' |
593
|
|
|
); |
594
|
|
|
$otherModuleLangFileContent = file_get_contents($otherModuleLangFile); |
595
|
|
|
$this->assertContains( |
596
|
|
|
" ENTITY: 'Other Module Entity'\n", |
597
|
|
|
$otherModuleLangFileContent |
598
|
|
|
); |
599
|
|
|
$this->assertContains( |
600
|
|
|
" MAINTEMPLATE: 'Main Template Other Module'\n", |
601
|
|
|
$otherModuleLangFileContent |
602
|
|
|
); |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
public function testCollectFromEntityProvidersInCustomObject() |
606
|
|
|
{ |
607
|
|
|
// note: Disable _fakewebroot manifest for this test |
608
|
|
|
$this->popManifests(); |
609
|
|
|
|
610
|
|
|
$c = i18nTextCollector::create(); |
611
|
|
|
|
612
|
|
|
// Collect from MyObject.php |
613
|
|
|
$filePath = __DIR__ . '/i18nTest/MyObject.php'; |
614
|
|
|
$matches = $c->collectFromEntityProviders($filePath); |
615
|
|
|
$this->assertEquals( |
616
|
|
|
[ |
617
|
|
|
'SilverStripe\Admin\LeftAndMain.OTHER_TITLE' => [ |
618
|
|
|
'default' => 'Other title', |
619
|
|
|
'module' => 'admin', |
620
|
|
|
], |
621
|
|
|
'SilverStripe\i18n\Tests\i18nTest\MyObject.PLURALNAME' => 'My Objects', |
622
|
|
|
'SilverStripe\i18n\Tests\i18nTest\MyObject.PLURALS' => [ |
623
|
|
|
'one' => 'A My Object', |
624
|
|
|
'other' => '{count} My Objects', |
625
|
|
|
], |
626
|
|
|
'SilverStripe\i18n\Tests\i18nTest\MyObject.SINGULARNAME' => 'My Object', |
627
|
|
|
], |
628
|
|
|
$matches |
629
|
|
|
); |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
public function testCollectFromEntityProvidersInWebRoot() |
633
|
|
|
{ |
634
|
|
|
// Collect from i18nProviderClass |
635
|
|
|
$c = i18nTextCollector::create(); |
636
|
|
|
$c->setWarnOnEmptyDefault(false); |
637
|
|
|
$c->setWriter(new YamlWriter()); |
638
|
|
|
$c->basePath = $this->alternateBasePath; |
639
|
|
|
$c->baseSavePath = $this->alternateBaseSavePath; |
640
|
|
|
$entitiesByModule = $c->collect(null, false); |
|
|
|
|
641
|
|
|
$this->assertEquals( |
642
|
|
|
[ |
643
|
|
|
'comment' => 'Plural forms for the test class', |
644
|
|
|
'one' => 'A class', |
645
|
|
|
'other' => '{count} classes', |
646
|
|
|
], |
647
|
|
|
$entitiesByModule['i18nothermodule']['i18nProviderClass.PLURALS'] |
648
|
|
|
); |
649
|
|
|
$this->assertEquals( |
650
|
|
|
'My Provider Class', |
651
|
|
|
$entitiesByModule['i18nothermodule']['i18nProviderClass.TITLE'] |
652
|
|
|
); |
653
|
|
|
$this->assertEquals( |
654
|
|
|
[ |
655
|
|
|
'comment' => 'Test string in another module', |
656
|
|
|
'default' => 'i18ntestmodule string defined in i18nothermodule', |
657
|
|
|
], |
658
|
|
|
$entitiesByModule['i18ntestmodule']['i18nProviderClass.OTHER_MODULE'] |
659
|
|
|
); |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
/** |
663
|
|
|
* Test that duplicate keys are resolved to the appropriate modules |
664
|
|
|
*/ |
665
|
|
|
public function testResolveDuplicates() |
666
|
|
|
{ |
667
|
|
|
$collector = new Collector(); |
668
|
|
|
|
669
|
|
|
// Dummy data as collected |
670
|
|
|
$data1 = [ |
671
|
|
|
'i18ntestmodule' => [ |
672
|
|
|
'i18nTestModule.PLURALNAME' => 'Data Objects', |
673
|
|
|
'i18nTestModule.SINGULARNAME' => 'Data Object', |
674
|
|
|
], |
675
|
|
|
'mymodule' => [ |
676
|
|
|
'i18nTestModule.PLURALNAME' => 'Ignored String', |
677
|
|
|
'i18nTestModule.STREETNAME' => 'Shortland Street', |
678
|
|
|
], |
679
|
|
|
]; |
680
|
|
|
$expected = [ |
681
|
|
|
'i18ntestmodule' => [ |
682
|
|
|
'i18nTestModule.PLURALNAME' => 'Data Objects', |
683
|
|
|
'i18nTestModule.SINGULARNAME' => 'Data Object', |
684
|
|
|
], |
685
|
|
|
'mymodule' => [ |
686
|
|
|
// Removed PLURALNAME because this key doesn't exist in i18ntestmodule strings |
687
|
|
|
'i18nTestModule.STREETNAME' => 'Shortland Street' |
688
|
|
|
] |
689
|
|
|
]; |
690
|
|
|
|
691
|
|
|
$resolved = $collector->resolveDuplicateConflicts_Test($data1); |
692
|
|
|
$this->assertEquals($expected, $resolved); |
693
|
|
|
|
694
|
|
|
// Test getConflicts |
695
|
|
|
$data2 = [ |
696
|
|
|
'module1' => [ |
697
|
|
|
'i18ntestmodule.ONE' => 'One', |
698
|
|
|
'i18ntestmodule.TWO' => 'Two', |
699
|
|
|
'i18ntestmodule.THREE' => 'Three', |
700
|
|
|
], |
701
|
|
|
'module2' => [ |
702
|
|
|
'i18ntestmodule.THREE' => 'Three', |
703
|
|
|
], |
704
|
|
|
'module3' => [ |
705
|
|
|
'i18ntestmodule.TWO' => 'Two', |
706
|
|
|
'i18ntestmodule.THREE' => 'Three', |
707
|
|
|
], |
708
|
|
|
]; |
709
|
|
|
$conflictsA = $collector->getConflicts_Test($data2); |
710
|
|
|
sort($conflictsA); |
711
|
|
|
$this->assertEquals( |
712
|
|
|
array('i18ntestmodule.THREE', 'i18ntestmodule.TWO'), |
713
|
|
|
$conflictsA |
714
|
|
|
); |
715
|
|
|
|
716
|
|
|
// Removing module3 should remove a conflict |
717
|
|
|
unset($data2['module3']); |
718
|
|
|
$conflictsB = $collector->getConflicts_Test($data2); |
719
|
|
|
$this->assertEquals( |
720
|
|
|
array('i18ntestmodule.THREE'), |
721
|
|
|
$conflictsB |
722
|
|
|
); |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
/** |
726
|
|
|
* Test ability for textcollector to detect modules |
727
|
|
|
*/ |
728
|
|
|
public function testModuleDetection() |
729
|
|
|
{ |
730
|
|
|
$collector = new Collector(); |
731
|
|
|
$modules = ModuleLoader::instance()->getManifest()->getModules(); |
732
|
|
|
$this->assertEquals( |
733
|
|
|
array( |
734
|
|
|
'i18nnonstandardmodule', |
735
|
|
|
'i18ntestmodule', |
736
|
|
|
'i18nothermodule' |
737
|
|
|
), |
738
|
|
|
array_keys($modules) |
739
|
|
|
); |
740
|
|
|
|
741
|
|
|
$this->assertEquals('i18ntestmodule', $collector->findModuleForClass_Test('i18nTestNamespacedClass')); |
742
|
|
|
$this->assertEquals( |
743
|
|
|
'i18ntestmodule', |
744
|
|
|
$collector->findModuleForClass_Test('i18nTest\\i18nTestNamespacedClass') |
745
|
|
|
); |
746
|
|
|
$this->assertEquals('i18ntestmodule', $collector->findModuleForClass_Test('i18nTestSubModule')); |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
/** |
750
|
|
|
* Test that text collector can detect module file lists properly |
751
|
|
|
*/ |
752
|
|
|
public function testModuleFileList() |
753
|
|
|
{ |
754
|
|
|
$collector = new Collector(); |
755
|
|
|
$collector->basePath = $this->alternateBasePath; |
756
|
|
|
$collector->baseSavePath = $this->alternateBaseSavePath; |
757
|
|
|
|
758
|
|
|
// Non-standard modules can't be safely filtered, so just index everything |
759
|
|
|
$nonStandardFiles = $collector->getFileListForModule_Test('i18nnonstandardmodule'); |
760
|
|
|
$nonStandardRoot = $this->alternateBasePath . '/i18nnonstandardmodule'; |
761
|
|
|
$this->assertEquals(3, count($nonStandardFiles)); |
762
|
|
|
$this->assertArrayHasKey("{$nonStandardRoot}/_config.php", $nonStandardFiles); |
763
|
|
|
$this->assertArrayHasKey("{$nonStandardRoot}/phpfile.php", $nonStandardFiles); |
764
|
|
|
$this->assertArrayHasKey("{$nonStandardRoot}/template.ss", $nonStandardFiles); |
765
|
|
|
|
766
|
|
|
// Normal module should have predictable dir structure |
767
|
|
|
$testFiles = $collector->getFileListForModule_Test('i18ntestmodule'); |
768
|
|
|
$testRoot = $this->alternateBasePath . '/i18ntestmodule'; |
769
|
|
|
$this->assertEquals(7, count($testFiles)); |
770
|
|
|
// Code in code folder is detected |
771
|
|
|
$this->assertArrayHasKey("{$testRoot}/code/i18nTestModule.php", $testFiles); |
772
|
|
|
$this->assertArrayHasKey("{$testRoot}/code/subfolder/_config.php", $testFiles); |
773
|
|
|
$this->assertArrayHasKey("{$testRoot}/code/subfolder/i18nTestSubModule.php", $testFiles); |
774
|
|
|
$this->assertArrayHasKey("{$testRoot}/code/subfolder/i18nTestNamespacedClass.php", $testFiles); |
775
|
|
|
// Templates in templates folder is detected |
776
|
|
|
$this->assertArrayHasKey("{$testRoot}/templates/Includes/i18nTestModuleInclude.ss", $testFiles); |
777
|
|
|
$this->assertArrayHasKey("{$testRoot}/templates/Layout/i18nTestModule.ss", $testFiles); |
778
|
|
|
$this->assertArrayHasKey("{$testRoot}/templates/i18nTestModule.ss", $testFiles); |
779
|
|
|
|
780
|
|
|
// Standard modules with code in odd places should only have code in those directories detected |
781
|
|
|
$otherFiles = $collector->getFileListForModule_Test('i18nothermodule'); |
782
|
|
|
$otherRoot = $this->alternateBasePath . '/i18nothermodule'; |
783
|
|
|
$this->assertEquals(4, count($otherFiles)); |
784
|
|
|
// Only detect well-behaved files |
785
|
|
|
$this->assertArrayHasKey("{$otherRoot}/code/i18nOtherModule.php", $otherFiles); |
786
|
|
|
$this->assertArrayHasKey("{$otherRoot}/code/i18nProviderClass.php", $otherFiles); |
787
|
|
|
$this->assertArrayHasKey("{$otherRoot}/code/i18nTestModuleDecorator.php", $otherFiles); |
788
|
|
|
$this->assertArrayHasKey("{$otherRoot}/templates/i18nOtherModule.ss", $otherFiles); |
789
|
|
|
} |
790
|
|
|
} |
791
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.