diff --git a/node_menu_multiple.module b/node_menu_multiple.module
index 2149c3c..c46d6b1 100644
--- a/node_menu_multiple.module
+++ b/node_menu_multiple.module
@@ -5,133 +5,20 @@
  * Node menu multilingual.
  */
 
-use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\node_menu_multiple\Hook\NodeMenuMultipleHooks;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\menu_link_content\Entity\MenuLinkContent;
 use Drupal\node\NodeInterface;
 use Drupal\node\NodeTypeInterface;
-use Drupal\system\Entity\Menu;
-use Drupal\system\MenuInterface;
 
 /**
  * Implements hook_form_BASE_FORM_ID_alter().
  */
+#[LegacyHook]
 function node_menu_multiple_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  if (!isset($form['menu'])) {
-    return;
-  }
-
-  /** @var \Drupal\node\Entity\Node $node */
-  $node = $form_state->getFormObject()->getEntity();
-  /** @var \Drupal\node\Entity\NodeType $node_type */
-  $node_type = $node->type->entity;
-  /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
-  $menu_parent_selector = \Drupal::service('menu.parent_form_selector');
-
-  if (!$node->isTranslatable() || !$node_type->getThirdPartySetting('node_menus', 'enabled', FALSE)) {
-    return;
-  }
-  $menu_names = array_map(function ($menu) {
-    return $menu->label();
-  }, Menu::loadMultiple());
-  $currentLangcode = $node->language()->getId();
-  $defaults = node_menu_multiple_get_menu_link_defaults($node);
-  // Remove first menu for default menu link.
-  if (count($defaults) > 1) {
-    array_shift($defaults);
-  }
-  $available_menus = [];
-  $node_menus = $node_type->getThirdPartySetting('node_menus', 'languages', [$currentLangcode]);
-  if (!empty($node_menus[$currentLangcode]['available_menus'])) {
-    foreach ($node_menus[$currentLangcode]['available_menus'] as $menu) {
-      $available_menus[$menu] = $menu_names[$menu];
-    }
-  }
-  else {
-    $type_menus = $node_type->getThirdPartySetting('menu_ui', 'available_menus', ['main']);
-    foreach ($type_menus as $menu) {
-      $available_menus[$menu] = $menu_names[$menu];
-    }
-  }
-
-  $form['menu']['menu_form_node'] = [
-    '#type' => 'details',
-    '#title' => t('Menu Form Nodes'),
-    '#prefix' => '<div id="multiple-node-menu-wrapper">',
-    '#suffix' => '</div>',
-    '#open' => TRUE,
-    '#states' => [
-      'invisible' => [
-        'input[name="menu[enabled]"]' => ['checked' => FALSE],
-      ],
-    ],
-  ];
-
-  $links_count = \Drupal::state()->get('menu_options_' . $node->bundle() . 'links_count');
-  for ($i = 0; $i <= $links_count; $i++) {
-    if ($defaults[$i]) {
-      $default = $defaults[$i]['menu_name'] . ':' . $defaults[$i]['parent'];
-    }
-    else {
-      $default = $node_type->getThirdPartySetting('menu_ui', 'parent', 'main:');
-    }
-    $parent_element = $menu_parent_selector->parentSelectElement($default, $defaults[$i]['id'], $available_menus);
-
-    foreach (['id', 'entity_id'] as $key) {
-      $form['menu']['menu_form_node'][$i][$key] = [
-        '#type' => 'value',
-        '#value' => $defaults[$i][$key],
-      ];
-    }
-
-    $form['menu']['menu_form_node'][$i]['title'] = [
-      '#type' => 'textfield',
-      '#title' => $form["menu"]["link"]["title"]["#title"],
-      '#default_value' => $defaults[$i]['title'],
-    ];
-    $form['menu']['menu_form_node'][$i]['description'] = [
-      '#type' => 'textarea',
-      '#title' => $form["menu"]["link"]["description"]["#title"],
-      '#default_value' => $defaults[$i]['description'],
-      '#rows' => 1,
-      '#description' => $form["menu"]["link"]["description"]["#description"],
-    ];
-
-    $form['menu']['menu_form_node'][$i]['menu_parent'] = $parent_element;
-    $form['menu']['menu_form_node'][$i]['enabled'] = [
-      '#type' => 'checkbox',
-      '#title' => t('Enabled'),
-      '#default_value' => $defaults[$i]['enabled'] ?? 0,
-    ];
-
-    $form['menu']['menu_form_node'][$i]['delete'] = [
-      '#type' => 'submit',
-      '#value' => t('Delete'),
-      '#submit' => ['node_menu_multiple_remove_link_submit'],
-      '#ajax' => [
-        'callback' => 'node_menu_multiple_ajax_callback',
-        'wrapper' => 'multiple-node-menu-wrapper',
-      ],
-    ];
-  }
-
-  $form['menu']['menu_form_node']['add_link_submit'] = [
-    '#type' => 'submit',
-    '#value' => t('Add new menu link'),
-    '#submit' => ['node_menu_multiple_add_link_submit'],
-    '#ajax' => [
-      'callback' => 'node_menu_multiple_ajax_callback',
-      'wrapper' => 'multiple-node-menu-wrapper',
-    ],
-  ];
-
-  foreach (array_keys($form['actions']) as $action) {
-    if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
-      $form['actions'][$action]['#submit'][] = 'node_menu_multiple_node_form_submit';
-    }
-  }
-
+  return \Drupal::service(NodeMenuMultipleHooks::class)->formNodeFormAlter($form, $form_state, $form_id);
 }
 
 /**
@@ -221,79 +108,9 @@ function node_menu_multiple_get_menu_link_defaults(NodeInterface $node) {
  * @see NodeTypeForm::form()
  * @see node_menu_multiple_form_node_type_form_submit()
  */
+#[LegacyHook]
 function node_menu_multiple_form_node_type_form_alter(&$form, FormStateInterface $form_state) {
-  /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
-  $menu_parent_selector = \Drupal::service('menu.parent_form_selector');
-  $menu_options = array_map(function (MenuInterface $menu) {
-    return $menu->label();
-  }, Menu::loadMultiple());
-  asort($menu_options);
-  /** @var \Drupal\node\NodeTypeInterface $type */
-  $type = $form_state->getFormObject()->getEntity();
-
-  $form['node_menus'] = [
-    '#type' => 'details',
-    '#title' => t('Language menu settings'),
-    '#description' => t('Make sure the content type is translatable and more than one language is added. The language selection must be also visible on the form.'),
-    '#attached' => [
-      'library' => ['node_menu_multiple/admin'],
-    ],
-    '#group' => 'additional_settings',
-    '#tree' => TRUE,
-  ];
-
-  $form['node_menus']['enable_language_menus'] = [
-    '#type' => 'checkbox',
-    '#title' => t('Enable language menus'),
-    '#default_value' => $type->getThirdPartySetting('node_menus', 'enabled', FALSE),
-  ];
-
-  $options_cacheability = new CacheableMetadata();
-  $options = $menu_parent_selector->getParentSelectOptions('', NULL, $options_cacheability);
-  $options_cacheability->applyTo($form['menu']['menu_parent']);
-
-  $languages = \Drupal::languageManager()->getLanguages();
-  $settings = $type->getThirdPartySetting('node_menus', 'languages', []);
-
-  $form['node_menus']['languages'] = [
-    '#type' => 'contain',
-  ];
-
-  foreach ($languages as $language) {
-    $langcode = $language->getId();
-    $form['node_menus']['languages'][$langcode] = [
-      '#type' => 'fieldset',
-      '#title' => $language->getName(),
-      '#attributes' => [
-        'class' => ['js-node-menus'],
-        'data-langcode' => $langcode,
-      ],
-      '#states' => [
-        'visible' => [
-          ':input[name="node_menus[enable_language_menus]"]' => ['checked' => TRUE],
-        ],
-      ],
-    ];
-
-    $form['node_menus']['languages'][$language->getId()]['menu_options'] = [
-      '#type' => 'checkboxes',
-      '#title' => t('Available menus'),
-      '#default_value' => $settings[$langcode]['available_menus'] ?? ['main'],
-      '#options' => $menu_options,
-      '#description' => t('The menus available to place links in for this content type.'),
-    ];
-
-    $form['node_menus']['languages'][$language->getId()]['menu_parent'] = [
-      '#type' => 'select',
-      '#title' => t('Default parent item'),
-      '#default_value' => $settings[$langcode]['parent'] ?? 'main:',
-      '#options' => $options,
-      '#description' => t('Choose the menu item to be the default parent for a new link in the content authoring form.'),
-      '#attributes' => ['class' => ['menu-title-select']],
-    ];
-  }
-
-  $form['#entity_builders'][] = 'node_menu_multiple_form_node_type_form_builder';
+  return \Drupal::service(NodeMenuMultipleHooks::class)->formNodeTypeFormAlter($form, $form_state);
 }
 
 /**
@@ -415,21 +232,7 @@ function node_menu_multiple_save(NodeInterface $node, array $menu_item) {
 /**
  * Implements hook_help().
  */
+#[LegacyHook]
 function node_menu_multiple_help($route_name, RouteMatchInterface $route_match) {
-  switch ($route_name) {
-    case 'help.page.node_menu_multiple':
-      $text = file_get_contents(dirname(__FILE__) . '/README.md');
-      if (!\Drupal::moduleHandler()->moduleExists('markdown')) {
-        return '<pre>' . $text . '</pre>';
-      }
-      else {
-        // Use the Markdown filter to render the README.
-        $filter_manager = \Drupal::service('plugin.manager.filter');
-        $settings = \Drupal::configFactory()->get('markdown.settings')->getRawData();
-        $config = ['settings' => $settings];
-        $filter = $filter_manager->createInstance('markdown', $config);
-        return $filter->process($text, 'en');
-      }
-  }
-  return NULL;
+  return \Drupal::service(NodeMenuMultipleHooks::class)->help($route_name, $route_match);
 }
diff --git a/node_menu_multiple.services.yml b/node_menu_multiple.services.yml
new file mode 100644
index 0000000..846c290
--- /dev/null
+++ b/node_menu_multiple.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\node_menu_multiple\Hook\NodeMenuMultipleHooks:
+    class: Drupal\node_menu_multiple\Hook\NodeMenuMultipleHooks
+    autowire: true
diff --git a/src/Hook/NodeMenuMultipleHooks.php b/src/Hook/NodeMenuMultipleHooks.php
new file mode 100644
index 0000000..e0a2751
--- /dev/null
+++ b/src/Hook/NodeMenuMultipleHooks.php
@@ -0,0 +1,254 @@
+<?php
+
+namespace Drupal\node_menu_multiple\Hook;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\system\MenuInterface;
+use Drupal\system\Entity\Menu;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for node_menu_multiple.
+ */
+class NodeMenuMultipleHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_form_BASE_FORM_ID_alter().
+   */
+  #[Hook('form_node_form_alter')]
+  public function formNodeFormAlter(&$form, FormStateInterface $form_state, $form_id) {
+    if (!isset($form['menu'])) {
+      return;
+    }
+    /** @var \Drupal\node\Entity\Node $node */
+    $node = $form_state->getFormObject()->getEntity();
+    /** @var \Drupal\node\Entity\NodeType $node_type */
+    $node_type = $node->type->entity;
+    /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
+    $menu_parent_selector = \Drupal::service('menu.parent_form_selector');
+    if (!$node->isTranslatable() || !$node_type->getThirdPartySetting('node_menus', 'enabled', FALSE)) {
+      return;
+    }
+    $menu_names = array_map(function ($menu) {
+        return $menu->label();
+    }, Menu::loadMultiple());
+    $currentLangcode = $node->language()->getId();
+    $defaults = node_menu_multiple_get_menu_link_defaults($node);
+    // Remove first menu for default menu link.
+    if (count($defaults) > 1) {
+      array_shift($defaults);
+    }
+    $available_menus = [];
+    $node_menus = $node_type->getThirdPartySetting('node_menus', 'languages', [
+      $currentLangcode,
+    ]);
+    if (!empty($node_menus[$currentLangcode]['available_menus'])) {
+      foreach ($node_menus[$currentLangcode]['available_menus'] as $menu) {
+        $available_menus[$menu] = $menu_names[$menu];
+      }
+    }
+    else {
+      $type_menus = $node_type->getThirdPartySetting('menu_ui', 'available_menus', [
+        'main',
+      ]);
+      foreach ($type_menus as $menu) {
+        $available_menus[$menu] = $menu_names[$menu];
+      }
+    }
+    $form['menu']['menu_form_node'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Menu Form Nodes'),
+      '#prefix' => '<div id="multiple-node-menu-wrapper">',
+      '#suffix' => '</div>',
+      '#open' => TRUE,
+      '#states' => [
+        'invisible' => [
+          'input[name="menu[enabled]"]' => [
+            'checked' => FALSE,
+          ],
+        ],
+      ],
+    ];
+    $links_count = \Drupal::state()->get('menu_options_' . $node->bundle() . 'links_count');
+    for ($i = 0; $i <= $links_count; $i++) {
+      if ($defaults[$i]) {
+        $default = $defaults[$i]['menu_name'] . ':' . $defaults[$i]['parent'];
+      }
+      else {
+        $default = $node_type->getThirdPartySetting('menu_ui', 'parent', 'main:');
+      }
+      $parent_element = $menu_parent_selector->parentSelectElement($default, $defaults[$i]['id'], $available_menus);
+      foreach ([
+        'id',
+        'entity_id',
+      ] as $key) {
+        $form['menu']['menu_form_node'][$i][$key] = [
+          '#type' => 'value',
+          '#value' => $defaults[$i][$key],
+        ];
+      }
+      $form['menu']['menu_form_node'][$i]['title'] = [
+        '#type' => 'textfield',
+        '#title' => $form["menu"]["link"]["title"]["#title"],
+        '#default_value' => $defaults[$i]['title'],
+      ];
+      $form['menu']['menu_form_node'][$i]['description'] = [
+        '#type' => 'textarea',
+        '#title' => $form["menu"]["link"]["description"]["#title"],
+        '#default_value' => $defaults[$i]['description'],
+        '#rows' => 1,
+        '#description' => $form["menu"]["link"]["description"]["#description"],
+      ];
+      $form['menu']['menu_form_node'][$i]['menu_parent'] = $parent_element;
+      $form['menu']['menu_form_node'][$i]['enabled'] = [
+        '#type' => 'checkbox',
+        '#title' => $this->t('Enabled'),
+        '#default_value' => $defaults[$i]['enabled'] ?? 0,
+      ];
+      $form['menu']['menu_form_node'][$i]['delete'] = [
+        '#type' => 'submit',
+        '#value' => $this->t('Delete'),
+        '#submit' => [
+          'node_menu_multiple_remove_link_submit',
+        ],
+        '#ajax' => [
+          'callback' => 'node_menu_multiple_ajax_callback',
+          'wrapper' => 'multiple-node-menu-wrapper',
+        ],
+      ];
+    }
+    $form['menu']['menu_form_node']['add_link_submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Add new menu link'),
+      '#submit' => [
+        'node_menu_multiple_add_link_submit',
+      ],
+      '#ajax' => [
+        'callback' => 'node_menu_multiple_ajax_callback',
+        'wrapper' => 'multiple-node-menu-wrapper',
+      ],
+    ];
+    foreach (array_keys($form['actions']) as $action) {
+      if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
+        $form['actions'][$action]['#submit'][] = 'node_menu_multiple_node_form_submit';
+      }
+    }
+  }
+
+  /**
+   * Implements hook_form_FORM_ID_alter() for \Drupal\node\NodeTypeForm.
+   *
+   * Adds menu options to the node type form.
+   *
+   * @see NodeTypeForm::form()
+   * @see node_menu_multiple_form_node_type_form_submit()
+   */
+  #[Hook('form_node_type_form_alter')]
+  public function formNodeTypeFormAlter(&$form, FormStateInterface $form_state) {
+    /** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
+    $menu_parent_selector = \Drupal::service('menu.parent_form_selector');
+    $menu_options = array_map(function (MenuInterface $menu) {
+        return $menu->label();
+    }, Menu::loadMultiple());
+    asort($menu_options);
+    /** @var \Drupal\node\NodeTypeInterface $type */
+    $type = $form_state->getFormObject()->getEntity();
+    $form['node_menus'] = [
+      '#type' => 'details',
+      '#title' => $this->t('Language menu settings'),
+      '#description' => $this->t('Make sure the content type is translatable and more than one language is added. The language selection must be also visible on the form.'),
+      '#attached' => [
+        'library' => [
+          'node_menu_multiple/admin',
+        ],
+      ],
+      '#group' => 'additional_settings',
+      '#tree' => TRUE,
+    ];
+    $form['node_menus']['enable_language_menus'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Enable language menus'),
+      '#default_value' => $type->getThirdPartySetting('node_menus', 'enabled', FALSE),
+    ];
+    $options_cacheability = new CacheableMetadata();
+    $options = $menu_parent_selector->getParentSelectOptions('', NULL, $options_cacheability);
+    $options_cacheability->applyTo($form['menu']['menu_parent']);
+    $languages = \Drupal::languageManager()->getLanguages();
+    $settings = $type->getThirdPartySetting('node_menus', 'languages', []);
+    $form['node_menus']['languages'] = [
+      '#type' => 'contain',
+    ];
+    foreach ($languages as $language) {
+      $langcode = $language->getId();
+      $form['node_menus']['languages'][$langcode] = [
+        '#type' => 'fieldset',
+        '#title' => $language->getName(),
+        '#attributes' => [
+          'class' => [
+            'js-node-menus',
+          ],
+          'data-langcode' => $langcode,
+        ],
+        '#states' => [
+          'visible' => [
+            ':input[name="node_menus[enable_language_menus]"]' => [
+              'checked' => TRUE,
+            ],
+          ],
+        ],
+      ];
+      $form['node_menus']['languages'][$language->getId()]['menu_options'] = [
+        '#type' => 'checkboxes',
+        '#title' => $this->t('Available menus'),
+        '#default_value' => $settings[$langcode]['available_menus'] ?? [
+          'main',
+        ],
+        '#options' => $menu_options,
+        '#description' => $this->t('The menus available to place links in for this content type.'),
+      ];
+      $form['node_menus']['languages'][$language->getId()]['menu_parent'] = [
+        '#type' => 'select',
+        '#title' => $this->t('Default parent item'),
+        '#default_value' => $settings[$langcode]['parent'] ?? 'main:',
+        '#options' => $options,
+        '#description' => $this->t('Choose the menu item to be the default parent for a new link in the content authoring form.'),
+        '#attributes' => [
+          'class' => [
+            'menu-title-select',
+          ],
+        ],
+      ];
+    }
+    $form['#entity_builders'][] = 'node_menu_multiple_form_node_type_form_builder';
+  }
+
+  /**
+   * Implements hook_help().
+   */
+  #[Hook('help')]
+  public function help($route_name, RouteMatchInterface $route_match) {
+    switch ($route_name) {
+      case 'help.page.node_menu_multiple':
+        $text = file_get_contents(dirname(__FILE__) . '/README.md');
+        if (!\Drupal::moduleHandler()->moduleExists('markdown')) {
+          return '<pre>' . $text . '</pre>';
+        }
+        else {
+          // Use the Markdown filter to render the README.
+          $filter_manager = \Drupal::service('plugin.manager.filter');
+          $settings = \Drupal::configFactory()->get('markdown.settings')->getRawData();
+          $config = [
+            'settings' => $settings,
+          ];
+          $filter = $filter_manager->createInstance('markdown', $config);
+          return $filter->process($text, 'en');
+        }
+    }
+    return NULL;
+  }
+
+}
