annotate core/modules/locale/src/PoDatabaseWriter.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\locale;
Chris@0 4
Chris@0 5 use Drupal\Component\Gettext\PoHeader;
Chris@0 6 use Drupal\Component\Gettext\PoItem;
Chris@0 7 use Drupal\Component\Gettext\PoReaderInterface;
Chris@0 8 use Drupal\Component\Gettext\PoWriterInterface;
Chris@0 9
Chris@0 10 /**
Chris@0 11 * Gettext PO writer working with the locale module database.
Chris@0 12 */
Chris@0 13 class PoDatabaseWriter implements PoWriterInterface {
Chris@0 14
Chris@0 15 /**
Chris@0 16 * An associative array indicating what data should be overwritten, if any.
Chris@0 17 *
Chris@0 18 * Elements of the array:
Chris@16 19 * - overwrite_options
Chris@0 20 * - not_customized: boolean indicating that not customized strings should
Chris@0 21 * be overwritten.
Chris@0 22 * - customized: boolean indicating that customized strings should be
Chris@0 23 * overwritten.
Chris@0 24 * - customized: the strings being imported should be saved as customized.
Chris@0 25 * One of LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
Chris@0 26 *
Chris@0 27 * @var array
Chris@0 28 */
Chris@0 29 private $options;
Chris@0 30
Chris@0 31 /**
Chris@0 32 * Language code of the language being written to the database.
Chris@0 33 *
Chris@0 34 * @var string
Chris@0 35 */
Chris@0 36 private $langcode;
Chris@0 37
Chris@0 38 /**
Chris@0 39 * Header of the po file written to the database.
Chris@0 40 *
Chris@0 41 * @var \Drupal\Component\Gettext\PoHeader
Chris@0 42 */
Chris@0 43 private $header;
Chris@0 44
Chris@0 45 /**
Chris@0 46 * Associative array summarizing the number of changes done.
Chris@0 47 *
Chris@0 48 * Keys for the array:
Chris@0 49 * - additions: number of source strings newly added
Chris@0 50 * - updates: number of translations updated
Chris@0 51 * - deletes: number of translations deleted
Chris@0 52 * - skips: number of strings skipped due to disallowed HTML
Chris@0 53 *
Chris@0 54 * @var array
Chris@0 55 */
Chris@0 56 private $report;
Chris@0 57
Chris@0 58 /**
Chris@0 59 * Constructor, initialize reporting array.
Chris@0 60 */
Chris@0 61 public function __construct() {
Chris@0 62 $this->setReport();
Chris@0 63 }
Chris@0 64
Chris@0 65 /**
Chris@0 66 * {@inheritdoc}
Chris@0 67 */
Chris@0 68 public function getLangcode() {
Chris@0 69 return $this->langcode;
Chris@0 70 }
Chris@0 71
Chris@0 72 /**
Chris@0 73 * {@inheritdoc}
Chris@0 74 */
Chris@0 75 public function setLangcode($langcode) {
Chris@0 76 $this->langcode = $langcode;
Chris@0 77 }
Chris@0 78
Chris@0 79 /**
Chris@0 80 * Get the report of the write operations.
Chris@0 81 */
Chris@0 82 public function getReport() {
Chris@0 83 return $this->report;
Chris@0 84 }
Chris@0 85
Chris@0 86 /**
Chris@0 87 * Set the report array of write operations.
Chris@0 88 *
Chris@0 89 * @param array $report
Chris@0 90 * Associative array with result information.
Chris@0 91 */
Chris@0 92 public function setReport($report = []) {
Chris@0 93 $report += [
Chris@0 94 'additions' => 0,
Chris@0 95 'updates' => 0,
Chris@0 96 'deletes' => 0,
Chris@0 97 'skips' => 0,
Chris@0 98 'strings' => [],
Chris@0 99 ];
Chris@0 100 $this->report = $report;
Chris@0 101 }
Chris@0 102
Chris@0 103 /**
Chris@0 104 * Get the options used by the writer.
Chris@0 105 */
Chris@0 106 public function getOptions() {
Chris@0 107 return $this->options;
Chris@0 108 }
Chris@0 109
Chris@0 110 /**
Chris@0 111 * Set the options for the current writer.
Chris@16 112 *
Chris@16 113 * @param array $options
Chris@16 114 * An associative array containing:
Chris@16 115 * - overwrite_options: An array of options. Each option contains:
Chris@16 116 * - not_customized: Boolean indicating that not customized strings should
Chris@16 117 * be overwritten.
Chris@16 118 * - customized: Boolean indicating that customized strings should be
Chris@16 119 * overwritten.
Chris@16 120 * - customized: The strings being imported should be saved as customized.
Chris@16 121 * One of LOCALE_CUSTOMIZED or LOCALE_NOT_CUSTOMIZED.
Chris@0 122 */
Chris@0 123 public function setOptions(array $options) {
Chris@0 124 if (!isset($options['overwrite_options'])) {
Chris@0 125 $options['overwrite_options'] = [];
Chris@0 126 }
Chris@0 127 $options['overwrite_options'] += [
Chris@0 128 'not_customized' => FALSE,
Chris@0 129 'customized' => FALSE,
Chris@0 130 ];
Chris@0 131 $options += [
Chris@0 132 'customized' => LOCALE_NOT_CUSTOMIZED,
Chris@0 133 ];
Chris@0 134 $this->options = $options;
Chris@0 135 }
Chris@0 136
Chris@0 137 /**
Chris@0 138 * {@inheritdoc}
Chris@0 139 */
Chris@0 140 public function getHeader() {
Chris@0 141 return $this->header;
Chris@0 142 }
Chris@0 143
Chris@0 144 /**
Chris@0 145 * Implements Drupal\Component\Gettext\PoMetadataInterface::setHeader().
Chris@0 146 *
Chris@0 147 * Sets the header and configure Drupal accordingly.
Chris@0 148 *
Chris@0 149 * Before being able to process the given header we need to know in what
Chris@0 150 * context this database write is done. For this the options must be set.
Chris@0 151 *
Chris@0 152 * A langcode is required to set the current header's PluralForm.
Chris@0 153 *
Chris@0 154 * @param \Drupal\Component\Gettext\PoHeader $header
Chris@0 155 * Header metadata.
Chris@0 156 *
Chris@0 157 * @throws Exception
Chris@0 158 */
Chris@0 159 public function setHeader(PoHeader $header) {
Chris@0 160 $this->header = $header;
Chris@0 161 $locale_plurals = \Drupal::state()->get('locale.translation.plurals') ?: [];
Chris@0 162
Chris@0 163 // Check for options.
Chris@0 164 $options = $this->getOptions();
Chris@0 165 if (empty($options)) {
Chris@0 166 throw new \Exception('Options should be set before assigning a PoHeader.');
Chris@0 167 }
Chris@0 168 $overwrite_options = $options['overwrite_options'];
Chris@0 169
Chris@0 170 // Check for langcode.
Chris@0 171 $langcode = $this->langcode;
Chris@0 172 if (empty($langcode)) {
Chris@0 173 throw new \Exception('Langcode should be set before assigning a PoHeader.');
Chris@0 174 }
Chris@0 175
Chris@0 176 if (array_sum($overwrite_options) || empty($locale_plurals[$langcode]['plurals'])) {
Chris@0 177 // Get and store the plural formula if available.
Chris@0 178 $plural = $header->getPluralForms();
Chris@0 179 if (isset($plural) && $p = $header->parsePluralForms($plural)) {
Chris@0 180 list($nplurals, $formula) = $p;
Chris@0 181 \Drupal::service('locale.plural.formula')->setPluralFormula($langcode, $nplurals, $formula);
Chris@0 182 }
Chris@0 183 }
Chris@0 184 }
Chris@0 185
Chris@0 186 /**
Chris@0 187 * {@inheritdoc}
Chris@0 188 */
Chris@0 189 public function writeItem(PoItem $item) {
Chris@0 190 if ($item->isPlural()) {
Chris@18 191 $item->setSource(implode(PoItem::DELIMITER, $item->getSource()));
Chris@18 192 $item->setTranslation(implode(PoItem::DELIMITER, $item->getTranslation()));
Chris@0 193 }
Chris@0 194 $this->importString($item);
Chris@0 195 }
Chris@0 196
Chris@0 197 /**
Chris@0 198 * {@inheritdoc}
Chris@0 199 */
Chris@0 200 public function writeItems(PoReaderInterface $reader, $count = -1) {
Chris@0 201 $forever = $count == -1;
Chris@0 202 while (($count-- > 0 || $forever) && ($item = $reader->readItem())) {
Chris@0 203 $this->writeItem($item);
Chris@0 204 }
Chris@0 205 }
Chris@0 206
Chris@0 207 /**
Chris@0 208 * Imports one string into the database.
Chris@0 209 *
Chris@0 210 * @param \Drupal\Component\Gettext\PoItem $item
Chris@0 211 * The item being imported.
Chris@0 212 *
Chris@0 213 * @return int
Chris@0 214 * The string ID of the existing string modified or the new string added.
Chris@0 215 */
Chris@0 216 private function importString(PoItem $item) {
Chris@0 217 // Initialize overwrite options if not set.
Chris@0 218 $this->options['overwrite_options'] += [
Chris@0 219 'not_customized' => FALSE,
Chris@0 220 'customized' => FALSE,
Chris@0 221 ];
Chris@0 222 $overwrite_options = $this->options['overwrite_options'];
Chris@0 223 $customized = $this->options['customized'];
Chris@0 224
Chris@0 225 $context = $item->getContext();
Chris@0 226 $source = $item->getSource();
Chris@0 227 $translation = $item->getTranslation();
Chris@0 228
Chris@0 229 // Look up the source string and any existing translation.
Chris@0 230 $strings = \Drupal::service('locale.storage')->getTranslations([
Chris@0 231 'language' => $this->langcode,
Chris@0 232 'source' => $source,
Chris@0 233 'context' => $context,
Chris@0 234 ]);
Chris@0 235 $string = reset($strings);
Chris@0 236
Chris@0 237 if (!empty($translation)) {
Chris@0 238 // Skip this string unless it passes a check for dangerous code.
Chris@0 239 if (!locale_string_is_safe($translation)) {
Chris@0 240 \Drupal::logger('locale')->error('Import of string "%string" was skipped because of disallowed or malformed HTML.', ['%string' => $translation]);
Chris@0 241 $this->report['skips']++;
Chris@0 242 return 0;
Chris@0 243 }
Chris@0 244 elseif ($string) {
Chris@0 245 $string->setString($translation);
Chris@0 246 if ($string->isNew()) {
Chris@0 247 // No translation in this language.
Chris@0 248 $string->setValues([
Chris@0 249 'language' => $this->langcode,
Chris@0 250 'customized' => $customized,
Chris@0 251 ]);
Chris@0 252 $string->save();
Chris@0 253 $this->report['additions']++;
Chris@0 254 }
Chris@0 255 elseif ($overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
Chris@0 256 // Translation exists, only overwrite if instructed.
Chris@0 257 $string->customized = $customized;
Chris@0 258 $string->save();
Chris@0 259 $this->report['updates']++;
Chris@0 260 }
Chris@0 261 $this->report['strings'][] = $string->getId();
Chris@0 262 return $string->lid;
Chris@0 263 }
Chris@0 264 else {
Chris@0 265 // No such source string in the database yet.
Chris@0 266 $string = \Drupal::service('locale.storage')->createString(['source' => $source, 'context' => $context])
Chris@0 267 ->save();
Chris@0 268 \Drupal::service('locale.storage')->createTranslation([
Chris@0 269 'lid' => $string->getId(),
Chris@0 270 'language' => $this->langcode,
Chris@0 271 'translation' => $translation,
Chris@0 272 'customized' => $customized,
Chris@0 273 ])->save();
Chris@0 274
Chris@0 275 $this->report['additions']++;
Chris@0 276 $this->report['strings'][] = $string->getId();
Chris@0 277 return $string->lid;
Chris@0 278 }
Chris@0 279 }
Chris@0 280 elseif ($string && !$string->isNew() && $overwrite_options[$string->customized ? 'customized' : 'not_customized']) {
Chris@0 281 // Empty translation, remove existing if instructed.
Chris@0 282 $string->delete();
Chris@0 283 $this->report['deletes']++;
Chris@0 284 $this->report['strings'][] = $string->lid;
Chris@0 285 return $string->lid;
Chris@0 286 }
Chris@0 287 }
Chris@0 288
Chris@0 289 }