mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(i18n): review translations and fix i18n library misuse (#2750)
This commit is contained in:
1 parent
78f783f0c4
commit
8cb097f954
575 files changed
+192671
-160540
No files matched your search
@@ -42,6 +42,8 @@ let trayActionBridgeWebContentsId: number | null = null;
|
||||
let trayForcedExitTimer: NodeJS.Timeout | null = null;
|
||||
let pendingTrayActions: Array<TrayActionPayload> = [];
|
||||
|
||||
const BIDI_FIRST_STRONG_ISOLATE = '\u2068';
|
||||
const BIDI_POP_DIRECTIONAL_ISOLATE = '\u2069';
|
||||
const MAX_PENDING_TRAY_ACTIONS = 16;
|
||||
const LINUX_TRAY_MENU_ACTION_DELAY_MS = 25;
|
||||
const TRAY_FORCED_EXIT_DELAY_MS = 5000;
|
||||
@@ -296,6 +298,10 @@ function runTrayMenuAction(action: () => void): void {
|
||||
timeout.unref?.();
|
||||
}
|
||||
|
||||
function isolateUserText(value: string): string {
|
||||
return `${BIDI_FIRST_STRONG_ISOLATE}${value}${BIDI_POP_DIRECTIONAL_ISOLATE}`;
|
||||
}
|
||||
|
||||
function buildTrayMenu(): Menu {
|
||||
const mainWindow = controller?.getMainWindow() ?? null;
|
||||
const visible = Boolean(
|
||||
@@ -368,7 +374,7 @@ function buildTrayMenu(): Menu {
|
||||
});
|
||||
menuTemplate.push({
|
||||
label: trayState.voiceChannelLabel
|
||||
? t('desktop.tray.disconnectFrom', {channel: trayState.voiceChannelLabel})
|
||||
? t('desktop.tray.disconnectFrom', {channel: isolateUserText(trayState.voiceChannelLabel)})
|
||||
: t('desktop.tray.disconnectVoice'),
|
||||
click: () => runTrayMenuAction(() => dispatchTrayAction({action: 'disconnect-voice'})),
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import {createChildLogger} from '@electron/common/Logger';
|
||||
import {TASK_ARG_PREFIX} from '@electron/main/JumpList';
|
||||
import {getStableLinuxLaunchPath} from '@electron/main/LinuxLaunchPath';
|
||||
import {isFlatpakRuntime} from '@electron/main/LinuxSandbox';
|
||||
import {t} from '@electron/main/MainI18n';
|
||||
import {getNativeLocale, t} from '@electron/main/MainI18n';
|
||||
import {app} from 'electron';
|
||||
|
||||
const logger = createChildLogger('LinuxDesktopEntry');
|
||||
@@ -18,6 +18,8 @@ const APP_NAME = DESKTOP_APP_NAME;
|
||||
const APP_ID = LINUX_DESKTOP_ENTRY_ID;
|
||||
export const WM_CLASS = APP_ID;
|
||||
const DESKTOP_FILE_BASENAME = `${APP_ID}.desktop`;
|
||||
const GENERIC_NAME_DEFAULT = 'Instant Messenger';
|
||||
const COMMENT_DEFAULT = 'Instant messaging and VoIP';
|
||||
const GENERATED_MARKER = '# X-Generated-By=fluxer-desktop';
|
||||
const DESKTOP_ACTIONS = [
|
||||
{
|
||||
@@ -123,6 +125,15 @@ function buildDesktopActionEntries(execPath: string): Array<string> {
|
||||
return entries;
|
||||
}
|
||||
|
||||
function buildLocalizedEntryLines(key: string, defaultValue: string, localizedValue: string): Array<string> {
|
||||
const lines = [`${key}=${escapeDesktopValue(defaultValue)}`];
|
||||
if (localizedValue !== defaultValue) {
|
||||
const entryLocale = getNativeLocale().replace(/-/g, '_');
|
||||
lines.push(`${key}[${entryLocale}]=${escapeDesktopValue(localizedValue)}`);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function buildDesktopFileContents(execPath: string, hidden: boolean): string {
|
||||
const execLine = `${quoteExecArg(execPath)} %U`;
|
||||
return [
|
||||
@@ -131,8 +142,8 @@ function buildDesktopFileContents(execPath: string, hidden: boolean): string {
|
||||
'Type=Application',
|
||||
'Version=1.5',
|
||||
`Name=${escapeDesktopValue(APP_NAME)}`,
|
||||
'GenericName=Instant Messenger',
|
||||
'Comment=Instant messaging and VoIP',
|
||||
...buildLocalizedEntryLines('GenericName', GENERIC_NAME_DEFAULT, t('desktop.linuxEntry.genericName')),
|
||||
...buildLocalizedEntryLines('Comment', COMMENT_DEFAULT, t('desktop.linuxEntry.comment')),
|
||||
`Exec=${escapeDesktopValue(execLine)}`,
|
||||
`TryExec=${escapeDesktopValue(execPath)}`,
|
||||
`Icon=${escapeDesktopValue(resolveIconHint())}`,
|
||||
|
||||
@@ -13,56 +13,80 @@ const FALLBACK_STRINGS: Readonly<Record<string, string>> = Object.freeze({
|
||||
'desktop.appMenu.preferencesPlain': 'Preferences',
|
||||
'desktop.appMenu.hide': 'Hide {appName}',
|
||||
'desktop.appMenu.quit': 'Quit {appName}',
|
||||
'desktop.appMenu.exit': 'Exit',
|
||||
'desktop.appMenu.file': 'File',
|
||||
'desktop.appMenu.edit': 'Edit',
|
||||
'desktop.appMenu.undo': 'Undo',
|
||||
'desktop.appMenu.redo': 'Redo',
|
||||
'desktop.appMenu.cut': 'Cut',
|
||||
'desktop.appMenu.copy': 'Copy',
|
||||
'desktop.appMenu.paste': 'Paste',
|
||||
'desktop.appMenu.pasteAndMatchStyle': 'Paste and match style',
|
||||
'desktop.appMenu.delete': 'Delete',
|
||||
'desktop.appMenu.selectAll': 'Select all',
|
||||
'desktop.appMenu.speech': 'Speech',
|
||||
'desktop.appMenu.startSpeaking': 'Start speaking',
|
||||
'desktop.appMenu.stopSpeaking': 'Stop speaking',
|
||||
'desktop.appMenu.view': 'View',
|
||||
'desktop.appMenu.toggleDeveloperTools': 'Toggle Developer Tools',
|
||||
'desktop.appMenu.actualSize': 'Actual Size',
|
||||
'desktop.appMenu.zoomIn': 'Zoom In',
|
||||
'desktop.appMenu.zoomOut': 'Zoom Out',
|
||||
'desktop.appMenu.reload': 'Reload',
|
||||
'desktop.appMenu.forceReload': 'Force reload',
|
||||
'desktop.appMenu.toggleDeveloperTools': 'Toggle developer tools',
|
||||
'desktop.appMenu.actualSize': 'Actual size',
|
||||
'desktop.appMenu.zoomIn': 'Zoom in',
|
||||
'desktop.appMenu.zoomOut': 'Zoom out',
|
||||
'desktop.appMenu.toggleFullScreen': 'Toggle full screen',
|
||||
'desktop.appMenu.window': 'Window',
|
||||
'desktop.appMenu.minimize': 'Minimize',
|
||||
'desktop.appMenu.zoomWindow': 'Zoom',
|
||||
'desktop.appMenu.close': 'Close',
|
||||
'desktop.appMenu.help': 'Help',
|
||||
'desktop.appMenu.website': 'Website',
|
||||
'desktop.appMenu.github': 'GitHub',
|
||||
'desktop.appMenu.reportIssue': 'Report Issue',
|
||||
'desktop.appMenu.reportIssue': 'Report issue',
|
||||
'desktop.appMenu.troubleshooting': 'Troubleshooting',
|
||||
'desktop.troubleshooting.disableHardwareAccelerationAndRestart': 'Disable Hardware Acceleration and Restart',
|
||||
'desktop.troubleshooting.enableHardwareAccelerationAndRestart': 'Enable Hardware Acceleration and Restart',
|
||||
'desktop.troubleshooting.disableHardwareAccelerationAndRestart': 'Disable hardware acceleration and restart',
|
||||
'desktop.troubleshooting.enableHardwareAccelerationAndRestart': 'Enable hardware acceleration and restart',
|
||||
'desktop.troubleshooting.reload': 'Reload',
|
||||
'desktop.troubleshooting.resetAppDataAndRestart': 'Reset App Data and Restart',
|
||||
'desktop.troubleshooting.resetTitle': 'Reset App Data',
|
||||
'desktop.troubleshooting.resetMessage': 'Are you sure you want to reset Fluxer\u2019s app data?',
|
||||
'desktop.troubleshooting.resetAppDataAndRestart': 'Reset app data and restart',
|
||||
'desktop.troubleshooting.resetTitle': 'Reset app data',
|
||||
'desktop.troubleshooting.resetMessage': "Are you sure you want to reset {appName}'s app data?",
|
||||
'desktop.troubleshooting.resetDetail':
|
||||
'This will sign you out, clear cached files and stored sessions, and restart the desktop app. Your account, communities, and messages on the server are not affected.',
|
||||
'desktop.troubleshooting.resetConfirm': 'Reset and Restart',
|
||||
'This will sign you out, clear cached files and stored sessions, and restart the desktop app. Your account, communities, and messages in {appName} are not affected.',
|
||||
'desktop.troubleshooting.resetConfirm': 'Reset and restart',
|
||||
'desktop.troubleshooting.resetCancel': 'Cancel',
|
||||
'desktop.tray.show': 'Show {appName}',
|
||||
'desktop.tray.hide': 'Hide {appName}',
|
||||
'desktop.tray.openSettings': 'Open Settings',
|
||||
'desktop.tray.openSettings': 'Open settings',
|
||||
'desktop.tray.status': 'Status',
|
||||
'desktop.tray.statusOnline': 'Online',
|
||||
'desktop.tray.statusIdle': 'Idle',
|
||||
'desktop.tray.statusDnd': 'Do Not Disturb',
|
||||
'desktop.tray.statusDnd': 'Do not disturb',
|
||||
'desktop.tray.statusInvisible': 'Invisible',
|
||||
'desktop.tray.muteMic': 'Mute Microphone',
|
||||
'desktop.tray.unmuteMic': 'Unmute Microphone',
|
||||
'desktop.tray.muteMic': 'Mute microphone',
|
||||
'desktop.tray.unmuteMic': 'Unmute microphone',
|
||||
'desktop.tray.deafen': 'Deafen',
|
||||
'desktop.tray.undeafen': 'Undeafen',
|
||||
'desktop.tray.disconnectFrom': 'Disconnect from {channel}',
|
||||
'desktop.tray.disconnectVoice': 'Disconnect from Voice',
|
||||
'desktop.tray.checkForUpdates': 'Check for Updates',
|
||||
'desktop.tray.copyBuildInfo': 'Copy Build Info',
|
||||
'desktop.tray.disconnectVoice': 'Disconnect from voice',
|
||||
'desktop.tray.checkForUpdates': 'Check for updates',
|
||||
'desktop.tray.copyBuildInfo': 'Copy build info',
|
||||
'desktop.tray.restart': 'Restart {appName}',
|
||||
'desktop.tray.quit': 'Quit {appName}',
|
||||
'desktop.autostart.portalReason': 'Start {appName} automatically when you sign in.',
|
||||
'desktop.jumpList.tasks': 'Tasks',
|
||||
'desktop.jumpList.openSettings': 'Open Settings',
|
||||
'desktop.jumpList.openSettings': 'Open settings',
|
||||
'desktop.jumpList.openSettingsDescription': 'Open {appName} settings',
|
||||
'desktop.jumpList.newDirectMessage': 'New Direct Message',
|
||||
'desktop.jumpList.newDirectMessage': 'New direct message',
|
||||
'desktop.jumpList.newDirectMessageDescription': 'Compose a new direct message',
|
||||
'desktop.jumpList.recent': 'Recent',
|
||||
'desktop.notifications.open': 'Open',
|
||||
'desktop.themes.addLocalFiles': 'Add local files',
|
||||
'desktop.themes.importFolder': 'Import folder',
|
||||
'desktop.badge.unreadMessages': 'Unread messages',
|
||||
'desktop.badge.unreadMessagesCount': 'Unread messages: {count}',
|
||||
'desktop.startup.failedTitle': '{appName} failed to start',
|
||||
'desktop.linuxEntry.genericName': 'Instant Messenger',
|
||||
'desktop.linuxEntry.comment': 'Instant messaging and VoIP',
|
||||
});
|
||||
const STORAGE_FILE_NAME = 'native-strings.json';
|
||||
|
||||
|
||||
@@ -66,30 +66,40 @@ function buildTemplate(): Array<MenuItemConstructorOptions> {
|
||||
},
|
||||
},
|
||||
{type: 'separator'},
|
||||
{role: 'quit'},
|
||||
{
|
||||
role: 'quit',
|
||||
label: t('desktop.appMenu.exit'),
|
||||
},
|
||||
],
|
||||
});
|
||||
template.push({
|
||||
label: t('desktop.appMenu.edit'),
|
||||
submenu: [
|
||||
{role: 'undo'},
|
||||
{role: 'redo'},
|
||||
{role: 'undo', label: t('desktop.appMenu.undo')},
|
||||
{role: 'redo', label: t('desktop.appMenu.redo')},
|
||||
{type: 'separator'},
|
||||
{role: 'cut'},
|
||||
{role: 'copy'},
|
||||
{role: 'paste'},
|
||||
{role: 'cut', label: t('desktop.appMenu.cut')},
|
||||
{role: 'copy', label: t('desktop.appMenu.copy')},
|
||||
{role: 'paste', label: t('desktop.appMenu.paste')},
|
||||
...(isMac
|
||||
? [
|
||||
{role: 'pasteAndMatchStyle' as const},
|
||||
{role: 'delete' as const},
|
||||
{role: 'selectAll' as const},
|
||||
{role: 'pasteAndMatchStyle' as const, label: t('desktop.appMenu.pasteAndMatchStyle')},
|
||||
{role: 'delete' as const, label: t('desktop.appMenu.delete')},
|
||||
{role: 'selectAll' as const, label: t('desktop.appMenu.selectAll')},
|
||||
{type: 'separator' as const},
|
||||
{
|
||||
label: t('desktop.appMenu.speech'),
|
||||
submenu: [{role: 'startSpeaking' as const}, {role: 'stopSpeaking' as const}],
|
||||
submenu: [
|
||||
{role: 'startSpeaking' as const, label: t('desktop.appMenu.startSpeaking')},
|
||||
{role: 'stopSpeaking' as const, label: t('desktop.appMenu.stopSpeaking')},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [{role: 'delete' as const}, {type: 'separator' as const}, {role: 'selectAll' as const}]),
|
||||
: [
|
||||
{role: 'delete' as const, label: t('desktop.appMenu.delete')},
|
||||
{type: 'separator' as const},
|
||||
{role: 'selectAll' as const, label: t('desktop.appMenu.selectAll')},
|
||||
]),
|
||||
],
|
||||
});
|
||||
const zoomInHandler = () => {
|
||||
@@ -101,8 +111,8 @@ function buildTemplate(): Array<MenuItemConstructorOptions> {
|
||||
template.push({
|
||||
label: t('desktop.appMenu.view'),
|
||||
submenu: [
|
||||
{role: 'reload'},
|
||||
{role: 'forceReload'},
|
||||
{role: 'reload', label: t('desktop.appMenu.reload')},
|
||||
{role: 'forceReload', label: t('desktop.appMenu.forceReload')},
|
||||
{
|
||||
label: t('desktop.appMenu.toggleDeveloperTools'),
|
||||
accelerator: isMac ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
@@ -146,12 +156,13 @@ function buildTemplate(): Array<MenuItemConstructorOptions> {
|
||||
},
|
||||
},
|
||||
{type: 'separator'},
|
||||
{role: 'togglefullscreen'},
|
||||
{role: 'togglefullscreen', label: t('desktop.appMenu.toggleFullScreen')},
|
||||
...(isMac
|
||||
? []
|
||||
: [
|
||||
{
|
||||
role: 'togglefullscreen' as const,
|
||||
label: t('desktop.appMenu.toggleFullScreen'),
|
||||
accelerator: 'Alt+Enter',
|
||||
visible: false,
|
||||
},
|
||||
@@ -161,8 +172,8 @@ function buildTemplate(): Array<MenuItemConstructorOptions> {
|
||||
template.push({
|
||||
label: t('desktop.appMenu.window'),
|
||||
submenu: [
|
||||
{role: 'minimize'},
|
||||
{role: 'zoom'},
|
||||
{role: 'minimize', label: t('desktop.appMenu.minimize')},
|
||||
{role: 'zoom', label: t('desktop.appMenu.zoomWindow')},
|
||||
...(isMac
|
||||
? [
|
||||
{type: 'separator' as const},
|
||||
|
||||
@@ -5,6 +5,7 @@ import {DESKTOP_APP_NAME, LINUX_DESKTOP_ENTRY_ID} from '@electron/common/Desktop
|
||||
import {createChildLogger} from '@electron/common/Logger';
|
||||
import type {NotificationOptions} from '@electron/common/Types';
|
||||
import {getNativeNotificationsMode} from '@electron/main/LaunchOptions';
|
||||
import {t} from '@electron/main/MainI18n';
|
||||
import {resolveNotificationIcon} from '@electron/main/NotificationIcon';
|
||||
import {shouldPlayNotificationSound} from '@electron/main/NotificationState';
|
||||
import {requirePrivilegedRendererDocumentSender} from '@electron/main/PrivilegedRendererDocuments';
|
||||
@@ -291,7 +292,7 @@ async function showLinuxNativeNotification(
|
||||
actionIcons: false,
|
||||
...(imageData ? {imageData} : {}),
|
||||
},
|
||||
actions: [{key: 'default', label: 'Open'}],
|
||||
actions: [{key: 'default', label: t('desktop.notifications.open')}],
|
||||
};
|
||||
try {
|
||||
const nativeId = await client.notify(payload);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@electron/common/DesktopConfig';
|
||||
import {createChildLogger} from '@electron/common/Logger';
|
||||
import type {ThemeDirectoryCssFile, ThemeLocalFileReadResult, ThemeLocalFileReference} from '@electron/common/Types';
|
||||
import {t} from '@electron/main/MainI18n';
|
||||
import {BrowserWindow, dialog, ipcMain} from 'electron';
|
||||
|
||||
const logger = createChildLogger('ThemeLocalFiles');
|
||||
@@ -97,7 +98,7 @@ export function registerThemeLocalFileHandlers(getMainWindow: () => BrowserWindo
|
||||
ipcMain.handle('theme-local-files-pick', async (event): Promise<Array<ThemeLocalFileReference>> => {
|
||||
const parent = BrowserWindow.fromWebContents(event.sender) ?? getMainWindow();
|
||||
const result = await showThemeOpenDialog(parent, {
|
||||
title: 'Add theme files',
|
||||
title: t('desktop.themes.addLocalFiles'),
|
||||
properties: ['openFile', 'multiSelections'],
|
||||
});
|
||||
if (result.canceled) {
|
||||
@@ -159,7 +160,7 @@ export function registerThemeLocalFileHandlers(getMainWindow: () => BrowserWindo
|
||||
ipcMain.handle('theme-directory-import', async (event): Promise<Array<ThemeDirectoryCssFile>> => {
|
||||
const parent = BrowserWindow.fromWebContents(event.sender) ?? getMainWindow();
|
||||
const result = await showThemeOpenDialog(parent, {
|
||||
title: 'Import theme directory',
|
||||
title: t('desktop.themes.importFolder'),
|
||||
properties: ['openDirectory'],
|
||||
});
|
||||
if (result.canceled || result.filePaths.length === 0) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import {getNativeLocale, t} from '@electron/main/MainI18n';
|
||||
import type {BrowserWindow, NativeImage} from 'electron';
|
||||
import {nativeImage} from 'electron';
|
||||
|
||||
@@ -9,6 +10,7 @@ const badgeIcons: Array<NativeImage | null> = [];
|
||||
|
||||
let hasInit = false;
|
||||
let lastIndex: number | null = null;
|
||||
let lastDescription: string | null = null;
|
||||
let lastCount: number | null = null;
|
||||
|
||||
function isSupported(): boolean {
|
||||
@@ -32,6 +34,14 @@ function ensureInitialized(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function formatBadgeCount(count: number): string {
|
||||
try {
|
||||
return count.toLocaleString(getNativeLocale());
|
||||
} catch {
|
||||
return String(count);
|
||||
}
|
||||
}
|
||||
|
||||
function getOverlayIconData(count: number): {
|
||||
index: number | null;
|
||||
description: string;
|
||||
@@ -39,7 +49,7 @@ function getOverlayIconData(count: number): {
|
||||
if (count === -1) {
|
||||
return {
|
||||
index: 10,
|
||||
description: 'Unread messages',
|
||||
description: t('desktop.badge.unreadMessages'),
|
||||
};
|
||||
}
|
||||
if (count === 0) {
|
||||
@@ -51,7 +61,7 @@ function getOverlayIconData(count: number): {
|
||||
const index = Math.max(1, Math.min(count, 10)) - 1;
|
||||
return {
|
||||
index,
|
||||
description: `${index} notifications`,
|
||||
description: t('desktop.badge.unreadMessagesCount', {count: formatBadgeCount(count)}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,7 +73,7 @@ function applyOverlay(win: BrowserWindow | null, count: number, force: boolean):
|
||||
return;
|
||||
}
|
||||
const {index, description} = getOverlayIconData(count);
|
||||
if (force || lastIndex !== index) {
|
||||
if (force || lastIndex !== index || lastDescription !== description) {
|
||||
if (index == null) {
|
||||
win.setOverlayIcon(null, description);
|
||||
} else {
|
||||
@@ -71,6 +81,7 @@ function applyOverlay(win: BrowserWindow | null, count: number, force: boolean):
|
||||
win.setOverlayIcon(icon ?? null, description);
|
||||
}
|
||||
lastIndex = index;
|
||||
lastDescription = description;
|
||||
}
|
||||
lastCount = count;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ import {cleanupIpcHandlers, registerIpcHandlers} from '@electron/main/IpcHandler
|
||||
import {initializeJumpList} from '@electron/main/JumpList';
|
||||
import {describeLaunchDiagnosticOptions, shouldStartHiddenAtLogin} from '@electron/main/LaunchOptions';
|
||||
import {cleanupVirtmic, registerVirtmicHandlers} from '@electron/main/LinuxAudioCapture';
|
||||
import {initializeMainI18n} from '@electron/main/MainI18n';
|
||||
import {initializeMainI18n, t} from '@electron/main/MainI18n';
|
||||
import {createApplicationMenu} from '@electron/main/Menu';
|
||||
import {cleanupNativeAudio, registerNativeAudioHandlers} from '@electron/main/NativeAudio';
|
||||
import {
|
||||
@@ -237,6 +237,11 @@ if (launchConfigurationError) {
|
||||
if (hasLegacySquirrelArg) {
|
||||
app.exit(0);
|
||||
}
|
||||
try {
|
||||
runStartupPhase('main-i18n', initializeMainI18n);
|
||||
} catch (error) {
|
||||
log.error('[Init] Failed to initialize native i18n:', error);
|
||||
}
|
||||
try {
|
||||
runStartupPhase('native-module-preflight', runNativeModulePreflight);
|
||||
} catch (error) {
|
||||
@@ -244,7 +249,7 @@ if (launchConfigurationError) {
|
||||
log.error('[NativeModulePreflight] Fatal native module preflight failure:', error);
|
||||
console.error(message);
|
||||
try {
|
||||
dialog.showErrorBox('Fluxer failed to start', message);
|
||||
dialog.showErrorBox(t('desktop.startup.failedTitle'), message);
|
||||
} catch {}
|
||||
app.exit(1);
|
||||
process.exit(1);
|
||||
@@ -317,11 +322,6 @@ if (launchConfigurationError) {
|
||||
} catch (error) {
|
||||
log.error('[DebugInfo] Failed to collect desktop debug info:', error);
|
||||
}
|
||||
try {
|
||||
runStartupPhase('main-i18n', initializeMainI18n);
|
||||
} catch (error) {
|
||||
log.error('[Init] Failed to initialize native i18n:', error);
|
||||
}
|
||||
try {
|
||||
runStartupPhase('deep-links', initializeDeepLinks);
|
||||
} catch (error) {
|
||||
|
||||
Reference in new issue
Block a user