1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-13 23:03:32 +00:00

Add lang attr on desktop and browser (#14691)

This commit is contained in:
Justin Baur
2025-06-09 06:54:00 -04:00
committed by GitHub
parent 685f7a0fd8
commit b1f090e054
8 changed files with 102 additions and 9 deletions

View File

@@ -0,0 +1,36 @@
import { mock } from "jest-mock-extended";
import { Subject } from "rxjs";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DocumentLangSetter } from "./document-lang.setter";
describe("DocumentLangSetter", () => {
const document = mock<Document>();
const i18nService = mock<I18nService>();
const sut = new DocumentLangSetter(document, i18nService);
describe("start", () => {
it("reacts to locale changes while start called with a non-closed subscription", async () => {
const localeSubject = new Subject<string>();
i18nService.locale$ = localeSubject;
localeSubject.next("en");
expect(document.documentElement.lang).toBeFalsy();
const sub = sut.start();
localeSubject.next("es");
expect(document.documentElement.lang).toBe("es");
sub.unsubscribe();
localeSubject.next("ar");
expect(document.documentElement.lang).toBe("es");
});
});
});

View File

@@ -0,0 +1,26 @@
import { Subscription } from "rxjs";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
/**
* A service for managing the setting of the `lang="<locale>" attribute on the
* main document for the application.
*/
export class DocumentLangSetter {
constructor(
private readonly document: Document,
private readonly i18nService: I18nService,
) {}
/**
* Starts listening to an upstream source for the best locale for the user
* and applies it to the application document.
* @returns A subscription that can be unsubscribed if you wish to stop
* applying lang attribute updates to the application document.
*/
start(): Subscription {
return this.i18nService.locale$.subscribe((locale) => {
this.document.documentElement.lang = locale;
});
}
}

View File

@@ -0,0 +1 @@
export { DocumentLangSetter } from "./document-lang.setter";

View File

@@ -21,6 +21,7 @@ import { SafeInjectionToken } from "@bitwarden/ui-common";
export { SafeInjectionToken } from "@bitwarden/ui-common";
export const WINDOW = new SafeInjectionToken<Window>("WINDOW");
export const DOCUMENT = new SafeInjectionToken<Document>("DOCUMENT");
export const OBSERVABLE_MEMORY_STORAGE = new SafeInjectionToken<
AbstractStorageService & ObservableStorageService
>("OBSERVABLE_MEMORY_STORAGE");

View File

@@ -337,6 +337,7 @@ import {
import { DeviceTrustToastService as DeviceTrustToastServiceAbstraction } from "../auth/services/device-trust-toast.service.abstraction";
import { DeviceTrustToastService } from "../auth/services/device-trust-toast.service.implementation";
import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service";
import { DocumentLangSetter } from "../platform/i18n";
import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service";
import { LoggingErrorHandler } from "../platform/services/logging-error-handler";
import { AngularThemingService } from "../platform/services/theming/angular-theming.service";
@@ -349,6 +350,7 @@ import { NoopViewCacheService } from "../platform/view-cache/internal";
import {
CLIENT_TYPE,
DEFAULT_VAULT_TIMEOUT,
DOCUMENT,
ENV_ADDITIONAL_REGIONS,
HTTP_OPERATIONS,
INTRAPROCESS_MESSAGING_SUBJECT,
@@ -378,6 +380,7 @@ const safeProviders: SafeProvider[] = [
safeProvider(ModalService),
safeProvider(PasswordRepromptService),
safeProvider({ provide: WINDOW, useValue: window }),
safeProvider({ provide: DOCUMENT, useValue: document }),
safeProvider({
provide: LOCALE_ID as SafeInjectionToken<string>,
useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale,
@@ -1542,6 +1545,11 @@ const safeProviders: SafeProvider[] = [
useClass: MasterPasswordApiService,
deps: [ApiServiceAbstraction, LogService],
}),
safeProvider({
provide: DocumentLangSetter,
useClass: DocumentLangSetter,
deps: [DOCUMENT, I18nServiceAbstraction],
}),
safeProvider({
provide: CipherEncryptionService,
useClass: DefaultCipherEncryptionService,