diff --git a/Build/Sources/TypeScript/backend/localization.ts b/Build/Sources/TypeScript/backend/localization.ts
index 4333cc736aa9929a3b9cf51e8128412a08e40d78..a627a8b836745fefee1eaa2fc00974cd0ee47dd3 100644
--- a/Build/Sources/TypeScript/backend/localization.ts
+++ b/Build/Sources/TypeScript/backend/localization.ts
@@ -17,7 +17,8 @@ import { AjaxResponse } from '@typo3/core/ajax/ajax-response';
 import { SeverityEnum } from './enum/severity';
 import AjaxRequest from '@typo3/core/ajax/ajax-request';
 import Icons from './icons';
-import Wizard from './wizard';
+import Modal, { ModalElement } from './modal';
+import MultiStepWizard, { MultiStepWizardSettings } from './multi-step-wizard';
 import '@typo3/backend/element/icon-element';
 
 type LanguageRecord = {
@@ -44,9 +45,6 @@ type SummaryRecord = {
 
 class Localization {
   private readonly triggerButton: string = '.t3js-localize';
-  private localizationMode: string = null;
-  private sourceLanguage: number = null;
-  private records: Array<any> = [];
 
   constructor() {
     DocumentService.ready().then((): void => {
@@ -54,279 +52,284 @@ class Localization {
     });
   }
 
-  private initialize(): void {
-    Icons.getIcon('actions-localize', Icons.sizes.large).then((localizeIconMarkup: string): void => {
-      Icons.getIcon('actions-edit-copy', Icons.sizes.large).then((copyIconMarkup: string): void => {
-        $(this.triggerButton).removeClass('disabled');
-
-        $(document).on('click', this.triggerButton, (e: JQueryEventObject): void => {
-          e.preventDefault();
-
-          const $triggerButton = $(e.currentTarget);
-          const actions: Array<string> = [];
-          const availableLocalizationModes: Array<string> = [];
-          let slideStep1: string = '';
-
-          if ($triggerButton.data('allowTranslate')) {
-            actions.push(
-              '<div class="row">'
-              + '<div class="col-sm-3">'
-              + '<label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-translate">'
-              + localizeIconMarkup
-              + '<input type="radio" name="mode" id="mode_translate" value="localize" style="display: none">'
-              + '<br>' + TYPO3.lang['localize.wizard.button.translate'] + '</label>'
-              + '</div>'
-              + '<div class="col-sm-9">'
-              + '<p class="t3js-helptext t3js-helptext-translate text-body-secondary">' + TYPO3.lang['localize.educate.translate'] + '</p>'
-              + '</div>'
-              + '</div>',
-            );
-            availableLocalizationModes.push('localize');
+  private async initialize(): Promise<void> {
+    const localizeIconMarkup = await Icons.getIcon('actions-localize', Icons.sizes.large);
+    const copyIconMarkup = await Icons.getIcon('actions-edit-copy', Icons.sizes.large);
+
+    $(this.triggerButton).removeClass('disabled');
+
+    $(document).on('click', this.triggerButton, async (e: JQueryEventObject): Promise<void> => {
+      e.preventDefault();
+
+      const $triggerButton = $(e.currentTarget);
+      const actions: Array<string> = [];
+      const availableLocalizationModes: Array<string> = [];
+      let slideStep1: string = '';
+
+      if ($triggerButton.data('allowTranslate') === 0 && $triggerButton.data('allowCopy') === 0) {
+        Modal.confirm(
+          TYPO3.lang['window.localization.mixed_mode.title'],
+          TYPO3.lang['window.localization.mixed_mode.message'],
+          SeverityEnum.warning,
+          [
+            {
+              text: TYPO3?.lang?.['button.ok'] || 'OK',
+              btnClass: 'btn-warning',
+              name: 'ok',
+              trigger: (e: Event, modal: ModalElement): void => modal.hideModal()
+            }
+          ]
+        );
+        return;
+      }
+
+      const availableLanguages: LanguageRecord[] = await (await this.loadAvailableLanguages(
+        parseInt($triggerButton.data('pageId'), 10),
+        parseInt($triggerButton.data('languageId'), 10),
+      )).resolve();
+
+      if ($triggerButton.data('allowTranslate')) {
+        actions.push(
+          '<div class="row">'
+          + '<div class="col-sm-3">'
+          + '<label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-translate">'
+          + localizeIconMarkup
+          + '<input type="radio" name="mode" id="mode_translate" value="localize" style="display: none">'
+          + '<br>' + TYPO3.lang['localize.wizard.button.translate'] + '</label>'
+          + '</div>'
+          + '<div class="col-sm-9">'
+          + '<p class="t3js-helptext t3js-helptext-translate text-body-secondary">' + TYPO3.lang['localize.educate.translate'] + '</p>'
+          + '</div>'
+          + '</div>',
+        );
+        availableLocalizationModes.push('localize');
+      }
+
+      if ($triggerButton.data('allowCopy')) {
+        actions.push(
+          '<div class="row">'
+          + '<div class="col-sm-3">'
+          + '<label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-copy">'
+          + copyIconMarkup
+          + '<input type="radio" name="mode" id="mode_copy" value="copyFromLanguage" style="display: none">'
+          + '<br>' + TYPO3.lang['localize.wizard.button.copy'] + '</label>'
+          + '</div>'
+          + '<div class="col-sm-9">'
+          + '<p class="t3js-helptext t3js-helptext-copy text-body-secondary">' + TYPO3.lang['localize.educate.copy'] + '</p>'
+          + '</div>'
+          + '</div>',
+        );
+        availableLocalizationModes.push('copyFromLanguage');
+      }
+
+      if (availableLocalizationModes.length === 1) {
+        MultiStepWizard.set('localizationMode', availableLocalizationModes[0]);
+      } else {
+        slideStep1 += '<div data-bs-toggle="buttons">' + actions.join('') + '</div>';
+        MultiStepWizard.addSlide(
+          'localize-choose-action',
+          TYPO3.lang['localize.wizard.header_page']
+            .replace('{0}', $triggerButton.data('page'))
+            .replace('{1}', $triggerButton.data('languageName')),
+          slideStep1,
+          SeverityEnum.notice,
+          TYPO3.lang['localize.wizard.step.selectMode'],
+          ($slide: JQuery, settings: MultiStepWizardSettings): void => {
+            if (settings.localizationMode !== undefined) {
+              MultiStepWizard.unlockNextStep();
+            }
           }
+        );
+      }
+
+      if (availableLanguages.length === 1) {
+        MultiStepWizard.set('sourceLanguage', availableLanguages[0].uid);
+      } else {
+        MultiStepWizard.addSlide(
+          'localize-choose-language',
+          TYPO3.lang['localize.view.chooseLanguage'],
+          '',
+          SeverityEnum.notice,
+          TYPO3.lang['localize.wizard.step.chooseLanguage'],
+          async ($slide: JQuery, settings: MultiStepWizardSettings): Promise<void> => {
+            if (settings.sourceLanguage !== undefined) {
+              MultiStepWizard.unlockNextStep();
+            }
 
-          if ($triggerButton.data('allowCopy')) {
-            actions.push(
-              '<div class="row">'
-              + '<div class="col-sm-3">'
-              + '<label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-copy">'
-              + copyIconMarkup
-              + '<input type="radio" name="mode" id="mode_copy" value="copyFromLanguage" style="display: none">'
-              + '<br>' + TYPO3.lang['localize.wizard.button.copy'] + '</label>'
-              + '</div>'
-              + '<div class="col-sm-9">'
-              + '<p class="t3js-helptext t3js-helptext-copy text-body-secondary">' + TYPO3.lang['localize.educate.copy'] + '</p>'
-              + '</div>'
-              + '</div>',
-            );
-            availableLocalizationModes.push('copyFromLanguage');
-          }
+            $slide.html('<div class="text-center">' + (await Icons.getIcon('spinner-circle', Icons.sizes.large)) + '</div>');
+            MultiStepWizard.getComponent().on('click', '.t3js-language-option', (optionEvt: JQueryEventObject): void => {
+              const $me = $(optionEvt.currentTarget);
+              const $radio = $me.prev();
 
-          if ($triggerButton.data('allowTranslate') === 0 && $triggerButton.data('allowCopy') === 0) {
-            actions.push(
-              '<div class="row">'
-              + '<div class="col-sm-12">'
-              + '<div class="alert alert-warning">'
-              + '<div class="media">'
-              + '<div class="media-left">'
-              + '<span class="icon-emphasized"><typo3-backend-icon identifier="actions-exclamation" size="small"></typo3-backend-icon></span>'
-              + '</div>'
-              + '<div class="media-body">'
-              + '<p class="alert-message">' + TYPO3.lang['localize.educate.noTranslate'] + '</p>'
-              + '</div>'
-              + '</div>'
-              + '</div>'
-              + '</div>'
-              + '</div>',
-            );
-          }
+              MultiStepWizard.set('sourceLanguage', $radio.val());
+              MultiStepWizard.unlockNextStep();
+            });
 
-          slideStep1 += '<div data-bs-toggle="buttons">' + actions.join('') + '</div>';
-          Wizard.addSlide(
-            'localize-choose-action',
-            TYPO3.lang['localize.wizard.header_page']
-              .replace('{0}', $triggerButton.data('page'))
-              .replace('{1}', $triggerButton.data('languageName')),
-            slideStep1,
-            SeverityEnum.notice,
-            (): void => {
-              if (availableLocalizationModes.length === 1) {
-                // In case only one mode is available, select the mode and continue
-                this.localizationMode = availableLocalizationModes[0];
-                Wizard.unlockNextStep().get(0).click();
-              }
-            }
-          );
-          Wizard.addSlide(
-            'localize-choose-language',
-            TYPO3.lang['localize.view.chooseLanguage'],
-            '',
-            SeverityEnum.notice,
-            ($slide: JQuery): void => {
-              Icons.getIcon('spinner-circle', Icons.sizes.large).then((markup: string): void => {
-                $slide.html('<div class="text-center">' + markup + '</div>');
-
-                this.loadAvailableLanguages(
-                  parseInt($triggerButton.data('pageId'), 10),
-                  parseInt($triggerButton.data('languageId'), 10),
-                ).then(async (response: AjaxResponse): Promise<void> => {
-                  const result: Array<LanguageRecord> = await response.resolve();
-                  if (result.length === 1) {
-                    // We only have one result, auto select the record and continue
-                    this.sourceLanguage = result[0].uid;
-                    Wizard.unlockNextStep().get(0).click();
-                    return;
-                  }
-
-                  Wizard.getComponent().on('click', '.t3js-language-option', (optionEvt: JQueryEventObject): void => {
-                    const $me = $(optionEvt.currentTarget);
-                    const $radio = $me.prev();
-
-                    this.sourceLanguage = $radio.val();
-                    Wizard.unlockNextStep();
-                  });
-
-                  const $languageButtons = $('<div />', { class: 'row' });
-
-                  for (const languageObject of result) {
-                    const id: string = 'language' + languageObject.uid;
-                    const $input: JQuery = $('<input />', {
-                      type: 'radio',
-                      name: 'language',
-                      id: id,
-                      value: languageObject.uid,
-                      style: 'display: none;',
-                      class: 'btn-check'
-                    });
-                    const $label: JQuery = $('<label />', { class: 'btn btn-default d-block t3js-language-option option', 'for': id })
-                      .text(' ' + languageObject.title)
-                      .prepend(languageObject.flagIcon);
-
-                    $languageButtons.append(
-                      $('<div />', { class: 'col-sm-4' })
-                        .append($input)
-                        .append($label),
-                    );
-                  }
-                  $slide.empty().append($languageButtons);
-                });
-              });
-            },
-          );
-          Wizard.addSlide(
-            'localize-summary',
-            TYPO3.lang['localize.view.summary'],
-            '',
-            SeverityEnum.notice, ($slide: JQuery): void => {
-              Icons.getIcon('spinner-circle', Icons.sizes.large).then((markup: string): void => {
-                $slide.html('<div class="text-center">' + markup + '</div>');
+            const $languageButtons = $('<div />', { class: 'row' });
+
+            for (const languageObject of availableLanguages) {
+              const id: string = 'language' + languageObject.uid;
+              const $input: JQuery = $('<input />', {
+                type: 'radio',
+                name: 'language',
+                id: id,
+                value: languageObject.uid,
+                style: 'display: none;',
+                class: 'btn-check'
               });
-              this.getSummary(
-                parseInt($triggerButton.data('pageId'), 10),
-                parseInt($triggerButton.data('languageId'), 10),
-              ).then(async (response: AjaxResponse): Promise<void> => {
-                const result: SummaryRecord = await response.resolve();
-                $slide.empty();
-                this.records = [];
-
-                const columns = result.columns.columns;
-                const columnList = result.columns.columnList;
-
-                columnList.forEach((colPos: number): void => {
-                  if (typeof result.records[colPos] === 'undefined') {
-                    return;
-                  }
-
-                  const column = columns[colPos];
-                  const $row = $('<div />', { class: 'row' });
-
-                  result.records[colPos].forEach((record: SummaryColPosRecord): void => {
-                    const label = ' (' + record.uid + ') ' + record.title;
-                    this.records.push(record.uid);
-
-                    $row.append(
-                      $('<div />', { 'class': 'col-sm-6' }).append(
-                        $('<div />', { 'class': 'input-group' }).append(
-                          $('<span />', { 'class': 'input-group-text' }).append(
-                            $('<input />', {
-                              type: 'checkbox',
-                              'class': 't3js-localization-toggle-record',
-                              id: 'record-uid-' + record.uid,
-                              checked: 'checked',
-                              'data-uid': record.uid,
-                              'aria-label': label,
-                            }),
-                          ),
-                          $('<label />', {
-                            'class': 'form-control',
-                            for: 'record-uid-' + record.uid,
-                          }).text(label).prepend(record.icon),
-                        ),
-                      ),
-                    );
-                  });
-
-                  $slide.append(
-                    $('<fieldset />', {
-                      'class': 'localization-fieldset',
-                    }).append(
-                      $('<label />').text(column).prepend(
-                        $('<input />', {
-                          'class': 't3js-localization-toggle-column',
-                          type: 'checkbox',
-                          checked: 'checked',
-                        }),
-                      ),
-                      $row,
+              const $label: JQuery = $('<label />', {
+                class: 'btn btn-default d-block t3js-language-option option',
+                'for': id
+              })
+                .text(' ' + languageObject.title)
+                .prepend(languageObject.flagIcon);
+
+              $languageButtons.append(
+                $('<div />', { class: 'col-sm-4' })
+                  .append($input)
+                  .append($label),
+              );
+            }
+            $slide.empty().append($languageButtons);
+          },
+        );
+      }
+      MultiStepWizard.addSlide(
+        'localize-summary',
+        TYPO3.lang['localize.view.summary'],
+        '',
+        SeverityEnum.notice,
+        TYPO3.lang['localize.wizard.step.selectRecords'],
+        async ($slide: JQuery, settings: MultiStepWizardSettings): Promise<void> => {
+          $slide.empty().html('<div class="text-center">' + (await Icons.getIcon('spinner-circle', Icons.sizes.large)) + '</div>');
+
+          const result: SummaryRecord = await (await this.getSummary(
+            parseInt($triggerButton.data('pageId'), 10),
+            parseInt($triggerButton.data('languageId'), 10),
+            settings.sourceLanguage
+          )).resolve();
+
+          $slide.empty();
+
+          MultiStepWizard.set('records', []);
+
+          const columns = result.columns.columns;
+          const columnList = result.columns.columnList;
+
+          columnList.forEach((colPos: number): void => {
+            if (typeof result.records[colPos] === 'undefined') {
+              return;
+            }
+
+            const column = columns[colPos];
+            const $row = $('<div />', { class: 'row' });
+
+            result.records[colPos].forEach((record: SummaryColPosRecord): void => {
+              const label = ' (' + record.uid + ') ' + record.title;
+              settings.records.push(record.uid);
+
+              $row.append(
+                $('<div />', { 'class': 'col-sm-6' }).append(
+                  $('<div />', { 'class': 'input-group' }).append(
+                    $('<span />', { 'class': 'input-group-text' }).append(
+                      $('<input />', {
+                        type: 'checkbox',
+                        'class': 't3js-localization-toggle-record',
+                        id: 'record-uid-' + record.uid,
+                        checked: 'checked',
+                        'data-uid': record.uid,
+                        'aria-label': label,
+                      }),
                     ),
-                  );
-                });
-
-                Wizard.unlockNextStep();
-
-                Wizard.getComponent().on('change', '.t3js-localization-toggle-record', (cmpEvt: JQueryEventObject): void => {
-                  const $me = $(cmpEvt.currentTarget);
-                  const uid = $me.data('uid');
-                  const $parent = $me.closest('fieldset');
-                  const $columnCheckbox = $parent.find('.t3js-localization-toggle-column');
-
-                  if ($me.is(':checked')) {
-                    this.records.push(uid);
-                  } else {
-                    const index = this.records.indexOf(uid);
-                    if (index > -1) {
-                      this.records.splice(index, 1);
-                    }
-                  }
-
-                  const $allChildren = $parent.find('.t3js-localization-toggle-record');
-                  const $checkedChildren = $parent.find('.t3js-localization-toggle-record:checked');
-
-                  $columnCheckbox.prop('checked', $checkedChildren.length > 0);
-                  $columnCheckbox.prop('__indeterminate', $checkedChildren.length > 0 && $checkedChildren.length < $allChildren.length);
-
-                  if (this.records.length > 0) {
-                    Wizard.unlockNextStep();
-                  } else {
-                    Wizard.lockNextStep();
-                  }
-                }).on('change', '.t3js-localization-toggle-column', (toggleEvt: JQueryEventObject): void => {
-                  const $me = $(toggleEvt.currentTarget);
-                  const $children = $me.closest('fieldset').find('.t3js-localization-toggle-record');
-
-                  $children.prop('checked', $me.is(':checked'));
-                  $children.trigger('change');
-                });
-              });
-            },
-          );
-
-          Wizard.addFinalProcessingSlide((): void => {
-            this.localizeRecords(
-              parseInt($triggerButton.data('pageId'), 10),
-              parseInt($triggerButton.data('languageId'), 10),
-              this.records,
-            ).then((): void => {
-              Wizard.dismiss();
-              document.location.reload();
+                    $('<label />', {
+                      'class': 'form-control',
+                      for: 'record-uid-' + record.uid,
+                    }).text(label).prepend(record.icon),
+                  ),
+                ),
+              );
             });
-          }).then((): void => {
-            Wizard.show();
 
-            Wizard.getComponent().on('click', '.t3js-localization-option', (optionEvt: JQueryEventObject): void => {
-              const $me = $(optionEvt.currentTarget);
-              const $radio = $me.find('input[type="radio"]');
-
-              if ($me.data('helptext')) {
-                const $container = $(optionEvt.delegateTarget);
-                $container.find('.t3js-localization-option').removeClass('active');
-                $container.find('.t3js-helptext').addClass('text-body-secondary');
-                $me.addClass('active');
-                $container.find($me.data('helptext')).removeClass('text-body-secondary');
+            $slide.append(
+              $('<fieldset />', {
+                'class': 'localization-fieldset',
+              }).append(
+                $('<label />').text(column).prepend(
+                  $('<input />', {
+                    'class': 't3js-localization-toggle-column',
+                    type: 'checkbox',
+                    checked: 'checked',
+                  }),
+                ),
+                $row,
+              ),
+            );
+          });
+
+          MultiStepWizard.unlockNextStep();
+
+          MultiStepWizard.getComponent().on('change', '.t3js-localization-toggle-record', (cmpEvt: JQueryEventObject): void => {
+            const $me = $(cmpEvt.currentTarget);
+            const uid = $me.data('uid');
+            const $parent = $me.closest('fieldset');
+            const $columnCheckbox = $parent.find('.t3js-localization-toggle-column');
+
+            if ($me.is(':checked')) {
+              settings.records.push(uid);
+            } else {
+              const index = settings.records.indexOf(uid);
+              if (index > -1) {
+                settings.records.splice(index, 1);
               }
-              this.localizationMode = $radio.val();
-              Wizard.unlockNextStep();
-            });
+            }
+
+            const $allChildren = $parent.find('.t3js-localization-toggle-record');
+            const $checkedChildren = $parent.find('.t3js-localization-toggle-record:checked');
+
+            $columnCheckbox.prop('checked', $checkedChildren.length > 0);
+            $columnCheckbox.prop('__indeterminate', $checkedChildren.length > 0 && $checkedChildren.length < $allChildren.length);
+
+            if (settings.records.length > 0) {
+              MultiStepWizard.unlockNextStep();
+            } else {
+              MultiStepWizard.lockNextStep();
+            }
+          }).on('change', '.t3js-localization-toggle-column', (toggleEvt: JQueryEventObject): void => {
+            const $me = $(toggleEvt.currentTarget);
+            const $children = $me.closest('fieldset').find('.t3js-localization-toggle-record');
+
+            $children.prop('checked', $me.is(':checked'));
+            $children.trigger('change');
           });
+        },
+      );
+
+      MultiStepWizard.addFinalProcessingSlide(async ($slide: JQuery, settings: MultiStepWizardSettings): Promise<void> => {
+        await this.localizeRecords(
+          parseInt($triggerButton.data('pageId'), 10),
+          parseInt($triggerButton.data('languageId'), 10),
+          settings.sourceLanguage,
+          settings.localizationMode,
+          settings.records,
+        );
+        MultiStepWizard.dismiss();
+        document.location.reload();
+      }).then((): void => {
+        MultiStepWizard.show();
+
+        MultiStepWizard.getComponent().on('click', '.t3js-localization-option', (optionEvt: JQueryEventObject): void => {
+          const $me = $(optionEvt.currentTarget);
+          const $radio = $me.find('input[type="radio"]');
+
+          if ($me.data('helptext')) {
+            const $container = $(optionEvt.delegateTarget);
+            $container.find('.t3js-localization-option').removeClass('active');
+            $container.find('.t3js-helptext').addClass('text-body-secondary');
+            $me.addClass('active');
+            $container.find($me.data('helptext')).removeClass('text-body-secondary');
+          }
+          MultiStepWizard.set('localizationMode', $radio.val());
+          MultiStepWizard.unlockNextStep();
         });
       });
     });
@@ -348,33 +351,24 @@ class Localization {
 
   /**
    * Get summary for record processing
-   *
-   * @param {number} pageId
-   * @param {number} languageId
-   * @returns {Promise<AjaxResponse>}
    */
-  private getSummary(pageId: number, languageId: number): Promise<AjaxResponse> {
+  private getSummary(pageId: number, languageId: number, sourceLanguage: number): Promise<AjaxResponse> {
     return new AjaxRequest(TYPO3.settings.ajaxUrls.records_localize_summary).withQueryArguments({
       pageId: pageId,
       destLanguageId: languageId,
-      languageId: this.sourceLanguage,
+      languageId: sourceLanguage,
     }).get();
   }
 
   /**
    * Localize records
-   *
-   * @param {number} pageId
-   * @param {number} languageId
-   * @param {Array<number>} uidList
-   * @returns {Promise<AjaxResponse>}
    */
-  private localizeRecords(pageId: number, languageId: number, uidList: Array<number>): Promise<AjaxResponse> {
+  private localizeRecords(pageId: number, languageId: number, sourceLanguage: number, localizationMode: string, uidList: Array<number>): Promise<AjaxResponse> {
     return new AjaxRequest(TYPO3.settings.ajaxUrls.records_localize).withQueryArguments({
       pageId: pageId,
-      srcLanguageId: this.sourceLanguage,
+      srcLanguageId: sourceLanguage,
       destLanguageId: languageId,
-      action: this.localizationMode,
+      action: localizationMode,
       uidList: uidList,
     }).get();
   }
diff --git a/Build/Sources/TypeScript/backend/multi-step-wizard.ts b/Build/Sources/TypeScript/backend/multi-step-wizard.ts
index 2ddb31dca28b0745702ee6fb158f773bedfd1f1f..209637d1d63fe9ba53bd62765b88780e9c5c3561 100644
--- a/Build/Sources/TypeScript/backend/multi-step-wizard.ts
+++ b/Build/Sources/TypeScript/backend/multi-step-wizard.ts
@@ -20,12 +20,12 @@ import Icons from './icons';
 
 type SlideCallback = ($slide: JQuery, settings: MultiStepWizardSettings, identifier: string) => void;
 
-interface MultiStepWizardSettings {
+export interface MultiStepWizardSettings {
   [key: string]: any;
 }
 
 interface MultiStepWizardSetup {
-  slides: Array<any>;
+  slides: Slide[];
   settings: MultiStepWizardSettings;
   forceSelection: boolean;
   $carousel: JQuery;
@@ -108,23 +108,22 @@ class MultiStepWizard {
    * @param {Function} callback
    * @returns {Promise<string>}
    */
-  public addFinalProcessingSlide(callback?: SlideCallback): Promise<void> {
+  public async addFinalProcessingSlide(callback?: SlideCallback): Promise<void> {
     if (!callback) {
       callback = (): void => {
         this.dismiss();
       };
     }
 
-    return Icons.getIcon('spinner-circle', Icons.sizes.default, null, null).then((markup: string) => {
-      const $processingSlide = $('<div />', { class: 'text-center' }).append(markup);
-      this.addSlide(
-        'final-processing-slide', top.TYPO3.lang['wizard.processing.title'],
-        $processingSlide[0].outerHTML,
-        Severity.info,
-        null,
-        callback,
-      );
-    });
+    const spinnerIcon = await Icons.getIcon('spinner-circle', Icons.sizes.large, null, null);
+    const $processingSlide = $('<div />', { class: 'text-center' }).append(spinnerIcon);
+    this.addSlide(
+      'final-processing-slide', top.TYPO3.lang['wizard.processing.title'],
+      $processingSlide[0].outerHTML,
+      Severity.info,
+      null,
+      callback,
+    );
   }
 
   /**
@@ -166,6 +165,12 @@ class MultiStepWizard {
     });
 
     this.getComponent().on('wizard-visible', (): void => {
+      if (this.setup.forceSelection) {
+        // @todo: This is a hack as modal buttons cannot be initially disabled.
+        this.lockPrevStep();
+        this.lockNextStep();
+      }
+
       this.runSlideCallback(firstSlide, this.setup.$carousel.find('.carousel-item').first());
     }).on('wizard-dismissed', (): void => {
       this.setup = $.extend(true, {}, this.originalSetup);
@@ -282,11 +287,11 @@ class MultiStepWizard {
       const currentIndex = this.setup.$carousel.data('currentIndex');
       const slide = this.setup.slides[currentIndex];
 
-      this.runSlideCallback(slide, $(evt.relatedTarget));
-
       if (this.setup.forceSelection) {
         this.lockNextStep();
       }
+
+      this.runSlideCallback(slide, $(evt.relatedTarget));
     });
 
     // Custom event, closes the wizard
@@ -331,9 +336,15 @@ class MultiStepWizard {
     const nextSlideNumber = this.setup.$carousel.data('currentSlide') + 1;
     const currentIndex = this.setup.$carousel.data('currentIndex');
     const nextIndex = currentIndex + 1;
+    const $slideContent = $modal.find('.carousel-item:eq(' + nextIndex + ')');
 
+    // Flush content when sliding
+    $slideContent.empty().append(this.setup.slides[nextIndex].content);
     $modalTitle.text(this.setup.slides[nextIndex].title);
 
+    // Always unlock previous step
+    this.unlockPrevStep();
+
     this.setup.$carousel.data('currentSlide', nextSlideNumber);
     this.setup.$carousel.data('currentIndex', nextIndex);
 
@@ -368,12 +379,22 @@ class MultiStepWizard {
     const nextSlideNumber = this.setup.$carousel.data('currentSlide') - 1;
     const currentIndex = this.setup.$carousel.data('currentIndex');
     const nextIndex = currentIndex - 1;
+    const $slideContent = $modal.find('.carousel-item:eq(' + nextIndex + ')');
+
+    // Flush content when sliding
+    $slideContent.empty().append(this.setup.slides[nextIndex].content);
+    $modalTitle.text(this.setup.slides[nextIndex].title);
+
+    // Always unlock previous step if there is any
+    if (nextIndex > 0) {
+      this.unlockPrevStep();
+    } else {
+      this.lockPrevStep();
+    }
 
     this.setup.$carousel.data('currentSlide', nextSlideNumber);
     this.setup.$carousel.data('currentIndex', nextIndex);
 
-    $modalTitle.text(this.setup.slides[nextIndex].title);
-
     $modalFooter.find('.progress-bar.last-step')
       .width(this.setup.$carousel.data('initialStep') + '%')
       .text(this.getProgressBarTitle(this.setup.$carousel.data('slideCount') - 1));
diff --git a/typo3/sysext/backend/Resources/Private/Language/locallang_layout.xlf b/typo3/sysext/backend/Resources/Private/Language/locallang_layout.xlf
index 75b9c93db0710d5aec7bdceb14d4b06419146dff..733028994f31b679fff18a135512494f225812cf 100644
--- a/typo3/sysext/backend/Resources/Private/Language/locallang_layout.xlf
+++ b/typo3/sysext/backend/Resources/Private/Language/locallang_layout.xlf
@@ -366,6 +366,18 @@
 			<trans-unit id="localize.wizard.button.process" resname="localize.wizard.button.process">
 				<source>Start processing</source>
 			</trans-unit>
+			<trans-unit id="localize.wizard.step.selectMode" resname="localize.wizard.step.selectMode">
+				<source>Localization mode</source>
+			</trans-unit>
+			<trans-unit id="localize.wizard.step.chooseLanguage" resname="localize.wizard.step.chooseLanguage">
+				<source>Source Language</source>
+			</trans-unit>
+			<trans-unit id="localize.wizard.step.chooseLanguage" resname="localize.wizard.step.chooseLanguage">
+				<source>Source Language</source>
+			</trans-unit>
+			<trans-unit id="localize.wizard.step.selectRecords" resname="localize.wizard.step.selectRecords">
+				<source>Records</source>
+			</trans-unit>
 			<trans-unit id="localize.view.chooseLanguage" resname="localize.view.chooseLanguage">
 				<source>Choose the language from which you want to localize the content</source>
 			</trans-unit>
@@ -390,10 +402,12 @@
 					&lt;strong&gt;Use this when you want to have freedom in designing your translated website.&lt;/strong&gt;
 				</source>
 			</trans-unit>
-			<trans-unit id="localize.educate.noTranslate" resname="localize.educate.noTranslate" xml:space="preserve">
-				<source>Using the translation wizard is not possible, because either translation is disabled or the translated page is in "mixed mode". Being in "mixed mode" means that both translated records that have a source language and "free mode" records without a parent, are present.&lt;br&gt;
-					We highlighted the problematic records for you in this case.
-				</source>
+			<trans-unit id="window.localization.mixed_mode.title" resname="window.localization.mixed_mode.title">
+				<source>Localization not possible</source>
+			</trans-unit>
+			<trans-unit id="window.localization.mixed_mode.message" resname="window.localization.mixed_mode.message">
+				<source>Using the translation wizard is not possible, because either translation is disabled or the translated page is in "mixed mode". Being in "mixed mode" means that both translated records that have a source language and "free mode" records without a parent, are present.
+					We highlighted the problematic records for you in this case.</source>
 			</trans-unit>
 			<trans-unit id="error.invalidBackendLayout" resname="error.invalidBackendLayout">
 				<source>The selected page layout is mis-configured, no columns are specified to be editable. Please update your backend layout to have at least one parameter "colPos" set.</source>
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/localization.js b/typo3/sysext/backend/Resources/Public/JavaScript/localization.js
index 5a2e9fb79a566a57925edcd65e3f6ee29c55f1d4..96be7d8b0171a1638099153955fafe3aee161582 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/localization.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/localization.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Icons from"@typo3/backend/icons.js";import Wizard from"@typo3/backend/wizard.js";import"@typo3/backend/element/icon-element.js";class Localization{constructor(){this.triggerButton=".t3js-localize",this.localizationMode=null,this.sourceLanguage=null,this.records=[],DocumentService.ready().then((()=>{this.initialize()}))}initialize(){Icons.getIcon("actions-localize",Icons.sizes.large).then((e=>{Icons.getIcon("actions-edit-copy",Icons.sizes.large).then((a=>{$(this.triggerButton).removeClass("disabled"),$(document).on("click",this.triggerButton,(t=>{t.preventDefault();const o=$(t.currentTarget),i=[],l=[];let s="";o.data("allowTranslate")&&(i.push('<div class="row"><div class="col-sm-3"><label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-translate">'+e+'<input type="radio" name="mode" id="mode_translate" value="localize" style="display: none"><br>'+TYPO3.lang["localize.wizard.button.translate"]+'</label></div><div class="col-sm-9"><p class="t3js-helptext t3js-helptext-translate text-body-secondary">'+TYPO3.lang["localize.educate.translate"]+"</p></div></div>"),l.push("localize")),o.data("allowCopy")&&(i.push('<div class="row"><div class="col-sm-3"><label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-copy">'+a+'<input type="radio" name="mode" id="mode_copy" value="copyFromLanguage" style="display: none"><br>'+TYPO3.lang["localize.wizard.button.copy"]+'</label></div><div class="col-sm-9"><p class="t3js-helptext t3js-helptext-copy text-body-secondary">'+TYPO3.lang["localize.educate.copy"]+"</p></div></div>"),l.push("copyFromLanguage")),0===o.data("allowTranslate")&&0===o.data("allowCopy")&&i.push('<div class="row"><div class="col-sm-12"><div class="alert alert-warning"><div class="media"><div class="media-left"><span class="icon-emphasized"><typo3-backend-icon identifier="actions-exclamation" size="small"></typo3-backend-icon></span></div><div class="media-body"><p class="alert-message">'+TYPO3.lang["localize.educate.noTranslate"]+"</p></div></div></div></div></div>"),s+='<div data-bs-toggle="buttons">'+i.join("")+"</div>",Wizard.addSlide("localize-choose-action",TYPO3.lang["localize.wizard.header_page"].replace("{0}",o.data("page")).replace("{1}",o.data("languageName")),s,SeverityEnum.notice,(()=>{1===l.length&&(this.localizationMode=l[0],Wizard.unlockNextStep().get(0).click())})),Wizard.addSlide("localize-choose-language",TYPO3.lang["localize.view.chooseLanguage"],"",SeverityEnum.notice,(e=>{Icons.getIcon("spinner-circle",Icons.sizes.large).then((a=>{e.html('<div class="text-center">'+a+"</div>"),this.loadAvailableLanguages(parseInt(o.data("pageId"),10),parseInt(o.data("languageId"),10)).then((async a=>{const t=await a.resolve();if(1===t.length)return this.sourceLanguage=t[0].uid,void Wizard.unlockNextStep().get(0).click();Wizard.getComponent().on("click",".t3js-language-option",(e=>{const a=$(e.currentTarget).prev();this.sourceLanguage=a.val(),Wizard.unlockNextStep()}));const o=$("<div />",{class:"row"});for(const e of t){const a="language"+e.uid,t=$("<input />",{type:"radio",name:"language",id:a,value:e.uid,style:"display: none;",class:"btn-check"}),i=$("<label />",{class:"btn btn-default d-block t3js-language-option option",for:a}).text(" "+e.title).prepend(e.flagIcon);o.append($("<div />",{class:"col-sm-4"}).append(t).append(i))}e.empty().append(o)}))}))})),Wizard.addSlide("localize-summary",TYPO3.lang["localize.view.summary"],"",SeverityEnum.notice,(e=>{Icons.getIcon("spinner-circle",Icons.sizes.large).then((a=>{e.html('<div class="text-center">'+a+"</div>")})),this.getSummary(parseInt(o.data("pageId"),10),parseInt(o.data("languageId"),10)).then((async a=>{const t=await a.resolve();e.empty(),this.records=[];const o=t.columns.columns;t.columns.columnList.forEach((a=>{if(void 0===t.records[a])return;const i=o[a],l=$("<div />",{class:"row"});t.records[a].forEach((e=>{const a=" ("+e.uid+") "+e.title;this.records.push(e.uid),l.append($("<div />",{class:"col-sm-6"}).append($("<div />",{class:"input-group"}).append($("<span />",{class:"input-group-text"}).append($("<input />",{type:"checkbox",class:"t3js-localization-toggle-record",id:"record-uid-"+e.uid,checked:"checked","data-uid":e.uid,"aria-label":a})),$("<label />",{class:"form-control",for:"record-uid-"+e.uid}).text(a).prepend(e.icon))))})),e.append($("<fieldset />",{class:"localization-fieldset"}).append($("<label />").text(i).prepend($("<input />",{class:"t3js-localization-toggle-column",type:"checkbox",checked:"checked"})),l))})),Wizard.unlockNextStep(),Wizard.getComponent().on("change",".t3js-localization-toggle-record",(e=>{const a=$(e.currentTarget),t=a.data("uid"),o=a.closest("fieldset"),i=o.find(".t3js-localization-toggle-column");if(a.is(":checked"))this.records.push(t);else{const e=this.records.indexOf(t);e>-1&&this.records.splice(e,1)}const l=o.find(".t3js-localization-toggle-record"),s=o.find(".t3js-localization-toggle-record:checked");i.prop("checked",s.length>0),i.prop("__indeterminate",s.length>0&&s.length<l.length),this.records.length>0?Wizard.unlockNextStep():Wizard.lockNextStep()})).on("change",".t3js-localization-toggle-column",(e=>{const a=$(e.currentTarget),t=a.closest("fieldset").find(".t3js-localization-toggle-record");t.prop("checked",a.is(":checked")),t.trigger("change")}))}))})),Wizard.addFinalProcessingSlide((()=>{this.localizeRecords(parseInt(o.data("pageId"),10),parseInt(o.data("languageId"),10),this.records).then((()=>{Wizard.dismiss(),document.location.reload()}))})).then((()=>{Wizard.show(),Wizard.getComponent().on("click",".t3js-localization-option",(e=>{const a=$(e.currentTarget),t=a.find('input[type="radio"]');if(a.data("helptext")){const t=$(e.delegateTarget);t.find(".t3js-localization-option").removeClass("active"),t.find(".t3js-helptext").addClass("text-body-secondary"),a.addClass("active"),t.find(a.data("helptext")).removeClass("text-body-secondary")}this.localizationMode=t.val(),Wizard.unlockNextStep()}))}))}))}))}))}loadAvailableLanguages(e,a){return new AjaxRequest(TYPO3.settings.ajaxUrls.page_languages).withQueryArguments({pageId:e,languageId:a}).get()}getSummary(e,a){return new AjaxRequest(TYPO3.settings.ajaxUrls.records_localize_summary).withQueryArguments({pageId:e,destLanguageId:a,languageId:this.sourceLanguage}).get()}localizeRecords(e,a,t){return new AjaxRequest(TYPO3.settings.ajaxUrls.records_localize).withQueryArguments({pageId:e,srcLanguageId:this.sourceLanguage,destLanguageId:a,action:this.localizationMode,uidList:t}).get()}}export default new Localization;
\ No newline at end of file
+import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import Icons from"@typo3/backend/icons.js";import Modal from"@typo3/backend/modal.js";import MultiStepWizard from"@typo3/backend/multi-step-wizard.js";import"@typo3/backend/element/icon-element.js";class Localization{constructor(){this.triggerButton=".t3js-localize",DocumentService.ready().then((()=>{this.initialize()}))}async initialize(){const e=await Icons.getIcon("actions-localize",Icons.sizes.large),t=await Icons.getIcon("actions-edit-copy",Icons.sizes.large);$(this.triggerButton).removeClass("disabled"),$(document).on("click",this.triggerButton,(async a=>{a.preventDefault();const o=$(a.currentTarget),l=[],i=[];let n="";if(0===o.data("allowTranslate")&&0===o.data("allowCopy"))return void Modal.confirm(TYPO3.lang["window.localization.mixed_mode.title"],TYPO3.lang["window.localization.mixed_mode.message"],SeverityEnum.warning,[{text:TYPO3?.lang?.["button.ok"]||"OK",btnClass:"btn-warning",name:"ok",trigger:(e,t)=>t.hideModal()}]);const s=await(await this.loadAvailableLanguages(parseInt(o.data("pageId"),10),parseInt(o.data("languageId"),10))).resolve();o.data("allowTranslate")&&(l.push('<div class="row"><div class="col-sm-3"><label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-translate">'+e+'<input type="radio" name="mode" id="mode_translate" value="localize" style="display: none"><br>'+TYPO3.lang["localize.wizard.button.translate"]+'</label></div><div class="col-sm-9"><p class="t3js-helptext t3js-helptext-translate text-body-secondary">'+TYPO3.lang["localize.educate.translate"]+"</p></div></div>"),i.push("localize")),o.data("allowCopy")&&(l.push('<div class="row"><div class="col-sm-3"><label class="btn btn-default d-block t3js-localization-option" data-helptext=".t3js-helptext-copy">'+t+'<input type="radio" name="mode" id="mode_copy" value="copyFromLanguage" style="display: none"><br>'+TYPO3.lang["localize.wizard.button.copy"]+'</label></div><div class="col-sm-9"><p class="t3js-helptext t3js-helptext-copy text-body-secondary">'+TYPO3.lang["localize.educate.copy"]+"</p></div></div>"),i.push("copyFromLanguage")),1===i.length?MultiStepWizard.set("localizationMode",i[0]):(n+='<div data-bs-toggle="buttons">'+l.join("")+"</div>",MultiStepWizard.addSlide("localize-choose-action",TYPO3.lang["localize.wizard.header_page"].replace("{0}",o.data("page")).replace("{1}",o.data("languageName")),n,SeverityEnum.notice,TYPO3.lang["localize.wizard.step.selectMode"],((e,t)=>{void 0!==t.localizationMode&&MultiStepWizard.unlockNextStep()}))),1===s.length?MultiStepWizard.set("sourceLanguage",s[0].uid):MultiStepWizard.addSlide("localize-choose-language",TYPO3.lang["localize.view.chooseLanguage"],"",SeverityEnum.notice,TYPO3.lang["localize.wizard.step.chooseLanguage"],(async(e,t)=>{void 0!==t.sourceLanguage&&MultiStepWizard.unlockNextStep(),e.html('<div class="text-center">'+await Icons.getIcon("spinner-circle",Icons.sizes.large)+"</div>"),MultiStepWizard.getComponent().on("click",".t3js-language-option",(e=>{const t=$(e.currentTarget).prev();MultiStepWizard.set("sourceLanguage",t.val()),MultiStepWizard.unlockNextStep()}));const a=$("<div />",{class:"row"});for(const e of s){const t="language"+e.uid,o=$("<input />",{type:"radio",name:"language",id:t,value:e.uid,style:"display: none;",class:"btn-check"}),l=$("<label />",{class:"btn btn-default d-block t3js-language-option option",for:t}).text(" "+e.title).prepend(e.flagIcon);a.append($("<div />",{class:"col-sm-4"}).append(o).append(l))}e.empty().append(a)})),MultiStepWizard.addSlide("localize-summary",TYPO3.lang["localize.view.summary"],"",SeverityEnum.notice,TYPO3.lang["localize.wizard.step.selectRecords"],(async(e,t)=>{e.empty().html('<div class="text-center">'+await Icons.getIcon("spinner-circle",Icons.sizes.large)+"</div>");const a=await(await this.getSummary(parseInt(o.data("pageId"),10),parseInt(o.data("languageId"),10),t.sourceLanguage)).resolve();e.empty(),MultiStepWizard.set("records",[]);const l=a.columns.columns;a.columns.columnList.forEach((o=>{if(void 0===a.records[o])return;const i=l[o],n=$("<div />",{class:"row"});a.records[o].forEach((e=>{const a=" ("+e.uid+") "+e.title;t.records.push(e.uid),n.append($("<div />",{class:"col-sm-6"}).append($("<div />",{class:"input-group"}).append($("<span />",{class:"input-group-text"}).append($("<input />",{type:"checkbox",class:"t3js-localization-toggle-record",id:"record-uid-"+e.uid,checked:"checked","data-uid":e.uid,"aria-label":a})),$("<label />",{class:"form-control",for:"record-uid-"+e.uid}).text(a).prepend(e.icon))))})),e.append($("<fieldset />",{class:"localization-fieldset"}).append($("<label />").text(i).prepend($("<input />",{class:"t3js-localization-toggle-column",type:"checkbox",checked:"checked"})),n))})),MultiStepWizard.unlockNextStep(),MultiStepWizard.getComponent().on("change",".t3js-localization-toggle-record",(e=>{const a=$(e.currentTarget),o=a.data("uid"),l=a.closest("fieldset"),i=l.find(".t3js-localization-toggle-column");if(a.is(":checked"))t.records.push(o);else{const e=t.records.indexOf(o);e>-1&&t.records.splice(e,1)}const n=l.find(".t3js-localization-toggle-record"),s=l.find(".t3js-localization-toggle-record:checked");i.prop("checked",s.length>0),i.prop("__indeterminate",s.length>0&&s.length<n.length),t.records.length>0?MultiStepWizard.unlockNextStep():MultiStepWizard.lockNextStep()})).on("change",".t3js-localization-toggle-column",(e=>{const t=$(e.currentTarget),a=t.closest("fieldset").find(".t3js-localization-toggle-record");a.prop("checked",t.is(":checked")),a.trigger("change")}))})),MultiStepWizard.addFinalProcessingSlide((async(e,t)=>{await this.localizeRecords(parseInt(o.data("pageId"),10),parseInt(o.data("languageId"),10),t.sourceLanguage,t.localizationMode,t.records),MultiStepWizard.dismiss(),document.location.reload()})).then((()=>{MultiStepWizard.show(),MultiStepWizard.getComponent().on("click",".t3js-localization-option",(e=>{const t=$(e.currentTarget),a=t.find('input[type="radio"]');if(t.data("helptext")){const a=$(e.delegateTarget);a.find(".t3js-localization-option").removeClass("active"),a.find(".t3js-helptext").addClass("text-body-secondary"),t.addClass("active"),a.find(t.data("helptext")).removeClass("text-body-secondary")}MultiStepWizard.set("localizationMode",a.val()),MultiStepWizard.unlockNextStep()}))}))}))}loadAvailableLanguages(e,t){return new AjaxRequest(TYPO3.settings.ajaxUrls.page_languages).withQueryArguments({pageId:e,languageId:t}).get()}getSummary(e,t,a){return new AjaxRequest(TYPO3.settings.ajaxUrls.records_localize_summary).withQueryArguments({pageId:e,destLanguageId:t,languageId:a}).get()}localizeRecords(e,t,a,o,l){return new AjaxRequest(TYPO3.settings.ajaxUrls.records_localize).withQueryArguments({pageId:e,srcLanguageId:a,destLanguageId:t,action:o,uidList:l}).get()}}export default new Localization;
\ No newline at end of file
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js b/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js
index af3e70a780db02c9fb582b858dd5f9dd62e1ef53..562fb5cf4e248ceb093b1292a349dc9cfd5c7385 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/multi-step-wizard.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import{Carousel}from"bootstrap";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Icons from"@typo3/backend/icons.js";class MultiStepWizard{constructor(){this.setup={slides:[],settings:{},forceSelection:!0,$carousel:null,carousel:null},this.originalSetup=$.extend(!0,{},this.setup)}set(t,e){return this.setup.settings[t]=e,this}addSlide(t,e,s="",i=SeverityEnum.info,r,a){const l={identifier:t,title:e,content:s,severity:i,progressBarTitle:r,callback:a};return this.setup.slides.push(l),this}addFinalProcessingSlide(t){return t||(t=()=>{this.dismiss()}),Icons.getIcon("spinner-circle",Icons.sizes.default,null,null).then((e=>{const s=$("<div />",{class:"text-center"}).append(e);this.addSlide("final-processing-slide",top.TYPO3.lang["wizard.processing.title"],s[0].outerHTML,Severity.info,null,t)}))}show(){const t=this.generateSlides(),e=this.setup.slides[0];Modal.advanced({title:e.title,content:t,severity:e.severity,staticBackdrop:!0,buttons:[{text:top.TYPO3.lang["wizard.button.cancel"],active:!0,btnClass:"btn-default float-start",name:"cancel",trigger:()=>{this.getComponent().trigger("wizard-dismiss")}},{text:top.TYPO3.lang["wizard.button.prev"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"prev"},{text:top.TYPO3.lang["wizard.button.next"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"next"}],additionalCssClasses:["modal-multi-step-wizard"],callback:t=>{this.setup.carousel=new Carousel(t.querySelector(".carousel")),this.addButtonContainer(),this.addProgressBar(),this.initializeEvents()}}),this.getComponent().on("wizard-visible",(()=>{this.runSlideCallback(e,this.setup.$carousel.find(".carousel-item").first())})).on("wizard-dismissed",(()=>{this.setup=$.extend(!0,{},this.originalSetup)}))}getComponent(){return null===this.setup.$carousel&&this.generateSlides(),this.setup.$carousel}dismiss(){Modal.dismiss()}lockNextStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!0),t}unlockNextStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!1),t}lockPrevStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!0),t}unlockPrevStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!1),t}triggerStepButton(t){const e=this.setup.$carousel.closest(".modal").find('button[name="'+t+'"]');return e.length>0&&!0!==e.prop("disabled")&&e.get(0).click(),e}blurCancelStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="cancel"]');return t.trigger("blur"),t}initializeEvents(){const t=this.setup.$carousel.closest(".modal");this.initializeSlideNextEvent(t),this.initializeSlidePrevEvent(t),this.setup.$carousel.get(0).addEventListener("slide.bs.carousel",(e=>{"left"===e.direction?this.nextSlideChanges(t):this.prevSlideChanges(t)})),this.setup.$carousel.get(0).addEventListener("slid.bs.carousel",(t=>{const e=this.setup.$carousel.data("currentIndex"),s=this.setup.slides[e];this.runSlideCallback(s,$(t.relatedTarget)),this.setup.forceSelection&&this.lockNextStep()}));const e=this.getComponent();e.on("wizard-dismiss",this.dismiss),Modal.currentModal.addEventListener("typo3-modal-hidden",(()=>{e.trigger("wizard-dismissed")})),Modal.currentModal.addEventListener("typo3-modal-shown",(()=>{e.trigger("wizard-visible")}))}initializeSlideNextEvent(t){t.find(".modal-footer").find('button[name="next"]').off().on("click",(()=>{this.setup.carousel.next()}))}initializeSlidePrevEvent(t){t.find(".modal-footer").find('button[name="prev"]').off().on("click",(()=>{this.setup.carousel.prev()}))}nextSlideChanges(t){this.initializeSlideNextEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=this.setup.$carousel.data("currentSlide")+1,r=this.setup.$carousel.data("currentIndex"),a=r+1;e.text(this.setup.slides[a].title),this.setup.$carousel.data("currentSlide",i),this.setup.$carousel.data("currentIndex",a);const l=s.find(".progress-bar");l.eq(r).width("0%"),l.eq(a).width(this.setup.$carousel.data("initialStep")*i+"%").removeClass("inactive"),this.updateCurrentSeverity(t,r,a)}prevSlideChanges(t){this.initializeSlidePrevEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=s.find('button[name="next"]'),r=this.setup.$carousel.data("currentSlide")-1,a=this.setup.$carousel.data("currentIndex"),l=a-1;this.setup.$carousel.data("currentSlide",r),this.setup.$carousel.data("currentIndex",l),e.text(this.setup.slides[l].title),s.find(".progress-bar.last-step").width(this.setup.$carousel.data("initialStep")+"%").text(this.getProgressBarTitle(this.setup.$carousel.data("slideCount")-1)),i.text(top.TYPO3.lang["wizard.button.next"]);const n=s.find(".progress-bar");n.eq(a).width(this.setup.$carousel.data("initialStep")+"%").addClass("inactive"),n.eq(l).width(this.setup.$carousel.data("initialStep")*r+"%").removeClass("inactive"),this.updateCurrentSeverity(t,a,l)}updateCurrentSeverity(t,e,s){t.find(".modal-footer").find('button[name="next"]').removeClass("btn-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("btn-"+Severity.getCssClass(this.setup.slides[s].severity)),t.removeClass("modal-severity-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("modal-severity-"+Severity.getCssClass(this.setup.slides[s].severity))}getProgressBarTitle(t){let e;return e=null===this.setup.slides[t].progressBarTitle?0===t?top.TYPO3.lang["wizard.progressStep.start"]:t>=this.setup.$carousel.data("slideCount")-1?top.TYPO3.lang["wizard.progressStep.finish"]:top.TYPO3.lang["wizard.progressStep"]+String(t+1):this.setup.slides[t].progressBarTitle,e}runSlideCallback(t,e){"function"==typeof t.callback&&t.callback(e,this.setup.settings,t.identifier)}addProgressBar(){const t=this.setup.$carousel.find(".carousel-item").length,e=Math.max(1,t),s=Math.round(100/e),i=this.setup.$carousel.closest(".modal").find(".modal-footer");if(this.setup.$carousel.data("initialStep",s).data("slideCount",e).data("realSlideCount",t).data("currentIndex",0).data("currentSlide",1),e>1){i.prepend($("<div />",{class:"progress"}));for(let t=0;t<this.setup.slides.length;++t){let e;e=0===t?"progress-bar first-step":t===this.setup.$carousel.data("slideCount")-1?"progress-bar last-step inactive":"progress-bar step inactive",i.find(".progress").append($("<div />",{role:"progressbar",class:e,"aria-valuemin":0,"aria-valuenow":s,"aria-valuemax":100}).width(s+"%").text(this.getProgressBarTitle(t)))}}}addButtonContainer(){this.setup.$carousel.closest(".modal").find(".modal-footer .btn").wrapAll('<div class="modal-btn-group" />')}generateSlides(){if(null!==this.setup.$carousel)return this.setup.$carousel;let t='<div class="carousel slide" data-bs-ride="false"><div class="carousel-inner" role="listbox">';for(let e=0;e<this.setup.slides.length;++e){const s=this.setup.slides[e];let i=s.content;"object"==typeof i&&(i=i.html()),t+='<div class="carousel-item" data-bs-slide="'+s.identifier+'" data-step="'+e+'">'+i+"</div>"}return t+="</div></div>",this.setup.$carousel=$(t),this.setup.$carousel.find(".carousel-item").first().addClass("active"),this.setup.$carousel}}let multistepWizardObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.MultiStepWizard&&(multistepWizardObject=window.opener.TYPO3.MultiStepWizard),parent&&parent.window.TYPO3&&parent.window.TYPO3.MultiStepWizard&&(multistepWizardObject=parent.window.TYPO3.MultiStepWizard),top&&top.TYPO3&&top.TYPO3.MultiStepWizard&&(multistepWizardObject=top.TYPO3.MultiStepWizard)}catch(t){}multistepWizardObject||(multistepWizardObject=new MultiStepWizard,"undefined"!=typeof TYPO3&&(TYPO3.MultiStepWizard=multistepWizardObject));export default multistepWizardObject;
\ No newline at end of file
+import{SeverityEnum}from"@typo3/backend/enum/severity.js";import $ from"jquery";import{Carousel}from"bootstrap";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Icons from"@typo3/backend/icons.js";class MultiStepWizard{constructor(){this.setup={slides:[],settings:{},forceSelection:!0,$carousel:null,carousel:null},this.originalSetup=$.extend(!0,{},this.setup)}set(t,e){return this.setup.settings[t]=e,this}addSlide(t,e,s="",i=SeverityEnum.info,r,a){const l={identifier:t,title:e,content:s,severity:i,progressBarTitle:r,callback:a};return this.setup.slides.push(l),this}async addFinalProcessingSlide(t){t||(t=()=>{this.dismiss()});const e=await Icons.getIcon("spinner-circle",Icons.sizes.large,null,null),s=$("<div />",{class:"text-center"}).append(e);this.addSlide("final-processing-slide",top.TYPO3.lang["wizard.processing.title"],s[0].outerHTML,Severity.info,null,t)}show(){const t=this.generateSlides(),e=this.setup.slides[0];Modal.advanced({title:e.title,content:t,severity:e.severity,staticBackdrop:!0,buttons:[{text:top.TYPO3.lang["wizard.button.cancel"],active:!0,btnClass:"btn-default float-start",name:"cancel",trigger:()=>{this.getComponent().trigger("wizard-dismiss")}},{text:top.TYPO3.lang["wizard.button.prev"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"prev"},{text:top.TYPO3.lang["wizard.button.next"],btnClass:"btn-"+Severity.getCssClass(e.severity),name:"next"}],additionalCssClasses:["modal-multi-step-wizard"],callback:t=>{this.setup.carousel=new Carousel(t.querySelector(".carousel")),this.addButtonContainer(),this.addProgressBar(),this.initializeEvents()}}),this.getComponent().on("wizard-visible",(()=>{this.setup.forceSelection&&(this.lockPrevStep(),this.lockNextStep()),this.runSlideCallback(e,this.setup.$carousel.find(".carousel-item").first())})).on("wizard-dismissed",(()=>{this.setup=$.extend(!0,{},this.originalSetup)}))}getComponent(){return null===this.setup.$carousel&&this.generateSlides(),this.setup.$carousel}dismiss(){Modal.dismiss()}lockNextStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!0),t}unlockNextStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="next"]');return t.prop("disabled",!1),t}lockPrevStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!0),t}unlockPrevStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="prev"]');return t.prop("disabled",!1),t}triggerStepButton(t){const e=this.setup.$carousel.closest(".modal").find('button[name="'+t+'"]');return e.length>0&&!0!==e.prop("disabled")&&e.get(0).click(),e}blurCancelStep(){const t=this.setup.$carousel.closest(".modal").find('button[name="cancel"]');return t.trigger("blur"),t}initializeEvents(){const t=this.setup.$carousel.closest(".modal");this.initializeSlideNextEvent(t),this.initializeSlidePrevEvent(t),this.setup.$carousel.get(0).addEventListener("slide.bs.carousel",(e=>{"left"===e.direction?this.nextSlideChanges(t):this.prevSlideChanges(t)})),this.setup.$carousel.get(0).addEventListener("slid.bs.carousel",(t=>{const e=this.setup.$carousel.data("currentIndex"),s=this.setup.slides[e];this.setup.forceSelection&&this.lockNextStep(),this.runSlideCallback(s,$(t.relatedTarget))}));const e=this.getComponent();e.on("wizard-dismiss",this.dismiss),Modal.currentModal.addEventListener("typo3-modal-hidden",(()=>{e.trigger("wizard-dismissed")})),Modal.currentModal.addEventListener("typo3-modal-shown",(()=>{e.trigger("wizard-visible")}))}initializeSlideNextEvent(t){t.find(".modal-footer").find('button[name="next"]').off().on("click",(()=>{this.setup.carousel.next()}))}initializeSlidePrevEvent(t){t.find(".modal-footer").find('button[name="prev"]').off().on("click",(()=>{this.setup.carousel.prev()}))}nextSlideChanges(t){this.initializeSlideNextEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=this.setup.$carousel.data("currentSlide")+1,r=this.setup.$carousel.data("currentIndex"),a=r+1;t.find(".carousel-item:eq("+a+")").empty().append(this.setup.slides[a].content),e.text(this.setup.slides[a].title),this.unlockPrevStep(),this.setup.$carousel.data("currentSlide",i),this.setup.$carousel.data("currentIndex",a);const l=s.find(".progress-bar");l.eq(r).width("0%"),l.eq(a).width(this.setup.$carousel.data("initialStep")*i+"%").removeClass("inactive"),this.updateCurrentSeverity(t,r,a)}prevSlideChanges(t){this.initializeSlidePrevEvent(t);const e=t.find(".modal-title"),s=t.find(".modal-footer"),i=s.find('button[name="next"]'),r=this.setup.$carousel.data("currentSlide")-1,a=this.setup.$carousel.data("currentIndex"),l=a-1;t.find(".carousel-item:eq("+l+")").empty().append(this.setup.slides[l].content),e.text(this.setup.slides[l].title),l>0?this.unlockPrevStep():this.lockPrevStep(),this.setup.$carousel.data("currentSlide",r),this.setup.$carousel.data("currentIndex",l),s.find(".progress-bar.last-step").width(this.setup.$carousel.data("initialStep")+"%").text(this.getProgressBarTitle(this.setup.$carousel.data("slideCount")-1)),i.text(top.TYPO3.lang["wizard.button.next"]);const n=s.find(".progress-bar");n.eq(a).width(this.setup.$carousel.data("initialStep")+"%").addClass("inactive"),n.eq(l).width(this.setup.$carousel.data("initialStep")*r+"%").removeClass("inactive"),this.updateCurrentSeverity(t,a,l)}updateCurrentSeverity(t,e,s){t.find(".modal-footer").find('button[name="next"]').removeClass("btn-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("btn-"+Severity.getCssClass(this.setup.slides[s].severity)),t.removeClass("modal-severity-"+Severity.getCssClass(this.setup.slides[e].severity)).addClass("modal-severity-"+Severity.getCssClass(this.setup.slides[s].severity))}getProgressBarTitle(t){let e;return e=null===this.setup.slides[t].progressBarTitle?0===t?top.TYPO3.lang["wizard.progressStep.start"]:t>=this.setup.$carousel.data("slideCount")-1?top.TYPO3.lang["wizard.progressStep.finish"]:top.TYPO3.lang["wizard.progressStep"]+String(t+1):this.setup.slides[t].progressBarTitle,e}runSlideCallback(t,e){"function"==typeof t.callback&&t.callback(e,this.setup.settings,t.identifier)}addProgressBar(){const t=this.setup.$carousel.find(".carousel-item").length,e=Math.max(1,t),s=Math.round(100/e),i=this.setup.$carousel.closest(".modal").find(".modal-footer");if(this.setup.$carousel.data("initialStep",s).data("slideCount",e).data("realSlideCount",t).data("currentIndex",0).data("currentSlide",1),e>1){i.prepend($("<div />",{class:"progress"}));for(let t=0;t<this.setup.slides.length;++t){let e;e=0===t?"progress-bar first-step":t===this.setup.$carousel.data("slideCount")-1?"progress-bar last-step inactive":"progress-bar step inactive",i.find(".progress").append($("<div />",{role:"progressbar",class:e,"aria-valuemin":0,"aria-valuenow":s,"aria-valuemax":100}).width(s+"%").text(this.getProgressBarTitle(t)))}}}addButtonContainer(){this.setup.$carousel.closest(".modal").find(".modal-footer .btn").wrapAll('<div class="modal-btn-group" />')}generateSlides(){if(null!==this.setup.$carousel)return this.setup.$carousel;let t='<div class="carousel slide" data-bs-ride="false"><div class="carousel-inner" role="listbox">';for(let e=0;e<this.setup.slides.length;++e){const s=this.setup.slides[e];let i=s.content;"object"==typeof i&&(i=i.html()),t+='<div class="carousel-item" data-bs-slide="'+s.identifier+'" data-step="'+e+'">'+i+"</div>"}return t+="</div></div>",this.setup.$carousel=$(t),this.setup.$carousel.find(".carousel-item").first().addClass("active"),this.setup.$carousel}}let multistepWizardObject;try{window.opener&&window.opener.TYPO3&&window.opener.TYPO3.MultiStepWizard&&(multistepWizardObject=window.opener.TYPO3.MultiStepWizard),parent&&parent.window.TYPO3&&parent.window.TYPO3.MultiStepWizard&&(multistepWizardObject=parent.window.TYPO3.MultiStepWizard),top&&top.TYPO3&&top.TYPO3.MultiStepWizard&&(multistepWizardObject=top.TYPO3.MultiStepWizard)}catch(t){}multistepWizardObject||(multistepWizardObject=new MultiStepWizard,"undefined"!=typeof TYPO3&&(TYPO3.MultiStepWizard=multistepWizardObject));export default multistepWizardObject;
\ No newline at end of file