[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 { 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";
|
||||
|
||||
@Injectable()
|
||||
|
@ -51,7 +51,7 @@ export class InitService {
|
|||
this.syncService.fullSync(true);
|
||||
await this.vaultTimeoutService.init(true);
|
||||
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.twoFactorService.init();
|
||||
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 { ElectronStateService } from "../../platform/services/electron-state.service";
|
||||
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 { NativeMessageHandlerService } from "../../services/native-message-handler.service";
|
||||
import { NativeMessagingService } from "../../services/native-messaging.service";
|
||||
|
@ -91,7 +91,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK");
|
|||
},
|
||||
{
|
||||
provide: I18nServiceAbstraction,
|
||||
useClass: I18nService,
|
||||
useClass: I18nRendererService,
|
||||
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 { ElectronStateService } from "./platform/services/electron-state.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";
|
||||
|
||||
export class Main {
|
||||
logService: ElectronLogService;
|
||||
i18nService: I18nService;
|
||||
i18nService: I18nMainService;
|
||||
storageService: ElectronStorageService;
|
||||
memoryStorageService: MemoryStorageService;
|
||||
messagingService: ElectronMainMessagingService;
|
||||
|
@ -76,7 +76,7 @@ export class Main {
|
|||
}
|
||||
|
||||
this.logService = new ElectronLogService(null, app.getPath("userData"));
|
||||
this.i18nService = new I18nService("en", "./locales/");
|
||||
this.i18nService = new I18nMainService("en", "./locales/");
|
||||
|
||||
const storageDefaults: any = {};
|
||||
// Default vault timeout to "on restart", and action to "lock"
|
||||
|
|
|
@ -39,6 +39,9 @@ export default {
|
|||
ipcRenderer.on("systemThemeUpdated", (_event, theme: ThemeType) => callback(theme));
|
||||
},
|
||||
|
||||
getLanguageFile: (formattedLocale: string): Promise<object> =>
|
||||
ipcRenderer.invoke("getLanguageFile", formattedLocale),
|
||||
|
||||
sendMessage: (message: { command: string } & any) =>
|
||||
ipcRenderer.send("messagingService", message),
|
||||
onMessage: (callback: (message: { command: string } & any) => void) => {
|
||||
|
|
|
@ -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";
|
||||
|
||||
export class I18nService extends BaseI18nService {
|
||||
export class I18nRendererService extends BaseI18nService {
|
||||
constructor(systemLanguage: string, localesDirectory: string) {
|
||||
super(systemLanguage, localesDirectory, (formattedLocale: string) => {
|
||||
const filePath = path.join(
|
||||
__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);
|
||||
return ipc.platform.getLanguageFile(formattedLocale);
|
||||
});
|
||||
|
||||
// Please leave 'en' where it is, as it's our fallback language in case no translation can be found
|
Loading…
Reference in New Issue