diff --git a/connectors/l10n_drupal/l10n_drupal.drupalorg.inc b/connectors/l10n_drupal/l10n_drupal.drupalorg.inc
index 0746bca..615a2b4 100644
--- a/connectors/l10n_drupal/l10n_drupal.drupalorg.inc
+++ b/connectors/l10n_drupal/l10n_drupal.drupalorg.inc
@@ -384,7 +384,7 @@ function l10n_drupal_drupalorg_parse_package($package_file, $release) {
   // Potx module is already a dependency.
   module_load_include('inc', 'potx');
   module_load_include('inc', 'l10n_drupal', 'l10n_drupal.files');
-  module_load_include('inc', 'potx', 'potx.local');
+  module_load_include('inc', 'l10n_drupal', 'l10n_drupal.potx');
 
   // Set up status messages if not in automated mode.
   potx_status('set', POTX_STATUS_MESSAGE);
@@ -420,7 +420,7 @@ function l10n_drupal_drupalorg_parse_package($package_file, $release) {
 
         // Get all source files and save strings with our callback for this release.
         $version = l10n_drupal_detect_major_version($package_file);
-        potx_local_init($temp_path);
+        _l10n_drupal_potx_init();
         $files = _potx_explore_dir($temp_path, '*', $version);
         l10n_drupal_save_file(array($release->pid, $release->rid));
         l10n_drupal_added_string_counter(NULL, TRUE);
diff --git a/connectors/l10n_drupal/l10n_drupal.files.inc b/connectors/l10n_drupal/l10n_drupal.files.inc
index 1f82b20..615b7fd 100644
--- a/connectors/l10n_drupal/l10n_drupal.files.inc
+++ b/connectors/l10n_drupal/l10n_drupal.files.inc
@@ -223,6 +223,7 @@ function l10n_drupal_parse_package($package_file, $release) {
 
   // Potx module is already a dependency.
   module_load_include('inc', 'potx');
+  module_load_include('inc', 'l10n_drupal', 'l10n_drupal.potx');
 
   // Set up status messages if not in automated mode.
   potx_status('set', POTX_STATUS_STRUCTURED);
@@ -254,12 +255,13 @@ function l10n_drupal_parse_package($package_file, $release) {
       // Get all source files and save strings with our callback for this release.
       l10n_drupal_callback_save_version(array($release->pid, $release->rid));
       $version = l10n_drupal_detect_major_version($package_file);
+      _l10n_drupal_potx_init();
       $files = _potx_explore_dir($temp_path, '*', $version);
       l10n_drupal_added_string_counter(NULL, TRUE);
       foreach ($files as $name) {
         _potx_process_file($name, substr($name, strlen($temp_path) + 1), 'l10n_drupal_callback_save_string', 'l10n_drupal_callback_save_version', $version);
       }
-      potx_finish_processing('l10n_drupal_save_string', $version);
+      potx_finish_processing('l10n_drupal_callback_save_string', $version);
 
       $sid_count = l10n_drupal_added_string_counter();
 
diff --git a/connectors/l10n_drupal/l10n_drupal.install b/connectors/l10n_drupal/l10n_drupal.install
index a351ab3..660b42d 100644
--- a/connectors/l10n_drupal/l10n_drupal.install
+++ b/connectors/l10n_drupal/l10n_drupal.install
@@ -60,3 +60,105 @@ function l10n_drupal_require_potx_with_context() {
 
   return $requirement;
 }
+
+/**
+ * Implements hook_schema().
+ */
+function l10n_drupal_schema() {
+  $schema = array();
+
+  $schema['l10n_drupal_parsed_module_data'] = array(
+    'description' => 'Parsed data for modules (Drupal 8+)',
+    'fields' => array(
+      'module_name' => array(
+        'description' => 'Module name.',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+      ),
+      'dependencies' => array(
+        'description' => 'The module\'s dependencies.',
+        'type' => 'text',
+        'size' => 'medium',
+        'serialize' => TRUE,
+      ),
+      'parsed_schema' => array(
+        'description' => 'The module\'s parsed schema.',
+        'type' => 'text',
+        'size' => 'big',
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('module_name'),
+  );
+  $schema['l10n_drupal_schema_reverse_lookup'] = array(
+    'description' => 'A reverse lookup table for finding modules based on schema key',
+    'fields' => array(
+      'schema_key' => array(
+        'description' => 'The schema key',
+        'type' => 'varchar',
+        'length' => '500',
+      ),
+      'module_name' => array(
+        'description' => 'The array name',
+        'type' => 'varchar',
+        'length' => '255',
+      )
+    ),
+    'indexes' => array(
+      'schema_key' => array('schema_key'),
+      'module_name' => array('module_name'),
+    ),
+  );
+
+  return $schema;
+}
+
+/**
+ * Install newly required tables potx_parsed_data and potx_schema_reverse_lookup
+ */
+function l10n_drupal_update_7100() {
+  db_create_table('l10n_drupal_parsed_module_data', array(
+    'description' => 'Parsed data for modules (Drupal 8+)',
+    'fields' => array(
+      'module_name' => array(
+        'description' => 'Module name.',
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+      ),
+      'dependencies' => array(
+        'description' => 'The module\'s dependencies.',
+        'type' => 'text',
+        'size' => 'medium',
+        'serialize' => TRUE,
+      ),
+      'parsed_schema' => array(
+        'description' => 'The module\'s parsed schema.',
+        'type' => 'text',
+        'size' => 'big',
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('module_name'),
+  ));
+  db_create_table('l10n_drupal_schema_reverse_lookup', array(
+    'description' => 'A reverse lookup table for finding modules based on schema key',
+    'fields' => array(
+      'schema_key' => array(
+        'description' => 'The schema key',
+        'type' => 'varchar',
+        'length' => '500',
+      ),
+      'module_name' => array(
+        'description' => 'The array name',
+        'type' => 'varchar',
+        'length' => '255',
+      )
+    ),
+    'indexes' => array(
+      'schema_key' => array('schema_key'),
+      'module_name' => array('module_name'),
+    ),
+  ));
+}
diff --git a/connectors/l10n_drupal/l10n_drupal.potx.inc b/connectors/l10n_drupal/l10n_drupal.potx.inc
new file mode 100644
index 0000000..7ec02ff
--- /dev/null
+++ b/connectors/l10n_drupal/l10n_drupal.potx.inc
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * Initialize potx to run locally, e.g. by drush.
+ *
+ * @param string $module_path
+ *   Path to the module that is being parsed by potx.
+ */
+function _l10n_drupal_potx_init() {
+  global $_potx_module_metadata;
+  global $potx_callbacks;
+
+  $_potx_module_metadata = array();
+
+  $potx_callbacks = array(
+    'schema_store_lookup' => '_l10n_drupal_schema_store_lookup',
+    'schema_reverse_lookup' => '_l10n_drupal_schema_reverse_lookup',
+    'load_module_metadata' => '_l10n_drupal_load_module_metadata',
+    'store_module_metadata' => '_l10n_drupal_store_module_metadata',
+    'schema_store' => '_l10n_drupal_schema_store',
+    'schema_load' => '_l10n_drupal_schema_load',
+  );
+}
+
+/**
+ * Store a config schema file's matching keys (e.g. field.field.*.*.*), and
+ *  the module that contains the schema, in a lookup table.
+ *
+ * @param array $keys
+ *   List of config matching keys in a config schema file.
+ *
+ * @param string $module_name
+ *   Name of the module containing the schema.
+ */
+function _l10n_drupal_schema_store_lookup($keys, $module_name) {
+  if (!is_array($keys)) {
+    return;
+  }
+
+  $result = db_query("SELECT * FROM {l10n_drupal_schema_reverse_lookup} WHERE schema_key IN (:keys) AND module_name = :module_name",
+      array(':keys' => $keys, ':module_name' => $module_name));
+
+  // Exclude existing records
+  foreach ($result as $record) {
+    unset($keys[array_search($record->schema_key, $keys)]);
+  }
+
+  if (!is_array($keys)) {
+    return;
+  }
+
+  $query = db_insert('l10n_drupal_schema_reverse_lookup')->fields(array('schema_key', 'module_name'));
+  foreach ($keys as $key) {
+    $query->values(array(
+      'schema_key' => $key,
+      'module_name' => $module_name,
+    ));
+  }
+
+  $query->execute();
+}
+
+/**
+ * Find the module containing the schema for a specific config file, based on
+ *  its matching candidates, using a reverse lookup table.
+ *
+ * @param array $matching_candidates
+ *   The list of matching candidates for a config file, ordered from specific to
+ *    generic.
+ *
+ * @return string|null
+ *   The module containing the matching schema, if exists, otherwise NULL.
+ *
+ * @see _potx_find_matching_schema()
+ */
+function _l10n_drupal_schema_reverse_lookup($matching_candidates) {
+  // Search for modules that contain any schema definitions that could possibly match with this config file.
+  $result = db_query("SELECT * FROM {l10n_drupal_schema_reverse_lookup} WHERE schema_key IN (:matching_candidates)", array(':matching_candidates' => $matching_candidates));
+
+  $mapped = array();
+  foreach ($result as $record) {
+    $mapped[$record->schema_key] = $record->module_name;
+  }
+
+  foreach ($matching_candidates as $candidate) {
+    if (isset($mapped[$candidate])) {
+      return $mapped[$candidate];
+    }
+  }
+
+  return NULL;
+}
+
+/**
+ * Load a module's metadata, including its dependencies and list of config
+ *  schema files.
+ *
+ * @param string $module_name
+ *   The module's name.
+ *
+ * @return bool
+ *   TRUE if the module was found, FALSE otherwise.
+ */
+function _l10n_drupal_load_module_metadata($module_name) {
+  global $_potx_module_metadata;
+
+  $_potx_module_metadata[$module_name]['dependencies'] = array();
+
+  $record = db_query("SELECT * FROM {l10n_drupal_parsed_module_data} WHERE module_name = :module_name", array(':module_name' => $module_name))->fetchObject();
+
+  if ($record === FALSE) {
+    // Module metadata not found.
+    return FALSE;
+  }
+
+  $record->dependencies = unserialize($record->dependencies);
+  if (is_array($record->dependencies)) {
+    $_potx_module_metadata[$module_name]['dependencies'] = $record->dependencies;
+  }
+
+  return TRUE;
+}
+
+/**
+ * Store the metadata for a module, including its dependencies, in the database.
+ *
+ * @param string $module_name
+ *   The module's name
+ * @param array $metadata
+ *   The module's metadata
+ */
+function _l10n_drupal_store_module_metadata($module_name, $metadata) {
+  $dependencies = isset($metadata['dependencies']) ? $metadata['dependencies'] : array();
+  db_merge('l10n_drupal_parsed_module_data')
+    ->key(array('module_name' => $module_name))
+    ->fields(array('dependencies' => serialize($dependencies)))
+    ->execute();
+}
+
+/**
+ * Store a module's processed schema, in a cache (for local potx), or database
+ *  (for l10n_server).
+ *
+ * The processed schema is found in the "$_potx_module_schema" global.
+ *
+ * @param string $module_name
+ *   The module name.
+ */
+function _l10n_drupal_schema_store($module_name) {
+  global $_potx_module_schema;
+
+  db_merge('l10n_drupal_parsed_module_data')
+    ->key(array('module_name' => $module_name))
+    ->fields(array('parsed_schema' => serialize($_potx_module_schema)))
+    ->execute();
+}
+
+/**
+ * Load a module's processed schema, from cache (for local potx), or database
+ *  (for l10n_server).
+ *
+ * @param string $module_name
+ *   The module's name.
+ */
+function _l10n_drupal_schema_load($module_name) {
+  $record = db_query("SELECT * FROM {l10n_drupal_parsed_module_data} WHERE module_name = :module_name", array(':module_name' => $module_name))->fetchObject();
+
+  if ($record === FALSE) {
+    // Module metadata not found.
+    return NULL;
+  }
+
+  return unserialize($record->parsed_schema);
+}
