diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json
index 8e52b1ba00..0e4c0da245 100644
--- a/apps/browser/src/_locales/en/messages.json
+++ b/apps/browser/src/_locales/en/messages.json
@@ -2877,6 +2877,20 @@
"generateEmail": {
"message": "Generate email"
},
+ "generatorBoundariesHint": {
+ "message": "Value must be between $MIN$ and $MAX$",
+ "description": "Explains spin box minimum and maximum values to the user",
+ "placeholders": {
+ "min": {
+ "content": "$1",
+ "example": "8"
+ },
+ "max": {
+ "content": "$2",
+ "example": "128"
+ }
+ }
+ },
"usernameType": {
"message": "Username type"
},
diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json
index d24616edab..414f254e7a 100644
--- a/apps/desktop/src/locales/en/messages.json
+++ b/apps/desktop/src/locales/en/messages.json
@@ -2393,6 +2393,20 @@
"generateEmail": {
"message": "Generate email"
},
+ "generatorBoundariesHint": {
+ "message": "Value must be between $MIN$ and $MAX$",
+ "description": "Explains spin box minimum and maximum values to the user",
+ "placeholders": {
+ "min": {
+ "content": "$1",
+ "example": "8"
+ },
+ "max": {
+ "content": "$2",
+ "example": "128"
+ }
+ }
+ },
"usernameType": {
"message": "Username type"
},
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index f25f03c281..eead4fd80d 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -6412,6 +6412,20 @@
"generateEmail": {
"message": "Generate email"
},
+ "generatorBoundariesHint": {
+ "message": "Value must be between $MIN$ and $MAX$",
+ "description": "Explains spin box minimum and maximum values to the user",
+ "placeholders": {
+ "min": {
+ "content": "$1",
+ "example": "8"
+ },
+ "max": {
+ "content": "$2",
+ "example": "128"
+ }
+ }
+ },
"usernameType": {
"message": "Username type"
},
diff --git a/libs/tools/generator/components/src/passphrase-settings.component.html b/libs/tools/generator/components/src/passphrase-settings.component.html
index 25e9684e86..d089de7a07 100644
--- a/libs/tools/generator/components/src/passphrase-settings.component.html
+++ b/libs/tools/generator/components/src/passphrase-settings.component.html
@@ -8,6 +8,7 @@
{{ "numWords" | i18n }}
+ {{ numWordsBoundariesHint$ | async }}
diff --git a/libs/tools/generator/components/src/passphrase-settings.component.ts b/libs/tools/generator/components/src/passphrase-settings.component.ts
index 4c171e0c20..d65e897f4e 100644
--- a/libs/tools/generator/components/src/passphrase-settings.component.ts
+++ b/libs/tools/generator/components/src/passphrase-settings.component.ts
@@ -1,9 +1,10 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { OnInit, Input, Output, EventEmitter, Component, OnDestroy } from "@angular/core";
import { FormBuilder } from "@angular/forms";
-import { BehaviorSubject, skip, takeUntil, Subject } from "rxjs";
+import { BehaviorSubject, skip, takeUntil, Subject, ReplaySubject } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { UserId } from "@bitwarden/common/types/guid";
import {
Generators,
@@ -29,11 +30,13 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
/** Instantiates the component
* @param accountService queries user availability
* @param generatorService settings and policy logic
+ * @param i18nService localize hints
* @param formBuilder reactive form controls
*/
constructor(
private formBuilder: FormBuilder,
private generatorService: CredentialGeneratorService,
+ private i18nService: I18nService,
private accountService: AccountService,
) {}
@@ -97,6 +100,13 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
this.toggleEnabled(Controls.capitalize, !constraints.capitalize?.readonly);
this.toggleEnabled(Controls.includeNumber, !constraints.includeNumber?.readonly);
+
+ const boundariesHint = this.i18nService.t(
+ "generatorBoundariesHint",
+ constraints.numWords.min,
+ constraints.numWords.max,
+ );
+ this.numWordsBoundariesHint.next(boundariesHint);
});
// now that outputs are set up, connect inputs
@@ -106,6 +116,11 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
/** display binding for enterprise policy notice */
protected policyInEffect: boolean;
+ private numWordsBoundariesHint = new ReplaySubject(1);
+
+ /** display binding for min/max constraints of `numWords` */
+ protected numWordsBoundariesHint$ = this.numWordsBoundariesHint.asObservable();
+
private toggleEnabled(setting: keyof typeof Controls, enabled: boolean) {
if (enabled) {
this.settings.get(setting).enable({ emitEvent: false });
diff --git a/libs/tools/generator/components/src/password-settings.component.html b/libs/tools/generator/components/src/password-settings.component.html
index 5e1d1941e7..aa12a3247c 100644
--- a/libs/tools/generator/components/src/password-settings.component.html
+++ b/libs/tools/generator/components/src/password-settings.component.html
@@ -8,6 +8,7 @@
{{ "length" | i18n }}
+ {{ lengthBoundariesHint$ | async }}
diff --git a/libs/tools/generator/components/src/password-settings.component.ts b/libs/tools/generator/components/src/password-settings.component.ts
index 7832818d67..6e9d106b71 100644
--- a/libs/tools/generator/components/src/password-settings.component.ts
+++ b/libs/tools/generator/components/src/password-settings.component.ts
@@ -1,9 +1,10 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { OnInit, Input, Output, EventEmitter, Component, OnDestroy } from "@angular/core";
import { FormBuilder } from "@angular/forms";
-import { BehaviorSubject, takeUntil, Subject, map, filter, tap, skip } from "rxjs";
+import { BehaviorSubject, takeUntil, Subject, map, filter, tap, skip, ReplaySubject } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
+import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { UserId } from "@bitwarden/common/types/guid";
import {
Generators,
@@ -33,11 +34,13 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy {
/** Instantiates the component
* @param accountService queries user availability
* @param generatorService settings and policy logic
+ * @param i18nService localize hints
* @param formBuilder reactive form controls
*/
constructor(
private formBuilder: FormBuilder,
private generatorService: CredentialGeneratorService,
+ private i18nService: I18nService,
private accountService: AccountService,
) {}
@@ -147,6 +150,13 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy {
for (const [control, enabled] of toggles) {
this.toggleEnabled(control, enabled);
}
+
+ const boundariesHint = this.i18nService.t(
+ "generatorBoundariesHint",
+ constraints.length.min,
+ constraints.length.max,
+ );
+ this.lengthBoundariesHint.next(boundariesHint);
});
// cascade selections between checkboxes and spinboxes
@@ -208,6 +218,11 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy {
/** display binding for enterprise policy notice */
protected policyInEffect: boolean;
+ private lengthBoundariesHint = new ReplaySubject(1);
+
+ /** display binding for min/max constraints of `length` */
+ protected lengthBoundariesHint$ = this.lengthBoundariesHint.asObservable();
+
private toggleEnabled(setting: keyof typeof Controls, enabled: boolean) {
if (enabled) {
this.settings.get(setting).enable({ emitEvent: false });