diff --git a/core/modules/ckeditor5/tests/modules/ckeditor5_incompatible_filter_test/src/Plugin/Filter/FilterIsIncompatible.php b/core/modules/ckeditor5/tests/modules/ckeditor5_incompatible_filter_test/src/Plugin/Filter/FilterIsIncompatible.php
index e2f23131c8287f5c89f2f8e0ac4bc2dee70e65e0..7494fa174e5e7169f107aa0b0eea9d489ede9a26 100644
--- a/core/modules/ckeditor5/tests/modules/ckeditor5_incompatible_filter_test/src/Plugin/Filter/FilterIsIncompatible.php
+++ b/core/modules/ckeditor5/tests/modules/ckeditor5_incompatible_filter_test/src/Plugin/Filter/FilterIsIncompatible.php
@@ -2,18 +2,20 @@
 
 namespace Drupal\ckeditor5_incompatible_filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter incompatible with CKEditor 5.
- *
- * @Filter(
- *   id = "filter_incompatible",
- *   title = @Translation("A TYPE_MARKUP_LANGUAGE filter incompatible with CKEditor 5"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE
- * )
  */
+#[Filter(
+  id: "filter_incompatible",
+  title: new TranslatableMarkup("A TYPE_MARKUP_LANGUAGE filter incompatible with CKEditor 5"),
+  type: FilterInterface::TYPE_MARKUP_LANGUAGE
+)]
 class FilterIsIncompatible extends FilterBase {
 
   /**
diff --git a/core/modules/editor/src/Plugin/Filter/EditorFileReference.php b/core/modules/editor/src/Plugin/Filter/EditorFileReference.php
index d107b95347bb13f648724541fffa358c82fec954..2e920d2ea694dcb72ec29576ce6d2c3d6fc00d27 100644
--- a/core/modules/editor/src/Plugin/Filter/EditorFileReference.php
+++ b/core/modules/editor/src/Plugin/Filter/EditorFileReference.php
@@ -6,23 +6,25 @@
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Image\ImageFactory;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\file\FileInterface;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides a filter to track images uploaded via a Text Editor.
  *
  * Generates file URLs and associates the cache tags of referenced files.
- *
- * @Filter(
- *   id = "editor_file_reference",
- *   title = @Translation("Track images uploaded via a Text Editor"),
- *   description = @Translation("Ensures that the latest versions of images uploaded via a Text Editor are displayed, along with their dimensions."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "editor_file_reference",
+  title: new TranslatableMarkup("Track images uploaded via a Text Editor"),
+  description: new TranslatableMarkup("Ensures that the latest versions of images uploaded via a Text Editor are displayed, along with their dimensions."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE
+)]
 class EditorFileReference extends FilterBase implements ContainerFactoryPluginInterface {
 
   /**
diff --git a/core/modules/filter/src/Attribute/Filter.php b/core/modules/filter/src/Attribute/Filter.php
new file mode 100644
index 0000000000000000000000000000000000000000..3e37bbbe5db72ed646cceb0edcb6fe43db00d0b6
--- /dev/null
+++ b/core/modules/filter/src/Attribute/Filter.php
@@ -0,0 +1,56 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\filter\Attribute;
+
+use Drupal\Component\Plugin\Attribute\Plugin;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Defines a filter attribute for plugin discovery.
+ *
+ * Plugin Namespace: Plugin\Filter
+ *
+ * For a working example, see \Drupal\filter\Plugin\Filter\FilterHtml
+ *
+ * @see \Drupal\filter\FilterPluginManager
+ * @see \Drupal\filter\Plugin\FilterInterface
+ * @see \Drupal\filter\Plugin\FilterBase
+ * @see plugin_api
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class Filter extends Plugin {
+
+  /**
+   * Constructs a Filter attribute.
+   *
+   * @param string $id
+   *   The plugin ID.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $title
+   *   The human-readable name of the filter. This is used as an administrative
+   *   summary of what the filter does.
+   * @param int $type
+   *   The filter type. Values are defined in
+   *   \Drupal\filter\Plugin\FilterInterface.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $description
+   *   (optional) Additional administrative information about the filter's
+   *   behavior.
+   * @param int $weight
+   *   (optional) A default weight for the filter in new text formats.
+   * @param bool $status
+   *   (optional) Whether this filter is enabled or disabled by default.
+   * @param array $settings
+   *   (optional) The default settings for the filter.
+   */
+  public function __construct(
+    public readonly string $id,
+    public readonly TranslatableMarkup $title,
+    public readonly int $type,
+    public readonly ?TranslatableMarkup $description = NULL,
+    public readonly int $weight = 0,
+    public readonly bool $status = FALSE,
+    public readonly array $settings = [],
+  ) {}
+
+}
diff --git a/core/modules/filter/src/FilterPluginManager.php b/core/modules/filter/src/FilterPluginManager.php
index f41e016c0b52aae0bd16a53d727b92e07e326ff4..1604ad52dc7f5d42f4c6be58891c5f0bd3c772a5 100644
--- a/core/modules/filter/src/FilterPluginManager.php
+++ b/core/modules/filter/src/FilterPluginManager.php
@@ -6,6 +6,7 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\filter\Attribute\Filter;
 
 /**
  * Manages text processing filters.
@@ -30,7 +31,7 @@ class FilterPluginManager extends DefaultPluginManager implements FallbackPlugin
    *   The module handler to invoke the alter hook with.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
-    parent::__construct('Plugin/Filter', $namespaces, $module_handler, 'Drupal\filter\Plugin\FilterInterface', 'Drupal\filter\Annotation\Filter');
+    parent::__construct('Plugin/Filter', $namespaces, $module_handler, 'Drupal\filter\Plugin\FilterInterface', Filter::class, 'Drupal\filter\Annotation\Filter');
     $this->alterInfo('filter_info');
     $this->setCacheBackend($cache_backend, 'filter_plugins');
   }
diff --git a/core/modules/filter/src/Plugin/Filter/FilterAlign.php b/core/modules/filter/src/Plugin/Filter/FilterAlign.php
index a3739ab8246d3ebb1d965f5a92d045af1ef18b9b..5100eb4a235b693d1b574251a531a7104687fbb7 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterAlign.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterAlign.php
@@ -3,19 +3,21 @@
 namespace Drupal\filter\Plugin\Filter;
 
 use Drupal\Component\Utility\Html;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to align elements.
- *
- * @Filter(
- *   id = "filter_align",
- *   title = @Translation("Align images"),
- *   description = @Translation("Uses a <code>data-align</code> attribute on <code>&lt;img&gt;</code> tags to align images."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_align",
+  title: new TranslatableMarkup("Align images"),
+  description: new TranslatableMarkup("Uses a <code>data-align</code> attribute on <code>&lt;img&gt;</code> tags to align images."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
+)]
 class FilterAlign extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterAutoP.php b/core/modules/filter/src/Plugin/Filter/FilterAutoP.php
index 2e96654358aab95009b2746d6de6def2520532a4..10e4268a2513d2a96e1e22f391d0b81cbc57bbb1 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterAutoP.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterAutoP.php
@@ -2,18 +2,20 @@
 
 namespace Drupal\filter\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to convert line breaks to HTML.
- *
- * @Filter(
- *   id = "filter_autop",
- *   title = @Translation("Convert line breaks into HTML (i.e. <code>&lt;br&gt;</code> and <code>&lt;p&gt;</code>)"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE
- * )
  */
+#[Filter(
+  id: "filter_autop",
+  title: new TranslatableMarkup("Convert line breaks into HTML (i.e. <code>&lt;br&gt;</code> and <code>&lt;p&gt;</code>)"),
+  type: FilterInterface::TYPE_MARKUP_LANGUAGE
+)]
 class FilterAutoP extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
index 7847dbdf7e4058aeaa0814c473f171b6ea2a72bf..692ff827b9632b5e1f05ceba083cf58929ea1695 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterCaption.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
@@ -4,9 +4,12 @@
 
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterPluginManager;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 use Drupal\filter\Render\FilteredMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -14,14 +17,13 @@
  * Provides a filter to caption elements.
  *
  * When used in combination with the filter_align filter, this must run last.
- *
- * @Filter(
- *   id = "filter_caption",
- *   title = @Translation("Caption images"),
- *   description = @Translation("Uses a <code>data-caption</code> attribute on <code>&lt;img&gt;</code> tags to caption images."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_caption",
+  title: new TranslatableMarkup("Caption images"),
+  description: new TranslatableMarkup("Uses a <code>data-caption</code> attribute on <code>&lt;img&gt;</code> tags to caption images."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE
+)]
 class FilterCaption extends FilterBase implements ContainerFactoryPluginInterface {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterHtml.php b/core/modules/filter/src/Plugin/Filter/FilterHtml.php
index b24aa06ab5a74eb7dc680edb6c65fc26b09287d8..4bee00b5741c95cfd528572797aab01f5192622b 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterHtml.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterHtml.php
@@ -4,9 +4,12 @@
 
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Component\Utility\Html;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 use Masterminds\HTML5\Parser\DOMTreeBuilder;
 use Masterminds\HTML5\Parser\Scanner;
 use Masterminds\HTML5\Parser\Tokenizer;
@@ -17,19 +20,18 @@
  * The attributes in the annotation show examples of allowing all attributes
  * by only having the attribute name, or allowing a fixed list of values, or
  * allowing a value with a wildcard prefix.
- *
- * @Filter(
- *   id = "filter_html",
- *   title = @Translation("Limit allowed HTML tags and correct faulty HTML"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_HTML_RESTRICTOR,
- *   settings = {
- *     "allowed_html" = "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>",
- *     "filter_html_help" = TRUE,
- *     "filter_html_nofollow" = FALSE
- *   },
- *   weight = -10
- * )
  */
+#[Filter(
+  id: "filter_html",
+  title: new TranslatableMarkup("Limit allowed HTML tags and correct faulty HTML"),
+  type: FilterInterface::TYPE_HTML_RESTRICTOR,
+  weight: -10,
+  settings: [
+    "allowed_html" => "<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>",
+    "filter_html_help" => TRUE,
+    "filter_html_nofollow" => FALSE,
+  ],
+)]
 class FilterHtml extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterHtmlCorrector.php b/core/modules/filter/src/Plugin/Filter/FilterHtmlCorrector.php
index e057baf1cc36aec53939ba11512501afa4dff182..474b92339fc78c09dcb5051b1b677e1e34df6b79 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterHtmlCorrector.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterHtmlCorrector.php
@@ -3,19 +3,21 @@
 namespace Drupal\filter\Plugin\Filter;
 
 use Drupal\Component\Utility\Html;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to correct faulty and chopped off HTML.
- *
- * @Filter(
- *   id = "filter_htmlcorrector",
- *   title = @Translation("Correct faulty and chopped off HTML"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
- *   weight = 10
- * )
  */
+#[Filter(
+  id: "filter_htmlcorrector",
+  title: new TranslatableMarkup("Correct faulty and chopped off HTML"),
+  type: FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
+  weight: 10
+)]
 class FilterHtmlCorrector extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterHtmlEscape.php b/core/modules/filter/src/Plugin/Filter/FilterHtmlEscape.php
index a99945f5cc7b7688c2d5b83b73b7f20612894130..9987c0dd8607abc56a0bb716be915e280836657e 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterHtmlEscape.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterHtmlEscape.php
@@ -2,19 +2,21 @@
 
 namespace Drupal\filter\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to display any HTML as plain text.
- *
- * @Filter(
- *   id = "filter_html_escape",
- *   title = @Translation("Display any HTML as plain text"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_HTML_RESTRICTOR,
- *   weight = -10
- * )
  */
+#[Filter(
+  id: "filter_html_escape",
+  title: new TranslatableMarkup("Display any HTML as plain text"),
+  type: FilterInterface::TYPE_HTML_RESTRICTOR,
+  weight: -10
+)]
 class FilterHtmlEscape extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterHtmlImageSecure.php b/core/modules/filter/src/Plugin/Filter/FilterHtmlImageSecure.php
index 8b574d749cac40c6d7f311b4497139f8cdc679e8..dcc574ef8491d827dfcaf94534b2e0b727e591de 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterHtmlImageSecure.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterHtmlImageSecure.php
@@ -2,20 +2,22 @@
 
 namespace Drupal\filter\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to restrict images to site.
- *
- * @Filter(
- *   id = "filter_html_image_secure",
- *   title = @Translation("Restrict images to this site"),
- *   description = @Translation("Disallows usage of &lt;img&gt; tag sources that are not hosted on this site by replacing them with a placeholder image."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
- *   weight = 9
- * )
  */
+#[Filter(
+  id: "filter_html_image_secure",
+  title: new TranslatableMarkup("Restrict images to this site"),
+  description: new TranslatableMarkup("Disallows usage of &lt;img&gt; tag sources that are not hosted on this site by replacing them with a placeholder image."),
+  type: FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
+  weight: 9
+)]
 class FilterHtmlImageSecure extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterImageLazyLoad.php b/core/modules/filter/src/Plugin/Filter/FilterImageLazyLoad.php
index ee0802e1c8a6f8254babf7e11058358be7f46743..3e48e16d9e2b59c49a47ebeaf6ba82d9461a9bca 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterImageLazyLoad.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterImageLazyLoad.php
@@ -5,20 +5,22 @@
 namespace Drupal\filter\Plugin\Filter;
 
 use Drupal\Component\Utility\Html;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to lazy load tracked images.
- *
- * @Filter(
- *   id = "filter_image_lazy_load",
- *   title = @Translation("Lazy load images"),
- *   description = @Translation("Instruct browsers to lazy load images if dimensions are specified. Use in conjunction with and place after the 'Track images uploaded via a Text Editor' filter that adds image dimensions required for lazy loading. Results can be overridden by <code>&lt;img loading=&quot;eager&quot;&gt;</code>."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
- *   weight = 15
- * )
  */
+#[Filter(
+  id: "filter_image_lazy_load",
+  title: new TranslatableMarkup("Lazy load images"),
+  description: new TranslatableMarkup("Instruct browsers to lazy load images if dimensions are specified. Use in conjunction with and place after the 'Track images uploaded via a Text Editor' filter that adds image dimensions required for lazy loading. Results can be overridden by <code>&lt;img loading=&quot;eager&quot;&gt;</code>."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
+  weight: 15
+)]
 final class FilterImageLazyLoad extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterNull.php b/core/modules/filter/src/Plugin/Filter/FilterNull.php
index afac124bdfe5046d2ec3b0e96949898669c93485..4fb81780e9058b58c97b2652924290675788be6a 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterNull.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterNull.php
@@ -2,8 +2,11 @@
 
 namespace Drupal\filter\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a fallback placeholder filter to use for missing filters.
@@ -11,14 +14,13 @@
  * The filter system uses this filter to replace missing filters (for example,
  * if a filter module has been disabled) that are still part of defined text
  * formats. It returns an empty string.
- *
- * @Filter(
- *   id = "filter_null",
- *   title = @Translation("Provides a fallback for missing filters. Do not use."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_HTML_RESTRICTOR,
- *   weight = -10
- * )
  */
+#[Filter(
+  id: "filter_null",
+  title: new TranslatableMarkup("Provides a fallback for missing filters. Do not use."),
+  type: FilterInterface::TYPE_HTML_RESTRICTOR,
+  weight: -10
+)]
 class FilterNull extends FilterBase {
 
   /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterUrl.php b/core/modules/filter/src/Plugin/Filter/FilterUrl.php
index d3d2c96b0e5df8f8be4f1d5aa1654af3863f09e5..8c28c2732fa45975cf53b5263e9ab3eecc4ae7be 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterUrl.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterUrl.php
@@ -3,21 +3,23 @@
 namespace Drupal\filter\Plugin\Filter;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to convert URLs into links.
- *
- * @Filter(
- *   id = "filter_url",
- *   title = @Translation("Convert URLs into links"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE,
- *   settings = {
- *     "filter_url_length" = 72
- *   }
- * )
  */
+#[Filter(
+  id: "filter_url",
+  title: new TranslatableMarkup("Convert URLs into links"),
+  type: FilterInterface::TYPE_MARKUP_LANGUAGE,
+  settings: [
+    "filter_url_length" => 72,
+  ]
+)]
 class FilterUrl extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestAssets.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestAssets.php
index ea34240ca52308a3cb179998274e16a3f3508ef7..4e9f39aa5ca642790a6616047bfeaf5b913b41eb 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestAssets.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestAssets.php
@@ -2,19 +2,21 @@
 
 namespace Drupal\filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a test filter to attach assets.
- *
- * @Filter(
- *   id = "filter_test_assets",
- *   title = @Translation("Testing filter"),
- *   description = @Translation("Does not change content; attaches assets."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_test_assets",
+  title: new TranslatableMarkup("Testing filter"),
+  description: new TranslatableMarkup("Does not change content; attaches assets."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE
+)]
 class FilterTestAssets extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheContexts.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheContexts.php
index c285e28477da1b0995c1c6a844c409cb18dcc43c..07fa338f6eacd453806cc5647adef233f89bb78d 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheContexts.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheContexts.php
@@ -2,20 +2,22 @@
 
 namespace Drupal\filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a test filter to associate cache contexts.
- *
- * @Filter(
- *   id = "filter_test_cache_contexts",
- *   title = @Translation("Testing filter"),
- *   description = @Translation("Does not change content; associates cache contexts."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_test_cache_contexts",
+  title: new TranslatableMarkup("Testing filter"),
+  description: new TranslatableMarkup("Does not change content; associates cache contexts."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE
+)]
 class FilterTestCacheContexts extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheMerge.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheMerge.php
index 678d1e84950e8c1fe24cb445f593aceea2207179..fa63647bcbd4059bec6e955ed1b4503ac5994858 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheMerge.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheMerge.php
@@ -2,20 +2,22 @@
 
 namespace Drupal\filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
 use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a test filter to merge with CacheableMetadata.
- *
- * @Filter(
- *   id = "filter_test_cache_merge",
- *   title = @Translation("Testing filter"),
- *   description = @Translation("Does not change content; merges cacheable metadata."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_test_cache_merge",
+  title: new TranslatableMarkup("Testing filter"),
+  description: new TranslatableMarkup("Does not change content; merges cacheable metadata."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE
+)]
 class FilterTestCacheMerge extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php
index a3a63bb4ee48ada0ec3a36e4a8a05c0900a9793f..0496b0e829ab35e697c16ae62d061b5f5e90fd61 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestCacheTags.php
@@ -2,19 +2,21 @@
 
 namespace Drupal\filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a test filter to associate cache tags.
- *
- * @Filter(
- *   id = "filter_test_cache_tags",
- *   title = @Translation("Testing filter"),
- *   description = @Translation("Does not change content; associates cache tags."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_test_cache_tags",
+  title: new TranslatableMarkup("Testing filter"),
+  description: new TranslatableMarkup("Does not change content; associates cache tags."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE
+)]
 class FilterTestCacheTags extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestPlaceholders.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestPlaceholders.php
index 6e34bd6139f0b2ac599a0c9964c9c59785dc851e..618c1d85d88a051ff119907525224cb92a920e2c 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestPlaceholders.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestPlaceholders.php
@@ -2,21 +2,23 @@
 
 namespace Drupal\filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Security\TrustedCallbackInterface;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a test filter to use placeholders.
- *
- * @Filter(
- *   id = "filter_test_placeholders",
- *   title = @Translation("Testing filter"),
- *   description = @Translation("Appends a placeholder to the content; associates #lazy_builder callback."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_test_placeholders",
+  title: new TranslatableMarkup("Testing filter"),
+  description: new TranslatableMarkup("Appends a placeholder to the content; associates #lazy_builder callback."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE
+)]
 class FilterTestPlaceholders extends FilterBase implements TrustedCallbackInterface {
 
   /**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestReplace.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestReplace.php
index c697e250816f4abbb9afd1c57103801ec9240e0f..fd91e4c78be46239ffaf044a9cd9ca81ba49e44e 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestReplace.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestReplace.php
@@ -2,19 +2,21 @@
 
 namespace Drupal\filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a test filter to replace all content.
- *
- * @Filter(
- *   id = "filter_test_replace",
- *   title = @Translation("Testing filter"),
- *   description = @Translation("Replaces all content with filter and text format information."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE
- * )
  */
+#[Filter(
+  id: "filter_test_replace",
+  title: new TranslatableMarkup("Testing filter"),
+  description: new TranslatableMarkup("Replaces all content with filter and text format information."),
+  type: FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE
+)]
 class FilterTestReplace extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php
index 26cf1ac8a6924a049323fcc3229ca6ca5916d837..67063e7924c5462492bdaa50cd5dd8a7fc873d56 100644
--- a/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php
+++ b/core/modules/filter/tests/filter_test/src/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php
@@ -2,20 +2,22 @@
 
 namespace Drupal\filter_test\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 use Drupal\Component\Utility\Xss;
 
 /**
  * Provides a test filter to restrict HTML tags and attributes.
- *
- * @Filter(
- *   id = "filter_test_restrict_tags_and_attributes",
- *   title = @Translation("Tag and attribute restricting filter"),
- *   description = @Translation("Used for testing \Drupal\filter\Entity\FilterFormatInterface::getHtmlRestrictions()."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_HTML_RESTRICTOR
- * )
  */
+#[Filter(
+  id: "filter_test_restrict_tags_and_attributes",
+  title: new TranslatableMarkup("Tag and attribute restricting filter"),
+  description: new TranslatableMarkup("Used for testing \Drupal\filter\Entity\FilterFormatInterface::getHtmlRestrictions()."),
+  type: FilterInterface::TYPE_HTML_RESTRICTOR
+)]
 class FilterTestRestrictTagsAndAttributes extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php b/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php
index f363fa9eae1cf35434568933a0df8ce87a68e987..c87715a09e94f91c53dc59e26da00b8f9abecd26 100644
--- a/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php
+++ b/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php
@@ -2,8 +2,11 @@
 
 namespace Drupal\filter_test_plugin\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter to limit allowed HTML tags.
@@ -12,15 +15,14 @@
  * test.
  *
  * @see \Drupal\Tests\filter\Functional\FilterFormTest::testFilterForm()
- *
- * @Filter(
- *   id = "filter_sparkles",
- *   title = @Translation("Sparkles filter"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_HTML_RESTRICTOR,
- *   settings = {},
- *   weight = -10
- * )
  */
+#[Filter(
+  id: "filter_sparkles",
+  title: new TranslatableMarkup("Sparkles filter"),
+  type: FilterInterface::TYPE_HTML_RESTRICTOR,
+  weight: -10,
+  settings: [],
+)]
 class FilterSparkles extends FilterBase {
 
   /**
diff --git a/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterTestStatic.php b/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterTestStatic.php
index 5e5c262ffc9ba15dfd8bea855f948e977cdd05dd..b0393ba6687c847956f91915c16374953bb68325 100644
--- a/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterTestStatic.php
+++ b/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterTestStatic.php
@@ -2,19 +2,21 @@
 
 namespace Drupal\filter_test_plugin\Plugin\Filter;
 
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 
 /**
  * Provides a filter that returns the same static text.
- *
- * @Filter(
- *   id = "filter_static_text",
- *   title = @Translation("Static filter"),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_HTML_RESTRICTOR,
- *   settings = {},
- * )
  */
+#[Filter(
+  id: "filter_static_text",
+  title: new TranslatableMarkup("Static filter"),
+  type: FilterInterface::TYPE_HTML_RESTRICTOR,
+  settings: [],
+)]
 class FilterTestStatic extends FilterBase {
 
   /**
diff --git a/core/modules/media/src/Plugin/Filter/MediaEmbed.php b/core/modules/media/src/Plugin/Filter/MediaEmbed.php
index 22007a36fcbbb117a897076d476736c756c06f86..118422bf2c28d594569aa58a806ef722e4e0c4e4 100644
--- a/core/modules/media/src/Plugin/Filter/MediaEmbed.php
+++ b/core/modules/media/src/Plugin/Filter/MediaEmbed.php
@@ -15,8 +15,11 @@
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Security\TrustedCallbackInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\filter\Attribute\Filter;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
+use Drupal\filter\Plugin\FilterInterface;
 use Drupal\image\Plugin\Field\FieldType\ImageItem;
 use Drupal\media\MediaInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -24,21 +27,20 @@
 /**
  * Provides a filter to embed media items using a custom tag.
  *
- * @Filter(
- *   id = "media_embed",
- *   title = @Translation("Embed media"),
- *   description = @Translation("Embeds media items using a custom tag, <code>&lt;drupal-media&gt;</code>. If used in conjunction with the 'Align/Caption' filters, make sure this filter is configured to run after them."),
- *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
- *   settings = {
- *     "default_view_mode" = "default",
- *     "allowed_view_modes" = {},
- *     "allowed_media_types" = {},
- *   },
- *   weight = 100,
- * )
- *
  * @internal
  */
+#[Filter(
+  id: "media_embed",
+  title: new TranslatableMarkup("Embed media"),
+  description: new TranslatableMarkup("Embeds media items using a custom tag, <code>&lt;drupal-media&gt;</code>. If used in conjunction with the 'Align/Caption' filters, make sure this filter is configured to run after them."),
+  type: FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
+  weight: 100,
+  settings: [
+    "default_view_mode" => "default",
+    "allowed_view_modes" => [],
+    "allowed_media_types" => [],
+  ],
+)]
 class MediaEmbed extends FilterBase implements ContainerFactoryPluginInterface, TrustedCallbackInterface {
 
   /**
diff --git a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTestBase.php b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTestBase.php
index dcf5065785a716fb98ad85541a09a5e8404f3b36..84b66c07e21b0f7fcb67f83b7028aa119c47cf70 100644
--- a/core/modules/media/tests/src/Kernel/MediaEmbedFilterTestBase.php
+++ b/core/modules/media/tests/src/Kernel/MediaEmbedFilterTestBase.php
@@ -162,7 +162,7 @@ protected function createEmbedCode(array $attributes) {
   }
 
   /**
-   * Applies the `@Filter=media_embed` filter to text, pipes to raw content.
+   * Applies the `media_embed` filter to text, pipes to raw content.
    *
    * @param string $text
    *   The text string to be filtered.