-            {{errorMessages[error]}}
+            {{ errorMessages[error] | translate }}
             
                 {{ 'core.login.invalidvaluemax' | translate:{$a: control.errors!.max.max} }}
             
diff --git a/src/core/components/input-errors/input-errors.ts b/src/core/components/input-errors/input-errors.ts
index 117867649..570386555 100644
--- a/src/core/components/input-errors/input-errors.ts
+++ b/src/core/components/input-errors/input-errors.ts
@@ -14,7 +14,6 @@
 
 import { Component, ElementRef, HostBinding, Input, OnChanges, OnInit, SimpleChange } from '@angular/core';
 import { FormControl } from '@angular/forms';
-import { Translate } from '@singletons';
 
 /**
  * Component to show errors if an input isn't valid.
@@ -30,8 +29,7 @@ import { Translate } from '@singletons';
  * Example usage:
  *
  * 
- *     {{ 'core.login.username' | translate }}
- *     
+ *     
  *     
  * 
  */
@@ -42,12 +40,12 @@ import { Translate } from '@singletons';
 })
 export class CoreInputErrorsComponent implements OnInit, OnChanges {
 
-    @Input() control?: FormControl;
-    @Input() errorMessages: Record = {};
+    @Input() control?: FormControl; // Needed to be able to check the validity of the input.
+    @Input() errorMessages: Record = {}; // Error messages to show. Keys must be the name of the error.
     @Input() errorText = ''; // Set other non automatic errors.
     errorKeys: string[] = [];
 
-    protected element: HTMLElement;
+    protected hostElement: HTMLElement;
 
     @HostBinding('class.has-errors')
     get hasErrors(): boolean {
@@ -59,31 +57,60 @@ export class CoreInputErrorsComponent implements OnInit, OnChanges {
     constructor(
         element: ElementRef,
     ) {
-        this.element = element.nativeElement;
+        this.hostElement = element.nativeElement;
     }
 
     /**
      * Initialize some common errors if they aren't set.
      */
     protected initErrorMessages(): void {
-        this.errorMessages.required = this.errorMessages.required || Translate.instant('core.required');
-        this.errorMessages.email = this.errorMessages.email || Translate.instant('core.login.invalidemail');
-        this.errorMessages.date = this.errorMessages.date || Translate.instant('core.login.invaliddate');
-        this.errorMessages.datetime = this.errorMessages.datetime || Translate.instant('core.login.invaliddate');
-        this.errorMessages.datetimelocal = this.errorMessages.datetimelocal || Translate.instant('core.login.invaliddate');
-        this.errorMessages.time = this.errorMessages.time || Translate.instant('core.login.invalidtime');
-        this.errorMessages.url = this.errorMessages.url || Translate.instant('core.login.invalidurl');
+        this.errorMessages = {
+            required: this.errorMessages.required || 'core.required',
+            email: this.errorMessages.email || 'core.login.invalidemail',
+            date: this.errorMessages.date || 'core.login.invaliddate',
+            datetime: this.errorMessages.datetime || 'core.login.invaliddate',
+            datetimelocal: this.errorMessages.datetimelocal || 'core.login.invaliddate',
+            time: this.errorMessages.time || 'core.login.invalidtime',
+            url: this.errorMessages.url || 'core.login.invalidurl',
+            // Set empty values by default, the default error messages will be built in the template when needed.
+            max: this.errorMessages.max || '',
+            min: this.errorMessages.min || '',
+        };
 
-        // Set empty values by default, the default error messages will be built in the template when needed.
-        this.errorMessages.max = this.errorMessages.max || '';
-        this.errorMessages.min = this.errorMessages.min || '';
+        this.errorMessages.requiredTrue = this.errorMessages.required;
+
+        this.errorKeys = Object.keys(this.errorMessages);
     }
 
     /**
      * @inheritdoc
      */
     ngOnInit(): void {
-        this.element.closest('ion-item')?.classList.add('has-core-input-errors');
+        const parent = this.hostElement.parentElement;
+        let item: HTMLElement | null = null;
+
+        if (parent?.tagName === 'ION-ITEM') {
+            item = parent;
+
+            // Get all elements on the parent and wrap them with a div.
+            // This is needed because otherwise the error message will be shown on the right of the input. Or overflowing the item.
+            const wrapper = document.createElement('div');
+
+            wrapper.classList.add('core-input-errors-wrapper');
+
+            Array.from(parent.children).forEach((child) => {
+                if (!child.slot) {
+                    wrapper.appendChild(child);
+                }
+            });
+
+            parent.appendChild(wrapper);
+        } else {
+            item = this.hostElement.closest('ion-item');
+        }
+
+        item?.classList.add('has-core-input-errors');
+
     }
 
     /**
@@ -92,11 +119,6 @@ export class CoreInputErrorsComponent implements OnInit, OnChanges {
     ngOnChanges(changes: { [name: string]: SimpleChange }): void {
         if ((changes.control || changes.errorMessages) && this.control) {
             this.initErrorMessages();
-
-            this.errorKeys = this.errorMessages ? Object.keys(this.errorMessages) : [];
-        }
-        if (changes.errorText) {
-            this.errorText = changes.errorText.currentValue;
         }
     }
 
diff --git a/src/core/components/mark-required/mark-required.ts b/src/core/components/mark-required/mark-required.ts
index 5b72cf378..f0bc34b51 100644
--- a/src/core/components/mark-required/mark-required.ts
+++ b/src/core/components/mark-required/mark-required.ts
@@ -37,13 +37,13 @@ export class CoreMarkRequiredComponent implements OnInit, AfterViewInit {
 
     @Input('core-mark-required') coreMarkRequired: boolean | string = true;
 
-    protected element: HTMLElement;
+    protected hostElement: HTMLElement;
     requiredLabel = Translate.instant('core.required');
 
     constructor(
         element: ElementRef,
     ) {
-        this.element = element.nativeElement;
+        this.hostElement = element.nativeElement;
     }
 
     /**
@@ -59,18 +59,21 @@ export class CoreMarkRequiredComponent implements OnInit, AfterViewInit {
     ngAfterViewInit(): void {
         if (this.coreMarkRequired) {
             // Add the "required" to the aria-label.
-            const ariaLabel = this.element.getAttribute('aria-label') ||
-                CoreTextUtils.cleanTags(this.element.innerHTML, { singleLine: true });
+            const ariaLabel = this.hostElement.getAttribute('aria-label') ||
+                CoreTextUtils.cleanTags(this.hostElement.innerHTML, { singleLine: true });
             if (ariaLabel) {
-                this.element.setAttribute('aria-label', ariaLabel + ' ' + this.requiredLabel);
+                this.hostElement.setAttribute('aria-label', ariaLabel + '. ' + this.requiredLabel);
             }
         } else {
             // Remove the "required" from the aria-label.
-            const ariaLabel = this.element.getAttribute('aria-label');
+            const ariaLabel = this.hostElement.getAttribute('aria-label');
             if (ariaLabel) {
-                this.element.setAttribute('aria-label', ariaLabel.replace(' ' + this.requiredLabel, ''));
+                this.hostElement.setAttribute('aria-label', ariaLabel.replace('. ' + this.requiredLabel, ''));
             }
         }
+
+        const input = this.hostElement.closest('ion-input, ion-textarea');
+        input?.setAttribute('required', this.coreMarkRequired ? 'true' : 'false');
     }
 
 }
diff --git a/src/core/features/login/pages/email-signup/email-signup.ts b/src/core/features/login/pages/email-signup/email-signup.ts
index 8938b13eb..43797e233 100644
--- a/src/core/features/login/pages/email-signup/email-signup.ts
+++ b/src/core/features/login/pages/email-signup/email-signup.ts
@@ -105,15 +105,14 @@ export class CoreLoginEmailSignupPage implements OnInit {
         });
 
         // Setup validation errors.
-        this.usernameErrors = CoreLoginHelper.getErrorMessages('core.login.usernamerequired');
-        this.passwordErrors = CoreLoginHelper.getErrorMessages('core.login.passwordrequired');
-        this.emailErrors = CoreLoginHelper.getErrorMessages('core.login.missingemail');
-        this.policyErrors = CoreLoginHelper.getErrorMessages('core.login.policyagree');
-        this.email2Errors = CoreLoginHelper.getErrorMessages(
-            'core.login.missingemail',
-            undefined,
-            'core.login.emailnotmatch',
-        );
+        this.usernameErrors = { required: 'core.login.usernamerequired' };
+        this.passwordErrors = { required: 'core.login.passwordrequired' };
+        this.emailErrors = { required: 'core.login.missingemail' };
+        this.policyErrors = { required: 'core.login.policyagree' };
+        this.email2Errors = {
+            required: 'core.login.missingemail',
+            pattern: 'core.login.emailnotmatch',
+        };
     }
 
     /**
@@ -224,7 +223,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
         const namefieldsErrors = {};
         if (this.settings.namefields) {
             this.settings.namefields.forEach((field) => {
-                namefieldsErrors[field] = CoreLoginHelper.getErrorMessages('core.login.missing' + field);
+                namefieldsErrors[field] = { required: 'core.login.missing' + field };
             });
         }
         this.namefieldsErrors = namefieldsErrors;
diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts
index aed48d583..a8c22c345 100644
--- a/src/core/features/login/services/login-helper.ts
+++ b/src/core/features/login/services/login-helper.ts
@@ -252,60 +252,6 @@ export class CoreLoginHelperProvider {
         return CoreTextUtils.treatDisabledFeatures(disabledFeatures);
     }
 
-    /**
-     * Builds an object with error messages for some common errors.
-     * Please notice that this function doesn't support all possible error types.
-     *
-     * @param requiredMsg Code of the string for required error.
-     * @param emailMsg Code of the string for invalid email error.
-     * @param patternMsg Code of the string for pattern not match error.
-     * @param urlMsg Code of the string for invalid url error.
-     * @param minlengthMsg Code of the string for "too short" error.
-     * @param maxlengthMsg Code of the string for "too long" error.
-     * @param minMsg Code of the string for min value error.
-     * @param maxMsg Code of the string for max value error.
-     * @returns Object with the errors.
-     */
-    getErrorMessages(
-        requiredMsg?: string,
-        emailMsg?: string,
-        patternMsg?: string,
-        urlMsg?: string,
-        minlengthMsg?: string,
-        maxlengthMsg?: string,
-        minMsg?: string,
-        maxMsg?: string,
-    ): Record {
-        const errors: Record = {};
-
-        if (requiredMsg) {
-            errors.required = errors.requiredTrue = Translate.instant(requiredMsg);
-        }
-        if (emailMsg) {
-            errors.email = Translate.instant(emailMsg);
-        }
-        if (patternMsg) {
-            errors.pattern = Translate.instant(patternMsg);
-        }
-        if (urlMsg) {
-            errors.url = Translate.instant(urlMsg);
-        }
-        if (minlengthMsg) {
-            errors.minlength = Translate.instant(minlengthMsg);
-        }
-        if (maxlengthMsg) {
-            errors.maxlength = Translate.instant(maxlengthMsg);
-        }
-        if (minMsg) {
-            errors.min = Translate.instant(minMsg);
-        }
-        if (maxMsg) {
-            errors.max = Translate.instant(maxMsg);
-        }
-
-        return errors;
-    }
-
     /**
      * Get logo URL from a site public config.
      *
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index 929209ef8..c50f2471a 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -126,6 +126,10 @@ ion-item > .in-item,
     white-space: normal !important;
 }
 
+ion-item .core-input-errors-wrapper {
+    width: 100%;
+}
+
 @each $color-name, $unused in $colors {
     .text-#{$color-name},
     p.text-#{$color-name} {
diff --git a/upgrade.txt b/upgrade.txt
index 76fb4cca7..f188871bc 100644
--- a/upgrade.txt
+++ b/upgrade.txt
@@ -10,6 +10,7 @@ For more information about upgrading, read the official documentation: https://m
  - Removed CoreToLocaleStringPipe deprecated since 3.6.0
  - With the upgrade to Ionic 7 ion-slides is no longer supported and now you need to use swiper-container and swiper-slide. More info here: https://ionicframework.com/docs/angular/slides
  - With the upgrade to Ionic7 ion-datetime has changed its usage. We recommend using ion-datetime-button. More info here: https://ionicframework.com/docs/updating/6-0#datetime
+ - CoreLoginHelper.getErrorMessages has been removed. Please create the messages object yourself.
 
 === 4.3.0 ===