mirror of
https://github.com/bitwarden/browser.git
synced 2024-09-27 04:03:00 +02:00
[PM-3753] Update electron desktop language handling (#6482)
* [PM-3753] Update desktop language handling * Remove i18n service import aliases * Validate the provided locale before loading it * Support underscores in locales
This commit is contained in:
parent
17897cfe35
commit
222345f0c9
@ -17,7 +17,7 @@ import { EventUploadService } from "@bitwarden/common/services/event/event-uploa
|
|||||||
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
|
||||||
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||||
|
|
||||||
import { I18nService } from "../../platform/services/i18n.service";
|
import { I18nRendererService } from "../../platform/services/i18n.renderer.service";
|
||||||
import { NativeMessagingService } from "../../services/native-messaging.service";
|
import { NativeMessagingService } from "../../services/native-messaging.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -51,7 +51,7 @@ export class InitService {
|
|||||||
this.syncService.fullSync(true);
|
this.syncService.fullSync(true);
|
||||||
await this.vaultTimeoutService.init(true);
|
await this.vaultTimeoutService.init(true);
|
||||||
const locale = await this.stateService.getLocale();
|
const locale = await this.stateService.getLocale();
|
||||||
await (this.i18nService as I18nService).init(locale);
|
await (this.i18nService as I18nRendererService).init(locale);
|
||||||
(this.eventUploadService as EventUploadService).init(true);
|
(this.eventUploadService as EventUploadService).init(true);
|
||||||
this.twoFactorService.init();
|
this.twoFactorService.init();
|
||||||
setTimeout(() => this.notificationsService.init(), 3000);
|
setTimeout(() => this.notificationsService.init(), 3000);
|
||||||
|
@ -48,7 +48,7 @@ import { ElectronRendererSecureStorageService } from "../../platform/services/el
|
|||||||
import { ElectronRendererStorageService } from "../../platform/services/electron-renderer-storage.service";
|
import { ElectronRendererStorageService } from "../../platform/services/electron-renderer-storage.service";
|
||||||
import { ElectronStateService } from "../../platform/services/electron-state.service";
|
import { ElectronStateService } from "../../platform/services/electron-state.service";
|
||||||
import { ElectronStateService as ElectronStateServiceAbstraction } from "../../platform/services/electron-state.service.abstraction";
|
import { ElectronStateService as ElectronStateServiceAbstraction } from "../../platform/services/electron-state.service.abstraction";
|
||||||
import { I18nService } from "../../platform/services/i18n.service";
|
import { I18nRendererService } from "../../platform/services/i18n.renderer.service";
|
||||||
import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service";
|
import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service";
|
||||||
import { NativeMessageHandlerService } from "../../services/native-message-handler.service";
|
import { NativeMessageHandlerService } from "../../services/native-message-handler.service";
|
||||||
import { NativeMessagingService } from "../../services/native-messaging.service";
|
import { NativeMessagingService } from "../../services/native-messaging.service";
|
||||||
@ -91,7 +91,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: I18nServiceAbstraction,
|
provide: I18nServiceAbstraction,
|
||||||
useClass: I18nService,
|
useClass: I18nRendererService,
|
||||||
deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY],
|
deps: [SYSTEM_LANGUAGE, LOCALES_DIRECTORY],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -21,12 +21,12 @@ import { DesktopCredentialStorageListener } from "./platform/main/desktop-creden
|
|||||||
import { ElectronLogService } from "./platform/services/electron-log.service";
|
import { ElectronLogService } from "./platform/services/electron-log.service";
|
||||||
import { ElectronStateService } from "./platform/services/electron-state.service";
|
import { ElectronStateService } from "./platform/services/electron-state.service";
|
||||||
import { ElectronStorageService } from "./platform/services/electron-storage.service";
|
import { ElectronStorageService } from "./platform/services/electron-storage.service";
|
||||||
import { I18nService } from "./platform/services/i18n.service";
|
import { I18nMainService } from "./platform/services/i18n.main.service";
|
||||||
import { ElectronMainMessagingService } from "./services/electron-main-messaging.service";
|
import { ElectronMainMessagingService } from "./services/electron-main-messaging.service";
|
||||||
|
|
||||||
export class Main {
|
export class Main {
|
||||||
logService: ElectronLogService;
|
logService: ElectronLogService;
|
||||||
i18nService: I18nService;
|
i18nService: I18nMainService;
|
||||||
storageService: ElectronStorageService;
|
storageService: ElectronStorageService;
|
||||||
memoryStorageService: MemoryStorageService;
|
memoryStorageService: MemoryStorageService;
|
||||||
messagingService: ElectronMainMessagingService;
|
messagingService: ElectronMainMessagingService;
|
||||||
@ -76,7 +76,7 @@ export class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logService = new ElectronLogService(null, app.getPath("userData"));
|
this.logService = new ElectronLogService(null, app.getPath("userData"));
|
||||||
this.i18nService = new I18nService("en", "./locales/");
|
this.i18nService = new I18nMainService("en", "./locales/");
|
||||||
|
|
||||||
const storageDefaults: any = {};
|
const storageDefaults: any = {};
|
||||||
// Default vault timeout to "on restart", and action to "lock"
|
// Default vault timeout to "on restart", and action to "lock"
|
||||||
|
@ -39,6 +39,9 @@ export default {
|
|||||||
ipcRenderer.on("systemThemeUpdated", (_event, theme: ThemeType) => callback(theme));
|
ipcRenderer.on("systemThemeUpdated", (_event, theme: ThemeType) => callback(theme));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getLanguageFile: (formattedLocale: string): Promise<object> =>
|
||||||
|
ipcRenderer.invoke("getLanguageFile", formattedLocale),
|
||||||
|
|
||||||
sendMessage: (message: { command: string } & any) =>
|
sendMessage: (message: { command: string } & any) =>
|
||||||
ipcRenderer.send("messagingService", message),
|
ipcRenderer.send("messagingService", message),
|
||||||
onMessage: (callback: (message: { command: string } & any) => void) => {
|
onMessage: (callback: (message: { command: string } & any) => void) => {
|
||||||
|
90
apps/desktop/src/platform/services/i18n.main.service.ts
Normal file
90
apps/desktop/src/platform/services/i18n.main.service.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { promises as fs } from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
import { ipcMain } from "electron";
|
||||||
|
|
||||||
|
import { I18nService as BaseI18nService } from "@bitwarden/common/platform/services/i18n.service";
|
||||||
|
|
||||||
|
export class I18nMainService extends BaseI18nService {
|
||||||
|
constructor(systemLanguage: string, localesDirectory: string) {
|
||||||
|
super(systemLanguage, localesDirectory, (formattedLocale: string) =>
|
||||||
|
this.readLanguageFile(formattedLocale)
|
||||||
|
);
|
||||||
|
|
||||||
|
ipcMain.handle("getLanguageFile", async (event, formattedLocale: string) =>
|
||||||
|
this.readLanguageFile(formattedLocale)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Please leave 'en' where it is, as it's our fallback language in case no translation can be found
|
||||||
|
this.supportedTranslationLocales = [
|
||||||
|
"en",
|
||||||
|
"af",
|
||||||
|
"ar",
|
||||||
|
"az",
|
||||||
|
"be",
|
||||||
|
"bg",
|
||||||
|
"bn",
|
||||||
|
"bs",
|
||||||
|
"ca",
|
||||||
|
"cs",
|
||||||
|
"da",
|
||||||
|
"de",
|
||||||
|
"el",
|
||||||
|
"en-GB",
|
||||||
|
"en-IN",
|
||||||
|
"eo",
|
||||||
|
"es",
|
||||||
|
"et",
|
||||||
|
"eu",
|
||||||
|
"fa",
|
||||||
|
"fi",
|
||||||
|
"fil",
|
||||||
|
"fr",
|
||||||
|
"he",
|
||||||
|
"hi",
|
||||||
|
"hr",
|
||||||
|
"hu",
|
||||||
|
"id",
|
||||||
|
"it",
|
||||||
|
"ja",
|
||||||
|
"ka",
|
||||||
|
"km",
|
||||||
|
"kn",
|
||||||
|
"ko",
|
||||||
|
"lv",
|
||||||
|
"me",
|
||||||
|
"ml",
|
||||||
|
"nb",
|
||||||
|
"nl",
|
||||||
|
"nn",
|
||||||
|
"pl",
|
||||||
|
"pt-BR",
|
||||||
|
"pt-PT",
|
||||||
|
"ro",
|
||||||
|
"ru",
|
||||||
|
"si",
|
||||||
|
"sk",
|
||||||
|
"sl",
|
||||||
|
"sr",
|
||||||
|
"sv",
|
||||||
|
"th",
|
||||||
|
"tr",
|
||||||
|
"uk",
|
||||||
|
"vi",
|
||||||
|
"zh-CN",
|
||||||
|
"zh-TW",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private async readLanguageFile(formattedLocale: string): Promise<any> {
|
||||||
|
// Check that the provided locale only contains letters and dashes and underscores to avoid possible path traversal
|
||||||
|
if (!/^[a-zA-Z_-]+$/.test(formattedLocale)) {
|
||||||
|
return Promise.resolve({});
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(__dirname, this.localesDirectory, formattedLocale, "messages.json");
|
||||||
|
const localesJson = await fs.readFile(filePath, "utf8");
|
||||||
|
const locales = JSON.parse(localesJson.replace(/^\uFEFF/, "")); // strip the BOM
|
||||||
|
return Promise.resolve(locales);
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,9 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
|
|
||||||
import { I18nService as BaseI18nService } from "@bitwarden/common/platform/services/i18n.service";
|
import { I18nService as BaseI18nService } from "@bitwarden/common/platform/services/i18n.service";
|
||||||
|
|
||||||
export class I18nService extends BaseI18nService {
|
export class I18nRendererService extends BaseI18nService {
|
||||||
constructor(systemLanguage: string, localesDirectory: string) {
|
constructor(systemLanguage: string, localesDirectory: string) {
|
||||||
super(systemLanguage, localesDirectory, (formattedLocale: string) => {
|
super(systemLanguage, localesDirectory, (formattedLocale: string) => {
|
||||||
const filePath = path.join(
|
return ipc.platform.getLanguageFile(formattedLocale);
|
||||||
__dirname,
|
|
||||||
this.localesDirectory + "/" + formattedLocale + "/messages.json"
|
|
||||||
);
|
|
||||||
const localesJson = fs.readFileSync(filePath, "utf8");
|
|
||||||
const locales = JSON.parse(localesJson.replace(/^\uFEFF/, "")); // strip the BOM
|
|
||||||
return Promise.resolve(locales);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Please leave 'en' where it is, as it's our fallback language in case no translation can be found
|
// Please leave 'en' where it is, as it's our fallback language in case no translation can be found
|
Loading…
Reference in New Issue
Block a user