Newer
Older
<?php
/**
* @file
* Definition of Drupal\locale\Tests\LocaleTranslationTest.
*/
namespace Drupal\locale\Tests;
use Drupal\simpletest\WebTestBase;
Dries Buytaert
committed
use Drupal\Core\Language\Language;
/**
* Functional test for string translation and validation.
*/
class LocaleTranslationTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('locale');
public static function getInfo() {
return array(
'name' => 'String translate, search and validate',
'description' => 'Adds a new locale and translates its name. Checks the validation of translation strings and search results.',
'group' => 'Locale',
);
}
/**
* Enable interface translation to English
*/
function testEnglishTranslation() {
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
$this->drupalLogin($admin_user);
$this->drupalPost('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language'));
$this->assertLinkByHref('/admin/config/regional/translate/translate?langcode=en', 0, t('Enabled interface translation to English.'));
}
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
* Adds a language and tests string translation by users with the appropriate permissions.
*/
function testStringTranslation() {
global $base_url;
// User to add and remove language.
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
// User to translate and delete string.
$translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
// Code for the language.
$langcode = 'xx';
// The English name for the language. This will be translated.
$name = $this->randomName(16);
// This is the language indicator on the translation search screen for
// untranslated strings.
$language_indicator = "<em class=\"locale-untranslated\">$langcode</em> ";
// This will be the translation of $name.
$translation = $this->randomName(16);
$translation_to_en = $this->randomName(16);
// Add custom language.
$this->drupalLogin($admin_user);
$edit = array(
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'name' => $name,
'direction' => '0',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
locale_reset();
Dries Buytaert
committed
$this->assertRaw('"edit-languages-' . $langcode .'-weight"', t('Language code found.'));
$this->assertText(t($name), t('Test language added.'));
$this->drupalLogout();
// Search for the name and translate it.
$this->drupalLogin($translate_user);
$search = array(
'string' => $name,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'untranslated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
Angie Byron
committed
$this->assertText($name, t('Search found the string as untranslated.'));
// Assume this is the only result, given the random name.
// We save the lid from the path.
Angie Byron
committed
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
$edit = array(
$lid => $this->randomName(),
);
// No t() here, it's surely not translated yet.
$this->assertText($name, t('name found on edit screen.'));
$this->assertNoText('English', t('No way to translate the string to English.'));
$this->drupalLogout();
$this->drupalLogin($admin_user);
$this->drupalPost('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language'));
$this->drupalLogout();
$this->drupalLogin($translate_user);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
Angie Byron
committed
$this->assertText($name, t('Search found the string as untranslated.'));
// Assume this is the only result, given the random name.
Angie Byron
committed
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
Angie Byron
committed
$lid => $translation,
Angie Byron
committed
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
$this->assertText(t('The strings have been saved.'), t('The strings have been saved.'));
$this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
Angie Byron
committed
$search = array(
'string' => $name,
'langcode' => $langcode,
'translation' => 'translated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertRaw($translation, t('Non-English translation properly saved.'));
Angie Byron
committed
$search = array(
'string' => $name,
'langcode' => 'en',
'translation' => 'untranslated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
$edit = array(
$lid => $translation_to_en,
);
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
$search = array(
'string' => $name,
'langcode' => 'en',
'translation' => 'translated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertRaw($translation_to_en, t('English translation properly saved.'));
Angie Byron
committed
// Reset the tag cache on the tester side in order to pick up the call to
// cache()->deleteTags() on the tested side.
drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache');
$this->assertTrue($name != $translation && t($name, array(), array('langcode' => $langcode)) == $translation, t('t() works for non-English.'));
// Refresh the locale() cache to get fresh data from t() below. We are in
// the same HTTP request and therefore t() is not refreshed by saving the
// translation above.
locale_reset();
// Now we should get the proper fresh translation from t().
$this->assertTrue($name != $translation_to_en && t($name, array(), array('langcode' => 'en')) == $translation_to_en, t('t() works for English.'));
$this->assertTrue(t($name, array(), array('langcode' => Language::LANGCODE_SYSTEM)) == $name, 't() works for Language::LANGCODE_SYSTEM.');
Angie Byron
committed
$search = array(
'string' => $name,
'langcode' => 'en',
'translation' => 'untranslated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
Angie Byron
committed
$this->assertText(t('No strings available.'), t('String is translated.'));
$this->drupalLogout();
// Delete the language.
$this->drupalLogin($admin_user);
$path = 'admin/config/regional/language/delete/' . $langcode;
// This a confirm form, we do not need any fields changed.
$this->drupalPost($path, array(), t('Delete'));
// We need raw here because %language and %langcode will add HTML.
$t_args = array('%language' => $name, '%langcode' => $langcode);
$this->assertRaw(t('The %language (%langcode) language has been removed.', $t_args), t('The test language has been removed.'));
// Reload to remove $name.
$this->drupalGet($path);
// Verify that language is no longer found.
$this->assertResponse(404, t('Language no longer found.'));
$this->drupalLogout();
// Delete the string.
$this->drupalLogin($translate_user);
$search = array(
'string' => $name,
Angie Byron
committed
'langcode' => 'en',
'translation' => 'translated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// Assume this is the only result, given the random name.
Angie Byron
committed
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
$edit = array(
$lid => '',
);
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
$this->assertRaw($name, t('The strings have been saved.'));
$this->drupalLogin($translate_user);
$search = array(
'string' => $name,
'langcode' => 'en',
'translation' => 'untranslated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
Angie Byron
committed
$this->assertNoText(t('No strings available.'), t('The translation has been removed'));
}
/*
* Adds a language and checks that the JavaScript translation files are
* properly created and rebuilt on deletion.
*/
function testJavaScriptTranslation() {
$user = $this->drupalCreateUser(array('translate interface', 'administer languages', 'access administration pages'));
$this->drupalLogin($user);
Dries Buytaert
committed
$config = config('locale.settings');
$langcode = 'xx';
// The English name for the language. This will be translated.
$name = $this->randomName(16);
// Add custom language.
$edit = array(
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'name' => $name,
'direction' => '0',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
drupal_static_reset('language_list');
// Build the JavaScript translation file.
Angie Byron
committed
// Retrieve the source string of the first string available in the
// {locales_source} table and translate it.
$source = db_select('locales_source', 'l')
->fields('l', array('source'))
->condition('l.source', '%.js%', 'LIKE')
Angie Byron
committed
->range(0, 1)
->execute()
->fetchField();
$search = array(
'string' => $source,
'langcode' => $langcode,
'translation' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
$edit = array(
$lid => $this->randomName(),
);
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
// Trigger JavaScript translation parsing and building.
_locale_rebuild_js($langcode);
Dries Buytaert
committed
$locale_javascripts = state()->get('locale.translation.javascript') ?: array();
$js_file = 'public://' . $config->get('javascript.directory') . '/' . $langcode . '_' . $locale_javascripts[$langcode] . '.js';
$this->assertTrue($result = file_exists($js_file), t('JavaScript file created: %file', array('%file' => $result ? $js_file : t('not found'))));
// Test JavaScript translation rebuilding.
file_unmanaged_delete($js_file);
$this->assertTrue($result = !file_exists($js_file), t('JavaScript file deleted: %file', array('%file' => $result ? $js_file : t('found'))));
cache_invalidate_tags(array('content' => TRUE));
_locale_rebuild_js($langcode);
$this->assertTrue($result = file_exists($js_file), t('JavaScript file rebuilt: %file', array('%file' => $result ? $js_file : t('not found'))));
}
/**
* Tests the validation of the translation input.
*/
function testStringValidation() {
global $base_url;
// User to add language and strings.
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface'));
$this->drupalLogin($admin_user);
$langcode = 'xx';
// The English name for the language. This will be translated.
$name = $this->randomName(16);
Angie Byron
committed
// These will be the invalid translations of $name.
$key = $this->randomName(16);
$bad_translations[$key] = "<script>alert('xss');</script>" . $key;
$key = $this->randomName(16);
$bad_translations[$key] = '<img SRC="javascript:alert(\'xss\');">' . $key;
$key = $this->randomName(16);
$bad_translations[$key] = '<<SCRIPT>alert("xss");//<</SCRIPT>' . $key;
$key = $this->randomName(16);
$bad_translations[$key] ="<BODY ONLOAD=alert('xss')>" . $key;
// Add custom language.
$edit = array(
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'name' => $name,
'direction' => '0',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
$search = array(
'string' => $name,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// Find the edit path.
Angie Byron
committed
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
foreach ($bad_translations as $key => $translation) {
$edit = array(
Angie Byron
committed
$lid => $translation,
Angie Byron
committed
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
// Check for a form error on the textarea.
$form_class = $this->xpath('//form[@id="locale-translate-edit-form"]//textarea/@class');
$this->assertNotIdentical(FALSE, strpos($form_class[0], 'error'), t('The string was rejected as unsafe.'));
$this->assertNoText(t('The string has been saved.'), t('The string was not saved.'));
}
}
/**
* Tests translation search form.
*/
function testStringSearch() {
global $base_url;
// User to add and remove language.
$admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
// User to translate and delete string.
$translate_user = $this->drupalCreateUser(array('translate interface', 'access administration pages'));
// Code for the language.
$langcode = 'xx';
// The English name for the language. This will be translated.
$name = $this->randomName(16);
// This is the language indicator on the translation search screen for
// untranslated strings.
$language_indicator = "<em class=\"locale-untranslated\">$langcode</em> ";
// This will be the translation of $name.
$translation = $this->randomName(16);
// Add custom language.
$this->drupalLogin($admin_user);
$edit = array(
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'name' => $name,
'direction' => '0',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
Angie Byron
committed
$edit = array(
'predefined_langcode' => 'custom',
'langcode' => 'yy',
'name' => $this->randomName(16),
'direction' => '0',
);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
// Add string.
t($name, array(), array('langcode' => $langcode));
// Reset locale cache.
locale_reset();
$this->drupalLogout();
// Search for the name.
$this->drupalLogin($translate_user);
$search = array(
'string' => $name,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
// assertText() seems to remove the input field where $name always could be
// found, so this is not a false assert. See how assertNoText succeeds
// later.
$this->assertText($name, t('Search found the string.'));
// Ensure untranslated string doesn't appear if searching on 'only
// translated strings'.
$search = array(
'string' => $name,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'translated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings available.'), t("Search didn't find the string."));
// Ensure untranslated string appears if searching on 'only untranslated
// strings'.
$search = array(
'string' => $name,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'untranslated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings available.'), t('Search found the string.'));
// Add translation.
// Assume this is the only result, given the random name.
// We save the lid from the path.
Angie Byron
committed
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
Angie Byron
committed
$lid => $translation,
Angie Byron
committed
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
// Ensure translated string does appear if searching on 'only
// translated strings'.
$search = array(
'string' => $translation,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'translated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings available.'), t('Search found the translation.'));
// Ensure translated source string doesn't appear if searching on 'only
// untranslated strings'.
$search = array(
'string' => $name,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'untranslated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings available.'), t("Search didn't find the source string."));
// Ensure translated string doesn't appear if searching on 'only
// untranslated strings'.
$search = array(
'string' => $translation,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'untranslated',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings available.'), t("Search didn't find the translation."));
// Ensure translated string does appear if searching on the custom language.
$search = array(
'string' => $translation,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertNoText(t('No strings available.'), t('Search found the translation.'));
// Ensure translated string doesn't appear if searching in System (English).
$search = array(
'string' => $translation,
Angie Byron
committed
'langcode' => 'yy',
'translation' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings available.'), t("Search didn't find the translation."));
// Search for a string that isn't in the system.
$unavailable_string = $this->randomName(16);
$search = array(
'string' => $unavailable_string,
Angie Byron
committed
'langcode' => $langcode,
'translation' => 'all',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText(t('No strings available.'), t("Search didn't find the invalid string."));
}
Dries Buytaert
committed
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/**
* Tests that only changed strings are saved customized when edited.
*/
function testUICustomizedStrings(){
$user = $this->drupalCreateUser(array('translate interface', 'administer languages', 'access administration pages'));
$this->drupalLogin($user);
$language = new Language(array('langcode' => 'de'));
language_save($language);
// Create test source string
$string = locale_storage()->createString(array(
'source' => $this->randomName(100),
'context' => $this->randomName(20),
))->save();
// Create translation for new string and save it as non-customized.
$translation = locale_storage()->createTranslation(array(
'lid' => $string->lid,
'language' => 'de',
'translation' => $this->randomName(100),
'customized' => 0,
))->save();
// Reset locale cache.
locale_reset();
// Ensure non-customized translation string does appear if searching Non-customized translation.
$search = array(
'string' => $string->getString(),
'langcode' => 'de',
'translation' => 'translated',
'customized' => '0',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$source = $this->assertText($translation->getString(), 'Translation is found in search result.');
// Submit the translations without changing the translation.
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
$edit = array(
$lid => $translation->getString(),
);
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
// Ensure unchanged translation string does appear if searching non-customized translation.
$search = array(
'string' => $string->getString(),
'langcode' => 'de',
'translation' => 'translated',
'customized' => '0',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$source = $this->assertText($string->getString(), 'Translation is not marked as customized.');
// Submit the translations with a new translation.
$textarea = current($this->xpath('//textarea'));
$lid = (string) $textarea[0]['name'];
$edit = array(
$lid => $this->randomName(100),
);
$this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
// Ensure changed translation string does appear if searching customized translation.
$search = array(
'string' => $string->getString(),
'langcode' => 'de',
'translation' => 'translated',
'customized' => '1',
);
$this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
$this->assertText($string->getString(), "Translation is marked as customized.");
}