summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwebchick2012-08-26 15:45:11 (GMT)
committerwebchick2012-08-26 15:45:11 (GMT)
commit62b1f23eec32842b75180ebe19b38adad48ee5c2 (patch)
tree5081638b0c6cfa9535b1f37607e332ce5ab451a8
parentb93818ac56fa897f32329a0722d695c617de5484 (diff)
Issue #1627006 by Sutharsan, Berdir, lazysoundsystem, andypost, webflo, jepSter, MantasK, Gábor Hojtsy: Collect project information for translation update.
-rw-r--r--core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php77
-rw-r--r--core/modules/locale/locale.api.php111
-rw-r--r--core/modules/locale/locale.compare.inc265
-rw-r--r--core/modules/locale/locale.install159
-rw-r--r--core/modules/locale/tests/modules/locale_test/locale_test.info10
-rw-r--r--core/modules/locale/tests/modules/locale_test/locale_test.module27
-rw-r--r--core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.info10
-rw-r--r--core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.module6
8 files changed, 665 insertions, 0 deletions
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php
new file mode 100644
index 0000000..25d5e71
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\locale\Tests\LocaleCompareTest.
+ */
+
+namespace Drupal\locale\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests for comparing status of existing project translations with available translations.
+ */
+class LocaleCompareTest extends WebTestBase {
+
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = array('update', 'locale', 'locale_test');
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Compare project states',
+ 'description' => 'Tests for comparing status of existing project translations with available translations.',
+ 'group' => 'Locale',
+ );
+ }
+
+ /**
+ * Test for translation status storage and translation status comparison.
+ */
+ function testLocaleCompare() {
+ // Create and login user.
+ $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer languages', 'access administration pages'));
+ $this->drupalLogin($admin_user);
+
+ module_load_include('compare.inc', 'locale');
+
+ // Check if hidden modules are not included.
+ $projects = locale_translation_project_list();
+ $this->assertFalse(isset($projects['locale_test']), 'Hidden module not found');
+
+ // Make the test modules look like a normal custom module. i.e. make the
+ // modules not hidden. locale_test_system_info_alter() modifies the project
+ // info of the locale_test and locale_test_disabled modules.
+ variable_set('locale_translation_test_system_info_alter', TRUE);
+
+ // Check if interface translation data is collected from hook_info.
+ drupal_static_reset('locale_translation_project_list');
+ $projects = locale_translation_project_list();
+ $this->assertEqual($projects['locale_test']['info']['interface translation server pattern'], 'core/modules/locale/test/modules/locale_test/%project-%version.%language.po', 'Interface translation parameter found in project info.');
+ $this->assertEqual($projects['locale_test']['name'] , 'locale_test', format_string('%key found in project info.', array('%key' => 'interface translation project')));
+
+ // Check if disabled modules are detected.
+ variable_set('locale_translation_check_disabled', TRUE);
+ drupal_static_reset('locale_translation_project_list');
+ $projects = locale_translation_project_list();
+ $this->assertTrue(isset($projects['locale_test_disabled']), 'Disabled module found');
+
+ // Check the fully processed list of project data of both enabled and
+ // disabled modules.
+ variable_set('locale_translation_check_disabled', TRUE);
+ drupal_static_reset('locale_translation_project_list');
+ $projects = locale_translation_get_projects();
+ $this->assertEqual($projects['drupal']->name, 'drupal', 'Core project found');
+ $this->assertEqual($projects['locale_test']->server_pattern, 'core/modules/locale/test/modules/locale_test/%project-%version.%language.po', 'Interface translation parameter found in project info.');
+ $this->assertEqual($projects['locale_test_disabled']->status, '0', 'Disabled module found');
+ variable_del('locale_translation_check_disabled');
+
+ // Return the locale test modules back to their hidden state.
+ variable_del('locale_translation_test_system_info_alter');
+ }
+
+}
diff --git a/core/modules/locale/locale.api.php b/core/modules/locale/locale.api.php
new file mode 100644
index 0000000..9aabb0e
--- /dev/null
+++ b/core/modules/locale/locale.api.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Locale module.
+ */
+
+/**
+ * @defgroup interface_translation_properties Interface translation properties
+ * @{
+ *
+ * .info file properties for interface translation settings.
+ *
+ * Modules hosted on drupal.org, a project definition is automatically added to
+ * the .info file. Only modules with this project definition are discovered by
+ * the update module and use it to check for new releases. Locale module uses
+ * the same data to build a list of module to check for new translations.
+ * Therefore modules not hosted at drupal.org, such as custom modules, custom
+ * themes, features and distributions, need a way to identify themselves to
+ * the Locale module if they have translations that require to be updated.
+ *
+ * Custom module which contain new strings should provide po file(s) containing
+ * source strings and string translations in gettext format. The translation
+ * file can be located both local and remote. Use the following .info file
+ * properties to inform Locale module to load and import the translations.
+ *
+ * Example .info file properties for a custom module with a po file located in
+ * the module's folder.
+ * @code
+ * interface translation project = example_module
+ * interface translation server pattern = sites/example.com/modules/custom/example_module/%project-%version.%language.po
+ * @endcode
+ *
+ * Multiple custom modules or themes sharing the same po file should have
+ * matching definitions. Such as modules and sub-modules or multiple modules in
+ * the same project/code tree. Both "interface translation project" and
+ * "interface translation server pattern" definitions of these modules should match.
+ *
+ * Example .info file properties for a custom module with a po file located on
+ * a remote translation server.
+ * @code
+ * interface translation project = example_module
+ * interface translation server pattern = http://example.com/files/translations/%core/%project/%project-%version.%language.po
+ * @endcode
+ *
+ * Custom themes, features and distributions can implement these .info file
+ * properties in their .info file too.
+ *
+ * To change the interface translation settings of modules and themes hosted at
+ * drupal.org use hook_locale_translation_projects_alter(). Possible changes
+ * include changing the po file location (server pattern) or removing the
+ * project from the translation update list.
+ *
+ * Available .info file properties:
+ * - "interface translation project": project name. Required.
+ * Name of the project a (sub-)module belongs to. Multiple modules sharing
+ * the same project name will be listed as one the translation status list.
+ * - "interface translation server pattern": URL of the .po translation files
+ * used to download the files from. The URL contains tokens which will be
+ * replaced by appropriate values. The file can be locate both at a local
+ * relative path, a local absolute path and a remote server location.
+ *
+ * The following tokens are available for the server pattern:
+ * - "%core": Core version. Value example: "8.x".
+ * - "%project": Project name. Value examples: "drupal", "media_gallery".
+ * - "%version": Project version release. Value examples: "8.1", "8.x-1.0".
+ * - "%language": Language code. Value examples: "fr", "pt-pt".
+ *
+ * @} End of "defgroup interface_translation_properties".
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Alter the list of projects to be updated by locale's interface translation.
+ *
+ * Locale module attempts to update the translation of those modules returned
+ * by update_get_projects(). Using this hook the data returned by
+ * update_get_projects() can be altered or extended.
+ *
+ * Modules or distributions that use a dedicated translation server should use
+ * this hook to specify the interface translation server pattern.
+ * - "interface translation server pattern": URL of the .po translation files
+ * used to download the files from. The URL contains tokens which will be
+ * replaced by appropriate values.
+ * The following tokens are available for the server pattern:
+ * - "%core": Core version. Value example: "8.x".
+ * - "%project": Project name. Value examples: "drupal", "media_gallery".
+ * - "%version": Project version release. Value examples: "8.1", "8.x-1.0".
+ * - "%language": Language code. Value examples: "fr", "pt-pt".
+ *
+ * @param array $projects
+ * Project data as returned by update_get_projects().
+ *
+ * @see locale_project_list().
+ */
+function hook_locale_translation_projects_alter(&$projects) {
+ // The translations are located at a custom translation sever.
+ $projects['existing_project'] = array(
+ 'info' => array(
+ 'interface translation server pattern' => 'http://example.com/files/translations/%core/%project/%project-%version.%language.po',
+ ),
+ );
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/core/modules/locale/locale.compare.inc b/core/modules/locale/locale.compare.inc
new file mode 100644
index 0000000..55597d1
--- /dev/null
+++ b/core/modules/locale/locale.compare.inc
@@ -0,0 +1,265 @@
+<?php
+
+/**
+ * @file
+ * The API for comparing project translation status with available translation.
+ */
+
+/**
+ * Default location of gettext file on the translation server.
+ *
+ * @see locale_translation_default_translation_server().
+ */
+const LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN = 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po';
+
+use Drupal\Core\Cache;
+
+/**
+ * Get array of projects which are available for interface translation.
+ *
+ * This project data contains all projects which will be checked for available
+ * interface translations.
+ *
+ * For full functionality this function depends on Update module.
+ * When Update module is enabled the project data will contain the most recent
+ * module status; both in enabled status as in version. When Update module is
+ * disabled this function will return the last known module state. The status
+ * will only be updated once Update module is enabled.
+ *
+ * @see locale_translation_build_projects().
+ *
+ * @return array
+ * Array of project data for translation update. See
+ * locale_translation_build_projects() for details.
+ */
+function locale_translation_get_projects() {
+ $projects = &drupal_static(__FUNCTION__, array());
+
+ if (empty($projects)) {
+ // Get project data from the database.
+ $projects = array();
+ $result = db_query('SELECT * FROM {locale_project}');
+
+ if ($result->rowCount() == 0 && module_exists('update')) {
+ // At least the core project should be in the database, so we build the
+ // data if none are found.
+ locale_translation_build_projects();
+ $result = db_query('SELECT * FROM {locale_project}');
+ }
+
+ foreach ($result as $project) {
+ $projects[$project->name] = $project;
+ }
+ }
+ return $projects;
+}
+
+/**
+ * Clear the project data table.
+ */
+function locale_translation_flush_projects() {
+ db_truncate('locale_project')->execute();
+}
+
+/**
+ * Builds list of projects and stores the result in the database.
+ *
+ * The project data is based on the project list supplied by the Update module.
+ * Only the properties required by Locale module is included and additional
+ * (custom) modules and translation server data is added.
+ *
+ * In case the Update module is disabled this function will return an empty
+ * array.
+ *
+ * @return array
+ * Array of project data:
+ * - "name": Project system name.
+ * - "project_type": Project type, e.g. 'module', 'theme'.
+ * - "core": Core release version, e.g. 8.x
+ * - "version": Project release version, e.g. 8.x-1.0
+ * - "server_pattern": Translation server po file pattern.
+ * - "status": Project status, 1 = enabled.
+ */
+function locale_translation_build_projects() {
+ // This function depends on Update module. We degrade gracefully.
+ if (!module_exists('update')) {
+ return array();
+ }
+
+ // Get the project list based on .info files.
+ $projects = locale_translation_project_list();
+
+ // Mark all previous projects as disabled and store new project data.
+ db_update('locale_project')
+ ->fields(array(
+ 'status' => 0,
+ ))
+ ->execute();
+
+ $default_server = locale_translation_default_translation_server();
+
+ $project_updates = update_get_available(TRUE);
+ foreach ($projects as $name => $data) {
+ if (isset($project_updates[$name]['releases']) && $project_updates[$name]['project_status'] != 'not-fetched') {
+ // Find out if a dev version is installed.
+ if (preg_match("/^[0-9]+\.x-([0-9]+)\..*-dev$/", $data['info']['version'], $matches)) {
+ // Find a suitable release to use as alternative translation.
+ foreach ($project_updates[$name]['releases'] as $project_release) {
+ // The first release with the same major release number which is not a
+ // dev release is the one. Releases are sorted the most recent first.
+ if ($project_release['version_major'] == $matches[1] &&
+ (!isset($project_release['version_extra']) || $project_release['version_extra'] != 'dev')) {
+ $release = $project_release;
+ break;
+ }
+ }
+ }
+ elseif ($name == "drupal" || preg_match("/HEAD/", $data['info']['version'], $matches)) {
+ // Pick latest available release.
+ $release = array_shift($project_updates[$name]['releases']);
+ }
+
+ if (!empty($release['version'])) {
+ $data['info']['version'] = $release['version'];
+ }
+
+ unset($release);
+ }
+
+ $data += array(
+ 'version' => isset($data['info']['version']) ? $data['info']['version'] : '',
+ 'core' => isset($data['info']['core']) ? $data['info']['core'] : DRUPAL_CORE_COMPATIBILITY,
+ // A project can provide the path and filename pattern to download the
+ // gettext file. Use the default if not.
+ 'server_pattern' => isset($data['info']['interface translation server pattern']) ? $data['info']['interface translation server pattern'] : $default_server['pattern'],
+ 'status' => $data['project_status'] ? 1 : 0,
+ );
+ $project = (object) $data;
+ $projects[$name] = $project;
+
+ // Create or update the project record.
+ db_merge('locale_project')
+ ->key(array('name' => $project->name))
+ ->fields(array(
+ 'name' => $project->name,
+ 'project_type' => $project->project_type,
+ 'core' => $project->core,
+ 'version' => $project->version,
+ 'server_pattern' => $project->server_pattern,
+ 'status' => $project->status,
+ ))
+ ->execute();
+ }
+ return $projects;
+}
+
+/**
+ * Fetch an array of projects for translation update.
+ *
+ * @return array
+ * Array of project data including .info file data.
+ */
+function locale_translation_project_list() {
+ // This function depends on Update module. We degrade gracefully.
+ if (!module_exists('update')) {
+ return array();
+ }
+
+ $projects = &drupal_static(__FUNCTION__, array());
+ if (empty($projects)) {
+ module_load_include('compare.inc', 'update');
+ $projects = array();
+
+ $additional_whitelist = array(
+ 'interface translation project',
+ 'interface translation server pattern',
+ );
+ $module_data = _locale_translation_prepare_project_list(system_rebuild_module_data(), 'module');
+ $theme_data = _locale_translation_prepare_project_list(system_rebuild_theme_data(), 'theme');
+ update_process_info_list($projects, $module_data, 'module', TRUE, $additional_whitelist);
+ update_process_info_list($projects, $theme_data, 'theme', TRUE, $additional_whitelist);
+ if (variable_get('locale_translation_check_disabled', 0)) {
+ update_process_info_list($projects, $module_data, 'module', FALSE, $additional_whitelist);
+ update_process_info_list($projects, $theme_data, 'theme', FALSE, $additional_whitelist);
+ }
+
+ // Allow other modules to alter projects before fetching and comparing.
+ drupal_alter('locale_translation_projects', $projects);
+ }
+ return $projects;
+}
+
+/**
+ * Prepare module and theme data.
+ *
+ * Modify .info file data before it is processed by update_process_info_list().
+ * In order for update_process_info_list() to recognize a project, it requires
+ * the 'project' parameter in the .info file data.
+ * Custom modules or themes can bring their own gettext translation file. To
+ * enable import of this file the module or theme defines "interface translation
+ * project = myproject" in its .info file. This function will add a project
+ * "myproject" to the info data.
+ *
+ * @param array $data
+ * Array of .info file data.
+ * @param string $type
+ * The project type. i.e. module, theme.
+ *
+ * @return array
+ * Array of .info file data.
+ */
+function _locale_translation_prepare_project_list($data, $type) {
+ foreach ($data as $name => $file) {
+ // Include interface translation projects.
+ // Custom modules can bring their own gettext translation file.
+ // To enable import of this file the module must define
+ // 'interface translation project = myproject' in its .info file.
+ // To allow update_process_info_list() to identify this as a project
+ // the 'project' property is filled with the 'interface translation project'
+ // value.
+ if (isset($file->info['interface translation project'])) {
+ $data[$name]->info['project'] = $file->info['interface translation project'];
+ }
+ }
+ return $data;
+}
+
+/**
+ * Retrieve data for default server.
+ *
+ * @return array
+ * Array of server parameters:
+ * - "server_pattern": URL containing po file pattern.
+ */
+function locale_translation_default_translation_server() {
+ return array(
+ 'pattern' => variable_get('locale_translation_default_server_pattern', LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN),
+ );
+}
+
+/**
+ * Build path to translation source, out of a server path replacement pattern.
+ *
+ * @param stdClass $project
+ * Project object containing data to be inserted in the template.
+ * @param string $template
+ * String containing placeholders. Available placeholders:
+ * - "%project": Project name.
+ * - "%version": Project version.
+ * - "%core": Project core version.
+ * - "%language": Language code.
+ * - "%filename": Project file name.
+ *
+ * @return string
+ * String with replaced placeholders.
+ */
+function locale_translation_build_server_pattern($project, $template) {
+ $variables = array(
+ '%project' => $project->name,
+ '%version' => $project->version,
+ '%core' => $project->core,
+ '%language' => isset($project->language) ? $project->language : '%language',
+ '%filename' => isset($project->filename) ? $project->filename : '%filename',
+ );
+ return strtr($template, $variables);
+}
diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install
index 334b401..35c8185 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -32,6 +32,9 @@ function locale_uninstall() {
variable_del('locale_cache_length');
variable_del('locale_translation_plurals');
variable_del('locale_translation_javascript');
+ variable_del('locale_translation_test_system_info_alter');
+ variable_del('locale_translation_check_disabled');
+ variable_del('locale_translation_default_server_pattern');
// Remove all node type language variables. Node module might have been
// enabled, but may be disabled, so use a wildcard delete.
@@ -159,6 +162,55 @@ function locale_schema() {
'primary key' => array('uri', 'langcode'),
);
+ $schema['locale_project'] = array(
+ 'description' => 'Translation status information for projects and server data.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'A unique short name to identify the project.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ ),
+ 'project_type' => array(
+ 'description' => 'Project type, may be core, module, theme',
+ 'type' => 'varchar',
+ 'length' => 15,
+ 'not null' => TRUE,
+ ),
+ 'core' => array(
+ 'description' => 'Core compatibility string for this project.',
+ 'type' => 'varchar',
+ 'length' => 4,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'version' => array(
+ 'description' => 'The version release of the project.',
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'server_pattern' => array(
+ 'description' => 'Pattern of path and name of the gettext file at the translation server.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'status' => array(
+ 'description' => 'The update status of the project.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 1,
+ ),
+ ),
+ 'primary key' => array('name'),
+ );
+
+ $schema['cache_locale'] = drupal_get_schema_unprocessed('system', 'cache');
+ $schema['cache_locale']['description'] = 'Cache table for the locale module to store various data.';
+
return $schema;
}
@@ -623,6 +675,113 @@ function locale_update_8010() {
}
/**
+ * Add a cache table and locale_project table for the locale module.
+ */
+function locale_update_8011() {
+ // Add a 'locale' cache table.
+ db_create_table('cache_locale', array(
+ 'description' => 'Cache table for the locale module to store various data.',
+ 'fields' => array(
+ 'cid' => array(
+ 'description' => 'Primary Key: Unique cache ID.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'data' => array(
+ 'description' => 'A collection of data to cache.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ ),
+ 'expire' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'created' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry was created.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'serialized' => array(
+ 'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+ 'type' => 'int',
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'tags' => array(
+ 'description' => 'Space-separated list of cache tags for this entry.',
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'checksum' => array(
+ 'description' => 'The tag invalidation sum when this entry was saved.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'indexes' => array(
+ 'expire' => array('expire'),
+ ),
+ 'primary key' => array('cid'),
+ ));
+
+ // Add locale_project table.
+ db_create_table('locale_project', array(
+ 'description' => 'Translation status information for projects and server data.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'A unique short name to identify the project.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ ),
+ 'project_type' => array(
+ 'description' => 'Project type, may be core, module, theme',
+ 'type' => 'varchar',
+ 'length' => 15,
+ 'not null' => TRUE,
+ ),
+ 'core' => array(
+ 'description' => 'Core compatibility string for this project.',
+ 'type' => 'varchar',
+ 'length' => 4,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'version' => array(
+ 'description' => 'The release version of the project.',
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'server_pattern' => array(
+ 'description' => 'Pattern of path and name of the gettext file at the translation server.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'status' => array(
+ 'description' => 'The update status of the project.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 1,
+ ),
+ ),
+ 'primary key' => array('name'),
+ ));
+}
+
+/**
* @} End of "addtogroup updates-7.x-to-8.x".
* The next series of updates should start at 9000.
*/
diff --git a/core/modules/locale/tests/modules/locale_test/locale_test.info b/core/modules/locale/tests/modules/locale_test/locale_test.info
new file mode 100644
index 0000000..1ec7508
--- /dev/null
+++ b/core/modules/locale/tests/modules/locale_test/locale_test.info
@@ -0,0 +1,10 @@
+name = Locale test
+description = Support module for locale module testing.
+package = Testing
+version = 1.2
+core = 8.x
+hidden = TRUE
+
+; Definitions for interface translations.
+interface translation project = locale_test
+interface translation server pattern = core/modules/locale/test/modules/locale_test/%project-%version.%language.po
diff --git a/core/modules/locale/tests/modules/locale_test/locale_test.module b/core/modules/locale/tests/modules/locale_test/locale_test.module
new file mode 100644
index 0000000..6044a07
--- /dev/null
+++ b/core/modules/locale/tests/modules/locale_test/locale_test.module
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Simulate a custom module with a local po file.
+ */
+
+/**
+ * Implements hook_system_info_alter().
+ *
+ * Make the test scripts to be believe this is not a hidden test module, but
+ * a regular custom module.
+ */
+function locale_test_system_info_alter(&$info, $file, $type) {
+ // Only modify the system info if required.
+ // By default the locale_test modules are hidden and have a project specified.
+ // To test the module detection proces by locale_project_list() the
+ // test modules should mimic a custom module. I.e. be non-hidden.
+ if (!variable_get('locale_translation_test_system_info_alter', FALSE)) {
+ return;
+ }
+
+ if ($file->name == 'locale_test' || $file->name == 'locale_test_disabled') {
+ // Make the module appear as not-disabled.
+ $info['hidden'] = FALSE;
+ }
+}
diff --git a/core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.info b/core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.info
new file mode 100644
index 0000000..7eddf25
--- /dev/null
+++ b/core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.info
@@ -0,0 +1,10 @@
+name = Disabled locale test
+description = Disabled support module for locale module testing.
+package = Testing
+version = VERSION
+core = 8.x
+hidden = TRUE
+project = locale_test_disabled
+
+; Definitions for interface translation.
+interface translation project = locale_test_disabled
diff --git a/core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.module b/core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.module
new file mode 100644
index 0000000..a80d9da
--- /dev/null
+++ b/core/modules/locale/tests/modules/locale_test_disabled/locale_test_disabled.module
@@ -0,0 +1,6 @@
+<?php
+
+/**
+ * @file
+ * Simulate a disabled contrib for Locale test scripts.
+ */