function LocaleJs::parseJsFile

Same name and namespace in other branches
  1. main core/modules/locale/src/LocaleJs.php \Drupal\locale\LocaleJs::parseJsFile()

Parses a JavaScript file, extracts translatable strings, and saves them.

Strings are extracted from both Drupal.t() and Drupal.formatPlural().

@internal

Parameters

string $filepath: File name to parse.

Throws

\Exception If a non-local file is attempted to be parsed.

1 call to LocaleJs::parseJsFile()
LocaleJs::jsTranslate in core/modules/locale/src/LocaleJs.php
Returns a list of translation files given a list of JavaScript files.

File

core/modules/locale/src/LocaleJs.php, line 150

Class

LocaleJs
Provides the locale javascript related methods.

Namespace

Drupal\locale

Code

public function parseJsFile(string $filepath) : void {
  // Regular expression pattern used to localize JavaScript strings.
  $jsStringRegex = '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\\s*\\+\\s*)?)+';
  // Regular expression pattern used to match simple JS object literal.
  // This pattern matches a basic JS object, but will fail on an object with
  // nested objects. Used in JS file parsing for string arg processing.
  $jsObjectRegex = '\\{.*?\\}';
  // Regular expression to match an object containing a key 'context'.
  // Pattern to match a JS object containing a 'context key' with a string
  // value, which is captured. Will fail if there are nested objects.
  $jsObjectContextRegex = '
      \\{              # match object literal start
      .*?             # match anything, non-greedy
      (?:             # match a form of "context"
        \'context\'
        |
        "context"
        |
        context
      )
      \\s*:\\s*         # match key-value separator ":"
      (' . $jsStringRegex . ')  # match context string
      .*?             # match anything, non-greedy
      \\}              # match end of object literal
    ';
  // The file path might contain a query string, so make sure we only use the
  // actual file.
  $parsedUrl = UrlHelper::parse($filepath);
  $filepath = $parsedUrl['path'];
  // If there is still a protocol component in the path, reject that.
  if (strpos($filepath, ':')) {
    throw new \Exception('Only local files should be passed to LocaleJs::parseJsFile().');
  }
  // Load the JavaScript file.
  $file = file_get_contents($filepath);
  // Match all calls to Drupal.t() in an array.
  // Note: \s also matches newlines with the 's' modifier.
  preg_match_all('~
      [^\\w]Drupal\\s*\\.\\s*t\\s*                       # match "Drupal.t" with whitespace
      \\(\\s*                                         # match "(" argument list start
      (' . $jsStringRegex . ')\\s*                 # capture string argument
      (?:,\\s*' . $jsObjectRegex . '\\s*            # optionally capture str args
        (?:,\\s*' . $jsObjectContextRegex . '\\s*) # optionally capture context
      ?)?                                           # close optional args
      [,\\)]                                         # match ")" or "," to finish
      ~sx', $file, $tMatches);
  // Match all Drupal.formatPlural() calls in another array.
  preg_match_all('~
      [^\\w]Drupal\\s*\\.\\s*formatPlural\\s*  # match "Drupal.formatPlural" with whitespace
      \\(                                  # match "(" argument list start
      \\s*.+?\\s*,\\s*                       # match count argument
      (' . $jsStringRegex . ')\\s*,\\s*   # match singular string argument
      (                             # capture plural string argument
        (?:                         # non-capturing group to repeat string pieces
          (?:
            \'(?:\\\\\'|[^\'])*\'   # match single-quoted string with any character except unescaped single-quote
            |
            "(?:\\\\"|[^"])*"       # match double-quoted string with any character except unescaped double-quote
          )
          (?:\\s*\\+\\s*)?             # match "+" with possible whitespace, for str concat
        )+                          # match multiple because we supports concatenating strs
      )\\s*                          # end capturing of plural string argument
      (?:,\\s*' . $jsObjectRegex . '\\s*          # optionally capture string args
        (?:,\\s*' . $jsObjectContextRegex . '\\s*)?  # optionally capture context
      )?
      [,\\)]
      ~sx', $file, $pluralMatches);
  $matches = [];
  // Add strings from Drupal.t().
  foreach ($tMatches[1] as $key => $string) {
    $matches[] = [
      'source' => $this->stripQuotes($string),
      'context' => $this->stripQuotes($tMatches[2][$key]),
    ];
  }
  // Add string from Drupal.formatPlural().
  foreach ($pluralMatches[1] as $key => $string) {
    $matches[] = [
      'source' => $this->stripQuotes($string) . PoItem::DELIMITER . $this->stripQuotes($pluralMatches[2][$key]),
      'context' => $this->stripQuotes($pluralMatches[3][$key]),
    ];
  }
  // Loop through all matches and process them.
  foreach ($matches as $match) {
    $source = $this->localeStorage
      ->findString($match);
    if (!$source) {
      // We don't have the source string yet, thus we insert it into the
      // database.
      $source = $this->localeStorage
        ->createString($match);
    }
    // Besides adding the location this will tag it for current version.
    $source->addLocation('javascript', $filepath);
    $source->save();
  }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.