Chris@0: drupalCreateUser(['administer modules', 'administer site configuration', 'administer languages', 'access administration pages', 'translate interface']); Chris@0: $this->drupalLogin($admin_user); Chris@0: // We use German as test language. This language must match the translation Chris@0: // file that come with the locale_test module (test.de.po) and can therefore Chris@0: // not be chosen randomly. Chris@0: $this->addLanguage('de'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if local or remote translation sources are detected. Chris@0: * Chris@0: * The translation status process by default checks the status of the Chris@0: * installed projects. For testing purpose a predefined set of modules with Chris@0: * fixed file names and release versions is used. This custom project Chris@0: * definition is applied using a hook_locale_translation_projects_alter Chris@0: * implementation in the locale_test module. Chris@0: * Chris@0: * This test generates a set of local and remote translation files in their Chris@0: * respective local and remote translation directory. The test checks whether Chris@0: * the most recent files are selected in the different check scenarios: check Chris@0: * for local files only, check for both local and remote files. Chris@0: */ Chris@0: public function testUpdateCheckStatus() { Chris@0: // Case when contributed modules are absent. Chris@0: $this->drupalGet('admin/reports/translations'); Chris@0: $this->assertText(t('Missing translations for one project')); Chris@0: Chris@0: $config = $this->config('locale.settings'); Chris@0: // Set a flag to let the locale_test module replace the project data with a Chris@0: // set of test projects. Chris@0: \Drupal::state()->set('locale.test_projects_alter', TRUE); Chris@0: Chris@0: // Create local and remote translations files. Chris@0: $this->setTranslationFiles(); Chris@0: $config->set('translation.default_filename', '%project-%version.%language._po')->save(); Chris@0: Chris@0: // Set the test conditions. Chris@0: $edit = [ Chris@0: 'use_source' => LOCALE_TRANSLATION_USE_SOURCE_LOCAL, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration')); Chris@0: Chris@0: // Get status of translation sources at local file system. Chris@0: $this->drupalGet('admin/reports/translations/check'); Chris@0: $result = locale_translation_get_status(); Chris@0: $this->assertEqual($result['contrib_module_one']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of contrib_module_one found'); Chris@0: $this->assertEqual($result['contrib_module_one']['de']->timestamp, $this->timestampOld, 'Translation timestamp found'); Chris@0: $this->assertEqual($result['contrib_module_two']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of contrib_module_two found'); Chris@0: $this->assertEqual($result['contrib_module_two']['de']->timestamp, $this->timestampNew, 'Translation timestamp found'); Chris@0: $this->assertEqual($result['locale_test']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of locale_test found'); Chris@0: $this->assertEqual($result['custom_module_one']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of custom_module_one found'); Chris@0: Chris@0: // Set the test conditions. Chris@0: $edit = [ Chris@0: 'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration')); Chris@0: Chris@0: // Get status of translation sources at both local and remote locations. Chris@0: $this->drupalGet('admin/reports/translations/check'); Chris@0: $result = locale_translation_get_status(); Chris@0: $this->assertEqual($result['contrib_module_one']['de']->type, LOCALE_TRANSLATION_REMOTE, 'Translation of contrib_module_one found'); Chris@0: $this->assertEqual($result['contrib_module_one']['de']->timestamp, $this->timestampNew, 'Translation timestamp found'); Chris@0: $this->assertEqual($result['contrib_module_two']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of contrib_module_two found'); Chris@0: $this->assertEqual($result['contrib_module_two']['de']->timestamp, $this->timestampNew, 'Translation timestamp found'); Chris@0: $this->assertEqual($result['contrib_module_three']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of contrib_module_three found'); Chris@0: $this->assertEqual($result['contrib_module_three']['de']->timestamp, $this->timestampOld, 'Translation timestamp found'); Chris@0: $this->assertEqual($result['locale_test']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of locale_test found'); Chris@0: $this->assertEqual($result['custom_module_one']['de']->type, LOCALE_TRANSLATION_LOCAL, 'Translation of custom_module_one found'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests translation import from remote sources. Chris@0: * Chris@0: * Test conditions: Chris@0: * - Source: remote and local files Chris@0: * - Import overwrite: all existing translations Chris@0: */ Chris@0: public function testUpdateImportSourceRemote() { Chris@0: $config = $this->config('locale.settings'); Chris@0: Chris@0: // Build the test environment. Chris@0: $this->setTranslationFiles(); Chris@0: $this->setCurrentTranslations(); Chris@0: $config->set('translation.default_filename', '%project-%version.%language._po'); Chris@0: Chris@0: // Set the update conditions for this test. Chris@0: $edit = [ Chris@0: 'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL, Chris@0: 'overwrite' => LOCALE_TRANSLATION_OVERWRITE_ALL, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration')); Chris@0: Chris@0: // Get the translation status. Chris@0: $this->drupalGet('admin/reports/translations/check'); Chris@0: Chris@0: // Check the status on the Available translation status page. Chris@0: $this->assertRaw('', 'German language found'); Chris@0: $this->assertText('Updates for: Contributed module one, Contributed module two, Custom module one, Locale test', 'Updates found'); Chris@18: /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */ Chris@18: $date_formatter = $this->container->get('date.formatter'); Chris@18: $this->assertText('Contributed module one (' . $date_formatter->format($this->timestampNew, 'html_date') . ')', 'Updates for Contrib module one'); Chris@18: $this->assertText('Contributed module two (' . $date_formatter->format($this->timestampNew, 'html_date') . ')', 'Updates for Contrib module two'); Chris@0: Chris@0: // Execute the translation update. Chris@0: $this->drupalPostForm('admin/reports/translations', [], t('Update translations')); Chris@0: Chris@0: // Check if the translation has been updated, using the status cache. Chris@0: $status = locale_translation_get_status(); Chris@0: $this->assertEqual($status['contrib_module_one']['de']->type, LOCALE_TRANSLATION_CURRENT, 'Translation of contrib_module_one found'); Chris@0: $this->assertEqual($status['contrib_module_two']['de']->type, LOCALE_TRANSLATION_CURRENT, 'Translation of contrib_module_two found'); Chris@0: $this->assertEqual($status['contrib_module_three']['de']->type, LOCALE_TRANSLATION_CURRENT, 'Translation of contrib_module_three found'); Chris@0: Chris@0: // Check the new translation status. Chris@0: // The static cache needs to be flushed first to get the most recent data Chris@0: // from the database. The function was called earlier during this test. Chris@0: drupal_static_reset('locale_translation_get_file_history'); Chris@0: $history = locale_translation_get_file_history(); Chris@0: $this->assertTrue($history['contrib_module_one']['de']->timestamp >= $this->timestampNow, 'Translation of contrib_module_one is imported'); Chris@0: $this->assertTrue($history['contrib_module_one']['de']->last_checked >= $this->timestampNow, 'Translation of contrib_module_one is updated'); Chris@0: $this->assertEqual($history['contrib_module_two']['de']->timestamp, $this->timestampNew, 'Translation of contrib_module_two is imported'); Chris@0: $this->assertTrue($history['contrib_module_two']['de']->last_checked >= $this->timestampNow, 'Translation of contrib_module_two is updated'); Chris@0: $this->assertEqual($history['contrib_module_three']['de']->timestamp, $this->timestampMedium, 'Translation of contrib_module_three is not imported'); Chris@0: $this->assertEqual($history['contrib_module_three']['de']->last_checked, $this->timestampMedium, 'Translation of contrib_module_three is not updated'); Chris@0: Chris@0: // Check whether existing translations have (not) been overwritten. Chris@0: $this->assertEqual(t('January', [], ['langcode' => 'de']), 'Januar_1', 'Translation of January'); Chris@0: $this->assertEqual(t('February', [], ['langcode' => 'de']), 'Februar_2', 'Translation of February'); Chris@0: $this->assertEqual(t('March', [], ['langcode' => 'de']), 'Marz_2', 'Translation of March'); Chris@0: $this->assertEqual(t('April', [], ['langcode' => 'de']), 'April_2', 'Translation of April'); Chris@0: $this->assertEqual(t('May', [], ['langcode' => 'de']), 'Mai_customized', 'Translation of May'); Chris@0: $this->assertEqual(t('June', [], ['langcode' => 'de']), 'Juni', 'Translation of June'); Chris@0: $this->assertEqual(t('Monday', [], ['langcode' => 'de']), 'Montag', 'Translation of Monday'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests translation import from local sources. Chris@0: * Chris@0: * Test conditions: Chris@0: * - Source: local files only Chris@0: * - Import overwrite: all existing translations Chris@0: */ Chris@0: public function testUpdateImportSourceLocal() { Chris@0: $config = $this->config('locale.settings'); Chris@0: Chris@0: // Build the test environment. Chris@0: $this->setTranslationFiles(); Chris@0: $this->setCurrentTranslations(); Chris@0: $config->set('translation.default_filename', '%project-%version.%language._po'); Chris@0: Chris@0: // Set the update conditions for this test. Chris@0: $edit = [ Chris@0: 'use_source' => LOCALE_TRANSLATION_USE_SOURCE_LOCAL, Chris@0: 'overwrite' => LOCALE_TRANSLATION_OVERWRITE_ALL, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration')); Chris@0: Chris@0: // Execute the translation update. Chris@0: $this->drupalGet('admin/reports/translations/check'); Chris@0: $this->drupalPostForm('admin/reports/translations', [], t('Update translations')); Chris@0: Chris@0: // Check if the translation has been updated, using the status cache. Chris@0: $status = locale_translation_get_status(); Chris@0: $this->assertEqual($status['contrib_module_one']['de']->type, LOCALE_TRANSLATION_CURRENT, 'Translation of contrib_module_one found'); Chris@0: $this->assertEqual($status['contrib_module_two']['de']->type, LOCALE_TRANSLATION_CURRENT, 'Translation of contrib_module_two found'); Chris@0: $this->assertEqual($status['contrib_module_three']['de']->type, LOCALE_TRANSLATION_CURRENT, 'Translation of contrib_module_three found'); Chris@0: Chris@0: // Check the new translation status. Chris@0: // The static cache needs to be flushed first to get the most recent data Chris@0: // from the database. The function was called earlier during this test. Chris@0: drupal_static_reset('locale_translation_get_file_history'); Chris@0: $history = locale_translation_get_file_history(); Chris@0: $this->assertTrue($history['contrib_module_one']['de']->timestamp >= $this->timestampMedium, 'Translation of contrib_module_one is imported'); Chris@0: $this->assertEqual($history['contrib_module_one']['de']->last_checked, $this->timestampMedium, 'Translation of contrib_module_one is updated'); Chris@0: $this->assertEqual($history['contrib_module_two']['de']->timestamp, $this->timestampNew, 'Translation of contrib_module_two is imported'); Chris@0: $this->assertTrue($history['contrib_module_two']['de']->last_checked >= $this->timestampNow, 'Translation of contrib_module_two is updated'); Chris@0: $this->assertEqual($history['contrib_module_three']['de']->timestamp, $this->timestampMedium, 'Translation of contrib_module_three is not imported'); Chris@0: $this->assertEqual($history['contrib_module_three']['de']->last_checked, $this->timestampMedium, 'Translation of contrib_module_three is not updated'); Chris@0: Chris@0: // Check whether existing translations have (not) been overwritten. Chris@0: $this->assertEqual(t('January', [], ['langcode' => 'de']), 'Januar_customized', 'Translation of January'); Chris@0: $this->assertEqual(t('February', [], ['langcode' => 'de']), 'Februar_2', 'Translation of February'); Chris@0: $this->assertEqual(t('March', [], ['langcode' => 'de']), 'Marz_2', 'Translation of March'); Chris@0: $this->assertEqual(t('April', [], ['langcode' => 'de']), 'April_2', 'Translation of April'); Chris@0: $this->assertEqual(t('May', [], ['langcode' => 'de']), 'Mai_customized', 'Translation of May'); Chris@0: $this->assertEqual(t('June', [], ['langcode' => 'de']), 'Juni', 'Translation of June'); Chris@0: $this->assertEqual(t('Monday', [], ['langcode' => 'de']), 'Montag', 'Translation of Monday'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests translation import and only overwrite non-customized translations. Chris@0: * Chris@0: * Test conditions: Chris@0: * - Source: remote and local files Chris@0: * - Import overwrite: only overwrite non-customized translations Chris@0: */ Chris@0: public function testUpdateImportModeNonCustomized() { Chris@0: $config = $this->config('locale.settings'); Chris@0: Chris@0: // Build the test environment. Chris@0: $this->setTranslationFiles(); Chris@0: $this->setCurrentTranslations(); Chris@0: $config->set('translation.default_filename', '%project-%version.%language._po'); Chris@0: Chris@0: // Set the test conditions. Chris@0: $edit = [ Chris@0: 'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL, Chris@0: 'overwrite' => LOCALE_TRANSLATION_OVERWRITE_NON_CUSTOMIZED, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration')); Chris@0: Chris@0: // Execute translation update. Chris@0: $this->drupalGet('admin/reports/translations/check'); Chris@0: $this->drupalPostForm('admin/reports/translations', [], t('Update translations')); Chris@0: Chris@0: // Check whether existing translations have (not) been overwritten. Chris@0: $this->assertEqual(t('January', [], ['langcode' => 'de']), 'Januar_customized', 'Translation of January'); Chris@0: $this->assertEqual(t('February', [], ['langcode' => 'de']), 'Februar_customized', 'Translation of February'); Chris@0: $this->assertEqual(t('March', [], ['langcode' => 'de']), 'Marz_2', 'Translation of March'); Chris@0: $this->assertEqual(t('April', [], ['langcode' => 'de']), 'April_2', 'Translation of April'); Chris@0: $this->assertEqual(t('May', [], ['langcode' => 'de']), 'Mai_customized', 'Translation of May'); Chris@0: $this->assertEqual(t('June', [], ['langcode' => 'de']), 'Juni', 'Translation of June'); Chris@0: $this->assertEqual(t('Monday', [], ['langcode' => 'de']), 'Montag', 'Translation of Monday'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests translation import and don't overwrite any translation. Chris@0: * Chris@0: * Test conditions: Chris@0: * - Source: remote and local files Chris@0: * - Import overwrite: don't overwrite any existing translation Chris@0: */ Chris@0: public function testUpdateImportModeNone() { Chris@0: $config = $this->config('locale.settings'); Chris@0: Chris@0: // Build the test environment. Chris@0: $this->setTranslationFiles(); Chris@0: $this->setCurrentTranslations(); Chris@0: $config->set('translation.default_filename', '%project-%version.%language._po'); Chris@0: Chris@0: // Set the test conditions. Chris@0: $edit = [ Chris@0: 'use_source' => LOCALE_TRANSLATION_USE_SOURCE_REMOTE_AND_LOCAL, Chris@0: 'overwrite' => LOCALE_TRANSLATION_OVERWRITE_NONE, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate/settings', $edit, t('Save configuration')); Chris@0: Chris@0: // Execute translation update. Chris@0: $this->drupalGet('admin/reports/translations/check'); Chris@0: $this->drupalPostForm('admin/reports/translations', [], t('Update translations')); Chris@0: Chris@0: // Check whether existing translations have (not) been overwritten. Chris@0: $this->assertTranslation('January', 'Januar_customized', 'de'); Chris@0: $this->assertTranslation('February', 'Februar_customized', 'de'); Chris@0: $this->assertTranslation('March', 'Marz', 'de'); Chris@0: $this->assertTranslation('April', 'April_2', 'de'); Chris@0: $this->assertTranslation('May', 'Mai_customized', 'de'); Chris@0: $this->assertTranslation('June', 'Juni', 'de'); Chris@0: $this->assertTranslation('Monday', 'Montag', 'de'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests automatic translation import when a module is enabled. Chris@0: */ Chris@0: public function testEnableUninstallModule() { Chris@0: // Make the hidden test modules look like a normal custom module. Chris@0: \Drupal::state()->set('locale.test_system_info_alter', TRUE); Chris@0: Chris@0: // Check if there is no translation yet. Chris@0: $this->assertTranslation('Tuesday', '', 'de'); Chris@0: Chris@0: // Enable a module. Chris@0: $edit = [ Chris@0: 'modules[locale_test_translate][enable]' => 'locale_test_translate', Chris@0: ]; Chris@0: $this->drupalPostForm('admin/modules', $edit, t('Install')); Chris@0: Chris@0: // Check if translations have been imported. Chris@0: $this->assertRaw(t('One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.', Chris@0: ['%number' => 7, '%update' => 0, '%delete' => 0]), 'One translation file imported.'); Chris@0: $this->assertTranslation('Tuesday', 'Dienstag', 'de'); Chris@0: Chris@0: $edit = [ Chris@0: 'uninstall[locale_test_translate]' => 1, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall')); Chris@0: $this->drupalPostForm(NULL, [], t('Uninstall')); Chris@0: Chris@0: // Check if the file data is removed from the database. Chris@0: $history = locale_translation_get_file_history(); Chris@0: $this->assertFalse(isset($history['locale_test_translate']), 'Project removed from the file history'); Chris@0: $projects = locale_translation_get_projects(); Chris@0: $this->assertFalse(isset($projects['locale_test_translate']), 'Project removed from the project list'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests automatic translation import when a language is added. Chris@0: * Chris@0: * When a language is added, the system will check for translations files of Chris@0: * enabled modules and will import them. When a language is removed the system Chris@0: * will remove all translations of that language from the database. Chris@0: */ Chris@0: public function testEnableLanguage() { Chris@0: // Make the hidden test modules look like a normal custom module. Chris@0: \Drupal::state()->set('locale.test_system_info_alter', TRUE); Chris@0: Chris@0: // Enable a module. Chris@0: $edit = [ Chris@0: 'modules[locale_test_translate][enable]' => 'locale_test_translate', Chris@0: ]; Chris@0: $this->drupalPostForm('admin/modules', $edit, t('Install')); Chris@0: Chris@0: // Check if there is no Dutch translation yet. Chris@0: $this->assertTranslation('Extraday', '', 'nl'); Chris@0: $this->assertTranslation('Tuesday', 'Dienstag', 'de'); Chris@0: Chris@0: // Add a language. Chris@0: $edit = [ Chris@0: 'predefined_langcode' => 'nl', Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language')); Chris@0: Chris@0: // Check if the right number of translations are added. Chris@0: $this->assertRaw(t('One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.', Chris@0: ['%number' => 8, '%update' => 0, '%delete' => 0]), 'One language added.'); Chris@0: $this->assertTranslation('Extraday', 'extra dag', 'nl'); Chris@0: Chris@0: // Check if the language data is added to the database. Chris@0: $result = db_query("SELECT project FROM {locale_file} WHERE langcode='nl'")->fetchField(); Chris@0: $this->assertTrue($result, 'Files added to file history'); Chris@0: Chris@0: // Remove a language. Chris@0: $this->drupalPostForm('admin/config/regional/language/delete/nl', [], t('Delete')); Chris@0: Chris@0: // Check if the language data is removed from the database. Chris@0: $result = db_query("SELECT project FROM {locale_file} WHERE langcode='nl'")->fetchField(); Chris@0: $this->assertFalse($result, 'Files removed from file history'); Chris@0: Chris@0: // Check that the Dutch translation is gone. Chris@0: $this->assertTranslation('Extraday', '', 'nl'); Chris@0: $this->assertTranslation('Tuesday', 'Dienstag', 'de'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests automatic translation import when a custom language is added. Chris@0: */ Chris@0: public function testEnableCustomLanguage() { Chris@0: // Make the hidden test modules look like a normal custom module. Chris@0: \Drupal::state()->set('locale.test_system_info_alter', TRUE); Chris@0: Chris@0: // Enable a module. Chris@0: $edit = [ Chris@0: 'modules[locale_test_translate][enable]' => 'locale_test_translate', Chris@0: ]; Chris@0: $this->drupalPostForm('admin/modules', $edit, t('Install')); Chris@0: Chris@0: // Create a custom language with language code 'xx' and a random Chris@0: // name. Chris@0: $langcode = 'xx'; Chris@0: $name = $this->randomMachineName(16); Chris@0: $edit = [ Chris@0: 'predefined_langcode' => 'custom', Chris@0: 'langcode' => $langcode, Chris@0: 'label' => $name, Chris@0: 'direction' => LanguageInterface::DIRECTION_LTR, Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language')); Chris@0: Chris@0: // Ensure the translation file is automatically imported when the language Chris@0: // was added. Chris@0: $this->assertText(t('One translation file imported.'), 'Language file automatically imported.'); Chris@0: $this->assertText(t('One translation string was skipped because of disallowed or malformed HTML'), 'Language file automatically imported.'); Chris@0: Chris@0: // Ensure the strings were successfully imported. Chris@0: $search = [ Chris@0: 'string' => 'lundi', Chris@0: 'langcode' => $langcode, Chris@0: 'translation' => 'translated', Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); Chris@0: $this->assertNoText(t('No strings available.'), 'String successfully imported.'); Chris@0: Chris@0: // Ensure the multiline string was imported. Chris@0: $search = [ Chris@0: 'string' => 'Source string for multiline translation', Chris@0: 'langcode' => $langcode, Chris@0: 'translation' => 'all', Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); Chris@0: $this->assertText('Multiline translation string to make sure that import works with it.', 'String successfully imported.'); Chris@0: Chris@0: // Ensure 'Allowed HTML source string' was imported but the translation for Chris@0: // 'Another allowed HTML source string' was not because it contains invalid Chris@0: // HTML. Chris@0: $search = [ Chris@0: 'string' => 'HTML source string', Chris@0: 'langcode' => $langcode, Chris@0: 'translation' => 'all', Chris@0: ]; Chris@0: $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); Chris@0: $this->assertText('Allowed HTML source string', 'String successfully imported.'); Chris@0: $this->assertNoText('Another allowed HTML source string', 'String with disallowed translation not imported.'); Chris@0: } Chris@0: Chris@0: }