From 0dd32c853a7d231f884f3a437875b26663413806 Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Sun, 6 Apr 2025 12:24:55 -0500 Subject: [PATCH] Change password view enhancement to support and display password validations --- src/app/core/utils/password.validator.ts | 35 ++++++ src/app/core/utils/passwords-utility.ts | 14 ++- .../change-password-dialog.component.html | 94 +++++---------- .../change-password-dialog.component.scss | 8 ++ .../change-password-dialog.component.ts | 12 +- .../input-password.component.html | 16 +++ .../input-password.component.scss | 11 ++ .../input-password.component.spec.ts | 22 ++++ .../input-password.component.ts | 114 ++++++++++++++++++ src/app/shared/shared.module.ts | 4 +- src/assets/env.js | 20 +-- src/assets/translations/cs-CS.json | 8 +- src/assets/translations/de-DE.json | 8 +- src/assets/translations/en-US.json | 8 +- src/assets/translations/es-CL.json | 8 +- src/assets/translations/es-MX.json | 8 +- src/assets/translations/fr-FR.json | 8 +- src/assets/translations/it-IT.json | 8 +- src/assets/translations/ko-KO.json | 8 +- src/assets/translations/lt-LT.json | 8 +- src/assets/translations/lv-LV.json | 8 +- src/assets/translations/ne-NE.json | 8 +- src/assets/translations/pt-PT.json | 8 +- src/assets/translations/sw-SW.json | 8 +- 24 files changed, 352 insertions(+), 102 deletions(-) create mode 100644 src/app/core/utils/password.validator.ts create mode 100644 src/app/shared/input-password/input-password.component.html create mode 100644 src/app/shared/input-password/input-password.component.scss create mode 100644 src/app/shared/input-password/input-password.component.spec.ts create mode 100644 src/app/shared/input-password/input-password.component.ts diff --git a/src/app/core/utils/password.validator.ts b/src/app/core/utils/password.validator.ts new file mode 100644 index 000000000..b1a6c1c00 --- /dev/null +++ b/src/app/core/utils/password.validator.ts @@ -0,0 +1,35 @@ +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; +import { environment } from 'environments/environment'; + +export function passwordValidator(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value = control.value; + if (!value) return null; // No validation if the field is empty + + const errors: ValidationErrors = {}; + + if (value.length < environment.minPasswordLength) { + errors['minLength'] = 'Password must be at least ' + environment.minPasswordLength + ' characters long'; + } + if (value.length > 50) { + errors['maxLength'] = 'Password must be maximum 50 characters long'; + } + if (!/[A-Z]/.test(value)) { + errors['uppercase'] = 'Password must contain at least one uppercase letter'; + } + if (!/[a-z]/.test(value)) { + errors['lowercase'] = 'Password must contain at least one lowercase letter'; + } + if (!/\d/.test(value)) { + errors['number'] = 'Password must contain at least one number'; + } + if (!/^(?:(.)(?!\1))+$/.test(value)) { + errors['repeated'] = 'Password must have not consecutive repeating characters'; + } + if (!/[@$!%*?&]/.test(value)) { + errors['specialChar'] = 'Password must contain at least one special character (@$!%*?&)'; + } + + return Object.keys(errors).length > 0 ? errors : null; + }; +} diff --git a/src/app/core/utils/passwords-utility.ts b/src/app/core/utils/passwords-utility.ts index 32ae86296..238ddb62e 100644 --- a/src/app/core/utils/passwords-utility.ts +++ b/src/app/core/utils/passwords-utility.ts @@ -1,21 +1,27 @@ import { Injectable } from '@angular/core'; import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; -import { environment } from 'environments/environment'; import { Subscription } from 'rxjs'; +import { passwordValidator } from './password.validator'; +import { environment } from 'environments/environment'; @Injectable({ providedIn: 'root' }) export class PasswordsUtility { + minPasswordLength: number = environment.minPasswordLength | 12; // password regex pattern - public static PASSWORD_REGEX = '^(?!.*(.)\\1{1,})(?!.*\\s)(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^\\w\\s]).{12,50}$'; + public static PASSWORD_REGEX = + '^(?!.*(.)\\1{1,})(?!.*\\s)(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^\\w\\s]).{' + + (environment.minPasswordLength | 12) + + ',50}$'; public getPasswordValidators(): ValidatorFn[] { return [ Validators.required, - Validators.pattern(PasswordsUtility.PASSWORD_REGEX), + Validators.minLength(this.minPasswordLength), Validators.maxLength(50), - Validators.minLength(environment.minPasswordLength)]; + passwordValidator() + ]; } /** diff --git a/src/app/shared/change-password-dialog/change-password-dialog.component.html b/src/app/shared/change-password-dialog/change-password-dialog.component.html index 9d7806294..ae393b263 100644 --- a/src/app/shared/change-password-dialog/change-password-dialog.component.html +++ b/src/app/shared/change-password-dialog/change-password-dialog.component.html @@ -3,68 +3,40 @@
- - {{ 'labels.inputs.Password' | translate }} - - - - {{ 'labels.inputs.Password' | translate }} {{ 'labels.commons.is required' | translate }} - - - {{ 'labels.commons.Password should be at least' | translate }} - {{ 'labels.commons.12 characters long' | translate }} - - - {{ 'labels.commons.Password should not be more than' | translate }} - {{ 'labels.commons.50 characters long' | translate }} - - - {{ 'labels.commons.Password should include a' | translate }} - {{ 'labels.commons.numeral' | translate }} {{ 'labels.commons.and at' | translate }} - {{ 'labels.commons.least one uppercase' | translate }}, {{ 'labels.commons.lowercase and special character' | translate }} - - + +
+

+ {{ changePasswordForm.get('password').errors?.['uppercase'] | translateKey: 'inputs' }} +

+

+ {{ changePasswordForm.get('password').errors?.['number'] | translateKey: 'inputs' }} +

+

+ {{ changePasswordForm.get('password').errors?.['repeated'] | translateKey: 'inputs' }} +

+

+ {{ changePasswordForm.get('password').errors?.['specialChar'] | translateKey: 'inputs' }} +

+

+ {{ 'Password minimun length must be' | translateKey: 'inputs' }} {{ minPasswordLength }} +

+
- - {{ 'labels.inputs.Repeat Password' | translate }} - - - - {{ 'labels.inputs.Password' | translate }} {{ 'labels.commons.required' | translate }} - - - {{ 'labels.inputs.Password' | translate }} {{ 'labels.commons.does not match' | translate }} - - + +
+ +
+

+ {{ 'Passwords do not match' | translateKey: 'inputs' }} +

diff --git a/src/app/shared/change-password-dialog/change-password-dialog.component.scss b/src/app/shared/change-password-dialog/change-password-dialog.component.scss index e69de29bb..e6103db4d 100644 --- a/src/app/shared/change-password-dialog/change-password-dialog.component.scss +++ b/src/app/shared/change-password-dialog/change-password-dialog.component.scss @@ -0,0 +1,8 @@ +.error { + color: red; +} + +mat-dialog-content { + min-width: 240px; + width: 240px; +} diff --git a/src/app/shared/change-password-dialog/change-password-dialog.component.ts b/src/app/shared/change-password-dialog/change-password-dialog.component.ts index 8c92c887c..6f72726c7 100644 --- a/src/app/shared/change-password-dialog/change-password-dialog.component.ts +++ b/src/app/shared/change-password-dialog/change-password-dialog.component.ts @@ -4,6 +4,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { UntypedFormBuilder, Validators, ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms'; import { Subscription } from 'rxjs'; import { PasswordsUtility } from 'app/core/utils/passwords-utility'; +import { environment } from 'environments/environment'; /** * Change Password Dialog component. @@ -14,6 +15,8 @@ import { PasswordsUtility } from 'app/core/utils/passwords-utility'; styleUrls: ['./change-password-dialog.component.scss'] }) export class ChangePasswordDialogComponent implements OnInit { + minPasswordLength: number = environment.minPasswordLength | 12; + /** Change Password Form */ changePasswordForm: any; /** Password input field type. */ @@ -37,15 +40,6 @@ export class ChangePasswordDialogComponent implements OnInit { this.createChangePasswordForm(); } - /** - * Toggles the visibility of the password input field. - * - * Changes the input type between 'password' and 'text'. - */ - togglePasswordVisibility(index: number) { - this.passwordInputType[index] = this.passwordInputType[index] === 'password' ? 'text' : 'password'; - } - /** Change Password form */ createChangePasswordForm() { this.changePasswordForm = this.formBuilder.group({ diff --git a/src/app/shared/input-password/input-password.component.html b/src/app/shared/input-password/input-password.component.html new file mode 100644 index 000000000..eb26b78ee --- /dev/null +++ b/src/app/shared/input-password/input-password.component.html @@ -0,0 +1,16 @@ + + {{ label }} + + {{ icon }} + {{ label }} is a required field. + {{ customErrorMessage }} + diff --git a/src/app/shared/input-password/input-password.component.scss b/src/app/shared/input-password/input-password.component.scss new file mode 100644 index 000000000..edb3305a9 --- /dev/null +++ b/src/app/shared/input-password/input-password.component.scss @@ -0,0 +1,11 @@ +:host { + width: 100%; + + mat-icon { + font-size: 16px; + } + + .mat-form-field { + width: 100%; + } +} diff --git a/src/app/shared/input-password/input-password.component.spec.ts b/src/app/shared/input-password/input-password.component.spec.ts new file mode 100644 index 000000000..036b78adb --- /dev/null +++ b/src/app/shared/input-password/input-password.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { InputPasswordComponent } from './input-password.component'; + +describe('InputPasswordComponent', () => { + let component: InputPasswordComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [InputPasswordComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(InputPasswordComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/input-password/input-password.component.ts b/src/app/shared/input-password/input-password.component.ts new file mode 100644 index 000000000..c94761b2b --- /dev/null +++ b/src/app/shared/input-password/input-password.component.ts @@ -0,0 +1,114 @@ +import { Component, Input, Optional, Self, ViewChild } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormGroupDirective, + NgControl, + NgForm, + Validators +} from '@angular/forms'; +import { MatInput } from '@angular/material/input'; +import { ErrorStateMatcher } from '@angular/material/core'; + +@Component({ + selector: 'mifosx-input-password', + templateUrl: './input-password.component.html', + styleUrls: ['./input-password.component.scss'] +}) +export class InputPasswordComponent implements ControlValueAccessor, ErrorStateMatcher { + disabled = false; + + icon = 'visibility_off'; + + @Input() + label: string = null; + + @Input() + customErrorMessage: string = null; + + @Input() + customErrorName: string = null; + + get matcher() { + return this; + } + + @ViewChild(MatInput) + matInput: MatInput; + + get required(): boolean { + return this._required ?? this.ngControl?.control?.hasValidator(Validators.required) ?? false; + } + + @Input() + set required(value: boolean) { + this._required = value; + } + + touched = false; + + type = 'password'; + + value: string = null; + + private _required: boolean = null; + + private _visible = false; + + constructor(@Optional() @Self() public ngControl: NgControl) { + if (ngControl !== null) { + ngControl.valueAccessor = this; + } + } + + isErrorState(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean { + return this.touched && (this.ngControl?.control?.invalid ?? false); + } + + onChange = (value: any) => {}; + + onFocusOut(): void { + this.touched = true; + this.onTouched(); + this.matInput.updateErrorState(); + } + + onInput($event: any): void { + this.value = $event.currentTarget.value; + this.touched = true; + this.onChange(this.value); + } + + onTouched = () => {}; + + onVisibilityClick($event: Event): void { + if (this._visible) { + this.icon = 'visibility_off'; + this.type = 'password'; + } else { + this.icon = 'visibility'; + this.type = 'text'; + } + + // Invert the value. + this._visible = !this._visible; + + $event.stopPropagation(); + } + + registerOnChange(fn: any) { + this.onChange = fn; + } + + registerOnTouched(fn: any) { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean) { + this.disabled = isDisabled; + } + + writeValue(obj: any): void { + this.value = obj; + } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 1ce645c2a..a97d3efb9 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -50,6 +50,7 @@ import { ThemeToggleComponent } from './theme-toggle/theme-toggle.component'; import { LongTextComponent } from './long-text/long-text.component'; import { DropdownComponent } from './dropdown/dropdown.component'; import { InputAmountComponent } from './input-amount/input-amount.component'; +import { InputPasswordComponent } from './input-password/input-password.component'; /** * Shared Module @@ -106,7 +107,8 @@ import { InputAmountComponent } from './input-amount/input-amount.component'; ThemeToggleComponent, LongTextComponent, DropdownComponent, - InputAmountComponent + InputAmountComponent, + InputPasswordComponent ], exports: [ FileUploadComponent, diff --git a/src/assets/env.js b/src/assets/env.js index 7c9af188b..334e1c240 100644 --- a/src/assets/env.js +++ b/src/assets/env.js @@ -2,18 +2,18 @@ window["env"] = window["env"] || {}; // BackEnd Environment variables - window['env']['fineractApiUrls'] = ''; - window['env']['fineractApiUrl'] = ''; + window["env"]["fineractApiUrls"] = ''; + window["env"]["fineractApiUrl"] = ''; - window['env']['apiProvider'] = ''; - window['env']['apiVersion'] = ''; + window["env"]["apiProvider"] = ''; + window["env"]["apiVersion"] = ''; window["env"]["fineractPlatformTenantId"] = ''; window["env"]["fineractPlatformTenantIds"] = ''; // Language Environment variables - window['env']['defaultLanguage'] = ''; - window['env']['supportedLanguages'] = ''; + window["env"]["defaultLanguage"] = ''; + window["env"]["supportedLanguages"] = ''; window['env']['preloadClients'] = ''; @@ -44,12 +44,4 @@ // OAuth Client Id window['env']['oauthAppId'] = ''; - // Min Password length - window['env']['minPasswordLength'] = '12'; - - window["env"]["vNextApiUrl"] = ''; - window["env"]["vNextApiProvider"] = ''; - window["env"]["vNextApiVersion"] = ''; - window["env"]["interbankTransfers"] = "false"; - })(this); diff --git a/src/assets/translations/cs-CS.json b/src/assets/translations/cs-CS.json index de52fc74b..79e841a43 100644 --- a/src/assets/translations/cs-CS.json +++ b/src/assets/translations/cs-CS.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "VRÁCENÍ ÚROKU", "Supported Interest Refund Types": "Unterstützte Zinsrückerstattungsarten", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Podporované typy vrácení úroků", - "Charge-off reason": "Důvod vyúčtování" + "Charge-off reason": "Důvod vyúčtování", + "Password must contain at least one uppercase letter": "Heslo musí obsahovat alespoň jedno velké písmeno", + "Password must contain at least one number": "Heslo musí obsahovat alespoň jedno číslo", + "Password must have not consecutive repeating characters": "Heslo nesmí obsahovat po sobě jdoucí opakující se znaky", + "Password must contain at least one special character (@$!%*?&)": "Heslo musí obsahovat alespoň jeden speciální znak (@$!%*?&)", + "Password minimun length must be": "Minimální délka hesla musí být", + "Passwords do not match": "Hesla se neshodují" }, "links": { "Community": "Společenství", diff --git a/src/assets/translations/de-DE.json b/src/assets/translations/de-DE.json index 7136114df..cc98a40a8 100644 --- a/src/assets/translations/de-DE.json +++ b/src/assets/translations/de-DE.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "ZINSRÜCKERSTATTUNG", "Supported Interest Refund Types": "Unterstützte Zinsrückerstattungsarten", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Unterstützte Zinsrückerstattungsarten", - "Charge-off reason": "Abschreibungsgrund" + "Charge-off reason": "Abschreibungsgrund", + "Password must contain at least one uppercase letter": "Das Passwort muss mindestens einen Großbuchstaben enthalten", + "Password must contain at least one number": "Das Passwort muss mindestens eine Zahl enthalten", + "Password must have not consecutive repeating characters": "Das Passwort darf keine aufeinanderfolgenden, sich wiederholenden Zeichen enthalten", + "Password must contain at least one special character (@$!%*?&)": "Das Passwort muss mindestens ein Sonderzeichen enthalten (@$!%*?&)", + "Password minimun length must be": "Die Mindestlänge des Passworts muss betragen", + "Passwords do not match": "Passwörter stimmen nicht überein" }, "links": { "Community": "Gemeinschaft", diff --git a/src/assets/translations/en-US.json b/src/assets/translations/en-US.json index 31c1ff425..ee68fed41 100644 --- a/src/assets/translations/en-US.json +++ b/src/assets/translations/en-US.json @@ -2456,7 +2456,13 @@ "INTEREST REFUND": "INTEREST REFUND", "Supported Interest Refund Types": "Supported Interest Refund Types", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Supported interest refund types", - "Charge-off reason": "Charge-off reason" + "Charge-off reason": "Charge-off reason", + "Password must contain at least one uppercase letter": "Password must contain at least one uppercase letter", + "Password must contain at least one number": "Password must contain at least one number", + "Password must have not consecutive repeating characters": "Password must have not consecutive repeating characters", + "Password must contain at least one special character (@$!%*?&)": "Password must contain at least one special character (@$!%*?&)", + "Password minimun length must be": "Password minimun length must be", + "Passwords do not match": "Passwords do not match" }, "links": { "Community": "Community", diff --git a/src/assets/translations/es-CL.json b/src/assets/translations/es-CL.json index a6f3669e1..39b50298f 100644 --- a/src/assets/translations/es-CL.json +++ b/src/assets/translations/es-CL.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "REEMBOLSO DE INTERESES", "Supported Interest Refund Types": "Tipos de reembolso de intereses admitidos", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Tipos de reembolso de intereses admitidos", - "Charge-off reason": "Motivo de la cancelación" + "Charge-off reason": "Motivo de la cancelación", + "Password must contain at least one uppercase letter": "La contraseña debe contener al menos una letra mayúscula", + "Password must contain at least one number": "La contraseña debe contener al menos un número", + "Password must have not consecutive repeating characters": "La contraseña no debe tener caracteres repetidos consecutivos.", + "Password must contain at least one special character (@$!%*?&)": "La contraseña debe contener al menos un carácter especial (@$!%*?&)", + "Password minimun length must be": "La longitud mínima de la contraseña debe ser", + "Passwords do not match": "Las contraseñas no coinciden" }, "links": { "Community": "Comunidad", diff --git a/src/assets/translations/es-MX.json b/src/assets/translations/es-MX.json index eac7b8cf9..90ccd49f4 100644 --- a/src/assets/translations/es-MX.json +++ b/src/assets/translations/es-MX.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "REEMBOLSO DE INTERESES", "Supported Interest Refund Types": "Tipos de reembolso de intereses admitidos", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Tipos de reembolso de intereses admitidos", - "Charge-off reason": "Motivo de la cancelación" + "Charge-off reason": "Motivo de la cancelación", + "Password must contain at least one uppercase letter": "La contraseña debe contener al menos una letra mayúscula", + "Password must contain at least one number": "La contraseña debe contener al menos un número", + "Password must have not consecutive repeating characters": "La contraseña no debe tener caracteres repetidos consecutivos.", + "Password must contain at least one special character (@$!%*?&)": "La contraseña debe contener al menos un carácter especial (@$!%*?&)", + "Password minimun length must be": "La longitud mínima de la contraseña debe ser", + "Passwords do not match": "Las contraseñas no coinciden" }, "links": { "Community": "Comunidad", diff --git a/src/assets/translations/fr-FR.json b/src/assets/translations/fr-FR.json index 1b12ac309..c72cb785f 100644 --- a/src/assets/translations/fr-FR.json +++ b/src/assets/translations/fr-FR.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "REMBOURSEMENT DES INTÉRÊTS", "Supported Interest Refund Types": "Types de remboursement d'intérêts pris en charge", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Types de remboursement d'intérêts pris en charge", - "Charge-off reason": "Motif de la radiation" + "Charge-off reason": "Motif de la radiation", + "Password must contain at least one uppercase letter": "Le mot de passe doit contenir au moins une lettre majuscule", + "Password must contain at least one number": "Le mot de passe doit contenir au moins un chiffre", + "Password must have not consecutive repeating characters": "Le mot de passe ne doit pas contenir de caractères répétitifs consécutifs", + "Password must contain at least one special character (@$!%*?&)": "Le mot de passe doit contenir au moins un caractère spécial (@$!%*?&)", + "Password minimun length must be": "La longueur minimale du mot de passe doit être", + "Passwords do not match": "Les mots de passe ne correspondent pas" }, "links": { "Community": "Communauté", diff --git a/src/assets/translations/it-IT.json b/src/assets/translations/it-IT.json index 7677b2c58..c62911f54 100644 --- a/src/assets/translations/it-IT.json +++ b/src/assets/translations/it-IT.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "RIMBORSO DEGLI INTERESSI", "Supported Interest Refund Types": "Tipi di rimborso degli interessi supportati", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Tipi di rimborso degli interessi supportati", - "Charge-off reason": "Motivo dell'addebito" + "Charge-off reason": "Motivo dell'addebito", + "Password must contain at least one uppercase letter": "La password deve contenere almeno una lettera maiuscola", + "Password must contain at least one number": "La password deve contenere almeno un numero", + "Password must have not consecutive repeating characters": "La password non deve contenere caratteri ripetuti consecutivi", + "Password must contain at least one special character (@$!%*?&)": "La password deve contenere almeno un carattere speciale (@$!%*?&)", + "Password minimun length must be": "La lunghezza minima della password deve essere", + "Passwords do not match": "Le password non corrispondono" }, "links": { "Community": "Comunità", diff --git a/src/assets/translations/ko-KO.json b/src/assets/translations/ko-KO.json index 56bf118ff..da807e2a6 100644 --- a/src/assets/translations/ko-KO.json +++ b/src/assets/translations/ko-KO.json @@ -2451,7 +2451,13 @@ "INTEREST REFUND": "이자환급", "Supported Interest Refund Types": "지원되는 이자 환불 유형", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "지원되는 이자 환불 유형", - "Charge-off reason": "청구 취소 사유" + "Charge-off reason": "청구 취소 사유", + "Password must contain at least one uppercase letter": "비밀번호에는 대문자가 하나 이상 포함되어야 합니다.", + "Password must contain at least one number": "비밀번호에는 숫자가 하나 이상 포함되어야 합니다.", + "Password must have not consecutive repeating characters": "비밀번호에는 연속된 반복 문자가 없어야 합니다.", + "Password must contain at least one special character (@$!%*?&)": "비밀번호에는 특수 문자가 하나 이상 포함되어야 합니다. (@$!%*?&)", + "Password minimun length must be": "비밀번호의 최소 길이는 다음과 같아야 합니다.", + "Passwords do not match": "비밀번호가 일치하지 않습니다." }, "links": { "Community": "지역 사회", diff --git a/src/assets/translations/lt-LT.json b/src/assets/translations/lt-LT.json index d1c91805c..d7c0e577e 100644 --- a/src/assets/translations/lt-LT.json +++ b/src/assets/translations/lt-LT.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "PALŪKANŲ GRĄŽINIMAS", "Supported Interest Refund Types": "Palaikomi palūkanų grąžinimo tipai", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Palaikomi palūkanų grąžinimo tipai", - "Charge-off reason": "Apmokestinimo priežastis" + "Charge-off reason": "Apmokestinimo priežastis", + "Password must contain at least one uppercase letter": "Slaptažodyje turi būti bent viena didžioji raidė", + "Password must contain at least one number": "Slaptažodyje turi būti bent vienas skaičius", + "Password must have not consecutive repeating characters": "Slaptažodyje neturi būti iš eilės besikartojančių simbolių", + "Password must contain at least one special character (@$!%*?&)": "Slaptažodyje turi būti bent vienas specialusis simbolis (@$!%*?&)", + "Password minimun length must be": "Minimalus slaptažodžio ilgis turi būti", + "Passwords do not match": "Slaptažodžiai nesutampa" }, "links": { "Community": "bendruomenė", diff --git a/src/assets/translations/lv-LV.json b/src/assets/translations/lv-LV.json index 55c1ea903..7acaba761 100644 --- a/src/assets/translations/lv-LV.json +++ b/src/assets/translations/lv-LV.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "PROCENTU ATMAKSĀŠANA", "Supported Interest Refund Types": "Atbalstītie procentu atmaksas veidi", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Atbalstītie procentu atmaksas veidi", - "Charge-off reason": "Izmaksas iemesls" + "Charge-off reason": "Izmaksas iemesls", + "Password must contain at least one uppercase letter": "Parolē jāsatur vismaz viens lielais burts", + "Password must contain at least one number": "Parolē ir jāsatur vismaz viens cipars", + "Password must have not consecutive repeating characters": "Parolē nedrīkst būt secīgas atkārtotas rakstzīmes", + "Password must contain at least one special character (@$!%*?&)": "Parolē jāsatur vismaz viena speciālā rakstzīme (@$!%*?&)", + "Password minimun length must be": "Paroles minimālajam garumam jābūt", + "Passwords do not match": "Paroles nesakrīt" }, "links": { "Community": "kopiena", diff --git a/src/assets/translations/ne-NE.json b/src/assets/translations/ne-NE.json index db1ee5fa1..b762abcf2 100644 --- a/src/assets/translations/ne-NE.json +++ b/src/assets/translations/ne-NE.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "व्याज परतावा", "Supported Interest Refund Types": "समर्थित ब्याज फिर्ता प्रकारहरू", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "समर्थित व्याज परतावा प्रकार", - "Charge-off reason": "चार्ज अफ कारण" + "Charge-off reason": "चार्ज अफ कारण", + "Password must contain at least one uppercase letter": "पासवर्डमा कम्तिमा एउटा ठूलो अक्षर हुनु पर्छ", + "Password must contain at least one number": "पासवर्डमा कम्तिमा एउटा नम्बर हुनुपर्छ", + "Password must have not consecutive repeating characters": "पासवर्डमा लगातार दोहोरिने क्यारेक्टरहरू हुनु हुँदैन", + "Password must contain at least one special character (@$!%*?&)": "पासवर्डमा कम्तिमा एउटा विशेष वर्ण हुनुपर्छ (@$!%*?&)", + "Password minimun length must be": "पासवर्ड न्यूनतम लम्बाइ हुनुपर्छ", + "Passwords do not match": "पासवर्डहरू मेल खाँदैनन्" }, "links": { "Community": "समुदाय", diff --git a/src/assets/translations/pt-PT.json b/src/assets/translations/pt-PT.json index a090802c1..5d8bc1488 100644 --- a/src/assets/translations/pt-PT.json +++ b/src/assets/translations/pt-PT.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "REEMBOLSO DE JUROS", "Supported Interest Refund Types": "Tipos de reembolso de juros suportados", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Tipos de reembolso de juros suportados", - "Charge-off reason": "Motivo da cobrança" + "Charge-off reason": "Motivo da cobrança", + "Password must contain at least one uppercase letter": "A senha deve conter pelo menos uma letra maiúscula", + "Password must contain at least one number": "A senha deve conter pelo menos um número", + "Password must have not consecutive repeating characters": "A senha não deve conter caracteres repetidos consecutivos", + "Password must contain at least one special character (@$!%*?&)": "A senha deve conter pelo menos um caractere especial (@$!%*?&)", + "Password minimun length must be": "O comprimento mínimo da senha deve ser", + "Passwords do not match": "As senhas não coincidem" }, "links": { "Community": "Comunidade", diff --git a/src/assets/translations/sw-SW.json b/src/assets/translations/sw-SW.json index d76fb5a87..6e0dd830e 100644 --- a/src/assets/translations/sw-SW.json +++ b/src/assets/translations/sw-SW.json @@ -2450,7 +2450,13 @@ "INTEREST REFUND": "MAREJESHO YA RIBA", "Supported Interest Refund Types": "Aina za Urejeshaji wa Riba Zinazotumika", "SUPPORTED REFUND TRANSACTIONS FOR INTEREST REFUND": "Aina za kurejesha riba zinazotumika", - "Charge-off reason": "Sababu ya malipo" + "Charge-off reason": "Sababu ya malipo", + "Password must contain at least one uppercase letter": "Nenosiri lazima liwe na angalau herufi kubwa moja", + "Password must contain at least one number": "Nenosiri lazima liwe na angalau nambari moja", + "Password must have not consecutive repeating characters": "Nenosiri lazima lisiwe na vibambo vinavyorudiwa mfululizo", + "Password must contain at least one special character (@$!%*?&)": "Nenosiri lazima liwe na angalau herufi moja maalum (@$!%*?&)", + "Password minimun length must be": "Nenosiri la urefu wa chini lazima liwe", + "Passwords do not match": "Manenosiri hayalingani" }, "links": { "Community": "Jumuiya",