feat: add localization to the node side of the app

pull/3206/head
Ryan Miller 8 months ago
parent 913078d940
commit a9be08bd4e

@ -159,13 +159,16 @@ if (windowFromUserConfig) {
import { readFile } from 'fs-extra';
import { getAppRootPath } from '../node/getRootPath';
import { setLastestRelease } from '../node/latest_desktop_release';
import { load as loadLocale } from '../node/locale';
import { isDevProd, isTestIntegration } from '../shared/env_vars';
import { classicDark } from '../themes';
import type { SetupI18nReturnType } from '../types/Localizer';
import { getTranslationDictionary } from '../util/i18n/translationDictionaries';
import { getLocale, isLocaleSet, type Locale } from '../util/i18n/shared';
import { loadLocalizedDictionary } from '../node/locale';
// Both of these will be set after app fires the 'ready' event
let logger: Logger | null = null;
let locale: ReturnType<typeof loadLocale>;
let i18n: SetupI18nReturnType;
function assertLogger(): Logger {
if (!logger) {
@ -181,7 +184,7 @@ function prepareURL(pathSegments: Array<string>, moreKeys?: { theme: any }) {
slashes: true,
query: {
name: packageJson.productName,
locale: locale.name,
locale: getLocale(),
version: app.getVersion(),
commitHash: config.get('commitHash'),
environment: (config as any).environment,
@ -343,7 +346,7 @@ async function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow(windowOptions);
setupSpellChecker(mainWindow, locale.messages);
setupSpellChecker(mainWindow, i18n);
const setWindowFocus = () => {
if (!mainWindow) {
@ -492,6 +495,7 @@ ipc.on('set-release-from-file-server', (_event, releaseGotFromFileServer) => {
});
let isReadyForUpdates = false;
async function readyForUpdates() {
console.log('[updater] isReadyForUpdates', isReadyForUpdates);
if (isReadyForUpdates) {
@ -503,12 +507,13 @@ async function readyForUpdates() {
// Second, start checking for app updates
try {
// if the user disabled auto updates, this will actually not start the updater
await updater.start(getMainWindow, userConfig, locale.messages, logger);
await updater.start(getMainWindow, userConfig, i18n, logger);
} catch (error) {
const log = logger || console;
log.error('Error starting update checks:', error && error.stack ? error.stack : error);
}
}
ipc.once('ready-for-updates', readyForUpdates);
// Forcefully call readyForUpdates after 10 minutes.
@ -527,6 +532,7 @@ function openSupportPage() {
}
let passwordWindow: BrowserWindow | null = null;
async function showPasswordWindow() {
if (passwordWindow) {
passwordWindow.show();
@ -614,7 +620,7 @@ async function showAbout() {
width: 500,
height: 500,
resizeable: true,
title: locale.messages.about,
title: i18n('about'),
autoHideMenuBar: true,
backgroundColor: classicDark['--background-primary-color'],
show: false,
@ -710,10 +716,11 @@ app.on('ready', async () => {
logger = getLogger();
assertLogger().info('app ready');
assertLogger().info(`starting version ${packageJson.version}`);
if (!locale) {
const appLocale = process.env.LANGUAGE || app.getLocale() || 'en';
locale = loadLocale({ appLocale, logger });
assertLogger().info(`locale is ${appLocale}`);
if (!isLocaleSet()) {
const appLocale = (process.env.LANGUAGE || app.getLocale() || 'en') as Locale;
const loadedLocale = loadLocalizedDictionary({ appLocale, logger });
i18n = loadedLocale.i18n;
assertLogger().info(`locale is ${loadedLocale.locale}`);
}
const key = getDefaultSQLKey();
@ -774,7 +781,7 @@ async function showMainWindow(sqlKey: string, passwordAttempt = false) {
await sqlNode.initializeSql({
configDir: userDataPath,
key: sqlKey,
messages: locale.messages,
i18n,
passwordAttempt,
});
appStartInitialSpellcheckSetting = await getSpellCheckSetting();
@ -789,7 +796,7 @@ async function showMainWindow(sqlKey: string, passwordAttempt = false) {
await createWindow();
if (getStartInTray().usingTrayIcon) {
tray = createTrayIcon(getMainWindow, locale.messages);
tray = createTrayIcon(getMainWindow, i18n);
}
setupMenu();
@ -806,7 +813,7 @@ function setupMenu() {
openSupportPage,
platform,
};
const template = createTemplate(menuOptions, locale.messages);
const template = createTemplate(menuOptions, i18n);
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
@ -897,7 +904,10 @@ app.on('web-contents-created', (_createEvent, contents) => {
// Ingested in preload.js via a sendSync call
ipc.on('locale-data', event => {
// eslint-disable-next-line no-param-reassign
event.returnValue = locale.messages;
event.returnValue = {
dictionary: getTranslationDictionary(),
locale: getLocale(),
};
});
ipc.on('draw-attention', () => {
@ -949,8 +959,7 @@ ipc.on('password-window-login', async (event, passPhrase) => {
await showMainWindow(passPhrase, passwordAttempt);
sendResponse(undefined);
} catch (e) {
const localisedError = locale.messages.passwordIncorrect;
sendResponse(localisedError);
sendResponse(i18n('passwordIncorrect'));
}
});
@ -959,7 +968,7 @@ ipc.on('start-in-tray-on-start', (event, newValue) => {
userConfig.set('startInTray', newValue);
if (newValue) {
if (!tray) {
tray = createTrayIcon(getMainWindow, locale.messages);
tray = createTrayIcon(getMainWindow, i18n);
}
} else {
// destroy is not working for a lot of desktop env. So for simplicity, we don't destroy it here but just
@ -1011,10 +1020,7 @@ ipc.on('set-password', async (event, passPhrase, oldPhrase) => {
const hashMatches = oldPhrase && PasswordUtil.matchesHash(oldPhrase, hash);
if (hash && !hashMatches) {
const incorrectOldPassword = locale.messages.passwordCurrentIncorrect;
sendResponse(
incorrectOldPassword || 'Failed to set password: Old password provided is invalid'
);
sendResponse(i18n('passwordCurrentIncorrect'));
return;
}
@ -1033,8 +1039,7 @@ ipc.on('set-password', async (event, passPhrase, oldPhrase) => {
sendResponse(updatedHash);
}
} catch (e) {
const localisedError = locale.messages.passwordFailed;
sendResponse(localisedError || 'Failed to set password');
sendResponse(i18n('passwordFailed'));
}
});

@ -1,8 +1,10 @@
import fs from 'fs';
import _ from 'lodash';
import path from 'path';
import type { LocalizerDictionary } from '../types/Localizer';
import type { LocalizerDictionary, SetupI18nReturnType } from '../types/Localizer';
import { getAppRootPath } from './getRootPath';
import type { Locale } from '../util/i18n/shared';
import { en } from '../localization/locales';
import { setupI18n } from '../util/i18n/i18n';
function normalizeLocaleName(locale: string) {
const dashedLocale = locale.replaceAll('_', '-');
@ -41,9 +43,12 @@ function getLocaleMessages(locale: string): LocalizerDictionary {
return JSON.parse(fs.readFileSync(targetFile, 'utf-8'));
}
export function load({ appLocale, logger }: { appLocale?: string; logger?: any } = {}): {
name: string;
messages: LocalizerDictionary;
export function loadLocalizedDictionary({
appLocale,
logger,
}: { appLocale?: Locale; logger?: any } = {}): {
locale: Locale;
i18n: SetupI18nReturnType;
} {
if (!appLocale) {
throw new TypeError('`appLocale` is required');
@ -53,29 +58,31 @@ export function load({ appLocale, logger }: { appLocale?: string; logger?: any }
throw new TypeError('`logger.error` is required');
}
const english = getLocaleMessages('en');
const normalizedLocaleName = normalizeLocaleName(appLocale);
// Load locale - if we can't load messages for the current locale, we
// default to 'en'
//
// possible locales:
// https://github.com/electron/electron/blob/master/docs/api/locales.md
let locale = normalizeLocaleName(appLocale) as Locale;
let translationDictionary;
try {
// Load locale - if we can't load messages for the current locale, we
// default to 'en'
//
// possible locales:
// https://github.com/electron/electron/blob/master/docs/api/locales.md
// We start with english, then overwrite that with anything present in locale
const messages = _.merge(english, getLocaleMessages(normalizedLocaleName));
return {
name: normalizedLocaleName,
messages,
};
translationDictionary = getLocaleMessages(locale);
} catch (e) {
logger.error(`Problem loading messages for locale ${normalizedLocaleName} ${e.stack}`);
logger.error(`Problem loading messages for locale ${locale} ${e.stack}`);
logger.error('Falling back to en locale');
return {
name: 'en',
messages: english,
};
locale = 'en';
translationDictionary = en;
}
const i18n = setupI18n({
locale,
translationDictionary,
});
return {
locale,
i18n,
};
}

@ -1,5 +1,5 @@
import { isString } from 'lodash';
import type { LocalizerDictionary } from '../types/Localizer';
import type { SetupI18nReturnType } from '../types/Localizer';
import { LOCALE_DEFAULTS } from '../localization/constants';
/**
@ -22,7 +22,7 @@ export const createTemplate = (
saveDebugLog: (_event: any, additionalInfo?: string) => void;
showWindow: () => void;
},
messages: LocalizerDictionary
i18n: SetupI18nReturnType
) => {
if (!isString(options.platform)) {
throw new TypeError('`options.platform` must be a string');
@ -33,81 +33,81 @@ export const createTemplate = (
const template = [
{
label: withAcceleratorPrefix(messages.file),
label: withAcceleratorPrefix(i18n('file')),
submenu: [
{
type: 'separator',
},
{
role: 'quit',
label: messages.quit,
label: i18n('quit'),
},
],
},
{
label: withAcceleratorPrefix(messages.edit),
label: withAcceleratorPrefix(i18n('edit')),
submenu: [
{
role: 'undo',
label: messages.undo,
label: i18n('undo'),
},
{
role: 'redo',
label: messages.redo,
label: i18n('redo'),
},
{
type: 'separator',
},
{
role: 'cut',
label: messages.cut,
label: i18n('cut'),
},
{
role: 'copy',
label: messages.copy,
label: i18n('copy'),
},
{
role: 'paste',
label: messages.paste,
label: i18n('paste'),
},
{
role: 'delete',
label: messages.delete,
label: i18n('delete'),
},
{
role: 'selectall',
label: messages.selectAll,
label: i18n('selectAll'),
},
],
},
{
label: withAcceleratorPrefix(messages.view),
label: withAcceleratorPrefix(i18n('view')),
submenu: [
{
role: 'resetzoom',
label: messages.actualSize,
label: i18n('actualSize'),
},
{
accelerator: platform === 'darwin' ? 'Command+=' : 'Control+Plus',
role: 'zoomin',
label: messages.appearanceZoomIn,
label: i18n('appearanceZoomIn'),
},
{
role: 'zoomout',
label: messages.appearanceZoomOut,
label: i18n('appearanceZoomOut'),
},
{
type: 'separator',
},
{
role: 'togglefullscreen',
label: messages.fullScreenToggle,
label: i18n('fullScreenToggle'),
},
{
type: 'separator',
},
{
label: messages.debugLog,
label: i18n('debugLog'),
click: () => {
saveDebugLog('save-debug-log');
},
@ -117,37 +117,37 @@ export const createTemplate = (
},
{
role: 'toggledevtools',
label: messages.developerToolsToggle,
label: i18n('developerToolsToggle'),
},
],
},
{
label: withAcceleratorPrefix(messages.window),
label: withAcceleratorPrefix(i18n('window')),
role: 'window',
submenu: [
{
role: 'minimize',
label: messages.minimize,
label: i18n('minimize'),
},
],
},
{
label: withAcceleratorPrefix(messages.sessionHelp),
label: withAcceleratorPrefix(i18n('sessionHelp')),
role: 'help',
submenu: [
{
label: messages.updateReleaseNotes,
label: i18n('updateReleaseNotes'),
click: openReleaseNotes,
},
{
label: messages.supportGoTo,
label: i18n('supportGoTo'),
click: openSupportPage,
},
{
type: 'separator',
},
{
label: messages.about,
label: i18n('about'),
click: showAbout,
},
],
@ -155,7 +155,7 @@ export const createTemplate = (
];
if (platform === 'darwin') {
return updateForMac(template, messages, {
return updateForMac(template, i18n, {
showAbout,
showWindow,
});
@ -166,7 +166,7 @@ export const createTemplate = (
function updateForMac(
template: any,
messages: LocalizerDictionary,
i18n: SetupI18nReturnType,
options: { showAbout: () => void; showWindow: () => void }
) {
const { showAbout, showWindow } = options;
@ -183,7 +183,7 @@ function updateForMac(
label: LOCALE_DEFAULTS.app_name,
submenu: [
{
label: messages.about,
label: i18n('about'),
click: showAbout,
},
{
@ -193,22 +193,22 @@ function updateForMac(
type: 'separator',
},
{
label: messages.hide,
label: i18n('hide'),
role: 'hide',
},
{
label: messages.hideOthers,
label: i18n('hideOthers'),
role: 'hideothers',
},
{
label: messages.showAll,
label: i18n('showAll'),
role: 'unhide',
},
{
type: 'separator',
},
{
label: messages.quit,
label: i18n('quit'),
role: 'quit',
},
],
@ -219,21 +219,21 @@ function updateForMac(
// eslint-disable-next-line no-param-reassign
template[windowMenuTemplateIndex].submenu = [
{
label: messages.closeWindow,
label: i18n('closeWindow'),
accelerator: 'CmdOrCtrl+W',
role: 'close',
},
{
label: messages.minimize,
label: i18n('minimize'),
accelerator: 'CmdOrCtrl+M',
role: 'minimize',
},
{
label: messages.appearanceZoom,
label: i18n('appearanceZoom'),
role: 'zoom',
},
{
label: messages.show,
label: i18n('show'),
click: showWindow,
},
];

@ -1,7 +1,8 @@
import { BrowserWindow, Menu } from 'electron';
import { type BrowserWindow, Menu } from 'electron';
import { sync as osLocaleSync } from 'os-locale';
import type { SetupI18nReturnType } from '../types/Localizer';
export const setup = (browserWindow: BrowserWindow, messages: any) => {
export const setup = (browserWindow: BrowserWindow, i18n: SetupI18nReturnType) => {
const { session } = browserWindow.webContents;
const userLocale = process.env.LANGUAGE
? process.env.LANGUAGE
@ -36,7 +37,7 @@ export const setup = (browserWindow: BrowserWindow, messages: any) => {
);
} else {
template.push({
label: messages.noSuggestions,
label: i18n('noSuggestions'),
enabled: false,
});
}
@ -45,30 +46,30 @@ export const setup = (browserWindow: BrowserWindow, messages: any) => {
if (params.isEditable) {
if (editFlags.canUndo) {
template.push({ label: messages.undo, role: 'undo' });
template.push({ label: i18n('undo'), role: 'undo' });
}
// This is only ever `true` if undo was triggered via the context menu
// (not ctrl/cmd+z)
if (editFlags.canRedo) {
template.push({ label: messages.redo, role: 'redo' });
template.push({ label: i18n('redo'), role: 'redo' });
}
if (editFlags.canUndo || editFlags.canRedo) {
template.push({ type: 'separator' });
}
if (editFlags.canCut) {
template.push({ label: messages.cut, role: 'cut' });
template.push({ label: i18n('cut'), role: 'cut' });
}
}
if (editFlags.canPaste) {
template.push({ label: messages.paste, role: 'paste' });
template.push({ label: i18n('paste'), role: 'paste' });
}
// Only enable select all in editors because select all in non-editors
// results in all the UI being selected
if (editFlags.canSelectAll && params.isEditable) {
template.push({
label: messages.selectAll,
label: i18n('selectAll'),
role: 'selectall',
});
}

@ -15,6 +15,7 @@ import {
intersection,
isArray,
isEmpty,
isFunction,
isNumber,
isObject,
isString,
@ -47,7 +48,7 @@ import {
OPEN_GROUP_ROOMS_V2_TABLE,
toSqliteBoolean,
} from './database_utility';
import type { LocalizerDictionary } from '../types/Localizer'; // checked - only node
import type { SetupI18nReturnType } from '../types/Localizer'; // checked - only node
import { StorageItem } from './storage_item'; // checked - only node
import {
@ -139,12 +140,12 @@ function showFailedToStart() {
async function initializeSql({
configDir,
key,
messages,
i18n,
passwordAttempt,
}: {
configDir: string;
key: string;
messages: LocalizerDictionary;
i18n: SetupI18nReturnType;
passwordAttempt: boolean;
}) {
console.info('initializeSql sqlnode');
@ -158,8 +159,8 @@ async function initializeSql({
if (!isString(key)) {
throw new Error('initialize: key is required!');
}
if (!isObject(messages)) {
throw new Error('initialize: message is required!');
if (!isFunction(i18n)) {
throw new Error('initialize: i18n is required!');
}
_initializePaths(configDir);
@ -209,10 +210,10 @@ async function initializeSql({
}
console.log('Database startup error:', error.stack);
const button = await dialog.showMessageBox({
buttons: [messages.errorCopyAndQuit, messages.clearDataAll],
buttons: [i18n('errorCopyAndQuit'), i18n('clearDataAll')],
defaultId: 0,
detail: redactAll(error.stack),
message: messages.errorDatabase,
message: i18n('errorDatabase'),
noLink: true,
type: 'error',
});
@ -249,6 +250,7 @@ function removeDB(configDir = null) {
// Password hash
const PASS_HASH_ID = 'passHash';
function getPasswordHash() {
const item = getItemById(PASS_HASH_ID);
return item && item.value;
@ -304,15 +306,18 @@ function updateGuardNodes(nodes: Array<string>) {
function createOrUpdateItem(data: StorageItem, instance?: BetterSqlite3.Database) {
createOrUpdate(ITEMS_TABLE, data, instance);
}
function getItemById(id: string, instance?: BetterSqlite3.Database) {
return getById(ITEMS_TABLE, id, instance);
}
function getAllItems() {
const rows = assertGlobalInstance()
.prepare(`SELECT json FROM ${ITEMS_TABLE} ORDER BY id ASC;`)
.all();
return map(rows, row => jsonToObject(row.json));
}
function removeItemById(id: string) {
removeById(ITEMS_TABLE, id);
}
@ -1858,9 +1863,11 @@ function resetAttachmentDownloadPending() {
.prepare(`UPDATE ${ATTACHMENT_DOWNLOADS_TABLE} SET pending = 0 WHERE pending != 0;`)
.run();
}
function removeAttachmentDownloadJob(id: string) {
removeById(ATTACHMENT_DOWNLOADS_TABLE, id);
}
function removeAllAttachmentDownloadJobs() {
assertGlobalInstance().exec(`DELETE FROM ${ATTACHMENT_DOWNLOADS_TABLE};`);
}

@ -1,7 +1,7 @@
import path from 'path';
import { app, BrowserWindow, Menu, Tray } from 'electron';
import { LocalizerDictionary } from '../types/Localizer';
import { app, type BrowserWindow, Menu, Tray } from 'electron';
import type { SetupI18nReturnType } from '../types/Localizer';
import { getAppRootPath } from './getRootPath';
import { LOCALE_DEFAULTS } from '../localization/constants';
@ -11,7 +11,7 @@ let trayAny: any;
export function createTrayIcon(
getMainWindow: () => BrowserWindow | null,
messages: LocalizerDictionary
i18n: SetupI18nReturnType
) {
// keep the duplicated part to allow for search and find
const iconFile = process.platform === 'darwin' ? 'session_icon_16.png' : 'session_icon_32.png';
@ -65,12 +65,12 @@ export function createTrayIcon(
trayContextMenu = Menu.buildFromTemplate([
{
id: 'toggleWindowVisibility',
label: messages[mainWindow?.isVisible() ? 'hide' : 'show'],
label: mainWindow?.isVisible() ? i18n('hide') : i18n('show'),
click: trayAny.toggleWindowVisibility,
},
{
id: 'quit',
label: messages.quit,
label: i18n('quit'),
click: app.quit.bind(app),
},
]);

@ -89,12 +89,14 @@ async function doSearch(query: string): Promise<SearchResultsPayloadType> {
messages: filteredMessages,
};
}
export function clearSearch(): ClearSearchActionType {
return {
type: 'SEARCH_CLEAR',
payload: null,
};
}
export function updateSearchTerm(query: string): UpdateSearchTermActionType {
return {
type: 'SEARCH_UPDATE',

@ -5,10 +5,9 @@ import { ConfigDumpData } from '../../../data/configDump/configDump';
import { Data } from '../../../data/data';
import { OpenGroupData } from '../../../data/opengroups';
import { load } from '../../../node/locale';
import { loadLocalizedDictionary } from '../../../node/locale';
import * as libsessionWorker from '../../../webworker/workers/browser/libsession_worker_interface';
import * as utilWorker from '../../../webworker/workers/browser/util_worker_interface';
import { setupI18n } from '../../../util/i18n/i18n';
const globalAny: any = global;
@ -140,6 +139,6 @@ export async function expectAsyncToThrow(toAwait: () => Promise<any>, errorMessa
/** You must call stubWindowLog() before using */
export const stubI18n = () => {
const locale = load({ appLocale: 'en', logger: window.log });
stubWindow('i18n', setupI18n({ locale: 'en', translationDictionary: locale.messages }));
const { i18n } = loadLocalizedDictionary({ appLocale: 'en', logger: window.log });
stubWindow('i18n', i18n);
};

@ -1,4 +1,5 @@
import { BrowserWindow, dialog } from 'electron';
import type { SetupI18nReturnType } from '../types/Localizer';
export type MessagesType = {
[key: string]: string;
@ -17,16 +18,16 @@ export type LoggerType = {
export async function showDownloadUpdateDialog(
mainWindow: BrowserWindow,
messages: MessagesType
i18n: SetupI18nReturnType
): Promise<boolean> {
const DOWNLOAD_BUTTON = 0;
const LATER_BUTTON = 1;
const options = {
type: 'info' as const,
buttons: [messages.download, messages.later],
title: messages.updateSession,
message: messages.updateNewVersionDescription,
detail: messages.updateNewVersionDescription,
buttons: [i18n('download'), i18n('later')],
title: i18n('updateSession'),
message: i18n('updateNewVersionDescription'),
detail: i18n('updateNewVersionDescription'),
defaultId: LATER_BUTTON,
cancelId: DOWNLOAD_BUTTON,
};
@ -38,16 +39,16 @@ export async function showDownloadUpdateDialog(
export async function showUpdateDialog(
mainWindow: BrowserWindow,
messages: MessagesType
i18n: SetupI18nReturnType
): Promise<boolean> {
const RESTART_BUTTON = 0;
const LATER_BUTTON = 1;
const options = {
type: 'info' as const,
buttons: [messages.restart, messages.later],
title: messages.updateSession,
message: messages.updateDownloaded,
detail: messages.updateDownloaded,
buttons: [i18n('restart'), i18n('later')],
title: i18n('updateSession'),
message: i18n('updateDownloaded'),
detail: i18n('updateDownloaded'),
defaultId: LATER_BUTTON,
cancelId: RESTART_BUTTON,
};
@ -56,12 +57,12 @@ export async function showUpdateDialog(
return ret.response === RESTART_BUTTON;
}
export async function showCannotUpdateDialog(mainWindow: BrowserWindow, messages: MessagesType) {
export async function showCannotUpdateDialog(mainWindow: BrowserWindow, i18n: SetupI18nReturnType) {
const options = {
type: 'error' as const,
buttons: [messages.ok],
title: messages.updateError,
message: messages.updateErrorDescription,
buttons: [i18n('okay')],
title: i18n('updateError'),
message: i18n('updateErrorDescription'),
};
await dialog.showMessageBox(mainWindow, options);
}

@ -1,7 +1,8 @@
import { BrowserWindow } from 'electron';
import type { BrowserWindow } from 'electron';
import { start as startUpdater, stop as stopUpdater } from './updater';
import { LoggerType, MessagesType } from './common';
import { UserConfig } from '../node/config/user_config';
import type { LoggerType } from './common';
import type { UserConfig } from '../node/config/user_config';
import type { SetupI18nReturnType } from '../types/Localizer';
let initialized = false;
let localUserConfig: UserConfig;
@ -9,7 +10,7 @@ let localUserConfig: UserConfig;
export async function start(
getMainWindow: () => BrowserWindow | null,
userConfig: UserConfig,
messages: MessagesType,
i18n: SetupI18nReturnType,
logger?: LoggerType | null
) {
if (initialized) {
@ -20,8 +21,8 @@ export async function start(
throw new Error('updater/start: userConfig is needed!');
}
if (!messages) {
throw new Error('updater/start: Must provide messages!');
if (!i18n) {
throw new Error('updater/start: Must provide i18n!');
}
if (!logger) {
throw new Error('updater/start: Must provide logger!');
@ -35,7 +36,7 @@ export async function start(
return;
}
await startUpdater(getMainWindow, messages, logger);
await startUpdater(getMainWindow, i18n, logger);
}
export function stop() {

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
/* eslint-disable no-console */
import * as path from 'path';
import { app, BrowserWindow } from 'electron';
import { autoUpdater, UpdateInfo } from 'electron-updater';
import { app, type BrowserWindow } from 'electron';
import { autoUpdater, type UpdateInfo } from 'electron-updater';
import * as fs from 'fs-extra';
import { gt as isVersionGreaterThan, parse as parseVersion } from 'semver';
@ -11,22 +11,23 @@ import { windowMarkShouldQuit } from '../node/window_state';
import { getLastestRelease } from '../node/latest_desktop_release';
import {
getPrintableError,
LoggerType,
MessagesType,
type LoggerType,
showCannotUpdateDialog,
showDownloadUpdateDialog,
showUpdateDialog,
} from './common';
import type { SetupI18nReturnType } from '../types/Localizer';
let isUpdating = false;
let downloadIgnored = false;
let interval: NodeJS.Timeout | undefined;
let stopped = false;
// eslint:disable: no-console
export async function start(
getMainWindow: () => BrowserWindow | null,
messages: MessagesType,
i18n: SetupI18nReturnType,
logger: LoggerType
) {
if (interval) {
@ -43,7 +44,7 @@ export async function start(
interval = global.setInterval(
async () => {
try {
await checkForUpdates(getMainWindow, messages, logger);
await checkForUpdates(getMainWindow, i18n, logger);
} catch (error) {
logger.error('auto-update: error:', getPrintableError(error));
}
@ -55,7 +56,7 @@ export async function start(
global.setTimeout(
async () => {
try {
await checkForUpdates(getMainWindow, messages, logger);
await checkForUpdates(getMainWindow, i18n, logger);
} catch (error) {
logger.error('auto-update: error:', getPrintableError(error));
}
@ -74,7 +75,7 @@ export function stop() {
async function checkForUpdates(
getMainWindow: () => BrowserWindow | null,
messages: MessagesType,
i18n: SetupI18nReturnType,
logger: LoggerType
) {
logger.info('[updater] checkForUpdates');
@ -141,7 +142,7 @@ async function checkForUpdates(
return;
}
logger.info('[updater] showing download dialog...');
const shouldDownload = await showDownloadUpdateDialog(mainWindow, messages);
const shouldDownload = await showDownloadUpdateDialog(mainWindow, i18n);
logger.info('[updater] shouldDownload:', shouldDownload);
if (!shouldDownload) {
@ -157,7 +158,7 @@ async function checkForUpdates(
console.error('cannot showDownloadUpdateDialog, mainWindow is unset');
return;
}
await showCannotUpdateDialog(mainWindow, messages);
await showCannotUpdateDialog(mainWindow, i18n);
throw error;
}
const window = getMainWindow();
@ -167,7 +168,7 @@ async function checkForUpdates(
}
// Update downloaded successfully, we should ask the user to update
logger.info('[updater] showing update dialog...');
const shouldUpdate = await showUpdateDialog(window, messages);
const shouldUpdate = await showUpdateDialog(window, i18n);
if (!shouldUpdate) {
return;
}

@ -9,9 +9,13 @@ let initialLocale: Locale | undefined;
*
*/
export function i18nLog(message: string) {
// eslint:disable: no-console
// eslint-disable-next-line no-console
(window?.log?.info ?? console.log)(`i18n: ${message}`);
if (typeof window !== 'undefined') {
// eslint-disable-next-line no-console
(window?.log?.error ?? console.log)(`i18n: ${message}`);
} else {
// eslint-disable-next-line no-console
console.log(`i18n: ${message}`);
}
}
export type Locale = keyof typeof timeLocaleMap;
@ -52,3 +56,7 @@ export function getBrowserLocale() {
export function setInitialLocale(locale: Locale) {
initialLocale = locale;
}
export function isLocaleSet() {
return initialLocale !== undefined;
}

Loading…
Cancel
Save