Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Contracts\Translation\Test;
13
 
14
use PHPUnit\Framework\TestCase;
15
use Symfony\Contracts\Translation\TranslatorInterface;
16
use Symfony\Contracts\Translation\TranslatorTrait;
17
 
18
/**
19
 * Test should cover all languages mentioned on http://translate.sourceforge.net/wiki/l10n/pluralforms
20
 * and Plural forms mentioned on http://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms.
21
 *
22
 * See also https://developer.mozilla.org/en/Localization_and_Plurals which mentions 15 rules having a maximum of 6 forms.
23
 * The mozilla code is also interesting to check for.
24
 *
25
 * As mentioned by chx http://drupal.org/node/1273968 we can cover all by testing number from 0 to 199
26
 *
27
 * The goal to cover all languages is to far fetched so this test case is smaller.
28
 *
29
 * @author Clemens Tolboom clemens@build2be.nl
30
 */
31
class TranslatorTest extends TestCase
32
{
33
    private $defaultLocale;
34
 
35
    protected function setUp(): void
36
    {
37
        $this->defaultLocale = \Locale::getDefault();
38
        \Locale::setDefault('en');
39
    }
40
 
41
    protected function tearDown(): void
42
    {
43
        \Locale::setDefault($this->defaultLocale);
44
    }
45
 
46
    public function getTranslator(): TranslatorInterface
47
    {
48
        return new class() implements TranslatorInterface {
49
            use TranslatorTrait;
50
        };
51
    }
52
 
53
    /**
54
     * @dataProvider getTransTests
55
     */
56
    public function testTrans($expected, $id, $parameters)
57
    {
58
        $translator = $this->getTranslator();
59
 
60
        $this->assertEquals($expected, $translator->trans($id, $parameters));
61
    }
62
 
63
    /**
64
     * @dataProvider getTransChoiceTests
65
     */
66
    public function testTransChoiceWithExplicitLocale($expected, $id, $number)
67
    {
68
        $translator = $this->getTranslator();
69
 
70
        $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
71
    }
72
 
73
    /**
74
     * @requires extension intl
75
     *
76
     * @dataProvider getTransChoiceTests
77
     */
78
    public function testTransChoiceWithDefaultLocale($expected, $id, $number)
79
    {
80
        $translator = $this->getTranslator();
81
 
82
        $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
83
    }
84
 
85
    /**
86
     * @dataProvider getTransChoiceTests
87
     */
88
    public function testTransChoiceWithEnUsPosix($expected, $id, $number)
89
    {
90
        $translator = $this->getTranslator();
91
        $translator->setLocale('en_US_POSIX');
92
 
93
        $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number]));
94
    }
95
 
96
    public function testGetSetLocale()
97
    {
98
        $translator = $this->getTranslator();
99
 
100
        $this->assertEquals('en', $translator->getLocale());
101
    }
102
 
103
    /**
104
     * @requires extension intl
105
     */
106
    public function testGetLocaleReturnsDefaultLocaleIfNotSet()
107
    {
108
        $translator = $this->getTranslator();
109
 
110
        \Locale::setDefault('pt_BR');
111
        $this->assertEquals('pt_BR', $translator->getLocale());
112
 
113
        \Locale::setDefault('en');
114
        $this->assertEquals('en', $translator->getLocale());
115
    }
116
 
117
    public function getTransTests()
118
    {
119
        return [
120
            ['Symfony is great!', 'Symfony is great!', []],
121
            ['Symfony is awesome!', 'Symfony is %what%!', ['%what%' => 'awesome']],
122
        ];
123
    }
124
 
125
    public function getTransChoiceTests()
126
    {
127
        return [
128
            ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
129
            ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
130
            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
131
            ['There are 0 apples', 'There is 1 apple|There are %count% apples', 0],
132
            ['There is 1 apple', 'There is 1 apple|There are %count% apples', 1],
133
            ['There are 10 apples', 'There is 1 apple|There are %count% apples', 10],
134
            // custom validation messages may be coded with a fixed value
135
            ['There are 2 apples', 'There are 2 apples', 2],
136
        ];
137
    }
138
 
139
    /**
140
     * @dataProvider getInternal
141
     */
142
    public function testInterval($expected, $number, $interval)
143
    {
144
        $translator = $this->getTranslator();
145
 
146
        $this->assertEquals($expected, $translator->trans($interval.' foo|[1,Inf[ bar', ['%count%' => $number]));
147
    }
148
 
149
    public function getInternal()
150
    {
151
        return [
152
            ['foo', 3, '{1,2, 3 ,4}'],
153
            ['bar', 10, '{1,2, 3 ,4}'],
154
            ['bar', 3, '[1,2]'],
155
            ['foo', 1, '[1,2]'],
156
            ['foo', 2, '[1,2]'],
157
            ['bar', 1, ']1,2['],
158
            ['bar', 2, ']1,2['],
159
            ['foo', log(0), '[-Inf,2['],
160
            ['foo', -log(0), '[-2,+Inf]'],
161
        ];
162
    }
163
 
164
    /**
165
     * @dataProvider getChooseTests
166
     */
167
    public function testChoose($expected, $id, $number, $locale = null)
168
    {
169
        $translator = $this->getTranslator();
170
 
171
        $this->assertEquals($expected, $translator->trans($id, ['%count%' => $number], null, $locale));
172
    }
173
 
174
    public function testReturnMessageIfExactlyOneStandardRuleIsGiven()
175
    {
176
        $translator = $this->getTranslator();
177
 
178
        $this->assertEquals('There are two apples', $translator->trans('There are two apples', ['%count%' => 2]));
179
    }
180
 
181
    /**
182
     * @dataProvider getNonMatchingMessages
183
     */
184
    public function testThrowExceptionIfMatchingMessageCannotBeFound($id, $number)
185
    {
186
        $this->expectException(\InvalidArgumentException::class);
187
        $translator = $this->getTranslator();
188
 
189
        $translator->trans($id, ['%count%' => $number]);
190
    }
191
 
192
    public function getNonMatchingMessages()
193
    {
194
        return [
195
            ['{0} There are no apples|{1} There is one apple', 2],
196
            ['{1} There is one apple|]1,Inf] There are %count% apples', 0],
197
            ['{1} There is one apple|]2,Inf] There are %count% apples', 2],
198
            ['{0} There are no apples|There is one apple', 2],
199
        ];
200
    }
201
 
202
    public function getChooseTests()
203
    {
204
        return [
205
            ['There are no apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
206
            ['There are no apples', '{0}     There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
207
            ['There are no apples', '{0}There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 0],
208
 
209
            ['There is one apple', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 1],
210
 
211
            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf] There are %count% apples', 10],
212
            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]There are %count% apples', 10],
213
            ['There are 10 apples', '{0} There are no apples|{1} There is one apple|]1,Inf]     There are %count% apples', 10],
214
 
215
            ['There are 0 apples', 'There is one apple|There are %count% apples', 0],
216
            ['There is one apple', 'There is one apple|There are %count% apples', 1],
217
            ['There are 10 apples', 'There is one apple|There are %count% apples', 10],
218
 
219
            ['There are 0 apples', 'one: There is one apple|more: There are %count% apples', 0],
220
            ['There is one apple', 'one: There is one apple|more: There are %count% apples', 1],
221
            ['There are 10 apples', 'one: There is one apple|more: There are %count% apples', 10],
222
 
223
            ['There are no apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 0],
224
            ['There is one apple', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 1],
225
            ['There are 10 apples', '{0} There are no apples|one: There is one apple|more: There are %count% apples', 10],
226
 
227
            ['', '{0}|{1} There is one apple|]1,Inf] There are %count% apples', 0],
228
            ['', '{0} There are no apples|{1}|]1,Inf] There are %count% apples', 1],
229
 
230
            // Indexed only tests which are Gettext PoFile* compatible strings.
231
            ['There are 0 apples', 'There is one apple|There are %count% apples', 0],
232
            ['There is one apple', 'There is one apple|There are %count% apples', 1],
233
            ['There are 2 apples', 'There is one apple|There are %count% apples', 2],
234
 
235
            // Tests for float numbers
236
            ['There is almost one apple', '{0} There are no apples|]0,1[ There is almost one apple|{1} There is one apple|[1,Inf] There is more than one apple', 0.7],
237
            ['There is one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1],
238
            ['There is more than one apple', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 1.7],
239
            ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
240
            ['There are no apples', '{0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0.0],
241
            ['There are no apples', '{0.0} There are no apples|]0,1[There are %count% apples|{1} There is one apple|[1,Inf] There is more than one apple', 0],
242
 
243
            // Test texts with new-lines
244
            // with double-quotes and \n in id & double-quotes and actual newlines in text
245
            ["This is a text with a\n            new-line in it. Selector = 0.", '{0}This is a text with a
246
            new-line in it. Selector = 0.|{1}This is a text with a
247
            new-line in it. Selector = 1.|[1,Inf]This is a text with a
248
            new-line in it. Selector > 1.', 0],
249
            // with double-quotes and \n in id and single-quotes and actual newlines in text
250
            ["This is a text with a\n            new-line in it. Selector = 1.", '{0}This is a text with a
251
            new-line in it. Selector = 0.|{1}This is a text with a
252
            new-line in it. Selector = 1.|[1,Inf]This is a text with a
253
            new-line in it. Selector > 1.', 1],
254
            ["This is a text with a\n            new-line in it. Selector > 1.", '{0}This is a text with a
255
            new-line in it. Selector = 0.|{1}This is a text with a
256
            new-line in it. Selector = 1.|[1,Inf]This is a text with a
257
            new-line in it. Selector > 1.', 5],
258
            // with double-quotes and id split accros lines
259
            ['This is a text with a
260
            new-line in it. Selector = 1.', '{0}This is a text with a
261
            new-line in it. Selector = 0.|{1}This is a text with a
262
            new-line in it. Selector = 1.|[1,Inf]This is a text with a
263
            new-line in it. Selector > 1.', 1],
264
            // with single-quotes and id split accros lines
265
            ['This is a text with a
266
            new-line in it. Selector > 1.', '{0}This is a text with a
267
            new-line in it. Selector = 0.|{1}This is a text with a
268
            new-line in it. Selector = 1.|[1,Inf]This is a text with a
269
            new-line in it. Selector > 1.', 5],
270
            // with single-quotes and \n in text
271
            ['This is a text with a\nnew-line in it. Selector = 0.', '{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.', 0],
272
            // with double-quotes and id split accros lines
273
            ["This is a text with a\nnew-line in it. Selector = 1.", "{0}This is a text with a\nnew-line in it. Selector = 0.|{1}This is a text with a\nnew-line in it. Selector = 1.|[1,Inf]This is a text with a\nnew-line in it. Selector > 1.", 1],
274
            // escape pipe
275
            ['This is a text with | in it. Selector = 0.', '{0}This is a text with || in it. Selector = 0.|{1}This is a text with || in it. Selector = 1.', 0],
276
            // Empty plural set (2 plural forms) from a .PO file
277
            ['', '|', 1],
278
            // Empty plural set (3 plural forms) from a .PO file
279
            ['', '||', 1],
280
 
281
            // Floating values
282
            ['1.5 liters', '%count% liter|%count% liters', 1.5],
283
            ['1.5 litre', '%count% litre|%count% litres', 1.5, 'fr'],
284
 
285
            // Negative values
286
            ['-1 degree', '%count% degree|%count% degrees', -1],
287
            ['-1 degré', '%count% degré|%count% degrés', -1],
288
            ['-1.5 degrees', '%count% degree|%count% degrees', -1.5],
289
            ['-1.5 degré', '%count% degré|%count% degrés', -1.5, 'fr'],
290
            ['-2 degrees', '%count% degree|%count% degrees', -2],
291
            ['-2 degrés', '%count% degré|%count% degrés', -2],
292
        ];
293
    }
294
 
295
    /**
296
     * @dataProvider failingLangcodes
297
     */
298
    public function testFailedLangcodes($nplural, $langCodes)
299
    {
300
        $matrix = $this->generateTestData($langCodes);
301
        $this->validateMatrix($nplural, $matrix, false);
302
    }
303
 
304
    /**
305
     * @dataProvider successLangcodes
306
     */
307
    public function testLangcodes($nplural, $langCodes)
308
    {
309
        $matrix = $this->generateTestData($langCodes);
310
        $this->validateMatrix($nplural, $matrix);
311
    }
312
 
313
    /**
314
     * This array should contain all currently known langcodes.
315
     *
316
     * As it is impossible to have this ever complete we should try as hard as possible to have it almost complete.
317
     */
318
    public function successLangcodes(): array
319
    {
320
        return [
321
            ['1', ['ay', 'bo', 'cgg', 'dz', 'id', 'ja', 'jbo', 'ka', 'kk', 'km', 'ko', 'ky']],
322
            ['2', ['nl', 'fr', 'en', 'de', 'de_GE', 'hy', 'hy_AM', 'en_US_POSIX']],
323
            ['3', ['be', 'bs', 'cs', 'hr']],
324
            ['4', ['cy', 'mt', 'sl']],
325
            ['6', ['ar']],
326
        ];
327
    }
328
 
329
    /**
330
     * This array should be at least empty within the near future.
331
     *
332
     * This both depends on a complete list trying to add above as understanding
333
     * the plural rules of the current failing languages.
334
     *
335
     * @return array with nplural together with langcodes
336
     */
337
    public function failingLangcodes(): array
338
    {
339
        return [
340
            ['1', ['fa']],
341
            ['2', ['jbo']],
342
            ['3', ['cbs']],
343
            ['4', ['gd', 'kw']],
344
            ['5', ['ga']],
345
        ];
346
    }
347
 
348
    /**
349
     * We validate only on the plural coverage. Thus the real rules is not tested.
350
     *
351
     * @param string $nplural Plural expected
352
     * @param array  $matrix  Containing langcodes and their plural index values
353
     */
354
    protected function validateMatrix(string $nplural, array $matrix, bool $expectSuccess = true)
355
    {
356
        foreach ($matrix as $langCode => $data) {
357
            $indexes = array_flip($data);
358
            if ($expectSuccess) {
359
                $this->assertCount($nplural, $indexes, "Langcode '$langCode' has '$nplural' plural forms.");
360
            } else {
361
                $this->assertNotEquals((int) $nplural, \count($indexes), "Langcode '$langCode' has '$nplural' plural forms.");
362
            }
363
        }
364
    }
365
 
366
    protected function generateTestData($langCodes)
367
    {
368
        $translator = new class() {
369
            use TranslatorTrait {
370
                getPluralizationRule as public;
371
            }
372
        };
373
 
374
        $matrix = [];
375
        foreach ($langCodes as $langCode) {
376
            for ($count = 0; $count < 200; ++$count) {
377
                $plural = $translator->getPluralizationRule($count, $langCode);
378
                $matrix[$langCode][$count] = $plural;
379
            }
380
        }
381
 
382
        return $matrix;
383
    }
384
}