|
1 <?php |
|
2 |
|
3 /** |
|
4 * @file |
|
5 * Tests for locale.module. |
|
6 * |
|
7 * The test file includes: |
|
8 * - a functional test for the language configuration forms; |
|
9 * - functional tests for the translation functionalities, including searching; |
|
10 * - a functional test for the PO files import feature, including validation; |
|
11 * - functional tests for translations and templates export feature; |
|
12 * - functional tests for the uninstall process; |
|
13 * - a functional test for the language switching feature; |
|
14 * - a functional test for a user's ability to change their default language; |
|
15 * - a functional test for configuring a different path alias per language; |
|
16 * - a functional test for configuring a different path alias per language; |
|
17 * - a functional test for multilingual support by content type and on nodes. |
|
18 * - a functional test for multilingual fields. |
|
19 * - a functional test for comment language. |
|
20 * - a functional test fot language types/negotiation info. |
|
21 */ |
|
22 |
|
23 |
|
24 /** |
|
25 * Functional tests for the language configuration forms. |
|
26 */ |
|
27 class LocaleConfigurationTest extends DrupalWebTestCase { |
|
28 public static function getInfo() { |
|
29 return array( |
|
30 'name' => 'Language configuration', |
|
31 'description' => 'Adds a new locale and tests changing its status and the default language.', |
|
32 'group' => 'Locale', |
|
33 ); |
|
34 } |
|
35 |
|
36 function setUp() { |
|
37 parent::setUp('locale'); |
|
38 } |
|
39 |
|
40 /** |
|
41 * Functional tests for adding, editing and deleting languages. |
|
42 */ |
|
43 function testLanguageConfiguration() { |
|
44 global $base_url; |
|
45 |
|
46 // User to add and remove language. |
|
47 $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); |
|
48 $this->drupalLogin($admin_user); |
|
49 |
|
50 // Add predefined language. |
|
51 $edit = array( |
|
52 'langcode' => 'fr', |
|
53 ); |
|
54 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
55 $this->assertText('fr', 'Language added successfully.'); |
|
56 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
57 |
|
58 // Add custom language. |
|
59 // Code for the language. |
|
60 $langcode = 'xx'; |
|
61 // The English name for the language. |
|
62 $name = $this->randomName(16); |
|
63 // The native name for the language. |
|
64 $native = $this->randomName(16); |
|
65 // The domain prefix. |
|
66 $prefix = $langcode; |
|
67 $edit = array( |
|
68 'langcode' => $langcode, |
|
69 'name' => $name, |
|
70 'native' => $native, |
|
71 'prefix' => $prefix, |
|
72 'direction' => '0', |
|
73 ); |
|
74 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
75 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
76 $this->assertText($langcode, 'Language code found.'); |
|
77 $this->assertText($name, 'Name found.'); |
|
78 $this->assertText($native, 'Native found.'); |
|
79 $this->assertText($native, 'Test language added.'); |
|
80 |
|
81 // Check if we can change the default language. |
|
82 $path = 'admin/config/regional/language'; |
|
83 $this->drupalGet($path); |
|
84 $this->assertFieldChecked('edit-site-default-en', 'English is the default language.'); |
|
85 // Change the default language. |
|
86 $edit = array( |
|
87 'site_default' => $langcode, |
|
88 ); |
|
89 $this->drupalPost(NULL, $edit, t('Save configuration')); |
|
90 $this->assertNoFieldChecked('edit-site-default-en', 'Default language updated.'); |
|
91 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
92 |
|
93 // Check if a valid language prefix is added after changing the default |
|
94 // language. |
|
95 $this->drupalGet('admin/config/regional/language/edit/en'); |
|
96 $this->assertFieldByXPath('//input[@name="prefix"]', 'en', 'A valid path prefix has been added to the previous default language.'); |
|
97 |
|
98 // Ensure we can't delete the default language. |
|
99 $this->drupalGet('admin/config/regional/language/delete/' . $langcode); |
|
100 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
101 $this->assertText(t('The default language cannot be deleted.'), 'Failed to delete the default language.'); |
|
102 |
|
103 // Check if we can disable a language. |
|
104 $edit = array( |
|
105 'enabled[en]' => FALSE, |
|
106 ); |
|
107 $this->drupalPost($path, $edit, t('Save configuration')); |
|
108 $this->assertNoFieldChecked('edit-enabled-en', 'Language disabled.'); |
|
109 |
|
110 // Set disabled language to be the default and ensure it is re-enabled. |
|
111 $edit = array( |
|
112 'site_default' => 'en', |
|
113 ); |
|
114 $this->drupalPost(NULL, $edit, t('Save configuration')); |
|
115 $this->assertFieldChecked('edit-enabled-en', 'Default language re-enabled.'); |
|
116 |
|
117 // Ensure 'edit' link works. |
|
118 $this->clickLink(t('edit')); |
|
119 $this->assertTitle(t('Edit language | Drupal'), 'Page title is "Edit language".'); |
|
120 // Edit a language. |
|
121 $name = $this->randomName(16); |
|
122 $edit = array( |
|
123 'name' => $name, |
|
124 ); |
|
125 $this->drupalPost('admin/config/regional/language/edit/' . $langcode, $edit, t('Save language')); |
|
126 $this->assertRaw($name, 'The language has been updated.'); |
|
127 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
128 |
|
129 // Ensure 'delete' link works. |
|
130 $this->drupalGet('admin/config/regional/language'); |
|
131 $this->clickLink(t('delete')); |
|
132 $this->assertText(t('Are you sure you want to delete the language'), '"delete" link is correct.'); |
|
133 // Delete an enabled language. |
|
134 $this->drupalGet('admin/config/regional/language/delete/' . $langcode); |
|
135 // First test the 'cancel' link. |
|
136 $this->clickLink(t('Cancel')); |
|
137 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
138 $this->assertRaw($name, 'The language was not deleted.'); |
|
139 // Delete the language for real. This a confirm form, we do not need any |
|
140 // fields changed. |
|
141 $this->drupalPost('admin/config/regional/language/delete/' . $langcode, array(), t('Delete')); |
|
142 // We need raw here because %locale will add HTML. |
|
143 $this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), 'The test language has been removed.'); |
|
144 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
145 // Verify that language is no longer found. |
|
146 $this->drupalGet('admin/config/regional/language/delete/' . $langcode); |
|
147 $this->assertResponse(404, 'Language no longer found.'); |
|
148 // Make sure the "language_count" variable has been updated correctly. |
|
149 drupal_static_reset('language_list'); |
|
150 $enabled = language_list('enabled'); |
|
151 $this->assertEqual(variable_get('language_count', 1), count($enabled[1]), 'Language count is correct.'); |
|
152 // Delete a disabled language. |
|
153 // Disable an enabled language. |
|
154 $edit = array( |
|
155 'enabled[fr]' => FALSE, |
|
156 ); |
|
157 $this->drupalPost($path, $edit, t('Save configuration')); |
|
158 $this->assertNoFieldChecked('edit-enabled-fr', 'French language disabled.'); |
|
159 // Get the count of enabled languages. |
|
160 drupal_static_reset('language_list'); |
|
161 $enabled = language_list('enabled'); |
|
162 // Delete the disabled language. |
|
163 $this->drupalPost('admin/config/regional/language/delete/fr', array(), t('Delete')); |
|
164 // We need raw here because %locale will add HTML. |
|
165 $this->assertRaw(t('The language %locale has been removed.', array('%locale' => 'French')), 'Disabled language has been removed.'); |
|
166 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
167 // Verify that language is no longer found. |
|
168 $this->drupalGet('admin/config/regional/language/delete/fr'); |
|
169 $this->assertResponse(404, 'Language no longer found.'); |
|
170 // Make sure the "language_count" variable has not changed. |
|
171 $this->assertEqual(variable_get('language_count', 1), count($enabled[1]), 'Language count is correct.'); |
|
172 |
|
173 |
|
174 // Ensure we can't delete the English language. |
|
175 $this->drupalGet('admin/config/regional/language/delete/en'); |
|
176 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
177 $this->assertText(t('The English language cannot be deleted.'), 'Failed to delete English language.'); |
|
178 } |
|
179 |
|
180 } |
|
181 |
|
182 /** |
|
183 * Tests localization of the JavaScript libraries. |
|
184 * |
|
185 * Currently, only the jQuery datepicker is localized using Drupal translations. |
|
186 */ |
|
187 class LocaleLibraryInfoAlterTest extends DrupalWebTestCase { |
|
188 public static function getInfo() { |
|
189 return array( |
|
190 'name' => 'Javascript library localisation', |
|
191 'description' => 'Tests the localisation of JavaScript libraries.', |
|
192 'group' => 'Locale', |
|
193 ); |
|
194 } |
|
195 |
|
196 function setUp() { |
|
197 parent::setUp('locale', 'locale_test'); |
|
198 } |
|
199 |
|
200 /** |
|
201 * Verifies that the datepicker can be localized. |
|
202 * |
|
203 * @see locale_library_info_alter() |
|
204 */ |
|
205 public function testLibraryInfoAlter() { |
|
206 drupal_add_library('system', 'ui.datepicker'); |
|
207 $scripts = drupal_get_js(); |
|
208 $this->assertTrue(strpos($scripts, 'locale.datepicker.js'), 'locale.datepicker.js added to scripts.'); |
|
209 } |
|
210 } |
|
211 |
|
212 /** |
|
213 * Functional tests for JavaScript parsing for translatable strings. |
|
214 */ |
|
215 class LocaleJavascriptTranslationTest extends DrupalWebTestCase { |
|
216 public static function getInfo() { |
|
217 return array( |
|
218 'name' => 'Javascript translation', |
|
219 'description' => 'Tests parsing js files for translatable strings', |
|
220 'group' => 'Locale', |
|
221 ); |
|
222 } |
|
223 |
|
224 function setUp() { |
|
225 parent::setUp('locale', 'locale_test'); |
|
226 } |
|
227 |
|
228 function testFileParsing() { |
|
229 |
|
230 $filename = drupal_get_path('module', 'locale_test') . '/locale_test.js'; |
|
231 |
|
232 // Parse the file to look for source strings. |
|
233 _locale_parse_js_file($filename); |
|
234 |
|
235 // Get all of the source strings that were found. |
|
236 $source_strings = db_select('locales_source', 's') |
|
237 ->fields('s', array('source', 'context')) |
|
238 ->condition('s.location', $filename) |
|
239 ->execute() |
|
240 ->fetchAllKeyed(); |
|
241 |
|
242 // List of all strings that should be in the file. |
|
243 $test_strings = array( |
|
244 "Standard Call t" => '', |
|
245 "Whitespace Call t" => '', |
|
246 |
|
247 "Single Quote t" => '', |
|
248 "Single Quote \\'Escaped\\' t" => '', |
|
249 "Single Quote Concat strings t" => '', |
|
250 |
|
251 "Double Quote t" => '', |
|
252 "Double Quote \\\"Escaped\\\" t" => '', |
|
253 "Double Quote Concat strings t" => '', |
|
254 |
|
255 "Context !key Args t" => "Context string", |
|
256 |
|
257 "Context Unquoted t" => "Context string unquoted", |
|
258 "Context Single Quoted t" => "Context string single quoted", |
|
259 "Context Double Quoted t" => "Context string double quoted", |
|
260 |
|
261 "Standard Call plural" => '', |
|
262 "Standard Call @count plural" => '', |
|
263 "Whitespace Call plural" => '', |
|
264 "Whitespace Call @count plural" => '', |
|
265 |
|
266 "Single Quote plural" => '', |
|
267 "Single Quote @count plural" => '', |
|
268 "Single Quote \\'Escaped\\' plural" => '', |
|
269 "Single Quote \\'Escaped\\' @count plural" => '', |
|
270 |
|
271 "Double Quote plural" => '', |
|
272 "Double Quote @count plural" => '', |
|
273 "Double Quote \\\"Escaped\\\" plural" => '', |
|
274 "Double Quote \\\"Escaped\\\" @count plural" => '', |
|
275 |
|
276 "Context !key Args plural" => "Context string", |
|
277 "Context !key Args @count plural" => "Context string", |
|
278 |
|
279 "Context Unquoted plural" => "Context string unquoted", |
|
280 "Context Unquoted @count plural" => "Context string unquoted", |
|
281 "Context Single Quoted plural" => "Context string single quoted", |
|
282 "Context Single Quoted @count plural" => "Context string single quoted", |
|
283 "Context Double Quoted plural" => "Context string double quoted", |
|
284 "Context Double Quoted @count plural" => "Context string double quoted", |
|
285 ); |
|
286 |
|
287 // Assert that all strings were found properly. |
|
288 foreach ($test_strings as $str => $context) { |
|
289 $args = array('%source' => $str, '%context' => $context); |
|
290 |
|
291 // Make sure that the string was found in the file. |
|
292 $this->assertTrue(isset($source_strings[$str]), format_string('Found source string: %source', $args)); |
|
293 |
|
294 // Make sure that the proper context was matched. |
|
295 $this->assertTrue(isset($source_strings[$str]) && $source_strings[$str] === $context, strlen($context) > 0 ? format_string('Context for %source is %context', $args) : format_string('Context for %source is blank', $args)); |
|
296 } |
|
297 |
|
298 $this->assertEqual(count($source_strings), count($test_strings), 'Found correct number of source strings.'); |
|
299 } |
|
300 } |
|
301 /** |
|
302 * Functional test for string translation and validation. |
|
303 */ |
|
304 class LocaleTranslationFunctionalTest extends DrupalWebTestCase { |
|
305 public static function getInfo() { |
|
306 return array( |
|
307 'name' => 'String translate, search and validate', |
|
308 'description' => 'Adds a new locale and translates its name. Checks the validation of translation strings and search results.', |
|
309 'group' => 'Locale', |
|
310 ); |
|
311 } |
|
312 |
|
313 function setUp() { |
|
314 parent::setUp('locale'); |
|
315 } |
|
316 |
|
317 /** |
|
318 * Adds a language and tests string translation by users with the appropriate permissions. |
|
319 */ |
|
320 function testStringTranslation() { |
|
321 global $base_url; |
|
322 |
|
323 // User to add and remove language. |
|
324 $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); |
|
325 // User to translate and delete string. |
|
326 $translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages')); |
|
327 // Code for the language. |
|
328 $langcode = 'xx'; |
|
329 // The English name for the language. This will be translated. |
|
330 $name = $this->randomName(16); |
|
331 // The native name for the language. |
|
332 $native = $this->randomName(16); |
|
333 // The domain prefix. |
|
334 $prefix = $langcode; |
|
335 // This is the language indicator on the translation search screen for |
|
336 // untranslated strings. Copied straight from locale.inc. |
|
337 $language_indicator = "<em class=\"locale-untranslated\">$langcode</em> "; |
|
338 // This will be the translation of $name. |
|
339 $translation = $this->randomName(16); |
|
340 |
|
341 // Add custom language. |
|
342 $this->drupalLogin($admin_user); |
|
343 $edit = array( |
|
344 'langcode' => $langcode, |
|
345 'name' => $name, |
|
346 'native' => $native, |
|
347 'prefix' => $prefix, |
|
348 'direction' => '0', |
|
349 ); |
|
350 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
351 // Add string. |
|
352 t($name, array(), array('langcode' => $langcode)); |
|
353 // Reset locale cache. |
|
354 locale_reset(); |
|
355 $this->assertText($langcode, 'Language code found.'); |
|
356 $this->assertText($name, 'Name found.'); |
|
357 $this->assertText($native, 'Native found.'); |
|
358 // No t() here, we do not want to add this string to the database and it's |
|
359 // surely not translated yet. |
|
360 $this->assertText($native, 'Test language added.'); |
|
361 $this->drupalLogout(); |
|
362 |
|
363 // Search for the name and translate it. |
|
364 $this->drupalLogin($translate_user); |
|
365 $search = array( |
|
366 'string' => $name, |
|
367 'language' => 'all', |
|
368 'translation' => 'all', |
|
369 'group' => 'all', |
|
370 ); |
|
371 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
372 // assertText() seems to remove the input field where $name always could be |
|
373 // found, so this is not a false assert. See how assertNoText succeeds |
|
374 // later. |
|
375 $this->assertText($name, 'Search found the name.'); |
|
376 $this->assertRaw($language_indicator, 'Name is untranslated.'); |
|
377 // Assume this is the only result, given the random name. |
|
378 $this->clickLink(t('edit')); |
|
379 // We save the lid from the path. |
|
380 $matches = array(); |
|
381 preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches); |
|
382 $lid = $matches[1]; |
|
383 // No t() here, it's surely not translated yet. |
|
384 $this->assertText($name, 'name found on edit screen.'); |
|
385 $edit = array( |
|
386 "translations[$langcode]" => $translation, |
|
387 ); |
|
388 $this->drupalPost(NULL, $edit, t('Save translations')); |
|
389 $this->assertText(t('The string has been saved.'), 'The string has been saved.'); |
|
390 $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
391 $this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, 't() works.'); |
|
392 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
393 // The indicator should not be here. |
|
394 $this->assertNoRaw($language_indicator, 'String is translated.'); |
|
395 |
|
396 // Verify that a translation set which has an empty target string can be |
|
397 // updated without any database error. |
|
398 db_update('locales_target') |
|
399 ->fields(array('translation' => '')) |
|
400 ->condition('language', $langcode, '=') |
|
401 ->condition('lid', $lid, '=') |
|
402 ->execute(); |
|
403 $this->drupalPost('admin/config/regional/translate/edit/' . $lid, $edit, t('Save translations')); |
|
404 $this->assertText(t('The string has been saved.'), 'The string has been saved.'); |
|
405 |
|
406 // Try to edit a non-existent string and ensure we're redirected correctly. |
|
407 // Assuming we don't have 999,999 strings already. |
|
408 $random_lid = 999999; |
|
409 $this->drupalGet('admin/config/regional/translate/edit/' . $random_lid); |
|
410 $this->assertText(t('String not found'), 'String not found.'); |
|
411 $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
412 $this->drupalLogout(); |
|
413 |
|
414 // Delete the language. |
|
415 $this->drupalLogin($admin_user); |
|
416 $path = 'admin/config/regional/language/delete/' . $langcode; |
|
417 // This a confirm form, we do not need any fields changed. |
|
418 $this->drupalPost($path, array(), t('Delete')); |
|
419 // We need raw here because %locale will add HTML. |
|
420 $this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), 'The test language has been removed.'); |
|
421 // Reload to remove $name. |
|
422 $this->drupalGet($path); |
|
423 // Verify that language is no longer found. |
|
424 $this->assertResponse(404, 'Language no longer found.'); |
|
425 $this->drupalLogout(); |
|
426 |
|
427 // Delete the string. |
|
428 $this->drupalLogin($translate_user); |
|
429 $search = array( |
|
430 'string' => $name, |
|
431 'language' => 'all', |
|
432 'translation' => 'all', |
|
433 'group' => 'all', |
|
434 ); |
|
435 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
436 // Assume this is the only result, given the random name. |
|
437 $this->clickLink(t('delete')); |
|
438 $this->assertText(t('Are you sure you want to delete the string'), '"delete" link is correct.'); |
|
439 // Delete the string. |
|
440 $path = 'admin/config/regional/translate/delete/' . $lid; |
|
441 $this->drupalGet($path); |
|
442 // First test the 'cancel' link. |
|
443 $this->clickLink(t('Cancel')); |
|
444 $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
445 $this->assertRaw($name, 'The string was not deleted.'); |
|
446 // Delete the name string. |
|
447 $this->drupalPost('admin/config/regional/translate/delete/' . $lid, array(), t('Delete')); |
|
448 $this->assertText(t('The string has been removed.'), 'The string has been removed message.'); |
|
449 $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
450 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
451 $this->assertNoText($name, 'Search now can not find the name.'); |
|
452 } |
|
453 |
|
454 /* |
|
455 * Adds a language and checks that the JavaScript translation files are |
|
456 * properly created and rebuilt on deletion. |
|
457 */ |
|
458 function testJavaScriptTranslation() { |
|
459 $user = $this->drupalCreateUser(array('translate interface', 'administer languages', 'access administration pages')); |
|
460 $this->drupalLogin($user); |
|
461 |
|
462 $langcode = 'xx'; |
|
463 // The English name for the language. This will be translated. |
|
464 $name = $this->randomName(16); |
|
465 // The native name for the language. |
|
466 $native = $this->randomName(16); |
|
467 // The domain prefix. |
|
468 $prefix = $langcode; |
|
469 |
|
470 // Add custom language. |
|
471 $edit = array( |
|
472 'langcode' => $langcode, |
|
473 'name' => $name, |
|
474 'native' => $native, |
|
475 'prefix' => $prefix, |
|
476 'direction' => '0', |
|
477 ); |
|
478 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
479 drupal_static_reset('language_list'); |
|
480 |
|
481 // Build the JavaScript translation file. |
|
482 $this->drupalGet('admin/config/regional/translate/translate'); |
|
483 |
|
484 // Retrieve the id of the first string available in the {locales_source} |
|
485 // table and translate it. |
|
486 $query = db_select('locales_source', 'l'); |
|
487 $query->addExpression('min(l.lid)', 'lid'); |
|
488 $result = $query->condition('l.location', '%.js%', 'LIKE') |
|
489 ->condition('l.textgroup', 'default') |
|
490 ->execute(); |
|
491 $url = 'admin/config/regional/translate/edit/' . $result->fetchObject()->lid; |
|
492 $edit = array('translations['. $langcode .']' => $this->randomName()); |
|
493 $this->drupalPost($url, $edit, t('Save translations')); |
|
494 |
|
495 // Trigger JavaScript translation parsing and building. |
|
496 require_once DRUPAL_ROOT . '/includes/locale.inc'; |
|
497 _locale_rebuild_js($langcode); |
|
498 |
|
499 // Retrieve the JavaScript translation hash code for the custom language to |
|
500 // check that the translation file has been properly built. |
|
501 $file = db_select('languages', 'l') |
|
502 ->fields('l', array('javascript')) |
|
503 ->condition('language', $langcode) |
|
504 ->execute() |
|
505 ->fetchObject(); |
|
506 $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/' . $langcode . '_' . $file->javascript . '.js'; |
|
507 $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file created: %file', array('%file' => $result ? $js_file : 'not found'))); |
|
508 |
|
509 // Test JavaScript translation rebuilding. |
|
510 file_unmanaged_delete($js_file); |
|
511 $this->assertTrue($result = !file_exists($js_file), format_string('JavaScript file deleted: %file', array('%file' => $result ? $js_file : 'found'))); |
|
512 cache_clear_all(); |
|
513 _locale_rebuild_js($langcode); |
|
514 $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file rebuilt: %file', array('%file' => $result ? $js_file : 'not found'))); |
|
515 } |
|
516 |
|
517 /** |
|
518 * Tests the validation of the translation input. |
|
519 */ |
|
520 function testStringValidation() { |
|
521 global $base_url; |
|
522 |
|
523 // User to add language and strings. |
|
524 $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface')); |
|
525 $this->drupalLogin($admin_user); |
|
526 $langcode = 'xx'; |
|
527 // The English name for the language. This will be translated. |
|
528 $name = $this->randomName(16); |
|
529 // The native name for the language. |
|
530 $native = $this->randomName(16); |
|
531 // The domain prefix. |
|
532 $prefix = $langcode; |
|
533 // This is the language indicator on the translation search screen for |
|
534 // untranslated strings. Copied straight from locale.inc. |
|
535 $language_indicator = "<em class=\"locale-untranslated\">$langcode</em> "; |
|
536 // These will be the invalid translations of $name. |
|
537 $key = $this->randomName(16); |
|
538 $bad_translations[$key] = "<script>alert('xss');</script>" . $key; |
|
539 $key = $this->randomName(16); |
|
540 $bad_translations[$key] = '<img SRC="javascript:alert(\'xss\');">' . $key; |
|
541 $key = $this->randomName(16); |
|
542 $bad_translations[$key] = '<<SCRIPT>alert("xss");//<</SCRIPT>' . $key; |
|
543 $key = $this->randomName(16); |
|
544 $bad_translations[$key] ="<BODY ONLOAD=alert('xss')>" . $key; |
|
545 |
|
546 // Add custom language. |
|
547 $edit = array( |
|
548 'langcode' => $langcode, |
|
549 'name' => $name, |
|
550 'native' => $native, |
|
551 'prefix' => $prefix, |
|
552 'direction' => '0', |
|
553 ); |
|
554 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
555 // Add string. |
|
556 t($name, array(), array('langcode' => $langcode)); |
|
557 // Reset locale cache. |
|
558 $search = array( |
|
559 'string' => $name, |
|
560 'language' => 'all', |
|
561 'translation' => 'all', |
|
562 'group' => 'all', |
|
563 ); |
|
564 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
565 // Find the edit path. |
|
566 $content = $this->drupalGetContent(); |
|
567 $this->assertTrue(preg_match('@(admin/config/regional/translate/edit/[0-9]+)@', $content, $matches), 'Found the edit path.'); |
|
568 $path = $matches[0]; |
|
569 foreach ($bad_translations as $key => $translation) { |
|
570 $edit = array( |
|
571 "translations[$langcode]" => $translation, |
|
572 ); |
|
573 $this->drupalPost($path, $edit, t('Save translations')); |
|
574 // Check for a form error on the textarea. |
|
575 $form_class = $this->xpath('//form[@id="locale-translate-edit-form"]//textarea/@class'); |
|
576 $this->assertNotIdentical(FALSE, strpos($form_class[0], 'error'), 'The string was rejected as unsafe.'); |
|
577 $this->assertNoText(t('The string has been saved.'), 'The string was not saved.'); |
|
578 } |
|
579 } |
|
580 |
|
581 /** |
|
582 * Tests translation search form. |
|
583 */ |
|
584 function testStringSearch() { |
|
585 global $base_url; |
|
586 |
|
587 // User to add and remove language. |
|
588 $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); |
|
589 // User to translate and delete string. |
|
590 $translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages')); |
|
591 |
|
592 // Code for the language. |
|
593 $langcode = 'xx'; |
|
594 // The English name for the language. This will be translated. |
|
595 $name = $this->randomName(16); |
|
596 // The native name for the language. |
|
597 $native = $this->randomName(16); |
|
598 // The domain prefix. |
|
599 $prefix = $langcode; |
|
600 // This is the language indicator on the translation search screen for |
|
601 // untranslated strings. Copied straight from locale.inc. |
|
602 $language_indicator = "<em class=\"locale-untranslated\">$langcode</em> "; |
|
603 // This will be the translation of $name. |
|
604 $translation = $this->randomName(16); |
|
605 |
|
606 // Add custom language. |
|
607 $this->drupalLogin($admin_user); |
|
608 $edit = array( |
|
609 'langcode' => $langcode, |
|
610 'name' => $name, |
|
611 'native' => $native, |
|
612 'prefix' => $prefix, |
|
613 'direction' => '0', |
|
614 ); |
|
615 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
616 // Add string. |
|
617 t($name, array(), array('langcode' => $langcode)); |
|
618 // Reset locale cache. |
|
619 locale_reset(); |
|
620 $this->drupalLogout(); |
|
621 |
|
622 // Search for the name. |
|
623 $this->drupalLogin($translate_user); |
|
624 $search = array( |
|
625 'string' => $name, |
|
626 'language' => 'all', |
|
627 'translation' => 'all', |
|
628 'group' => 'all', |
|
629 ); |
|
630 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
631 // assertText() seems to remove the input field where $name always could be |
|
632 // found, so this is not a false assert. See how assertNoText succeeds |
|
633 // later. |
|
634 $this->assertText($name, 'Search found the string.'); |
|
635 |
|
636 // Ensure untranslated string doesn't appear if searching on 'only |
|
637 // translated strings'. |
|
638 $search = array( |
|
639 'string' => $name, |
|
640 'language' => 'all', |
|
641 'translation' => 'translated', |
|
642 'group' => 'all', |
|
643 ); |
|
644 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
645 $this->assertText(t('No strings available.'), "Search didn't find the string."); |
|
646 |
|
647 // Ensure untranslated string appears if searching on 'only untranslated |
|
648 // strings' in "all" (hasn't been translated to any language). |
|
649 $search = array( |
|
650 'string' => $name, |
|
651 'language' => 'all', |
|
652 'translation' => 'untranslated', |
|
653 'group' => 'all', |
|
654 ); |
|
655 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
656 $this->assertNoText(t('No strings available.'), 'Search found the string.'); |
|
657 |
|
658 // Ensure untranslated string appears if searching on 'only untranslated |
|
659 // strings' in the custom language (hasn't been translated to that specific language). |
|
660 $search = array( |
|
661 'string' => $name, |
|
662 'language' => $langcode, |
|
663 'translation' => 'untranslated', |
|
664 'group' => 'all', |
|
665 ); |
|
666 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
667 $this->assertNoText(t('No strings available.'), 'Search found the string.'); |
|
668 |
|
669 // Add translation. |
|
670 // Assume this is the only result, given the random name. |
|
671 $this->clickLink(t('edit')); |
|
672 // We save the lid from the path. |
|
673 $matches = array(); |
|
674 preg_match('!admin/config/regional/translate/edit/(\d)+!', $this->getUrl(), $matches); |
|
675 $lid = $matches[1]; |
|
676 $edit = array( |
|
677 "translations[$langcode]" => $translation, |
|
678 ); |
|
679 $this->drupalPost(NULL, $edit, t('Save translations')); |
|
680 |
|
681 // Ensure translated string does appear if searching on 'only |
|
682 // translated strings'. |
|
683 $search = array( |
|
684 'string' => $translation, |
|
685 'language' => 'all', |
|
686 'translation' => 'translated', |
|
687 'group' => 'all', |
|
688 ); |
|
689 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
690 $this->assertNoText(t('No strings available.'), 'Search found the translation.'); |
|
691 |
|
692 // Ensure translated source string doesn't appear if searching on 'only |
|
693 // untranslated strings'. |
|
694 $search = array( |
|
695 'string' => $name, |
|
696 'language' => 'all', |
|
697 'translation' => 'untranslated', |
|
698 'group' => 'all', |
|
699 ); |
|
700 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
701 $this->assertText(t('No strings available.'), "Search didn't find the source string."); |
|
702 |
|
703 // Ensure translated string doesn't appear if searching on 'only |
|
704 // untranslated strings'. |
|
705 $search = array( |
|
706 'string' => $translation, |
|
707 'language' => 'all', |
|
708 'translation' => 'untranslated', |
|
709 'group' => 'all', |
|
710 ); |
|
711 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
712 $this->assertText(t('No strings available.'), "Search didn't find the translation."); |
|
713 |
|
714 // Ensure translated string does appear if searching on the custom language. |
|
715 $search = array( |
|
716 'string' => $translation, |
|
717 'language' => $langcode, |
|
718 'translation' => 'all', |
|
719 'group' => 'all', |
|
720 ); |
|
721 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
722 $this->assertNoText(t('No strings available.'), 'Search found the translation.'); |
|
723 |
|
724 // Ensure translated string doesn't appear if searching on English. |
|
725 $search = array( |
|
726 'string' => $translation, |
|
727 'language' => 'en', |
|
728 'translation' => 'all', |
|
729 'group' => 'all', |
|
730 ); |
|
731 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
732 $this->assertText(t('No strings available.'), "Search didn't find the translation."); |
|
733 |
|
734 // Search for a string that isn't in the system. |
|
735 $unavailable_string = $this->randomName(16); |
|
736 $search = array( |
|
737 'string' => $unavailable_string, |
|
738 'language' => 'all', |
|
739 'translation' => 'all', |
|
740 'group' => 'all', |
|
741 ); |
|
742 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
743 $this->assertText(t('No strings available.'), "Search didn't find the invalid string."); |
|
744 } |
|
745 } |
|
746 |
|
747 /** |
|
748 * Tests plural index computation functionality. |
|
749 */ |
|
750 class LocalePluralFormatTest extends DrupalWebTestCase { |
|
751 public static function getInfo() { |
|
752 return array( |
|
753 'name' => 'Plural formula evaluation', |
|
754 'description' => 'Tests plural formula evaluation for various languages.', |
|
755 'group' => 'Locale', |
|
756 ); |
|
757 } |
|
758 |
|
759 function setUp() { |
|
760 parent::setUp('locale', 'locale_test'); |
|
761 |
|
762 $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages')); |
|
763 $this->drupalLogin($admin_user); |
|
764 |
|
765 // Import some .po files with formulas to set up the environment. |
|
766 // These will also add the languages to the system and enable them. |
|
767 $this->importPoFile($this->getPoFileWithSimplePlural(), array( |
|
768 'langcode' => 'fr', |
|
769 )); |
|
770 $this->importPoFile($this->getPoFileWithComplexPlural(), array( |
|
771 'langcode' => 'hr', |
|
772 )); |
|
773 } |
|
774 |
|
775 /** |
|
776 * Tests locale_get_plural() functionality. |
|
777 */ |
|
778 function testGetPluralFormat() { |
|
779 $this->drupalGet('locale_test_plural_format_page'); |
|
780 $tests = _locale_test_plural_format_tests(); |
|
781 $result = array(); |
|
782 foreach ($tests as $test) { |
|
783 $this->assertPluralFormat($test['count'], $test['language'], $test['expected-result']); |
|
784 } |
|
785 } |
|
786 |
|
787 /** |
|
788 * Helper assert to test locale_get_plural page. |
|
789 * |
|
790 * @param $count |
|
791 * Number for testing. |
|
792 * @param $lang |
|
793 * Language for testing |
|
794 * @param $expected_result |
|
795 * Expected result. |
|
796 * @param $message |
|
797 */ |
|
798 function assertPluralFormat($count, $lang, $expected_result) { |
|
799 $message_param = array( |
|
800 '@lang' => $lang, |
|
801 '@count' => $count, |
|
802 '@expected_result' => $expected_result, |
|
803 ); |
|
804 $message = t("Computed plural index for '@lang' with count @count is @expected_result.", $message_param); |
|
805 |
|
806 $message_param = array( |
|
807 '@lang' => $lang, |
|
808 '@expected_result' => $expected_result, |
|
809 ); |
|
810 $this->assertText(format_string('Language: @lang, locale_get_plural: @expected_result.', $message_param, $message)); |
|
811 } |
|
812 |
|
813 /** |
|
814 * Imports a standalone .po file in a given language. |
|
815 * |
|
816 * @param $contents |
|
817 * Contents of the .po file to import. |
|
818 * @param $options |
|
819 * Additional options to pass to the translation import form. |
|
820 */ |
|
821 function importPoFile($contents, array $options = array()) { |
|
822 $name = drupal_tempnam('temporary://', "po_") . '.po'; |
|
823 file_put_contents($name, $contents); |
|
824 $options['files[file]'] = $name; |
|
825 $this->drupalPost('admin/config/regional/translate/import', $options, t('Import')); |
|
826 drupal_unlink($name); |
|
827 } |
|
828 |
|
829 /** |
|
830 * Returns a .po file with a simple plural formula. |
|
831 */ |
|
832 function getPoFileWithSimplePlural() { |
|
833 return <<< EOF |
|
834 msgid "" |
|
835 msgstr "" |
|
836 "Project-Id-Version: Drupal 7\\n" |
|
837 "MIME-Version: 1.0\\n" |
|
838 "Content-Type: text/plain; charset=UTF-8\\n" |
|
839 "Content-Transfer-Encoding: 8bit\\n" |
|
840 "Plural-Forms: nplurals=2; plural=(n!=1);\\n" |
|
841 |
|
842 msgid "One sheep" |
|
843 msgid_plural "@count sheep" |
|
844 msgstr[0] "un mouton" |
|
845 msgstr[1] "@count moutons" |
|
846 |
|
847 msgid "Monday" |
|
848 msgstr "lundi" |
|
849 EOF; |
|
850 } |
|
851 |
|
852 /** |
|
853 * Returns a .po file with a complex plural formula. |
|
854 */ |
|
855 function getPoFileWithComplexPlural() { |
|
856 return <<< EOF |
|
857 msgid "" |
|
858 msgstr "" |
|
859 "Project-Id-Version: Drupal 7\\n" |
|
860 "MIME-Version: 1.0\\n" |
|
861 "Content-Type: text/plain; charset=UTF-8\\n" |
|
862 "Content-Transfer-Encoding: 8bit\\n" |
|
863 "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n" |
|
864 |
|
865 msgid "1 hour" |
|
866 msgid_plural "@count hours" |
|
867 msgstr[0] "@count sat" |
|
868 msgstr[1] "@count sata" |
|
869 msgstr[2] "@count sati" |
|
870 |
|
871 msgid "Monday" |
|
872 msgstr "Ponedjeljak" |
|
873 EOF; |
|
874 } |
|
875 } |
|
876 |
|
877 /** |
|
878 * Functional tests for the import of translation files. |
|
879 */ |
|
880 class LocaleImportFunctionalTest extends DrupalWebTestCase { |
|
881 public static function getInfo() { |
|
882 return array( |
|
883 'name' => 'Translation import', |
|
884 'description' => 'Tests the import of locale files.', |
|
885 'group' => 'Locale', |
|
886 ); |
|
887 } |
|
888 |
|
889 /** |
|
890 * A user able to create languages and import translations. |
|
891 */ |
|
892 protected $admin_user = NULL; |
|
893 |
|
894 function setUp() { |
|
895 parent::setUp('locale', 'locale_test'); |
|
896 |
|
897 $this->admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages')); |
|
898 $this->drupalLogin($this->admin_user); |
|
899 } |
|
900 |
|
901 /** |
|
902 * Test import of standalone .po files. |
|
903 */ |
|
904 function testStandalonePoFile() { |
|
905 // Try importing a .po file. |
|
906 $this->importPoFile($this->getPoFile(), array( |
|
907 'langcode' => 'fr', |
|
908 )); |
|
909 |
|
910 // The import should automatically create the corresponding language. |
|
911 $this->assertRaw(t('The language %language has been created.', array('%language' => 'French')), 'The language has been automatically created.'); |
|
912 |
|
913 // The import should have created 7 strings. |
|
914 $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 9, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.'); |
|
915 |
|
916 // This import should have saved plural forms to have 2 variants. |
|
917 $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'Plural number initialized.'); |
|
918 |
|
919 // Ensure we were redirected correctly. |
|
920 $this->assertEqual($this->getUrl(), url('admin/config/regional/translate', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
921 |
|
922 |
|
923 // Try importing a .po file with invalid tags in the default text group. |
|
924 $this->importPoFile($this->getBadPoFile(), array( |
|
925 'langcode' => 'fr', |
|
926 )); |
|
927 |
|
928 // The import should have created 1 string and rejected 2. |
|
929 $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.'); |
|
930 $skip_message = format_plural(2, 'One translation string was skipped because it contains disallowed HTML.', '@count translation strings were skipped because they contain disallowed HTML.'); |
|
931 $this->assertRaw($skip_message, 'Unsafe strings were skipped.'); |
|
932 |
|
933 |
|
934 // Try importing a .po file with invalid tags in a non default text group. |
|
935 $this->importPoFile($this->getBadPoFile(), array( |
|
936 'langcode' => 'fr', |
|
937 'group' => 'custom', |
|
938 )); |
|
939 |
|
940 // The import should have created 3 strings. |
|
941 $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 3, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.'); |
|
942 |
|
943 |
|
944 // Try importing a .po file which doesn't exist. |
|
945 $name = $this->randomName(16); |
|
946 $this->drupalPost('admin/config/regional/translate/import', array( |
|
947 'langcode' => 'fr', |
|
948 'files[file]' => $name, |
|
949 'group' => 'custom', |
|
950 ), t('Import')); |
|
951 $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/import', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
952 $this->assertText(t('File to import not found.'), 'File to import not found message.'); |
|
953 |
|
954 |
|
955 // Try importing a .po file with overriding strings, and ensure existing |
|
956 // strings are kept. |
|
957 $this->importPoFile($this->getOverwritePoFile(), array( |
|
958 'langcode' => 'fr', |
|
959 'mode' => 1, // Existing strings are kept, only new strings are added. |
|
960 )); |
|
961 |
|
962 // The import should have created 1 string. |
|
963 $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.'); |
|
964 // Ensure string wasn't overwritten. |
|
965 $search = array( |
|
966 'string' => 'Montag', |
|
967 'language' => 'fr', |
|
968 'translation' => 'translated', |
|
969 'group' => 'all', |
|
970 ); |
|
971 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
972 $this->assertText(t('No strings available.'), 'String not overwritten by imported string.'); |
|
973 |
|
974 // This import should not have changed number of plural forms. |
|
975 $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'Plural numbers untouched.'); |
|
976 |
|
977 $this->importPoFile($this->getPoFileWithBrokenPlural(), array( |
|
978 'langcode' => 'fr', |
|
979 'mode' => 1, // Existing strings are kept, only new strings are added. |
|
980 )); |
|
981 |
|
982 // Attempt to import broken .po file as well to prove that this |
|
983 // will not overwrite the proper plural formula imported above. |
|
984 $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'Broken plurals: plural numbers untouched.'); |
|
985 |
|
986 $this->importPoFile($this->getPoFileWithMissingPlural(), array( |
|
987 'langcode' => 'fr', |
|
988 'mode' => 1, // Existing strings are kept, only new strings are added. |
|
989 )); |
|
990 |
|
991 // Attempt to import .po file which has no plurals and prove that this |
|
992 // will not overwrite the proper plural formula imported above. |
|
993 $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 2, 'No plurals: plural numbers untouched.'); |
|
994 |
|
995 |
|
996 // Try importing a .po file with overriding strings, and ensure existing |
|
997 // strings are overwritten. |
|
998 $this->importPoFile($this->getOverwritePoFile(), array( |
|
999 'langcode' => 'fr', |
|
1000 'mode' => 0, // Strings in the uploaded file replace existing ones, new ones are added. |
|
1001 )); |
|
1002 |
|
1003 // The import should have updated 2 strings. |
|
1004 $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 2, '%delete' => 0)), 'The translation file was successfully imported.'); |
|
1005 // Ensure string was overwritten. |
|
1006 $search = array( |
|
1007 'string' => 'Montag', |
|
1008 'language' => 'fr', |
|
1009 'translation' => 'translated', |
|
1010 'group' => 'all', |
|
1011 ); |
|
1012 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
1013 $this->assertNoText(t('No strings available.'), 'String overwritten by imported string.'); |
|
1014 // This import should have changed number of plural forms. |
|
1015 $this->assert(db_query("SELECT plurals FROM {languages} WHERE language = 'fr'")->fetchField() == 3, 'Plural numbers changed.'); |
|
1016 } |
|
1017 |
|
1018 /** |
|
1019 * Test automatic import of a module's translation files when a language is |
|
1020 * enabled. |
|
1021 */ |
|
1022 function testAutomaticModuleTranslationImportLanguageEnable() { |
|
1023 // Code for the language - manually set to match the test translation file. |
|
1024 $langcode = 'xx'; |
|
1025 // The English name for the language. |
|
1026 $name = $this->randomName(16); |
|
1027 // The native name for the language. |
|
1028 $native = $this->randomName(16); |
|
1029 // The domain prefix. |
|
1030 $prefix = $langcode; |
|
1031 |
|
1032 // Create a custom language. |
|
1033 $edit = array( |
|
1034 'langcode' => $langcode, |
|
1035 'name' => $name, |
|
1036 'native' => $native, |
|
1037 'prefix' => $prefix, |
|
1038 'direction' => '0', |
|
1039 ); |
|
1040 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
1041 |
|
1042 // Ensure the translation file was automatically imported when language was |
|
1043 // added. |
|
1044 $this->assertText(t('One translation file imported for the enabled modules.'), 'Language file automatically imported.'); |
|
1045 |
|
1046 // Ensure strings were successfully imported. |
|
1047 $search = array( |
|
1048 'string' => 'lundi', |
|
1049 'language' => $langcode, |
|
1050 'translation' => 'translated', |
|
1051 'group' => 'all', |
|
1052 ); |
|
1053 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
1054 $this->assertNoText(t('No strings available.'), 'String successfully imported.'); |
|
1055 } |
|
1056 |
|
1057 /** |
|
1058 * Test msgctxt context support. |
|
1059 */ |
|
1060 function testLanguageContext() { |
|
1061 // Try importing a .po file. |
|
1062 $this->importPoFile($this->getPoFileWithContext(), array( |
|
1063 'langcode' => 'hr', |
|
1064 )); |
|
1065 |
|
1066 $this->assertIdentical(t('May', array(), array('langcode' => 'hr', 'context' => 'Long month name')), 'Svibanj', 'Long month name context is working.'); |
|
1067 $this->assertIdentical(t('May', array(), array('langcode' => 'hr')), 'Svi.', 'Default context is working.'); |
|
1068 } |
|
1069 |
|
1070 /** |
|
1071 * Test empty msgstr at end of .po file see #611786. |
|
1072 */ |
|
1073 function testEmptyMsgstr() { |
|
1074 $langcode = 'hu'; |
|
1075 |
|
1076 // Try importing a .po file. |
|
1077 $this->importPoFile($this->getPoFileWithMsgstr(), array( |
|
1078 'langcode' => $langcode, |
|
1079 )); |
|
1080 |
|
1081 $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 1, '%update' => 0, '%delete' => 0)), 'The translation file was successfully imported.'); |
|
1082 $this->assertIdentical(t('Operations', array(), array('langcode' => $langcode)), 'Műveletek', 'String imported and translated.'); |
|
1083 |
|
1084 // Try importing a .po file. |
|
1085 $this->importPoFile($this->getPoFileWithEmptyMsgstr(), array( |
|
1086 'langcode' => $langcode, |
|
1087 'mode' => 0, |
|
1088 )); |
|
1089 $this->assertRaw(t('The translation was successfully imported. There are %number newly created translated strings, %update strings were updated and %delete strings were removed.', array('%number' => 0, '%update' => 0, '%delete' => 1)), 'The translation file was successfully imported.'); |
|
1090 // This is the language indicator on the translation search screen for |
|
1091 // untranslated strings. Copied straight from locale.inc. |
|
1092 $language_indicator = "<em class=\"locale-untranslated\">$langcode</em> "; |
|
1093 $str = "Operations"; |
|
1094 $search = array( |
|
1095 'string' => $str, |
|
1096 'language' => 'all', |
|
1097 'translation' => 'all', |
|
1098 'group' => 'all', |
|
1099 ); |
|
1100 $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); |
|
1101 // assertText() seems to remove the input field where $str always could be |
|
1102 // found, so this is not a false assert. |
|
1103 $this->assertText($str, 'Search found the string.'); |
|
1104 $this->assertRaw($language_indicator, 'String is untranslated again.'); |
|
1105 } |
|
1106 |
|
1107 /** |
|
1108 * Helper function: import a standalone .po file in a given language. |
|
1109 * |
|
1110 * @param $contents |
|
1111 * Contents of the .po file to import. |
|
1112 * @param $options |
|
1113 * Additional options to pass to the translation import form. |
|
1114 */ |
|
1115 function importPoFile($contents, array $options = array()) { |
|
1116 $name = drupal_tempnam('temporary://', "po_") . '.po'; |
|
1117 file_put_contents($name, $contents); |
|
1118 $options['files[file]'] = $name; |
|
1119 $this->drupalPost('admin/config/regional/translate/import', $options, t('Import')); |
|
1120 drupal_unlink($name); |
|
1121 } |
|
1122 |
|
1123 /** |
|
1124 * Helper function that returns a proper .po file. |
|
1125 */ |
|
1126 function getPoFile() { |
|
1127 return <<< EOF |
|
1128 msgid "" |
|
1129 msgstr "" |
|
1130 "Project-Id-Version: Drupal 7\\n" |
|
1131 "MIME-Version: 1.0\\n" |
|
1132 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1133 "Content-Transfer-Encoding: 8bit\\n" |
|
1134 "Plural-Forms: nplurals=2; plural=(n > 1);\\n" |
|
1135 |
|
1136 msgid "One sheep" |
|
1137 msgid_plural "@count sheep" |
|
1138 msgstr[0] "un mouton" |
|
1139 msgstr[1] "@count moutons" |
|
1140 |
|
1141 msgid "Monday" |
|
1142 msgstr "lundi" |
|
1143 |
|
1144 msgid "Tuesday" |
|
1145 msgstr "mardi" |
|
1146 |
|
1147 msgid "Wednesday" |
|
1148 msgstr "mercredi" |
|
1149 |
|
1150 msgid "Thursday" |
|
1151 msgstr "jeudi" |
|
1152 |
|
1153 msgid "Friday" |
|
1154 msgstr "vendredi" |
|
1155 |
|
1156 msgid "Saturday" |
|
1157 msgstr "samedi" |
|
1158 |
|
1159 msgid "Sunday" |
|
1160 msgstr "dimanche" |
|
1161 EOF; |
|
1162 } |
|
1163 |
|
1164 /** |
|
1165 * Helper function that returns a bad .po file. |
|
1166 */ |
|
1167 function getBadPoFile() { |
|
1168 return <<< EOF |
|
1169 msgid "" |
|
1170 msgstr "" |
|
1171 "Project-Id-Version: Drupal 7\\n" |
|
1172 "MIME-Version: 1.0\\n" |
|
1173 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1174 "Content-Transfer-Encoding: 8bit\\n" |
|
1175 "Plural-Forms: nplurals=2; plural=(n > 1);\\n" |
|
1176 |
|
1177 msgid "Save configuration" |
|
1178 msgstr "Enregistrer la configuration" |
|
1179 |
|
1180 msgid "edit" |
|
1181 msgstr "modifier<img SRC="javascript:alert(\'xss\');">" |
|
1182 |
|
1183 msgid "delete" |
|
1184 msgstr "supprimer<script>alert('xss');</script>" |
|
1185 |
|
1186 EOF; |
|
1187 } |
|
1188 |
|
1189 /** |
|
1190 * Helper function that returns a proper .po file, for testing overwriting |
|
1191 * existing translations. |
|
1192 */ |
|
1193 function getOverwritePoFile() { |
|
1194 return <<< EOF |
|
1195 msgid "" |
|
1196 msgstr "" |
|
1197 "Project-Id-Version: Drupal 7\\n" |
|
1198 "MIME-Version: 1.0\\n" |
|
1199 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1200 "Content-Transfer-Encoding: 8bit\\n" |
|
1201 "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n" |
|
1202 |
|
1203 msgid "Monday" |
|
1204 msgstr "Montag" |
|
1205 |
|
1206 msgid "Day" |
|
1207 msgstr "Jour" |
|
1208 EOF; |
|
1209 } |
|
1210 |
|
1211 /** |
|
1212 * Helper function that returns a .po file with context. |
|
1213 */ |
|
1214 function getPoFileWithContext() { |
|
1215 // Croatian (code hr) is one of the languages that have a different |
|
1216 // form for the full name and the abbreviated name for the month May. |
|
1217 return <<< EOF |
|
1218 msgid "" |
|
1219 msgstr "" |
|
1220 "Project-Id-Version: Drupal 7\\n" |
|
1221 "MIME-Version: 1.0\\n" |
|
1222 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1223 "Content-Transfer-Encoding: 8bit\\n" |
|
1224 "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n" |
|
1225 |
|
1226 msgctxt "Long month name" |
|
1227 msgid "May" |
|
1228 msgstr "Svibanj" |
|
1229 |
|
1230 msgid "May" |
|
1231 msgstr "Svi." |
|
1232 EOF; |
|
1233 } |
|
1234 |
|
1235 /** |
|
1236 * Helper function that returns a .po file with an empty last item. |
|
1237 */ |
|
1238 function getPoFileWithEmptyMsgstr() { |
|
1239 return <<< EOF |
|
1240 msgid "" |
|
1241 msgstr "" |
|
1242 "Project-Id-Version: Drupal 7\\n" |
|
1243 "MIME-Version: 1.0\\n" |
|
1244 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1245 "Content-Transfer-Encoding: 8bit\\n" |
|
1246 "Plural-Forms: nplurals=2; plural=(n > 1);\\n" |
|
1247 |
|
1248 msgid "Operations" |
|
1249 msgstr "" |
|
1250 |
|
1251 EOF; |
|
1252 } |
|
1253 /** |
|
1254 * Helper function that returns a .po file with an empty last item. |
|
1255 */ |
|
1256 function getPoFileWithMsgstr() { |
|
1257 return <<< EOF |
|
1258 msgid "" |
|
1259 msgstr "" |
|
1260 "Project-Id-Version: Drupal 7\\n" |
|
1261 "MIME-Version: 1.0\\n" |
|
1262 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1263 "Content-Transfer-Encoding: 8bit\\n" |
|
1264 "Plural-Forms: nplurals=2; plural=(n > 1);\\n" |
|
1265 |
|
1266 msgid "Operations" |
|
1267 msgstr "Műveletek" |
|
1268 |
|
1269 msgid "Will not appear in Drupal core, so we can ensure the test passes" |
|
1270 msgstr "" |
|
1271 |
|
1272 EOF; |
|
1273 } |
|
1274 |
|
1275 |
|
1276 /** |
|
1277 * Returns a .po file with a missing plural formula. |
|
1278 */ |
|
1279 function getPoFileWithMissingPlural() { |
|
1280 return <<< EOF |
|
1281 msgid "" |
|
1282 msgstr "" |
|
1283 "Project-Id-Version: Drupal 7\\n" |
|
1284 "MIME-Version: 1.0\\n" |
|
1285 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1286 "Content-Transfer-Encoding: 8bit\\n" |
|
1287 |
|
1288 msgid "Monday" |
|
1289 msgstr "Ponedjeljak" |
|
1290 EOF; |
|
1291 } |
|
1292 |
|
1293 /** |
|
1294 * Returns a .po file with a broken plural formula. |
|
1295 */ |
|
1296 function getPoFileWithBrokenPlural() { |
|
1297 return <<< EOF |
|
1298 msgid "" |
|
1299 msgstr "" |
|
1300 "Project-Id-Version: Drupal 7\\n" |
|
1301 "MIME-Version: 1.0\\n" |
|
1302 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1303 "Content-Transfer-Encoding: 8bit\\n" |
|
1304 "Plural-Forms: broken, will not parse\\n" |
|
1305 |
|
1306 msgid "Monday" |
|
1307 msgstr "lundi" |
|
1308 EOF; |
|
1309 } |
|
1310 |
|
1311 } |
|
1312 |
|
1313 /** |
|
1314 * Functional tests for the export of translation files. |
|
1315 */ |
|
1316 class LocaleExportFunctionalTest extends DrupalWebTestCase { |
|
1317 public static function getInfo() { |
|
1318 return array( |
|
1319 'name' => 'Translation export', |
|
1320 'description' => 'Tests the exportation of locale files.', |
|
1321 'group' => 'Locale', |
|
1322 ); |
|
1323 } |
|
1324 |
|
1325 /** |
|
1326 * A user able to create languages and export translations. |
|
1327 */ |
|
1328 protected $admin_user = NULL; |
|
1329 |
|
1330 function setUp() { |
|
1331 parent::setUp('locale', 'locale_test'); |
|
1332 |
|
1333 $this->admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages')); |
|
1334 $this->drupalLogin($this->admin_user); |
|
1335 } |
|
1336 |
|
1337 /** |
|
1338 * Test exportation of translations. |
|
1339 */ |
|
1340 function testExportTranslation() { |
|
1341 // First import some known translations. |
|
1342 // This will also automatically enable the 'fr' language. |
|
1343 $name = drupal_tempnam('temporary://', "po_") . '.po'; |
|
1344 file_put_contents($name, $this->getPoFile()); |
|
1345 $this->drupalPost('admin/config/regional/translate/import', array( |
|
1346 'langcode' => 'fr', |
|
1347 'files[file]' => $name, |
|
1348 ), t('Import')); |
|
1349 drupal_unlink($name); |
|
1350 |
|
1351 // Get the French translations. |
|
1352 $this->drupalPost('admin/config/regional/translate/export', array( |
|
1353 'langcode' => 'fr', |
|
1354 ), t('Export')); |
|
1355 |
|
1356 // Ensure we have a translation file. |
|
1357 $this->assertRaw('# French translation of Drupal', 'Exported French translation file.'); |
|
1358 // Ensure our imported translations exist in the file. |
|
1359 $this->assertRaw('msgstr "lundi"', 'French translations present in exported file.'); |
|
1360 } |
|
1361 |
|
1362 /** |
|
1363 * Test exportation of translation template file. |
|
1364 */ |
|
1365 function testExportTranslationTemplateFile() { |
|
1366 // Get the translation template file. |
|
1367 // There are two 'Export' buttons on this page, but it somehow works. It'd |
|
1368 // be better if we could use the submit button id like documented but that |
|
1369 // doesn't work. |
|
1370 $this->drupalPost('admin/config/regional/translate/export', array(), t('Export')); |
|
1371 // Ensure we have a translation file. |
|
1372 $this->assertRaw('# LANGUAGE translation of PROJECT', 'Exported translation template file.'); |
|
1373 } |
|
1374 |
|
1375 /** |
|
1376 * Helper function that returns a proper .po file. |
|
1377 */ |
|
1378 function getPoFile() { |
|
1379 return <<< EOF |
|
1380 msgid "" |
|
1381 msgstr "" |
|
1382 "Project-Id-Version: Drupal 6\\n" |
|
1383 "MIME-Version: 1.0\\n" |
|
1384 "Content-Type: text/plain; charset=UTF-8\\n" |
|
1385 "Content-Transfer-Encoding: 8bit\\n" |
|
1386 "Plural-Forms: nplurals=2; plural=(n > 1);\\n" |
|
1387 |
|
1388 msgid "Monday" |
|
1389 msgstr "lundi" |
|
1390 EOF; |
|
1391 } |
|
1392 |
|
1393 } |
|
1394 |
|
1395 /** |
|
1396 * Tests for the st() function. |
|
1397 */ |
|
1398 class LocaleInstallTest extends DrupalWebTestCase { |
|
1399 public static function getInfo() { |
|
1400 return array( |
|
1401 'name' => 'String translation using st()', |
|
1402 'description' => 'Tests that st() works like t().', |
|
1403 'group' => 'Locale', |
|
1404 ); |
|
1405 } |
|
1406 |
|
1407 function setUp() { |
|
1408 parent::setUp('locale'); |
|
1409 |
|
1410 // st() lives in install.inc, so ensure that it is loaded for all tests. |
|
1411 require_once DRUPAL_ROOT . '/includes/install.inc'; |
|
1412 } |
|
1413 |
|
1414 /** |
|
1415 * Verify that function signatures of t() and st() are equal. |
|
1416 */ |
|
1417 function testFunctionSignatures() { |
|
1418 $reflector_t = new ReflectionFunction('t'); |
|
1419 $reflector_st = new ReflectionFunction('st'); |
|
1420 $this->assertEqual($reflector_t->getParameters(), $reflector_st->getParameters(), 'Function signatures of t() and st() are equal.'); |
|
1421 } |
|
1422 } |
|
1423 |
|
1424 /** |
|
1425 * Locale uninstall with English UI functional test. |
|
1426 */ |
|
1427 class LocaleUninstallFunctionalTest extends DrupalWebTestCase { |
|
1428 public static function getInfo() { |
|
1429 return array( |
|
1430 'name' => 'Locale uninstall (EN)', |
|
1431 'description' => 'Tests the uninstall process using the built-in UI language.', |
|
1432 'group' => 'Locale', |
|
1433 ); |
|
1434 } |
|
1435 |
|
1436 /** |
|
1437 * The default language set for the UI before uninstall. |
|
1438 */ |
|
1439 protected $language; |
|
1440 |
|
1441 function setUp() { |
|
1442 parent::setUp('locale'); |
|
1443 $this->language = 'en'; |
|
1444 } |
|
1445 |
|
1446 /** |
|
1447 * Check if the values of the Locale variables are correct after uninstall. |
|
1448 */ |
|
1449 function testUninstallProcess() { |
|
1450 $locale_module = array('locale'); |
|
1451 |
|
1452 // Add a new language and optionally set it as default. |
|
1453 require_once DRUPAL_ROOT . '/includes/locale.inc'; |
|
1454 locale_add_language('fr', 'French', 'Français', LANGUAGE_LTR, '', '', TRUE, $this->language == 'fr'); |
|
1455 |
|
1456 // Check the UI language. |
|
1457 drupal_language_initialize(); |
|
1458 global $language; |
|
1459 $this->assertEqual($language->language, $this->language, format_string('Current language: %lang', array('%lang' => $language->language))); |
|
1460 |
|
1461 // Enable multilingual workflow option for articles. |
|
1462 variable_set('language_content_type_article', 1); |
|
1463 |
|
1464 // Change JavaScript translations directory. |
|
1465 variable_set('locale_js_directory', 'js_translations'); |
|
1466 |
|
1467 // Build the JavaScript translation file for French. |
|
1468 $user = $this->drupalCreateUser(array('translate interface', 'access administration pages')); |
|
1469 $this->drupalLogin($user); |
|
1470 $this->drupalGet('admin/config/regional/translate/translate'); |
|
1471 $string = db_query('SELECT min(lid) AS lid FROM {locales_source} WHERE location LIKE :location AND textgroup = :textgroup', array( |
|
1472 ':location' => '%.js%', |
|
1473 ':textgroup' => 'default', |
|
1474 ))->fetchObject(); |
|
1475 $edit = array('translations[fr]' => 'french translation'); |
|
1476 $this->drupalPost('admin/config/regional/translate/edit/' . $string->lid, $edit, t('Save translations')); |
|
1477 _locale_rebuild_js('fr'); |
|
1478 $file = db_query('SELECT javascript FROM {languages} WHERE language = :language', array(':language' => 'fr'))->fetchObject(); |
|
1479 $js_file = 'public://' . variable_get('locale_js_directory', 'languages') . '/fr_' . $file->javascript . '.js'; |
|
1480 $this->assertTrue($result = file_exists($js_file), format_string('JavaScript file created: %file', array('%file' => $result ? $js_file : 'none'))); |
|
1481 |
|
1482 // Disable string caching. |
|
1483 variable_set('locale_cache_strings', 0); |
|
1484 |
|
1485 // Change language negotiation options. |
|
1486 drupal_load('module', 'locale'); |
|
1487 variable_set('language_types', drupal_language_types() + array('language_custom' => TRUE)); |
|
1488 variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, locale_language_negotiation_info()); |
|
1489 variable_set('language_negotiation_' . LANGUAGE_TYPE_CONTENT, locale_language_negotiation_info()); |
|
1490 variable_set('language_negotiation_' . LANGUAGE_TYPE_URL, locale_language_negotiation_info()); |
|
1491 |
|
1492 // Change language providers settings. |
|
1493 variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX); |
|
1494 variable_set('locale_language_negotiation_session_param', TRUE); |
|
1495 |
|
1496 // Uninstall Locale. |
|
1497 module_disable($locale_module); |
|
1498 drupal_uninstall_modules($locale_module); |
|
1499 |
|
1500 // Visit the front page. |
|
1501 $this->drupalGet(''); |
|
1502 |
|
1503 // Check the init language logic. |
|
1504 drupal_language_initialize(); |
|
1505 $this->assertEqual($language->language, 'en', format_string('Language after uninstall: %lang', array('%lang' => $language->language))); |
|
1506 |
|
1507 // Check JavaScript files deletion. |
|
1508 $this->assertTrue($result = !file_exists($js_file), format_string('JavaScript file deleted: %file', array('%file' => $result ? $js_file : 'found'))); |
|
1509 |
|
1510 // Check language count. |
|
1511 $language_count = variable_get('language_count', 1); |
|
1512 $this->assertEqual($language_count, 1, format_string('Language count: %count', array('%count' => $language_count))); |
|
1513 |
|
1514 // Check language negotiation. |
|
1515 require_once DRUPAL_ROOT . '/includes/language.inc'; |
|
1516 $this->assertTrue(count(language_types()) == count(drupal_language_types()), 'Language types reset'); |
|
1517 $language_negotiation = language_negotiation_get(LANGUAGE_TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_DEFAULT; |
|
1518 $this->assertTrue($language_negotiation, format_string('Interface language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set'))); |
|
1519 $language_negotiation = language_negotiation_get(LANGUAGE_TYPE_CONTENT) == LANGUAGE_NEGOTIATION_DEFAULT; |
|
1520 $this->assertTrue($language_negotiation, format_string('Content language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set'))); |
|
1521 $language_negotiation = language_negotiation_get(LANGUAGE_TYPE_URL) == LANGUAGE_NEGOTIATION_DEFAULT; |
|
1522 $this->assertTrue($language_negotiation, format_string('URL language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set'))); |
|
1523 |
|
1524 // Check language providers settings. |
|
1525 $this->assertFalse(variable_get('locale_language_negotiation_url_part', FALSE), 'URL language provider indicator settings cleared.'); |
|
1526 $this->assertFalse(variable_get('locale_language_negotiation_session_param', FALSE), 'Visit language provider settings cleared.'); |
|
1527 |
|
1528 // Check JavaScript parsed. |
|
1529 $javascript_parsed_count = count(variable_get('javascript_parsed', array())); |
|
1530 $this->assertEqual($javascript_parsed_count, 0, format_string('JavaScript parsed count: %count', array('%count' => $javascript_parsed_count))); |
|
1531 |
|
1532 // Check multilingual workflow option for articles. |
|
1533 $multilingual = variable_get('language_content_type_article', 0); |
|
1534 $this->assertEqual($multilingual, 0, format_string('Multilingual workflow option: %status', array('%status' => $multilingual ? 'enabled': 'disabled'))); |
|
1535 |
|
1536 // Check JavaScript translations directory. |
|
1537 $locale_js_directory = variable_get('locale_js_directory', 'languages'); |
|
1538 $this->assertEqual($locale_js_directory, 'languages', format_string('JavaScript translations directory: %dir', array('%dir' => $locale_js_directory))); |
|
1539 |
|
1540 // Check string caching. |
|
1541 $locale_cache_strings = variable_get('locale_cache_strings', 1); |
|
1542 $this->assertEqual($locale_cache_strings, 1, format_string('String caching: %status', array('%status' => $locale_cache_strings ? 'enabled': 'disabled'))); |
|
1543 } |
|
1544 } |
|
1545 |
|
1546 /** |
|
1547 * Locale uninstall with French UI functional test. |
|
1548 * |
|
1549 * Because this class extends LocaleUninstallFunctionalTest, it doesn't require a new |
|
1550 * test of its own. Rather, it switches the default UI language in setUp and then |
|
1551 * runs the testUninstallProcess (which it inherits from LocaleUninstallFunctionalTest) |
|
1552 * to test with this new language. |
|
1553 */ |
|
1554 class LocaleUninstallFrenchFunctionalTest extends LocaleUninstallFunctionalTest { |
|
1555 public static function getInfo() { |
|
1556 return array( |
|
1557 'name' => 'Locale uninstall (FR)', |
|
1558 'description' => 'Tests the uninstall process using French as interface language.', |
|
1559 'group' => 'Locale', |
|
1560 ); |
|
1561 } |
|
1562 |
|
1563 function setUp() { |
|
1564 parent::setUp(); |
|
1565 $this->language = 'fr'; |
|
1566 } |
|
1567 } |
|
1568 |
|
1569 /** |
|
1570 * Functional tests for the language switching feature. |
|
1571 */ |
|
1572 class LocaleLanguageSwitchingFunctionalTest extends DrupalWebTestCase { |
|
1573 |
|
1574 public static function getInfo() { |
|
1575 return array( |
|
1576 'name' => 'Language switching', |
|
1577 'description' => 'Tests for the language switching feature.', |
|
1578 'group' => 'Locale', |
|
1579 ); |
|
1580 } |
|
1581 |
|
1582 function setUp() { |
|
1583 parent::setUp('locale'); |
|
1584 |
|
1585 // Create and login user. |
|
1586 $admin_user = $this->drupalCreateUser(array('administer blocks', 'administer languages', 'translate interface', 'access administration pages')); |
|
1587 $this->drupalLogin($admin_user); |
|
1588 } |
|
1589 |
|
1590 /** |
|
1591 * Functional tests for the language switcher block. |
|
1592 */ |
|
1593 function testLanguageBlock() { |
|
1594 // Enable the language switching block. |
|
1595 $language_type = LANGUAGE_TYPE_INTERFACE; |
|
1596 $edit = array( |
|
1597 "blocks[locale_{$language_type}][region]" => 'sidebar_first', |
|
1598 ); |
|
1599 $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); |
|
1600 |
|
1601 // Add language. |
|
1602 $edit = array( |
|
1603 'langcode' => 'fr', |
|
1604 ); |
|
1605 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
1606 |
|
1607 // Enable URL language detection and selection. |
|
1608 $edit = array('language[enabled][locale-url]' => '1'); |
|
1609 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
1610 |
|
1611 // Assert that the language switching block is displayed on the frontpage. |
|
1612 $this->drupalGet(''); |
|
1613 $this->assertText(t('Languages'), 'Language switcher block found.'); |
|
1614 |
|
1615 // Assert that only the current language is marked as active. |
|
1616 list($language_switcher) = $this->xpath('//div[@id=:id]/div[@class="content"]', array(':id' => 'block-locale-' . $language_type)); |
|
1617 $links = array( |
|
1618 'active' => array(), |
|
1619 'inactive' => array(), |
|
1620 ); |
|
1621 $anchors = array( |
|
1622 'active' => array(), |
|
1623 'inactive' => array(), |
|
1624 ); |
|
1625 foreach ($language_switcher->ul->li as $link) { |
|
1626 $classes = explode(" ", (string) $link['class']); |
|
1627 list($language) = array_intersect($classes, array('en', 'fr')); |
|
1628 if (in_array('active', $classes)) { |
|
1629 $links['active'][] = $language; |
|
1630 } |
|
1631 else { |
|
1632 $links['inactive'][] = $language; |
|
1633 } |
|
1634 $anchor_classes = explode(" ", (string) $link->a['class']); |
|
1635 if (in_array('active', $anchor_classes)) { |
|
1636 $anchors['active'][] = $language; |
|
1637 } |
|
1638 else { |
|
1639 $anchors['inactive'][] = $language; |
|
1640 } |
|
1641 } |
|
1642 $this->assertIdentical($links, array('active' => array('en'), 'inactive' => array('fr')), 'Only the current language list item is marked as active on the language switcher block.'); |
|
1643 $this->assertIdentical($anchors, array('active' => array('en'), 'inactive' => array('fr')), 'Only the current language anchor is marked as active on the language switcher block.'); |
|
1644 } |
|
1645 } |
|
1646 |
|
1647 /** |
|
1648 * Test browser language detection. |
|
1649 */ |
|
1650 class LocaleBrowserDetectionTest extends DrupalUnitTestCase { |
|
1651 |
|
1652 public static function getInfo() { |
|
1653 return array( |
|
1654 'name' => 'Browser language detection', |
|
1655 'description' => 'Tests for the browser language detection.', |
|
1656 'group' => 'Locale', |
|
1657 ); |
|
1658 } |
|
1659 |
|
1660 /** |
|
1661 * Unit tests for the locale_language_from_browser() function. |
|
1662 */ |
|
1663 function testLanguageFromBrowser() { |
|
1664 // Load the required functions. |
|
1665 require_once DRUPAL_ROOT . '/includes/locale.inc'; |
|
1666 |
|
1667 $languages = array( |
|
1668 // In our test case, 'en' has priority over 'en-US'. |
|
1669 'en' => (object) array( |
|
1670 'language' => 'en', |
|
1671 ), |
|
1672 'en-US' => (object) array( |
|
1673 'language' => 'en-US', |
|
1674 ), |
|
1675 // But 'fr-CA' has priority over 'fr'. |
|
1676 'fr-CA' => (object) array( |
|
1677 'language' => 'fr-CA', |
|
1678 ), |
|
1679 'fr' => (object) array( |
|
1680 'language' => 'fr', |
|
1681 ), |
|
1682 // 'es-MX' is alone. |
|
1683 'es-MX' => (object) array( |
|
1684 'language' => 'es-MX', |
|
1685 ), |
|
1686 // 'pt' is alone. |
|
1687 'pt' => (object) array( |
|
1688 'language' => 'pt', |
|
1689 ), |
|
1690 // Language codes with more then one dash are actually valid. |
|
1691 // eh-oh-laa-laa is the official language code of the Teletubbies. |
|
1692 'eh-oh-laa-laa' => (object) array( |
|
1693 'language' => 'eh-oh-laa-laa', |
|
1694 ), |
|
1695 ); |
|
1696 |
|
1697 $test_cases = array( |
|
1698 // Equal qvalue for each language, choose the site preferred one. |
|
1699 'en,en-US,fr-CA,fr,es-MX' => 'en', |
|
1700 'en-US,en,fr-CA,fr,es-MX' => 'en', |
|
1701 'fr,en' => 'en', |
|
1702 'en,fr' => 'en', |
|
1703 'en-US,fr' => 'en', |
|
1704 'fr,en-US' => 'en', |
|
1705 'fr,fr-CA' => 'fr-CA', |
|
1706 'fr-CA,fr' => 'fr-CA', |
|
1707 'fr' => 'fr-CA', |
|
1708 'fr;q=1' => 'fr-CA', |
|
1709 'fr,es-MX' => 'fr-CA', |
|
1710 'fr,es' => 'fr-CA', |
|
1711 'es,fr' => 'fr-CA', |
|
1712 'es-MX,de' => 'es-MX', |
|
1713 'de,es-MX' => 'es-MX', |
|
1714 |
|
1715 // Different cases and whitespace. |
|
1716 'en' => 'en', |
|
1717 'En' => 'en', |
|
1718 'EN' => 'en', |
|
1719 ' en' => 'en', |
|
1720 'en ' => 'en', |
|
1721 'en, fr' => 'en', |
|
1722 |
|
1723 // A less specific language from the browser matches a more specific one |
|
1724 // from the website, and the other way around for compatibility with |
|
1725 // some versions of Internet Explorer. |
|
1726 'es' => 'es-MX', |
|
1727 'es-MX' => 'es-MX', |
|
1728 'pt' => 'pt', |
|
1729 'pt-PT' => 'pt', |
|
1730 'pt-PT;q=0.5,pt-BR;q=1,en;q=0.7' => 'en', |
|
1731 'pt-PT;q=1,pt-BR;q=0.5,en;q=0.7' => 'en', |
|
1732 'pt-PT;q=0.4,pt-BR;q=0.1,en;q=0.7' => 'en', |
|
1733 'pt-PT;q=0.1,pt-BR;q=0.4,en;q=0.7' => 'en', |
|
1734 |
|
1735 // Language code with several dashes are valid. The less specific language |
|
1736 // from the browser matches the more specific one from the website. |
|
1737 'eh-oh-laa-laa' => 'eh-oh-laa-laa', |
|
1738 'eh-oh-laa' => 'eh-oh-laa-laa', |
|
1739 'eh-oh' => 'eh-oh-laa-laa', |
|
1740 'eh' => 'eh-oh-laa-laa', |
|
1741 |
|
1742 // Different qvalues. |
|
1743 'fr,en;q=0.5' => 'fr-CA', |
|
1744 'fr,en;q=0.5,fr-CA;q=0.25' => 'fr', |
|
1745 |
|
1746 // Silly wildcards are also valid. |
|
1747 '*,fr-CA;q=0.5' => 'en', |
|
1748 '*,en;q=0.25' => 'fr-CA', |
|
1749 'en,en-US;q=0.5,fr;q=0.25' => 'en', |
|
1750 'en-US,en;q=0.5,fr;q=0.25' => 'en-US', |
|
1751 |
|
1752 // Unresolvable cases. |
|
1753 '' => FALSE, |
|
1754 'de,pl' => FALSE, |
|
1755 'iecRswK4eh' => FALSE, |
|
1756 $this->randomName(10) => FALSE, |
|
1757 ); |
|
1758 |
|
1759 foreach ($test_cases as $accept_language => $expected_result) { |
|
1760 $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $accept_language; |
|
1761 $result = locale_language_from_browser($languages); |
|
1762 $this->assertIdentical($result, $expected_result, format_string("Language selection '@accept-language' selects '@result', result = '@actual'", array('@accept-language' => $accept_language, '@result' => $expected_result, '@actual' => isset($result) ? $result : 'none'))); |
|
1763 } |
|
1764 } |
|
1765 } |
|
1766 |
|
1767 /** |
|
1768 * Functional tests for a user's ability to change their default language. |
|
1769 */ |
|
1770 class LocaleUserLanguageFunctionalTest extends DrupalWebTestCase { |
|
1771 public static function getInfo() { |
|
1772 return array( |
|
1773 'name' => 'User language settings', |
|
1774 'description' => "Tests user's ability to change their default language.", |
|
1775 'group' => 'Locale', |
|
1776 ); |
|
1777 } |
|
1778 |
|
1779 function setUp() { |
|
1780 parent::setUp('locale'); |
|
1781 } |
|
1782 |
|
1783 /** |
|
1784 * Test if user can change their default language. |
|
1785 */ |
|
1786 function testUserLanguageConfiguration() { |
|
1787 global $base_url; |
|
1788 |
|
1789 // User to add and remove language. |
|
1790 $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); |
|
1791 // User to change their default language. |
|
1792 $web_user = $this->drupalCreateUser(); |
|
1793 |
|
1794 // Add custom language. |
|
1795 $this->drupalLogin($admin_user); |
|
1796 // Code for the language. |
|
1797 $langcode = 'xx'; |
|
1798 // The English name for the language. |
|
1799 $name = $this->randomName(16); |
|
1800 // The native name for the language. |
|
1801 $native = $this->randomName(16); |
|
1802 // The domain prefix. |
|
1803 $prefix = 'xx'; |
|
1804 $edit = array( |
|
1805 'langcode' => $langcode, |
|
1806 'name' => $name, |
|
1807 'native' => $native, |
|
1808 'prefix' => $prefix, |
|
1809 'direction' => '0', |
|
1810 ); |
|
1811 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
1812 |
|
1813 // Add custom language and disable it. |
|
1814 // Code for the language. |
|
1815 $langcode_disabled = 'xx-yy'; |
|
1816 // The English name for the language. This will be translated. |
|
1817 $name_disabled = $this->randomName(16); |
|
1818 // The native name for the language. |
|
1819 $native_disabled = $this->randomName(16); |
|
1820 // The domain prefix. |
|
1821 $prefix_disabled = $langcode_disabled; |
|
1822 $edit = array( |
|
1823 'langcode' => $langcode_disabled, |
|
1824 'name' => $name_disabled, |
|
1825 'native' => $native_disabled, |
|
1826 'prefix' => $prefix_disabled, |
|
1827 'direction' => '0', |
|
1828 ); |
|
1829 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
1830 // Disable the language. |
|
1831 $edit = array( |
|
1832 'enabled[' . $langcode_disabled . ']' => FALSE, |
|
1833 ); |
|
1834 $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); |
|
1835 $this->drupalLogout(); |
|
1836 |
|
1837 // Login as normal user and edit account settings. |
|
1838 $this->drupalLogin($web_user); |
|
1839 $path = 'user/' . $web_user->uid . '/edit'; |
|
1840 $this->drupalGet($path); |
|
1841 // Ensure language settings fieldset is available. |
|
1842 $this->assertText(t('Language settings'), 'Language settings available.'); |
|
1843 // Ensure custom language is present. |
|
1844 $this->assertText($name, 'Language present on form.'); |
|
1845 // Ensure disabled language isn't present. |
|
1846 $this->assertNoText($name_disabled, 'Disabled language not present on form.'); |
|
1847 // Switch to our custom language. |
|
1848 $edit = array( |
|
1849 'language' => $langcode, |
|
1850 ); |
|
1851 $this->drupalPost($path, $edit, t('Save')); |
|
1852 // Ensure form was submitted successfully. |
|
1853 $this->assertText(t('The changes have been saved.'), 'Changes were saved.'); |
|
1854 // Check if language was changed. |
|
1855 $elements = $this->xpath('//input[@id=:id]', array(':id' => 'edit-language-' . $langcode)); |
|
1856 $this->assertTrue(isset($elements[0]) && !empty($elements[0]['checked']), 'Default language successfully updated.'); |
|
1857 |
|
1858 $this->drupalLogout(); |
|
1859 } |
|
1860 } |
|
1861 |
|
1862 /** |
|
1863 * Functional test for language handling during user creation. |
|
1864 */ |
|
1865 class LocaleUserCreationTest extends DrupalWebTestCase { |
|
1866 |
|
1867 public static function getInfo() { |
|
1868 return array( |
|
1869 'name' => 'User creation', |
|
1870 'description' => 'Tests whether proper language is stored for new users and access to language selector.', |
|
1871 'group' => 'Locale', |
|
1872 ); |
|
1873 } |
|
1874 |
|
1875 function setUp() { |
|
1876 parent::setUp('locale'); |
|
1877 variable_set('user_register', USER_REGISTER_VISITORS); |
|
1878 } |
|
1879 |
|
1880 /** |
|
1881 * Functional test for language handling during user creation. |
|
1882 */ |
|
1883 function testLocalUserCreation() { |
|
1884 // User to add and remove language and create new users. |
|
1885 $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'administer users')); |
|
1886 $this->drupalLogin($admin_user); |
|
1887 |
|
1888 // Add predefined language. |
|
1889 $langcode = 'fr'; |
|
1890 $edit = array( |
|
1891 'langcode' => 'fr', |
|
1892 ); |
|
1893 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
1894 $this->assertText($langcode, 'Language added successfully.'); |
|
1895 $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.'); |
|
1896 |
|
1897 // Set language negotiation. |
|
1898 $edit = array( |
|
1899 'language[enabled][locale-url]' => TRUE, |
|
1900 ); |
|
1901 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
1902 $this->assertText(t('Language negotiation configuration saved.'), 'Set language negotiation.'); |
|
1903 |
|
1904 // Check if the language selector is available on admin/people/create and |
|
1905 // set to the currently active language. |
|
1906 $this->drupalGet($langcode . '/admin/people/create'); |
|
1907 $this->assertFieldChecked("edit-language-$langcode", 'Global language set in the language selector.'); |
|
1908 |
|
1909 // Create a user with the admin/people/create form and check if the correct |
|
1910 // language is set. |
|
1911 $username = $this->randomName(10); |
|
1912 $edit = array( |
|
1913 'name' => $username, |
|
1914 'mail' => $this->randomName(4) . '@example.com', |
|
1915 'pass[pass1]' => $username, |
|
1916 'pass[pass2]' => $username, |
|
1917 ); |
|
1918 |
|
1919 $this->drupalPost($langcode . '/admin/people/create', $edit, t('Create new account')); |
|
1920 |
|
1921 $user = user_load_by_name($username); |
|
1922 $this->assertEqual($user->language, $langcode, 'New user has correct language set.'); |
|
1923 |
|
1924 // Register a new user and check if the language selector is hidden. |
|
1925 $this->drupalLogout(); |
|
1926 |
|
1927 $this->drupalGet($langcode . '/user/register'); |
|
1928 $this->assertNoFieldByName('language[fr]', 'Language selector is not accessible.'); |
|
1929 |
|
1930 $username = $this->randomName(10); |
|
1931 $edit = array( |
|
1932 'name' => $username, |
|
1933 'mail' => $this->randomName(4) . '@example.com', |
|
1934 ); |
|
1935 |
|
1936 $this->drupalPost($langcode . '/user/register', $edit, t('Create new account')); |
|
1937 |
|
1938 $user = user_load_by_name($username); |
|
1939 $this->assertEqual($user->language, $langcode, 'New user has correct language set.'); |
|
1940 |
|
1941 // Test if the admin can use the language selector and if the |
|
1942 // correct language is was saved. |
|
1943 $user_edit = $langcode . '/user/' . $user->uid . '/edit'; |
|
1944 |
|
1945 $this->drupalLogin($admin_user); |
|
1946 $this->drupalGet($user_edit); |
|
1947 $this->assertFieldChecked("edit-language-$langcode", 'Language selector is accessible and correct language is selected.'); |
|
1948 |
|
1949 // Set pass_raw so we can login the new user. |
|
1950 $user->pass_raw = $this->randomName(10); |
|
1951 $edit = array( |
|
1952 'pass[pass1]' => $user->pass_raw, |
|
1953 'pass[pass2]' => $user->pass_raw, |
|
1954 ); |
|
1955 |
|
1956 $this->drupalPost($user_edit, $edit, t('Save')); |
|
1957 |
|
1958 $this->drupalLogin($user); |
|
1959 $this->drupalGet($user_edit); |
|
1960 $this->assertFieldChecked("edit-language-$langcode", 'Language selector is accessible and correct language is selected.'); |
|
1961 } |
|
1962 } |
|
1963 |
|
1964 /** |
|
1965 * Functional tests for configuring a different path alias per language. |
|
1966 */ |
|
1967 class LocalePathFunctionalTest extends DrupalWebTestCase { |
|
1968 public static function getInfo() { |
|
1969 return array( |
|
1970 'name' => 'Path language settings', |
|
1971 'description' => 'Checks you can configure a language for individual URL aliases.', |
|
1972 'group' => 'Locale', |
|
1973 ); |
|
1974 } |
|
1975 |
|
1976 function setUp() { |
|
1977 parent::setUp('locale', 'path'); |
|
1978 } |
|
1979 |
|
1980 /** |
|
1981 * Test if a language can be associated with a path alias. |
|
1982 */ |
|
1983 function testPathLanguageConfiguration() { |
|
1984 global $base_url; |
|
1985 |
|
1986 // User to add and remove language. |
|
1987 $admin_user = $this->drupalCreateUser(array('administer languages', 'create page content', 'administer url aliases', 'create url aliases', 'access administration pages')); |
|
1988 |
|
1989 // Add custom language. |
|
1990 $this->drupalLogin($admin_user); |
|
1991 // Code for the language. |
|
1992 $langcode = 'xx'; |
|
1993 // The English name for the language. |
|
1994 $name = $this->randomName(16); |
|
1995 // The native name for the language. |
|
1996 $native = $this->randomName(16); |
|
1997 // The domain prefix. |
|
1998 $prefix = $langcode; |
|
1999 $edit = array( |
|
2000 'langcode' => $langcode, |
|
2001 'name' => $name, |
|
2002 'native' => $native, |
|
2003 'prefix' => $prefix, |
|
2004 'direction' => '0', |
|
2005 ); |
|
2006 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
2007 |
|
2008 // Check that the "xx" front page is not available when path prefixes are |
|
2009 // not enabled yet. |
|
2010 $this->drupalPost('admin/config/regional/language/configure', array(), t('Save settings')); |
|
2011 $this->drupalGet($prefix); |
|
2012 $this->assertResponse(404, 'The "xx" front page is not available yet.'); |
|
2013 |
|
2014 // Enable URL language detection and selection. |
|
2015 $edit = array('language[enabled][locale-url]' => 1); |
|
2016 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
2017 |
|
2018 // Create a node. |
|
2019 $node = $this->drupalCreateNode(array('type' => 'page')); |
|
2020 |
|
2021 // Create a path alias in default language (English). |
|
2022 $path = 'admin/config/search/path/add'; |
|
2023 $english_path = $this->randomName(8); |
|
2024 $edit = array( |
|
2025 'source' => 'node/' . $node->nid, |
|
2026 'alias' => $english_path, |
|
2027 'language' => 'en', |
|
2028 ); |
|
2029 $this->drupalPost($path, $edit, t('Save')); |
|
2030 |
|
2031 // Create a path alias in new custom language. |
|
2032 $custom_language_path = $this->randomName(8); |
|
2033 $edit = array( |
|
2034 'source' => 'node/' . $node->nid, |
|
2035 'alias' => $custom_language_path, |
|
2036 'language' => $langcode, |
|
2037 ); |
|
2038 $this->drupalPost($path, $edit, t('Save')); |
|
2039 |
|
2040 // Confirm English language path alias works. |
|
2041 $this->drupalGet($english_path); |
|
2042 $this->assertText($node->title, 'English alias works.'); |
|
2043 |
|
2044 // Confirm custom language path alias works. |
|
2045 $this->drupalGet($prefix . '/' . $custom_language_path); |
|
2046 $this->assertText($node->title, 'Custom language alias works.'); |
|
2047 |
|
2048 // Create a custom path. |
|
2049 $custom_path = $this->randomName(8); |
|
2050 |
|
2051 // Check priority of language for alias by source path. |
|
2052 $edit = array( |
|
2053 'source' => 'node/' . $node->nid, |
|
2054 'alias' => $custom_path, |
|
2055 'language' => LANGUAGE_NONE, |
|
2056 ); |
|
2057 path_save($edit); |
|
2058 $lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, 'en'); |
|
2059 $this->assertEqual($english_path, $lookup_path, 'English language alias has priority.'); |
|
2060 // Same check for language 'xx'. |
|
2061 $lookup_path = drupal_lookup_path('alias', 'node/' . $node->nid, $prefix); |
|
2062 $this->assertEqual($custom_language_path, $lookup_path, 'Custom language alias has priority.'); |
|
2063 path_delete($edit); |
|
2064 |
|
2065 // Create language nodes to check priority of aliases. |
|
2066 $first_node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1)); |
|
2067 $second_node = $this->drupalCreateNode(array('type' => 'article', 'promote' => 1)); |
|
2068 |
|
2069 // Assign a custom path alias to the first node with the English language. |
|
2070 $edit = array( |
|
2071 'source' => 'node/' . $first_node->nid, |
|
2072 'alias' => $custom_path, |
|
2073 'language' => 'en', |
|
2074 ); |
|
2075 path_save($edit); |
|
2076 |
|
2077 // Assign a custom path alias to second node with LANGUAGE_NONE. |
|
2078 $edit = array( |
|
2079 'source' => 'node/' . $second_node->nid, |
|
2080 'alias' => $custom_path, |
|
2081 'language' => LANGUAGE_NONE, |
|
2082 ); |
|
2083 path_save($edit); |
|
2084 |
|
2085 // Test that both node titles link to our path alias. |
|
2086 $this->drupalGet('<front>'); |
|
2087 $custom_path_url = base_path() . (variable_get('clean_url', 0) ? $custom_path : '?q=' . $custom_path); |
|
2088 $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $first_node->title)); |
|
2089 $this->assertTrue(!empty($elements), 'First node links to the path alias.'); |
|
2090 $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $second_node->title)); |
|
2091 $this->assertTrue(!empty($elements), 'Second node links to the path alias.'); |
|
2092 |
|
2093 // Confirm that the custom path leads to the first node. |
|
2094 $this->drupalGet($custom_path); |
|
2095 $this->assertText($first_node->title, 'Custom alias returns first node.'); |
|
2096 |
|
2097 // Confirm that the custom path with prefix leads to the second node. |
|
2098 $this->drupalGet($prefix . '/' . $custom_path); |
|
2099 $this->assertText($second_node->title, 'Custom alias with prefix returns second node.'); |
|
2100 } |
|
2101 } |
|
2102 |
|
2103 /** |
|
2104 * Functional tests for multilingual support on nodes. |
|
2105 */ |
|
2106 class LocaleContentFunctionalTest extends DrupalWebTestCase { |
|
2107 public static function getInfo() { |
|
2108 return array( |
|
2109 'name' => 'Content language settings', |
|
2110 'description' => 'Checks you can enable multilingual support on content types and configure a language for a node.', |
|
2111 'group' => 'Locale', |
|
2112 ); |
|
2113 } |
|
2114 |
|
2115 function setUp() { |
|
2116 parent::setUp('locale'); |
|
2117 } |
|
2118 |
|
2119 /** |
|
2120 * Verifies that machine name fields are always LTR. |
|
2121 */ |
|
2122 function testMachineNameLTR() { |
|
2123 // User to add and remove language. |
|
2124 $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages')); |
|
2125 |
|
2126 // Log in as admin. |
|
2127 $this->drupalLogin($admin_user); |
|
2128 |
|
2129 // Verify that the machine name field is LTR for a new content type. |
|
2130 $this->drupalGet('admin/structure/types/add'); |
|
2131 $this->assertFieldByXpath('//input[@name="type" and @dir="ltr"]', NULL, 'The machine name field is LTR when no additional language is configured.'); |
|
2132 |
|
2133 // Install the Arabic language (which is RTL) and configure as the default. |
|
2134 $edit = array(); |
|
2135 $edit['langcode'] = 'ar'; |
|
2136 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
2137 |
|
2138 $edit = array(); |
|
2139 $edit['site_default'] = 'ar'; |
|
2140 $this->drupalPost(NULL, $edit, t('Save configuration')); |
|
2141 |
|
2142 // Verify that the machine name field is still LTR for a new content type. |
|
2143 $this->drupalGet('admin/structure/types/add'); |
|
2144 $this->assertFieldByXpath('//input[@name="type" and @dir="ltr"]', NULL, 'The machine name field is LTR when the default language is RTL.'); |
|
2145 } |
|
2146 |
|
2147 /** |
|
2148 * Test if a content type can be set to multilingual and language setting is |
|
2149 * present on node add and edit forms. |
|
2150 */ |
|
2151 function testContentTypeLanguageConfiguration() { |
|
2152 global $base_url; |
|
2153 |
|
2154 // User to add and remove language. |
|
2155 $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages')); |
|
2156 // User to create a node. |
|
2157 $web_user = $this->drupalCreateUser(array('create article content', 'create page content', 'edit any page content')); |
|
2158 |
|
2159 // Add custom language. |
|
2160 $this->drupalLogin($admin_user); |
|
2161 // Code for the language. |
|
2162 $langcode = 'xx'; |
|
2163 // The English name for the language. |
|
2164 $name = $this->randomName(16); |
|
2165 // The native name for the language. |
|
2166 $native = $this->randomName(16); |
|
2167 // The domain prefix. |
|
2168 $prefix = $langcode; |
|
2169 $edit = array( |
|
2170 'langcode' => $langcode, |
|
2171 'name' => $name, |
|
2172 'native' => $native, |
|
2173 'prefix' => $prefix, |
|
2174 'direction' => '0', |
|
2175 ); |
|
2176 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
2177 |
|
2178 // Add disabled custom language. |
|
2179 // Code for the language. |
|
2180 $langcode_disabled = 'xx-yy'; |
|
2181 // The English name for the language. |
|
2182 $name_disabled = $this->randomName(16); |
|
2183 // The native name for the language. |
|
2184 $native_disabled = $this->randomName(16); |
|
2185 // The domain prefix. |
|
2186 $prefix_disabled = $langcode_disabled; |
|
2187 $edit = array( |
|
2188 'langcode' => $langcode_disabled, |
|
2189 'name' => $name_disabled, |
|
2190 'native' => $native_disabled, |
|
2191 'prefix' => $prefix_disabled, |
|
2192 'direction' => '0', |
|
2193 ); |
|
2194 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); |
|
2195 // Disable second custom language. |
|
2196 $path = 'admin/config/regional/language'; |
|
2197 $edit = array( |
|
2198 'enabled[' . $langcode_disabled . ']' => FALSE, |
|
2199 ); |
|
2200 $this->drupalPost($path, $edit, t('Save configuration')); |
|
2201 |
|
2202 // Set "Basic page" content type to use multilingual support. |
|
2203 $this->drupalGet('admin/structure/types/manage/page'); |
|
2204 $this->assertText(t('Multilingual support'), 'Multilingual support fieldset present on content type configuration form.'); |
|
2205 $edit = array( |
|
2206 'language_content_type' => 1, |
|
2207 ); |
|
2208 $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type')); |
|
2209 $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), 'Basic page content type has been updated.'); |
|
2210 $this->drupalLogout(); |
|
2211 |
|
2212 // Verify language selection is not present on add article form. |
|
2213 $this->drupalLogin($web_user); |
|
2214 $this->drupalGet('node/add/article'); |
|
2215 // Verify language select list is not present. |
|
2216 $this->assertNoFieldByName('language', NULL, 'Language select not present on add article form.'); |
|
2217 |
|
2218 // Verify language selection appears on add "Basic page" form. |
|
2219 $this->drupalGet('node/add/page'); |
|
2220 // Verify language select list is present. |
|
2221 $this->assertFieldByName('language', NULL, 'Language select present on add Basic page form.'); |
|
2222 // Ensure enabled language appears. |
|
2223 $this->assertText($name, 'Enabled language present.'); |
|
2224 // Ensure disabled language doesn't appear. |
|
2225 $this->assertNoText($name_disabled, 'Disabled language not present.'); |
|
2226 |
|
2227 // Create "Basic page" content. |
|
2228 $node_title = $this->randomName(); |
|
2229 $node_body = $this->randomName(); |
|
2230 $edit = array( |
|
2231 'type' => 'page', |
|
2232 'title' => $node_title, |
|
2233 'body' => array($langcode => array(array('value' => $node_body))), |
|
2234 'language' => $langcode, |
|
2235 ); |
|
2236 $node = $this->drupalCreateNode($edit); |
|
2237 // Edit the content and ensure correct language is selected. |
|
2238 $path = 'node/' . $node->nid . '/edit'; |
|
2239 $this->drupalGet($path); |
|
2240 $this->assertRaw('<option value="' . $langcode . '" selected="selected">' . $name . '</option>', 'Correct language selected.'); |
|
2241 // Ensure we can change the node language. |
|
2242 $edit = array( |
|
2243 'language' => 'en', |
|
2244 ); |
|
2245 $this->drupalPost($path, $edit, t('Save')); |
|
2246 $this->assertRaw(t('%title has been updated.', array('%title' => $node_title)), 'Basic page content updated.'); |
|
2247 |
|
2248 $this->drupalLogout(); |
|
2249 } |
|
2250 |
|
2251 /** |
|
2252 * Verifies that nodes may be created with different languages. |
|
2253 */ |
|
2254 function testNodeCreationWithLanguage() { |
|
2255 // Create an admin user and log them in. |
|
2256 $perms = array( |
|
2257 // Standard node permissions. |
|
2258 'create page content', |
|
2259 'administer content types', |
|
2260 'administer nodes', |
|
2261 'bypass node access', |
|
2262 // Locale. |
|
2263 'administer languages', |
|
2264 ); |
|
2265 $web_user = $this->drupalCreateUser($perms); |
|
2266 $this->drupalLogin($web_user); |
|
2267 |
|
2268 // Create some test nodes using different langcodes. |
|
2269 foreach (array(LANGUAGE_NONE, 'en', 'fr') as $langcode) { |
|
2270 $node_args = array( |
|
2271 'type' => 'page', |
|
2272 'promote' => 1, |
|
2273 'language' => $langcode, |
|
2274 ); |
|
2275 $node = $this->drupalCreateNode($node_args); |
|
2276 $node_reloaded = node_load($node->nid, NULL, TRUE); |
|
2277 $this->assertEqual($node_reloaded->language, $langcode, format_string('The language code of the node was successfully set to @langcode.', array('@langcode' => $langcode))); |
|
2278 } |
|
2279 } |
|
2280 |
|
2281 } |
|
2282 |
|
2283 /** |
|
2284 * Test UI language negotiation |
|
2285 * 1. URL (PATH) > DEFAULT |
|
2286 * UI Language base on URL prefix, browser language preference has no |
|
2287 * influence: |
|
2288 * admin/config |
|
2289 * UI in site default language |
|
2290 * zh-hans/admin/config |
|
2291 * UI in Chinese |
|
2292 * blah-blah/admin/config |
|
2293 * 404 |
|
2294 * 2. URL (PATH) > BROWSER > DEFAULT |
|
2295 * admin/config |
|
2296 * UI in user's browser language preference if the site has that |
|
2297 * language enabled, if not, the default language |
|
2298 * zh-hans/admin/config |
|
2299 * UI in Chinese |
|
2300 * blah-blah/admin/config |
|
2301 * 404 |
|
2302 * 3. URL (DOMAIN) > DEFAULT |
|
2303 * http://example.com/admin/config |
|
2304 * UI language in site default |
|
2305 * http://example.cn/admin/config |
|
2306 * UI language in Chinese |
|
2307 */ |
|
2308 class LocaleUILanguageNegotiationTest extends DrupalWebTestCase { |
|
2309 public static function getInfo() { |
|
2310 return array( |
|
2311 'name' => 'UI language negotiation', |
|
2312 'description' => 'Test UI language switching by URL path prefix and domain.', |
|
2313 'group' => 'Locale', |
|
2314 ); |
|
2315 } |
|
2316 |
|
2317 function setUp() { |
|
2318 parent::setUp('locale', 'locale_test'); |
|
2319 require_once DRUPAL_ROOT . '/includes/language.inc'; |
|
2320 drupal_load('module', 'locale'); |
|
2321 $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages', 'administer blocks')); |
|
2322 $this->drupalLogin($admin_user); |
|
2323 } |
|
2324 |
|
2325 /** |
|
2326 * Tests for language switching by URL path. |
|
2327 */ |
|
2328 function testUILanguageNegotiation() { |
|
2329 // A few languages to switch to. |
|
2330 // This one is unknown, should get the default lang version. |
|
2331 $language_unknown = 'blah-blah'; |
|
2332 // For testing browser lang preference. |
|
2333 $language_browser_fallback = 'vi'; |
|
2334 // For testing path prefix. |
|
2335 $language = 'zh-hans'; |
|
2336 // For setting browser language preference to 'vi'. |
|
2337 $http_header_browser_fallback = array("Accept-Language: $language_browser_fallback;q=1"); |
|
2338 // For setting browser language preference to some unknown. |
|
2339 $http_header_blah = array("Accept-Language: blah;q=1"); |
|
2340 |
|
2341 // This domain should switch the UI to Chinese. |
|
2342 $language_domain = 'example.cn'; |
|
2343 |
|
2344 // Setup the site languages by installing two languages. |
|
2345 require_once DRUPAL_ROOT . '/includes/locale.inc'; |
|
2346 locale_add_language($language_browser_fallback); |
|
2347 locale_add_language($language); |
|
2348 |
|
2349 // We will look for this string in the admin/config screen to see if the |
|
2350 // corresponding translated string is shown. |
|
2351 $default_string = 'Configure languages for content and the user interface'; |
|
2352 |
|
2353 // Set the default language in order for the translated string to be registered |
|
2354 // into database when seen by t(). Without doing this, our target string |
|
2355 // is for some reason not found when doing translate search. This might |
|
2356 // be some bug. |
|
2357 drupal_static_reset('language_list'); |
|
2358 $languages = language_list('enabled'); |
|
2359 variable_set('language_default', $languages[1]['vi']); |
|
2360 // First visit this page to make sure our target string is searchable. |
|
2361 $this->drupalGet('admin/config'); |
|
2362 // Now the t()'ed string is in db so switch the language back to default. |
|
2363 variable_del('language_default'); |
|
2364 |
|
2365 // Translate the string. |
|
2366 $language_browser_fallback_string = "In $language_browser_fallback In $language_browser_fallback In $language_browser_fallback"; |
|
2367 $language_string = "In $language In $language In $language"; |
|
2368 // Do a translate search of our target string. |
|
2369 $edit = array( 'string' => $default_string); |
|
2370 $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Filter')); |
|
2371 // Should find the string and now click edit to post translated string. |
|
2372 $this->clickLink('edit'); |
|
2373 $edit = array( |
|
2374 "translations[$language_browser_fallback]" => $language_browser_fallback_string, |
|
2375 "translations[$language]" => $language_string, |
|
2376 ); |
|
2377 $this->drupalPost(NULL, $edit, t('Save translations')); |
|
2378 |
|
2379 // Configure URL language rewrite. |
|
2380 variable_set('locale_language_negotiation_url_type', LANGUAGE_TYPE_INTERFACE); |
|
2381 |
|
2382 $tests = array( |
|
2383 // Default, browser preference should have no influence. |
|
2384 array( |
|
2385 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), |
|
2386 'path' => 'admin/config', |
|
2387 'expect' => $default_string, |
|
2388 'expected_provider' => LANGUAGE_NEGOTIATION_DEFAULT, |
|
2389 'http_header' => $http_header_browser_fallback, |
|
2390 'message' => 'URL (PATH) > DEFAULT: no language prefix, UI language is default and the browser language preference setting is not used.', |
|
2391 ), |
|
2392 // Language prefix. |
|
2393 array( |
|
2394 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), |
|
2395 'path' => "$language/admin/config", |
|
2396 'expect' => $language_string, |
|
2397 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_URL, |
|
2398 'http_header' => $http_header_browser_fallback, |
|
2399 'message' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix', |
|
2400 ), |
|
2401 // Default, go by browser preference. |
|
2402 array( |
|
2403 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER), |
|
2404 'path' => 'admin/config', |
|
2405 'expect' => $language_browser_fallback_string, |
|
2406 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_BROWSER, |
|
2407 'http_header' => $http_header_browser_fallback, |
|
2408 'message' => 'URL (PATH) > BROWSER: no language prefix, UI language is determined by browser language preference', |
|
2409 ), |
|
2410 // Prefix, switch to the language. |
|
2411 array( |
|
2412 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER), |
|
2413 'path' => "$language/admin/config", |
|
2414 'expect' => $language_string, |
|
2415 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_URL, |
|
2416 'http_header' => $http_header_browser_fallback, |
|
2417 'message' => 'URL (PATH) > BROWSER: with langage prefix, UI language is based on path prefix', |
|
2418 ), |
|
2419 // Default, browser language preference is not one of site's lang. |
|
2420 array( |
|
2421 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LOCALE_LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_DEFAULT), |
|
2422 'path' => 'admin/config', |
|
2423 'expect' => $default_string, |
|
2424 'expected_provider' => LANGUAGE_NEGOTIATION_DEFAULT, |
|
2425 'http_header' => $http_header_blah, |
|
2426 'message' => 'URL (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language', |
|
2427 ), |
|
2428 ); |
|
2429 |
|
2430 foreach ($tests as $test) { |
|
2431 $this->runTest($test); |
|
2432 } |
|
2433 |
|
2434 // Unknown language prefix should return 404. |
|
2435 variable_set('language_negotiation_' . LANGUAGE_TYPE_INTERFACE, locale_language_negotiation_info()); |
|
2436 $this->drupalGet("$language_unknown/admin/config", array(), $http_header_browser_fallback); |
|
2437 $this->assertResponse(404, "Unknown language path prefix should return 404"); |
|
2438 |
|
2439 // Setup for domain negotiation, first configure the language to have domain |
|
2440 // URL. We use HTTPS and a port to make sure that only the domain name is used. |
|
2441 $edit = array('prefix' => '', 'domain' => "https://$language_domain:99"); |
|
2442 $this->drupalPost("admin/config/regional/language/edit/$language", $edit, t('Save language')); |
|
2443 // Set the site to use domain language negotiation. |
|
2444 |
|
2445 $tests = array( |
|
2446 // Default domain, browser preference should have no influence. |
|
2447 array( |
|
2448 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), |
|
2449 'locale_language_negotiation_url_part' => LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN, |
|
2450 'path' => 'admin/config', |
|
2451 'expect' => $default_string, |
|
2452 'expected_provider' => LANGUAGE_NEGOTIATION_DEFAULT, |
|
2453 'http_header' => $http_header_browser_fallback, |
|
2454 'message' => 'URL (DOMAIN) > DEFAULT: default domain should get default language', |
|
2455 ), |
|
2456 // Language domain specific URL, we set the $_SERVER['HTTP_HOST'] in |
|
2457 // locale_test.module hook_boot() to simulate this. |
|
2458 array( |
|
2459 'language_negotiation' => array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT), |
|
2460 'locale_language_negotiation_url_part' => LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN, |
|
2461 'locale_test_domain' => $language_domain . ':88', |
|
2462 'path' => 'admin/config', |
|
2463 'expect' => $language_string, |
|
2464 'expected_provider' => LOCALE_LANGUAGE_NEGOTIATION_URL, |
|
2465 'http_header' => $http_header_browser_fallback, |
|
2466 'message' => 'URL (DOMAIN) > DEFAULT: domain example.cn should switch to Chinese', |
|
2467 ), |
|
2468 ); |
|
2469 |
|
2470 foreach ($tests as $test) { |
|
2471 $this->runTest($test); |
|
2472 } |
|
2473 } |
|
2474 |
|
2475 private function runTest($test) { |
|
2476 if (!empty($test['language_negotiation'])) { |
|
2477 $negotiation = array_flip($test['language_negotiation']); |
|
2478 language_negotiation_set(LANGUAGE_TYPE_INTERFACE, $negotiation); |
|
2479 } |
|
2480 if (!empty($test['locale_language_negotiation_url_part'])) { |
|
2481 variable_set('locale_language_negotiation_url_part', $test['locale_language_negotiation_url_part']); |
|
2482 } |
|
2483 if (!empty($test['locale_test_domain'])) { |
|
2484 variable_set('locale_test_domain', $test['locale_test_domain']); |
|
2485 } |
|
2486 $this->drupalGet($test['path'], array(), $test['http_header']); |
|
2487 $this->assertText($test['expect'], $test['message']); |
|
2488 $this->assertText(t('Language negotiation provider: @name', array('@name' => $test['expected_provider']))); |
|
2489 } |
|
2490 |
|
2491 /** |
|
2492 * Test URL language detection when the requested URL has no language. |
|
2493 */ |
|
2494 function testUrlLanguageFallback() { |
|
2495 // Add the Italian language. |
|
2496 $language_browser_fallback = 'it'; |
|
2497 locale_add_language($language_browser_fallback); |
|
2498 $languages = language_list(); |
|
2499 |
|
2500 // Enable the path prefix for the default language: this way any unprefixed |
|
2501 // URL must have a valid fallback value. |
|
2502 $edit = array('prefix' => 'en'); |
|
2503 $this->drupalPost('admin/config/regional/language/edit/en', $edit, t('Save language')); |
|
2504 |
|
2505 // Enable browser and URL language detection. |
|
2506 $edit = array( |
|
2507 'language[enabled][locale-browser]' => TRUE, |
|
2508 'language[enabled][locale-url]' => TRUE, |
|
2509 'language[weight][locale-browser]' => -8, |
|
2510 'language[weight][locale-url]' => -10, |
|
2511 ); |
|
2512 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
2513 $this->drupalGet('admin/config/regional/language/configure'); |
|
2514 |
|
2515 // Enable the language switcher block. |
|
2516 $edit = array('blocks[locale_language][region]' => 'sidebar_first'); |
|
2517 $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); |
|
2518 |
|
2519 // Access the front page without specifying any valid URL language prefix |
|
2520 // and having as browser language preference a non-default language. |
|
2521 $http_header = array("Accept-Language: $language_browser_fallback;q=1"); |
|
2522 $this->drupalGet('', array(), $http_header); |
|
2523 |
|
2524 // Check that the language switcher active link matches the given browser |
|
2525 // language. |
|
2526 $args = array(':url' => base_path() . (!empty($GLOBALS['conf']['clean_url']) ? $language_browser_fallback : "?q=$language_browser_fallback")); |
|
2527 $fields = $this->xpath('//div[@id="block-locale-language"]//a[@class="language-link active" and @href=:url]', $args); |
|
2528 $this->assertTrue($fields[0] == $languages[$language_browser_fallback]->native, 'The browser language is the URL active language'); |
|
2529 |
|
2530 // Check that URLs are rewritten using the given browser language. |
|
2531 $fields = $this->xpath('//div[@id="site-name"]//a[@rel="home" and @href=:url]//span', $args); |
|
2532 $this->assertTrue($fields[0] == 'Drupal', 'URLs are rewritten using the browser language.'); |
|
2533 } |
|
2534 |
|
2535 /** |
|
2536 * Tests url() when separate domains are used for multiple languages. |
|
2537 */ |
|
2538 function testLanguageDomain() { |
|
2539 // Add the Italian language, without protocol. |
|
2540 $langcode = 'it'; |
|
2541 locale_add_language($langcode, 'Italian', 'Italian', LANGUAGE_LTR, 'it.example.com', '', TRUE, FALSE); |
|
2542 |
|
2543 // Add the French language, with protocol. |
|
2544 $langcode = 'fr'; |
|
2545 locale_add_language($langcode, 'French', 'French', LANGUAGE_LTR, 'http://fr.example.com', '', TRUE, FALSE); |
|
2546 |
|
2547 // Enable language URL detection. |
|
2548 $negotiation = array_flip(array(LOCALE_LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_DEFAULT)); |
|
2549 language_negotiation_set(LANGUAGE_TYPE_INTERFACE, $negotiation); |
|
2550 |
|
2551 variable_set('locale_language_negotiation_url_part', 1); |
|
2552 |
|
2553 global $is_https; |
|
2554 $languages = language_list(); |
|
2555 |
|
2556 foreach (array('it', 'fr') as $langcode) { |
|
2557 // Build the link we're going to test based on the clean URL setting. |
|
2558 $link = (!empty($GLOBALS['conf']['clean_url'])) ? $langcode . '.example.com/admin' : $langcode . '.example.com/?q=admin'; |
|
2559 |
|
2560 // Test URL in another language. |
|
2561 // Base path gives problems on the testbot, so $correct_link is hard-coded. |
|
2562 // @see UrlAlterFunctionalTest::assertUrlOutboundAlter (path.test). |
|
2563 $url = url('admin', array('language' => $languages[$langcode])); |
|
2564 $url_scheme = ($is_https) ? 'https://' : 'http://'; |
|
2565 $correct_link = $url_scheme . $link; |
|
2566 $this->assertTrue($url == $correct_link, format_string('The url() function returns the right url (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link))); |
|
2567 |
|
2568 // Test HTTPS via options. |
|
2569 variable_set('https', TRUE); |
|
2570 $url = url('admin', array('https' => TRUE, 'language' => $languages[$langcode])); |
|
2571 $correct_link = 'https://' . $link; |
|
2572 $this->assertTrue($url == $correct_link, format_string('The url() function returns the right https url (via options) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link))); |
|
2573 variable_set('https', FALSE); |
|
2574 |
|
2575 // Test HTTPS via current URL scheme. |
|
2576 $temp_https = $is_https; |
|
2577 $is_https = TRUE; |
|
2578 $url = url('admin', array('language' => $languages[$langcode])); |
|
2579 $correct_link = 'https://' . $link; |
|
2580 $this->assertTrue($url == $correct_link, format_string('The url() function returns the right url (via current url scheme) (@url) in accordance with the chosen language', array('@url' => $url . " == " . $correct_link))); |
|
2581 $is_https = $temp_https; |
|
2582 } |
|
2583 } |
|
2584 } |
|
2585 |
|
2586 /** |
|
2587 * Test that URL rewriting works as expected. |
|
2588 */ |
|
2589 class LocaleUrlRewritingTest extends DrupalWebTestCase { |
|
2590 public static function getInfo() { |
|
2591 return array( |
|
2592 'name' => 'URL rewriting', |
|
2593 'description' => 'Test that URL rewriting works as expected.', |
|
2594 'group' => 'Locale', |
|
2595 ); |
|
2596 } |
|
2597 |
|
2598 function setUp() { |
|
2599 parent::setUp('locale'); |
|
2600 |
|
2601 // Create and login user. |
|
2602 $this->web_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); |
|
2603 $this->drupalLogin($this->web_user); |
|
2604 |
|
2605 // Install French language. |
|
2606 $edit = array(); |
|
2607 $edit['langcode'] = 'fr'; |
|
2608 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
2609 |
|
2610 // Install Italian language. |
|
2611 $edit = array(); |
|
2612 $edit['langcode'] = 'it'; |
|
2613 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
2614 |
|
2615 // Disable Italian language. |
|
2616 $edit = array('enabled[it]' => FALSE); |
|
2617 $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); |
|
2618 |
|
2619 // Enable URL language detection and selection. |
|
2620 $edit = array('language[enabled][locale-url]' => 1); |
|
2621 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
2622 |
|
2623 // Reset static caching. |
|
2624 drupal_static_reset('language_list'); |
|
2625 drupal_static_reset('locale_url_outbound_alter'); |
|
2626 drupal_static_reset('locale_language_url_rewrite_url'); |
|
2627 } |
|
2628 |
|
2629 /** |
|
2630 * Check that disabled or non-installed languages are not considered. |
|
2631 */ |
|
2632 function testUrlRewritingEdgeCases() { |
|
2633 // Check URL rewriting with a disabled language. |
|
2634 $languages = language_list(); |
|
2635 $this->checkUrl($languages['it'], 'Path language is ignored if language is disabled.', 'URL language negotiation does not work with disabled languages'); |
|
2636 |
|
2637 // Check URL rewriting with a non-installed language. |
|
2638 $non_existing = language_default(); |
|
2639 $non_existing->language = $this->randomName(); |
|
2640 $non_existing->prefix = $this->randomName(); |
|
2641 $this->checkUrl($non_existing, 'Path language is ignored if language is not installed.', 'URL language negotiation does not work with non-installed languages'); |
|
2642 } |
|
2643 |
|
2644 /** |
|
2645 * Check URL rewriting for the given language. |
|
2646 * |
|
2647 * The test is performed with a fixed URL (the default front page) to simply |
|
2648 * check that language prefixes are not added to it and that the prefixed URL |
|
2649 * is actually not working. |
|
2650 * |
|
2651 * @param string $language |
|
2652 * The language prefix, e.g. 'es'. |
|
2653 * @param string $message1 |
|
2654 * Message to display in assertion that language prefixes are not added. |
|
2655 * @param string $message2 |
|
2656 * The message to display confirming prefixed URL is not working. |
|
2657 */ |
|
2658 private function checkUrl($language, $message1, $message2) { |
|
2659 $options = array('language' => $language); |
|
2660 $base_path = trim(base_path(), '/'); |
|
2661 $rewritten_path = trim(str_replace(array('?q=', $base_path), '', url('node', $options)), '/'); |
|
2662 $segments = explode('/', $rewritten_path, 2); |
|
2663 $prefix = $segments[0]; |
|
2664 $path = isset($segments[1]) ? $segments[1] : $prefix; |
|
2665 // If the rewritten URL has not a language prefix we pick the right one from |
|
2666 // the language object so we can always check the prefixed URL. |
|
2667 if ($this->assertNotEqual($language->prefix, $prefix, $message1)) { |
|
2668 $prefix = $language->prefix; |
|
2669 } |
|
2670 $this->drupalGet("$prefix/$path"); |
|
2671 $this->assertResponse(404, $message2); |
|
2672 } |
|
2673 |
|
2674 /** |
|
2675 * Check URL rewriting when using a domain name and a non-standard port. |
|
2676 */ |
|
2677 function testDomainNameNegotiationPort() { |
|
2678 $language_domain = 'example.fr'; |
|
2679 $edit = array( |
|
2680 'locale_language_negotiation_url_part' => 1, |
|
2681 ); |
|
2682 $this->drupalPost('admin/config/regional/language/configure/url', $edit, t('Save configuration')); |
|
2683 $edit = array( |
|
2684 'prefix' => '', |
|
2685 'domain' => $language_domain |
|
2686 ); |
|
2687 $this->drupalPost('admin/config/regional/language/edit/fr', $edit, t('Save language')); |
|
2688 |
|
2689 // Enable domain configuration. |
|
2690 variable_set('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN); |
|
2691 |
|
2692 // Reset static caching. |
|
2693 drupal_static_reset('language_list'); |
|
2694 drupal_static_reset('language_url_outbound_alter'); |
|
2695 drupal_static_reset('language_url_rewrite_url'); |
|
2696 |
|
2697 // In case index.php is part of the URLs, we need to adapt the asserted |
|
2698 // URLs as well. |
|
2699 $index_php = strpos(url('', array('absolute' => TRUE)), 'index.php') !== FALSE; |
|
2700 |
|
2701 // Remember current HTTP_HOST. |
|
2702 $http_host = $_SERVER['HTTP_HOST']; |
|
2703 |
|
2704 // Fake a different port. |
|
2705 $_SERVER['HTTP_HOST'] .= ':88'; |
|
2706 |
|
2707 // Create an absolute French link. |
|
2708 $languages = language_list(); |
|
2709 $language = $languages['fr']; |
|
2710 $url = url('', array( |
|
2711 'absolute' => TRUE, |
|
2712 'language' => $language |
|
2713 )); |
|
2714 |
|
2715 $expected = 'http://example.fr:88/'; |
|
2716 $expected .= $index_php ? 'index.php/' : ''; |
|
2717 |
|
2718 $this->assertEqual($url, $expected, 'The right port is used.'); |
|
2719 |
|
2720 // If we set the port explicitly in url(), it should not be overriden. |
|
2721 $url = url('', array( |
|
2722 'absolute' => TRUE, |
|
2723 'language' => $language, |
|
2724 'base_url' => $GLOBALS['base_url'] . ':90', |
|
2725 )); |
|
2726 |
|
2727 $expected = 'http://example.fr:90/'; |
|
2728 $expected .= $index_php ? 'index.php/' : ''; |
|
2729 |
|
2730 $this->assertEqual($url, $expected, 'A given port is not overriden.'); |
|
2731 |
|
2732 // Restore HTTP_HOST. |
|
2733 $_SERVER['HTTP_HOST'] = $http_host; |
|
2734 } |
|
2735 } |
|
2736 |
|
2737 /** |
|
2738 * Functional test for multilingual fields. |
|
2739 */ |
|
2740 class LocaleMultilingualFieldsFunctionalTest extends DrupalWebTestCase { |
|
2741 public static function getInfo() { |
|
2742 return array( |
|
2743 'name' => 'Multilingual fields', |
|
2744 'description' => 'Test multilingual support for fields.', |
|
2745 'group' => 'Locale', |
|
2746 ); |
|
2747 } |
|
2748 |
|
2749 function setUp() { |
|
2750 parent::setUp('locale'); |
|
2751 // Setup users. |
|
2752 $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages', 'create page content', 'edit own page content')); |
|
2753 $this->drupalLogin($admin_user); |
|
2754 |
|
2755 // Add a new language. |
|
2756 require_once DRUPAL_ROOT . '/includes/locale.inc'; |
|
2757 locale_add_language('it', 'Italian', 'Italiano', LANGUAGE_LTR, '', '', TRUE, FALSE); |
|
2758 |
|
2759 // Enable URL language detection and selection. |
|
2760 $edit = array('language[enabled][locale-url]' => '1'); |
|
2761 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
2762 |
|
2763 // Set "Basic page" content type to use multilingual support. |
|
2764 $edit = array( |
|
2765 'language_content_type' => 1, |
|
2766 ); |
|
2767 $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type')); |
|
2768 $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), 'Basic page content type has been updated.'); |
|
2769 |
|
2770 // Make node body translatable. |
|
2771 $field = field_info_field('body'); |
|
2772 $field['translatable'] = TRUE; |
|
2773 field_update_field($field); |
|
2774 } |
|
2775 |
|
2776 /** |
|
2777 * Test if field languages are correctly set through the node form. |
|
2778 */ |
|
2779 function testMultilingualNodeForm() { |
|
2780 // Create "Basic page" content. |
|
2781 $langcode = LANGUAGE_NONE; |
|
2782 $title_key = "title"; |
|
2783 $title_value = $this->randomName(8); |
|
2784 $body_key = "body[$langcode][0][value]"; |
|
2785 $body_value = $this->randomName(16); |
|
2786 |
|
2787 // Create node to edit. |
|
2788 $edit = array(); |
|
2789 $edit[$title_key] = $title_value; |
|
2790 $edit[$body_key] = $body_value; |
|
2791 $edit['language'] = 'en'; |
|
2792 $this->drupalPost('node/add/page', $edit, t('Save')); |
|
2793 |
|
2794 // Check that the node exists in the database. |
|
2795 $node = $this->drupalGetNodeByTitle($edit[$title_key]); |
|
2796 $this->assertTrue($node, 'Node found in database.'); |
|
2797 |
|
2798 $assert = isset($node->body['en']) && !isset($node->body[LANGUAGE_NONE]) && $node->body['en'][0]['value'] == $body_value; |
|
2799 $this->assertTrue($assert, 'Field language correctly set.'); |
|
2800 |
|
2801 // Change node language. |
|
2802 $this->drupalGet("node/$node->nid/edit"); |
|
2803 $edit = array( |
|
2804 $title_key => $this->randomName(8), |
|
2805 'language' => 'it' |
|
2806 ); |
|
2807 $this->drupalPost(NULL, $edit, t('Save')); |
|
2808 $node = $this->drupalGetNodeByTitle($edit[$title_key]); |
|
2809 $this->assertTrue($node, 'Node found in database.'); |
|
2810 |
|
2811 $assert = isset($node->body['it']) && !isset($node->body['en']) && $node->body['it'][0]['value'] == $body_value; |
|
2812 $this->assertTrue($assert, 'Field language correctly changed.'); |
|
2813 |
|
2814 // Enable content language URL detection. |
|
2815 language_negotiation_set(LANGUAGE_TYPE_CONTENT, array(LOCALE_LANGUAGE_NEGOTIATION_URL => 0)); |
|
2816 |
|
2817 // Test multilingual field language fallback logic. |
|
2818 $this->drupalGet("it/node/$node->nid"); |
|
2819 $this->assertRaw($body_value, 'Body correctly displayed using Italian as requested language'); |
|
2820 |
|
2821 $this->drupalGet("node/$node->nid"); |
|
2822 $this->assertRaw($body_value, 'Body correctly displayed using English as requested language'); |
|
2823 } |
|
2824 |
|
2825 /* |
|
2826 * Test multilingual field display settings. |
|
2827 */ |
|
2828 function testMultilingualDisplaySettings() { |
|
2829 // Create "Basic page" content. |
|
2830 $langcode = LANGUAGE_NONE; |
|
2831 $title_key = "title"; |
|
2832 $title_value = $this->randomName(8); |
|
2833 $body_key = "body[$langcode][0][value]"; |
|
2834 $body_value = $this->randomName(16); |
|
2835 |
|
2836 // Create node to edit. |
|
2837 $edit = array(); |
|
2838 $edit[$title_key] = $title_value; |
|
2839 $edit[$body_key] = $body_value; |
|
2840 $edit['language'] = 'en'; |
|
2841 $this->drupalPost('node/add/page', $edit, t('Save')); |
|
2842 |
|
2843 // Check that the node exists in the database. |
|
2844 $node = $this->drupalGetNodeByTitle($edit[$title_key]); |
|
2845 $this->assertTrue($node, 'Node found in database.'); |
|
2846 |
|
2847 // Check if node body is showed. |
|
2848 $this->drupalGet("node/$node->nid"); |
|
2849 $body = $this->xpath('//div[@id=:id]//div[@property="content:encoded"]/p', array(':id' => 'node-' . $node->nid)); |
|
2850 $this->assertEqual(current($body), $node->body['en'][0]['value'], 'Node body is correctly showed.'); |
|
2851 } |
|
2852 } |
|
2853 |
|
2854 /** |
|
2855 * Functional tests for comment language. |
|
2856 */ |
|
2857 class LocaleCommentLanguageFunctionalTest extends DrupalWebTestCase { |
|
2858 |
|
2859 public static function getInfo() { |
|
2860 return array( |
|
2861 'name' => 'Comment language', |
|
2862 'description' => 'Tests for comment language.', |
|
2863 'group' => 'Locale', |
|
2864 ); |
|
2865 } |
|
2866 |
|
2867 function setUp() { |
|
2868 parent::setUp('locale', 'locale_test'); |
|
2869 |
|
2870 // Create and login user. |
|
2871 $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'administer content types', 'administer comments', 'create article content')); |
|
2872 $this->drupalLogin($admin_user); |
|
2873 |
|
2874 // Add language. |
|
2875 $edit = array('langcode' => 'fr'); |
|
2876 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
2877 |
|
2878 // Set "Article" content type to use multilingual support. |
|
2879 $edit = array('language_content_type' => 1); |
|
2880 $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type')); |
|
2881 |
|
2882 // Enable content language negotiation UI. |
|
2883 variable_set('locale_test_content_language_type', TRUE); |
|
2884 |
|
2885 // Set interface language detection to user and content language detection |
|
2886 // to URL. Disable inheritance from interface language to ensure content |
|
2887 // language will fall back to the default language if no URL language can be |
|
2888 // detected. |
|
2889 $edit = array( |
|
2890 'language[enabled][locale-user]' => TRUE, |
|
2891 'language_content[enabled][locale-url]' => TRUE, |
|
2892 'language_content[enabled][locale-interface]' => FALSE, |
|
2893 ); |
|
2894 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
2895 |
|
2896 // Change user language preference, this way interface language is always |
|
2897 // French no matter what path prefix the URLs have. |
|
2898 $edit = array('language' => 'fr'); |
|
2899 $this->drupalPost("user/{$admin_user->uid}/edit", $edit, t('Save')); |
|
2900 |
|
2901 // Make comment body translatable. |
|
2902 $field = field_info_field('comment_body'); |
|
2903 $field['translatable'] = TRUE; |
|
2904 field_update_field($field); |
|
2905 $this->assertTrue(field_is_translatable('comment', $field), 'Comment body is translatable.'); |
|
2906 } |
|
2907 |
|
2908 /** |
|
2909 * Test that comment language is properly set. |
|
2910 */ |
|
2911 function testCommentLanguage() { |
|
2912 drupal_static_reset('language_list'); |
|
2913 |
|
2914 // Create two nodes, one for english and one for french, and comment each |
|
2915 // node using both english and french as content language by changing URL |
|
2916 // language prefixes. Meanwhile interface language is always French, which |
|
2917 // is the user language preference. This way we can ensure that node |
|
2918 // language and interface language do not influence comment language, as |
|
2919 // only content language has to. |
|
2920 foreach (language_list() as $node_langcode => $node_language) { |
|
2921 $language_none = LANGUAGE_NONE; |
|
2922 |
|
2923 // Create "Article" content. |
|
2924 $title = $this->randomName(); |
|
2925 $edit = array( |
|
2926 "title" => $title, |
|
2927 "body[$language_none][0][value]" => $this->randomName(), |
|
2928 "language" => $node_langcode, |
|
2929 ); |
|
2930 $this->drupalPost("node/add/article", $edit, t('Save')); |
|
2931 $node = $this->drupalGetNodeByTitle($title); |
|
2932 |
|
2933 foreach (language_list() as $langcode => $language) { |
|
2934 // Post a comment with content language $langcode. |
|
2935 $prefix = empty($language->prefix) ? '' : $language->prefix . '/'; |
|
2936 $comment_values[$node_langcode][$langcode] = $this->randomName(); |
|
2937 // Initially field form widgets have no language. |
|
2938 $edit = array( |
|
2939 'subject' => $this->randomName(), |
|
2940 "comment_body[$language_none][0][value]" => $comment_values[$node_langcode][$langcode], |
|
2941 ); |
|
2942 $this->drupalPost("{$prefix}node/{$node->nid}", $edit, t('Preview')); |
|
2943 // After the first submit the submitted entity language is taken into |
|
2944 // account. |
|
2945 $edit = array( |
|
2946 'subject' => $edit['subject'], |
|
2947 "comment_body[$langcode][0][value]" => $comment_values[$node_langcode][$langcode], |
|
2948 ); |
|
2949 $this->drupalPost(NULL, $edit, t('Save')); |
|
2950 |
|
2951 // Check that comment language matches the current content language. |
|
2952 $cid = db_select('comment', 'c') |
|
2953 ->fields('c', array('cid')) |
|
2954 ->condition('nid', $node->nid) |
|
2955 ->orderBy('cid', 'DESC') |
|
2956 ->range(0, 1) |
|
2957 ->execute() |
|
2958 ->fetchField(); |
|
2959 $comment = comment_load($cid); |
|
2960 $comment_langcode = entity_language('comment', $comment); |
|
2961 $args = array('%node_language' => $node_langcode, '%comment_language' => $comment_langcode, '%langcode' => $langcode); |
|
2962 $this->assertEqual($comment_langcode, $langcode, format_string('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args)); |
|
2963 $this->assertEqual($comment->comment_body[$langcode][0]['value'], $comment_values[$node_langcode][$langcode], 'Comment body correctly stored.'); |
|
2964 } |
|
2965 } |
|
2966 |
|
2967 // Check that comment bodies appear in the administration UI. |
|
2968 $this->drupalGet('admin/content/comment'); |
|
2969 foreach ($comment_values as $node_values) { |
|
2970 foreach ($node_values as $value) { |
|
2971 $this->assertRaw($value); |
|
2972 } |
|
2973 } |
|
2974 } |
|
2975 |
|
2976 } |
|
2977 |
|
2978 /** |
|
2979 * Functional tests for localizing date formats. |
|
2980 */ |
|
2981 class LocaleDateFormatsFunctionalTest extends DrupalWebTestCase { |
|
2982 |
|
2983 public static function getInfo() { |
|
2984 return array( |
|
2985 'name' => 'Localize date formats', |
|
2986 'description' => 'Tests for the localization of date formats.', |
|
2987 'group' => 'Locale', |
|
2988 ); |
|
2989 } |
|
2990 |
|
2991 function setUp() { |
|
2992 parent::setUp('locale'); |
|
2993 |
|
2994 // Create and login user. |
|
2995 $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages', 'create article content')); |
|
2996 $this->drupalLogin($admin_user); |
|
2997 } |
|
2998 |
|
2999 /** |
|
3000 * Functional tests for localizing date formats. |
|
3001 */ |
|
3002 function testLocalizeDateFormats() { |
|
3003 // Add language. |
|
3004 $edit = array( |
|
3005 'langcode' => 'fr', |
|
3006 ); |
|
3007 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
3008 |
|
3009 // Set language negotiation. |
|
3010 $language_type = LANGUAGE_TYPE_INTERFACE; |
|
3011 $edit = array( |
|
3012 "{$language_type}[enabled][locale-url]" => TRUE, |
|
3013 ); |
|
3014 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
3015 |
|
3016 // Configure date formats. |
|
3017 $this->drupalGet('admin/config/regional/date-time/locale'); |
|
3018 $this->assertText('Français', 'Configured languages appear.'); |
|
3019 $edit = array( |
|
3020 'date_format_long' => 'd.m.Y - H:i', |
|
3021 'date_format_medium' => 'd.m.Y - H:i', |
|
3022 'date_format_short' => 'd.m.Y - H:i', |
|
3023 ); |
|
3024 $this->drupalPost('admin/config/regional/date-time/locale/fr/edit', $edit, t('Save configuration')); |
|
3025 $this->assertText(t('Configuration saved.'), 'French date formats updated.'); |
|
3026 $edit = array( |
|
3027 'date_format_long' => 'j M Y - g:ia', |
|
3028 'date_format_medium' => 'j M Y - g:ia', |
|
3029 'date_format_short' => 'j M Y - g:ia', |
|
3030 ); |
|
3031 $this->drupalPost('admin/config/regional/date-time/locale/en/edit', $edit, t('Save configuration')); |
|
3032 $this->assertText(t('Configuration saved.'), 'English date formats updated.'); |
|
3033 |
|
3034 // Create node content. |
|
3035 $node = $this->drupalCreateNode(array('type' => 'article')); |
|
3036 |
|
3037 // Configure format for the node posted date changes with the language. |
|
3038 $this->drupalGet('node/' . $node->nid); |
|
3039 $english_date = format_date($node->created, 'custom', 'j M Y'); |
|
3040 $this->assertText($english_date, 'English date format appears'); |
|
3041 $this->drupalGet('fr/node/' . $node->nid); |
|
3042 $french_date = format_date($node->created, 'custom', 'd.m.Y'); |
|
3043 $this->assertText($french_date, 'French date format appears'); |
|
3044 } |
|
3045 } |
|
3046 |
|
3047 /** |
|
3048 * Functional test for language types/negotiation info. |
|
3049 */ |
|
3050 class LocaleLanguageNegotiationInfoFunctionalTest extends DrupalWebTestCase { |
|
3051 |
|
3052 public static function getInfo() { |
|
3053 return array( |
|
3054 'name' => 'Language negotiation info', |
|
3055 'description' => 'Tests alterations to language types/negotiation info.', |
|
3056 'group' => 'Locale', |
|
3057 ); |
|
3058 } |
|
3059 |
|
3060 function setUp() { |
|
3061 parent::setUp('locale'); |
|
3062 require_once DRUPAL_ROOT .'/includes/language.inc'; |
|
3063 $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'view the administration theme')); |
|
3064 $this->drupalLogin($admin_user); |
|
3065 $this->drupalPost('admin/config/regional/language/add', array('langcode' => 'it'), t('Add language')); |
|
3066 } |
|
3067 |
|
3068 /** |
|
3069 * Tests alterations to language types/negotiation info. |
|
3070 */ |
|
3071 function testInfoAlterations() { |
|
3072 // Enable language type/negotiation info alterations. |
|
3073 variable_set('locale_test_language_types', TRUE); |
|
3074 variable_set('locale_test_language_negotiation_info', TRUE); |
|
3075 $this->languageNegotiationUpdate(); |
|
3076 |
|
3077 // Check that fixed language types are properly configured without the need |
|
3078 // of saving the language negotiation settings. |
|
3079 $this->checkFixedLanguageTypes(); |
|
3080 |
|
3081 // Make the content language type configurable by updating the language |
|
3082 // negotiation settings with the proper flag enabled. |
|
3083 variable_set('locale_test_content_language_type', TRUE); |
|
3084 $this->languageNegotiationUpdate(); |
|
3085 $type = LANGUAGE_TYPE_CONTENT; |
|
3086 $language_types = variable_get('language_types', drupal_language_types()); |
|
3087 $this->assertTrue($language_types[$type], 'Content language type is configurable.'); |
|
3088 |
|
3089 // Enable some core and custom language providers. The test language type is |
|
3090 // supposed to be configurable. |
|
3091 $test_type = 'test_language_type'; |
|
3092 $provider = LOCALE_LANGUAGE_NEGOTIATION_INTERFACE; |
|
3093 $test_provider = 'test_language_provider'; |
|
3094 $form_field = $type . '[enabled]['. $provider .']'; |
|
3095 $edit = array( |
|
3096 $form_field => TRUE, |
|
3097 $type . '[enabled][' . $test_provider . ']' => TRUE, |
|
3098 $test_type . '[enabled][' . $test_provider . ']' => TRUE, |
|
3099 ); |
|
3100 $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings')); |
|
3101 |
|
3102 // Remove the interface language provider by updating the language |
|
3103 // negotiation settings with the proper flag enabled. |
|
3104 variable_set('locale_test_language_negotiation_info_alter', TRUE); |
|
3105 $this->languageNegotiationUpdate(); |
|
3106 $negotiation = variable_get("language_negotiation_$type", array()); |
|
3107 $this->assertFalse(isset($negotiation[$provider]), 'Interface language provider removed from the stored settings.'); |
|
3108 $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, 'Interface language provider unavailable.'); |
|
3109 |
|
3110 // Check that type-specific language providers can be assigned only to the |
|
3111 // corresponding language types. |
|
3112 foreach (language_types_configurable() as $type) { |
|
3113 $form_field = $type . '[enabled][test_language_provider_ts]'; |
|
3114 if ($type == $test_type) { |
|
3115 $this->assertFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language provider available for %type.', array('%type' => $type))); |
|
3116 } |
|
3117 else { |
|
3118 $this->assertNoFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language provider unavailable for %type.', array('%type' => $type))); |
|
3119 } |
|
3120 } |
|
3121 |
|
3122 // Check language negotiation results. |
|
3123 $this->drupalGet(''); |
|
3124 $last = variable_get('locale_test_language_negotiation_last', array()); |
|
3125 foreach (language_types() as $type) { |
|
3126 $langcode = $last[$type]; |
|
3127 $value = $type == LANGUAGE_TYPE_CONTENT || strpos($type, 'test') !== FALSE ? 'it' : 'en'; |
|
3128 $this->assertEqual($langcode, $value, format_string('The negotiated language for %type is %language', array('%type' => $type, '%language' => $langcode))); |
|
3129 } |
|
3130 |
|
3131 // Disable locale_test and check that everything is set back to the original |
|
3132 // status. |
|
3133 $this->languageNegotiationUpdate('disable'); |
|
3134 |
|
3135 // Check that only the core language types are available. |
|
3136 foreach (language_types() as $type) { |
|
3137 $this->assertTrue(strpos($type, 'test') === FALSE, format_string('The %type language is still available', array('%type' => $type))); |
|
3138 } |
|
3139 |
|
3140 // Check that fixed language types are properly configured, even those |
|
3141 // previously set to configurable. |
|
3142 $this->checkFixedLanguageTypes(); |
|
3143 |
|
3144 // Check that unavailable language providers are not present in the |
|
3145 // negotiation settings. |
|
3146 $negotiation = variable_get("language_negotiation_$type", array()); |
|
3147 $this->assertFalse(isset($negotiation[$test_provider]), 'The disabled test language provider is not part of the content language negotiation settings.'); |
|
3148 |
|
3149 // Check that configuration page presents the correct options and settings. |
|
3150 $this->assertNoRaw(t('Test language detection'), 'No test language type configuration available.'); |
|
3151 $this->assertNoRaw(t('This is a test language provider'), 'No test language provider available.'); |
|
3152 } |
|
3153 |
|
3154 /** |
|
3155 * Update language types/negotiation information. |
|
3156 * |
|
3157 * Manually invoke locale_modules_enabled()/locale_modules_disabled() since |
|
3158 * they would not be invoked after enabling/disabling locale_test the first |
|
3159 * time. |
|
3160 */ |
|
3161 private function languageNegotiationUpdate($op = 'enable') { |
|
3162 static $last_op = NULL; |
|
3163 $modules = array('locale_test'); |
|
3164 |
|
3165 // Enable/disable locale_test only if we did not already before. |
|
3166 if ($last_op != $op) { |
|
3167 $function = "module_{$op}"; |
|
3168 $function($modules); |
|
3169 // Reset hook implementation cache. |
|
3170 module_implements(NULL, FALSE, TRUE); |
|
3171 } |
|
3172 |
|
3173 drupal_static_reset('language_types_info'); |
|
3174 drupal_static_reset('language_negotiation_info'); |
|
3175 $function = "locale_modules_{$op}d"; |
|
3176 if (function_exists($function)) { |
|
3177 $function($modules); |
|
3178 } |
|
3179 |
|
3180 $this->drupalGet('admin/config/regional/language/configure'); |
|
3181 } |
|
3182 |
|
3183 /** |
|
3184 * Check that language negotiation for fixed types matches the stored one. |
|
3185 */ |
|
3186 private function checkFixedLanguageTypes() { |
|
3187 drupal_static_reset('language_types_info'); |
|
3188 foreach (language_types_info() as $type => $info) { |
|
3189 if (isset($info['fixed'])) { |
|
3190 $negotiation = variable_get("language_negotiation_$type", array()); |
|
3191 $equal = count($info['fixed']) == count($negotiation); |
|
3192 while ($equal && list($id) = each($negotiation)) { |
|
3193 list(, $info_id) = each($info['fixed']); |
|
3194 $equal = $info_id == $id; |
|
3195 } |
|
3196 $this->assertTrue($equal, format_string('language negotiation for %type is properly set up', array('%type' => $type))); |
|
3197 } |
|
3198 } |
|
3199 } |
|
3200 } |
|
3201 |
|
3202 /** |
|
3203 * Functional tests for CSS alter functions. |
|
3204 */ |
|
3205 class LocaleCSSAlterTest extends DrupalWebTestCase { |
|
3206 public static function getInfo() { |
|
3207 return array( |
|
3208 'name' => 'CSS altering', |
|
3209 'description' => 'Test CSS alter functions.', |
|
3210 'group' => 'Locale', |
|
3211 ); |
|
3212 } |
|
3213 |
|
3214 function setUp() { |
|
3215 parent::setUp('locale'); |
|
3216 } |
|
3217 |
|
3218 /** |
|
3219 * Verifies that -rtl.css file is added directly after LTR .css file. |
|
3220 */ |
|
3221 function testCSSFilesOrderInRTLMode() { |
|
3222 global $base_url; |
|
3223 |
|
3224 // User to add and remove language. |
|
3225 $admin_user = $this->drupalCreateUser(array('administer languages', 'administer content types', 'access administration pages')); |
|
3226 |
|
3227 // Log in as admin. |
|
3228 $this->drupalLogin($admin_user); |
|
3229 |
|
3230 // Install the Arabic language (which is RTL) and configure as the default. |
|
3231 $edit = array(); |
|
3232 $edit['langcode'] = 'ar'; |
|
3233 $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); |
|
3234 |
|
3235 $edit = array(); |
|
3236 $edit['site_default'] = 'ar'; |
|
3237 $this->drupalPost(NULL, $edit, t('Save configuration')); |
|
3238 |
|
3239 // Verify that the -rtl.css file is added directly after LTR file. |
|
3240 $this->drupalGet(''); |
|
3241 $query_string = '?' . variable_get('css_js_query_string', '0'); |
|
3242 $this->assertRaw('@import url("' . $base_url . '/modules/system/system.base.css' . $query_string . '");' . "\n" . '@import url("' . $base_url . '/modules/system/system.base-rtl.css' . $query_string . '");' . "\n", 'CSS: system.base-rtl.css is added directly after system.base.css.'); |
|
3243 $this->assertRaw('@import url("' . $base_url . '/modules/system/system.menus.css' . $query_string . '");' . "\n" . '@import url("' . $base_url . '/modules/system/system.menus-rtl.css' . $query_string . '");' . "\n", 'CSS: system.menus-rtl.css is added directly after system.menus.css.'); |
|
3244 $this->assertRaw('@import url("' . $base_url . '/modules/system/system.messages.css' . $query_string . '");' . "\n" . '@import url("' . $base_url . '/modules/system/system.messages-rtl.css' . $query_string . '");' . "\n", 'CSS: system.messages-rtl.css is added directly after system.messages.css.'); |
|
3245 } |
|
3246 } |
|
3247 |
|
3248 /** |
|
3249 * Tests locale translation safe string handling. |
|
3250 */ |
|
3251 class LocaleStringIsSafeTest extends DrupalWebTestCase { |
|
3252 public static function getInfo() { |
|
3253 return array( |
|
3254 'name' => 'Test if a string is safe', |
|
3255 'description' => 'Tests locale translation safe string handling.', |
|
3256 'group' => 'Locale', |
|
3257 ); |
|
3258 } |
|
3259 |
|
3260 function setUp() { |
|
3261 parent::setUp('locale'); |
|
3262 } |
|
3263 |
|
3264 /** |
|
3265 * Tests for locale_string_is_safe(). |
|
3266 */ |
|
3267 public function testLocaleStringIsSafe() { |
|
3268 // Check a translatable string without HTML. |
|
3269 $string = 'Hello world!'; |
|
3270 $result = locale_string_is_safe($string); |
|
3271 $this->assertTrue($result); |
|
3272 |
|
3273 // Check a translatable string which includes trustable HTML. |
|
3274 $string = 'Hello <strong>world</strong>!'; |
|
3275 $result = locale_string_is_safe($string); |
|
3276 $this->assertTrue($result); |
|
3277 |
|
3278 // Check an untranslatable string which includes untrustable HTML (according |
|
3279 // to the locale_string_is_safe() function definition). |
|
3280 $string = 'Hello <img src="world.png" alt="world" />!'; |
|
3281 $result = locale_string_is_safe($string); |
|
3282 $this->assertFalse($result); |
|
3283 |
|
3284 // Check a translatable string which includes a token in an href attribute. |
|
3285 $string = 'Hi <a href="[current-user:url]">user</a>'; |
|
3286 $result = locale_string_is_safe($string); |
|
3287 $this->assertTrue($result); |
|
3288 } |
|
3289 } |