1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
<?php
/**
* @file
* Contains L10nUpdateTest.
*/
/**
* Tests for update translations.
*/
class L10nUpdateTestBase extends DrupalWebTestCase {
/**
* Timestamp for an old translation.
*
* @var integer
*/
protected $timestamp_old;
/**
* Timestamp for a medium aged translation.
*
* @var integer
*/
protected $timestamp_medium;
/**
* Timestamp for a new translation.
*
* @var integer
*/
protected $timestamp_new;
function setUp() {
parent::setUp('update', 'locale', 'l10n_update', 'l10n_update_test');
// Setup timestamps to identify old and new translation sources.
$this->timestamp_old = REQUEST_TIME - 300;
$this->timestamp_medium = REQUEST_TIME - 200;
$this->timestamp_new = REQUEST_TIME - 100;
$this->timestamp_now = REQUEST_TIME;
}
/**
* Sets the value of the default translations directory.
*
* @param string $path
* Path of the translations directory relative to the drupal installation
* directory.
*/
protected function setTranslationsDirectory($path) {
file_prepare_directory($path, FILE_CREATE_DIRECTORY);
variable_set('l10n_update_download_store', $path);
}
/**
* Adds a language.
*
* @param $langcode
* The language code of the language to add.
*/
protected function addLanguage($langcode) {
$edit = array('langcode' => $langcode);
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
drupal_static_reset('language_list');
$languages = language_list();
$this->assertTrue(isset($languages[$langcode]), format_string('Language %langcode added.', array('%langcode' => $langcode)));
}
/**
* Creates a translation file and tests its timestamp.
*
* @param string $path
* Path of the file relative to the public file path.
* @param string $filename
* Name of the file to create.
* @param integer $timestamp
* Timestamp to set the file to. Defaults to current time.
* @param array $translations
* Array of source/target value translation strings. Only singular strings
* are supported, no plurals. No double quotes are allowed in source and
* translations strings.
*/
protected function makePoFile($path, $filename, $timestamp = NULL, $translations = array()) {
$timestamp = $timestamp ? $timestamp : REQUEST_TIME;
$path = 'public://' . $path;
$text = '';
$po_header = <<<EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 7\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
EOF;
// Convert array of translations to Gettext source and translation strings.
if ($translations) {
foreach ($translations as $source => $target) {
$text .= 'msgid "'. $source . '"' . "\n";
$text .= 'msgstr "'. $target . '"' . "\n";
}
}
file_prepare_directory($path, FILE_CREATE_DIRECTORY);
$file = (object) array(
'uid' => 1,
'filename' => $filename,
'uri' => $path . '/' . $filename,
'filemime' => 'text/x-gettext-translation',
'timestamp' => $timestamp,
'status' => FILE_STATUS_PERMANENT,
);
file_put_contents($file->uri, $po_header . $text);
touch(drupal_realpath($file->uri), $timestamp);
file_save($file);
}
/**
* Setup the environment containing local and remote translation files.
*
* Update tests require a simulated environment for local and remote files.
* Normally remote files are located at a remote server (e.g. ftp.drupal.org).
* For testing we can not rely on this. A directory in the file system of the
* test site is designated for remote files and is addressed using an absolute
* URL. Because Drupal does not allow files with a po extension to be accessed
* (denied in .htaccess) the translation files get a _po extension. Another
* directory is designated for local translation files.
*
* The environment is set up with the following files. File creation times are
* set to create different variations in test conditions.
* contrib_module_one
* - remote file: timestamp new
* - local file: timestamp old
* - current: timestamp medium
* contrib_module_two
* - remote file: timestamp old
* - local file: timestamp new
* - current: timestamp medium
* contrib_module_three
* - remote file: timestamp old
* - local file: timestamp old
* - current: timestamp medium
* custom_module_one
* - local file: timestamp new
* - current: timestamp medium
* Time stamp of current translation set by setCurrentTranslations() is always
* timestamp medium. This makes it easy to predict which translation will be
* imported.
*/
protected function setTranslationFiles() {
// A flag is set to let the l10n_update_test module replace the project data with
// a set of test projects which match the below project files.
variable_set('l10n_update_test_projects_alter', TRUE);
// Setup the environment.
$public_path = drupal_realpath('public://');
$this->setTranslationsDirectory($public_path . '/local');
variable_set('l10n_update_default_filename', '%project-%release.%language._po');
// Setting up sets of translations for the translation files.
$translations_one = array('January' => 'Januar_1', 'February' => 'Februar_1', 'March' => 'Marz_1');
$translations_two = array( 'February' => 'Februar_2', 'March' => 'Marz_2', 'April' => 'April_2');
$translations_three = array('April' => 'April_3', 'May' => 'Mai_3', 'June' => 'Juni_3');
// Add a number of files to the local file system to serve as remote
// translation server and match the project definitions set in
// l10n_update_test_l10n_update_projects_alter().
$this->makePoFile('remote/7.x/contrib_module_one', 'contrib_module_one-7.x-1.1.de._po', $this->timestamp_new, $translations_one);
$this->makePoFile('remote/7.x/contrib_module_two', 'contrib_module_two-7.x-2.0-beta4.de._po', $this->timestamp_old, $translations_two);
$this->makePoFile('remote/7.x/contrib_module_three', 'contrib_module_three-7.x-1.0.de._po', $this->timestamp_old, $translations_three);
// Add a number of files to the local file system to serve as local
// translation files and match the project definitions set in
// l10n_update_test_l10n_update_projects_alter().
$this->makePoFile('local', 'contrib_module_one-7.x-1.1.de._po', $this->timestamp_old, $translations_one);
$this->makePoFile('local', 'contrib_module_two-7.x-2.0-beta4.de._po', $this->timestamp_new, $translations_two);
$this->makePoFile('local', 'contrib_module_three-7.x-1.0.de._po', $this->timestamp_old, $translations_three);
$this->makePoFile('local', 'custom_module_one.de.po', $this->timestamp_new);
}
/**
* Setup existing translations in the database and set up the status of
* existing translations.
*/
protected function setCurrentTranslations() {
// Setup to add German translations to the database.
$langcode = 'de';
$writer = new PoDatabaseWriter();
$writer->setLangcode($langcode);
$writer->setOptions(array(
'overwrite_options' => array(
'not_customized' => TRUE,
'customized' => TRUE,
),
));
// Add non customized translations to the database.
$writer->setOptions(array('customized' => L10N_UPDATE_NOT_CUSTOMIZED));
$non_customized_translations = array(
'March' => 'Marz',
'June' => 'Juni',
);
foreach ($non_customized_translations as $source => $translation) {
$poItem = new PoItem();
$poItem->setFromArray(array(
'source' => $source,
'translation' => $translation,
));
$writer->writeItem($poItem);
}
// Add customized translations to the database.
$writer->setOptions(array('customized' => L10N_UPDATE_CUSTOMIZED));
$customized_translations = array(
'January' => 'Januar_customized',
'February' => 'Februar_customized',
'May' => 'Mai_customized',
);
foreach ($customized_translations as $source => $translation) {
$poItem = new PoItem();
$poItem->setFromArray(array(
'source' => $source,
'translation' => $translation,
));
$writer->writeItem($poItem);
}
// Add a state of current translations in l10n_update_files.
$default = array(
'language' => $langcode,
'uri' => '',
'timestamp' => $this->timestamp_medium,
'last_checked' => $this->timestamp_medium,
);
$data[] = array(
'project' => 'contrib_module_one',
'filename' => 'contrib_module_one-7.x-1.1.de._po',
'version' => '7.x-1.1',
);
$data[] = array(
'project' => 'contrib_module_two',
'filename' => 'contrib_module_two-7.x-2.0-beta4.de._po',
'version' => '7.x-2.0-beta4',
);
$data[] = array(
'project' => 'contrib_module_three',
'filename' => 'contrib_module_three-7.x-1.0.de._po',
'version' => '7.x-1.0',
);
$data[] = array(
'project' => 'custom_module_one',
'filename' => 'custom_module_one.de.po',
'version' => '',
);
foreach ($data as $file) {
$file = (object) array_merge($default, $file);
drupal_write_record('l10n_update_file', $file);
}
}
/**
* Checks the translation of a string.
*
* @param string $source
* Translation source string
* @param string $translation
* Translation to check. Use empty string to check for a not existing
* translation.
* @param string $langcode
* Language code of the language to translate to.
* @param string $message
* (optional) A message to display with the assertion.
*/
protected function assertTranslation($source, $translation, $langcode, $message = '') {
$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', array(':source' => $source, ':langcode' => $langcode))->fetchField();
$db_translation = $db_translation == FALSE ? '' : $db_translation;
$this->assertEqual($translation, $db_translation, $message ? $message : format_string('Correct translation of %source (%language)', array('%source' => $source, '%language' => $langcode)));
}
}
|