Verified Commit 65f2db7e authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3493070 by penyaskito, griffynh, wim leers, longwave, pdureau,...

Issue #3493070 by penyaskito, griffynh, wim leers, longwave, pdureau, effulgentsia, xjm, phenaproxima, mradcliffe, danielveza, lauriii, catch: SDC `enum` props should have translatable labels: use `meta:enum`

(cherry picked from commit 9d55d1e6)
parent f6ae1909
Loading
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -2,6 +2,24 @@
  "$id": "https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata-full.schema.json",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "$defs": {
    "propDefinition": {
      "$ref": "http://json-schema.org/draft-04/schema#",
      "meta:enum": {
        "type": "object",
        "minItems": 1,
        "uniqueItems": true,
        "patternProperties": {
          "additionalProperties": false,
          "^[a-zA-Z0-9_-]*$": {
            "type": "string"
          }
        }
      },
      "x-translation-context": {
        "type": "string",
        "title": "Translation Context"
      }
    },
    "slotDefinition": {
      "type": "object",
      "additionalProperties": false,
@@ -160,7 +178,7 @@
      ]
    },
    "props": {
      "$ref": "http://json-schema.org/draft-04/schema#"
      "$ref": "#/$defs/propDefinition"
    },
    "slots": {
      "$ref": "metadata.schema.json#/$defs/slotDefinition"
+19 −1
Original line number Diff line number Diff line
@@ -2,6 +2,24 @@
  "$id": "https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "$defs": {
    "propDefinition": {
      "$ref": "http://json-schema.org/draft-04/schema#",
      "meta:enum": {
        "type": "object",
        "minItems": 1,
        "uniqueItems": true,
        "patternProperties": {
          "additionalProperties": false,
          "^[a-zA-Z0-9_-]*$": {
            "type": "string"
          }
        }
      },
      "x-translation-context": {
        "type": "string",
        "title": "Translation Context"
      }
    },
    "slotDefinition": {
      "type": "object",
      "additionalProperties": false,
@@ -208,7 +226,7 @@
      ]
    },
    "props": {
      "$ref": "http://json-schema.org/draft-04/schema#"
      "$ref": "#/$defs/propDefinition"
    },
    "slots": {
      "$ref": "#/$defs/slotDefinition"
+56 −1
Original line number Diff line number Diff line
@@ -13,6 +13,11 @@ class ComponentMetadata {

  use StringTranslationTrait;

  /**
   * The ID of the component, in the form of provider:machine_name.
   */
  public readonly string $id;

  /**
   * The absolute path to the component directory.
   *
@@ -115,6 +120,7 @@ public function __construct(array $metadata_info, string $app_root, bool $enforc
    if (str_starts_with($path, $app_root)) {
      $path = substr($path, strlen($app_root));
    }
    $this->id = $metadata_info['id'];
    $this->mandatorySchemas = $enforce_schemas;
    $this->path = $path;

@@ -149,7 +155,7 @@ public function __construct(array $metadata_info, string $app_root, bool $enforc
  private function parseSchemaInfo(array $metadata_info): ?array {
    if (empty($metadata_info['props'])) {
      if ($this->mandatorySchemas) {
        throw new InvalidComponentException(sprintf('The component "%s" does not provide schema information. Schema definitions are mandatory for components declared in modules. For components declared in themes, schema definitions are only mandatory if the "enforce_prop_schemas" key is set to "true" in the theme info file.', $metadata_info['id']));
        throw new InvalidComponentException(sprintf('The component "%s" does not provide schema information. Schema definitions are mandatory for components declared in modules. For components declared in themes, schema definitions are only mandatory if the "enforce_prop_schemas" key is set to "true" in the theme info file.', $this->id));
      }
      $schema = NULL;
    }
@@ -167,6 +173,12 @@ private function parseSchemaInfo(array $metadata_info): ?array {
      $schema_props = $metadata_info['props'];
      foreach ($schema_props['properties'] ?? [] as $name => $prop_schema) {
        $type = $prop_schema['type'] ?? '';
        if (isset($prop_schema['enum'], $prop_schema['meta:enum'])) {
          $enum_keys_diff = array_diff($prop_schema['enum'], array_keys($prop_schema['meta:enum']));
          if (!empty($enum_keys_diff)) {
            throw new InvalidComponentException(sprintf('The values for the %s prop enum in component %s must be defined in meta:enum.', $name, $this->id));
          }
        }
        $schema['properties'][$name]['type'] = array_unique([
          ...(array) $type,
          'object',
@@ -197,6 +209,14 @@ public function getThumbnailPath(): string {
   *   The normalized value object.
   */
  public function normalize(): array {
    $meta = [];
    if (!empty($this->schema['properties'])) {
      foreach ($this->schema['properties'] as $prop_name => $prop_definition) {
        if (!empty($prop_definition['meta:enum'])) {
          $meta['properties'][$prop_name] = $this->getEnumOptions($prop_name);
        }
      }
    }
    return [
      'path' => $this->path,
      'machineName' => $this->machineName,
@@ -204,7 +224,42 @@ public function normalize(): array {
      'name' => $this->name,
      'group' => $this->group,
      'variants' => $this->variants,
      'meta' => $meta,
    ];
  }

  /**
   * Get translated options labels from enumeration.
   *
   * @param string $propertyName
   *   The enum property name.
   *
   * @return array<string, \Drupal\Core\StringTranslation\TranslatableMarkup>
   *   An array with enum options as keys and the (non-rendered)
   *   translated labels as values.
   */
  public function getEnumOptions(string $propertyName): array {
    $options = [];
    if (isset($this->schema['properties'][$propertyName])) {
      $prop_definition = $this->schema['properties'][$propertyName];
      if (!empty($prop_definition['enum'])) {
        $translation_context = $prop_definition['x-translation-context'] ?? '';
        // We convert ['a', 'b'], into ['a' => t('a'), 'b' => t('b')].
        $options = array_combine(
          $prop_definition['enum'],
          // @phpcs:ignore Drupal.Semantics.FunctionT.NotLiteralString
          array_map(fn($value) => $this->t($value, [], ['context' => $translation_context]), $prop_definition['enum']),
        );
        if (!empty($prop_definition['meta:enum'])) {
          foreach ($prop_definition['meta:enum'] as $enum_value => $enum_label) {
            $options[$enum_value] =
              // @phpcs:ignore Drupal.Semantics.FunctionT.NotLiteralString
              $this->t($enum_label, [], ['context' => $translation_context]);
          }
        }
      }
    }
    return $options;
  }

}
+3 −0
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ props:
      enum:
        - info
        - success
      meta:enum:
        info: Information
        success: Success
slots:
  label:
    type: string
+12 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ props:
        enum:
          - ellipsis
          - xs
        meta:enum:
          ellipsis: Ellipsis
          xs: 'Extra-small'
    extra_classes:
      type: array
      title: Extra classes.
@@ -43,6 +46,15 @@ props:
        - h5
        - h6
        - span
      meta:enum:
        h1: Heading 1
        h2: Heading 2
        h3: Heading 3
        h4: Heading 4
        h5: Heading 5
        h6: Heading 6
        span: Inline
      x-translation-context: HTML tag
      # Provide a default value
      default: h2
    icon:
Loading