diff options
Diffstat (limited to 'gui/src/main')
| -rw-r--r-- | gui/src/main/index.ts | 26 | ||||
| -rw-r--r-- | gui/src/main/load-translations.ts | 72 |
2 files changed, 86 insertions, 12 deletions
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts index ec67280e12..872c043f89 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -34,7 +34,7 @@ import { RelaySettingsUpdate, TunnelState, } from '../shared/daemon-rpc-types'; -import { loadTranslations, messages } from '../shared/gettext'; +import { messages, relayLocations } from '../shared/gettext'; import { SYSTEM_PREFERRED_LOCALE_KEY } from '../shared/gui-settings-state'; import { IpcMainEventChannel } from '../shared/ipc-event-channel'; import log, { ConsoleOutput, Logger } from '../shared/logging'; @@ -65,11 +65,13 @@ import { IpcInput, OLD_LOG_FILES, } from './logging'; +import { loadTranslations } from './load-translations'; import NotificationController from './notification-controller'; import { resolveBin } from './proc'; import ReconnectionBackoff from './reconnection-backoff'; import TrayIconController, { TrayIconType } from './tray-icon-controller'; import WindowController from './window-controller'; +import { ITranslations } from '../shared/ipc-schema'; // Only import when running app on Linux. const linuxSplitTunneling = process.platform === 'linux' && require('./linux-split-tunneling'); @@ -204,6 +206,7 @@ class ApplicationMain { private autoConnectFallbackScheduler = new Scheduler(); private rendererLog?: Logger; + private translations: ITranslations = { locale: this.locale }; public run() { // Remove window animations to combat window flickering when opening window. Can be removed when @@ -373,7 +376,7 @@ class ApplicationMain { this.blockRequests(); - this.updateCurrentLocale(); + this.translations = this.updateCurrentLocale(); this.daemonRpc.addConnectionObserver( new ConnectionObserver(this.onDaemonConnected, this.onDaemonDisconnected), @@ -1007,6 +1010,7 @@ class ApplicationMain { upgradeVersion: this.upgradeVersion, guiSettings: this.guiSettings.state, wireguardPublicKey: this.wireguardPublicKey, + translations: this.translations, })); IpcMainEventChannel.settings.handleSetAllowLan((allowLan: boolean) => @@ -1074,7 +1078,7 @@ class ApplicationMain { IpcMainEventChannel.guiSettings.handleSetPreferredLocale((locale: string) => { this.guiSettings.preferredLocale = locale; - this.didChangeLocale(); + return Promise.resolve(this.updateCurrentLocale()); }); IpcMainEventChannel.account.handleCreate(() => this.createNewAccount()); @@ -1368,15 +1372,13 @@ class ApplicationMain { log.info(`Detected locale: ${this.locale}`); - loadTranslations(this.locale, messages); - } - - private didChangeLocale() { - this.updateCurrentLocale(); - - if (this.windowController) { - IpcMainEventChannel.locale.notify(this.windowController.webContents, this.locale); - } + const messagesTranslations = loadTranslations(this.locale, messages); + const relayLocationsTranslations = loadTranslations(this.locale, relayLocations); + return { + locale: this.locale, + messages: messagesTranslations, + relayLocations: relayLocationsTranslations, + }; } // Since the app frontend never performs any network requests, all requests originating from the diff --git a/gui/src/main/load-translations.ts b/gui/src/main/load-translations.ts new file mode 100644 index 0000000000..63d76f0ac6 --- /dev/null +++ b/gui/src/main/load-translations.ts @@ -0,0 +1,72 @@ +import fs from 'fs'; +import { GetTextTranslations, po } from 'gettext-parser'; +import Gettext from 'node-gettext'; +import path from 'path'; +import log from '../shared/logging'; + +const SOURCE_LANGUAGE = 'en'; +const LOCALES_DIR = path.resolve(__dirname, '../../locales'); + +export function loadTranslations( + currentLocale: string, + catalogue: Gettext, +): GetTextTranslations | undefined { + // First look for exact match of the current locale + const preferredLocales = []; + + if (currentLocale !== SOURCE_LANGUAGE) { + preferredLocales.push(currentLocale); + } + + // In case of region bound locale like en-US, fallback to en. + const language = Gettext.getLanguageCode(currentLocale); + if (currentLocale !== language) { + preferredLocales.push(language); + } + + const domain = catalogue.domain; + for (const locale of preferredLocales) { + const parsedTranslations = parseTranslation(locale, domain, catalogue); + if (parsedTranslations) { + log.info(`Loaded translations ${locale}/${domain}`); + catalogue.setLocale(locale); + return parsedTranslations; + } + } + + // Reset the locale to source language if we couldn't load the catalogue for the requested locale + // Add empty translations to suppress some of the warnings produces by node-gettext + catalogue.addTranslations(SOURCE_LANGUAGE, domain, {}); + catalogue.setLocale(SOURCE_LANGUAGE); + return; +} + +function parseTranslation( + locale: string, + domain: string, + catalogue: Gettext, +): GetTextTranslations | undefined { + const filename = path.join(LOCALES_DIR, locale, `${domain}.po`); + let contents: string; + + try { + contents = fs.readFileSync(filename, { encoding: 'utf8' }); + } catch (error) { + if (error.code !== 'ENOENT') { + log.error(`Cannot read the gettext file "${filename}": ${error.message}`); + } + return undefined; + } + + let translations: GetTextTranslations; + try { + translations = po.parse(contents); + } catch (error) { + log.error(`Cannot parse the gettext file "${filename}": ${error.message}`); + return undefined; + } + + catalogue.addTranslations(locale, domain, translations); + + return translations; +} |
