annotate core/modules/locale/locale.compare.inc @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /**
Chris@0 4 * @file
Chris@0 5 * The API for comparing project translation status with available translation.
Chris@0 6 */
Chris@0 7
Chris@0 8 use Drupal\Core\Utility\ProjectInfo;
Chris@0 9
Chris@0 10 /**
Chris@0 11 * Load common APIs.
Chris@0 12 */
Chris@0 13 // @todo Combine functions differently in files to avoid unnecessary includes.
Chris@0 14 // Follow-up issue: https://www.drupal.org/node/1834298.
Chris@0 15 require_once __DIR__ . '/locale.translation.inc';
Chris@0 16
Chris@0 17 /**
Chris@0 18 * Clear the project data table.
Chris@0 19 */
Chris@0 20 function locale_translation_flush_projects() {
Chris@0 21 \Drupal::service('locale.project')->deleteAll();
Chris@0 22 }
Chris@0 23
Chris@0 24 /**
Chris@0 25 * Builds list of projects and stores the result in the database.
Chris@0 26 *
Chris@0 27 * The project data is based on the project list supplied by the Update module.
Chris@0 28 * Only the properties required by Locale module is included and additional
Chris@0 29 * (custom) modules and translation server data is added.
Chris@0 30 *
Chris@0 31 * In case the Update module is disabled this function will return an empty
Chris@0 32 * array.
Chris@0 33 *
Chris@0 34 * @return array
Chris@0 35 * Array of project data:
Chris@0 36 * - "name": Project system name.
Chris@0 37 * - "project_type": Project type, e.g. 'module', 'theme'.
Chris@0 38 * - "core": Core release version, e.g. 8.x
Chris@0 39 * - "version": Project release version, e.g. 8.x-1.0
Chris@0 40 * See http://drupalcode.org/project/drupalorg.git/blob/refs/heads/7.x-3.x:/drupalorg_project/plugins/release_packager/DrupalorgProjectPackageRelease.class.php#l219
Chris@0 41 * for how the version strings are created.
Chris@0 42 * - "server_pattern": Translation server po file pattern.
Chris@0 43 * - "status": Project status, 1 = enabled.
Chris@0 44 */
Chris@0 45 function locale_translation_build_projects() {
Chris@0 46 // Get the project list based on .info.yml files.
Chris@0 47 $projects = locale_translation_project_list();
Chris@0 48
Chris@0 49 // Mark all previous projects as disabled and store new project data.
Chris@0 50 \Drupal::service('locale.project')->disableAll();
Chris@0 51
Chris@0 52 $default_server = locale_translation_default_translation_server();
Chris@0 53
Chris@0 54 foreach ($projects as $name => $data) {
Chris@0 55 // For dev releases, remove the '-dev' part and trust the translation server
Chris@0 56 // to fall back to the latest stable release for that branch.
Chris@0 57 if (isset($data['info']['version']) && strpos($data['info']['version'], '-dev')) {
Chris@0 58 if (preg_match("/^(\d+\.x-\d+\.).*$/", $data['info']['version'], $matches)) {
Chris@0 59 // Example matches: 8.x-1.x-dev, 8.x-1.0-alpha1+5-dev => 8.x-1.x
Chris@0 60 $data['info']['version'] = $matches[1] . 'x';
Chris@0 61 }
Chris@0 62 elseif (preg_match("/^(\d+\.\d+\.).*$/", $data['info']['version'], $matches)) {
Chris@0 63 // Example match: 8.0.0-dev => 8.0.x (Drupal core)
Chris@0 64 $data['info']['version'] = $matches[1] . 'x';
Chris@0 65 }
Chris@0 66 }
Chris@0 67
Chris@0 68 // For every project store information.
Chris@0 69 $data += [
Chris@0 70 'name' => $name,
Chris@0 71 'version' => isset($data['info']['version']) ? $data['info']['version'] : '',
Chris@0 72 'core' => isset($data['info']['core']) ? $data['info']['core'] : \Drupal::CORE_COMPATIBILITY,
Chris@0 73 // A project can provide the path and filename pattern to download the
Chris@0 74 // gettext file. Use the default if not.
Chris@0 75 'server_pattern' => isset($data['info']['interface translation server pattern']) && $data['info']['interface translation server pattern'] ? $data['info']['interface translation server pattern'] : $default_server['pattern'],
Chris@0 76 'status' => !empty($data['project_status']) ? 1 : 0,
Chris@0 77 ];
Chris@0 78
Chris@0 79 $project = (object) $data;
Chris@0 80 $projects[$name] = $project;
Chris@0 81
Chris@0 82 // Create or update the project record.
Chris@0 83 \Drupal::service('locale.project')->set($project->name, $data);
Chris@0 84
Chris@0 85 // Invalidate the cache of translatable projects.
Chris@0 86 locale_translation_clear_cache_projects();
Chris@0 87 }
Chris@0 88 return $projects;
Chris@0 89 }
Chris@0 90
Chris@0 91 /**
Chris@0 92 * Fetch an array of projects for translation update.
Chris@0 93 *
Chris@0 94 * @return array
Chris@0 95 * Array of project data including .info.yml file data.
Chris@0 96 */
Chris@0 97 function locale_translation_project_list() {
Chris@0 98 $projects = &drupal_static(__FUNCTION__, []);
Chris@0 99 if (empty($projects)) {
Chris@0 100 $projects = [];
Chris@0 101
Chris@0 102 $additional_whitelist = [
Chris@0 103 'interface translation project',
Chris@0 104 'interface translation server pattern',
Chris@0 105 ];
Chris@0 106 $module_data = _locale_translation_prepare_project_list(system_rebuild_module_data(), 'module');
Chris@0 107 $theme_data = _locale_translation_prepare_project_list(\Drupal::service('theme_handler')->rebuildThemeData(), 'theme');
Chris@0 108 $project_info = new ProjectInfo();
Chris@0 109 $project_info->processInfoList($projects, $module_data, 'module', TRUE, $additional_whitelist);
Chris@0 110 $project_info->processInfoList($projects, $theme_data, 'theme', TRUE, $additional_whitelist);
Chris@0 111
Chris@0 112 // Allow other modules to alter projects before fetching and comparing.
Chris@0 113 \Drupal::moduleHandler()->alter('locale_translation_projects', $projects);
Chris@0 114 }
Chris@0 115 return $projects;
Chris@0 116 }
Chris@0 117
Chris@0 118 /**
Chris@0 119 * Prepare module and theme data.
Chris@0 120 *
Chris@0 121 * Modify .info.yml file data before it is processed by
Chris@0 122 * \Drupal\Core\Utility\ProjectInfo->processInfoList(). In order for
Chris@0 123 * \Drupal\Core\Utility\ProjectInfo->processInfoList() to recognize a project,
Chris@0 124 * it requires the 'project' parameter in the .info.yml file data.
Chris@0 125 *
Chris@0 126 * Custom modules or themes can bring their own gettext translation file. To
Chris@0 127 * enable import of this file the module or theme defines "interface translation
Chris@0 128 * project = myproject" in its .info.yml file. This function will add a project
Chris@0 129 * "myproject" to the info data.
Chris@0 130 *
Chris@0 131 * @param \Drupal\Core\Extension\Extension[] $data
Chris@0 132 * Array of .info.yml file data.
Chris@0 133 * @param string $type
Chris@0 134 * The project type. i.e. module, theme.
Chris@0 135 *
Chris@0 136 * @return array
Chris@0 137 * Array of .info.yml file data.
Chris@0 138 */
Chris@0 139 function _locale_translation_prepare_project_list($data, $type) {
Chris@0 140 foreach ($data as $name => $file) {
Chris@0 141 // Include interface translation projects. To allow
Chris@0 142 // \Drupal\Core\Utility\ProjectInfo->processInfoList() to identify this as
Chris@0 143 // a project the 'project' property is filled with the
Chris@0 144 // 'interface translation project' value.
Chris@0 145 if (isset($file->info['interface translation project'])) {
Chris@0 146 $data[$name]->info['project'] = $file->info['interface translation project'];
Chris@0 147 }
Chris@0 148 }
Chris@0 149 return $data;
Chris@0 150 }
Chris@0 151
Chris@0 152 /**
Chris@0 153 * Retrieve data for default server.
Chris@0 154 *
Chris@0 155 * @return array
Chris@0 156 * Array of server parameters:
Chris@0 157 * - "pattern": URI containing po file pattern.
Chris@0 158 */
Chris@0 159 function locale_translation_default_translation_server() {
Chris@0 160 $pattern = \Drupal::config('locale.settings')->get('translation.default_server_pattern');
Chris@0 161 // An additional check is required here. During the upgrade process
Chris@0 162 // \Drupal::config()->get() returns NULL. We use the defined value as
Chris@0 163 // fallback.
Chris@17 164 $pattern = $pattern ? $pattern : LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN;
Chris@0 165
Chris@0 166 return [
Chris@0 167 'pattern' => $pattern,
Chris@0 168 ];
Chris@0 169 }
Chris@0 170
Chris@0 171 /**
Chris@0 172 * Check for the latest release of project translations.
Chris@0 173 *
Chris@0 174 * @param array $projects
Chris@0 175 * Array of project names to check. Defaults to all translatable projects.
Chris@0 176 * @param string $langcodes
Chris@0 177 * Array of language codes. Defaults to all translatable languages.
Chris@0 178 *
Chris@0 179 * @return array
Chris@0 180 * Available sources indexed by project and language.
Chris@0 181 *
Chris@0 182 * @todo Return batch or NULL.
Chris@0 183 */
Chris@0 184 function locale_translation_check_projects($projects = [], $langcodes = []) {
Chris@0 185 if (locale_translation_use_remote_source()) {
Chris@0 186 // Retrieve the status of both remote and local translation sources by
Chris@0 187 // using a batch process.
Chris@0 188 locale_translation_check_projects_batch($projects, $langcodes);
Chris@0 189 }
Chris@0 190 else {
Chris@0 191 // Retrieve and save the status of local translations only.
Chris@0 192 locale_translation_check_projects_local($projects, $langcodes);
Chris@0 193 \Drupal::state()->set('locale.translation_last_checked', REQUEST_TIME);
Chris@0 194 }
Chris@0 195 }
Chris@0 196
Chris@0 197 /**
Chris@0 198 * Gets and stores the status and timestamp of remote po files.
Chris@0 199 *
Chris@0 200 * A batch process is used to check for po files at remote locations and (when
Chris@0 201 * configured) to check for po files in the local file system. The most recent
Chris@0 202 * translation source states are stored in the state variable
Chris@0 203 * 'locale.translation_status'.
Chris@0 204 *
Chris@0 205 * @param array $projects
Chris@0 206 * Array of project names to check. Defaults to all translatable projects.
Chris@0 207 * @param string $langcodes
Chris@0 208 * Array of language codes. Defaults to all translatable languages.
Chris@0 209 */
Chris@0 210 function locale_translation_check_projects_batch($projects = [], $langcodes = []) {
Chris@0 211 // Build and set the batch process.
Chris@0 212 $batch = locale_translation_batch_status_build($projects, $langcodes);
Chris@0 213 batch_set($batch);
Chris@0 214 }
Chris@0 215
Chris@0 216 /**
Chris@0 217 * Builds a batch to get the status of remote and local translation files.
Chris@0 218 *
Chris@0 219 * The batch process fetches the state of both local and (if configured) remote
Chris@0 220 * translation files. The data of the most recent translation is stored per
Chris@0 221 * per project and per language. This data is stored in a state variable
Chris@0 222 * 'locale.translation_status'. The timestamp it was last updated is stored
Chris@0 223 * in the state variable 'locale.translation_last_checked'.
Chris@0 224 *
Chris@0 225 * @param array $projects
Chris@0 226 * Array of project names for which to check the state of translation files.
Chris@0 227 * Defaults to all translatable projects.
Chris@0 228 * @param array $langcodes
Chris@0 229 * Array of language codes. Defaults to all translatable languages.
Chris@0 230 *
Chris@0 231 * @return array
Chris@0 232 * Batch definition array.
Chris@0 233 */
Chris@0 234 function locale_translation_batch_status_build($projects = [], $langcodes = []) {
Chris@0 235 $projects = $projects ? $projects : array_keys(locale_translation_get_projects());
Chris@0 236 $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
Chris@0 237 $options = _locale_translation_default_update_options();
Chris@0 238
Chris@0 239 $operations = _locale_translation_batch_status_operations($projects, $langcodes, $options);
Chris@0 240
Chris@0 241 $batch = [
Chris@0 242 'operations' => $operations,
Chris@0 243 'title' => t('Checking translations'),
Chris@0 244 'progress_message' => '',
Chris@0 245 'finished' => 'locale_translation_batch_status_finished',
Chris@0 246 'error_message' => t('Error checking translation updates.'),
Chris@0 247 'file' => drupal_get_path('module', 'locale') . '/locale.batch.inc',
Chris@0 248 ];
Chris@0 249 return $batch;
Chris@0 250 }
Chris@0 251
Chris@0 252 /**
Chris@0 253 * Helper function to construct batch operations checking remote translation
Chris@0 254 * status.
Chris@0 255 *
Chris@0 256 * @param array $projects
Chris@0 257 * Array of project names to be processed.
Chris@0 258 * @param array $langcodes
Chris@0 259 * Array of language codes.
Chris@0 260 * @param array $options
Chris@0 261 * Batch processing options.
Chris@0 262 *
Chris@0 263 * @return array
Chris@0 264 * Array of batch operations.
Chris@0 265 */
Chris@0 266 function _locale_translation_batch_status_operations($projects, $langcodes, $options = []) {
Chris@0 267 $operations = [];
Chris@0 268
Chris@0 269 foreach ($projects as $project) {
Chris@0 270 foreach ($langcodes as $langcode) {
Chris@0 271 // Check status of local and remote translation sources.
Chris@0 272 $operations[] = ['locale_translation_batch_status_check', [$project, $langcode, $options]];
Chris@0 273 }
Chris@0 274 }
Chris@0 275
Chris@0 276 return $operations;
Chris@0 277 }
Chris@0 278
Chris@0 279 /**
Chris@0 280 * Check and store the status and timestamp of local po files.
Chris@0 281 *
Chris@0 282 * Only po files in the local file system are checked. Any remote translation
Chris@0 283 * files will be ignored.
Chris@0 284 *
Chris@0 285 * Projects may contain a server_pattern option containing a pattern of the
Chris@0 286 * path to the po source files. If no server_pattern is defined the default
Chris@0 287 * translation directory is checked for the po file. When a server_pattern is
Chris@0 288 * defined the specified location is checked. The server_pattern can be set in
Chris@0 289 * the module's .info.yml file or by using
Chris@0 290 * hook_locale_translation_projects_alter().
Chris@0 291 *
Chris@0 292 * @param array $projects
Chris@0 293 * Array of project names for which to check the state of translation files.
Chris@0 294 * Defaults to all translatable projects.
Chris@0 295 * @param array $langcodes
Chris@0 296 * Array of language codes. Defaults to all translatable languages.
Chris@0 297 */
Chris@0 298 function locale_translation_check_projects_local($projects = [], $langcodes = []) {
Chris@0 299 $projects = locale_translation_get_projects($projects);
Chris@0 300 $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
Chris@0 301
Chris@0 302 // For each project and each language we check if a local po file is
Chris@0 303 // available. When found the source object is updated with the appropriate
Chris@0 304 // type and timestamp of the po file.
Chris@0 305 foreach ($projects as $name => $project) {
Chris@0 306 foreach ($langcodes as $langcode) {
Chris@0 307 $source = locale_translation_source_build($project, $langcode);
Chris@0 308 $file = locale_translation_source_check_file($source);
Chris@0 309 locale_translation_status_save($name, $langcode, LOCALE_TRANSLATION_LOCAL, $file);
Chris@0 310 }
Chris@0 311 }
Chris@0 312 }