annotate core/modules/locale/tests/src/Functional/LocaleUpdateBase.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\locale\Functional;
Chris@0 4
Chris@18 5 use Drupal\Core\Database\Database;
Chris@18 6 use Drupal\Core\File\FileSystemInterface;
Chris@0 7 use Drupal\Core\StreamWrapper\PublicStream;
Chris@0 8 use Drupal\file\Entity\File;
Chris@0 9 use Drupal\Tests\BrowserTestBase;
Chris@17 10 use Drupal\Component\Render\FormattableMarkup;
Chris@0 11
Chris@0 12 /**
Chris@0 13 * Base class for testing updates to string translations.
Chris@0 14 */
Chris@0 15 abstract class LocaleUpdateBase extends BrowserTestBase {
Chris@0 16
Chris@0 17 /**
Chris@0 18 * Timestamp for an old translation.
Chris@0 19 *
Chris@0 20 * @var int
Chris@0 21 */
Chris@0 22 protected $timestampOld;
Chris@0 23
Chris@0 24 /**
Chris@0 25 * Timestamp for a medium aged translation.
Chris@0 26 *
Chris@0 27 * @var int
Chris@0 28 */
Chris@0 29 protected $timestampMedium;
Chris@0 30
Chris@0 31 /**
Chris@0 32 * Timestamp for a new translation.
Chris@0 33 *
Chris@0 34 * @var int
Chris@0 35 */
Chris@0 36 protected $timestampNew;
Chris@0 37
Chris@0 38 /**
Chris@0 39 * Timestamp for current time.
Chris@0 40 *
Chris@0 41 * @var int
Chris@0 42 */
Chris@0 43 protected $timestampNow;
Chris@0 44
Chris@0 45 /**
Chris@0 46 * Modules to enable.
Chris@0 47 *
Chris@0 48 * @var array
Chris@0 49 */
Chris@0 50 public static $modules = ['locale', 'locale_test'];
Chris@0 51
Chris@0 52 /**
Chris@0 53 * {@inheritdoc}
Chris@0 54 */
Chris@0 55 protected function setUp() {
Chris@0 56 parent::setUp();
Chris@0 57
Chris@0 58 // Setup timestamps to identify old and new translation sources.
Chris@0 59 $this->timestampOld = REQUEST_TIME - 300;
Chris@0 60 $this->timestampMedium = REQUEST_TIME - 200;
Chris@0 61 $this->timestampNew = REQUEST_TIME - 100;
Chris@0 62 $this->timestampNow = REQUEST_TIME;
Chris@0 63
Chris@0 64 // Enable import of translations. By default this is disabled for automated
Chris@0 65 // tests.
Chris@0 66 $this->config('locale.settings')
Chris@0 67 ->set('translation.import_enabled', TRUE)
Chris@16 68 ->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
Chris@0 69 ->save();
Chris@0 70 }
Chris@0 71
Chris@0 72 /**
Chris@0 73 * Sets the value of the default translations directory.
Chris@0 74 *
Chris@0 75 * @param string $path
Chris@0 76 * Path of the translations directory relative to the drupal installation
Chris@0 77 * directory.
Chris@0 78 */
Chris@0 79 protected function setTranslationsDirectory($path) {
Chris@18 80 \Drupal::service('file_system')->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY);
Chris@0 81 $this->config('locale.settings')->set('translation.path', $path)->save();
Chris@0 82 }
Chris@0 83
Chris@0 84 /**
Chris@0 85 * Adds a language.
Chris@0 86 *
Chris@0 87 * @param string $langcode
Chris@0 88 * The language code of the language to add.
Chris@0 89 */
Chris@0 90 protected function addLanguage($langcode) {
Chris@0 91 $edit = ['predefined_langcode' => $langcode];
Chris@0 92 $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
Chris@0 93 $this->container->get('language_manager')->reset();
Chris@17 94 $this->assertTrue(\Drupal::languageManager()->getLanguage($langcode), new FormattableMarkup('Language %langcode added.', ['%langcode' => $langcode]));
Chris@0 95 }
Chris@0 96
Chris@0 97 /**
Chris@0 98 * Creates a translation file and tests its timestamp.
Chris@0 99 *
Chris@0 100 * @param string $path
Chris@0 101 * Path of the file relative to the public file path.
Chris@0 102 * @param string $filename
Chris@0 103 * Name of the file to create.
Chris@0 104 * @param int $timestamp
Chris@0 105 * (optional) Timestamp to set the file to. Defaults to current time.
Chris@0 106 * @param array $translations
Chris@0 107 * (optional) Array of source/target value translation strings. Only
Chris@0 108 * singular strings are supported, no plurals. No double quotes are allowed
Chris@0 109 * in source and translations strings.
Chris@0 110 */
Chris@0 111 protected function makePoFile($path, $filename, $timestamp = NULL, array $translations = []) {
Chris@0 112 $timestamp = $timestamp ? $timestamp : REQUEST_TIME;
Chris@0 113 $path = 'public://' . $path;
Chris@0 114 $text = '';
Chris@0 115 $po_header = <<<EOF
Chris@0 116 msgid ""
Chris@0 117 msgstr ""
Chris@0 118 "Project-Id-Version: Drupal 8\\n"
Chris@0 119 "MIME-Version: 1.0\\n"
Chris@0 120 "Content-Type: text/plain; charset=UTF-8\\n"
Chris@0 121 "Content-Transfer-Encoding: 8bit\\n"
Chris@0 122 "Plural-Forms: nplurals=2; plural=(n > 1);\\n"
Chris@0 123
Chris@0 124 EOF;
Chris@0 125
Chris@0 126 // Convert array of translations to Gettext source and translation strings.
Chris@0 127 if ($translations) {
Chris@0 128 foreach ($translations as $source => $target) {
Chris@0 129 $text .= 'msgid "' . $source . '"' . "\n";
Chris@0 130 $text .= 'msgstr "' . $target . '"' . "\n";
Chris@0 131 }
Chris@0 132 }
Chris@0 133
Chris@18 134 \Drupal::service('file_system')->prepareDirectory($path, FileSystemInterface::CREATE_DIRECTORY);
Chris@0 135 $file = File::create([
Chris@0 136 'uid' => 1,
Chris@0 137 'filename' => $filename,
Chris@0 138 'uri' => $path . '/' . $filename,
Chris@0 139 'filemime' => 'text/x-gettext-translation',
Chris@0 140 'timestamp' => $timestamp,
Chris@0 141 'status' => FILE_STATUS_PERMANENT,
Chris@0 142 ]);
Chris@0 143 file_put_contents($file->getFileUri(), $po_header . $text);
Chris@14 144 touch(\Drupal::service('file_system')->realpath($file->getFileUri()), $timestamp);
Chris@0 145 $file->save();
Chris@0 146 }
Chris@0 147
Chris@0 148 /**
Chris@0 149 * Setup the environment containing local and remote translation files.
Chris@0 150 *
Chris@0 151 * Update tests require a simulated environment for local and remote files.
Chris@0 152 * Normally remote files are located at a remote server (e.g. ftp.drupal.org).
Chris@0 153 * For testing we can not rely on this. A directory in the file system of the
Chris@0 154 * test site is designated for remote files and is addressed using an absolute
Chris@0 155 * URL. Because Drupal does not allow files with a po extension to be accessed
Chris@0 156 * (denied in .htaccess) the translation files get a _po extension. Another
Chris@0 157 * directory is designated for local translation files.
Chris@0 158 *
Chris@0 159 * The environment is set up with the following files. File creation times are
Chris@0 160 * set to create different variations in test conditions.
Chris@0 161 * contrib_module_one
Chris@0 162 * - remote file: timestamp new
Chris@0 163 * - local file: timestamp old
Chris@0 164 * contrib_module_two
Chris@0 165 * - remote file: timestamp old
Chris@0 166 * - local file: timestamp new
Chris@0 167 * contrib_module_three
Chris@0 168 * - remote file: timestamp old
Chris@0 169 * - local file: timestamp old
Chris@0 170 * custom_module_one
Chris@0 171 * - local file: timestamp new
Chris@0 172 * Time stamp of current translation set by setCurrentTranslations() is always
Chris@0 173 * timestamp medium. This makes it easy to predict which translation will be
Chris@0 174 * imported.
Chris@0 175 */
Chris@0 176 protected function setTranslationFiles() {
Chris@0 177 $config = $this->config('locale.settings');
Chris@0 178
Chris@0 179 // A flag is set to let the locale_test module replace the project data with
Chris@0 180 // a set of test projects which match the below project files.
Chris@0 181 \Drupal::state()->set('locale.test_projects_alter', TRUE);
Chris@0 182 \Drupal::state()->set('locale.remove_core_project', FALSE);
Chris@0 183
Chris@0 184 // Setup the environment.
Chris@0 185 $public_path = PublicStream::basePath();
Chris@0 186 $this->setTranslationsDirectory($public_path . '/local');
Chris@0 187 $config->set('translation.default_filename', '%project-%version.%language._po')->save();
Chris@0 188
Chris@0 189 // Setting up sets of translations for the translation files.
Chris@0 190 $translations_one = ['January' => 'Januar_1', 'February' => 'Februar_1', 'March' => 'Marz_1'];
Chris@0 191 $translations_two = ['February' => 'Februar_2', 'March' => 'Marz_2', 'April' => 'April_2'];
Chris@0 192 $translations_three = ['April' => 'April_3', 'May' => 'Mai_3', 'June' => 'Juni_3'];
Chris@0 193
Chris@0 194 // Add a number of files to the local file system to serve as remote
Chris@0 195 // translation server and match the project definitions set in
Chris@0 196 // locale_test_locale_translation_projects_alter().
Chris@0 197 $this->makePoFile('remote/8.x/contrib_module_one', 'contrib_module_one-8.x-1.1.de._po', $this->timestampNew, $translations_one);
Chris@0 198 $this->makePoFile('remote/8.x/contrib_module_two', 'contrib_module_two-8.x-2.0-beta4.de._po', $this->timestampOld, $translations_two);
Chris@0 199 $this->makePoFile('remote/8.x/contrib_module_three', 'contrib_module_three-8.x-1.0.de._po', $this->timestampOld, $translations_three);
Chris@0 200
Chris@0 201 // Add a number of files to the local file system to serve as local
Chris@0 202 // translation files and match the project definitions set in
Chris@0 203 // locale_test_locale_translation_projects_alter().
Chris@0 204 $this->makePoFile('local', 'contrib_module_one-8.x-1.1.de._po', $this->timestampOld, $translations_one);
Chris@0 205 $this->makePoFile('local', 'contrib_module_two-8.x-2.0-beta4.de._po', $this->timestampNew, $translations_two);
Chris@0 206 $this->makePoFile('local', 'contrib_module_three-8.x-1.0.de._po', $this->timestampOld, $translations_three);
Chris@0 207 $this->makePoFile('local', 'custom_module_one.de.po', $this->timestampNew);
Chris@0 208 }
Chris@0 209
Chris@0 210 /**
Chris@0 211 * Setup existing translations in the database and set up the status of
Chris@0 212 * existing translations.
Chris@0 213 */
Chris@0 214 protected function setCurrentTranslations() {
Chris@0 215 // Add non customized translations to the database.
Chris@0 216 $langcode = 'de';
Chris@0 217 $context = '';
Chris@0 218 $non_customized_translations = [
Chris@0 219 'March' => 'Marz',
Chris@0 220 'June' => 'Juni',
Chris@0 221 ];
Chris@0 222 foreach ($non_customized_translations as $source => $translation) {
Chris@0 223 $string = $this->container->get('locale.storage')->createString([
Chris@0 224 'source' => $source,
Chris@0 225 'context' => $context,
Chris@0 226 ])
Chris@0 227 ->save();
Chris@0 228 $this->container->get('locale.storage')->createTranslation([
Chris@0 229 'lid' => $string->getId(),
Chris@0 230 'language' => $langcode,
Chris@0 231 'translation' => $translation,
Chris@0 232 'customized' => LOCALE_NOT_CUSTOMIZED,
Chris@0 233 ])->save();
Chris@0 234 }
Chris@0 235
Chris@0 236 // Add customized translations to the database.
Chris@0 237 $customized_translations = [
Chris@0 238 'January' => 'Januar_customized',
Chris@0 239 'February' => 'Februar_customized',
Chris@0 240 'May' => 'Mai_customized',
Chris@0 241 ];
Chris@0 242 foreach ($customized_translations as $source => $translation) {
Chris@0 243 $string = $this->container->get('locale.storage')->createString([
Chris@0 244 'source' => $source,
Chris@0 245 'context' => $context,
Chris@0 246 ])
Chris@0 247 ->save();
Chris@0 248 $this->container->get('locale.storage')->createTranslation([
Chris@0 249 'lid' => $string->getId(),
Chris@0 250 'language' => $langcode,
Chris@0 251 'translation' => $translation,
Chris@0 252 'customized' => LOCALE_CUSTOMIZED,
Chris@0 253 ])->save();
Chris@0 254 }
Chris@0 255
Chris@0 256 // Add a state of current translations in locale_files.
Chris@0 257 $default = [
Chris@0 258 'langcode' => $langcode,
Chris@0 259 'uri' => '',
Chris@0 260 'timestamp' => $this->timestampMedium,
Chris@0 261 'last_checked' => $this->timestampMedium,
Chris@0 262 ];
Chris@0 263 $data[] = [
Chris@0 264 'project' => 'contrib_module_one',
Chris@0 265 'filename' => 'contrib_module_one-8.x-1.1.de._po',
Chris@0 266 'version' => '8.x-1.1',
Chris@0 267 ];
Chris@0 268 $data[] = [
Chris@0 269 'project' => 'contrib_module_two',
Chris@0 270 'filename' => 'contrib_module_two-8.x-2.0-beta4.de._po',
Chris@0 271 'version' => '8.x-2.0-beta4',
Chris@0 272 ];
Chris@0 273 $data[] = [
Chris@0 274 'project' => 'contrib_module_three',
Chris@0 275 'filename' => 'contrib_module_three-8.x-1.0.de._po',
Chris@0 276 'version' => '8.x-1.0',
Chris@0 277 ];
Chris@0 278 $data[] = [
Chris@0 279 'project' => 'custom_module_one',
Chris@0 280 'filename' => 'custom_module_one.de.po',
Chris@0 281 'version' => '',
Chris@0 282 ];
Chris@18 283 $connection = Database::getConnection();
Chris@0 284 foreach ($data as $file) {
Chris@0 285 $file = array_merge($default, $file);
Chris@18 286 $connection->insert('locale_file')->fields($file)->execute();
Chris@0 287 }
Chris@0 288 }
Chris@0 289
Chris@0 290 /**
Chris@0 291 * Checks the translation of a string.
Chris@0 292 *
Chris@0 293 * @param string $source
Chris@0 294 * Translation source string.
Chris@0 295 * @param string $translation
Chris@0 296 * Translation to check. Use empty string to check for a not existing
Chris@0 297 * translation.
Chris@0 298 * @param string $langcode
Chris@0 299 * Language code of the language to translate to.
Chris@0 300 * @param string $message
Chris@0 301 * (optional) A message to display with the assertion.
Chris@0 302 */
Chris@0 303 protected function assertTranslation($source, $translation, $langcode, $message = '') {
Chris@0 304 $db_translation = db_query('SELECT translation FROM {locales_target} lt INNER JOIN {locales_source} ls ON ls.lid = lt.lid WHERE ls.source = :source AND lt.language = :langcode', [':source' => $source, ':langcode' => $langcode])->fetchField();
Chris@0 305 $db_translation = $db_translation == FALSE ? '' : $db_translation;
Chris@0 306 $this->assertEqual($translation, $db_translation, $message ? $message : format_string('Correct translation of %source (%language)', ['%source' => $source, '%language' => $langcode]));
Chris@0 307 }
Chris@0 308
Chris@0 309 }