Chris@0: setReport(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLangcode() { Chris@0: return $this->langcode; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setLangcode($langcode) { Chris@0: $this->langcode = $langcode; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the report of the write operations. Chris@0: */ Chris@0: public function getReport() { Chris@0: return $this->report; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the report array of write operations. Chris@0: * Chris@0: * @param array $report Chris@0: * Associative array with result information. Chris@0: */ Chris@0: public function setReport($report = []) { Chris@0: $report += [ Chris@0: 'additions' => 0, Chris@0: 'updates' => 0, Chris@0: 'deletes' => 0, Chris@0: 'skips' => 0, Chris@0: 'strings' => [], Chris@0: ]; Chris@0: $this->report = $report; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get the options used by the writer. Chris@0: */ Chris@0: public function getOptions() { Chris@0: return $this->options; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set the options for the current writer. Chris@16: * Chris@16: * @param array $options Chris@16: * An associative array containing: Chris@16: * - overwrite_options: An array of options. Each option contains: Chris@16: * - not_customized: Boolean indicating that not customized strings should Chris@16: * be overwritten. Chris@16: * - customized: Boolean indicating that customized strings should be Chris@16: * overwritten. Chris@16: * - customized: The strings being imported should be saved as customized. Chris@16: * One of LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED. Chris@0: */ Chris@0: public function setOptions(array $options) { Chris@0: if (!isset($options['overwrite_options'])) { Chris@0: $options['overwrite_options'] = []; Chris@0: } Chris@0: $options['overwrite_options'] += [ Chris@0: 'not_customized' => FALSE, Chris@0: 'customized' => FALSE, Chris@0: ]; Chris@0: $options += [ Chris@0: 'customized' => LOCALE_NOT_CUSTOMIZED, Chris@0: ]; Chris@0: $this->options = $options; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getHeader() { Chris@0: return $this->header; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Implements Drupal\Component\Gettext\PoMetadataInterface::setHeader(). Chris@0: * Chris@0: * Sets the header and configure Drupal accordingly. Chris@0: * Chris@0: * Before being able to process the given header we need to know in what Chris@0: * context this database write is done. For this the options must be set. Chris@0: * Chris@0: * A langcode is required to set the current header's PluralForm. Chris@0: * Chris@0: * @param \Drupal\Component\Gettext\PoHeader $header Chris@0: * Header metadata. Chris@0: * Chris@0: * @throws Exception Chris@0: */ Chris@0: public function setHeader(PoHeader $header) { Chris@0: $this->header = $header; Chris@0: $locale_plurals = \Drupal::state()->get('locale.translation.plurals') ?: []; Chris@0: Chris@0: // Check for options. Chris@0: $options = $this->getOptions(); Chris@0: if (empty($options)) { Chris@0: throw new \Exception('Options should be set before assigning a PoHeader.'); Chris@0: } Chris@0: $overwrite_options = $options['overwrite_options']; Chris@0: Chris@0: // Check for langcode. Chris@0: $langcode = $this->langcode; Chris@0: if (empty($langcode)) { Chris@0: throw new \Exception('Langcode should be set before assigning a PoHeader.'); Chris@0: } Chris@0: Chris@0: if (array_sum($overwrite_options) || empty($locale_plurals[$langcode]['plurals'])) { Chris@0: // Get and store the plural formula if available. Chris@0: $plural = $header->getPluralForms(); Chris@0: if (isset($plural) && $p = $header->parsePluralForms($plural)) { Chris@0: list($nplurals, $formula) = $p; Chris@0: \Drupal::service('locale.plural.formula')->setPluralFormula($langcode, $nplurals, $formula); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function writeItem(PoItem $item) { Chris@0: if ($item->isPlural()) { Chris@18: $item->setSource(implode(PoItem::DELIMITER, $item->getSource())); Chris@18: $item->setTranslation(implode(PoItem::DELIMITER, $item->getTranslation())); Chris@0: } Chris@0: $this->importString($item); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function writeItems(PoReaderInterface $reader, $count = -1) { Chris@0: $forever = $count == -1; Chris@0: while (($count-- > 0 || $forever) && ($item = $reader->readItem())) { Chris@0: $this->writeItem($item); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Imports one string into the database. Chris@0: * Chris@0: * @param \Drupal\Component\Gettext\PoItem $item Chris@0: * The item being imported. Chris@0: * Chris@0: * @return int Chris@0: * The string ID of the existing string modified or the new string added. Chris@0: */ Chris@0: private function importString(PoItem $item) { Chris@0: // Initialize overwrite options if not set. Chris@0: $this->options['overwrite_options'] += [ Chris@0: 'not_customized' => FALSE, Chris@0: 'customized' => FALSE, Chris@0: ]; Chris@0: $overwrite_options = $this->options['overwrite_options']; Chris@0: $customized = $this->options['customized']; Chris@0: Chris@0: $context = $item->getContext(); Chris@0: $source = $item->getSource(); Chris@0: $translation = $item->getTranslation(); Chris@0: Chris@0: // Look up the source string and any existing translation. Chris@0: $strings = \Drupal::service('locale.storage')->getTranslations([ Chris@0: 'language' => $this->langcode, Chris@0: 'source' => $source, Chris@0: 'context' => $context, Chris@0: ]); Chris@0: $string = reset($strings); Chris@0: Chris@0: if (!empty($translation)) { Chris@0: // Skip this string unless it passes a check for dangerous code. Chris@0: if (!locale_string_is_safe($translation)) { Chris@0: \Drupal::logger('locale')->error('Import of string "%string" was skipped because of disallowed or malformed HTML.', ['%string' => $translation]); Chris@0: $this->report['skips']++; Chris@0: return 0; Chris@0: } Chris@0: elseif ($string) { Chris@0: $string->setString($translation); Chris@0: if ($string->isNew()) { Chris@0: // No translation in this language. Chris@0: $string->setValues([ Chris@0: 'language' => $this->langcode, Chris@0: 'customized' => $customized, Chris@0: ]); Chris@0: $string->save(); Chris@0: $this->report['additions']++; Chris@0: } Chris@0: elseif ($overwrite_options[$string->customized ? 'customized' : 'not_customized']) { Chris@0: // Translation exists, only overwrite if instructed. Chris@0: $string->customized = $customized; Chris@0: $string->save(); Chris@0: $this->report['updates']++; Chris@0: } Chris@0: $this->report['strings'][] = $string->getId(); Chris@0: return $string->lid; Chris@0: } Chris@0: else { Chris@0: // No such source string in the database yet. Chris@0: $string = \Drupal::service('locale.storage')->createString(['source' => $source, 'context' => $context]) Chris@0: ->save(); Chris@0: \Drupal::service('locale.storage')->createTranslation([ Chris@0: 'lid' => $string->getId(), Chris@0: 'language' => $this->langcode, Chris@0: 'translation' => $translation, Chris@0: 'customized' => $customized, Chris@0: ])->save(); Chris@0: Chris@0: $this->report['additions']++; Chris@0: $this->report['strings'][] = $string->getId(); Chris@0: return $string->lid; Chris@0: } Chris@0: } Chris@0: elseif ($string && !$string->isNew() && $overwrite_options[$string->customized ? 'customized' : 'not_customized']) { Chris@0: // Empty translation, remove existing if instructed. Chris@0: $string->delete(); Chris@0: $this->report['deletes']++; Chris@0: $this->report['strings'][] = $string->lid; Chris@0: return $string->lid; Chris@0: } Chris@0: } Chris@0: Chris@0: }