Compare commits
	
		
			75 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					35ad6fbad0 | ||
| 
						 | 
					97f8fe71af | ||
| 
						 | 
					a9d59b3dcd | ||
| 
						 | 
					b7240e1c40 | ||
| 
						 | 
					62aa849657 | ||
| 
						 | 
					c302ebe282 | ||
| 
						 | 
					6404bed519 | ||
| 
						 | 
					8d4d168988 | ||
| 
						 | 
					d4d3805be8 | ||
| 
						 | 
					e853af40c4 | ||
| 
						 | 
					941200cf3b | ||
| 
						 | 
					cf1f659ebf | ||
| 
						 | 
					eb381a87bc | ||
| 
						 | 
					68bc0ae4a0 | ||
| 
						 | 
					178bc7f401 | ||
| 
						 | 
					0f1245b975 | ||
| 
						 | 
					960312a932 | ||
| 
						 | 
					0e00f3bbce | ||
| 
						 | 
					ec205f68a6 | ||
| 
						 | 
					5fe5989710 | ||
| 
						 | 
					69141b5395 | ||
| 
						 | 
					8d66f05924 | ||
| 
						 | 
					e7330dbff8 | ||
| 
						 | 
					67fa9cca8c | ||
| 
						 | 
					a90bf1af08 | ||
| 
						 | 
					cb145acc73 | ||
| 
						 | 
					099e10673c | ||
| 
						 | 
					4b3608fc1e | ||
| 
						 | 
					6128c0e12a | ||
| 
						 | 
					14a1f5d3e1 | ||
| 
						 | 
					9cf26f4890 | ||
| 
						 | 
					397a7381b8 | ||
| 
						 | 
					24b28f9ded | ||
| 
						 | 
					9ceabe02d5 | ||
| 
						 | 
					b207ee57de | ||
| 
						 | 
					cf9d0c8aa2 | ||
| 
						 | 
					e97ab2e6dd | ||
| 
						 | 
					6a7f26d7e8 | ||
| 
						 | 
					b6e11f623a | ||
| 
						 | 
					1c60c335fd | ||
| 
						 | 
					c9249b1724 | ||
| 
						 | 
					9e957ba704 | ||
| 
						 | 
					6c37e30233 | ||
| 
						 | 
					addfe2e414 | ||
| 
						 | 
					bda0dd29df | ||
| 
						 | 
					01926e1234 | ||
| 
						 | 
					9138bbfaf2 | ||
| 
						 | 
					596561b731 | ||
| 
						 | 
					5943c21814 | ||
| 
						 | 
					2456bba5ae | ||
| 
						 | 
					0fff6336c7 | ||
| 
						 | 
					4261874e29 | ||
| 
						 | 
					f976270d33 | ||
| 
						 | 
					4782f1cfd3 | ||
| 
						 | 
					2f70621255 | ||
| 
						 | 
					b6f4e2b83c | ||
| 
						 | 
					25f4c3aba8 | ||
| 
						 | 
					1b23468375 | ||
| 
						 | 
					5d775405d9 | ||
| 
						 | 
					7f13d9162a | ||
| 
						 | 
					e0013c22ff | ||
| 
						 | 
					5c41afdccd | ||
| 
						 | 
					99a3530238 | ||
| 
						 | 
					a5ceffc856 | ||
| 
						 | 
					f55570f2f5 | ||
| 
						 | 
					4f890c0316 | ||
| 
						 | 
					bf651dece0 | ||
| 
						 | 
					1babd8da42 | ||
| 
						 | 
					bfd146f2d9 | ||
| 
						 | 
					3126510245 | ||
| 
						 | 
					d661895545 | ||
| 
						 | 
					ca9ab6168e | ||
| 
						 | 
					bbdf2c6017 | ||
| 
						 | 
					109a9bbd10 | ||
| 
						 | 
					22d7ef5615 | 
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,8 +0,0 @@
 | 
				
			|||||||
---
 | 
					 | 
				
			||||||
<!-- Please Include: -->
 | 
					 | 
				
			||||||
- **Operating System**:
 | 
					 | 
				
			||||||
  - [ ] Windows
 | 
					 | 
				
			||||||
  - [ ] Linux/Ubuntu
 | 
					 | 
				
			||||||
  - [ ] macOS
 | 
					 | 
				
			||||||
- **Clear steps to reproduce the issue**:
 | 
					 | 
				
			||||||
- **Relevant error messages and/or screenshots**:
 | 
					 | 
				
			||||||
							
								
								
									
										25
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					name: Bug report
 | 
				
			||||||
 | 
					about: Create a report to help us improve
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Describe the bug**
 | 
				
			||||||
 | 
					<!-- A clear and concise description of what the bug is. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**To Reproduce**
 | 
				
			||||||
 | 
					<!-- Clear steps to reproduce the issue. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Expected behavior**
 | 
				
			||||||
 | 
					<!-- A clear and concise description of what you expected to happen. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Screenshots**
 | 
				
			||||||
 | 
					<!-- If applicable, add screenshots to help explain your problem. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Desktop (please complete the following information):**
 | 
				
			||||||
 | 
					 - Operating System: 
 | 
				
			||||||
 | 
					 <!-- (Platform and Version) e.g. macOS 10.13.6 / Windows 10 (1803) / Ubuntu 18.04 x64 -->
 | 
				
			||||||
 | 
					 - Zulip Desktop Version:
 | 
				
			||||||
 | 
					<!-- e.g. 5.2.0 --> 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Additional context**
 | 
				
			||||||
 | 
					<!-- Add any other context about the problem here. -->
 | 
				
			||||||
							
								
								
									
										6
									
								
								.github/ISSUE_TEMPLATE/custom.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					name: Custom issue template
 | 
				
			||||||
 | 
					about: Describe this issue template's purpose here.
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					---
 | 
				
			||||||
 | 
					name: Feature request
 | 
				
			||||||
 | 
					about: Suggest an idea for this project
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Problem Description**
 | 
				
			||||||
 | 
					<!-- Please add a clear and concise description of what the problem is. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Proposed Solution**
 | 
				
			||||||
 | 
					<!-- Describe the solution you'd like in a clear and concise manner -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Describe alternatives you've considered**
 | 
				
			||||||
 | 
					<!-- A clear and concise description of any alternative solutions or features you've considered. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Additional context**
 | 
				
			||||||
 | 
					<!-- Add any other context or screenshots about the feature request here. -->
 | 
				
			||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -4,6 +4,9 @@
 | 
				
			|||||||
# npm cache directory
 | 
					# npm cache directory
 | 
				
			||||||
.npm
 | 
					.npm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# transifexrc - if user prefers it to be in working tree
 | 
				
			||||||
 | 
					.transifexrc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Compiled binary build directory
 | 
					# Compiled binary build directory
 | 
				
			||||||
dist/
 | 
					dist/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								.mailmap
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					Anders Kaseorg <anders@zulip.com> <anders@zulipchat.com>
 | 
				
			||||||
 | 
					Rishi Gupta <rishig@zulip.com> <rishig@zulipchat.com>
 | 
				
			||||||
 | 
					Rishi Gupta <rishig@zulip.com> <rishig@users.noreply.github.com>
 | 
				
			||||||
 | 
					Tim Abbott <tabbott@zulip.com> <tabbott@zulipchat.com>
 | 
				
			||||||
 | 
					Tim Abbott <tabbott@zulip.com> <tabbott@mit.edu>
 | 
				
			||||||
							
								
								
									
										9
									
								
								.tx/config
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					[main]
 | 
				
			||||||
 | 
					host = https://www.transifex.com
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[zulip.desktopjson]
 | 
				
			||||||
 | 
					file_filter = app/translations/<lang>.json
 | 
				
			||||||
 | 
					minimum_perc = 0
 | 
				
			||||||
 | 
					source_file = app/translations/en.json
 | 
				
			||||||
 | 
					source_lang = en
 | 
				
			||||||
 | 
					type = KEYVALUEJSON
 | 
				
			||||||
@@ -6,10 +6,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Desktop client for Zulip. Available for Mac, Linux, and Windows.
 | 
					Desktop client for Zulip. Available for Mac, Linux, and Windows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<img src="http://i.imgur.com/ChzTq4F.png"/>
 | 
					<img src="https://i.imgur.com/s1o6TRA.png"/>
 | 
				
			||||||
 | 
					<img src="https://i.imgur.com/vekKnW4.png"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Download
 | 
					# Download
 | 
				
			||||||
Please see the [installation guide](https://zulipchat.com/help/desktop-app-install-guide).
 | 
					Please see the [installation guide](https://zulip.com/help/desktop-app-install-guide).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Features
 | 
					# Features
 | 
				
			||||||
* Sign in to multiple organizations
 | 
					* Sign in to multiple organizations
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,26 @@
 | 
				
			|||||||
import {app, dialog} from 'electron';
 | 
					import {app, dialog, session} from 'electron';
 | 
				
			||||||
import {UpdateDownloadedEvent, UpdateInfo, autoUpdater} from 'electron-updater';
 | 
					import util from 'util';
 | 
				
			||||||
import {linuxUpdateNotification} from './linuxupdater';	// Required only in case of linux
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import log from 'electron-log';
 | 
					 | 
				
			||||||
import isDev from 'electron-is-dev';
 | 
					import isDev from 'electron-is-dev';
 | 
				
			||||||
 | 
					import log from 'electron-log';
 | 
				
			||||||
 | 
					import {UpdateDownloadedEvent, UpdateInfo, autoUpdater} from 'electron-updater';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
					import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
				
			||||||
import * as LinkUtil from '../renderer/js/utils/link-util';
 | 
					import * as LinkUtil from '../renderer/js/utils/link-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function appUpdater(updateFromMenu = false): void {
 | 
					import {linuxUpdateNotification} from './linuxupdater';	// Required only in case of linux
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sleep = util.promisify(setTimeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function appUpdater(updateFromMenu = false): Promise<void> {
 | 
				
			||||||
	// Don't initiate auto-updates in development
 | 
						// Don't initiate auto-updates in development
 | 
				
			||||||
	if (isDev) {
 | 
						if (isDev) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (process.platform === 'linux' && !process.env.APPIMAGE) {
 | 
						if (process.platform === 'linux' && !process.env.APPIMAGE) {
 | 
				
			||||||
		linuxUpdateNotification();
 | 
							const ses = session.fromPartition('persist:webviewsession');
 | 
				
			||||||
 | 
							await linuxUpdateNotification(ses);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,11 +80,11 @@ export function appUpdater(updateFromMenu = false): void {
 | 
				
			|||||||
				type: 'error',
 | 
									type: 'error',
 | 
				
			||||||
				buttons: ['Manual Download', 'Cancel'],
 | 
									buttons: ['Manual Download', 'Cancel'],
 | 
				
			||||||
				message: messageText,
 | 
									message: messageText,
 | 
				
			||||||
				detail: `Error: ${error.message}\n\nThe latest version of Zulip Desktop is available at -\nhttps://zulipchat.com/apps/.\n
 | 
									detail: `Error: ${error.message}\n\nThe latest version of Zulip Desktop is available at -\nhttps://zulip.com/apps/.\n
 | 
				
			||||||
				Current Version: ${app.getVersion()}`
 | 
									Current Version: ${app.getVersion()}`
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
			if (response === 0) {
 | 
								if (response === 0) {
 | 
				
			||||||
				await LinkUtil.openBrowser(new URL('https://zulipchat.com/apps/'));
 | 
									await LinkUtil.openBrowser(new URL('https://zulip.com/apps/'));
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
@@ -94,13 +100,12 @@ export function appUpdater(updateFromMenu = false): void {
 | 
				
			|||||||
			detail: 'It will be installed the next time you restart the application'
 | 
								detail: 'It will be installed the next time you restart the application'
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		if (response === 0) {
 | 
							if (response === 0) {
 | 
				
			||||||
			setTimeout(() => {
 | 
								await sleep(1000);
 | 
				
			||||||
				autoUpdater.quitAndInstall();
 | 
								autoUpdater.quitAndInstall();
 | 
				
			||||||
				// Force app to quit. This is just a workaround, ideally autoUpdater.quitAndInstall() should relaunch the app.
 | 
								// Force app to quit. This is just a workaround, ideally autoUpdater.quitAndInstall() should relaunch the app.
 | 
				
			||||||
				app.quit();
 | 
								app.quit();
 | 
				
			||||||
			}, 1000);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	// Init for updates
 | 
						// Init for updates
 | 
				
			||||||
	(async () => autoUpdater.checkForUpdates())();
 | 
						await autoUpdater.checkForUpdates();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,22 @@
 | 
				
			|||||||
import {sentryInit} from '../renderer/js/utils/sentry-util';
 | 
					
 | 
				
			||||||
import {appUpdater} from './autoupdater';
 | 
					import electron, {app, dialog, ipcMain, session} from 'electron';
 | 
				
			||||||
import {setAutoLaunch} from './startup';
 | 
					import fs from 'fs';
 | 
				
			||||||
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import windowStateKeeper from 'electron-window-state';
 | 
					import windowStateKeeper from 'electron-window-state';
 | 
				
			||||||
import path from 'path';
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					 | 
				
			||||||
import electron, {app, dialog, ipcMain, session} from 'electron';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as AppMenu from './menu';
 | 
					 | 
				
			||||||
import * as BadgeSettings from '../renderer/js/pages/preference/badge-settings';
 | 
					import * as BadgeSettings from '../renderer/js/pages/preference/badge-settings';
 | 
				
			||||||
import * as CertificateUtil from '../renderer/js/utils/certificate-util';
 | 
					import * as CertificateUtil from '../renderer/js/utils/certificate-util';
 | 
				
			||||||
import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
					import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
				
			||||||
import * as ProxyUtil from '../renderer/js/utils/proxy-util';
 | 
					import * as ProxyUtil from '../renderer/js/utils/proxy-util';
 | 
				
			||||||
 | 
					import {sentryInit} from '../renderer/js/utils/sentry-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface PatchedGlobal extends NodeJS.Global {
 | 
					import {appUpdater} from './autoupdater';
 | 
				
			||||||
	mainWindowState: windowStateKeeper.State;
 | 
					import * as AppMenu from './menu';
 | 
				
			||||||
}
 | 
					import {_getServerSettings, _saveServerIcon, _isOnline} from './request';
 | 
				
			||||||
 | 
					import {setAutoLaunch} from './startup';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const globalPatched = global as PatchedGlobal;
 | 
					let mainWindowState: windowStateKeeper.State;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Prevent window being garbage collected
 | 
					// Prevent window being garbage collected
 | 
				
			||||||
let mainWindow: Electron.BrowserWindow;
 | 
					let mainWindow: Electron.BrowserWindow;
 | 
				
			||||||
@@ -58,9 +57,7 @@ function makeRendererCallback(callback: (...args: any[]) => void): number {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
 | 
					const APP_ICON = path.join(__dirname, '../resources', 'Icon');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const iconPath = (): string => {
 | 
					const iconPath = (): string => APP_ICON + (process.platform === 'win32' ? '.ico' : '.png');
 | 
				
			||||||
	return APP_ICON + (process.platform === 'win32' ? '.ico' : '.png');
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Toggle the app window
 | 
					// Toggle the app window
 | 
				
			||||||
const toggleApp = (): void => {
 | 
					const toggleApp = (): void => {
 | 
				
			||||||
@@ -79,9 +76,6 @@ function createMainWindow(): Electron.BrowserWindow {
 | 
				
			|||||||
		path: `${app.getPath('userData')}/config`
 | 
							path: `${app.getPath('userData')}/config`
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Let's keep the window position global so that we can access it in other process
 | 
					 | 
				
			||||||
	globalPatched.mainWindowState = mainWindowState;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const win = new electron.BrowserWindow({
 | 
						const win = new electron.BrowserWindow({
 | 
				
			||||||
		// This settings needs to be saved in config
 | 
							// This settings needs to be saved in config
 | 
				
			||||||
		title: 'Zulip',
 | 
							title: 'Zulip',
 | 
				
			||||||
@@ -90,7 +84,7 @@ function createMainWindow(): Electron.BrowserWindow {
 | 
				
			|||||||
		y: mainWindowState.y,
 | 
							y: mainWindowState.y,
 | 
				
			||||||
		width: mainWindowState.width,
 | 
							width: mainWindowState.width,
 | 
				
			||||||
		height: mainWindowState.height,
 | 
							height: mainWindowState.height,
 | 
				
			||||||
		minWidth: 300,
 | 
							minWidth: 500,
 | 
				
			||||||
		minHeight: 400,
 | 
							minHeight: 400,
 | 
				
			||||||
		webPreferences: {
 | 
							webPreferences: {
 | 
				
			||||||
			plugins: true,
 | 
								plugins: true,
 | 
				
			||||||
@@ -170,6 +164,9 @@ app.on('ready', () => {
 | 
				
			|||||||
	const ses = session.fromPartition('persist:webviewsession');
 | 
						const ses = session.fromPartition('persist:webviewsession');
 | 
				
			||||||
	ses.setUserAgent(`ZulipElectron/${app.getVersion()} ${ses.getUserAgent()}`);
 | 
						ses.setUserAgent(`ZulipElectron/${app.getVersion()} ${ses.getUserAgent()}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ipcMain.on('set-spellcheck-langs', () => {
 | 
				
			||||||
 | 
							ses.setSpellCheckerLanguages(ConfigUtil.getConfigItem('spellcheckerLanguages'));
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
	AppMenu.setMenu({
 | 
						AppMenu.setMenu({
 | 
				
			||||||
		tabs: []
 | 
							tabs: []
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
@@ -208,27 +205,36 @@ app.on('ready', () => {
 | 
				
			|||||||
		event.returnValue = session.fromPartition('persist:webviewsession').getUserAgent();
 | 
							event.returnValue = session.fromPartition('persist:webviewsession').getUserAgent();
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	page.once('did-frame-finish-load', () => {
 | 
						ipcMain.handle('get-server-settings', async (event, domain: string) => _getServerSettings(domain, ses));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ipcMain.handle('save-server-icon', async (event, url: string) => _saveServerIcon(url, ses));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ipcMain.handle('is-online', async (event, url: string) => _isOnline(url, ses));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						page.once('did-frame-finish-load', async () => {
 | 
				
			||||||
		// Initiate auto-updates on MacOS and Windows
 | 
							// Initiate auto-updates on MacOS and Windows
 | 
				
			||||||
		if (ConfigUtil.getConfigItem('autoUpdate')) {
 | 
							if (ConfigUtil.getConfigItem('autoUpdate')) {
 | 
				
			||||||
			appUpdater();
 | 
								await appUpdater();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.on('certificate-error', (
 | 
						app.on('certificate-error', (
 | 
				
			||||||
		event: Event,
 | 
							event: Event,
 | 
				
			||||||
		webContents: Electron.WebContents,
 | 
							webContents: Electron.WebContents,
 | 
				
			||||||
		url: string,
 | 
							urlString: string,
 | 
				
			||||||
		error: string,
 | 
							error: string,
 | 
				
			||||||
		certificate: Electron.Certificate,
 | 
							certificate: Electron.Certificate,
 | 
				
			||||||
		callback: (isTrusted: boolean) => void
 | 
							callback: (isTrusted: boolean) => void
 | 
				
			||||||
	) /* eslint-disable-line max-params */ => {
 | 
						) /* eslint-disable-line max-params */ => {
 | 
				
			||||||
		// TODO: The entire concept of selectively ignoring certificate errors
 | 
							// TODO: The entire concept of selectively ignoring certificate errors
 | 
				
			||||||
		// is ill-conceived, and this handler needs to be deleted.
 | 
							// is ill-conceived, and this handler needs to be deleted.
 | 
				
			||||||
		event.preventDefault();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const {origin} = new URL(url);
 | 
							const url = new URL(urlString);
 | 
				
			||||||
		const filename = CertificateUtil.getCertificate(encodeURIComponent(origin));
 | 
							if (url.protocol === 'wss:') {
 | 
				
			||||||
 | 
								url.protocol = 'https:';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const filename = CertificateUtil.getCertificate(encodeURIComponent(url.origin));
 | 
				
			||||||
		if (filename !== undefined) {
 | 
							if (filename !== undefined) {
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				const savedCertificate = fs.readFileSync(
 | 
									const savedCertificate = fs.readFileSync(
 | 
				
			||||||
@@ -237,6 +243,7 @@ app.on('ready', () => {
 | 
				
			|||||||
				);
 | 
									);
 | 
				
			||||||
				if (certificate.data.replace(/[\r\n]/g, '') ===
 | 
									if (certificate.data.replace(/[\r\n]/g, '') ===
 | 
				
			||||||
					savedCertificate.replace(/[\r\n]/g, '')) {
 | 
										savedCertificate.replace(/[\r\n]/g, '')) {
 | 
				
			||||||
 | 
										event.preventDefault();
 | 
				
			||||||
					callback(true);
 | 
										callback(true);
 | 
				
			||||||
					return;
 | 
										return;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -245,20 +252,11 @@ app.on('ready', () => {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		page.send(
 | 
							dialog.showErrorBox(
 | 
				
			||||||
			'certificate-error',
 | 
								'Certificate error',
 | 
				
			||||||
			webContents.id === mainWindow.webContents.id ? null : webContents.id,
 | 
								`The server presented an invalid certificate for ${url.origin}:
 | 
				
			||||||
			makeRendererCallback(ignore => {
 | 
					 | 
				
			||||||
				callback(ignore);
 | 
					 | 
				
			||||||
				if (!ignore) {
 | 
					 | 
				
			||||||
					dialog.showErrorBox(
 | 
					 | 
				
			||||||
						'Certificate error',
 | 
					 | 
				
			||||||
						`The server presented an invalid certificate for ${origin}:
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
${error}`
 | 
					${error}`
 | 
				
			||||||
					);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -320,7 +318,7 @@ ${error}`
 | 
				
			|||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ipcMain.on('clear-app-settings', () => {
 | 
						ipcMain.on('clear-app-settings', () => {
 | 
				
			||||||
		globalPatched.mainWindowState.unmanage();
 | 
							mainWindowState.unmanage();
 | 
				
			||||||
		app.relaunch();
 | 
							app.relaunch();
 | 
				
			||||||
		app.exit();
 | 
							app.exit();
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
@@ -357,7 +355,7 @@ ${error}`
 | 
				
			|||||||
		AppMenu.setMenu(props);
 | 
							AppMenu.setMenu(props);
 | 
				
			||||||
		const activeTab = props.tabs[props.activeTabIndex];
 | 
							const activeTab = props.tabs[props.activeTabIndex];
 | 
				
			||||||
		if (activeTab) {
 | 
							if (activeTab) {
 | 
				
			||||||
			mainWindow.setTitle(`Zulip - ${activeTab.webview.props.name}`);
 | 
								mainWindow.setTitle(`Zulip - ${activeTab.webviewName}`);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -369,7 +367,7 @@ ${error}`
 | 
				
			|||||||
		page.downloadURL(url);
 | 
							page.downloadURL(url);
 | 
				
			||||||
		page.session.once('will-download', async (_event: Event, item) => {
 | 
							page.session.once('will-download', async (_event: Event, item) => {
 | 
				
			||||||
			if (ConfigUtil.getConfigItem('promptDownload', false)) {
 | 
								if (ConfigUtil.getConfigItem('promptDownload', false)) {
 | 
				
			||||||
				const showDialogOptions: object = {
 | 
									const showDialogOptions: electron.SaveDialogOptions = {
 | 
				
			||||||
					defaultPath: path.join(downloadPath, item.getFilename())
 | 
										defaultPath: path.join(downloadPath, item.getFilename())
 | 
				
			||||||
				};
 | 
									};
 | 
				
			||||||
				item.setSaveDialogOptions(showDialogOptions);
 | 
									item.setSaveDialogOptions(showDialogOptions);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,52 +1,45 @@
 | 
				
			|||||||
import {app, Notification} from 'electron';
 | 
					import {app, Notification, net} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import request from 'request';
 | 
					import getStream from 'get-stream';
 | 
				
			||||||
import semver from 'semver';
 | 
					import semver from 'semver';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
					import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
				
			||||||
import * as ProxyUtil from '../renderer/js/utils/proxy-util';
 | 
					 | 
				
			||||||
import * as LinuxUpdateUtil from '../renderer/js/utils/linux-update-util';
 | 
					import * as LinuxUpdateUtil from '../renderer/js/utils/linux-update-util';
 | 
				
			||||||
import Logger from '../renderer/js/utils/logger-util';
 | 
					import Logger from '../renderer/js/utils/logger-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {fetchResponse} from './request';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = new Logger({
 | 
					const logger = new Logger({
 | 
				
			||||||
	file: 'linux-update-util.log',
 | 
						file: 'linux-update-util.log',
 | 
				
			||||||
	timestamp: true
 | 
						timestamp: true
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function linuxUpdateNotification(): void {
 | 
					export async function linuxUpdateNotification(session: Electron.session): Promise<void> {
 | 
				
			||||||
	let	url = 'https://api.github.com/repos/zulip/zulip-desktop/releases';
 | 
						let	url = 'https://api.github.com/repos/zulip/zulip-desktop/releases';
 | 
				
			||||||
	url = ConfigUtil.getConfigItem('betaUpdate') ? url : url + '/latest';
 | 
						url = ConfigUtil.getConfigItem('betaUpdate') ? url : url + '/latest';
 | 
				
			||||||
	const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const options = {
 | 
						try {
 | 
				
			||||||
		url,
 | 
							const response = await fetchResponse(net.request({url, session}));
 | 
				
			||||||
		headers: {'User-Agent': 'request'},
 | 
							if (response.statusCode !== 200) {
 | 
				
			||||||
		proxy: proxyEnabled ? ProxyUtil.getProxy(url) : '',
 | 
								logger.log('Linux update response status: ', response.statusCode);
 | 
				
			||||||
		ecdhCurve: 'auto'
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	request(options, (error, response: request.Response, body: string) => {
 | 
					 | 
				
			||||||
		if (error) {
 | 
					 | 
				
			||||||
			logger.error('Linux update error.');
 | 
					 | 
				
			||||||
			logger.error(error);
 | 
					 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (response.statusCode < 400) {
 | 
							const data = JSON.parse(await getStream(response));
 | 
				
			||||||
			const data = JSON.parse(body);
 | 
							const latestVersion = ConfigUtil.getConfigItem('betaUpdate') ? data[0].tag_name : data.tag_name;
 | 
				
			||||||
			const latestVersion = ConfigUtil.getConfigItem('betaUpdate') ? data[0].tag_name : data.tag_name;
 | 
							if (typeof latestVersion !== 'string') {
 | 
				
			||||||
			if (typeof latestVersion !== 'string') {
 | 
								throw new TypeError('Expected string for tag_name');
 | 
				
			||||||
				throw new TypeError('Expected string for tag_name');
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (semver.gt(latestVersion, app.getVersion())) {
 | 
					 | 
				
			||||||
				const notified = LinuxUpdateUtil.getUpdateItem(latestVersion);
 | 
					 | 
				
			||||||
				if (notified === null) {
 | 
					 | 
				
			||||||
					new Notification({title: 'Zulip Update', body: `A new version ${latestVersion} is available. Please update using your package manager.`}).show();
 | 
					 | 
				
			||||||
					LinuxUpdateUtil.setUpdateItem(latestVersion, true);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			logger.log('Linux update response status: ', response.statusCode);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
					
 | 
				
			||||||
 | 
							if (semver.gt(latestVersion, app.getVersion())) {
 | 
				
			||||||
 | 
								const notified = LinuxUpdateUtil.getUpdateItem(latestVersion);
 | 
				
			||||||
 | 
								if (notified === null) {
 | 
				
			||||||
 | 
									new Notification({title: 'Zulip Update', body: `A new version ${latestVersion} is available. Please update using your package manager.`}).show();
 | 
				
			||||||
 | 
									LinuxUpdateUtil.setUpdateItem(latestVersion, true);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							logger.error('Linux update error.');
 | 
				
			||||||
 | 
							logger.error(error);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,23 @@
 | 
				
			|||||||
import {app, shell, BrowserWindow, Menu, dialog} from 'electron';
 | 
					import {app, shell, BrowserWindow, Menu} from 'electron';
 | 
				
			||||||
import {appUpdater} from './autoupdater';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import AdmZip from 'adm-zip';
 | 
					import AdmZip from 'adm-zip';
 | 
				
			||||||
import fs from 'fs-extra';
 | 
					
 | 
				
			||||||
import path from 'path';
 | 
					import type {TabData} from '../renderer/js/main';
 | 
				
			||||||
import * as DNDUtil from '../renderer/js/utils/dnd-util';
 | 
					 | 
				
			||||||
import Logger from '../renderer/js/utils/logger-util';
 | 
					 | 
				
			||||||
import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
					import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
				
			||||||
 | 
					import * as DNDUtil from '../renderer/js/utils/dnd-util';
 | 
				
			||||||
import * as LinkUtil from '../renderer/js/utils/link-util';
 | 
					import * as LinkUtil from '../renderer/js/utils/link-util';
 | 
				
			||||||
import * as t from '../renderer/js/utils/translation-util';
 | 
					import * as t from '../renderer/js/utils/translation-util';
 | 
				
			||||||
import type {ServerOrFunctionalTab} from '../renderer/js/main';
 | 
					
 | 
				
			||||||
 | 
					import {appUpdater} from './autoupdater';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface MenuProps {
 | 
					export interface MenuProps {
 | 
				
			||||||
	tabs: ServerOrFunctionalTab[];
 | 
						tabs: TabData[];
 | 
				
			||||||
	activeTabIndex?: number;
 | 
						activeTabIndex?: number;
 | 
				
			||||||
	enableMenu?: boolean;
 | 
						enableMenu?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const appName = app.name;
 | 
					const appName = app.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = new Logger({
 | 
					 | 
				
			||||||
	file: 'errors.log',
 | 
					 | 
				
			||||||
	timestamp: true
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function getHistorySubmenu(enableMenu: boolean): Electron.MenuItemConstructorOptions[] {
 | 
					function getHistorySubmenu(enableMenu: boolean): Electron.MenuItemConstructorOptions[] {
 | 
				
			||||||
	return [{
 | 
						return [{
 | 
				
			||||||
		label: t.__('Back'),
 | 
							label: t.__('Back'),
 | 
				
			||||||
@@ -49,8 +43,8 @@ function getHistorySubmenu(enableMenu: boolean): Electron.MenuItemConstructorOpt
 | 
				
			|||||||
function getToolsSubmenu(): Electron.MenuItemConstructorOptions[] {
 | 
					function getToolsSubmenu(): Electron.MenuItemConstructorOptions[] {
 | 
				
			||||||
	return [{
 | 
						return [{
 | 
				
			||||||
		label: t.__('Check for Updates'),
 | 
							label: t.__('Check for Updates'),
 | 
				
			||||||
		click() {
 | 
							async click() {
 | 
				
			||||||
			checkForUpdate();
 | 
								await checkForUpdate();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -62,13 +56,6 @@ function getToolsSubmenu(): Electron.MenuItemConstructorOptions[] {
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		type: 'separator'
 | 
							type: 'separator'
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		label: t.__('Factory Reset'),
 | 
					 | 
				
			||||||
		accelerator: process.platform === 'darwin' ? 'Command+Shift+D' : 'Ctrl+Shift+D',
 | 
					 | 
				
			||||||
		async click() {
 | 
					 | 
				
			||||||
			await resetAppSettings();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		label: t.__('Download App Logs'),
 | 
							label: t.__('Download App Logs'),
 | 
				
			||||||
		click() {
 | 
							click() {
 | 
				
			||||||
@@ -135,7 +122,7 @@ function getViewSubmenu(): Electron.MenuItemConstructorOptions[] {
 | 
				
			|||||||
		role: 'togglefullscreen'
 | 
							role: 'togglefullscreen'
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		label: t.__('Zoom In'),
 | 
							label: t.__('Zoom In'),
 | 
				
			||||||
		role: 'zoomIn',
 | 
							accelerator: 'CommandOrControl+=',
 | 
				
			||||||
		click(_item, focusedWindow) {
 | 
							click(_item, focusedWindow) {
 | 
				
			||||||
			if (focusedWindow) {
 | 
								if (focusedWindow) {
 | 
				
			||||||
				sendAction('zoomIn');
 | 
									sendAction('zoomIn');
 | 
				
			||||||
@@ -143,7 +130,6 @@ function getViewSubmenu(): Electron.MenuItemConstructorOptions[] {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		label: t.__('Zoom Out'),
 | 
							label: t.__('Zoom Out'),
 | 
				
			||||||
		role: 'zoomOut',
 | 
					 | 
				
			||||||
		accelerator: 'CommandOrControl+-',
 | 
							accelerator: 'CommandOrControl+-',
 | 
				
			||||||
		click(_item, focusedWindow) {
 | 
							click(_item, focusedWindow) {
 | 
				
			||||||
			if (focusedWindow) {
 | 
								if (focusedWindow) {
 | 
				
			||||||
@@ -152,7 +138,6 @@ function getViewSubmenu(): Electron.MenuItemConstructorOptions[] {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		label: t.__('Actual Size'),
 | 
							label: t.__('Actual Size'),
 | 
				
			||||||
		role: 'resetZoom',
 | 
					 | 
				
			||||||
		accelerator: 'CommandOrControl+0',
 | 
							accelerator: 'CommandOrControl+0',
 | 
				
			||||||
		click(_item, focusedWindow) {
 | 
							click(_item, focusedWindow) {
 | 
				
			||||||
			if (focusedWindow) {
 | 
								if (focusedWindow) {
 | 
				
			||||||
@@ -230,7 +215,7 @@ function getHelpSubmenu(): Electron.MenuItemConstructorOptions[] {
 | 
				
			|||||||
	];
 | 
						];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getWindowSubmenu(tabs: ServerOrFunctionalTab[], activeTabIndex: number): Electron.MenuItemConstructorOptions[] {
 | 
					function getWindowSubmenu(tabs: TabData[], activeTabIndex: number): Electron.MenuItemConstructorOptions[] {
 | 
				
			||||||
	const initialSubmenu: Electron.MenuItemConstructorOptions[] = [{
 | 
						const initialSubmenu: Electron.MenuItemConstructorOptions[] = [{
 | 
				
			||||||
		label: t.__('Minimize'),
 | 
							label: t.__('Minimize'),
 | 
				
			||||||
		role: 'minimize'
 | 
							role: 'minimize'
 | 
				
			||||||
@@ -246,17 +231,17 @@ function getWindowSubmenu(tabs: ServerOrFunctionalTab[], activeTabIndex: number)
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
		tabs.forEach(tab => {
 | 
							tabs.forEach(tab => {
 | 
				
			||||||
			// Do not add functional tab settings to list of windows in menu bar
 | 
								// Do not add functional tab settings to list of windows in menu bar
 | 
				
			||||||
			if (tab.props.role === 'function' && tab.props.name === 'Settings') {
 | 
								if (tab.role === 'function' && tab.name === 'Settings') {
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			initialSubmenu.push({
 | 
								initialSubmenu.push({
 | 
				
			||||||
				label: tab.props.name,
 | 
									label: tab.name,
 | 
				
			||||||
				accelerator: tab.props.role === 'function' ? '' : `${ShortcutKey} + ${tab.props.index + 1}`,
 | 
									accelerator: tab.role === 'function' ? '' : `${ShortcutKey} + ${tab.index + 1}`,
 | 
				
			||||||
				checked: tab.props.index === activeTabIndex,
 | 
									checked: tab.index === activeTabIndex,
 | 
				
			||||||
				click(_item, focusedWindow) {
 | 
									click(_item, focusedWindow) {
 | 
				
			||||||
					if (focusedWindow) {
 | 
										if (focusedWindow) {
 | 
				
			||||||
						sendAction('switch-server-tab', tab.props.index);
 | 
											sendAction('switch-server-tab', tab.index);
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				type: 'checkbox'
 | 
									type: 'checkbox'
 | 
				
			||||||
@@ -558,58 +543,28 @@ function sendAction(action: string, ...parameters: unknown[]): void {
 | 
				
			|||||||
	win.webContents.send(action, ...parameters);
 | 
						win.webContents.send(action, ...parameters);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkForUpdate(): void {
 | 
					async function checkForUpdate(): Promise<void> {
 | 
				
			||||||
	appUpdater(true);
 | 
						await appUpdater(true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getNextServer(tabs: ServerOrFunctionalTab[], activeTabIndex: number): number {
 | 
					function getNextServer(tabs: TabData[], activeTabIndex: number): number {
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		activeTabIndex = (activeTabIndex + 1) % tabs.length;
 | 
							activeTabIndex = (activeTabIndex + 1) % tabs.length;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	while (tabs[activeTabIndex].props.role !== 'server');
 | 
						while (tabs[activeTabIndex].role !== 'server');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return activeTabIndex;
 | 
						return activeTabIndex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getPreviousServer(tabs: ServerOrFunctionalTab[], activeTabIndex: number): number {
 | 
					function getPreviousServer(tabs: TabData[], activeTabIndex: number): number {
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
		activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length;
 | 
							activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	while (tabs[activeTabIndex].props.role !== 'server');
 | 
						while (tabs[activeTabIndex].role !== 'server');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return activeTabIndex;
 | 
						return activeTabIndex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function resetAppSettings(): Promise<void> {
 | 
					 | 
				
			||||||
	const resetAppSettingsMessage = 'By proceeding you will be removing all connected organizations and preferences from Zulip.';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// We save App's settings/configurations in following files
 | 
					 | 
				
			||||||
	const settingFiles = ['config/window-state.json', 'config/domain.json', 'config/settings.json', 'config/certificates.json'];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const {response} = await dialog.showMessageBox({
 | 
					 | 
				
			||||||
		type: 'warning',
 | 
					 | 
				
			||||||
		buttons: ['YES', 'NO'],
 | 
					 | 
				
			||||||
		defaultId: 0,
 | 
					 | 
				
			||||||
		message: 'Are you sure?',
 | 
					 | 
				
			||||||
		detail: resetAppSettingsMessage
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
	if (response === 0) {
 | 
					 | 
				
			||||||
		settingFiles.forEach(settingFileName => {
 | 
					 | 
				
			||||||
			const getSettingFilesPath = path.join(app.getPath('appData'), appName, settingFileName);
 | 
					 | 
				
			||||||
			fs.access(getSettingFilesPath, (error: NodeJS.ErrnoException) => {
 | 
					 | 
				
			||||||
				if (error) {
 | 
					 | 
				
			||||||
					logger.error('Error while resetting app settings.');
 | 
					 | 
				
			||||||
					logger.error(error);
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					fs.unlink(getSettingFilesPath, () => {
 | 
					 | 
				
			||||||
						sendAction('clear-app-data');
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function setMenu(props: MenuProps): void {
 | 
					export function setMenu(props: MenuProps): void {
 | 
				
			||||||
	const tpl = process.platform === 'darwin' ? getDarwinTpl(props) : getOtherTpl(props);
 | 
						const tpl = process.platform === 'darwin' ? getDarwinTpl(props) : getOtherTpl(props);
 | 
				
			||||||
	const menu = Menu.buildFromTemplate(tpl);
 | 
						const menu = Menu.buildFromTemplate(tpl);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										112
									
								
								app/main/request.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					import {ClientRequest, IncomingMessage, app, net} from 'electron';
 | 
				
			||||||
 | 
					import fs from 'fs';
 | 
				
			||||||
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					import stream from 'stream';
 | 
				
			||||||
 | 
					import util from 'util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import getStream from 'get-stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {ServerConf} from '../renderer/js/utils/domain-util';
 | 
				
			||||||
 | 
					import Logger from '../renderer/js/utils/logger-util';
 | 
				
			||||||
 | 
					import * as Messages from '../resources/messages';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function fetchResponse(request: ClientRequest): Promise<IncomingMessage> {
 | 
				
			||||||
 | 
						return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
							request.on('response', resolve);
 | 
				
			||||||
 | 
							request.on('abort', () => reject(new Error('Request aborted')));
 | 
				
			||||||
 | 
							request.on('error', reject);
 | 
				
			||||||
 | 
							request.end();
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const pipeline = util.promisify(stream.pipeline);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Request: domain-util */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const defaultIconUrl = '../renderer/img/icon.png';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const logger = new Logger({
 | 
				
			||||||
 | 
						file: 'domain-util.log',
 | 
				
			||||||
 | 
						timestamp: true
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const generateFilePath = (url: string): string => {
 | 
				
			||||||
 | 
						const dir = `${app.getPath('userData')}/server-icons`;
 | 
				
			||||||
 | 
						const extension = path.extname(url).split('?')[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let hash = 5381;
 | 
				
			||||||
 | 
						let {length} = url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (length) {
 | 
				
			||||||
 | 
							hash = (hash * 33) ^ url.charCodeAt(--length);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create 'server-icons' directory if not existed
 | 
				
			||||||
 | 
						if (!fs.existsSync(dir)) {
 | 
				
			||||||
 | 
							fs.mkdirSync(dir);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return `${dir}/${hash >>> 0}${extension}`;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const _getServerSettings = async (domain: string, session: Electron.session): Promise<ServerConf> => {
 | 
				
			||||||
 | 
						const response = await fetchResponse(net.request({
 | 
				
			||||||
 | 
							url: domain + '/api/v1/server_settings',
 | 
				
			||||||
 | 
							session
 | 
				
			||||||
 | 
						}));
 | 
				
			||||||
 | 
						if (response.statusCode !== 200) {
 | 
				
			||||||
 | 
							throw new Error(Messages.invalidZulipServerError(domain));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const {realm_name, realm_uri, realm_icon} = JSON.parse(await getStream(response));
 | 
				
			||||||
 | 
						if (
 | 
				
			||||||
 | 
							typeof realm_name !== 'string' ||
 | 
				
			||||||
 | 
								typeof realm_uri !== 'string' ||
 | 
				
			||||||
 | 
								typeof realm_icon !== 'string'
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							throw new TypeError(Messages.noOrgsError(domain));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							// Some Zulip Servers use absolute URL for server icon whereas others use relative URL
 | 
				
			||||||
 | 
							// Following check handles both the cases
 | 
				
			||||||
 | 
							icon: realm_icon.startsWith('/') ? realm_uri + realm_icon : realm_icon,
 | 
				
			||||||
 | 
							url: realm_uri,
 | 
				
			||||||
 | 
							alias: realm_name
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const _saveServerIcon = async (url: string, session: Electron.session): Promise<string> => {
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const response = await fetchResponse(net.request({url, session}));
 | 
				
			||||||
 | 
							if (response.statusCode !== 200) {
 | 
				
			||||||
 | 
								logger.log('Could not get server icon.');
 | 
				
			||||||
 | 
								return defaultIconUrl;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const filePath = generateFilePath(url);
 | 
				
			||||||
 | 
							await pipeline(response, fs.createWriteStream(filePath));
 | 
				
			||||||
 | 
							return filePath;
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							logger.log('Could not get server icon.');
 | 
				
			||||||
 | 
							logger.log(error);
 | 
				
			||||||
 | 
							logger.reportSentry(error);
 | 
				
			||||||
 | 
							return defaultIconUrl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Request: reconnect-util */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const _isOnline = async (url: string, session: Electron.session): Promise<boolean> => {
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
 | 
							const response = await fetchResponse(net.request({
 | 
				
			||||||
 | 
								url: `${url}/static/favicon.ico`,
 | 
				
			||||||
 | 
								session
 | 
				
			||||||
 | 
							}));
 | 
				
			||||||
 | 
							const isValidResponse = response.statusCode >= 200 && response.statusCode < 400;
 | 
				
			||||||
 | 
							return isValidResponse;
 | 
				
			||||||
 | 
						} catch (error) {
 | 
				
			||||||
 | 
							logger.log(error);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -2,6 +2,7 @@ import {app} from 'electron';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import AutoLaunch from 'auto-launch';
 | 
					import AutoLaunch from 'auto-launch';
 | 
				
			||||||
import isDev from 'electron-is-dev';
 | 
					import isDev from 'electron-is-dev';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
					import * as ConfigUtil from '../renderer/js/utils/config-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const setAutoLaunch = async (AutoLaunchValue: boolean): Promise<void> => {
 | 
					export const setAutoLaunch = async (AutoLaunchValue: boolean): Promise<void> => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
            <div class="maintenance-info">
 | 
					            <div class="maintenance-info">
 | 
				
			||||||
                <p class="detail maintainer">
 | 
					                <p class="detail maintainer">
 | 
				
			||||||
                    Maintained by
 | 
					                    Maintained by
 | 
				
			||||||
                    <a href="https://zulipchat.com" target="_blank" rel="noopener noreferrer">Zulip</a>
 | 
					                    <a href="https://zulip.com" target="_blank" rel="noopener noreferrer">Zulip</a>
 | 
				
			||||||
                </p>
 | 
					                </p>
 | 
				
			||||||
                <p class="detail license">
 | 
					                <p class="detail license">
 | 
				
			||||||
                    Available under the
 | 
					                    Available under the
 | 
				
			||||||
@@ -26,7 +26,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            const { app } = require('electron').remote;
 | 
					            const { app } = require('electron').remote;
 | 
				
			||||||
            const version_tag = document.querySelector('#version');
 | 
					            const version_tag = document.querySelector('#version');
 | 
				
			||||||
            version_tag.innerHTML = 'v' + app.getVersion();
 | 
					            version_tag.textContent = 'v' + app.getVersion();
 | 
				
			||||||
        </script>
 | 
					        </script>
 | 
				
			||||||
        <script>require('./js/shared/preventdrag.js')</script>
 | 
					        <script>require('./js/shared/preventdrag.js')</script>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								app/renderer/css/feedback.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					:host {
 | 
				
			||||||
 | 
					    --button-color: rgb(69, 166, 149);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button:hover,
 | 
				
			||||||
 | 
					button:focus {
 | 
				
			||||||
 | 
					    border-color: var(--button-color);
 | 
				
			||||||
 | 
					    color: var(--button-color);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					button:active {
 | 
				
			||||||
 | 
					    background-color: rgb(241, 241, 241);
 | 
				
			||||||
 | 
					    color: var(--button-color);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button {
 | 
				
			||||||
 | 
					    background-color: var(--button-color);
 | 
				
			||||||
 | 
					    border-color: var(--button-color);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -389,6 +389,10 @@ img.server-info-icon {
 | 
				
			|||||||
    margin: 6px;
 | 
					    margin: 6px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#note {
 | 
				
			||||||
 | 
					    font-size: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.code {
 | 
					.code {
 | 
				
			||||||
    font-family: Courier New, Courier, monospace;
 | 
					    font-family: Courier New, Courier, monospace;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -663,6 +667,26 @@ i.open-network-button {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* responsive grid */
 | 
					/* responsive grid */
 | 
				
			||||||
 | 
					@media (min-width: 500px) and (max-width: 720px) {
 | 
				
			||||||
 | 
					    #new-server-container {
 | 
				
			||||||
 | 
					        padding-left: 0px;
 | 
				
			||||||
 | 
					        width: 60vw;
 | 
				
			||||||
 | 
					        padding-right: 4vh;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .page-title {
 | 
				
			||||||
 | 
					        width: 60vw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (max-width: 500px) {
 | 
				
			||||||
 | 
					    #new-server-container {
 | 
				
			||||||
 | 
					        padding-left: 0px;
 | 
				
			||||||
 | 
					        width: 54%;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .page-title {
 | 
				
			||||||
 | 
					        width: 54%;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media (max-width: 650px) {
 | 
					@media (max-width: 650px) {
 | 
				
			||||||
    .selected-css-path,
 | 
					    .selected-css-path,
 | 
				
			||||||
@@ -695,9 +719,6 @@ i.open-network-button {
 | 
				
			|||||||
        margin-right: 6px;
 | 
					        margin-right: 6px;
 | 
				
			||||||
        width: 43%;
 | 
					        width: 43%;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    #new-server-container {
 | 
					 | 
				
			||||||
        padding-left: 0px;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media (max-width: 600px) {
 | 
					@media (max-width: 600px) {
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.6 KiB  | 
| 
		 Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.6 KiB  | 
| 
		 Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 14 KiB  | 
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import {clipboard} from 'electron';
 | 
					 | 
				
			||||||
import crypto from 'crypto';
 | 
					import crypto from 'crypto';
 | 
				
			||||||
 | 
					import {clipboard} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This helper is exposed via electron_bridge for use in the social
 | 
					// This helper is exposed via electron_bridge for use in the social
 | 
				
			||||||
// login flow.
 | 
					// login flow.
 | 
				
			||||||
@@ -59,7 +59,7 @@ export class ClipboardDecrypterImpl implements ClipboardDecrypter {
 | 
				
			|||||||
					plaintext =
 | 
										plaintext =
 | 
				
			||||||
						decipher.update(ciphertext, undefined, 'utf8') +
 | 
											decipher.update(ciphertext, undefined, 'utf8') +
 | 
				
			||||||
						decipher.final('utf8');
 | 
											decipher.final('utf8');
 | 
				
			||||||
				} catch (_) {
 | 
									} catch {
 | 
				
			||||||
					// If the parsing or decryption failed in any way,
 | 
										// If the parsing or decryption failed in any way,
 | 
				
			||||||
					// the correct token hasn’t been copied yet; try
 | 
										// the correct token hasn’t been copied yet; try
 | 
				
			||||||
					// again next time.
 | 
										// again next time.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
export default class BaseComponent {
 | 
					export default class BaseComponent {
 | 
				
			||||||
	generateNodeFromTemplate(template: string): Element | null {
 | 
						generateNodeFromHTML(html: string): Element | null {
 | 
				
			||||||
		const wrapper = document.createElement('div');
 | 
							const wrapper = document.createElement('div');
 | 
				
			||||||
		wrapper.innerHTML = template;
 | 
							wrapper.innerHTML = html;
 | 
				
			||||||
		return wrapper.firstElementChild;
 | 
							return wrapper.firstElementChild;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										115
									
								
								app/renderer/js/components/context-menu.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					import {remote, ContextMenuParams} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as t from '../utils/translation-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const {clipboard, Menu} = remote;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const contextMenu = (webContents: Electron.WebContents, event: Event, props: ContextMenuParams) => {
 | 
				
			||||||
 | 
						const isText = props.selectionText !== '';
 | 
				
			||||||
 | 
						const isLink = props.linkURL !== '';
 | 
				
			||||||
 | 
						const linkURL = isLink ? new URL(props.linkURL) : undefined;
 | 
				
			||||||
 | 
						const isEmailAddress = isLink ? linkURL.protocol === 'mailto:' : undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const makeSuggestion = (suggestion: string) => ({
 | 
				
			||||||
 | 
							label: suggestion,
 | 
				
			||||||
 | 
							visible: true,
 | 
				
			||||||
 | 
							async click() {
 | 
				
			||||||
 | 
								await webContents.insertText(suggestion);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						let menuTemplate: Electron.MenuItemConstructorOptions[] = [{
 | 
				
			||||||
 | 
							label: t.__('Add to Dictionary'),
 | 
				
			||||||
 | 
							visible: props.isEditable && isText && props.misspelledWord.length !== 0,
 | 
				
			||||||
 | 
							click(_item) {
 | 
				
			||||||
 | 
								webContents.session.addWordToSpellCheckerDictionary(props.misspelledWord);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							type: 'separator',
 | 
				
			||||||
 | 
							visible: props.isEditable && isText && props.misspelledWord.length !== 0
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: `${t.__('Look Up')} "${props.selectionText}"`,
 | 
				
			||||||
 | 
							visible: process.platform === 'darwin' && isText,
 | 
				
			||||||
 | 
							click(_item) {
 | 
				
			||||||
 | 
								webContents.showDefinitionForSelection();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							type: 'separator',
 | 
				
			||||||
 | 
							visible: process.platform === 'darwin' && isText
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: t.__('Cut'),
 | 
				
			||||||
 | 
							visible: isText,
 | 
				
			||||||
 | 
							enabled: props.isEditable,
 | 
				
			||||||
 | 
							accelerator: 'CommandOrControl+X',
 | 
				
			||||||
 | 
							click(_item) {
 | 
				
			||||||
 | 
								webContents.cut();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: t.__('Copy'),
 | 
				
			||||||
 | 
							accelerator: 'CommandOrControl+C',
 | 
				
			||||||
 | 
							enabled: props.editFlags.canCopy,
 | 
				
			||||||
 | 
							click(_item) {
 | 
				
			||||||
 | 
								webContents.copy();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: t.__('Paste'), // Bug: Paste replaces text
 | 
				
			||||||
 | 
							accelerator: 'CommandOrControl+V',
 | 
				
			||||||
 | 
							enabled: props.isEditable,
 | 
				
			||||||
 | 
							click() {
 | 
				
			||||||
 | 
								webContents.paste();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							type: 'separator'
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: isEmailAddress ? t.__('Copy Email Address') : t.__('Copy Link'),
 | 
				
			||||||
 | 
							visible: isLink,
 | 
				
			||||||
 | 
							click(_item) {
 | 
				
			||||||
 | 
								clipboard.write({
 | 
				
			||||||
 | 
									bookmark: props.linkText,
 | 
				
			||||||
 | 
									text: isEmailAddress ? linkURL.pathname : props.linkURL
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: t.__('Copy Image'),
 | 
				
			||||||
 | 
							visible: props.mediaType === 'image',
 | 
				
			||||||
 | 
							click(_item) {
 | 
				
			||||||
 | 
								webContents.copyImageAt(props.x, props.y);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: t.__('Copy Image URL'),
 | 
				
			||||||
 | 
							visible: props.mediaType === 'image',
 | 
				
			||||||
 | 
							click(_item) {
 | 
				
			||||||
 | 
								clipboard.write({
 | 
				
			||||||
 | 
									bookmark: props.srcURL,
 | 
				
			||||||
 | 
									text: props.srcURL
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							type: 'separator',
 | 
				
			||||||
 | 
							visible: isLink || props.mediaType === 'image'
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							label: t.__('Services'),
 | 
				
			||||||
 | 
							visible: process.platform === 'darwin',
 | 
				
			||||||
 | 
							role: 'services'
 | 
				
			||||||
 | 
						}];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (props.misspelledWord) {
 | 
				
			||||||
 | 
							if (props.dictionarySuggestions.length > 0) {
 | 
				
			||||||
 | 
								const suggestions: Electron.MenuItemConstructorOptions[] = props.dictionarySuggestions.map((suggestion: string) => makeSuggestion(suggestion));
 | 
				
			||||||
 | 
								menuTemplate = suggestions.concat(menuTemplate);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								menuTemplate.unshift({
 | 
				
			||||||
 | 
									label: t.__('No Suggestion Found'),
 | 
				
			||||||
 | 
									enabled: false
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Hide the invisible separators on Linux and Windows
 | 
				
			||||||
 | 
						// Electron has a bug which ignores visible: false parameter for separator menuitems. So we remove them here.
 | 
				
			||||||
 | 
						// https://github.com/electron/electron/issues/5869
 | 
				
			||||||
 | 
						// https://github.com/electron/electron/issues/6906
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const filteredMenuTemplate = menuTemplate.filter(menuItem => menuItem.visible ?? true);
 | 
				
			||||||
 | 
						const menu = Menu.buildFromTemplate(filteredMenuTemplate);
 | 
				
			||||||
 | 
						menu.popup();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Tab, {TabProps} from './tab';
 | 
					import Tab, {TabProps} from './tab';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class FunctionalTab extends Tab {
 | 
					export default class FunctionalTab extends Tab {
 | 
				
			||||||
@@ -8,19 +10,21 @@ export default class FunctionalTab extends Tab {
 | 
				
			|||||||
		this.init();
 | 
							this.init();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `<div class="tab functional-tab" data-tab-id="${this.props.tabIndex}">
 | 
							return htmlEscape`
 | 
				
			||||||
					<div class="server-tab-badge close-button">
 | 
								<div class="tab functional-tab" data-tab-id="${this.props.tabIndex}">
 | 
				
			||||||
						<i class="material-icons">close</i>
 | 
									<div class="server-tab-badge close-button">
 | 
				
			||||||
					</div>
 | 
										<i class="material-icons">close</i>
 | 
				
			||||||
					<div class="server-tab">
 | 
									</div>
 | 
				
			||||||
						<i class="material-icons">${this.props.materialIcon}</i>
 | 
									<div class="server-tab">
 | 
				
			||||||
					</div>
 | 
										<i class="material-icons">${this.props.materialIcon}</i>
 | 
				
			||||||
				</div>`;
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.$el = this.generateNodeFromTemplate(this.template());
 | 
							this.$el = this.generateNodeFromHTML(this.templateHTML());
 | 
				
			||||||
		if (this.props.name !== 'Settings') {
 | 
							if (this.props.name !== 'Settings') {
 | 
				
			||||||
			this.props.$root.append(this.$el);
 | 
								this.props.$root.append(this.$el);
 | 
				
			||||||
			this.$closeButton = this.$el.querySelectorAll('.server-tab-badge')[0];
 | 
								this.$closeButton = this.$el.querySelectorAll('.server-tab-badge')[0];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
import {ipcRenderer, remote} from 'electron';
 | 
					import {ipcRenderer, remote} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as LinkUtil from '../utils/link-util';
 | 
					 | 
				
			||||||
import * as ConfigUtil from '../utils/config-util';
 | 
					import * as ConfigUtil from '../utils/config-util';
 | 
				
			||||||
 | 
					import * as LinkUtil from '../utils/link-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import type WebView from './webview';
 | 
					import type WebView from './webview';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {shell, app} = remote;
 | 
					const {shell, app} = remote;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,11 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Tab, {TabProps} from './tab';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as SystemUtil from '../utils/system-util';
 | 
					import * as SystemUtil from '../utils/system-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Tab, {TabProps} from './tab';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class ServerTab extends Tab {
 | 
					export default class ServerTab extends Tab {
 | 
				
			||||||
	$badge: Element;
 | 
						$badge: Element;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,19 +14,21 @@ export default class ServerTab extends Tab {
 | 
				
			|||||||
		this.init();
 | 
							this.init();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `<div class="tab" data-tab-id="${this.props.tabIndex}">
 | 
							return htmlEscape`
 | 
				
			||||||
					<div class="server-tooltip" style="display:none">${this.props.name}</div>
 | 
								<div class="tab" data-tab-id="${this.props.tabIndex}">
 | 
				
			||||||
					<div class="server-tab-badge"></div>
 | 
									<div class="server-tooltip" style="display:none">${this.props.name}</div>
 | 
				
			||||||
					<div class="server-tab">
 | 
									<div class="server-tab-badge"></div>
 | 
				
			||||||
					<img class="server-icons" src='${this.props.icon}'/>
 | 
									<div class="server-tab">
 | 
				
			||||||
					</div>
 | 
										<img class="server-icons" src="${this.props.icon}"/>
 | 
				
			||||||
					<div class="server-tab-shortcut">${this.generateShortcutText()}</div>
 | 
									</div>
 | 
				
			||||||
				</div>`;
 | 
									<div class="server-tab-shortcut">${this.generateShortcutText()}</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.$el = this.generateNodeFromTemplate(this.template());
 | 
							this.$el = this.generateNodeFromHTML(this.templateHTML());
 | 
				
			||||||
		this.props.$root.append(this.$el);
 | 
							this.props.$root.append(this.$el);
 | 
				
			||||||
		this.registerListeners();
 | 
							this.registerListeners();
 | 
				
			||||||
		this.$badge = this.$el.querySelectorAll('.server-tab-badge')[0];
 | 
							this.$badge = this.$el.querySelectorAll('.server-tab-badge')[0];
 | 
				
			||||||
@@ -32,7 +37,7 @@ export default class ServerTab extends Tab {
 | 
				
			|||||||
	updateBadge(count: number): void {
 | 
						updateBadge(count: number): void {
 | 
				
			||||||
		if (count > 0) {
 | 
							if (count > 0) {
 | 
				
			||||||
			const formattedCount = count > 999 ? '1K+' : count.toString();
 | 
								const formattedCount = count > 999 ? '1K+' : count.toString();
 | 
				
			||||||
			this.$badge.innerHTML = formattedCount;
 | 
								this.$badge.textContent = formattedCount;
 | 
				
			||||||
			this.$badge.classList.add('active');
 | 
								this.$badge.classList.add('active');
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this.$badge.classList.remove('active');
 | 
								this.$badge.classList.remove('active');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
import WebView from './webview';
 | 
					 | 
				
			||||||
import BaseComponent from './base';
 | 
					import BaseComponent from './base';
 | 
				
			||||||
 | 
					import WebView from './webview';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface TabProps {
 | 
					export interface TabProps {
 | 
				
			||||||
	role: string;
 | 
						role: string;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,14 @@
 | 
				
			|||||||
import {ipcRenderer, remote} from 'electron';
 | 
					import {ipcRenderer, remote} from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import path from 'path';
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from '../utils/config-util';
 | 
					import * as ConfigUtil from '../utils/config-util';
 | 
				
			||||||
import * as SystemUtil from '../utils/system-util';
 | 
					import * as SystemUtil from '../utils/system-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseComponent from './base';
 | 
					import BaseComponent from './base';
 | 
				
			||||||
 | 
					import {contextMenu} from './context-menu';
 | 
				
			||||||
import handleExternalLink from './handle-external-link';
 | 
					import handleExternalLink from './handle-external-link';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {app, dialog} = remote;
 | 
					const {app, dialog} = remote;
 | 
				
			||||||
@@ -24,7 +28,6 @@ interface WebViewProps {
 | 
				
			|||||||
	nodeIntegration: boolean;
 | 
						nodeIntegration: boolean;
 | 
				
			||||||
	preload: boolean;
 | 
						preload: boolean;
 | 
				
			||||||
	onTitleChange: () => void;
 | 
						onTitleChange: () => void;
 | 
				
			||||||
	ignoreCerts?: boolean;
 | 
					 | 
				
			||||||
	hasPermission?: (origin: string, permission: string) => boolean;
 | 
						hasPermission?: (origin: string, permission: string) => boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -49,21 +52,27 @@ export default class WebView extends BaseComponent {
 | 
				
			|||||||
		this.$webviewsContainer = document.querySelector('#webviews-container').classList;
 | 
							this.$webviewsContainer = document.querySelector('#webviews-container').classList;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `<webview
 | 
							return htmlEscape`
 | 
				
			||||||
					class="disabled"
 | 
								<webview
 | 
				
			||||||
					data-tab-id="${this.props.tabIndex}"
 | 
									class="disabled"
 | 
				
			||||||
					src="${this.props.url}"
 | 
									data-tab-id="${this.props.tabIndex}"
 | 
				
			||||||
					${this.props.nodeIntegration ? 'nodeIntegration' : ''}
 | 
									src="${this.props.url}"
 | 
				
			||||||
					${this.props.preload ? 'preload="js/preload.js"' : ''}
 | 
									` + (this.props.nodeIntegration ? 'nodeIntegration' : '') + htmlEscape`
 | 
				
			||||||
					partition="persist:webviewsession"
 | 
									` + (this.props.preload ? 'preload="js/preload.js"' : '') + htmlEscape`
 | 
				
			||||||
					name="${this.props.name}"
 | 
									partition="persist:webviewsession"
 | 
				
			||||||
					webpreferences="${this.props.nodeIntegration ? '' : 'contextIsolation, '}javascript=yes">
 | 
									name="${this.props.name}"
 | 
				
			||||||
				</webview>`;
 | 
									webpreferences="
 | 
				
			||||||
 | 
										${this.props.nodeIntegration ? '' : 'contextIsolation,'}
 | 
				
			||||||
 | 
										${ConfigUtil.getConfigItem('enableSpellchecker') ? 'spellcheck,' : ''}
 | 
				
			||||||
 | 
										javascript
 | 
				
			||||||
 | 
									">
 | 
				
			||||||
 | 
								</webview>
 | 
				
			||||||
 | 
							`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.$el = this.generateNodeFromTemplate(this.template()) as Electron.WebviewTag;
 | 
							this.$el = this.generateNodeFromHTML(this.templateHTML()) as Electron.WebviewTag;
 | 
				
			||||||
		this.domReady = new Promise(resolve => {
 | 
							this.domReady = new Promise(resolve => {
 | 
				
			||||||
			this.$el.addEventListener('dom-ready', () => resolve(), true);
 | 
								this.$el.addEventListener('dom-ready', () => resolve(), true);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
@@ -118,6 +127,11 @@ export default class WebView extends BaseComponent {
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.$el.addEventListener('dom-ready', () => {
 | 
							this.$el.addEventListener('dom-ready', () => {
 | 
				
			||||||
 | 
								const webContents = remote.webContents.fromId(this.$el.getWebContentsId());
 | 
				
			||||||
 | 
								webContents.addListener('context-menu', (event, menuParameters) => {
 | 
				
			||||||
 | 
									contextMenu(webContents, event, menuParameters);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (this.props.role === 'server') {
 | 
								if (this.props.role === 'server') {
 | 
				
			||||||
				this.$el.classList.add('onload');
 | 
									this.$el.classList.add('onload');
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -207,7 +221,7 @@ export default class WebView extends BaseComponent {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	focus(): void {
 | 
						focus(): void {
 | 
				
			||||||
		// Focus Webview and it's contents when Window regain focus.
 | 
							// Focus Webview and it's contents when Window regain focus.
 | 
				
			||||||
		const webContents = this.$el.getWebContents();
 | 
							const webContents = remote.webContents.fromId(this.$el.getWebContentsId());
 | 
				
			||||||
		// HACK: webContents.isFocused() seems to be true even without the element
 | 
							// HACK: webContents.isFocused() seems to be true even without the element
 | 
				
			||||||
		// being in focus. So, we check against `document.activeElement`.
 | 
							// being in focus. So, we check against `document.activeElement`.
 | 
				
			||||||
		if (webContents && this.$el !== document.activeElement) {
 | 
							if (webContents && this.$el !== document.activeElement) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import {EventEmitter} from 'events';
 | 
					import {EventEmitter} from 'events';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {ClipboardDecrypterImpl} from './clipboard-decrypter';
 | 
					import {ClipboardDecrypterImpl} from './clipboard-decrypter';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
import {remote} from 'electron';
 | 
					import {remote} from 'electron';
 | 
				
			||||||
import SendFeedback from '@electron-elements/send-feedback';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import path from 'path';
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import SendFeedback from '@electron-elements/send-feedback';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {app} = remote;
 | 
					const {app} = remote;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,48 +11,20 @@ export const sendFeedback: SendFeedback = document.querySelector('send-feedback'
 | 
				
			|||||||
export const feedbackHolder = sendFeedback.parentElement;
 | 
					export const feedbackHolder = sendFeedback.parentElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Make the button color match zulip app's theme
 | 
					// Make the button color match zulip app's theme
 | 
				
			||||||
sendFeedback.customStyles = `
 | 
					sendFeedback.customStylesheet = 'css/feedback.css';
 | 
				
			||||||
button:hover, button:focus {
 | 
					 | 
				
			||||||
  border-color: #4EBFAC;
 | 
					 | 
				
			||||||
  color: #4EBFAC;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
button:active {
 | 
					// Customize the fields of custom elements
 | 
				
			||||||
  background-color: #f1f1f1;
 | 
					 | 
				
			||||||
  color: #4EBFAC;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
button {
 | 
					 | 
				
			||||||
  background-color: #4EBFAC;
 | 
					 | 
				
			||||||
  border-color: #4EBFAC;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* eslint-disable no-multi-str */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// customize the fields of custom elements
 | 
					 | 
				
			||||||
sendFeedback.title = 'Report Issue';
 | 
					sendFeedback.title = 'Report Issue';
 | 
				
			||||||
sendFeedback.titleLabel = 'Issue title:';
 | 
					sendFeedback.titleLabel = 'Issue title:';
 | 
				
			||||||
sendFeedback.titlePlaceholder = 'Enter issue title';
 | 
					sendFeedback.titlePlaceholder = 'Enter issue title';
 | 
				
			||||||
sendFeedback.textareaLabel = 'Describe the issue:';
 | 
					sendFeedback.textareaLabel = 'Describe the issue:';
 | 
				
			||||||
sendFeedback.textareaPlaceholder = 'Succinctly describe your issue and steps to reproduce it...\n\n\
 | 
					sendFeedback.textareaPlaceholder = 'Succinctly describe your issue and steps to reproduce it...';
 | 
				
			||||||
---\n\
 | 
					 | 
				
			||||||
<!-- Please Include: -->\n\
 | 
					 | 
				
			||||||
- **Operating System**:\n\
 | 
					 | 
				
			||||||
  - [ ] Windows\n\
 | 
					 | 
				
			||||||
  - [ ] Linux/Ubuntu\n\
 | 
					 | 
				
			||||||
  - [ ] macOS\n\
 | 
					 | 
				
			||||||
- **Clear steps to reproduce the issue**:\n\
 | 
					 | 
				
			||||||
- **Relevant error messages and/or screenshots**:\n\
 | 
					 | 
				
			||||||
';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* eslint-enable no-multi-str */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
sendFeedback.buttonLabel = 'Report Issue';
 | 
					sendFeedback.buttonLabel = 'Report Issue';
 | 
				
			||||||
sendFeedback.loaderSuccessText = '';
 | 
					sendFeedback.loaderSuccessText = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sendFeedback.useReporter('emailReporter', {
 | 
					sendFeedback.useReporter('emailReporter', {
 | 
				
			||||||
	email: 'akash@zulipchat.com'
 | 
						email: 'support@zulip.com'
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
feedbackHolder.addEventListener('click', (event: Event) => {
 | 
					feedbackHolder.addEventListener('click', (event: Event) => {
 | 
				
			||||||
@@ -69,6 +41,10 @@ sendFeedback.addEventListener('feedback-submitted', () => {
 | 
				
			|||||||
	}, 1000);
 | 
						}, 1000);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sendFeedback.addEventListener('feedback-cancelled', () => {
 | 
				
			||||||
 | 
						feedbackHolder.classList.remove('show');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dataDir = app.getPath('userData');
 | 
					const dataDir = app.getPath('userData');
 | 
				
			||||||
const logsDir = path.join(dataDir, '/Logs');
 | 
					const logsDir = path.join(dataDir, '/Logs');
 | 
				
			||||||
sendFeedback.logs.push(...fs.readdirSync(logsDir).map(file => path.join(logsDir, file)));
 | 
					sendFeedback.logs.push(...fs.readdirSync(logsDir).map(file => path.join(logsDir, file)));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,9 +52,7 @@ interface CompatElectronBridge extends ElectronBridge {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		const {page_params} = zulipWindow;
 | 
							const {page_params} = zulipWindow;
 | 
				
			||||||
		if (page_params) {
 | 
							if (page_params) {
 | 
				
			||||||
			electron_bridge.send_event('zulip-loaded', {
 | 
								electron_bridge.send_event('zulip-loaded');
 | 
				
			||||||
				serverLanguage: page_params.default_language
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	})();
 | 
						})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,27 +1,27 @@
 | 
				
			|||||||
import {ipcRenderer, remote, clipboard} from 'electron';
 | 
					import {ipcRenderer, remote, clipboard} from 'electron';
 | 
				
			||||||
import {feedbackHolder} from './feedback';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
import escape from 'escape-html';
 | 
					
 | 
				
			||||||
import isDev from 'electron-is-dev';
 | 
					import isDev from 'electron-is-dev';
 | 
				
			||||||
const {session, app, Menu, dialog} = remote;
 | 
					
 | 
				
			||||||
 | 
					import * as Messages from '../../resources/messages';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import FunctionalTab from './components/functional-tab';
 | 
				
			||||||
 | 
					import ServerTab from './components/server-tab';
 | 
				
			||||||
 | 
					import WebView from './components/webview';
 | 
				
			||||||
 | 
					import {feedbackHolder} from './feedback';
 | 
				
			||||||
 | 
					import * as ConfigUtil from './utils/config-util';
 | 
				
			||||||
 | 
					import * as DNDUtil from './utils/dnd-util';
 | 
				
			||||||
 | 
					import type {DNDSettings} from './utils/dnd-util';
 | 
				
			||||||
 | 
					import * as DomainUtil from './utils/domain-util';
 | 
				
			||||||
 | 
					import * as EnterpriseUtil from './utils/enterprise-util';
 | 
				
			||||||
 | 
					import * as LinkUtil from './utils/link-util';
 | 
				
			||||||
 | 
					import Logger from './utils/logger-util';
 | 
				
			||||||
 | 
					import ReconnectUtil from './utils/reconnect-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line import/no-unassigned-import
 | 
					// eslint-disable-next-line import/no-unassigned-import
 | 
				
			||||||
import './tray';
 | 
					import './tray';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as DomainUtil from './utils/domain-util';
 | 
					const {session, app, Menu, dialog} = remote;
 | 
				
			||||||
import WebView from './components/webview';
 | 
					 | 
				
			||||||
import ServerTab from './components/server-tab';
 | 
					 | 
				
			||||||
import FunctionalTab from './components/functional-tab';
 | 
					 | 
				
			||||||
import * as ConfigUtil from './utils/config-util';
 | 
					 | 
				
			||||||
import * as DNDUtil from './utils/dnd-util';
 | 
					 | 
				
			||||||
import ReconnectUtil from './utils/reconnect-util';
 | 
					 | 
				
			||||||
import Logger from './utils/logger-util';
 | 
					 | 
				
			||||||
import * as CommonUtil from './utils/common-util';
 | 
					 | 
				
			||||||
import * as EnterpriseUtil from './utils/enterprise-util';
 | 
					 | 
				
			||||||
import * as LinkUtil from './utils/link-util';
 | 
					 | 
				
			||||||
import * as Messages from '../../resources/messages';
 | 
					 | 
				
			||||||
import type {DNDSettings} from './utils/dnd-util';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface FunctionalTabProps {
 | 
					interface FunctionalTabProps {
 | 
				
			||||||
	name: string;
 | 
						name: string;
 | 
				
			||||||
@@ -50,6 +50,7 @@ interface SettingsOptions extends DNDSettings {
 | 
				
			|||||||
	quitOnClose: boolean;
 | 
						quitOnClose: boolean;
 | 
				
			||||||
	promptDownload: boolean;
 | 
						promptDownload: boolean;
 | 
				
			||||||
	dockBouncing?: boolean;
 | 
						dockBouncing?: boolean;
 | 
				
			||||||
 | 
						spellcheckerLanguages?: string[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = new Logger({
 | 
					const logger = new Logger({
 | 
				
			||||||
@@ -58,7 +59,14 @@ const logger = new Logger({
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const rendererDirectory = path.resolve(__dirname, '..');
 | 
					const rendererDirectory = path.resolve(__dirname, '..');
 | 
				
			||||||
export type ServerOrFunctionalTab = ServerTab | FunctionalTab;
 | 
					type ServerOrFunctionalTab = ServerTab | FunctionalTab;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface TabData {
 | 
				
			||||||
 | 
						role: string;
 | 
				
			||||||
 | 
						name: string;
 | 
				
			||||||
 | 
						index: number;
 | 
				
			||||||
 | 
						webviewName: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ServerManagerView {
 | 
					class ServerManagerView {
 | 
				
			||||||
	$addServerButton: HTMLButtonElement;
 | 
						$addServerButton: HTMLButtonElement;
 | 
				
			||||||
@@ -115,7 +123,7 @@ class ServerManagerView {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		this.$fullscreenPopup = document.querySelector('#fullscreen-popup');
 | 
							this.$fullscreenPopup = document.querySelector('#fullscreen-popup');
 | 
				
			||||||
		this.$fullscreenEscapeKey = process.platform === 'darwin' ? '^⌘F' : 'F11';
 | 
							this.$fullscreenEscapeKey = process.platform === 'darwin' ? '^⌘F' : 'F11';
 | 
				
			||||||
		this.$fullscreenPopup.innerHTML = `Press ${this.$fullscreenEscapeKey} to exit full screen`;
 | 
							this.$fullscreenPopup.textContent = `Press ${this.$fullscreenEscapeKey} to exit full screen`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.loading = new Set();
 | 
							this.loading = new Set();
 | 
				
			||||||
		this.activeTabIndex = -1;
 | 
							this.activeTabIndex = -1;
 | 
				
			||||||
@@ -137,6 +145,7 @@ class ServerManagerView {
 | 
				
			|||||||
		await this.initTabs();
 | 
							await this.initTabs();
 | 
				
			||||||
		this.initActions();
 | 
							this.initActions();
 | 
				
			||||||
		this.registerIpcs();
 | 
							this.registerIpcs();
 | 
				
			||||||
 | 
							ipcRenderer.send('set-spellcheck-langs');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async loadProxy(): Promise<void> {
 | 
						async loadProxy(): Promise<void> {
 | 
				
			||||||
@@ -179,7 +188,7 @@ class ServerManagerView {
 | 
				
			|||||||
			useSystemProxy: false,
 | 
								useSystemProxy: false,
 | 
				
			||||||
			showSidebar: true,
 | 
								showSidebar: true,
 | 
				
			||||||
			badgeOption: true,
 | 
								badgeOption: true,
 | 
				
			||||||
			startAtLogin: true,
 | 
								startAtLogin: false,
 | 
				
			||||||
			startMinimized: false,
 | 
								startMinimized: false,
 | 
				
			||||||
			enableSpellchecker: true,
 | 
								enableSpellchecker: true,
 | 
				
			||||||
			showNotification: true,
 | 
								showNotification: true,
 | 
				
			||||||
@@ -214,6 +223,7 @@ class ServerManagerView {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if (process.platform !== 'darwin') {
 | 
							if (process.platform !== 'darwin') {
 | 
				
			||||||
			settingOptions.autoHideMenubar = false;
 | 
								settingOptions.autoHideMenubar = false;
 | 
				
			||||||
 | 
								settingOptions.spellcheckerLanguages = ['en-US'];
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (const [setting, value] of Object.entries(settingOptions)) {
 | 
							for (const [setting, value] of Object.entries(settingOptions)) {
 | 
				
			||||||
@@ -346,7 +356,7 @@ class ServerManagerView {
 | 
				
			|||||||
		this.tabs.push(new ServerTab({
 | 
							this.tabs.push(new ServerTab({
 | 
				
			||||||
			role: 'server',
 | 
								role: 'server',
 | 
				
			||||||
			icon: server.icon,
 | 
								icon: server.icon,
 | 
				
			||||||
			name: CommonUtil.decodeString(server.alias),
 | 
								name: server.alias,
 | 
				
			||||||
			$root: this.$tabsContainer,
 | 
								$root: this.$tabsContainer,
 | 
				
			||||||
			onClick: this.activateLastTab.bind(this, index),
 | 
								onClick: this.activateLastTab.bind(this, index),
 | 
				
			||||||
			index,
 | 
								index,
 | 
				
			||||||
@@ -359,13 +369,10 @@ class ServerManagerView {
 | 
				
			|||||||
				tabIndex,
 | 
									tabIndex,
 | 
				
			||||||
				url: server.url,
 | 
									url: server.url,
 | 
				
			||||||
				role: 'server',
 | 
									role: 'server',
 | 
				
			||||||
				name: CommonUtil.decodeString(server.alias),
 | 
									name: server.alias,
 | 
				
			||||||
				ignoreCerts: server.ignoreCerts,
 | 
					 | 
				
			||||||
				hasPermission: (origin: string, permission: string) =>
 | 
									hasPermission: (origin: string, permission: string) =>
 | 
				
			||||||
					origin === server.url && permission === 'notifications',
 | 
										origin === server.url && permission === 'notifications',
 | 
				
			||||||
				isActive: () => {
 | 
									isActive: () => index === this.activeTabIndex,
 | 
				
			||||||
					return index === this.activeTabIndex;
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				switchLoading: (loading: boolean, url: string) => {
 | 
									switchLoading: (loading: boolean, url: string) => {
 | 
				
			||||||
					if (loading) {
 | 
										if (loading) {
 | 
				
			||||||
						this.loading.add(url);
 | 
											this.loading.add(url);
 | 
				
			||||||
@@ -454,7 +461,7 @@ class ServerManagerView {
 | 
				
			|||||||
		const $parent = $img.parentElement;
 | 
							const $parent = $img.parentElement;
 | 
				
			||||||
		const $container = $parent.parentElement;
 | 
							const $container = $parent.parentElement;
 | 
				
			||||||
		const webviewId = $container.dataset.tabId;
 | 
							const webviewId = $container.dataset.tabId;
 | 
				
			||||||
		const $webview = document.querySelector(`webview[data-tab-id="${webviewId}"]`);
 | 
							const $webview = document.querySelector(`webview[data-tab-id="${CSS.escape(webviewId)}"]`);
 | 
				
			||||||
		const realmName = $webview.getAttribute('name');
 | 
							const realmName = $webview.getAttribute('name');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (realmName === null) {
 | 
							if (realmName === null) {
 | 
				
			||||||
@@ -490,7 +497,7 @@ class ServerManagerView {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onHover(index: number): void {
 | 
						onHover(index: number): void {
 | 
				
			||||||
		// `this.$serverIconTooltip[index].innerHTML` already has realm name, so we are just
 | 
							// `this.$serverIconTooltip[index].textContent` already has realm name, so we are just
 | 
				
			||||||
		// removing the style.
 | 
							// removing the style.
 | 
				
			||||||
		this.$serverIconTooltip[index].removeAttribute('style');
 | 
							this.$serverIconTooltip[index].removeAttribute('style');
 | 
				
			||||||
		// To handle position of servers' tooltip due to scrolling of list of organizations
 | 
							// To handle position of servers' tooltip due to scrolling of list of organizations
 | 
				
			||||||
@@ -530,9 +537,7 @@ class ServerManagerView {
 | 
				
			|||||||
				url: tabProps.url,
 | 
									url: tabProps.url,
 | 
				
			||||||
				role: 'function',
 | 
									role: 'function',
 | 
				
			||||||
				name: tabProps.name,
 | 
									name: tabProps.name,
 | 
				
			||||||
				isActive: () => {
 | 
									isActive: () => this.functionalTabs.get(tabProps.name) === this.activeTabIndex,
 | 
				
			||||||
					return this.functionalTabs.get(tabProps.name) === this.activeTabIndex;
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				switchLoading: (loading: boolean, url: string) => {
 | 
									switchLoading: (loading: boolean, url: string) => {
 | 
				
			||||||
					if (loading) {
 | 
										if (loading) {
 | 
				
			||||||
						this.loading.add(url);
 | 
											this.loading.add(url);
 | 
				
			||||||
@@ -592,19 +597,13 @@ class ServerManagerView {
 | 
				
			|||||||
	// not crash app when this.tabs is passed into
 | 
						// not crash app when this.tabs is passed into
 | 
				
			||||||
	// ipcRenderer. Something about webview, and props.webview
 | 
						// ipcRenderer. Something about webview, and props.webview
 | 
				
			||||||
	// properties in ServerTab causes the app to crash.
 | 
						// properties in ServerTab causes the app to crash.
 | 
				
			||||||
	get tabsForIpc(): ServerOrFunctionalTab[] {
 | 
						get tabsForIpc(): TabData[] {
 | 
				
			||||||
		const tabs: ServerOrFunctionalTab[] = [];
 | 
							return this.tabs.map(tab => ({
 | 
				
			||||||
		this.tabs.forEach((tab: ServerOrFunctionalTab) => {
 | 
								role: tab.props.role,
 | 
				
			||||||
			const proto = Object.create(Object.getPrototypeOf(tab));
 | 
								name: tab.props.name,
 | 
				
			||||||
			const tabClone = Object.assign(proto, tab);
 | 
								index: tab.props.index,
 | 
				
			||||||
 | 
								webviewName: tab.webview.props.name
 | 
				
			||||||
			tabClone.webview = {props: {}};
 | 
							}));
 | 
				
			||||||
			tabClone.webview.props.name = tab.webview.props.name;
 | 
					 | 
				
			||||||
			delete tabClone.props.webview;
 | 
					 | 
				
			||||||
			tabs.push(tabClone);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return tabs;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	activateTab(index: number, hideOldTab = true): void {
 | 
						activateTab(index: number, hideOldTab = true): void {
 | 
				
			||||||
@@ -629,7 +628,7 @@ class ServerManagerView {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			this.tabs[index].webview.canGoBackButton();
 | 
								this.tabs[index].webview.canGoBackButton();
 | 
				
			||||||
		} catch (_) {
 | 
							} catch {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.activeTabIndex = index;
 | 
							this.activeTabIndex = index;
 | 
				
			||||||
@@ -683,8 +682,8 @@ class ServerManagerView {
 | 
				
			|||||||
		this.functionalTabs.clear();
 | 
							this.functionalTabs.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Clear DOM elements
 | 
							// Clear DOM elements
 | 
				
			||||||
		this.$tabsContainer.innerHTML = '';
 | 
							this.$tabsContainer.textContent = '';
 | 
				
			||||||
		this.$webviewsContainer.innerHTML = '';
 | 
							this.$webviewsContainer.textContent = '';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async reloadView(): Promise<void> {
 | 
						async reloadView(): Promise<void> {
 | 
				
			||||||
@@ -719,7 +718,7 @@ class ServerManagerView {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	updateGeneralSettings(setting: string, value: unknown): void {
 | 
						updateGeneralSettings(setting: string, value: unknown): void {
 | 
				
			||||||
		if (this.getActiveWebview()) {
 | 
							if (this.getActiveWebview()) {
 | 
				
			||||||
			const webContents = this.getActiveWebview().getWebContents();
 | 
								const webContents = remote.webContents.fromId(this.getActiveWebview().getWebContentsId());
 | 
				
			||||||
			webContents.send(setting, value);
 | 
								webContents.send(setting, value);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -816,21 +815,6 @@ class ServerManagerView {
 | 
				
			|||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ipcRenderer.on('certificate-error', (
 | 
					 | 
				
			||||||
			event: Event,
 | 
					 | 
				
			||||||
			webContentsId: number | null,
 | 
					 | 
				
			||||||
			rendererCallbackId: number
 | 
					 | 
				
			||||||
		) => {
 | 
					 | 
				
			||||||
			const ignore = webContentsId !== null &&
 | 
					 | 
				
			||||||
				this.tabs.some(
 | 
					 | 
				
			||||||
					({webview}) =>
 | 
					 | 
				
			||||||
						!webview.loading &&
 | 
					 | 
				
			||||||
						webview.$el.getWebContentsId() === webContentsId &&
 | 
					 | 
				
			||||||
						webview.props.ignoreCerts
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
			ipcRenderer.send('renderer-callback', rendererCallbackId, ignore);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ipcRenderer.on('permission-request', (
 | 
							ipcRenderer.on('permission-request', (
 | 
				
			||||||
			event: Event,
 | 
								event: Event,
 | 
				
			||||||
			{webContentsId, origin, permission}: {
 | 
								{webContentsId, origin, permission}: {
 | 
				
			||||||
@@ -846,7 +830,7 @@ class ServerManagerView {
 | 
				
			|||||||
					({webview}) =>
 | 
										({webview}) =>
 | 
				
			||||||
						!webview.loading &&
 | 
											!webview.loading &&
 | 
				
			||||||
						webview.$el.getWebContentsId() === webContentsId &&
 | 
											webview.$el.getWebContentsId() === webContentsId &&
 | 
				
			||||||
					webview.props.hasPermission?.(origin, permission)
 | 
											webview.props.hasPermission?.(origin, permission)
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			console.log(
 | 
								console.log(
 | 
				
			||||||
				grant ? 'Granted' : 'Denied', 'permissions request for',
 | 
									grant ? 'Granted' : 'Denied', 'permissions request for',
 | 
				
			||||||
@@ -867,7 +851,7 @@ class ServerManagerView {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		ipcRenderer.on('open-help', async () => {
 | 
							ipcRenderer.on('open-help', async () => {
 | 
				
			||||||
			// Open help page of current active server
 | 
								// Open help page of current active server
 | 
				
			||||||
			await LinkUtil.openBrowser(new URL('https://zulipchat.com/help/'));
 | 
								await LinkUtil.openBrowser(new URL('https://zulip.com/help/'));
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ipcRenderer.on('reload-viewer', this.reloadView.bind(this, this.tabs[this.activeTabIndex].props.index));
 | 
							ipcRenderer.on('reload-viewer', this.reloadView.bind(this, this.tabs[this.activeTabIndex].props.index));
 | 
				
			||||||
@@ -878,10 +862,6 @@ class ServerManagerView {
 | 
				
			|||||||
			ipcRenderer.send('reload-full-app');
 | 
								ipcRenderer.send('reload-full-app');
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ipcRenderer.on('clear-app-data', () => {
 | 
					 | 
				
			||||||
			ipcRenderer.send('clear-app-settings');
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ipcRenderer.on('switch-server-tab', (event: Event, index: number) => {
 | 
							ipcRenderer.on('switch-server-tab', (event: Event, index: number) => {
 | 
				
			||||||
			this.activateLastTab(index);
 | 
								this.activateLastTab(index);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
@@ -914,7 +894,7 @@ class ServerManagerView {
 | 
				
			|||||||
			webviews.forEach(webview => {
 | 
								webviews.forEach(webview => {
 | 
				
			||||||
				try {
 | 
									try {
 | 
				
			||||||
					webview.setAudioMuted(state);
 | 
										webview.setAudioMuted(state);
 | 
				
			||||||
				} catch (_) {
 | 
									} catch {
 | 
				
			||||||
					// Webview is not ready yet
 | 
										// Webview is not ready yet
 | 
				
			||||||
					webview.addEventListener('dom-ready', () => {
 | 
										webview.addEventListener('dom-ready', () => {
 | 
				
			||||||
						webview.setAudioMuted(state);
 | 
											webview.setAudioMuted(state);
 | 
				
			||||||
@@ -938,7 +918,7 @@ class ServerManagerView {
 | 
				
			|||||||
		ipcRenderer.on('toggle-dnd', (event: Event, state: boolean, newSettings: DNDSettings) => {
 | 
							ipcRenderer.on('toggle-dnd', (event: Event, state: boolean, newSettings: DNDSettings) => {
 | 
				
			||||||
			this.toggleDNDButton(state);
 | 
								this.toggleDNDButton(state);
 | 
				
			||||||
			ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent);
 | 
								ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent);
 | 
				
			||||||
			const webContents = this.getActiveWebview().getWebContents();
 | 
								const webContents = remote.webContents.fromId(this.getActiveWebview().getWebContentsId());
 | 
				
			||||||
			webContents.send('toggle-dnd', state, newSettings);
 | 
								webContents.send('toggle-dnd', state, newSettings);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -947,11 +927,11 @@ class ServerManagerView {
 | 
				
			|||||||
				if (domain.url.includes(serverURL)) {
 | 
									if (domain.url.includes(serverURL)) {
 | 
				
			||||||
					const serverTooltipSelector = '.tab .server-tooltip';
 | 
										const serverTooltipSelector = '.tab .server-tooltip';
 | 
				
			||||||
					const serverTooltips = document.querySelectorAll(serverTooltipSelector);
 | 
										const serverTooltips = document.querySelectorAll(serverTooltipSelector);
 | 
				
			||||||
					serverTooltips[index].innerHTML = escape(realmName);
 | 
										serverTooltips[index].textContent = realmName;
 | 
				
			||||||
					this.tabs[index].props.name = escape(realmName);
 | 
										this.tabs[index].props.name = realmName;
 | 
				
			||||||
					this.tabs[index].webview.props.name = realmName;
 | 
										this.tabs[index].webview.props.name = realmName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					domain.alias = escape(realmName);
 | 
										domain.alias = realmName;
 | 
				
			||||||
					DomainUtil.updateDomain(index, domain);
 | 
										DomainUtil.updateDomain(index, domain);
 | 
				
			||||||
					// Update the realm name also on the Window menu
 | 
										// Update the realm name also on the Window menu
 | 
				
			||||||
					ipcRenderer.send('update-menu', {
 | 
										ipcRenderer.send('update-menu', {
 | 
				
			||||||
@@ -990,9 +970,9 @@ class ServerManagerView {
 | 
				
			|||||||
		ipcRenderer.on('focus-webview-with-id', (event: Event, webviewId: number) => {
 | 
							ipcRenderer.on('focus-webview-with-id', (event: Event, webviewId: number) => {
 | 
				
			||||||
			const webviews: NodeListOf<Electron.WebviewTag> = document.querySelectorAll('webview');
 | 
								const webviews: NodeListOf<Electron.WebviewTag> = document.querySelectorAll('webview');
 | 
				
			||||||
			webviews.forEach(webview => {
 | 
								webviews.forEach(webview => {
 | 
				
			||||||
				const currentId = webview.getWebContents().id;
 | 
									const currentId = webview.getWebContentsId();
 | 
				
			||||||
				const tabId = webview.getAttribute('data-tab-id');
 | 
									const tabId = webview.getAttribute('data-tab-id');
 | 
				
			||||||
				const concurrentTab: HTMLButtonElement = document.querySelector(`div[data-tab-id="${tabId}"]`);
 | 
									const concurrentTab: HTMLButtonElement = document.querySelector(`div[data-tab-id="${CSS.escape(tabId)}"]`);
 | 
				
			||||||
				if (currentId === webviewId) {
 | 
									if (currentId === webviewId) {
 | 
				
			||||||
					concurrentTab.click();
 | 
										concurrentTab.click();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -1043,13 +1023,9 @@ class ServerManagerView {
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Redo and undo functionality since the default API doesn't work on macOS
 | 
							// Redo and undo functionality since the default API doesn't work on macOS
 | 
				
			||||||
		ipcRenderer.on('undo', () => {
 | 
							ipcRenderer.on('undo', () => this.getActiveWebview().undo());
 | 
				
			||||||
			return this.getActiveWebview().undo();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ipcRenderer.on('redo', () => {
 | 
							ipcRenderer.on('redo', () => this.getActiveWebview().redo());
 | 
				
			||||||
			return this.getActiveWebview().redo();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ipcRenderer.on('set-active', async () => {
 | 
							ipcRenderer.on('set-active', async () => {
 | 
				
			||||||
			const webviews: NodeListOf<Electron.WebviewTag> = document.querySelectorAll('webview');
 | 
								const webviews: NodeListOf<Electron.WebviewTag> = document.querySelectorAll('webview');
 | 
				
			||||||
@@ -1069,15 +1045,14 @@ class ServerManagerView {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
window.addEventListener('load', async () => {
 | 
					window.addEventListener('load', async () => {
 | 
				
			||||||
	// Only start electron-connect (auto reload on change) when its ran
 | 
						// Only start electron-connect (auto reload on change) when its ran
 | 
				
			||||||
	// from `npm run dev` or `gulp dev` and not from `npm start` when
 | 
						// from `npm run dev` or `gulp dev` and not from `npm start`
 | 
				
			||||||
	// app is started `npm start` main process's proces.argv will have
 | 
						if (isDev && remote.getGlobal('process').argv.includes('--electron-connect')) {
 | 
				
			||||||
	// `--no-electron-connect`
 | 
							// eslint-disable-next-line node/no-unsupported-features/es-syntax
 | 
				
			||||||
	if (isDev && !remote.getGlobal('process').argv.includes('--no-electron-connect')) {
 | 
							(await import('electron-connect')).client.create();
 | 
				
			||||||
		require('electron-connect').client.create();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const serverManagerView = new ServerManagerView();
 | 
						const serverManagerView = new ServerManagerView();
 | 
				
			||||||
	await serverManagerView.init();
 | 
						await serverManagerView.init();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {};
 | 
					export { };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,14 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import MacNotifier from 'node-mac-notifier';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import electron_bridge from '../electron-bridge';
 | 
				
			||||||
 | 
					import * as ConfigUtil from '../utils/config-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
	appId, customReply, focusCurrentServer, parseReply
 | 
						appId, customReply, focusCurrentServer, parseReply
 | 
				
			||||||
} from './helpers';
 | 
					} from './helpers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import MacNotifier from 'node-mac-notifier';
 | 
					 | 
				
			||||||
import * as ConfigUtil from '../utils/config-util';
 | 
					 | 
				
			||||||
import electron_bridge from '../electron-bridge';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ReplyHandler = (response: string) => void;
 | 
					type ReplyHandler = (response: string) => void;
 | 
				
			||||||
type ClickHandler = () => void;
 | 
					type ClickHandler = () => void;
 | 
				
			||||||
let replyHandler: ReplyHandler;
 | 
					let replyHandler: ReplyHandler;
 | 
				
			||||||
@@ -47,7 +49,7 @@ class DarwinNotification {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static requestPermission(): void {
 | 
						static requestPermission(): void {
 | 
				
			||||||
		return; // eslint-disable-line no-useless-return
 | 
							// Do nothing
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Override default Notification permission
 | 
						// Override default Notification permission
 | 
				
			||||||
@@ -104,7 +106,7 @@ class DarwinNotification {
 | 
				
			|||||||
	// Method specific to notification api
 | 
						// Method specific to notification api
 | 
				
			||||||
	// used by zulip
 | 
						// used by zulip
 | 
				
			||||||
	close(): void {
 | 
						close(): void {
 | 
				
			||||||
		return; // eslint-disable-line no-useless-return
 | 
							// Do nothing
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
import {focusCurrentServer} from './helpers';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from '../utils/config-util';
 | 
					import * as ConfigUtil from '../utils/config-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {focusCurrentServer} from './helpers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const NativeNotification = window.Notification;
 | 
					const NativeNotification = window.Notification;
 | 
				
			||||||
export default class BaseNotification extends NativeNotification {
 | 
					export default class BaseNotification extends NativeNotification {
 | 
				
			||||||
	constructor(title: string, options: NotificationOptions) {
 | 
						constructor(title: string, options: NotificationOptions) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
import {remote} from 'electron';
 | 
					import {remote} from 'electron';
 | 
				
			||||||
import electron_bridge from '../electron-bridge';
 | 
					 | 
				
			||||||
import {appId, loadBots} from './helpers';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import DefaultNotification from './default-notification';
 | 
					import DefaultNotification from './default-notification';
 | 
				
			||||||
 | 
					import {appId} from './helpers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {app} = remote;
 | 
					const {app} = remote;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
 | 
					// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
 | 
				
			||||||
@@ -67,7 +67,3 @@ export function newNotification(
 | 
				
			|||||||
		actions: notification.actions
 | 
							actions: notification.actions
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
electron_bridge.once('zulip-loaded', async () => {
 | 
					 | 
				
			||||||
	await loadBots();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
'use-strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import {remote, OpenDialogOptions} from 'electron';
 | 
					import {remote, OpenDialogOptions} from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseComponent from '../../components/base';
 | 
					import BaseComponent from '../../components/base';
 | 
				
			||||||
import * as CertificateUtil from '../../utils/certificate-util';
 | 
					import * as CertificateUtil from '../../utils/certificate-util';
 | 
				
			||||||
import * as DomainUtil from '../../utils/domain-util';
 | 
					import * as DomainUtil from '../../utils/domain-util';
 | 
				
			||||||
@@ -26,8 +26,8 @@ export default class AddCertificate extends BaseComponent {
 | 
				
			|||||||
		this._certFile = '';
 | 
							this._certFile = '';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
			<div class="settings-card certificates-card">
 | 
								<div class="settings-card certificates-card">
 | 
				
			||||||
				<div class="certificate-input">
 | 
									<div class="certificate-input">
 | 
				
			||||||
					<div>${t.__('Organization URL')}</div>
 | 
										<div>${t.__('Organization URL')}</div>
 | 
				
			||||||
@@ -42,7 +42,7 @@ export default class AddCertificate extends BaseComponent {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.$addCertificate = this.generateNodeFromTemplate(this.template());
 | 
							this.$addCertificate = this.generateNodeFromHTML(this.templateHTML());
 | 
				
			||||||
		this.props.$root.append(this.$addCertificate);
 | 
							this.props.$root.append(this.$addCertificate);
 | 
				
			||||||
		this.addCertificateButton = this.$addCertificate.querySelector('#add-certificate-button');
 | 
							this.addCertificateButton = this.$addCertificate.querySelector('#add-certificate-button');
 | 
				
			||||||
		this.serverUrl = this.$addCertificate.querySelectorAll('input.setting-input-value')[0] as HTMLInputElement;
 | 
							this.serverUrl = this.$addCertificate.querySelectorAll('input.setting-input-value')[0] as HTMLInputElement;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
import escape from 'escape-html';
 | 
					
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseComponent from '../../components/base';
 | 
					import BaseComponent from '../../components/base';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,9 +15,9 @@ export default class BaseSection extends BaseComponent {
 | 
				
			|||||||
	generateSettingOption(props: BaseSectionProps): void {
 | 
						generateSettingOption(props: BaseSectionProps): void {
 | 
				
			||||||
		const {$element, disabled, value, clickHandler} = props;
 | 
							const {$element, disabled, value, clickHandler} = props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		$element.innerHTML = '';
 | 
							$element.textContent = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const $optionControl = this.generateNodeFromTemplate(this.generateOptionTemplate(value, disabled));
 | 
							const $optionControl = this.generateNodeFromHTML(this.generateOptionHTML(value, disabled));
 | 
				
			||||||
		$element.append($optionControl);
 | 
							$element.append($optionControl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!disabled) {
 | 
							if (!disabled) {
 | 
				
			||||||
@@ -24,39 +25,39 @@ export default class BaseSection extends BaseComponent {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	generateOptionTemplate(settingOption: boolean, disabled?: boolean): string {
 | 
						generateOptionHTML(settingOption: boolean, disabled?: boolean): string {
 | 
				
			||||||
		const label = disabled ? '<label class="disallowed" title="Setting locked by system administrator."/>' : '<label/>';
 | 
							const labelHTML = disabled ? '<label class="disallowed" title="Setting locked by system administrator."></label>' : '<label></label>';
 | 
				
			||||||
		if (settingOption) {
 | 
							if (settingOption) {
 | 
				
			||||||
			return `
 | 
								return htmlEscape`
 | 
				
			||||||
				<div class="action">
 | 
									<div class="action">
 | 
				
			||||||
					<div class="switch">
 | 
										<div class="switch">
 | 
				
			||||||
					  <input class="toggle toggle-round" type="checkbox" checked disabled>
 | 
											<input class="toggle toggle-round" type="checkbox" checked disabled>
 | 
				
			||||||
					  ${label}
 | 
											` + labelHTML + htmlEscape`
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			`;
 | 
								`;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
				<div class="action">
 | 
								<div class="action">
 | 
				
			||||||
					<div class="switch">
 | 
									<div class="switch">
 | 
				
			||||||
					  <input class="toggle toggle-round" type="checkbox">
 | 
										<input class="toggle toggle-round" type="checkbox">
 | 
				
			||||||
					  ${label}
 | 
										` + labelHTML + htmlEscape`
 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			`;
 | 
								</div>
 | 
				
			||||||
 | 
							`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* A method that in future can be used to create dropdown menus using <select> <option> tags.
 | 
						/* A method that in future can be used to create dropdown menus using <select> <option> tags.
 | 
				
			||||||
		it needs an object which has ``key: value`` pairs and will return a string that can be appended to HTML
 | 
							it needs an object which has ``key: value`` pairs and will return a string that can be appended to HTML
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	generateSelectTemplate(options: {[key: string]: string}, className?: string, idName?: string): string {
 | 
						generateSelectHTML(options: {[key: string]: string}, className?: string, idName?: string): string {
 | 
				
			||||||
		let select = `<select class="${escape(className)}" id="${escape(idName)}">\n`;
 | 
							let html = htmlEscape`<select class="${className}" id="${idName}">\n`;
 | 
				
			||||||
		Object.keys(options).forEach(key => {
 | 
							Object.keys(options).forEach(key => {
 | 
				
			||||||
			select += `<option name="${escape(key)}" value="${escape(key)}">${escape(options[key])}</option>\n`;
 | 
								html += htmlEscape`<option name="${key}" value="${key}">${options[key]}</option>\n`;
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
		select += '</select>';
 | 
							html += '</select>';
 | 
				
			||||||
		return select;
 | 
							return html;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reloadApp(): void {
 | 
						reloadApp(): void {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,15 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseSection from './base-section';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as DomainUtil from '../../utils/domain-util';
 | 
					import * as DomainUtil from '../../utils/domain-util';
 | 
				
			||||||
import ServerInfoForm from './server-info-form';
 | 
					 | 
				
			||||||
import AddCertificate from './add-certificate';
 | 
					 | 
				
			||||||
import FindAccounts from './find-accounts';
 | 
					 | 
				
			||||||
import * as t from '../../utils/translation-util';
 | 
					import * as t from '../../utils/translation-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import AddCertificate from './add-certificate';
 | 
				
			||||||
 | 
					import BaseSection from './base-section';
 | 
				
			||||||
 | 
					import FindAccounts from './find-accounts';
 | 
				
			||||||
 | 
					import ServerInfoForm from './server-info-form';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ConnectedOrgSectionProps {
 | 
					interface ConnectedOrgSectionProps {
 | 
				
			||||||
	$root: Element;
 | 
						$root: Element;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -23,8 +26,8 @@ export default class ConnectedOrgSection extends BaseSection {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
			<div class="settings-pane" id="server-settings-pane">
 | 
								<div class="settings-pane" id="server-settings-pane">
 | 
				
			||||||
				<div class="page-title">${t.__('Connected organizations')}</div>
 | 
									<div class="page-title">${t.__('Connected organizations')}</div>
 | 
				
			||||||
				<div class="title" id="existing-servers">${t.__('All the connected orgnizations will appear here.')}</div>
 | 
									<div class="title" id="existing-servers">${t.__('All the connected orgnizations will appear here.')}</div>
 | 
				
			||||||
@@ -43,10 +46,10 @@ export default class ConnectedOrgSection extends BaseSection {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initServers(): void {
 | 
						initServers(): void {
 | 
				
			||||||
		this.props.$root.innerHTML = '';
 | 
							this.props.$root.textContent = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const servers = DomainUtil.getDomains();
 | 
							const servers = DomainUtil.getDomains();
 | 
				
			||||||
		this.props.$root.innerHTML = this.template();
 | 
							this.props.$root.innerHTML = this.templateHTML();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.$serverInfoContainer = document.querySelector('#server-info-container');
 | 
							this.$serverInfoContainer = document.querySelector('#server-info-container');
 | 
				
			||||||
		this.$existingServers = document.querySelector('#existing-servers');
 | 
							this.$existingServers = document.querySelector('#existing-servers');
 | 
				
			||||||
@@ -56,7 +59,7 @@ export default class ConnectedOrgSection extends BaseSection {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		const noServerText = t.__('All the connected orgnizations will appear here');
 | 
							const noServerText = t.__('All the connected orgnizations will appear here');
 | 
				
			||||||
		// Show noServerText if no servers are there otherwise hide it
 | 
							// Show noServerText if no servers are there otherwise hide it
 | 
				
			||||||
		this.$existingServers.innerHTML = servers.length === 0 ? noServerText : '';
 | 
							this.$existingServers.textContent = servers.length === 0 ? noServerText : '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (const [i, server] of servers.entries()) {
 | 
							for (const [i, server] of servers.entries()) {
 | 
				
			||||||
			new ServerInfoForm({
 | 
								new ServerInfoForm({
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
'use-strict';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseComponent from '../../components/base';
 | 
					import BaseComponent from '../../components/base';
 | 
				
			||||||
import * as LinkUtil from '../../utils/link-util';
 | 
					import * as LinkUtil from '../../utils/link-util';
 | 
				
			||||||
@@ -18,8 +18,8 @@ export default class FindAccounts extends BaseComponent {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
			<div class="settings-card certificate-card">
 | 
								<div class="settings-card certificate-card">
 | 
				
			||||||
				<div class="certificate-input">
 | 
									<div class="certificate-input">
 | 
				
			||||||
					<div>${t.__('Organization URL')}</div>
 | 
										<div>${t.__('Organization URL')}</div>
 | 
				
			||||||
@@ -33,7 +33,7 @@ export default class FindAccounts extends BaseComponent {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.$findAccounts = this.generateNodeFromTemplate(this.template());
 | 
							this.$findAccounts = this.generateNodeFromHTML(this.templateHTML());
 | 
				
			||||||
		this.props.$root.append(this.$findAccounts);
 | 
							this.props.$root.append(this.$findAccounts);
 | 
				
			||||||
		this.$findAccountsButton = this.$findAccounts.querySelector('#find-accounts-button');
 | 
							this.$findAccountsButton = this.$findAccounts.querySelector('#find-accounts-button');
 | 
				
			||||||
		this.$serverUrlField = this.$findAccounts.querySelectorAll('input.setting-input-value')[0] as HTMLInputElement;
 | 
							this.$serverUrlField = this.$findAccounts.querySelectorAll('input.setting-input-value')[0] as HTMLInputElement;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,16 +1,20 @@
 | 
				
			|||||||
import {ipcRenderer, remote, OpenDialogOptions} from 'electron';
 | 
					import {ipcRenderer, remote, OpenDialogOptions} from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import Tagify from '@yaireo/tagify';
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
import fs from 'fs-extra';
 | 
					import fs from 'fs-extra';
 | 
				
			||||||
 | 
					import ISO6391 from 'iso-639-1';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {app, dialog} = remote;
 | 
					import supportedLocales from '../../../../translations/supported-locales.json';
 | 
				
			||||||
const currentBrowserWindow = remote.getCurrentWindow();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import BaseSection from './base-section';
 | 
					 | 
				
			||||||
import * as ConfigUtil from '../../utils/config-util';
 | 
					import * as ConfigUtil from '../../utils/config-util';
 | 
				
			||||||
import * as EnterpriseUtil from '../../utils/enterprise-util';
 | 
					import * as EnterpriseUtil from '../../utils/enterprise-util';
 | 
				
			||||||
import * as t from '../../utils/translation-util';
 | 
					import * as t from '../../utils/translation-util';
 | 
				
			||||||
import supportedLocales from '../../../../translations/supported-locales.json';
 | 
					
 | 
				
			||||||
 | 
					import BaseSection from './base-section';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const {app, dialog, session} = remote;
 | 
				
			||||||
 | 
					const currentBrowserWindow = remote.getCurrentWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface GeneralSectionProps {
 | 
					interface GeneralSectionProps {
 | 
				
			||||||
	$root: Element;
 | 
						$root: Element;
 | 
				
			||||||
@@ -23,8 +27,8 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
            <div class="settings-pane">
 | 
					            <div class="settings-pane">
 | 
				
			||||||
                <div class="title">${t.__('Appearance')}</div>
 | 
					                <div class="title">${t.__('Appearance')}</div>
 | 
				
			||||||
                <div id="appearance-option-settings" class="settings-card">
 | 
					                <div id="appearance-option-settings" class="settings-card">
 | 
				
			||||||
@@ -93,7 +97,10 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
						<div class="setting-description">${t.__('Enable spellchecker (requires restart)')}</div>
 | 
											<div class="setting-description">${t.__('Enable spellchecker (requires restart)')}</div>
 | 
				
			||||||
						<div class="setting-control"></div>
 | 
											<div class="setting-control"></div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
 | 
										<div class="setting-row" id="spellcheck-langs" style= "display:${process.platform === 'darwin' ? 'none' : ''}"></div>
 | 
				
			||||||
 | 
										<div class="setting-row" id="note"></div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<div class="title">${t.__('Advanced')}</div>
 | 
									<div class="title">${t.__('Advanced')}</div>
 | 
				
			||||||
				<div class="settings-card">
 | 
									<div class="settings-card">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -138,12 +145,12 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
						<div class="setting-control"></div>
 | 
											<div class="setting-control"></div>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
				<div class="title">${t.__('Reset Application Data')}</div>
 | 
									<div class="title">${t.__('Factory Reset Data')}</div>
 | 
				
			||||||
                <div class="settings-card">
 | 
					                <div class="settings-card">
 | 
				
			||||||
					<div class="setting-row" id="resetdata-option">
 | 
										<div class="setting-row" id="factory-reset-option">
 | 
				
			||||||
						<div class="setting-description">${t.__('This will delete all application data including all added accounts and preferences')}
 | 
											<div class="setting-description">${t.__('Reset the application, thus deleting all the connected organizations, accounts, and certificates.')}
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
						<button class="reset-data-button red w-150">${t.__('Reset App Data')}</button>
 | 
											<button class="factory-reset-button red w-150">${t.__('Factory Reset')}</button>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
@@ -151,7 +158,7 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.props.$root.innerHTML = this.template();
 | 
							this.props.$root.innerHTML = this.templateHTML();
 | 
				
			||||||
		this.updateTrayOption();
 | 
							this.updateTrayOption();
 | 
				
			||||||
		this.updateBadgeOption();
 | 
							this.updateBadgeOption();
 | 
				
			||||||
		this.updateSilentOption();
 | 
							this.updateSilentOption();
 | 
				
			||||||
@@ -159,7 +166,7 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
		this.betaUpdateOption();
 | 
							this.betaUpdateOption();
 | 
				
			||||||
		this.updateSidebarOption();
 | 
							this.updateSidebarOption();
 | 
				
			||||||
		this.updateStartAtLoginOption();
 | 
							this.updateStartAtLoginOption();
 | 
				
			||||||
		this.updateResetDataOption();
 | 
							this.factoryReset();
 | 
				
			||||||
		this.showDesktopNotification();
 | 
							this.showDesktopNotification();
 | 
				
			||||||
		this.enableSpellchecker();
 | 
							this.enableSpellchecker();
 | 
				
			||||||
		this.minimizeOnStart();
 | 
							this.minimizeOnStart();
 | 
				
			||||||
@@ -171,6 +178,7 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
		this.updatePromptDownloadOption();
 | 
							this.updatePromptDownloadOption();
 | 
				
			||||||
		this.enableErrorReporting();
 | 
							this.enableErrorReporting();
 | 
				
			||||||
		this.setLocale();
 | 
							this.setLocale();
 | 
				
			||||||
 | 
							this.initSpellChecker();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Platform specific settings
 | 
							// Platform specific settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -356,6 +364,10 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
				const newValue = !ConfigUtil.getConfigItem('enableSpellchecker');
 | 
									const newValue = !ConfigUtil.getConfigItem('enableSpellchecker');
 | 
				
			||||||
				ConfigUtil.setConfigItem('enableSpellchecker', newValue);
 | 
									ConfigUtil.setConfigItem('enableSpellchecker', newValue);
 | 
				
			||||||
				this.enableSpellchecker();
 | 
									this.enableSpellchecker();
 | 
				
			||||||
 | 
									const spellcheckerLanguageInput: HTMLElement = document.querySelector('#spellcheck-langs');
 | 
				
			||||||
 | 
									const spellcheckerNote: HTMLElement = document.querySelector('#note');
 | 
				
			||||||
 | 
									spellcheckerLanguageInput.style.display = spellcheckerLanguageInput.style.display === 'none' ? '' : 'none';
 | 
				
			||||||
 | 
									spellcheckerNote.style.display = spellcheckerNote.style.display === 'none' ? '' : 'none';
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -372,23 +384,6 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async clearAppDataDialog(): Promise<void> {
 | 
					 | 
				
			||||||
		const clearAppDataMessage = 'By clicking proceed you will be removing all added accounts and preferences from Zulip. When the application restarts, it will be as if you are starting Zulip for the first time.';
 | 
					 | 
				
			||||||
		const getAppPath = path.join(app.getPath('appData'), app.name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		const {response} = await dialog.showMessageBox({
 | 
					 | 
				
			||||||
			type: 'warning',
 | 
					 | 
				
			||||||
			buttons: ['YES', 'NO'],
 | 
					 | 
				
			||||||
			defaultId: 0,
 | 
					 | 
				
			||||||
			message: 'Are you sure',
 | 
					 | 
				
			||||||
			detail: clearAppDataMessage
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		if (response === 0) {
 | 
					 | 
				
			||||||
			await fs.remove(getAppPath);
 | 
					 | 
				
			||||||
			ipcRenderer.send('forward-message', 'hard-reload');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	async customCssDialog(): Promise<void> {
 | 
						async customCssDialog(): Promise<void> {
 | 
				
			||||||
		const showDialogOptions: OpenDialogOptions = {
 | 
							const showDialogOptions: OpenDialogOptions = {
 | 
				
			||||||
			title: 'Select file',
 | 
								title: 'Select file',
 | 
				
			||||||
@@ -403,24 +398,16 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	updateResetDataOption(): void {
 | 
					 | 
				
			||||||
		const resetDataButton = document.querySelector('#resetdata-option .reset-data-button');
 | 
					 | 
				
			||||||
		resetDataButton.addEventListener('click', async () => {
 | 
					 | 
				
			||||||
			await this.clearAppDataDialog();
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	setLocale(): void {
 | 
						setLocale(): void {
 | 
				
			||||||
		const langDiv: HTMLSelectElement = document.querySelector('.lang-div');
 | 
							const langDiv: HTMLSelectElement = document.querySelector('.lang-div');
 | 
				
			||||||
		// This path is for the JSON file that stores key: value pairs for supported locales
 | 
							const langListHTML = this.generateSelectHTML(supportedLocales, 'lang-menu');
 | 
				
			||||||
		const langList = this.generateSelectTemplate(supportedLocales, 'lang-menu');
 | 
							langDiv.innerHTML += langListHTML;
 | 
				
			||||||
		langDiv.innerHTML += langList;
 | 
					 | 
				
			||||||
		// `langMenu` is the select-option dropdown menu formed after executing the previous command
 | 
							// `langMenu` is the select-option dropdown menu formed after executing the previous command
 | 
				
			||||||
		const langMenu: HTMLSelectElement = document.querySelector('.lang-menu');
 | 
							const langMenu: HTMLSelectElement = document.querySelector('.lang-menu');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// The next three lines set the selected language visible on the dropdown button
 | 
							// The next three lines set the selected language visible on the dropdown button
 | 
				
			||||||
		let language = ConfigUtil.getConfigItem('appLanguage');
 | 
							let language = ConfigUtil.getConfigItem('appLanguage');
 | 
				
			||||||
		language = language && langMenu.options.namedItem(language) ? language : 'en-US';
 | 
							language = language && langMenu.options.namedItem(language) ? language : 'en';
 | 
				
			||||||
		langMenu.options.namedItem(language).selected = true;
 | 
							langMenu.options.namedItem(language).selected = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		langMenu.addEventListener('change', (event: Event) => {
 | 
							langMenu.addEventListener('change', (event: Event) => {
 | 
				
			||||||
@@ -494,4 +481,103 @@ export default class GeneralSection extends BaseSection {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async factoryResetSettings(): Promise<void> {
 | 
				
			||||||
 | 
							const clearAppDataMessage = 'When the application restarts, it will be as if you have just downloaded Zulip app.';
 | 
				
			||||||
 | 
							const getAppPath = path.join(app.getPath('appData'), app.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const {response} = await dialog.showMessageBox({
 | 
				
			||||||
 | 
								type: 'warning',
 | 
				
			||||||
 | 
								buttons: ['YES', 'NO'],
 | 
				
			||||||
 | 
								defaultId: 0,
 | 
				
			||||||
 | 
								message: 'Are you sure?',
 | 
				
			||||||
 | 
								detail: clearAppDataMessage
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							if (response === 0) {
 | 
				
			||||||
 | 
								await fs.remove(getAppPath);
 | 
				
			||||||
 | 
								setTimeout(() => ipcRenderer.send('clear-app-settings'), 1000);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						factoryReset(): void {
 | 
				
			||||||
 | 
							const factoryResetButton = document.querySelector('#factory-reset-option .factory-reset-button');
 | 
				
			||||||
 | 
							factoryResetButton.addEventListener('click', async () => {
 | 
				
			||||||
 | 
								await this.factoryResetSettings();
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						initSpellChecker(): void {
 | 
				
			||||||
 | 
							// The elctron API is a no-op on macOS and macOS default spellchecker is used.
 | 
				
			||||||
 | 
							if (process.platform === 'darwin') {
 | 
				
			||||||
 | 
								const note: HTMLElement = document.querySelector('#note');
 | 
				
			||||||
 | 
								note.append(t.__('On macOS, the OS spellchecker is used.'));
 | 
				
			||||||
 | 
								note.append(document.createElement('br'));
 | 
				
			||||||
 | 
								note.append(t.__('Change the language from System Preferences → Keyboard → Text → Spelling.'));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								const note: HTMLElement = document.querySelector('#note');
 | 
				
			||||||
 | 
								note.append(t.__('You can select a maximum of 3 languages for spellchecking.'));
 | 
				
			||||||
 | 
								const spellDiv: HTMLElement = document.querySelector('#spellcheck-langs');
 | 
				
			||||||
 | 
								spellDiv.innerHTML += htmlEscape`
 | 
				
			||||||
 | 
									<div class="setting-description">${t.__('Spellchecker Languages')}</div>
 | 
				
			||||||
 | 
									<input name='spellcheck' placeholder='Enter Languages'>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const availableLanguages = session.fromPartition('persist:webviewsession').availableSpellCheckerLanguages;
 | 
				
			||||||
 | 
								let languagePairs: Map<string, string> = new Map();
 | 
				
			||||||
 | 
								availableLanguages.forEach((l: string) => {
 | 
				
			||||||
 | 
									if (ISO6391.validate(l)) {
 | 
				
			||||||
 | 
										languagePairs.set(ISO6391.getName(l), l);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Manually set names for languages not available in ISO6391
 | 
				
			||||||
 | 
								languagePairs.set('English (AU)', 'en-AU');
 | 
				
			||||||
 | 
								languagePairs.set('English (CA)', 'en-CA');
 | 
				
			||||||
 | 
								languagePairs.set('English (GB)', 'en-GB');
 | 
				
			||||||
 | 
								languagePairs.set('English (US)', 'en-US');
 | 
				
			||||||
 | 
								languagePairs.set('Spanish (Latin America)', 'es-419');
 | 
				
			||||||
 | 
								languagePairs.set('Spanish (Argentina)', 'es-AR');
 | 
				
			||||||
 | 
								languagePairs.set('Spanish (Mexico)', 'es-MX');
 | 
				
			||||||
 | 
								languagePairs.set('Spanish (US)', 'es-US');
 | 
				
			||||||
 | 
								languagePairs.set('Portuguese (Brazil)', 'pt-BR');
 | 
				
			||||||
 | 
								languagePairs.set('Portuguese (Portugal)', 'pt-PT');
 | 
				
			||||||
 | 
								languagePairs.set('Serbo-Croatian', 'sh');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								languagePairs = new Map([...languagePairs].sort((a, b) => ((a[0] < b[0]) ? -1 : 1)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const tagField: HTMLElement = document.querySelector('input[name=spellcheck]');
 | 
				
			||||||
 | 
								const tagify = new Tagify(tagField, {
 | 
				
			||||||
 | 
									whitelist: [...languagePairs.keys()],
 | 
				
			||||||
 | 
									enforceWhitelist: true,
 | 
				
			||||||
 | 
									maxTags: 3,
 | 
				
			||||||
 | 
									dropdown: {
 | 
				
			||||||
 | 
										enabled: 0,
 | 
				
			||||||
 | 
										maxItems: Infinity,
 | 
				
			||||||
 | 
										closeOnSelect: false,
 | 
				
			||||||
 | 
										highlightFirst: true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const configuredLanguages: string[] = ConfigUtil.getConfigItem('spellcheckerLanguages').map((code: string) => [...languagePairs].find(pair => (pair[1] === code))[0]);
 | 
				
			||||||
 | 
								tagify.addTags(configuredLanguages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								tagField.addEventListener('change', event => {
 | 
				
			||||||
 | 
									if ((event.target as HTMLInputElement).value.length === 0) {
 | 
				
			||||||
 | 
										ConfigUtil.setConfigItem('spellcheckerLanguages', []);
 | 
				
			||||||
 | 
										ipcRenderer.send('set-spellcheck-langs');
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										const spellLangs: string[] = [...JSON.parse((event.target as HTMLInputElement).value).values()].map(elt => languagePairs.get(elt.value));
 | 
				
			||||||
 | 
										ConfigUtil.setConfigItem('spellcheckerLanguages', spellLangs);
 | 
				
			||||||
 | 
										ipcRenderer.send('set-spellcheck-langs');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Do not display the spellchecker input and note if it is disabled
 | 
				
			||||||
 | 
							if (!ConfigUtil.getConfigItem('enableSpellchecker')) {
 | 
				
			||||||
 | 
								const spellcheckerLanguageInput: HTMLElement = document.querySelector('#spellcheck-langs');
 | 
				
			||||||
 | 
								const spellcheckerNote: HTMLElement = document.querySelector('#note');
 | 
				
			||||||
 | 
								spellcheckerLanguageInput.style.display = 'none';
 | 
				
			||||||
 | 
								spellcheckerNote.style.display = 'none';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseComponent from '../../components/base';
 | 
					import BaseComponent from '../../components/base';
 | 
				
			||||||
import * as t from '../../utils/translation-util';
 | 
					import * as t from '../../utils/translation-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,29 +19,29 @@ export default class PreferenceNav extends BaseComponent {
 | 
				
			|||||||
		this.init();
 | 
							this.init();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		let navItemsTemplate = '';
 | 
							let navItemsHTML = '';
 | 
				
			||||||
		for (const navItem of this.navItems) {
 | 
							for (const navItem of this.navItems) {
 | 
				
			||||||
			navItemsTemplate += `<div class="nav" id="nav-${navItem}">${t.__(navItem)}</div>`;
 | 
								navItemsHTML += htmlEscape`<div class="nav" id="nav-${navItem}">${t.__(navItem)}</div>`;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
			<div>
 | 
								<div>
 | 
				
			||||||
				<div id="settings-header">${t.__('Settings')}</div>
 | 
									<div id="settings-header">${t.__('Settings')}</div>
 | 
				
			||||||
				<div id="nav-container">${navItemsTemplate}</div>
 | 
									<div id="nav-container">` + navItemsHTML + htmlEscape`</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		`;
 | 
							`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.$el = this.generateNodeFromTemplate(this.template());
 | 
							this.$el = this.generateNodeFromHTML(this.templateHTML());
 | 
				
			||||||
		this.props.$root.append(this.$el);
 | 
							this.props.$root.append(this.$el);
 | 
				
			||||||
		this.registerListeners();
 | 
							this.registerListeners();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	registerListeners(): void {
 | 
						registerListeners(): void {
 | 
				
			||||||
		for (const navItem of this.navItems) {
 | 
							for (const navItem of this.navItems) {
 | 
				
			||||||
			const $item = document.querySelector(`#nav-${navItem}`);
 | 
								const $item = document.querySelector(`#nav-${CSS.escape(navItem)}`);
 | 
				
			||||||
			$item.addEventListener('click', () => {
 | 
								$item.addEventListener('click', () => {
 | 
				
			||||||
				this.props.onItemSelected(navItem);
 | 
									this.props.onItemSelected(navItem);
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
@@ -57,12 +59,12 @@ export default class PreferenceNav extends BaseComponent {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	activate(navItem: string): void {
 | 
						activate(navItem: string): void {
 | 
				
			||||||
		const $item = document.querySelector(`#nav-${navItem}`);
 | 
							const $item = document.querySelector(`#nav-${CSS.escape(navItem)}`);
 | 
				
			||||||
		$item.classList.add('active');
 | 
							$item.classList.add('active');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	deactivate(navItem: string): void {
 | 
						deactivate(navItem: string): void {
 | 
				
			||||||
		const $item = document.querySelector(`#nav-${navItem}`);
 | 
							const $item = document.querySelector(`#nav-${CSS.escape(navItem)}`);
 | 
				
			||||||
		$item.classList.remove('active');
 | 
							$item.classList.remove('active');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseSection from './base-section';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from '../../utils/config-util';
 | 
					import * as ConfigUtil from '../../utils/config-util';
 | 
				
			||||||
import * as t from '../../utils/translation-util';
 | 
					import * as t from '../../utils/translation-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import BaseSection from './base-section';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface NetworkSectionProps {
 | 
					interface NetworkSectionProps {
 | 
				
			||||||
	$root: Element;
 | 
						$root: Element;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -20,8 +23,8 @@ export default class NetworkSection extends BaseSection {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
            <div class="settings-pane">
 | 
					            <div class="settings-pane">
 | 
				
			||||||
                <div class="title">${t.__('Proxy')}</div>
 | 
					                <div class="title">${t.__('Proxy')}</div>
 | 
				
			||||||
                <div id="appearance-option-settings" class="settings-card">
 | 
					                <div id="appearance-option-settings" class="settings-card">
 | 
				
			||||||
@@ -58,7 +61,7 @@ export default class NetworkSection extends BaseSection {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.props.$root.innerHTML = this.template();
 | 
							this.props.$root.innerHTML = this.templateHTML();
 | 
				
			||||||
		this.$proxyPAC = document.querySelector('#proxy-pac-option .setting-input-value');
 | 
							this.$proxyPAC = document.querySelector('#proxy-pac-option .setting-input-value');
 | 
				
			||||||
		this.$proxyRules = document.querySelector('#proxy-rules-option .setting-input-value');
 | 
							this.$proxyRules = document.querySelector('#proxy-rules-option .setting-input-value');
 | 
				
			||||||
		this.$proxyBypass = document.querySelector('#proxy-bypass-option .setting-input-value');
 | 
							this.$proxyBypass = document.querySelector('#proxy-bypass-option .setting-input-value');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
import {ipcRenderer, remote} from 'electron';
 | 
					import {ipcRenderer, remote} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseComponent from '../../components/base';
 | 
					import BaseComponent from '../../components/base';
 | 
				
			||||||
import * as DomainUtil from '../../utils/domain-util';
 | 
					import * as DomainUtil from '../../utils/domain-util';
 | 
				
			||||||
import * as LinkUtil from '../../utils/link-util';
 | 
					import * as LinkUtil from '../../utils/link-util';
 | 
				
			||||||
@@ -22,8 +24,8 @@ export default class NewServerForm extends BaseComponent {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
			<div class="server-input-container">
 | 
								<div class="server-input-container">
 | 
				
			||||||
				<div class="title">${t.__('Organization URL')}</div>
 | 
									<div class="title">${t.__('Organization URL')}</div>
 | 
				
			||||||
				<div class="add-server-info-row">
 | 
									<div class="add-server-info-row">
 | 
				
			||||||
@@ -56,20 +58,20 @@ export default class NewServerForm extends BaseComponent {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initForm(): void {
 | 
						initForm(): void {
 | 
				
			||||||
		this.$newServerForm = this.generateNodeFromTemplate(this.template());
 | 
							this.$newServerForm = this.generateNodeFromHTML(this.templateHTML());
 | 
				
			||||||
		this.$saveServerButton = this.$newServerForm.querySelector('#connect');
 | 
							this.$saveServerButton = this.$newServerForm.querySelector('#connect');
 | 
				
			||||||
		this.props.$root.innerHTML = '';
 | 
							this.props.$root.textContent = '';
 | 
				
			||||||
		this.props.$root.append(this.$newServerForm);
 | 
							this.props.$root.append(this.$newServerForm);
 | 
				
			||||||
		this.$newServerUrl = this.$newServerForm.querySelectorAll('input.setting-input-value')[0] as HTMLInputElement;
 | 
							this.$newServerUrl = this.$newServerForm.querySelectorAll('input.setting-input-value')[0] as HTMLInputElement;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async submitFormHandler(): Promise<void> {
 | 
						async submitFormHandler(): Promise<void> {
 | 
				
			||||||
		this.$saveServerButton.innerHTML = 'Connecting...';
 | 
							this.$saveServerButton.textContent = 'Connecting...';
 | 
				
			||||||
		let serverConf;
 | 
							let serverConf;
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			serverConf = await DomainUtil.checkDomain(this.$newServerUrl.value);
 | 
								serverConf = await DomainUtil.checkDomain(this.$newServerUrl.value);
 | 
				
			||||||
		} catch (error) {
 | 
							} catch (error) {
 | 
				
			||||||
			this.$saveServerButton.innerHTML = 'Connect';
 | 
								this.$saveServerButton.textContent = 'Connect';
 | 
				
			||||||
			await dialog.showMessageBox({
 | 
								await dialog.showMessageBox({
 | 
				
			||||||
				type: 'error',
 | 
									type: 'error',
 | 
				
			||||||
				message: error.toString(),
 | 
									message: error.toString(),
 | 
				
			||||||
@@ -83,7 +85,7 @@ export default class NewServerForm extends BaseComponent {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	openCreateNewOrgExternalLink(): void {
 | 
						openCreateNewOrgExternalLink(): void {
 | 
				
			||||||
		const link = 'https://zulipchat.com/new/';
 | 
							const link = 'https://zulip.com/new/';
 | 
				
			||||||
		const externalCreateNewOrgElement = document.querySelector('#open-create-org-link');
 | 
							const externalCreateNewOrgElement = document.querySelector('#open-create-org-link');
 | 
				
			||||||
		externalCreateNewOrgElement.addEventListener('click', async () => {
 | 
							externalCreateNewOrgElement.addEventListener('click', async () => {
 | 
				
			||||||
			await LinkUtil.openBrowser(new URL(link));
 | 
								await LinkUtil.openBrowser(new URL(link));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,15 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseComponent from '../../components/base';
 | 
					import BaseComponent from '../../components/base';
 | 
				
			||||||
import Nav from './nav';
 | 
					 | 
				
			||||||
import ServersSection from './servers-section';
 | 
					 | 
				
			||||||
import GeneralSection from './general-section';
 | 
					 | 
				
			||||||
import NetworkSection from './network-section';
 | 
					 | 
				
			||||||
import ConnectedOrgSection from './connected-org-section';
 | 
					 | 
				
			||||||
import ShortcutsSection from './shortcuts-section';
 | 
					 | 
				
			||||||
import type {DNDSettings} from '../../utils/dnd-util';
 | 
					import type {DNDSettings} from '../../utils/dnd-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import ConnectedOrgSection from './connected-org-section';
 | 
				
			||||||
 | 
					import GeneralSection from './general-section';
 | 
				
			||||||
 | 
					import Nav from './nav';
 | 
				
			||||||
 | 
					import NetworkSection from './network-section';
 | 
				
			||||||
 | 
					import ServersSection from './servers-section';
 | 
				
			||||||
 | 
					import ShortcutsSection from './shortcuts-section';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Section = ServersSection | GeneralSection | NetworkSection | ConnectedOrgSection | ShortcutsSection;
 | 
					type Section = ServersSection | GeneralSection | NetworkSection | ConnectedOrgSection | ShortcutsSection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class PreferenceView extends BaseComponent {
 | 
					export default class PreferenceView extends BaseComponent {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,10 @@
 | 
				
			|||||||
import {remote, ipcRenderer} from 'electron';
 | 
					import {remote, ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as Messages from '../../../../resources/messages';
 | 
				
			||||||
import BaseComponent from '../../components/base';
 | 
					import BaseComponent from '../../components/base';
 | 
				
			||||||
import * as DomainUtil from '../../utils/domain-util';
 | 
					import * as DomainUtil from '../../utils/domain-util';
 | 
				
			||||||
import * as Messages from '../../../../resources/messages';
 | 
					 | 
				
			||||||
import * as t from '../../utils/translation-util';
 | 
					import * as t from '../../utils/translation-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {dialog} = remote;
 | 
					const {dialog} = remote;
 | 
				
			||||||
@@ -26,8 +28,8 @@ export default class ServerInfoForm extends BaseComponent {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
			<div class="settings-card">
 | 
								<div class="settings-card">
 | 
				
			||||||
				<div class="server-info-left">
 | 
									<div class="server-info-left">
 | 
				
			||||||
					<img class="server-info-icon" src="${this.props.server.icon}"/>
 | 
										<img class="server-info-icon" src="${this.props.server.icon}"/>
 | 
				
			||||||
@@ -56,7 +58,7 @@ export default class ServerInfoForm extends BaseComponent {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initForm(): void {
 | 
						initForm(): void {
 | 
				
			||||||
		this.$serverInfoForm = this.generateNodeFromTemplate(this.template());
 | 
							this.$serverInfoForm = this.generateNodeFromHTML(this.templateHTML());
 | 
				
			||||||
		this.$serverInfoAlias = this.$serverInfoForm.querySelectorAll('.server-info-alias')[0];
 | 
							this.$serverInfoAlias = this.$serverInfoForm.querySelectorAll('.server-info-alias')[0];
 | 
				
			||||||
		this.$serverIcon = this.$serverInfoForm.querySelectorAll('.server-info-icon')[0];
 | 
							this.$serverIcon = this.$serverInfoForm.querySelectorAll('.server-info-icon')[0];
 | 
				
			||||||
		this.$deleteServerButton = this.$serverInfoForm.querySelectorAll('.server-delete-action')[0];
 | 
							this.$deleteServerButton = this.$serverInfoForm.querySelectorAll('.server-delete-action')[0];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as t from '../../utils/translation-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseSection from './base-section';
 | 
					import BaseSection from './base-section';
 | 
				
			||||||
import NewServerForm from './new-server-form';
 | 
					import NewServerForm from './new-server-form';
 | 
				
			||||||
import * as t from '../../utils/translation-util';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ServersSectionProps {
 | 
					interface ServersSectionProps {
 | 
				
			||||||
	$root: Element;
 | 
						$root: Element;
 | 
				
			||||||
@@ -14,16 +17,16 @@ export default class ServersSection extends BaseSection {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	template(): string {
 | 
						templateHTML(): string {
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
		<div class="add-server-modal">
 | 
								<div class="add-server-modal">
 | 
				
			||||||
			<div class="modal-container">
 | 
									<div class="modal-container">
 | 
				
			||||||
				<div class="settings-pane" id="server-settings-pane">
 | 
										<div class="settings-pane" id="server-settings-pane">
 | 
				
			||||||
					<div class="page-title">${t.__('Add a Zulip organization')}</div>
 | 
											<div class="page-title">${t.__('Add a Zulip organization')}</div>
 | 
				
			||||||
					<div id="new-server-container"></div>
 | 
											<div id="new-server-container"></div>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
		`;
 | 
							`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,9 +35,9 @@ export default class ServersSection extends BaseSection {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initServers(): void {
 | 
						initServers(): void {
 | 
				
			||||||
		this.props.$root.innerHTML = '';
 | 
							this.props.$root.textContent = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.props.$root.innerHTML = this.template();
 | 
							this.props.$root.innerHTML = this.templateHTML();
 | 
				
			||||||
		this.$newServerContainer = document.querySelector('#new-server-container');
 | 
							this.$newServerContainer = document.querySelector('#new-server-container');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.initNewServerForm();
 | 
							this.initNewServerForm();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,10 @@
 | 
				
			|||||||
import BaseSection from './base-section';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as LinkUtil from '../../utils/link-util';
 | 
					import * as LinkUtil from '../../utils/link-util';
 | 
				
			||||||
import * as t from '../../utils/translation-util';
 | 
					import * as t from '../../utils/translation-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import BaseSection from './base-section';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ShortcutsSectionProps {
 | 
					interface ShortcutsSectionProps {
 | 
				
			||||||
	$root: Element;
 | 
						$root: Element;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -13,14 +16,14 @@ export default class ShortcutsSection extends BaseSection {
 | 
				
			|||||||
		this.props = props;
 | 
							this.props = props;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO - Deduplicate templateMac and templateWinLin functions. In theory
 | 
						// TODO - Deduplicate templateMacHTML and templateWinLinHTML functions. In theory
 | 
				
			||||||
	// they both should be the same the only thing different should be the userOSKey
 | 
						// they both should be the same the only thing different should be the userOSKey
 | 
				
			||||||
	// variable but there seems to be inconsistences between both function, one has more
 | 
						// variable but there seems to be inconsistences between both function, one has more
 | 
				
			||||||
	// lines though one may just be using more new lines and other thing is the use of +.
 | 
						// lines though one may just be using more new lines and other thing is the use of +.
 | 
				
			||||||
	templateMac(): string {
 | 
						templateMacHTML(): string {
 | 
				
			||||||
		const userOSKey = '⌘';
 | 
							const userOSKey = '⌘';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
						<div class="settings-pane">
 | 
											<div class="settings-pane">
 | 
				
			||||||
						<div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>${t.__('Tip')}:  </b>${t.__('These desktop app shortcuts extend the Zulip webapp\'s')} <span id="open-hotkeys-link"> ${t.__('keyboard shortcuts')}</span>.</p></div>
 | 
											<div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>${t.__('Tip')}:  </b>${t.__('These desktop app shortcuts extend the Zulip webapp\'s')} <span id="open-hotkeys-link"> ${t.__('keyboard shortcuts')}</span>.</p></div>
 | 
				
			||||||
							<div class="title">${t.__('Application Shortcuts')}</div>
 | 
												<div class="title">${t.__('Application Shortcuts')}</div>
 | 
				
			||||||
@@ -181,10 +184,10 @@ export default class ShortcutsSection extends BaseSection {
 | 
				
			|||||||
		`;
 | 
							`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	templateWinLin(): string {
 | 
						templateWinLinHTML(): string {
 | 
				
			||||||
		const userOSKey = 'Ctrl';
 | 
							const userOSKey = 'Ctrl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return `
 | 
							return htmlEscape`
 | 
				
			||||||
						<div class="settings-pane">
 | 
											<div class="settings-pane">
 | 
				
			||||||
						<div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>${t.__('Tip')}:  </b>${t.__('These desktop app shortcuts extend the Zulip webapp\'s')} <span id="open-hotkeys-link"> ${t.__('keyboard shortcuts')}</span>.</p></div>
 | 
											<div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>${t.__('Tip')}:  </b>${t.__('These desktop app shortcuts extend the Zulip webapp\'s')} <span id="open-hotkeys-link"> ${t.__('keyboard shortcuts')}</span>.</p></div>
 | 
				
			||||||
							<div class="title">${t.__('Application Shortcuts')}</div>
 | 
												<div class="title">${t.__('Application Shortcuts')}</div>
 | 
				
			||||||
@@ -330,7 +333,7 @@ export default class ShortcutsSection extends BaseSection {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	openHotkeysExternalLink(): void {
 | 
						openHotkeysExternalLink(): void {
 | 
				
			||||||
		const link = 'https://zulipchat.com/help/keyboard-shortcuts';
 | 
							const link = 'https://zulip.com/help/keyboard-shortcuts';
 | 
				
			||||||
		const externalCreateNewOrgElement = document.querySelector('#open-hotkeys-link');
 | 
							const externalCreateNewOrgElement = document.querySelector('#open-hotkeys-link');
 | 
				
			||||||
		externalCreateNewOrgElement.addEventListener('click', async () => {
 | 
							externalCreateNewOrgElement.addEventListener('click', async () => {
 | 
				
			||||||
			await LinkUtil.openBrowser(new URL(link));
 | 
								await LinkUtil.openBrowser(new URL(link));
 | 
				
			||||||
@@ -339,7 +342,7 @@ export default class ShortcutsSection extends BaseSection {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	init(): void {
 | 
						init(): void {
 | 
				
			||||||
		this.props.$root.innerHTML = (process.platform === 'darwin') ?
 | 
							this.props.$root.innerHTML = (process.platform === 'darwin') ?
 | 
				
			||||||
			this.templateMac() : this.templateWinLin();
 | 
								this.templateMacHTML() : this.templateWinLinHTML();
 | 
				
			||||||
		this.openHotkeysExternalLink();
 | 
							this.openHotkeysExternalLink();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,22 @@
 | 
				
			|||||||
import {contextBridge, ipcRenderer, webFrame} from 'electron';
 | 
					import {contextBridge, ipcRenderer, webFrame} from 'electron';
 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import * as SetupSpellChecker from './spellchecker';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import isDev from 'electron-is-dev';
 | 
					import isDev from 'electron-is-dev';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import electron_bridge from './electron-bridge';
 | 
				
			||||||
 | 
					import {loadBots} from './notification/helpers';
 | 
				
			||||||
import * as NetworkError from './pages/network';
 | 
					import * as NetworkError from './pages/network';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// eslint-disable-next-line import/no-unassigned-import
 | 
					 | 
				
			||||||
import './notification';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Prevent drag and drop event in main process which prevents remote code executaion
 | 
					// Prevent drag and drop event in main process which prevents remote code executaion
 | 
				
			||||||
// eslint-disable-next-line import/no-unassigned-import
 | 
					// eslint-disable-next-line import/no-unassigned-import
 | 
				
			||||||
import './shared/preventdrag';
 | 
					import './shared/preventdrag';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import electron_bridge from './electron-bridge';
 | 
					 | 
				
			||||||
contextBridge.exposeInMainWorld('raw_electron_bridge', electron_bridge);
 | 
					contextBridge.exposeInMainWorld('raw_electron_bridge', electron_bridge);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					electron_bridge.once('zulip-loaded', async () => {
 | 
				
			||||||
 | 
						await loadBots();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ipcRenderer.on('logout', () => {
 | 
					ipcRenderer.on('logout', () => {
 | 
				
			||||||
	// Create the menu for the below
 | 
						// Create the menu for the below
 | 
				
			||||||
	const dropdown: HTMLElement = document.querySelector('.dropdown-toggle');
 | 
						const dropdown: HTMLElement = document.querySelector('.dropdown-toggle');
 | 
				
			||||||
@@ -54,13 +55,7 @@ ipcRenderer.on('show-notification-settings', () => {
 | 
				
			|||||||
	}, 100);
 | 
						}, 100);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
electron_bridge.once('zulip-loaded', ({serverLanguage}) => {
 | 
					electron_bridge.once('zulip-loaded', () => {
 | 
				
			||||||
	// Get the default language of the server
 | 
					 | 
				
			||||||
	if (serverLanguage) {
 | 
					 | 
				
			||||||
		// Init spellchecker
 | 
					 | 
				
			||||||
		SetupSpellChecker.init(serverLanguage);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Redirect users to network troubleshooting page
 | 
						// Redirect users to network troubleshooting page
 | 
				
			||||||
	const getRestartButton = document.querySelector('.restart_get_events_button');
 | 
						const getRestartButton = document.querySelector('.restart_get_events_button');
 | 
				
			||||||
	if (getRestartButton) {
 | 
						if (getRestartButton) {
 | 
				
			||||||
@@ -70,12 +65,6 @@ electron_bridge.once('zulip-loaded', ({serverLanguage}) => {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Clean up spellchecker events after you navigate away from this page;
 | 
					 | 
				
			||||||
// otherwise, you may experience errors
 | 
					 | 
				
			||||||
window.addEventListener('beforeunload', (): void => {
 | 
					 | 
				
			||||||
	SetupSpellChecker.unsubscribeSpellChecker();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
window.addEventListener('load', (event: any): void => {
 | 
					window.addEventListener('load', (event: any): void => {
 | 
				
			||||||
	if (!event.target.URL.includes('app/renderer/network.html')) {
 | 
						if (!event.target.URL.includes('app/renderer/network.html')) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,61 +0,0 @@
 | 
				
			|||||||
import type {Subject} from 'rxjs';
 | 
					 | 
				
			||||||
import {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} from 'electron-spellchecker';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import * as ConfigUtil from './utils/config-util';
 | 
					 | 
				
			||||||
import Logger from './utils/logger-util';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
declare module 'electron-spellchecker' {
 | 
					 | 
				
			||||||
	interface SpellCheckHandler {
 | 
					 | 
				
			||||||
		currentSpellcheckerChanged: Subject<true>;
 | 
					 | 
				
			||||||
		currentSpellcheckerLanguage: string;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const logger = new Logger({
 | 
					 | 
				
			||||||
	file: 'errors.log',
 | 
					 | 
				
			||||||
	timestamp: true
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let spellCheckHandler: SpellCheckHandler;
 | 
					 | 
				
			||||||
let contextMenuListener: ContextMenuListener;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function init(serverLanguage: string): void {
 | 
					 | 
				
			||||||
	if (ConfigUtil.getConfigItem('enableSpellchecker')) {
 | 
					 | 
				
			||||||
		enableSpellChecker();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	enableContextMenu(serverLanguage);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function enableSpellChecker(): void {
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		spellCheckHandler = new SpellCheckHandler();
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		logger.error(error);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function enableContextMenu(serverLanguage: string): void {
 | 
					 | 
				
			||||||
	if (spellCheckHandler) {
 | 
					 | 
				
			||||||
		spellCheckHandler.attachToInput();
 | 
					 | 
				
			||||||
		spellCheckHandler.switchLanguage(serverLanguage);
 | 
					 | 
				
			||||||
		spellCheckHandler.currentSpellcheckerChanged.subscribe(() => {
 | 
					 | 
				
			||||||
			spellCheckHandler.switchLanguage(spellCheckHandler.currentSpellcheckerLanguage);
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const contextMenuBuilder = new ContextMenuBuilder(spellCheckHandler);
 | 
					 | 
				
			||||||
	contextMenuListener = new ContextMenuListener(info => {
 | 
					 | 
				
			||||||
		contextMenuBuilder.showPopupMenu(info);
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function unsubscribeSpellChecker(): void {
 | 
					 | 
				
			||||||
	if (spellCheckHandler) {
 | 
					 | 
				
			||||||
		spellCheckHandler.unsubscribe();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (contextMenuListener) {
 | 
					 | 
				
			||||||
		contextMenuListener.unsubscribe();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,16 +1,13 @@
 | 
				
			|||||||
import {ipcRenderer, remote, WebviewTag, NativeImage} from 'electron';
 | 
					import {ipcRenderer, remote, WebviewTag, NativeImage} from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from './utils/config-util';
 | 
					import * as ConfigUtil from './utils/config-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {Tray, Menu, nativeImage, BrowserWindow, nativeTheme} = remote;
 | 
					const {Tray, Menu, nativeImage, BrowserWindow} = remote;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let tray: Electron.Tray;
 | 
					let tray: Electron.Tray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Get the theme on macOS
 | 
					const ICON_DIR = '../../resources/tray';
 | 
				
			||||||
const theme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const ICON_DIR = process.platform === 'darwin' ? `../../resources/tray/${theme}` : '../../resources/tray';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TRAY_SUFFIX = 'tray';
 | 
					const TRAY_SUFFIX = 'tray';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,7 +18,7 @@ const iconPath = (): string => {
 | 
				
			|||||||
		return APP_ICON + 'linux.png';
 | 
							return APP_ICON + 'linux.png';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'osx.png');
 | 
						return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'macOSTemplate.png');
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let unread = 0;
 | 
					let unread = 0;
 | 
				
			||||||
@@ -210,7 +207,7 @@ function toggleTray(): void {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	const selector = 'webview:not([class*=disabled])';
 | 
						const selector = 'webview:not([class*=disabled])';
 | 
				
			||||||
	const webview: WebviewTag = document.querySelector(selector);
 | 
						const webview: WebviewTag = document.querySelector(selector);
 | 
				
			||||||
	const webContents = webview.getWebContents();
 | 
						const webContents = remote.webContents.fromId(webview.getWebContentsId());
 | 
				
			||||||
	webContents.send('toggletray', state);
 | 
						webContents.send('toggletray', state);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import electron from 'electron';
 | 
					import electron from 'electron';
 | 
				
			||||||
import {JsonDB} from 'node-json-db';
 | 
					 | 
				
			||||||
import {initSetUp} from './default-util';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {JsonDB} from 'node-json-db';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {initSetUp} from './default-util';
 | 
				
			||||||
import Logger from './logger-util';
 | 
					import Logger from './logger-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {app, dialog} =
 | 
					const {app, dialog} =
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
// Unescape already encoded/escaped strings
 | 
					 | 
				
			||||||
export function decodeString(stringInput: string): string {
 | 
					 | 
				
			||||||
	const parser = new DOMParser();
 | 
					 | 
				
			||||||
	const dom = parser.parseFromString(
 | 
					 | 
				
			||||||
		'<!doctype html><body>' + stringInput,
 | 
					 | 
				
			||||||
		'text/html');
 | 
					 | 
				
			||||||
	return dom.body.textContent;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,10 +1,11 @@
 | 
				
			|||||||
import {JsonDB} from 'node-json-db';
 | 
					import electron from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
import electron from 'electron';
 | 
					
 | 
				
			||||||
import Logger from './logger-util';
 | 
					import {JsonDB} from 'node-json-db';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as EnterpriseUtil from './enterprise-util';
 | 
					import * as EnterpriseUtil from './enterprise-util';
 | 
				
			||||||
 | 
					import Logger from './logger-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = new Logger({
 | 
					const logger = new Logger({
 | 
				
			||||||
	file: 'config-util.log',
 | 
						file: 'config-util.log',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,20 @@
 | 
				
			|||||||
import {JsonDB} from 'node-json-db';
 | 
					import {remote, ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import escape from 'escape-html';
 | 
					 | 
				
			||||||
import request from 'request';
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
import Logger from './logger-util';
 | 
					 | 
				
			||||||
import {remote} from 'electron';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as RequestUtil from './request-util';
 | 
					import {JsonDB} from 'node-json-db';
 | 
				
			||||||
import * as EnterpriseUtil from './enterprise-util';
 | 
					
 | 
				
			||||||
import * as Messages from '../../../resources/messages';
 | 
					import * as Messages from '../../../resources/messages';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as EnterpriseUtil from './enterprise-util';
 | 
				
			||||||
 | 
					import Logger from './logger-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {app, dialog} = remote;
 | 
					const {app, dialog} = remote;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ServerConf {
 | 
					export interface ServerConf {
 | 
				
			||||||
	url: string;
 | 
						url: string;
 | 
				
			||||||
	alias?: string;
 | 
						alias?: string;
 | 
				
			||||||
	icon?: string;
 | 
						icon?: string;
 | 
				
			||||||
	ignoreCerts?: boolean;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = new Logger({
 | 
					const logger = new Logger({
 | 
				
			||||||
@@ -55,26 +52,14 @@ export function getDomain(index: number): ServerConf {
 | 
				
			|||||||
	return db.getData(`/domains[${index}]`);
 | 
						return db.getData(`/domains[${index}]`);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function shouldIgnoreCerts(url: string): boolean {
 | 
					 | 
				
			||||||
	const domains = getDomains();
 | 
					 | 
				
			||||||
	for (const domain of domains) {
 | 
					 | 
				
			||||||
		if (domain.url === url) {
 | 
					 | 
				
			||||||
			return domain.ignoreCerts;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return null;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function updateDomain(index: number, server: ServerConf): void {
 | 
					export function updateDomain(index: number, server: ServerConf): void {
 | 
				
			||||||
	reloadDB();
 | 
						reloadDB();
 | 
				
			||||||
	db.push(`/domains[${index}]`, server, true);
 | 
						db.push(`/domains[${index}]`, server, true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function addDomain(server: ServerConf): Promise<void> {
 | 
					export async function addDomain(server: ServerConf): Promise<void> {
 | 
				
			||||||
	const {ignoreCerts} = server;
 | 
					 | 
				
			||||||
	if (server.icon) {
 | 
						if (server.icon) {
 | 
				
			||||||
		const localIconUrl = await saveServerIcon(server, ignoreCerts);
 | 
							const localIconUrl = await saveServerIcon(server);
 | 
				
			||||||
		server.icon = localIconUrl;
 | 
							server.icon = localIconUrl;
 | 
				
			||||||
		db.push('/domains[]', server, true);
 | 
							db.push('/domains[]', server, true);
 | 
				
			||||||
		reloadDB();
 | 
							reloadDB();
 | 
				
			||||||
@@ -118,33 +103,16 @@ async function checkCertError(domain: string, serverConf: ServerConf, error: any
 | 
				
			|||||||
	const certErrorMessage = Messages.certErrorMessage(domain, error);
 | 
						const certErrorMessage = Messages.certErrorMessage(domain, error);
 | 
				
			||||||
	const certErrorDetail = Messages.certErrorDetail();
 | 
						const certErrorDetail = Messages.certErrorDetail();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const {response} = await dialog.showMessageBox({
 | 
						await dialog.showMessageBox({
 | 
				
			||||||
		type: 'warning',
 | 
							type: 'error',
 | 
				
			||||||
		buttons: ['Yes', 'No'],
 | 
							buttons: ['OK'],
 | 
				
			||||||
		defaultId: 1,
 | 
					 | 
				
			||||||
		message: certErrorMessage,
 | 
							message: certErrorMessage,
 | 
				
			||||||
		detail: certErrorDetail
 | 
							detail: certErrorDetail
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
	if (response === 0) {
 | 
						throw new Error('Untrusted certificate.');
 | 
				
			||||||
		// Set ignoreCerts parameter to true in case user responds with yes
 | 
					 | 
				
			||||||
		serverConf.ignoreCerts = true;
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			return await getServerSettings(domain, serverConf.ignoreCerts);
 | 
					 | 
				
			||||||
		} catch (_) {
 | 
					 | 
				
			||||||
			if (error === Messages.noOrgsError(domain)) {
 | 
					 | 
				
			||||||
				throw new Error(error);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return serverConf;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		throw new Error('Untrusted certificate.');
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ignoreCerts parameter helps in fetching server icon and
 | 
					export async function checkDomain(domain: string, silent = false): Promise<ServerConf> {
 | 
				
			||||||
// other server details when user chooses to ignore certificate warnings
 | 
					 | 
				
			||||||
export async function checkDomain(domain: string, ignoreCerts = false, silent = false): Promise<ServerConf> {
 | 
					 | 
				
			||||||
	if (!silent && duplicateDomain(domain)) {
 | 
						if (!silent && duplicateDomain(domain)) {
 | 
				
			||||||
		// Do not check duplicate in silent mode
 | 
							// Do not check duplicate in silent mode
 | 
				
			||||||
		throw new Error('This server has been added.');
 | 
							throw new Error('This server has been added.');
 | 
				
			||||||
@@ -155,12 +123,11 @@ export async function checkDomain(domain: string, ignoreCerts = false, silent =
 | 
				
			|||||||
	const serverConf = {
 | 
						const serverConf = {
 | 
				
			||||||
		icon: defaultIconUrl,
 | 
							icon: defaultIconUrl,
 | 
				
			||||||
		url: domain,
 | 
							url: domain,
 | 
				
			||||||
		alias: domain,
 | 
							alias: domain
 | 
				
			||||||
		ignoreCerts
 | 
					 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		return await getServerSettings(domain, serverConf.ignoreCerts);
 | 
							return await getServerSettings(domain);
 | 
				
			||||||
	} catch (error_) {
 | 
						} catch (error_) {
 | 
				
			||||||
		// Make sure that error is an error or string not undefined
 | 
							// Make sure that error is an error or string not undefined
 | 
				
			||||||
		// so validation does not throw error.
 | 
							// so validation does not throw error.
 | 
				
			||||||
@@ -176,85 +143,20 @@ export async function checkDomain(domain: string, ignoreCerts = false, silent =
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function getServerSettings(domain: string, ignoreCerts = false): Promise<ServerConf> {
 | 
					async function getServerSettings(domain: string): Promise<ServerConf> {
 | 
				
			||||||
	const serverSettingsOptions = {
 | 
						return ipcRenderer.invoke('get-server-settings', domain);
 | 
				
			||||||
		url: domain + '/api/v1/server_settings',
 | 
					 | 
				
			||||||
		...RequestUtil.requestOptions(domain, ignoreCerts)
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
		request(serverSettingsOptions, (error: Error, response: request.Response) => {
 | 
					 | 
				
			||||||
			if (!error && response.statusCode === 200) {
 | 
					 | 
				
			||||||
				const {realm_name, realm_uri, realm_icon} = JSON.parse(response.body);
 | 
					 | 
				
			||||||
				if (
 | 
					 | 
				
			||||||
					typeof realm_name === 'string' &&
 | 
					 | 
				
			||||||
					typeof realm_uri === 'string' &&
 | 
					 | 
				
			||||||
					typeof realm_icon === 'string'
 | 
					 | 
				
			||||||
				) {
 | 
					 | 
				
			||||||
					resolve({
 | 
					 | 
				
			||||||
						// Some Zulip Servers use absolute URL for server icon whereas others use relative URL
 | 
					 | 
				
			||||||
						// Following check handles both the cases
 | 
					 | 
				
			||||||
						icon: realm_icon.startsWith('/') ? realm_uri + realm_icon : realm_icon,
 | 
					 | 
				
			||||||
						url: realm_uri,
 | 
					 | 
				
			||||||
						alias: escape(realm_name),
 | 
					 | 
				
			||||||
						ignoreCerts
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					reject(Messages.noOrgsError(domain));
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				reject(response);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function saveServerIcon(server: ServerConf, ignoreCerts = false): Promise<string> {
 | 
					export async function saveServerIcon(server: ServerConf): Promise<string> {
 | 
				
			||||||
	const url = server.icon;
 | 
						return ipcRenderer.invoke('save-server-icon', server.icon);
 | 
				
			||||||
	const domain = server.url;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const serverIconOptions = {
 | 
					 | 
				
			||||||
		url,
 | 
					 | 
				
			||||||
		...RequestUtil.requestOptions(domain, ignoreCerts)
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// The save will always succeed. If url is invalid, downgrade to default icon.
 | 
					 | 
				
			||||||
	return new Promise(resolve => {
 | 
					 | 
				
			||||||
		const filePath = generateFilePath(url);
 | 
					 | 
				
			||||||
		const file = fs.createWriteStream(filePath);
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			request(serverIconOptions).on('response', (response: request.Response) => {
 | 
					 | 
				
			||||||
				response.on('error', (err: Error) => {
 | 
					 | 
				
			||||||
					logger.log('Could not get server icon.');
 | 
					 | 
				
			||||||
					logger.log(err);
 | 
					 | 
				
			||||||
					logger.reportSentry(err);
 | 
					 | 
				
			||||||
					resolve(defaultIconUrl);
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
				response.pipe(file).on('finish', () => {
 | 
					 | 
				
			||||||
					resolve(filePath);
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
			}).on('error', (err: Error) => {
 | 
					 | 
				
			||||||
				logger.log('Could not get server icon.');
 | 
					 | 
				
			||||||
				logger.log(err);
 | 
					 | 
				
			||||||
				logger.reportSentry(err);
 | 
					 | 
				
			||||||
				resolve(defaultIconUrl);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		} catch (error) {
 | 
					 | 
				
			||||||
			logger.log('Could not get server icon.');
 | 
					 | 
				
			||||||
			logger.log(error);
 | 
					 | 
				
			||||||
			logger.reportSentry(error);
 | 
					 | 
				
			||||||
			resolve(defaultIconUrl);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function updateSavedServer(url: string, index: number): Promise<void> {
 | 
					export async function updateSavedServer(url: string, index: number): Promise<void> {
 | 
				
			||||||
	// Does not promise successful update
 | 
						// Does not promise successful update
 | 
				
			||||||
	const oldIcon = getDomain(index).icon;
 | 
						const oldIcon = getDomain(index).icon;
 | 
				
			||||||
	const {ignoreCerts} = getDomain(index);
 | 
					 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const newServerConf = await checkDomain(url, ignoreCerts, true);
 | 
							const newServerConf = await checkDomain(url, true);
 | 
				
			||||||
		const localIconUrl = await saveServerIcon(newServerConf, ignoreCerts);
 | 
							const localIconUrl = await saveServerIcon(newServerConf);
 | 
				
			||||||
		if (!oldIcon || localIconUrl !== '../renderer/img/icon.png') {
 | 
							if (!oldIcon || localIconUrl !== '../renderer/img/icon.png') {
 | 
				
			||||||
			newServerConf.icon = localIconUrl;
 | 
								newServerConf.icon = localIconUrl;
 | 
				
			||||||
			updateDomain(index, newServerConf);
 | 
								updateDomain(index, newServerConf);
 | 
				
			||||||
@@ -289,25 +191,6 @@ function reloadDB(): void {
 | 
				
			|||||||
	db = new JsonDB(domainJsonPath, true, true);
 | 
						db = new JsonDB(domainJsonPath, true, true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generateFilePath(url: string): string {
 | 
					 | 
				
			||||||
	const dir = `${app.getPath('userData')}/server-icons`;
 | 
					 | 
				
			||||||
	const extension = path.extname(url).split('?')[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let hash = 5381;
 | 
					 | 
				
			||||||
	let {length} = url;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (length) {
 | 
					 | 
				
			||||||
		hash = (hash * 33) ^ url.charCodeAt(--length);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Create 'server-icons' directory if not existed
 | 
					 | 
				
			||||||
	if (!fs.existsSync(dir)) {
 | 
					 | 
				
			||||||
		fs.mkdirSync(dir);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return `${dir}/${hash >>> 0}${extension}`;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function formatUrl(domain: string): string {
 | 
					export function formatUrl(domain: string): string {
 | 
				
			||||||
	if (domain.startsWith('http://') || domain.startsWith('https://')) {
 | 
						if (domain.startsWith('http://') || domain.startsWith('https://')) {
 | 
				
			||||||
		return domain;
 | 
							return domain;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
import {shell} from 'electron';
 | 
					import {shell} from 'electron';
 | 
				
			||||||
import escape from 'escape-html';
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import os from 'os';
 | 
					import os from 'os';
 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function isUploadsUrl(server: string, url: URL): boolean {
 | 
					export function isUploadsUrl(server: string, url: URL): boolean {
 | 
				
			||||||
	return url.origin === server && url.pathname.startsWith('/user_uploads/');
 | 
						return url.origin === server && url.pathname.startsWith('/user_uploads/');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -18,12 +19,12 @@ export async function openBrowser(url: URL): Promise<void> {
 | 
				
			|||||||
			path.join(os.tmpdir(), 'zulip-redirect-')
 | 
								path.join(os.tmpdir(), 'zulip-redirect-')
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		const file = path.join(dir, 'redirect.html');
 | 
							const file = path.join(dir, 'redirect.html');
 | 
				
			||||||
		fs.writeFileSync(file, `\
 | 
							fs.writeFileSync(file, htmlEscape`\
 | 
				
			||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html>
 | 
					<html>
 | 
				
			||||||
    <head>
 | 
					    <head>
 | 
				
			||||||
        <meta charset="UTF-8" />
 | 
					        <meta charset="UTF-8" />
 | 
				
			||||||
        <meta http-equiv="Refresh" content="0; url=${escape(url.href)}" />
 | 
					        <meta http-equiv="Refresh" content="0; url=${url.href}" />
 | 
				
			||||||
        <title>Redirecting</title>
 | 
					        <title>Redirecting</title>
 | 
				
			||||||
        <style>
 | 
					        <style>
 | 
				
			||||||
            html {
 | 
					            html {
 | 
				
			||||||
@@ -32,11 +33,11 @@ export async function openBrowser(url: URL): Promise<void> {
 | 
				
			|||||||
        </style>
 | 
					        </style>
 | 
				
			||||||
    </head>
 | 
					    </head>
 | 
				
			||||||
    <body>
 | 
					    <body>
 | 
				
			||||||
        <p>Opening <a href="${escape(url.href)}">${escape(url.href)}</a>…</p>
 | 
					        <p>Opening <a href="${url.href}">${url.href}</a>…</p>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
`);
 | 
					`);
 | 
				
			||||||
		shell.openItem(file);
 | 
							await shell.openPath(file);
 | 
				
			||||||
		setTimeout(() => {
 | 
							setTimeout(() => {
 | 
				
			||||||
			fs.unlinkSync(file);
 | 
								fs.unlinkSync(file);
 | 
				
			||||||
			fs.rmdirSync(dir);
 | 
								fs.rmdirSync(dir);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
import {JsonDB} from 'node-json-db';
 | 
					import electron from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
import electron from 'electron';
 | 
					
 | 
				
			||||||
 | 
					import {JsonDB} from 'node-json-db';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Logger from './logger-util';
 | 
					import Logger from './logger-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const remote =
 | 
					const remote =
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,12 @@
 | 
				
			|||||||
import {Console} from 'console'; // eslint-disable-line node/prefer-global/console
 | 
					import {Console} from 'console'; // eslint-disable-line node/prefer-global/console
 | 
				
			||||||
import {initSetUp} from './default-util';
 | 
					import electron from 'electron';
 | 
				
			||||||
import {sentryInit, captureException} from './sentry-util';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					import fs from 'fs';
 | 
				
			||||||
import os from 'os';
 | 
					import os from 'os';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import isDev from 'electron-is-dev';
 | 
					import isDev from 'electron-is-dev';
 | 
				
			||||||
import electron from 'electron';
 | 
					
 | 
				
			||||||
 | 
					import {initSetUp} from './default-util';
 | 
				
			||||||
 | 
					import {sentryInit, captureException} from './sentry-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface LoggerOptions {
 | 
					interface LoggerOptions {
 | 
				
			||||||
	timestamp?: true | (() => string);
 | 
						timestamp?: true | (() => string);
 | 
				
			||||||
@@ -89,20 +90,21 @@ export default class Logger {
 | 
				
			|||||||
			nodeConsole, timestamp, level, logInDevMode
 | 
								nodeConsole, timestamp, level, logInDevMode
 | 
				
			||||||
		} = this;
 | 
							} = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* eslint-disable no-fallthrough */
 | 
					 | 
				
			||||||
		switch (true) {
 | 
							switch (true) {
 | 
				
			||||||
			case typeof timestamp === 'function':
 | 
								case typeof timestamp === 'function':
 | 
				
			||||||
				args.unshift(timestamp() + ' |\t');
 | 
									args.unshift(timestamp() + ' |\t');
 | 
				
			||||||
 | 
									// Fall through
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case (level):
 | 
								case (level):
 | 
				
			||||||
				args.unshift(type.toUpperCase() + ' |');
 | 
									args.unshift(type.toUpperCase() + ' |');
 | 
				
			||||||
 | 
									// Fall through
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case isDev || logInDevMode:
 | 
								case isDev || logInDevMode:
 | 
				
			||||||
				nodeConsole[type](...args);
 | 
									nodeConsole[type](...args);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			default: break;
 | 
								default:
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* eslint-enable no-fallthrough */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		console[type](...args);
 | 
							console[type](...args);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,45 +5,6 @@ export interface ProxyRule {
 | 
				
			|||||||
	port?: number;
 | 
						port?: number;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Return proxy to be used for a particular uri, to be used for request
 | 
					 | 
				
			||||||
export function getProxy(_uri: string): ProxyRule | void {
 | 
					 | 
				
			||||||
	let uri;
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		uri = new URL(_uri);
 | 
					 | 
				
			||||||
	} catch (_) {
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const proxyRules = ConfigUtil.getConfigItem('proxyRules', '').split(';');
 | 
					 | 
				
			||||||
	// If SPS is on and system uses no proxy then request should not try to use proxy from
 | 
					 | 
				
			||||||
	// environment. NO_PROXY = '*' makes request ignore all environment proxy variables.
 | 
					 | 
				
			||||||
	if (proxyRules[0] === '') {
 | 
					 | 
				
			||||||
		process.env.NO_PROXY = '*';
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const proxyRule: any = {};
 | 
					 | 
				
			||||||
	if (uri.protocol === 'http:') {
 | 
					 | 
				
			||||||
		proxyRules.forEach((proxy: string) => {
 | 
					 | 
				
			||||||
			if (proxy.includes('http=')) {
 | 
					 | 
				
			||||||
				proxyRule.hostname = proxy.split('http=')[1].trim().split(':')[0];
 | 
					 | 
				
			||||||
				proxyRule.port = proxy.split('http=')[1].trim().split(':')[1];
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		return proxyRule;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (uri.protocol === 'https:') {
 | 
					 | 
				
			||||||
		proxyRules.forEach((proxy: string) => {
 | 
					 | 
				
			||||||
			if (proxy.includes('https=')) {
 | 
					 | 
				
			||||||
				proxyRule.hostname = proxy.split('https=')[1].trim().split(':')[0];
 | 
					 | 
				
			||||||
				proxyRule.port = proxy.split('https=')[1].trim().split(':')[1];
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
		return proxyRule;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// TODO: Refactor to async function
 | 
					// TODO: Refactor to async function
 | 
				
			||||||
export async function resolveSystemProxy(mainWindow: Electron.BrowserWindow): Promise<void> {
 | 
					export async function resolveSystemProxy(mainWindow: Electron.BrowserWindow): Promise<void> {
 | 
				
			||||||
	const page = mainWindow.webContents;
 | 
						const page = mainWindow.webContents;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,11 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import type WebView from '../components/webview';
 | 
					 | 
				
			||||||
import backoff from 'backoff';
 | 
					import backoff from 'backoff';
 | 
				
			||||||
import request from 'request';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import type WebView from '../components/webview';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Logger from './logger-util';
 | 
					import Logger from './logger-util';
 | 
				
			||||||
import * as RequestUtil from './request-util';
 | 
					 | 
				
			||||||
import * as DomainUtil from './domain-util';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const logger = new Logger({
 | 
					const logger = new Logger({
 | 
				
			||||||
	file: 'domain-util.log',
 | 
						file: 'domain-util.log',
 | 
				
			||||||
@@ -33,28 +33,7 @@ export default class ReconnectUtil {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async isOnline(): Promise<boolean> {
 | 
						async isOnline(): Promise<boolean> {
 | 
				
			||||||
		return new Promise(resolve => {
 | 
							return ipcRenderer.invoke('is-online', this.url);
 | 
				
			||||||
			try {
 | 
					 | 
				
			||||||
				const ignoreCerts = DomainUtil.shouldIgnoreCerts(this.url);
 | 
					 | 
				
			||||||
				if (ignoreCerts === null) {
 | 
					 | 
				
			||||||
					return;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				request(
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						url: `${this.url}/static/favicon.ico`,
 | 
					 | 
				
			||||||
						...RequestUtil.requestOptions(this.url, ignoreCerts)
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					(error: Error, response: request.Response) => {
 | 
					 | 
				
			||||||
						const isValidResponse =
 | 
					 | 
				
			||||||
							!error && response.statusCode >= 200 && response.statusCode < 400;
 | 
					 | 
				
			||||||
						resolve(isValidResponse);
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
			} catch (error) {
 | 
					 | 
				
			||||||
				logger.log(error);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pollInternetAndReload(): void {
 | 
						pollInternetAndReload(): void {
 | 
				
			||||||
@@ -82,7 +61,7 @@ export default class ReconnectUtil {
 | 
				
			|||||||
		logger.log('There is no internet connection, try checking network cables, modem and router.');
 | 
							logger.log('There is no internet connection, try checking network cables, modem and router.');
 | 
				
			||||||
		const errorMessageHolder = document.querySelector('#description');
 | 
							const errorMessageHolder = document.querySelector('#description');
 | 
				
			||||||
		if (errorMessageHolder) {
 | 
							if (errorMessageHolder) {
 | 
				
			||||||
			errorMessageHolder.innerHTML = `
 | 
								errorMessageHolder.innerHTML = htmlEscape`
 | 
				
			||||||
						<div>Your internet connection doesn't seem to work properly!</div>
 | 
											<div>Your internet connection doesn't seem to work properly!</div>
 | 
				
			||||||
						<div>Verify that it works and then click try again.</div>`;
 | 
											<div>Verify that it works and then click try again.</div>`;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,71 +0,0 @@
 | 
				
			|||||||
import {remote} from 'electron';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import fs from 'fs';
 | 
					 | 
				
			||||||
import path from 'path';
 | 
					 | 
				
			||||||
import * as ConfigUtil from './config-util';
 | 
					 | 
				
			||||||
import Logger from './logger-util';
 | 
					 | 
				
			||||||
import * as ProxyUtil from './proxy-util';
 | 
					 | 
				
			||||||
import * as CertificateUtil from './certificate-util';
 | 
					 | 
				
			||||||
import * as SystemUtil from './system-util';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const {app} = remote;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const logger = new Logger({
 | 
					 | 
				
			||||||
	file: 'request-util.log',
 | 
					 | 
				
			||||||
	timestamp: true
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface RequestUtilResponse {
 | 
					 | 
				
			||||||
	ca: string;
 | 
					 | 
				
			||||||
	proxy: string | void | ProxyUtil.ProxyRule;
 | 
					 | 
				
			||||||
	ecdhCurve: 'auto';
 | 
					 | 
				
			||||||
	headers: { 'User-Agent': string };
 | 
					 | 
				
			||||||
	rejectUnauthorized: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ignoreCerts parameter helps in fetching server icon and
 | 
					 | 
				
			||||||
// other server details when user chooses to ignore certificate warnings
 | 
					 | 
				
			||||||
export function requestOptions(domain: string, ignoreCerts: boolean): RequestUtilResponse {
 | 
					 | 
				
			||||||
	domain = formatUrl(domain);
 | 
					 | 
				
			||||||
	const certificate = CertificateUtil.getCertificate(
 | 
					 | 
				
			||||||
		encodeURIComponent(domain)
 | 
					 | 
				
			||||||
	);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let certificateFile = null;
 | 
					 | 
				
			||||||
	if (certificate?.includes('/')) {
 | 
					 | 
				
			||||||
		// Certificate saved using old app version
 | 
					 | 
				
			||||||
		certificateFile = certificate;
 | 
					 | 
				
			||||||
	} else if (certificate) {
 | 
					 | 
				
			||||||
		certificateFile = path.join(`${app.getPath('userData')}/certificates`, certificate);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	let certificateLocation = '';
 | 
					 | 
				
			||||||
	if (certificate) {
 | 
					 | 
				
			||||||
		// To handle case where certificate has been moved from the location in certificates.json
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			certificateLocation = fs.readFileSync(certificateFile, 'utf8');
 | 
					 | 
				
			||||||
		} catch (error) {
 | 
					 | 
				
			||||||
			logger.warn('Error while trying to get certificate:', error);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
 | 
					 | 
				
			||||||
	// If certificate for the domain exists add it as a ca key in the request's parameter else consider only domain as the parameter for request
 | 
					 | 
				
			||||||
	// Add proxy as a parameter if it is being used.
 | 
					 | 
				
			||||||
	return {
 | 
					 | 
				
			||||||
		ca: certificateLocation ? certificateLocation : '',
 | 
					 | 
				
			||||||
		proxy: proxyEnabled ? ProxyUtil.getProxy(domain) : '',
 | 
					 | 
				
			||||||
		ecdhCurve: 'auto',
 | 
					 | 
				
			||||||
		headers: {'User-Agent': SystemUtil.getUserAgent()},
 | 
					 | 
				
			||||||
		rejectUnauthorized: !ignoreCerts
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function formatUrl(domain: string): string {
 | 
					 | 
				
			||||||
	const hasPrefix = domain.startsWith('http', 0);
 | 
					 | 
				
			||||||
	if (hasPrefix) {
 | 
					 | 
				
			||||||
		return domain;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return domain.includes('localhost:') ? `http://${domain}` : `https://${domain}`;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
import {init} from '@sentry/electron';
 | 
					import {init} from '@sentry/electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import isDev from 'electron-is-dev';
 | 
					import isDev from 'electron-is-dev';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const sentryInit = (): void => {
 | 
					export const sentryInit = (): void => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
import {ipcRenderer} from 'electron';
 | 
					import {ipcRenderer} from 'electron';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import os from 'os';
 | 
					import os from 'os';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const connectivityERR: string[] = [
 | 
					export const connectivityERR: string[] = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,12 @@
 | 
				
			|||||||
import path from 'path';
 | 
					import path from 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import i18n from 'i18n';
 | 
					import i18n from 'i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as ConfigUtil from './config-util';
 | 
					import * as ConfigUtil from './config-util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
i18n.configure({
 | 
					i18n.configure({
 | 
				
			||||||
	directory: path.join(__dirname, '../../../translations/')
 | 
						directory: path.join(__dirname, '../../../translations/'),
 | 
				
			||||||
 | 
						updateFiles: false
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Fetches the current appLocale from settings.json */
 | 
					/* Fetches the current appLocale from settings.json */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,7 +52,7 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div id="feedback-modal">
 | 
					        <div id="feedback-modal">
 | 
				
			||||||
            <send-feedback></send-feedback>
 | 
					            <send-feedback show-cancel-button="show"></send-feedback>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
        <meta name="viewport" content="width=device-width">
 | 
					        <meta name="viewport" content="width=device-width">
 | 
				
			||||||
        <title>Zulip - Settings</title>
 | 
					        <title>Zulip - Settings</title>
 | 
				
			||||||
        <link rel="stylesheet" href="css/preference.css" type="text/css" media="screen">
 | 
					        <link rel="stylesheet" href="css/preference.css" type="text/css" media="screen">
 | 
				
			||||||
 | 
					        <link id="tagify-css" rel="stylesheet" href="data:text/css,">
 | 
				
			||||||
    </head>
 | 
					    </head>
 | 
				
			||||||
    <body>
 | 
					    <body>
 | 
				
			||||||
        <div id="content">
 | 
					        <div id="content">
 | 
				
			||||||
@@ -13,6 +14,7 @@
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
    <script>
 | 
					    <script>
 | 
				
			||||||
 | 
					        document.querySelector('#tagify-css').href = require.resolve('@yaireo/tagify/dist/tagify.css');
 | 
				
			||||||
        require('./js/pages/preference/preference.js');
 | 
					        require('./js/pages/preference/preference.js');
 | 
				
			||||||
        require('./js/shared/preventdrag.js')
 | 
					        require('./js/shared/preventdrag.js')
 | 
				
			||||||
    </script>
 | 
					    </script>
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 106 KiB  | 
| 
		 Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.6 KiB  | 
@@ -19,14 +19,12 @@ export function noOrgsError(domain: string): string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function certErrorMessage(domain: string, error: string): string {
 | 
					export function certErrorMessage(domain: string, error: string): string {
 | 
				
			||||||
	return `Do you trust certificate from ${domain}? \n ${error}`;
 | 
						return `Certificate error for ${domain}\n${error}`;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function certErrorDetail(): string {
 | 
					export function certErrorDetail(): string {
 | 
				
			||||||
	return `The organization you're connecting to is either someone impersonating the Zulip server you entered, or the server you're trying to connect to is configured in an insecure way.
 | 
						return `The organization you're connecting to is either someone impersonating the Zulip server you entered, or the server you're trying to connect to is configured in an insecure way.
 | 
				
			||||||
	\nIf you have a valid certificate please add it from Settings>Organizations and try to add the organization again.
 | 
						\nIf you have a valid certificate please add it from Settings>Organizations and try to add the organization again.`;
 | 
				
			||||||
	\nUnless you have a good reason to believe otherwise, you should not proceed.
 | 
					 | 
				
			||||||
	\nYou can click here if you'd like to proceed with the connection.`;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function enterpriseOrgError(length: number, domains: string[]): DialogBoxError {
 | 
					export function enterpriseOrgError(length: number, domains: string[]): DialogBoxError {
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 436 B  | 
| 
		 Before Width: | Height: | Size: 900 B  | 
| 
		 Before Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 1.4 KiB  | 
| 
		 Before Width: | Height: | Size: 737 B  | 
| 
		 Before Width: | Height: | Size: 1.7 KiB  | 
| 
		 Before Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 3.8 KiB  | 
| 
		 Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 1.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/resources/tray/traymacOSTemplate.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 321 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/resources/tray/traymacOSTemplate@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 631 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/resources/tray/traymacOSTemplate@3x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 932 B  | 
							
								
								
									
										
											BIN
										
									
								
								app/resources/tray/traymacOSTemplate@4x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 106 KiB  | 
| 
		 Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 13 KiB  | 
							
								
								
									
										134
									
								
								app/translations/ar.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "حول \"زوليب\"",
 | 
				
			||||||
 | 
						"Actual Size": "الحجم الفعلي",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "إضافة رخصة معدلة",
 | 
				
			||||||
 | 
						"Add Organization": "إضافة منظمة",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "إضافة منظمة \"زوليب\"",
 | 
				
			||||||
 | 
						"Add custom CSS": "إصافة CSS معدلة",
 | 
				
			||||||
 | 
						"Advanced": "متقدم",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "جميع المنظمات المتصلة تعرض هنا",
 | 
				
			||||||
 | 
						"Always start minimized": "دائماً إبدأ بالقليل",
 | 
				
			||||||
 | 
						"App Updates": "تحديث التطبيق",
 | 
				
			||||||
 | 
						"Appearance": "المظهر",
 | 
				
			||||||
 | 
						"Application Shortcuts": "إختصارات التطبيق",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "هل أنت متأكد من فصل هذة المنظمة؟",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "أخف القائمة تلقائياً",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "أخف القائمة تلقائياً (إضغط Alt لعرض القائمة)",
 | 
				
			||||||
 | 
						"Back": "رجوع",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "أخرج المنصة في حال رسالة خاصة جديدة",
 | 
				
			||||||
 | 
						"Certificate file": "ملف الشهادة",
 | 
				
			||||||
 | 
						"Change": "تغيير",
 | 
				
			||||||
 | 
						"Check for Updates": "التحقق من التحديثات",
 | 
				
			||||||
 | 
						"Close": "اغلاق",
 | 
				
			||||||
 | 
						"Connect": "اتصال",
 | 
				
			||||||
 | 
						"Connect to another organization": "التوصيل مع منظمة أخرى",
 | 
				
			||||||
 | 
						"Connected organizations": "المنظمات المتصلة",
 | 
				
			||||||
 | 
						"Copy": "نسخ",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "نسخ رابط زوليب",
 | 
				
			||||||
 | 
						"Create a new organization": "Create a new organization",
 | 
				
			||||||
 | 
						"Cut": "قص",
 | 
				
			||||||
 | 
						"Default download location": "موقع التحميل الافتراضي",
 | 
				
			||||||
 | 
						"Delete": "حذف",
 | 
				
			||||||
 | 
						"Desktop App Settings": "إعدادت تطبيق سطح المكتب",
 | 
				
			||||||
 | 
						"Desktop Notifications": "إشعارات سطح المكتب",
 | 
				
			||||||
 | 
						"Desktop Settings": "إعدادات سطح المكتب",
 | 
				
			||||||
 | 
						"Disconnect": "قطع الاتصال",
 | 
				
			||||||
 | 
						"Download App Logs": "تنزيل سجلات التطبيق",
 | 
				
			||||||
 | 
						"Edit": "تعديل",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "تعديل الاختصارات",
 | 
				
			||||||
 | 
						"Enable auto updates": "تفعيل التحديثات التلقائية",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "تفعيل تقارير الأخطاء (يتطلب إعادة التشغيل)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "إعادة ضبط المصنع",
 | 
				
			||||||
 | 
						"File": "ملف",
 | 
				
			||||||
 | 
						"Find accounts": "Find accounts",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Help",
 | 
				
			||||||
 | 
						"Help Center": "Help Center",
 | 
				
			||||||
 | 
						"History": "History",
 | 
				
			||||||
 | 
						"History Shortcuts": "History Shortcuts",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "OR",
 | 
				
			||||||
 | 
						"Organization URL": "Organization URL",
 | 
				
			||||||
 | 
						"Organizations": "Organizations",
 | 
				
			||||||
 | 
						"Paste": "Paste",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Quit",
 | 
				
			||||||
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Save",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Settings",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"About Zulip": "За Zulip",
 | 
						"About Zulip": "Относно Zulip",
 | 
				
			||||||
	"Actual Size": "Действителен размер",
 | 
						"Actual Size": "Действителен размер",
 | 
				
			||||||
	"Add Custom Certificates": "Добавяне на персонализирани сертификати",
 | 
						"Add Custom Certificates": "Добавяне на персонализирани сертификати",
 | 
				
			||||||
	"Add Organization": "Добавяне на организация",
 | 
						"Add Organization": "Добавяне на организация",
 | 
				
			||||||
@@ -75,9 +75,6 @@
 | 
				
			|||||||
	"Release Notes": "Бележки към изданието",
 | 
						"Release Notes": "Бележки към изданието",
 | 
				
			||||||
	"Reload": "Презареди",
 | 
						"Reload": "Презареди",
 | 
				
			||||||
	"Report an Issue": "Подаване на сигнал за проблем",
 | 
						"Report an Issue": "Подаване на сигнал за проблем",
 | 
				
			||||||
	"Reset App Data": "Нулиране на данни за приложения",
 | 
					 | 
				
			||||||
	"Reset App Settings": "Нулиране на настройките на приложението",
 | 
					 | 
				
			||||||
	"Reset Application Data": "Нулиране на данните на приложението",
 | 
					 | 
				
			||||||
	"Save": "Запази",
 | 
						"Save": "Запази",
 | 
				
			||||||
	"Select All": "Избери всички",
 | 
						"Select All": "Избери всички",
 | 
				
			||||||
	"Settings": "Настройки",
 | 
						"Settings": "Настройки",
 | 
				
			||||||
@@ -113,5 +110,25 @@
 | 
				
			|||||||
	"Zoom Out": "Отдалечавам",
 | 
						"Zoom Out": "Отдалечавам",
 | 
				
			||||||
	"Zulip Help": "Помощ за Zulip",
 | 
						"Zulip Help": "Помощ за Zulip",
 | 
				
			||||||
	"keyboard shortcuts": "комбинация от клавиши",
 | 
						"keyboard shortcuts": "комбинация от клавиши",
 | 
				
			||||||
	"script": "писменост"
 | 
						"script": "писменост",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								app/translations/ca.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "Quant a Zulip",
 | 
				
			||||||
 | 
						"Actual Size": "Actual Size",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Add Custom Certificates",
 | 
				
			||||||
 | 
						"Add Organization": "Add Organization",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "Add a Zulip organization",
 | 
				
			||||||
 | 
						"Add custom CSS": "Add custom CSS",
 | 
				
			||||||
 | 
						"Advanced": "Advanced",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "All the connected organizations will appear here",
 | 
				
			||||||
 | 
						"Always start minimized": "Always start minimized",
 | 
				
			||||||
 | 
						"App Updates": "App Updates",
 | 
				
			||||||
 | 
						"Appearance": "Appearance",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Application Shortcuts",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Auto hide Menu bar",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
				
			||||||
 | 
						"Back": "Back",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certificate file",
 | 
				
			||||||
 | 
						"Change": "Change",
 | 
				
			||||||
 | 
						"Check for Updates": "Check for Updates",
 | 
				
			||||||
 | 
						"Close": "Tancar",
 | 
				
			||||||
 | 
						"Connect": "Connect",
 | 
				
			||||||
 | 
						"Connect to another organization": "Connect to another organization",
 | 
				
			||||||
 | 
						"Connected organizations": "Connected organizations",
 | 
				
			||||||
 | 
						"Copy": "Copia",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "Copy Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Crea una nova organització",
 | 
				
			||||||
 | 
						"Cut": "Cut",
 | 
				
			||||||
 | 
						"Default download location": "Default download location",
 | 
				
			||||||
 | 
						"Delete": "Elimina",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Configuració de l'aplicació d'escriptori",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop Notifications",
 | 
				
			||||||
 | 
						"Desktop Settings": "Configuració d'escriptori",
 | 
				
			||||||
 | 
						"Disconnect": "Disconnect",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "Edita",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Edit Shortcuts",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "Fitxer",
 | 
				
			||||||
 | 
						"Find accounts": "Find accounts",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Help",
 | 
				
			||||||
 | 
						"Help Center": "Centre d'ajuda",
 | 
				
			||||||
 | 
						"History": "Historial",
 | 
				
			||||||
 | 
						"History Shortcuts": "Dreceres d'historial",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Silencia tots els sons de Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "OR",
 | 
				
			||||||
 | 
						"Organization URL": "URL d'organització",
 | 
				
			||||||
 | 
						"Organizations": "Organizations",
 | 
				
			||||||
 | 
						"Paste": "Paste",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Quit",
 | 
				
			||||||
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Guardar",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Configuració",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Mostrar una marca en la icona si hi ha missatges no llegits",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Pujada",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,117 +1,134 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"About Zulip": "O uživateli Zulip",
 | 
						"About Zulip": "O Zulipu",
 | 
				
			||||||
	"Actual Size": "Aktuální velikost",
 | 
						"Actual Size": "Skutečná velikost",
 | 
				
			||||||
	"Add Custom Certificates": "Přidat vlastní certifikáty",
 | 
						"Add Custom Certificates": "Přidat vlastní certifikáty",
 | 
				
			||||||
	"Add Organization": "Přidat organizaci",
 | 
						"Add Organization": "Přidat organizaci",
 | 
				
			||||||
	"Add a Zulip organization": "Přidejte organizaci Zulip",
 | 
						"Add a Zulip organization": "Přidat organizaci Zulip",
 | 
				
			||||||
	"Add custom CSS": "Přidat vlastní CSS",
 | 
						"Add custom CSS": "Přidat vlastní CSS",
 | 
				
			||||||
	"Advanced": "Pokročilý",
 | 
						"Advanced": "Rozšířené",
 | 
				
			||||||
	"All the connected organizations will appear here": "Zde se zobrazí všechny připojené organizace",
 | 
						"All the connected organizations will appear here": "Všechny připojené organizace se objeví zde",
 | 
				
			||||||
	"Always start minimized": "Vždy začněte minimalizovat",
 | 
						"Always start minimized": "Vždy spouštět minimalizované",
 | 
				
			||||||
	"App Updates": "Aktualizace aplikací",
 | 
						"App Updates": "Aktualizace aplikace",
 | 
				
			||||||
	"Appearance": "Vzhled",
 | 
						"Appearance": "Vzhled",
 | 
				
			||||||
	"Application Shortcuts": "Zkratky aplikací",
 | 
						"Application Shortcuts": "Zkratky programu",
 | 
				
			||||||
	"Are you sure you want to disconnect this organization?": "Opravdu chcete tuto organizaci odpojit?",
 | 
						"Are you sure you want to disconnect this organization?": "Opravdu chcete odpojit tuto organizaci?",
 | 
				
			||||||
	"Auto hide Menu bar": "Auto hide Menu bar",
 | 
						"Auto hide Menu bar": "Automaticky skrývat menu",
 | 
				
			||||||
	"Auto hide menu bar (Press Alt key to display)": "Automaticky skrýt lištu menu (stiskněte klávesu Alt pro zobrazení)",
 | 
						"Auto hide menu bar (Press Alt key to display)": "Automaticky skrývat menu (pro zobrazení stiskněte klávesu Alt)",
 | 
				
			||||||
	"Back": "Zadní",
 | 
						"Back": "Zpět",
 | 
				
			||||||
	"Bounce dock on new private message": "Bounce dock na nové soukromé zprávy",
 | 
						"Bounce dock on new private message": "Poskakování ikony v docku po obdržení nové soukromé zprávy",
 | 
				
			||||||
	"Certificate file": "Soubor certifikátu",
 | 
						"Certificate file": "Soubor s certifikátem",
 | 
				
			||||||
	"Change": "Změna",
 | 
						"Change": "Změnit",
 | 
				
			||||||
	"Check for Updates": "Kontrola aktualizací",
 | 
						"Check for Updates": "Zkontrolovat aktualizace",
 | 
				
			||||||
	"Close": "Zavřít",
 | 
						"Close": "Zavřít",
 | 
				
			||||||
	"Connect": "Připojit",
 | 
						"Connect": "Připojit se",
 | 
				
			||||||
	"Connect to another organization": "Připojte se k jiné organizaci",
 | 
						"Connect to another organization": "Připojit se k jiné organizaci",
 | 
				
			||||||
	"Connected organizations": "Připojené organizace",
 | 
						"Connected organizations": "Připojené organizace",
 | 
				
			||||||
	"Copy": "kopírovat",
 | 
						"Copy": "Kopírovat",
 | 
				
			||||||
	"Copy Zulip URL": "Kopírovat adresu URL aplikace Zulip",
 | 
						"Copy Zulip URL": "Kopírovat adresu (URL) Zulipu",
 | 
				
			||||||
	"Create a new organization": "Vytvořit novou organizaci",
 | 
						"Create a new organization": "Vytvořit novou organizaci",
 | 
				
			||||||
	"Cut": "Střih",
 | 
						"Cut": "Vyjmout",
 | 
				
			||||||
	"Default download location": "Výchozí umístění stahování",
 | 
						"Default download location": "Výchozí umístění stahování",
 | 
				
			||||||
	"Delete": "Smazat",
 | 
						"Delete": "Smazat",
 | 
				
			||||||
	"Desktop App Settings": "Nastavení aplikace Desktop",
 | 
						"Desktop App Settings": "Nastavení desktopové aplikace",
 | 
				
			||||||
	"Desktop Notifications": "Oznámení o pracovní ploše",
 | 
						"Desktop Notifications": "Oznámení na ploše",
 | 
				
			||||||
	"Desktop Settings": "Nastavení plochy",
 | 
						"Desktop Settings": "Nastavení plochy",
 | 
				
			||||||
	"Disconnect": "Odpojit",
 | 
						"Disconnect": "Odpojit",
 | 
				
			||||||
	"Download App Logs": "Stáhnout App Logs",
 | 
						"Download App Logs": "Stáhnout záznamy programu",
 | 
				
			||||||
	"Edit": "Upravit",
 | 
						"Edit": "Upravit",
 | 
				
			||||||
	"Edit Shortcuts": "Upravit zkratky",
 | 
						"Edit Shortcuts": "Upravit zkratky",
 | 
				
			||||||
	"Enable auto updates": "Povolit automatické aktualizace",
 | 
						"Enable auto updates": "Povolit automatické aktualizace",
 | 
				
			||||||
	"Enable error reporting (requires restart)": "Povolit hlášení chyb (vyžaduje restart)",
 | 
						"Enable error reporting (requires restart)": "Povolit hlášení chyb (vyžaduje opětovné spuštění programu)",
 | 
				
			||||||
	"Enable spellchecker (requires restart)": "Povolit kontrolu pravopisu (vyžaduje restartování)",
 | 
						"Enable spellchecker (requires restart)": "Povolit kontrolu pravopisu (vyžaduje opětovné spuštění programu)",
 | 
				
			||||||
	"Factory Reset": "Tovární nastavení",
 | 
						"Factory Reset": "Obnovení do továrního nastavení",
 | 
				
			||||||
	"File": "Soubor",
 | 
						"File": "Soubor",
 | 
				
			||||||
	"Find accounts": "Najít účty",
 | 
						"Find accounts": "Najít účty",
 | 
				
			||||||
	"Find accounts by email": "Najít účty e-mailem",
 | 
						"Find accounts by email": "Najít účty podle adresy elektronické pošty",
 | 
				
			||||||
	"Flash taskbar on new message": "Hlavní panel Flash na nové zprávě",
 | 
						"Flash taskbar on new message": "Blikání ikony v hlavním panelu při obdržení nové zprávy",
 | 
				
			||||||
	"Forward": "Vpřed",
 | 
						"Forward": "Vpřed",
 | 
				
			||||||
	"Functionality": "Funkčnost",
 | 
						"Functionality": "Funkce",
 | 
				
			||||||
	"General": "Všeobecné",
 | 
						"General": "Obecné",
 | 
				
			||||||
	"Get beta updates": "Získejte aktualizace beta",
 | 
						"Get beta updates": "Dostávat beta aktualizace",
 | 
				
			||||||
	"Hard Reload": "Hard Reload",
 | 
						"Hard Reload": "Tvrdé znovunahrání",
 | 
				
			||||||
	"Help": "Pomoc",
 | 
						"Help": "Nápověda",
 | 
				
			||||||
	"Help Center": "Centrum nápovědy",
 | 
						"Help Center": "Centrum nápovědy",
 | 
				
			||||||
	"History": "Dějiny",
 | 
						"History": "Historie",
 | 
				
			||||||
	"History Shortcuts": "Zkratky historie",
 | 
						"History Shortcuts": "Zkratky pro historii",
 | 
				
			||||||
	"Keyboard Shortcuts": "Klávesové zkratky",
 | 
						"Keyboard Shortcuts": "Klávesové zkratky",
 | 
				
			||||||
	"Log Out": "Odhlásit se",
 | 
						"Log Out": "Odhlásit se",
 | 
				
			||||||
	"Log Out of Organization": "Odhlásit se z organizace",
 | 
						"Log Out of Organization": "Odhlásit se z organizace",
 | 
				
			||||||
	"Manual proxy configuration": "Ruční konfigurace proxy",
 | 
						"Manual proxy configuration": "Ruční nastavení proxy",
 | 
				
			||||||
	"Minimize": "Minimalizovat",
 | 
						"Minimize": "Minimalizovat",
 | 
				
			||||||
	"Mute all sounds from Zulip": "Vypněte všechny zvuky ze Zulipu",
 | 
						"Mute all sounds from Zulip": "Ztlumit všechny zvuky ze Zulipu",
 | 
				
			||||||
	"NO": "NE",
 | 
						"NO": "NE",
 | 
				
			||||||
	"Network": "Síť",
 | 
						"Network": "Síť",
 | 
				
			||||||
	"OR": "NEBO",
 | 
						"OR": "NEBO",
 | 
				
			||||||
	"Organization URL": "URL organizace",
 | 
						"Organization URL": "Adresa organizace",
 | 
				
			||||||
	"Organizations": "Organizace",
 | 
						"Organizations": "Organizace",
 | 
				
			||||||
	"Paste": "Vložit",
 | 
						"Paste": "Vložit",
 | 
				
			||||||
	"Paste and Match Style": "Vložit a shodit styl",
 | 
						"Paste and Match Style": "Vložit a ponechat styl",
 | 
				
			||||||
	"Proxy": "Proxy",
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
	"Proxy bypass rules": "Proxy bypass pravidla",
 | 
						"Proxy bypass rules": "Pravidla pro obejití Proxy",
 | 
				
			||||||
	"Proxy rules": "Proxy pravidla",
 | 
						"Proxy rules": "Pravidla Proxy",
 | 
				
			||||||
	"Quit": "Přestat",
 | 
						"Quit": "Ukončit",
 | 
				
			||||||
	"Quit Zulip": "Ukončete Zulip",
 | 
						"Quit Zulip": "Ukončit Zulip",
 | 
				
			||||||
	"Redo": "Předělat",
 | 
						"Redo": "Znovu",
 | 
				
			||||||
	"Release Notes": "Poznámky k vydání",
 | 
						"Release Notes": "Poznámky k této verzi",
 | 
				
			||||||
	"Reload": "Znovu načíst",
 | 
						"Reload": "Nahrát znovu",
 | 
				
			||||||
	"Report an Issue": "Nahlásit problém",
 | 
						"Report an Issue": "Nahlásit problém",
 | 
				
			||||||
	"Reset App Data": "Resetovat data aplikace",
 | 
					 | 
				
			||||||
	"Reset App Settings": "Obnovit nastavení aplikace",
 | 
					 | 
				
			||||||
	"Reset Application Data": "Resetovat data aplikace",
 | 
					 | 
				
			||||||
	"Save": "Uložit",
 | 
						"Save": "Uložit",
 | 
				
			||||||
	"Select All": "Vybrat vše",
 | 
						"Select All": "Vybrat vše",
 | 
				
			||||||
	"Settings": "Nastavení",
 | 
						"Settings": "Nastavení",
 | 
				
			||||||
	"Shortcuts": "Zkratky",
 | 
						"Shortcuts": "Zkratky",
 | 
				
			||||||
	"Show App Logs": "Zobrazit protokoly aplikací",
 | 
						"Show App Logs": "Zobrazit záznamy programu",
 | 
				
			||||||
	"Show app icon in system tray": "Zobrazit ikonu aplikace v systémové liště",
 | 
						"Show app icon in system tray": "Zobrazovat ikonu programu v oznamovací oblasti panelu",
 | 
				
			||||||
	"Show app unread badge": "Zobrazit nepřečtený odznak aplikace",
 | 
						"Show app unread badge": "Zobrazovat u ikony aplikace symbol nepřečteno",
 | 
				
			||||||
	"Show desktop notifications": "Zobrazit oznámení na ploše",
 | 
						"Show desktop notifications": "Zobrazovat oznámení na ploše",
 | 
				
			||||||
	"Show downloaded files in file manager": "Zobrazit stažené soubory ve správci souborů",
 | 
						"Show downloaded files in file manager": "Zobrazit stažené soubory ve správci souborů",
 | 
				
			||||||
	"Show sidebar": "Zobrazit postranní panel",
 | 
						"Show sidebar": "Zobrazovat postranní panel",
 | 
				
			||||||
	"Start app at login": "Spuštění aplikace při přihlášení",
 | 
						"Start app at login": "Spustit program při přihlášení",
 | 
				
			||||||
	"Switch to Next Organization": "Přepnout na další organizaci",
 | 
						"Switch to Next Organization": "Přepnout na další organizaci",
 | 
				
			||||||
	"Switch to Previous Organization": "Přepněte na předchozí organizaci",
 | 
						"Switch to Previous Organization": "Přepnout na předchozí organizaci",
 | 
				
			||||||
	"These desktop app shortcuts extend the Zulip webapp's": "Tyto klávesové zkratky pro aplikaci na ploše rozšiřují aplikaci Zulip webapp",
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "Tyto zkratky rozšiřují webovou aplikaci Zulipu",
 | 
				
			||||||
	"This will delete all application data including all added accounts and preferences": "Tím odstraníte všechna data aplikace včetně všech přidaných účtů a předvoleb",
 | 
						"This will delete all application data including all added accounts and preferences": "Toto smaže všechna data programu včetně všech přidaných účtů a nastavení",
 | 
				
			||||||
	"Tip": "Tip",
 | 
						"Tip": "Tip",
 | 
				
			||||||
	"Toggle DevTools for Active Tab": "Přepnout DevTools pro Active Tab",
 | 
						"Toggle DevTools for Active Tab": "Přepnout vývojářské nástroje pro aktivní kartu",
 | 
				
			||||||
	"Toggle DevTools for Zulip App": "Přepnout DevTools pro Zulip App",
 | 
						"Toggle DevTools for Zulip App": "Přepnout vývojářské nástroje pro program Zulip",
 | 
				
			||||||
	"Toggle Do Not Disturb": "Přepnout Do Nerušit",
 | 
						"Toggle Do Not Disturb": "Přepnout mód Nerušit",
 | 
				
			||||||
	"Toggle Full Screen": "Přepnout na celou obrazovku",
 | 
						"Toggle Full Screen": "Přepnout na celou obrazovku",
 | 
				
			||||||
	"Toggle Sidebar": "Přepnout postranní panel",
 | 
						"Toggle Sidebar": "Přepnout zobrazení postranního panelu",
 | 
				
			||||||
	"Toggle Tray Icon": "Přepnout ikonu zásobníku",
 | 
						"Toggle Tray Icon": "Přepnout ikonu v oznamovací oblasti panelu",
 | 
				
			||||||
	"Tools": "Nástroje",
 | 
						"Tools": "Nástroje",
 | 
				
			||||||
	"Undo": "vrátit",
 | 
						"Undo": "Zpět",
 | 
				
			||||||
	"Upload": "nahrát",
 | 
						"Upload": "Nahrát",
 | 
				
			||||||
	"Use system proxy settings (requires restart)": "Použít nastavení proxy serveru (vyžaduje restart)",
 | 
						"Use system proxy settings (requires restart)": "Použít systémová nastavení proxy (vyžaduje opětovné spuštění programu)",
 | 
				
			||||||
	"View": "Pohled",
 | 
						"View": "Zobrazení",
 | 
				
			||||||
	"View Shortcuts": "Zobrazit zástupce",
 | 
						"View Shortcuts": "Zobrazit zkratky",
 | 
				
			||||||
	"Window": "Okno",
 | 
						"Window": "Okno",
 | 
				
			||||||
	"Window Shortcuts": "Klávesové zkratky",
 | 
						"Window Shortcuts": "Zkratky pro okno",
 | 
				
			||||||
	"YES": "ANO",
 | 
						"YES": "ANO",
 | 
				
			||||||
	"Zoom In": "Přiblížit",
 | 
						"Zoom In": "Přiblížit",
 | 
				
			||||||
	"Zoom Out": "Oddálit",
 | 
						"Zoom Out": "Oddálit",
 | 
				
			||||||
	"Zulip Help": "Nápověda Zulip",
 | 
						"Zulip Help": "Nápověda Zulipu",
 | 
				
			||||||
	"keyboard shortcuts": "klávesové zkratky",
 | 
						"keyboard shortcuts": "klávesové zkratky",
 | 
				
			||||||
	"script": "skript"
 | 
						"script": "skript",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Ukončit, když je okno zavřeno",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Před stažením se zeptat kam uložit soubory",
 | 
				
			||||||
 | 
						"Services": "Služby",
 | 
				
			||||||
 | 
						"Hide": "Skrýt",
 | 
				
			||||||
 | 
						"Hide Others": "Skrýt jiné",
 | 
				
			||||||
 | 
						"Unhide": "Zobrazit",
 | 
				
			||||||
 | 
						"AddServer": "Přidat server",
 | 
				
			||||||
 | 
						"App language (requires restart)": "Jazyk programu (vyžaduje opětovné spuštění programu)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Obnovení dat do továrního nastavení",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Obnovit program do výchozího nastavení. čili smazat všechny připojené organizace, účty a certifikáty.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "Na macOS se používá kontrola pravopisu OS.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Změnit jazyk v Nastavení systému → Klávesnice → Text → Kontrola pravopisu.",
 | 
				
			||||||
 | 
						"Copy Link": "Kopírovat odkaz",
 | 
				
			||||||
 | 
						"Copy Image": "Kopírovat obrázek",
 | 
				
			||||||
 | 
						"Copy Image URL": "Kopírovat adresu (URL) obrázku",
 | 
				
			||||||
 | 
						"No Suggestion Found": "Nenalezen žádný návrh",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "Pro kontrolu pravopisu můžete vybrat nejvíce 3 jazyky.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Kontrola pravopisu jazyků",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Přidat do slovníku",
 | 
				
			||||||
 | 
						"Look Up": "Vyhledat"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								app/translations/da.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "Om Zulip",
 | 
				
			||||||
 | 
						"Actual Size": "Faktisk størrelse",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Tilføj egne certifikater",
 | 
				
			||||||
 | 
						"Add Organization": "Opret organisation",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "Opret en Zulip organisation",
 | 
				
			||||||
 | 
						"Add custom CSS": "Tilføj egen CSS",
 | 
				
			||||||
 | 
						"Advanced": "Avanceret",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "Alle de tilsluttede organisationer vil blive vist her",
 | 
				
			||||||
 | 
						"Always start minimized": "Start altid minimeret",
 | 
				
			||||||
 | 
						"App Updates": "App-opdateringer",
 | 
				
			||||||
 | 
						"Appearance": "Udseende",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Genveje",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Er du sikker på du vil frakoble denne organisation? ",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Skjul menu automatisk",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Skjul menu automatisk (tryk på Alt-tasten for at vise)",
 | 
				
			||||||
 | 
						"Back": "Tilbage",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certifikat-fil",
 | 
				
			||||||
 | 
						"Change": "Skift",
 | 
				
			||||||
 | 
						"Check for Updates": "Tjek for opdateringer",
 | 
				
			||||||
 | 
						"Close": "Luk",
 | 
				
			||||||
 | 
						"Connect": "Tilslut",
 | 
				
			||||||
 | 
						"Connect to another organization": "Forbind til en anden organisation",
 | 
				
			||||||
 | 
						"Connected organizations": "Tilsluttede organisationer",
 | 
				
			||||||
 | 
						"Copy": "Kopiér",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "Kopiér Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Opret ny organisation",
 | 
				
			||||||
 | 
						"Cut": "Klip",
 | 
				
			||||||
 | 
						"Default download location": "Default download placering",
 | 
				
			||||||
 | 
						"Delete": "Slet",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop app-indstillinger",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop-notifikationer",
 | 
				
			||||||
 | 
						"Desktop Settings": "Desktop-indstillinger",
 | 
				
			||||||
 | 
						"Disconnect": "Frakobl",
 | 
				
			||||||
 | 
						"Download App Logs": "Download app-logfiler",
 | 
				
			||||||
 | 
						"Edit": "Redigér",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Redigér genveje",
 | 
				
			||||||
 | 
						"Enable auto updates": "Aktivér auto-opdateringer",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Aktivér fejlrapportering (kræver genstart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Aktivér stavekontrol (kræver genstart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Nulstil til fabriksindstillinger",
 | 
				
			||||||
 | 
						"File": "Fil",
 | 
				
			||||||
 | 
						"Find accounts": "Find konti",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Funktionalitet",
 | 
				
			||||||
 | 
						"General": "Generelt",
 | 
				
			||||||
 | 
						"Get beta updates": "Få beta opdateringer",
 | 
				
			||||||
 | 
						"Hard Reload": "Hård reload",
 | 
				
			||||||
 | 
						"Help": "Hjælp",
 | 
				
			||||||
 | 
						"Help Center": "Hjælpecenter",
 | 
				
			||||||
 | 
						"History": "Historik",
 | 
				
			||||||
 | 
						"History Shortcuts": "Historik genveje",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Tastatur genveje",
 | 
				
			||||||
 | 
						"Log Out": "Log ud",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log ud af organisation",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manuel proxy opsætning",
 | 
				
			||||||
 | 
						"Minimize": "Minimér",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NEJ",
 | 
				
			||||||
 | 
						"Network": "Netværk",
 | 
				
			||||||
 | 
						"OR": "ELLER",
 | 
				
			||||||
 | 
						"Organization URL": "Organisation URL",
 | 
				
			||||||
 | 
						"Organizations": "Organisationer",
 | 
				
			||||||
 | 
						"Paste": "Indsæt",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Indsæt med samme formattering",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass regler",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy regler",
 | 
				
			||||||
 | 
						"Quit": "Luk",
 | 
				
			||||||
 | 
						"Quit Zulip": "Luk Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Save",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Settings",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,117 +1,134 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"About Zulip": "Über Zulip",
 | 
						"About Zulip": "Über Zulip",
 | 
				
			||||||
	"Actual Size": "Tatsächliche Größe",
 | 
						"Actual Size": "Tatsächliche Größe",
 | 
				
			||||||
	"Add Custom Certificates": "Benutzerdefinierte Zertifikate hinzufügen",
 | 
						"Add Custom Certificates": "Eigene Zertifikate hinzufügen",
 | 
				
			||||||
	"Add Organization": "Organisation hinzufügen",
 | 
						"Add Organization": "Organisation hinzufügen",
 | 
				
			||||||
	"Add a Zulip organization": "Fügen Sie eine Zulip-Organisation hinzu",
 | 
						"Add a Zulip organization": "Zulip-Organisation hinzufügen",
 | 
				
			||||||
	"Add custom CSS": "Fügen Sie benutzerdefiniertes CSS hinzu",
 | 
						"Add custom CSS": "Eigenes CSS hinzufügen",
 | 
				
			||||||
	"Advanced": "Fortgeschritten",
 | 
						"Advanced": "Erweitert",
 | 
				
			||||||
	"All the connected organizations will appear here": "Alle verbundenen Organisationen werden hier angezeigt",
 | 
						"All the connected organizations will appear here": "Alle verbundenen Organisationen erscheinen hier",
 | 
				
			||||||
	"Always start minimized": "Immer minimiert anfangen",
 | 
						"Always start minimized": "Immer minimiert öffnen",
 | 
				
			||||||
	"App Updates": "App-Updates",
 | 
						"App Updates": "App Updates",
 | 
				
			||||||
	"Appearance": "Aussehen",
 | 
						"Appearance": "Erscheinungsbild",
 | 
				
			||||||
	"Application Shortcuts": "Anwendungsverknüpfungen",
 | 
						"Application Shortcuts": "App-Verknüpfungen",
 | 
				
			||||||
	"Are you sure you want to disconnect this organization?": "Möchten Sie diese Organisation wirklich trennen?",
 | 
						"Are you sure you want to disconnect this organization?": "Bist du dir sicher, dass du die Verbindung zur Organisation trennen möchtest?",
 | 
				
			||||||
	"Auto hide Menu bar": "Menüleiste automatisch ausblenden",
 | 
						"Auto hide Menu bar": "Menü automatisch verstecken",
 | 
				
			||||||
	"Auto hide menu bar (Press Alt key to display)": "Menüleiste automatisch ausblenden (Drücken Sie die Alt-Taste, um anzuzeigen)",
 | 
						"Auto hide menu bar (Press Alt key to display)": "Menü automatisch verstecken (zum Anzeigen die Alt-Taste drücken)",
 | 
				
			||||||
	"Back": "Zurück",
 | 
						"Back": "Zurück",
 | 
				
			||||||
	"Bounce dock on new private message": "Bounce-Dock für neue private Nachricht",
 | 
						"Bounce dock on new private message": "Im Dock hüpfen, wenn neue private Nachrichten eingehen",
 | 
				
			||||||
	"Certificate file": "Zertifikatsdatei",
 | 
						"Certificate file": "Zertifikatsdatei",
 | 
				
			||||||
	"Change": "Veränderung",
 | 
						"Change": "Ändern",
 | 
				
			||||||
	"Check for Updates": "Auf Updates prüfen",
 | 
						"Check for Updates": "Auf Updates prüfen",
 | 
				
			||||||
	"Close": "Schließen",
 | 
						"Close": "Schließen",
 | 
				
			||||||
	"Connect": "Verbinden",
 | 
						"Connect": "Verbinden",
 | 
				
			||||||
	"Connect to another organization": "Stellen Sie eine Verbindung zu einer anderen Organisation her",
 | 
						"Connect to another organization": "Mit einer anderen Organisation verbinden",
 | 
				
			||||||
	"Connected organizations": "Verbundene Organisationen",
 | 
						"Connected organizations": "Verbundene Organisationen",
 | 
				
			||||||
	"Copy": "Kopieren",
 | 
						"Copy": "Kopieren",
 | 
				
			||||||
	"Copy Zulip URL": "Zulip-URL kopieren",
 | 
						"Copy Zulip URL": "Zulip-URL kopieren",
 | 
				
			||||||
	"Create a new organization": "Erstellen Sie eine neue Organisation",
 | 
						"Create a new organization": "Eine neue Organisation erstellen",
 | 
				
			||||||
	"Cut": "Schnitt",
 | 
						"Cut": "Ausschneiden",
 | 
				
			||||||
	"Default download location": "Standard-Download-Speicherort",
 | 
						"Default download location": "Voreingestelltes Ziel für Downloads",
 | 
				
			||||||
	"Delete": "Löschen",
 | 
						"Delete": "Löschen",
 | 
				
			||||||
	"Desktop App Settings": "Desktop App-Einstellungen",
 | 
						"Desktop App Settings": "Einstellungen für Desktop-App",
 | 
				
			||||||
	"Desktop Notifications": "Desktop-Benachrichtigungen",
 | 
						"Desktop Notifications": "Desktopbenachrichtigungen",
 | 
				
			||||||
	"Desktop Settings": "Desktop-Einstellungen",
 | 
						"Desktop Settings": "Desktop-Einstellungen",
 | 
				
			||||||
	"Disconnect": "Trennen",
 | 
						"Disconnect": "Verbindung trennen",
 | 
				
			||||||
	"Download App Logs": "App-Protokolle herunterladen",
 | 
						"Download App Logs": "Logdateien der App herunterladen",
 | 
				
			||||||
	"Edit": "Bearbeiten",
 | 
						"Edit": "Bearbeiten",
 | 
				
			||||||
	"Edit Shortcuts": "Verknüpfungen bearbeiten",
 | 
						"Edit Shortcuts": "Tastenkürzel bearbeiten",
 | 
				
			||||||
	"Enable auto updates": "Automatische Updates aktivieren",
 | 
						"Enable auto updates": "Automatisch aktualisieren",
 | 
				
			||||||
	"Enable error reporting (requires restart)": "Fehlerberichterstattung aktivieren (Neustart erforderlich)",
 | 
						"Enable error reporting (requires restart)": "Fehlerberichte aktivieren (erfordert Neustart)",
 | 
				
			||||||
	"Enable spellchecker (requires restart)": "Rechtschreibprüfung aktivieren (Neustart erforderlich)",
 | 
						"Enable spellchecker (requires restart)": "Rechtschreibprüfung aktivieren (erfordert Neustart)",
 | 
				
			||||||
	"Factory Reset": "Werkseinstellungen zurückgesetzt",
 | 
						"Factory Reset": "Alle Einstellungen auf Standardwerte zurücksetzen",
 | 
				
			||||||
	"File": "Datei",
 | 
						"File": "Datei",
 | 
				
			||||||
	"Find accounts": "Konten suchen",
 | 
						"Find accounts": "Accounts finden",
 | 
				
			||||||
	"Find accounts by email": "Finden Sie Konten per E-Mail",
 | 
						"Find accounts by email": "Accounts anhand E-Mail-Adresse finden",
 | 
				
			||||||
	"Flash taskbar on new message": "Flash-Taskleiste bei neuer Nachricht",
 | 
						"Flash taskbar on new message": "Farbliche Hervorhebung in Taskbar bei neuen Nachrichten",
 | 
				
			||||||
	"Forward": "Nach vorne",
 | 
						"Forward": "Weiter",
 | 
				
			||||||
	"Functionality": "Funktionalität",
 | 
						"Functionality": "Funktionalität",
 | 
				
			||||||
	"General": "Allgemeines",
 | 
						"General": "Allgemein",
 | 
				
			||||||
	"Get beta updates": "Erhalten Sie Beta-Updates",
 | 
						"Get beta updates": "Auf Betaversionen aktualisieren",
 | 
				
			||||||
	"Hard Reload": "Festes Nachladen",
 | 
						"Hard Reload": "Komplett neu laden",
 | 
				
			||||||
	"Help": "Hilfe",
 | 
						"Help": "Hilfe",
 | 
				
			||||||
	"Help Center": "Hilfezentrum",
 | 
						"Help Center": "Hilfe-Zentrum",
 | 
				
			||||||
	"History": "Geschichte",
 | 
						"History": "Verlauf",
 | 
				
			||||||
	"History Shortcuts": "Verlaufsverknüpfungen",
 | 
						"History Shortcuts": "Kurzbefehle für Verlauf",
 | 
				
			||||||
	"Keyboard Shortcuts": "Tastatürkürzel",
 | 
						"Keyboard Shortcuts": "Tastenkürzel",
 | 
				
			||||||
	"Log Out": "Ausloggen",
 | 
						"Log Out": "Abmelden",
 | 
				
			||||||
	"Log Out of Organization": "Aus Organisation ausloggen",
 | 
						"Log Out of Organization": "Von Organisation abmelden",
 | 
				
			||||||
	"Manual proxy configuration": "Manuelle Proxy-Konfiguration",
 | 
						"Manual proxy configuration": "Manuelle Proxy-Konfiguration",
 | 
				
			||||||
	"Minimize": "Minimieren",
 | 
						"Minimize": "Minimieren",
 | 
				
			||||||
	"Mute all sounds from Zulip": "Schaltet alle Sounds von Zulip stumm",
 | 
						"Mute all sounds from Zulip": "Alle Zulip-Klänge stummschalten",
 | 
				
			||||||
	"NO": "NEIN",
 | 
						"NO": "NEIN",
 | 
				
			||||||
	"Network": "Netzwerk",
 | 
						"Network": "Netzwerk",
 | 
				
			||||||
	"OR": "ODER",
 | 
						"OR": "ODER",
 | 
				
			||||||
	"Organization URL": "Organisations-URL",
 | 
						"Organization URL": "URL der Organisation",
 | 
				
			||||||
	"Organizations": "Organisationen",
 | 
						"Organizations": "Organisationen",
 | 
				
			||||||
	"Paste": "Einfügen",
 | 
						"Paste": "Einfügen",
 | 
				
			||||||
	"Paste and Match Style": "Einfügen und Format anpassen",
 | 
						"Paste and Match Style": "Ohne Formatierung einfügen",
 | 
				
			||||||
	"Proxy": "Proxy",
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
	"Proxy bypass rules": "Proxy-Umgehungsregeln",
 | 
						"Proxy bypass rules": "Proxy-Ausnahmen",
 | 
				
			||||||
	"Proxy rules": "Proxy-Regeln",
 | 
						"Proxy rules": "Proxy-Regeln",
 | 
				
			||||||
	"Quit": "Verlassen",
 | 
						"Quit": "Beenden",
 | 
				
			||||||
	"Quit Zulip": "Beenden Sie Zulip",
 | 
						"Quit Zulip": "Zulip beenden",
 | 
				
			||||||
	"Redo": "Redo",
 | 
						"Redo": "Wiederholen",
 | 
				
			||||||
	"Release Notes": "Versionshinweise",
 | 
						"Release Notes": "Hinweise zur Versionsfreigabe",
 | 
				
			||||||
	"Reload": "Neu laden",
 | 
						"Reload": "Neu laden",
 | 
				
			||||||
	"Report an Issue": "Ein Problem melden",
 | 
						"Report an Issue": "Ein Problem melden",
 | 
				
			||||||
	"Reset App Data": "App-Daten zurücksetzen",
 | 
						"Save": "Speichern",
 | 
				
			||||||
	"Reset App Settings": "App-Einstellungen zurücksetzen",
 | 
						"Select All": "Alles auswählen",
 | 
				
			||||||
	"Reset Application Data": "Anwendungsdaten zurücksetzen",
 | 
						"Settings": "Einstellungen",
 | 
				
			||||||
	"Save": "sparen",
 | 
						"Shortcuts": "Kurzbefehle",
 | 
				
			||||||
	"Select All": "Wählen Sie Alle",
 | 
						"Show App Logs": "Logdateien der App anzeigen",
 | 
				
			||||||
	"Settings": "die Einstellungen",
 | 
						"Show app icon in system tray": "App-Icon in Systemleiste anzeigen",
 | 
				
			||||||
	"Shortcuts": "Verknüpfungen",
 | 
						"Show app unread badge": "Anzahl ungelesener Nachrichten in App-Icon einblenden",
 | 
				
			||||||
	"Show App Logs": "App-Protokolle anzeigen",
 | 
						"Show desktop notifications": "Desktopbenachrichtigungen anzeigen",
 | 
				
			||||||
	"Show app icon in system tray": "App-Symbol in der Taskleiste anzeigen",
 | 
						"Show downloaded files in file manager": "Heruntergeladene Dateien in Dateimanager anzeigen",
 | 
				
			||||||
	"Show app unread badge": "Zeige App ungelesenen Badge",
 | 
					 | 
				
			||||||
	"Show desktop notifications": "Desktop-Benachrichtigungen anzeigen",
 | 
					 | 
				
			||||||
	"Show downloaded files in file manager": "Heruntergeladene Dateien im Dateimanager anzeigen",
 | 
					 | 
				
			||||||
	"Show sidebar": "Seitenleiste anzeigen",
 | 
						"Show sidebar": "Seitenleiste anzeigen",
 | 
				
			||||||
	"Start app at login": "App beim Login starten",
 | 
						"Start app at login": "App beim Login automatisch starten",
 | 
				
			||||||
	"Switch to Next Organization": "Wechseln Sie zur nächsten Organisation",
 | 
						"Switch to Next Organization": "Zur nächsten Organisation wechseln",
 | 
				
			||||||
	"Switch to Previous Organization": "Zur vorherigen Organisation wechseln",
 | 
						"Switch to Previous Organization": "Zur vorhergehenden Organisation wechseln",
 | 
				
			||||||
	"These desktop app shortcuts extend the Zulip webapp's": "Diese Desktop-App-Verknüpfungen erweitern die Zulip-Webanwendungen",
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "Dies sind zusätzliche Kurzbefehle in der Desktop-App gegenüber der Web-App",
 | 
				
			||||||
	"This will delete all application data including all added accounts and preferences": "Dadurch werden alle Anwendungsdaten einschließlich aller hinzugefügten Konten und Einstellungen gelöscht",
 | 
						"This will delete all application data including all added accounts and preferences": "Hiermit werden alle Anwendungsdaten einschließlich aller Accounts und Einstellungen gelöscht",
 | 
				
			||||||
	"Tip": "Spitze",
 | 
						"Tip": "Tipp",
 | 
				
			||||||
	"Toggle DevTools for Active Tab": "DevTools für aktive Registerkarte umschalten",
 | 
						"Toggle DevTools for Active Tab": "Entwickler-Tools für aktiven Tab umschalten",
 | 
				
			||||||
	"Toggle DevTools for Zulip App": "DevTools für Zulip App umschalten",
 | 
						"Toggle DevTools for Zulip App": "Entwickler-Tools für Zulip-App umschalten",
 | 
				
			||||||
	"Toggle Do Not Disturb": "Nicht stören umschalten",
 | 
						"Toggle Do Not Disturb": "Nicht-Stören-Modus umschalten",
 | 
				
			||||||
	"Toggle Full Screen": "Vollbild umschalten",
 | 
						"Toggle Full Screen": "Vollbildschirm umschalten",
 | 
				
			||||||
	"Toggle Sidebar": "Seitenleiste umschalten",
 | 
						"Toggle Sidebar": "Seitenleiste umschalten",
 | 
				
			||||||
	"Toggle Tray Icon": "Taskleistensymbol umschalten",
 | 
						"Toggle Tray Icon": "Tray-Icon umschalten",
 | 
				
			||||||
	"Tools": "Werkzeuge",
 | 
						"Tools": "Extras",
 | 
				
			||||||
	"Undo": "Rückgängig machen",
 | 
						"Undo": "Rückgängig",
 | 
				
			||||||
	"Upload": "Hochladen",
 | 
						"Upload": "Hochladen",
 | 
				
			||||||
	"Use system proxy settings (requires restart)": "System-Proxy-Einstellungen verwenden (Neustart erforderlich)",
 | 
						"Use system proxy settings (requires restart)": "Systemweite Proxy-Einstellungen verwenden (erfordert Neustart)",
 | 
				
			||||||
	"View": "Aussicht",
 | 
						"View": "Ansicht",
 | 
				
			||||||
	"View Shortcuts": "Verknüpfungen anzeigen",
 | 
						"View Shortcuts": "Tastenkürzel anzeigen",
 | 
				
			||||||
	"Window": "Fenster",
 | 
						"Window": "Fenster",
 | 
				
			||||||
	"Window Shortcuts": "Fensterverknüpfungen",
 | 
						"Window Shortcuts": "Kurzbefehle für Fenster",
 | 
				
			||||||
	"YES": "JA",
 | 
						"YES": "JA",
 | 
				
			||||||
	"Zoom In": "Hineinzoomen",
 | 
						"Zoom In": "Vergrößern",
 | 
				
			||||||
	"Zoom Out": "Rauszoomen",
 | 
						"Zoom Out": "Verkleinern",
 | 
				
			||||||
	"Zulip Help": "Zulip-Hilfe",
 | 
						"Zulip Help": "Hilfe zu Zulip",
 | 
				
			||||||
	"keyboard shortcuts": "Tastatürkürzel",
 | 
						"keyboard shortcuts": "Tastenkürzel",
 | 
				
			||||||
	"script": "Skript"
 | 
						"script": "Skript",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Beim Schließen des Fensters beenden",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Fragen, wo heruntergeladene Dateien gespeichert werden sollen",
 | 
				
			||||||
 | 
						"Services": "Dienste",
 | 
				
			||||||
 | 
						"Hide": "Verbergen",
 | 
				
			||||||
 | 
						"Hide Others": "Andere verbergen",
 | 
				
			||||||
 | 
						"Unhide": "Nicht mehr verbergen",
 | 
				
			||||||
 | 
						"AddServer": "ServerHinzufügen",
 | 
				
			||||||
 | 
						"App language (requires restart)": "Sprache der App (benötigt Neustart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Auf Werkseinstellung zurücksetzen",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Die App wird zurückgesetzt, somit werden alle verbundenen Organisationen, Konten und Zertifikate gelöscht.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "In macOS wird die OS Rechtschreibprüfung verwendet.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Ändere die Spracheinstellung über Systemeinstellungen → Tastatur → Text → Rechtschreibung.",
 | 
				
			||||||
 | 
						"Copy Link": "Link kopieren",
 | 
				
			||||||
 | 
						"Copy Image": "Bild kopieren",
 | 
				
			||||||
 | 
						"Copy Image URL": "Bild-URL kopieren",
 | 
				
			||||||
 | 
						"No Suggestion Found": "Keine Vorschläge gefunden",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "Du kannst höchstens 3 Sprachen für die Rechtschreibprüfung auswählen.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Sprachen für die Rechtschreibprüfung",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Zum Wörterbuch hinzufügen",
 | 
				
			||||||
 | 
						"Look Up": "Nachschlagen"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,119 +1,134 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"File": "File",
 | 
						"About Zulip": "About Zulip",
 | 
				
			||||||
 | 
						"Actual Size": "Actual Size",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Add Custom Certificates",
 | 
				
			||||||
	"Add Organization": "Add Organization",
 | 
						"Add Organization": "Add Organization",
 | 
				
			||||||
	"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
						"Add a Zulip organization": "Add a Zulip organization",
 | 
				
			||||||
	"Desktop Settings": "Desktop Settings",
 | 
						"Add custom CSS": "Add custom CSS",
 | 
				
			||||||
	"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
						"Advanced": "Advanced",
 | 
				
			||||||
	"Copy Zulip URL": "Copy Zulip URL",
 | 
						"All the connected organizations will appear here": "All the connected organizations will appear here",
 | 
				
			||||||
	"Log Out of Organization": "Log Out of Organization",
 | 
						"Always start minimized": "Always start minimized",
 | 
				
			||||||
	"Minimize": "Minimize",
 | 
						"App Updates": "App Updates",
 | 
				
			||||||
 | 
						"Appearance": "Appearance",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Application Shortcuts",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Auto hide Menu bar",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
				
			||||||
 | 
						"Back": "Back",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certificate file",
 | 
				
			||||||
 | 
						"Change": "Change",
 | 
				
			||||||
 | 
						"Check for Updates": "Check for Updates",
 | 
				
			||||||
	"Close": "Close",
 | 
						"Close": "Close",
 | 
				
			||||||
	"Quit": "Quit",
 | 
						"Connect": "Connect",
 | 
				
			||||||
	"Edit": "Edit",
 | 
						"Connect to another organization": "Connect to another organization",
 | 
				
			||||||
	"Undo": "Undo",
 | 
						"Connected organizations": "Connected organizations",
 | 
				
			||||||
	"Redo": "Redo",
 | 
					 | 
				
			||||||
	"Cut": "Cut",
 | 
					 | 
				
			||||||
	"Copy": "Copy",
 | 
						"Copy": "Copy",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "Copy Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Create a new organization",
 | 
				
			||||||
 | 
						"Cut": "Cut",
 | 
				
			||||||
 | 
						"Default download location": "Default download location",
 | 
				
			||||||
 | 
						"Delete": "Delete",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop App Settings",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop Notifications",
 | 
				
			||||||
 | 
						"Desktop Settings": "Desktop Settings",
 | 
				
			||||||
 | 
						"Disconnect": "Disconnect",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "Edit",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Edit Shortcuts",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "File",
 | 
				
			||||||
 | 
						"Find accounts": "Find accounts",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Help",
 | 
				
			||||||
 | 
						"Help Center": "Help Center",
 | 
				
			||||||
 | 
						"History": "History",
 | 
				
			||||||
 | 
						"History Shortcuts": "History Shortcuts",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "OR",
 | 
				
			||||||
 | 
						"Organization URL": "Organization URL",
 | 
				
			||||||
 | 
						"Organizations": "Organizations",
 | 
				
			||||||
	"Paste": "Paste",
 | 
						"Paste": "Paste",
 | 
				
			||||||
	"Paste and Match Style": "Paste and Match Style",
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
	"Select All": "Select All",
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
	"View": "View",
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
	"Reload": "Reload",
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
	"Hard Reload": "Hard Reload",
 | 
						"Quit": "Quit",
 | 
				
			||||||
	"Toggle Full Screen": "Toggle Full Screen",
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
	"Zoom In": "Zoom In",
 | 
						"Redo": "Redo",
 | 
				
			||||||
	"Zoom Out": "Zoom Out",
 | 
					 | 
				
			||||||
	"Actual Size": "Actual Size",
 | 
					 | 
				
			||||||
	"Toggle Tray Icon": "Toggle Tray Icon",
 | 
					 | 
				
			||||||
	"Toggle Sidebar": "Toggle Sidebar",
 | 
					 | 
				
			||||||
	"Auto hide Menu bar": "Auto hide Menu bar",
 | 
					 | 
				
			||||||
	"History": "History",
 | 
					 | 
				
			||||||
	"Back": "Back",
 | 
					 | 
				
			||||||
	"Forward": "Forward",
 | 
					 | 
				
			||||||
	"Window": "Window",
 | 
					 | 
				
			||||||
	"Tools": "Tools",
 | 
					 | 
				
			||||||
	"Check for Updates": "Check for Updates",
 | 
					 | 
				
			||||||
	"Release Notes": "Release Notes",
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
	"Factory Reset": "Factory Reset",
 | 
						"Reload": "Reload",
 | 
				
			||||||
	"Download App Logs": "Download App Logs",
 | 
					 | 
				
			||||||
	"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
					 | 
				
			||||||
	"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
					 | 
				
			||||||
	"Help": "Help",
 | 
					 | 
				
			||||||
	"About Zulip": "About Zulip",
 | 
					 | 
				
			||||||
	"Help Center": "Help Center",
 | 
					 | 
				
			||||||
	"Report an Issue": "Report an Issue",
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Save",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Settings",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
	"Switch to Next Organization": "Switch to Next Organization",
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
	"Switch to Previous Organization": "Switch to Previous Organization",
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
	"General": "General",
 | 
					 | 
				
			||||||
	"Network": "Network",
 | 
					 | 
				
			||||||
	"AddServer": "AddServer",
 | 
					 | 
				
			||||||
	"Organizations": "Organizations",
 | 
					 | 
				
			||||||
	"Shortcuts": "Shortcuts",
 | 
					 | 
				
			||||||
	"Appearance": "Appearance",
 | 
					 | 
				
			||||||
	"Show app icon in system tray": "Show app icon in system tray",
 | 
					 | 
				
			||||||
	"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
					 | 
				
			||||||
	"Show sidebar": "Show sidebar",
 | 
					 | 
				
			||||||
	"Show app unread badge": "Show app unread badge",
 | 
					 | 
				
			||||||
	"Bounce dock on new private message": "Bounce dock on new private message",
 | 
					 | 
				
			||||||
	"Flash taskbar on new message": "Flash taskbar on new message",
 | 
					 | 
				
			||||||
	"Desktop Notifications": "Desktop Notifications",
 | 
					 | 
				
			||||||
	"Show desktop notifications": "Show desktop notifications",
 | 
					 | 
				
			||||||
	"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
					 | 
				
			||||||
	"App Updates": "App Updates",
 | 
					 | 
				
			||||||
	"Enable auto updates": "Enable auto updates",
 | 
					 | 
				
			||||||
	"Get beta updates": "Get beta updates",
 | 
					 | 
				
			||||||
	"Functionality": "Functionality",
 | 
					 | 
				
			||||||
	"Start app at login": "Start app at login",
 | 
					 | 
				
			||||||
	"Always start minimized": "Always start minimized",
 | 
					 | 
				
			||||||
	"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
					 | 
				
			||||||
	"Advanced": "Advanced",
 | 
					 | 
				
			||||||
	"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
					 | 
				
			||||||
	"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
					 | 
				
			||||||
	"Add custom CSS": "Add custom CSS",
 | 
					 | 
				
			||||||
	"Upload": "Upload",
 | 
					 | 
				
			||||||
	"Delete": "Delete",
 | 
					 | 
				
			||||||
	"Default download location": "Default download location",
 | 
					 | 
				
			||||||
	"Change": "Change",
 | 
					 | 
				
			||||||
	"Reset Application Data": "Reset Application Data",
 | 
					 | 
				
			||||||
	"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
					 | 
				
			||||||
	"Reset App Data": "Reset App Data",
 | 
					 | 
				
			||||||
	"Proxy": "Proxy",
 | 
					 | 
				
			||||||
	"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
					 | 
				
			||||||
	"Manual proxy configuration": "Manual proxy configuration",
 | 
					 | 
				
			||||||
	"script": "script",
 | 
					 | 
				
			||||||
	"Proxy rules": "Proxy rules",
 | 
					 | 
				
			||||||
	"Proxy bypass rules": "Proxy bypass rules",
 | 
					 | 
				
			||||||
	"Save": "Save",
 | 
					 | 
				
			||||||
	"Connected organizations": "Connected organizations",
 | 
					 | 
				
			||||||
	"All the connected orgnizations will appear here.": "All the connected orgnizations will appear here.",
 | 
					 | 
				
			||||||
	"Connect to another organization": "Connect to another organization",
 | 
					 | 
				
			||||||
	"Add Custom Certificates": "Add Custom Certificates",
 | 
					 | 
				
			||||||
	"Find accounts by email": "Find accounts by email",
 | 
					 | 
				
			||||||
	"All the connected orgnizations will appear here": "All the connected orgnizations will appear here",
 | 
					 | 
				
			||||||
	"Disconnect": "Disconnect",
 | 
					 | 
				
			||||||
	"Organization URL": "Organization URL",
 | 
					 | 
				
			||||||
	"Certificate file": "Certificate file",
 | 
					 | 
				
			||||||
	"Find accounts": "Find accounts",
 | 
					 | 
				
			||||||
	"Tip": "Tip",
 | 
					 | 
				
			||||||
	"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
	"keyboard shortcuts": "keyboard shortcuts",
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
	"Application Shortcuts": "Application Shortcuts",
 | 
						"Tip": "Tip",
 | 
				
			||||||
	"Settings": "Settings",
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
	"Log Out": "Log Out",
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
	"Quit Zulip": "Quit Zulip",
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
	"Edit Shortcuts": "Edit Shortcuts",
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
	"View Shortcuts": "View Shortcuts",
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
	"History Shortcuts": "History Shortcuts",
 | 
						"Window": "Window",
 | 
				
			||||||
	"Window Shortcuts": "Window Shortcuts",
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
	"Quit when the window is closed": "Quit when the window is closed",
 | 
					 | 
				
			||||||
	"Force social login in app instead of browser": "Force social login in app instead of browser",
 | 
					 | 
				
			||||||
	"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
					 | 
				
			||||||
	"Add a Zulip organization": "Add a Zulip organization",
 | 
					 | 
				
			||||||
	"Connect": "Connect",
 | 
					 | 
				
			||||||
	"OR": "OR",
 | 
					 | 
				
			||||||
	"Create a new organization": "Create a new organization",
 | 
					 | 
				
			||||||
	"Network and Proxy Settings": "Network and Proxy Settings",
 | 
					 | 
				
			||||||
	"YES": "YES",
 | 
						"YES": "YES",
 | 
				
			||||||
	"NO": "NO",
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
	"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?"
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,122 +1,134 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "About Zulip",
 | 
				
			||||||
 | 
						"Actual Size": "Actual Size",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Add Custom Certificates",
 | 
				
			||||||
	"Add Organization": "Add Organization",
 | 
						"Add Organization": "Add Organization",
 | 
				
			||||||
	"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
						"Add a Zulip organization": "Add a Zulip organization",
 | 
				
			||||||
	"Desktop Settings": "Desktop Settings",
 | 
						"Add custom CSS": "Add custom CSS",
 | 
				
			||||||
	"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
						"Advanced": "Advanced",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "All the connected organizations will appear here",
 | 
				
			||||||
 | 
						"Always start minimized": "Always start minimized",
 | 
				
			||||||
 | 
						"App Updates": "App Updates",
 | 
				
			||||||
 | 
						"Appearance": "Appearance",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Application Shortcuts",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Auto hide Menu bar",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
				
			||||||
 | 
						"Back": "Back",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certificate file",
 | 
				
			||||||
 | 
						"Change": "Change",
 | 
				
			||||||
 | 
						"Check for Updates": "Check for Updates",
 | 
				
			||||||
 | 
						"Close": "Close",
 | 
				
			||||||
 | 
						"Connect": "Connect",
 | 
				
			||||||
 | 
						"Connect to another organization": "Connect to another organization",
 | 
				
			||||||
 | 
						"Connected organizations": "Connected organizations",
 | 
				
			||||||
 | 
						"Copy": "Copy",
 | 
				
			||||||
	"Copy Zulip URL": "Copy Zulip URL",
 | 
						"Copy Zulip URL": "Copy Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Create a new organization",
 | 
				
			||||||
 | 
						"Cut": "Cut",
 | 
				
			||||||
 | 
						"Default download location": "Default download location",
 | 
				
			||||||
 | 
						"Delete": "Delete",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop App Settings",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop Notifications",
 | 
				
			||||||
 | 
						"Desktop Settings": "Desktop Settings",
 | 
				
			||||||
 | 
						"Disconnect": "Disconnect",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "Edit",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Edit Shortcuts",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "File",
 | 
				
			||||||
 | 
						"Find accounts": "Find accounts",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Help",
 | 
				
			||||||
 | 
						"Help Center": "Help Center",
 | 
				
			||||||
 | 
						"History": "History",
 | 
				
			||||||
 | 
						"History Shortcuts": "History Shortcuts",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
	"Log Out of Organization": "Log Out of Organization",
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "OR",
 | 
				
			||||||
 | 
						"Organization URL": "Organization URL",
 | 
				
			||||||
 | 
						"Organizations": "Organizations",
 | 
				
			||||||
 | 
						"Paste": "Paste",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Quit",
 | 
				
			||||||
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Save",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Settings",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
	"Services": "Services",
 | 
						"Services": "Services",
 | 
				
			||||||
	"Hide": "Hide",
 | 
						"Hide": "Hide",
 | 
				
			||||||
	"Hide Others": "Hide Others",
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
	"Unhide": "Unhide",
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
	"Minimize": "Minimize",
 | 
					 | 
				
			||||||
	"Close": "Close",
 | 
					 | 
				
			||||||
	"Quit": "Quit",
 | 
					 | 
				
			||||||
	"Edit": "Edit",
 | 
					 | 
				
			||||||
	"Undo": "Undo",
 | 
					 | 
				
			||||||
	"Redo": "Redo",
 | 
					 | 
				
			||||||
	"Cut": "Cut",
 | 
					 | 
				
			||||||
	"Copy": "Copy",
 | 
					 | 
				
			||||||
	"Paste": "Paste",
 | 
					 | 
				
			||||||
	"Paste and Match Style": "Paste and Match Style",
 | 
					 | 
				
			||||||
	"Select All": "Select All",
 | 
					 | 
				
			||||||
	"View": "View",
 | 
					 | 
				
			||||||
	"Reload": "Reload",
 | 
					 | 
				
			||||||
	"Hard Reload": "Hard Reload",
 | 
					 | 
				
			||||||
	"Toggle Full Screen": "Toggle Full Screen",
 | 
					 | 
				
			||||||
	"Zoom In": "Zoom In",
 | 
					 | 
				
			||||||
	"Zoom Out": "Zoom Out",
 | 
					 | 
				
			||||||
	"Actual Size": "Actual Size",
 | 
					 | 
				
			||||||
	"Toggle Tray Icon": "Toggle Tray Icon",
 | 
					 | 
				
			||||||
	"Toggle Sidebar": "Toggle Sidebar",
 | 
					 | 
				
			||||||
	"Auto hide Menu bar": "Auto hide Menu bar",
 | 
					 | 
				
			||||||
	"History": "History",
 | 
					 | 
				
			||||||
	"Back": "Back",
 | 
					 | 
				
			||||||
	"Forward": "Forward",
 | 
					 | 
				
			||||||
	"Window": "Window",
 | 
					 | 
				
			||||||
	"Tools": "Tools",
 | 
					 | 
				
			||||||
	"Check for Updates": "Check for Updates",
 | 
					 | 
				
			||||||
	"Release Notes": "Release Notes",
 | 
					 | 
				
			||||||
	"Factory Reset": "Factory Reset",
 | 
					 | 
				
			||||||
	"Download App Logs": "Download App Logs",
 | 
					 | 
				
			||||||
	"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
					 | 
				
			||||||
	"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
					 | 
				
			||||||
	"Help": "Help",
 | 
					 | 
				
			||||||
	"About Zulip": "About Zulip",
 | 
					 | 
				
			||||||
	"Help Center": "Help Center",
 | 
					 | 
				
			||||||
	"Report an Issue": "Report an Issue",
 | 
					 | 
				
			||||||
	"Switch to Next Organization": "Switch to Next Organization",
 | 
					 | 
				
			||||||
	"Switch to Previous Organization": "Switch to Previous Organization",
 | 
					 | 
				
			||||||
	"General": "General",
 | 
					 | 
				
			||||||
	"Network": "Network",
 | 
					 | 
				
			||||||
	"AddServer": "AddServer",
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
	"Organizations": "Organizations",
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
	"Shortcuts": "Shortcuts",
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
	"Settings": "Settings",
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
	"Add a Zulip organization": "Add a Zulip organization",
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
	"Organization URL": "Organization URL",
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
	"Connect": "Connect",
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
	"OR": "OR",
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
	"Create a new organization": "Create a new organization",
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
	"Proxy": "Proxy",
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
	"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
	"Manual proxy configuration": "Manual proxy configuration",
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
	"script": "script",
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
	"Proxy rules": "Proxy rules",
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
	"Proxy bypass rules": "Proxy bypass rules",
 | 
					 | 
				
			||||||
	"Save": "Save",
 | 
					 | 
				
			||||||
	"Appearance": "Appearance",
 | 
					 | 
				
			||||||
	"Show app icon in system tray": "Show app icon in system tray",
 | 
					 | 
				
			||||||
	"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
					 | 
				
			||||||
	"Show sidebar": "Show sidebar",
 | 
					 | 
				
			||||||
	"Show app unread badge": "Show app unread badge",
 | 
					 | 
				
			||||||
	"Bounce dock on new private message": "Bounce dock on new private message",
 | 
					 | 
				
			||||||
	"Flash taskbar on new message": "Flash taskbar on new message",
 | 
					 | 
				
			||||||
	"Desktop Notifications": "Desktop Notifications",
 | 
					 | 
				
			||||||
	"Show desktop notifications": "Show desktop notifications",
 | 
					 | 
				
			||||||
	"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
					 | 
				
			||||||
	"App Updates": "App Updates",
 | 
					 | 
				
			||||||
	"Enable auto updates": "Enable auto updates",
 | 
					 | 
				
			||||||
	"Get beta updates": "Get beta updates",
 | 
					 | 
				
			||||||
	"Functionality": "Functionality",
 | 
					 | 
				
			||||||
	"Start app at login": "Start app at login",
 | 
					 | 
				
			||||||
	"Always start minimized": "Always start minimized",
 | 
					 | 
				
			||||||
	"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
					 | 
				
			||||||
	"Advanced": "Advanced",
 | 
					 | 
				
			||||||
	"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
					 | 
				
			||||||
	"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
					 | 
				
			||||||
	"Add custom CSS": "Add custom CSS",
 | 
					 | 
				
			||||||
	"Upload": "Upload",
 | 
					 | 
				
			||||||
	"Delete": "Delete",
 | 
					 | 
				
			||||||
	"Default download location": "Default download location",
 | 
					 | 
				
			||||||
	"Change": "Change",
 | 
					 | 
				
			||||||
	"Reset Application Data": "Reset Application Data",
 | 
					 | 
				
			||||||
	"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
					 | 
				
			||||||
	"Reset App Data": "Reset App Data",
 | 
					 | 
				
			||||||
	"Connected organizations": "Connected organizations",
 | 
					 | 
				
			||||||
	"All the connected orgnizations will appear here.": "All the connected orgnizations will appear here.",
 | 
					 | 
				
			||||||
	"Connect to another organization": "Connect to another organization",
 | 
					 | 
				
			||||||
	"Add Custom Certificates": "Add Custom Certificates",
 | 
					 | 
				
			||||||
	"Find accounts by email": "Find accounts by email",
 | 
					 | 
				
			||||||
	"All the connected orgnizations will appear here": "All the connected orgnizations will appear here",
 | 
					 | 
				
			||||||
	"Disconnect": "Disconnect",
 | 
					 | 
				
			||||||
	"Certificate file": "Certificate file",
 | 
					 | 
				
			||||||
	"Find accounts": "Find accounts",
 | 
					 | 
				
			||||||
	"Tip": "Tip",
 | 
					 | 
				
			||||||
	"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
					 | 
				
			||||||
	"keyboard shortcuts": "keyboard shortcuts",
 | 
					 | 
				
			||||||
	"Application Shortcuts": "Application Shortcuts",
 | 
					 | 
				
			||||||
	"Reset App Settings": "Reset App Settings",
 | 
					 | 
				
			||||||
	"Log Out": "Log Out",
 | 
					 | 
				
			||||||
	"Hide Zulip": "Hide Zulip",
 | 
					 | 
				
			||||||
	"Quit Zulip": "Quit Zulip",
 | 
					 | 
				
			||||||
	"Edit Shortcuts": "Edit Shortcuts",
 | 
					 | 
				
			||||||
	"Emoji & Symbols": "Emoji & Symbols",
 | 
					 | 
				
			||||||
	"View Shortcuts": "View Shortcuts",
 | 
					 | 
				
			||||||
	"Enter Full Screen": "Enter Full Screen",
 | 
					 | 
				
			||||||
	"History Shortcuts": "History Shortcuts",
 | 
					 | 
				
			||||||
	"Quit when the window is closed": "Quit when the window is closed",
 | 
					 | 
				
			||||||
	"File": "File",
 | 
					 | 
				
			||||||
	"Network and Proxy Settings": "Network and Proxy Settings",
 | 
					 | 
				
			||||||
	"Ask where to save files before downloading": "Ask where to save files before downloading"
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										134
									
								
								app/translations/en_GB.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "About Zulip",
 | 
				
			||||||
 | 
						"Actual Size": "Actual Size",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Add Custom Certificates",
 | 
				
			||||||
 | 
						"Add Organization": "Add Organisation",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "Add a Zulip organisation",
 | 
				
			||||||
 | 
						"Add custom CSS": "Add custom CSS",
 | 
				
			||||||
 | 
						"Advanced": "Advanced",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "All the connected organisations will appear here",
 | 
				
			||||||
 | 
						"Always start minimized": "Always start minimized",
 | 
				
			||||||
 | 
						"App Updates": "App Updates",
 | 
				
			||||||
 | 
						"Appearance": "Appearance",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Application Shortcuts",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organisation?",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Auto hide Menu bar",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
				
			||||||
 | 
						"Back": "Back",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certificate file",
 | 
				
			||||||
 | 
						"Change": "Change",
 | 
				
			||||||
 | 
						"Check for Updates": "Check for Updates",
 | 
				
			||||||
 | 
						"Close": "Close",
 | 
				
			||||||
 | 
						"Connect": "Connect",
 | 
				
			||||||
 | 
						"Connect to another organization": "Connect to another organisation",
 | 
				
			||||||
 | 
						"Connected organizations": "Connected organisations",
 | 
				
			||||||
 | 
						"Copy": "Copy",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "Copy Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Create a new organisation",
 | 
				
			||||||
 | 
						"Cut": "Cut",
 | 
				
			||||||
 | 
						"Default download location": "Default download location",
 | 
				
			||||||
 | 
						"Delete": "Delete",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop App Settings",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop Notifications",
 | 
				
			||||||
 | 
						"Desktop Settings": "Desktop Settings",
 | 
				
			||||||
 | 
						"Disconnect": "Disconnect",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "Edit",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Edit Shortcuts",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "File",
 | 
				
			||||||
 | 
						"Find accounts": "Find accounts",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Help",
 | 
				
			||||||
 | 
						"Help Center": "Help Center",
 | 
				
			||||||
 | 
						"History": "History",
 | 
				
			||||||
 | 
						"History Shortcuts": "History Shortcuts",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organisation",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "OR",
 | 
				
			||||||
 | 
						"Organization URL": "Organisation URL",
 | 
				
			||||||
 | 
						"Organizations": "Organisations",
 | 
				
			||||||
 | 
						"Paste": "Paste",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Quit",
 | 
				
			||||||
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Save",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Settings",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organisation",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organisation",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organisations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,117 +1,134 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"About Zulip": "Acerca de Zulip",
 | 
						"About Zulip": "Acerca de Zulip",
 | 
				
			||||||
	"Actual Size": "Tamaño real",
 | 
						"Actual Size": "Tamaño actual",
 | 
				
			||||||
	"Add Custom Certificates": "Añadir certificados personalizados",
 | 
						"Add Custom Certificates": "Añadir certificados personalizados",
 | 
				
			||||||
	"Add Organization": "Añadir organización",
 | 
						"Add Organization": "Añadir organización",
 | 
				
			||||||
	"Add a Zulip organization": "Añadir una organización Zulip",
 | 
						"Add a Zulip organization": "Añadir una organización de Zulip",
 | 
				
			||||||
	"Add custom CSS": "Añadir CSS personalizado",
 | 
						"Add custom CSS": "Añadir CSS personalizado",
 | 
				
			||||||
	"Advanced": "Avanzado",
 | 
						"Advanced": "Avanzado",
 | 
				
			||||||
	"All the connected organizations will appear here": "Todas las organizaciones conectadas aparecerán aquí.",
 | 
						"All the connected organizations will appear here": "Todas las organizaciones conectadas aparecerán aquí",
 | 
				
			||||||
	"Always start minimized": "Siempre empezar minimizado",
 | 
						"Always start minimized": "Iniciar siempre minimizado",
 | 
				
			||||||
	"App Updates": "Actualizaciones de la aplicación",
 | 
						"App Updates": "Actualizaciones de la aplicación",
 | 
				
			||||||
	"Appearance": "Apariencia",
 | 
						"Appearance": "Apariencia",
 | 
				
			||||||
	"Application Shortcuts": "Atajos de aplicación",
 | 
						"Application Shortcuts": "Atajos de la aplicación",
 | 
				
			||||||
	"Are you sure you want to disconnect this organization?": "¿Estás seguro de que quieres desconectar esta organización?",
 | 
						"Are you sure you want to disconnect this organization?": "Estas seguro de desconectar esta organización?",
 | 
				
			||||||
	"Auto hide Menu bar": "Ocultar automáticamente la barra de menú",
 | 
						"Auto hide Menu bar": "Ocultar la barra de menú automáticamente",
 | 
				
			||||||
	"Auto hide menu bar (Press Alt key to display)": "Ocultar automáticamente la barra de menú (presionar la tecla Alt para mostrar)",
 | 
						"Auto hide menu bar (Press Alt key to display)": "Ocultar la barra de menú automáticamente (mantén tecla Alt para mostrar)",
 | 
				
			||||||
	"Back": "Espalda",
 | 
						"Back": "Atrás",
 | 
				
			||||||
	"Bounce dock on new private message": "Bounce dock en nuevo mensaje privado",
 | 
						"Bounce dock on new private message": "Rebotar dock cuando se reciba un nuevo mensaje privado",
 | 
				
			||||||
	"Certificate file": "Archivo de certificado",
 | 
						"Certificate file": "Archivo de certificado",
 | 
				
			||||||
	"Change": "Cambio",
 | 
						"Change": "Cambiar",
 | 
				
			||||||
	"Check for Updates": "Buscar actualizaciones",
 | 
						"Check for Updates": "Comprobar actualizaciones",
 | 
				
			||||||
	"Close": "Cerrar",
 | 
						"Close": "Cerrar",
 | 
				
			||||||
	"Connect": "Conectar",
 | 
						"Connect": "Conectar",
 | 
				
			||||||
	"Connect to another organization": "Conectarse a otra organización",
 | 
						"Connect to another organization": "Conectar a otra organización",
 | 
				
			||||||
	"Connected organizations": "Organizaciones conectadas",
 | 
						"Connected organizations": "Organizaciones conectada",
 | 
				
			||||||
	"Copy": "Dupdo",
 | 
						"Copy": "Copiar",
 | 
				
			||||||
	"Copy Zulip URL": "Copiar URL de Zulip",
 | 
						"Copy Zulip URL": "Copiar URL de Zulip",
 | 
				
			||||||
	"Create a new organization": "Crear una nueva organización",
 | 
						"Create a new organization": "Crear una nueva organización",
 | 
				
			||||||
	"Cut": "Cortar",
 | 
						"Cut": "Cortar",
 | 
				
			||||||
	"Default download location": "Ubicación de descarga predeterminada",
 | 
						"Default download location": "Ubicación por defecto de descargas",
 | 
				
			||||||
	"Delete": "Borrar",
 | 
						"Delete": "Eliminar",
 | 
				
			||||||
	"Desktop App Settings": "Configuración de la aplicación de escritorio",
 | 
						"Desktop App Settings": "Ajustes de la aplicación de escritorio",
 | 
				
			||||||
	"Desktop Notifications": "Notificaciones de escritorio",
 | 
						"Desktop Notifications": "Notificaciones de escritorio",
 | 
				
			||||||
	"Desktop Settings": "Configuraciones de escritorio",
 | 
						"Desktop Settings": "Ajustes de escritorio",
 | 
				
			||||||
	"Disconnect": "Desconectar",
 | 
						"Disconnect": "Desconectar",
 | 
				
			||||||
	"Download App Logs": "Descargar App Logs",
 | 
						"Download App Logs": "Descargar registros de la aplicación",
 | 
				
			||||||
	"Edit": "Editar",
 | 
						"Edit": "Editar",
 | 
				
			||||||
	"Edit Shortcuts": "Editar accesos directos",
 | 
						"Edit Shortcuts": "Editar atajos",
 | 
				
			||||||
	"Enable auto updates": "Habilitar actualizaciones automáticas",
 | 
						"Enable auto updates": "Activar actualizaciones automáticas",
 | 
				
			||||||
	"Enable error reporting (requires restart)": "Habilitar informes de errores (requiere reinicio)",
 | 
						"Enable error reporting (requires restart)": "Activar reporte de fallos (necesita reinicio)",
 | 
				
			||||||
	"Enable spellchecker (requires restart)": "Habilitar el corrector ortográfico (requiere reinicio)",
 | 
						"Enable spellchecker (requires restart)": "Activar corrector ortográfico (necesita reinicio)",
 | 
				
			||||||
	"Factory Reset": "Restablecimiento de fábrica",
 | 
						"Factory Reset": "Reinicio de fábrica",
 | 
				
			||||||
	"File": "Expediente",
 | 
						"File": "Archivo",
 | 
				
			||||||
	"Find accounts": "Encontrar cuentas",
 | 
						"Find accounts": "Encontrar cuentas",
 | 
				
			||||||
	"Find accounts by email": "Encuentra cuentas por correo electrónico",
 | 
						"Find accounts by email": "Encontrar cuentas por correo electrónico",
 | 
				
			||||||
	"Flash taskbar on new message": "Flash barra de tareas en nuevo mensaje",
 | 
						"Flash taskbar on new message": "Hacer que la barra de tareas parpadee cuando se reciba un mensaje nuevo",
 | 
				
			||||||
	"Forward": "Adelante",
 | 
						"Forward": "Reenviar",
 | 
				
			||||||
	"Functionality": "Funcionalidad",
 | 
						"Functionality": "Funcionalidad",
 | 
				
			||||||
	"General": "General",
 | 
						"General": "General",
 | 
				
			||||||
	"Get beta updates": "Recibe actualizaciones beta",
 | 
						"Get beta updates": "Obtener actualizaciones beta",
 | 
				
			||||||
	"Hard Reload": "Recarga dura",
 | 
						"Hard Reload": "Recarga forzosa",
 | 
				
			||||||
	"Help": "Ayuda",
 | 
						"Help": "Ayuda",
 | 
				
			||||||
	"Help Center": "Centro de ayuda",
 | 
						"Help Center": "Centro de ayuda",
 | 
				
			||||||
	"History": "Historia",
 | 
						"History": "Historial",
 | 
				
			||||||
	"History Shortcuts": "Atajos de historia",
 | 
						"History Shortcuts": "Atajos del historial",
 | 
				
			||||||
	"Keyboard Shortcuts": "Atajos de teclado",
 | 
						"Keyboard Shortcuts": "Atajos de teclado",
 | 
				
			||||||
	"Log Out": "Cerrar sesión",
 | 
						"Log Out": "Cerrar sesión",
 | 
				
			||||||
	"Log Out of Organization": "Salir de la organización",
 | 
						"Log Out of Organization": "Cerrar sesión de organización",
 | 
				
			||||||
	"Manual proxy configuration": "Configuración de proxy manual",
 | 
						"Manual proxy configuration": "Configuración de proxy manual",
 | 
				
			||||||
	"Minimize": "Minimizar",
 | 
						"Minimize": "Minimizar",
 | 
				
			||||||
	"Mute all sounds from Zulip": "Silencia todos los sonidos de Zulip",
 | 
						"Mute all sounds from Zulip": "Silenciar todos los sonidos de Zulip",
 | 
				
			||||||
	"NO": "NO",
 | 
						"NO": "NO",
 | 
				
			||||||
	"Network": "Red",
 | 
						"Network": "Red",
 | 
				
			||||||
	"OR": "O",
 | 
						"OR": "O",
 | 
				
			||||||
	"Organization URL": "URL de la organización",
 | 
						"Organization URL": "URL de la organización",
 | 
				
			||||||
	"Organizations": "Organizaciones",
 | 
						"Organizations": "Organizaciones",
 | 
				
			||||||
	"Paste": "Pegar",
 | 
						"Paste": "Pegar",
 | 
				
			||||||
	"Paste and Match Style": "Pegar y combinar estilo",
 | 
						"Paste and Match Style": "Pegar y mantener estilo",
 | 
				
			||||||
	"Proxy": "Apoderado",
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
	"Proxy bypass rules": "Reglas de omisión de proxy",
 | 
						"Proxy bypass rules": "Reglas para ignorar proxy",
 | 
				
			||||||
	"Proxy rules": "Reglas de proxy",
 | 
						"Proxy rules": "Reglas del proxy",
 | 
				
			||||||
	"Quit": "Dejar",
 | 
						"Quit": "Cerrar",
 | 
				
			||||||
	"Quit Zulip": "Dejar Zulip",
 | 
						"Quit Zulip": "Cerrar Zulip",
 | 
				
			||||||
	"Redo": "Rehacer",
 | 
						"Redo": "Rehacer",
 | 
				
			||||||
	"Release Notes": "Notas de lanzamiento",
 | 
						"Release Notes": "Notas de la versión",
 | 
				
			||||||
	"Reload": "Recargar",
 | 
						"Reload": "Recargar",
 | 
				
			||||||
	"Report an Issue": "Reportar un problema",
 | 
						"Report an Issue": "Informar de un error",
 | 
				
			||||||
	"Reset App Data": "Restablecer datos de la aplicación",
 | 
						"Save": "Guardar",
 | 
				
			||||||
	"Reset App Settings": "Restablecer la configuración de la aplicación",
 | 
					 | 
				
			||||||
	"Reset Application Data": "Restablecer datos de aplicación",
 | 
					 | 
				
			||||||
	"Save": "Salvar",
 | 
					 | 
				
			||||||
	"Select All": "Seleccionar todo",
 | 
						"Select All": "Seleccionar todo",
 | 
				
			||||||
	"Settings": "Ajustes",
 | 
						"Settings": "Ajustes",
 | 
				
			||||||
	"Shortcuts": "Atajos",
 | 
						"Shortcuts": "Atajos de teclado",
 | 
				
			||||||
	"Show App Logs": "Mostrar registros de aplicaciones",
 | 
						"Show App Logs": "Mostrar registros de la aplicación",
 | 
				
			||||||
	"Show app icon in system tray": "Mostrar icono de la aplicación en la bandeja del sistema",
 | 
						"Show app icon in system tray": "Mostrar un icono de la aplicación en la bandeja del sistema",
 | 
				
			||||||
	"Show app unread badge": "Mostrar la aplicación de placa sin leer",
 | 
						"Show app unread badge": "Mostrar un globo en el icono si hay mensajes sin leer",
 | 
				
			||||||
	"Show desktop notifications": "Mostrar notificaciones de escritorio",
 | 
						"Show desktop notifications": "Mostrar notificaciones de escritorio",
 | 
				
			||||||
	"Show downloaded files in file manager": "Mostrar los archivos descargados en el administrador de archivos",
 | 
						"Show downloaded files in file manager": "Mostrar archivos descargados en el explorador",
 | 
				
			||||||
	"Show sidebar": "Mostrar barra lateral",
 | 
						"Show sidebar": "Mostrar barra lateral",
 | 
				
			||||||
	"Start app at login": "Iniciar la aplicación al iniciar sesión",
 | 
						"Start app at login": "Lanzar aplicación al inicio",
 | 
				
			||||||
	"Switch to Next Organization": "Cambiar a la siguiente organización",
 | 
						"Switch to Next Organization": "Cambiar a la siguiente organización",
 | 
				
			||||||
	"Switch to Previous Organization": "Cambiar a la organización anterior",
 | 
						"Switch to Previous Organization": "Cambiar a la anterior organización",
 | 
				
			||||||
	"These desktop app shortcuts extend the Zulip webapp's": "Estos accesos directos de aplicaciones de escritorio extienden las aplicaciones web de Zulip.",
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "Estos atajos de la aplicación de escritorio extienden los ya existentes en Zulip",
 | 
				
			||||||
	"This will delete all application data including all added accounts and preferences": "Esto eliminará todos los datos de la aplicación, incluidas todas las cuentas y preferencias agregadas.",
 | 
						"This will delete all application data including all added accounts and preferences": "Esto borrará todos los datos de la aplicación, incluyendo cuentas añadidas y preferencia",
 | 
				
			||||||
	"Tip": "Propina",
 | 
						"Tip": "Consej",
 | 
				
			||||||
	"Toggle DevTools for Active Tab": "Alternar DevTools para Active Tab",
 | 
						"Toggle DevTools for Active Tab": "Activar/desactivar herramientas de desarrollador para la pestaña activa",
 | 
				
			||||||
	"Toggle DevTools for Zulip App": "Alternar DevTools para Zulip App",
 | 
						"Toggle DevTools for Zulip App": "Activar/desactivar herramientas de desarrollador para la aplicación de Zulip",
 | 
				
			||||||
	"Toggle Do Not Disturb": "Alternar No molestar",
 | 
						"Toggle Do Not Disturb": "Activar/desactivar no molestar",
 | 
				
			||||||
	"Toggle Full Screen": "Alternar pantalla completa",
 | 
						"Toggle Full Screen": "Activar/desactivar pantalla completa",
 | 
				
			||||||
	"Toggle Sidebar": "Alternar barra lateral",
 | 
						"Toggle Sidebar": "Activar/desactivar barra lateral",
 | 
				
			||||||
	"Toggle Tray Icon": "Icono de bandeja de palanca",
 | 
						"Toggle Tray Icon": "Activar/desactivar icono en bandeja del sistema",
 | 
				
			||||||
	"Tools": "Herramientas",
 | 
						"Tools": "Herramientas",
 | 
				
			||||||
	"Undo": "Deshacer",
 | 
						"Undo": "Deshacer",
 | 
				
			||||||
	"Upload": "Subir",
 | 
						"Upload": "Subir",
 | 
				
			||||||
	"Use system proxy settings (requires restart)": "Usar la configuración del proxy del sistema (requiere reinicio)",
 | 
						"Use system proxy settings (requires restart)": "Usar ajustes de proxy del sistema (necesita reinicio)",
 | 
				
			||||||
	"View": "Ver",
 | 
						"View": "Ver",
 | 
				
			||||||
	"View Shortcuts": "Ver accesos directos",
 | 
						"View Shortcuts": "Ver atajos",
 | 
				
			||||||
	"Window": "Ventana",
 | 
						"Window": "Ventana",
 | 
				
			||||||
	"Window Shortcuts": "Atajos de ventana",
 | 
						"Window Shortcuts": "Atajos de ventana",
 | 
				
			||||||
	"YES": "SÍ",
 | 
						"YES": "SÍ",
 | 
				
			||||||
	"Zoom In": "Acercarse",
 | 
						"Zoom In": "Aumentar zoom",
 | 
				
			||||||
	"Zoom Out": "Disminuir el zoom",
 | 
						"Zoom Out": "Reducir zoom",
 | 
				
			||||||
	"Zulip Help": "Ayuda de Zulip",
 | 
						"Zulip Help": "Ayuda sobre Zulip",
 | 
				
			||||||
	"keyboard shortcuts": "atajos de teclado",
 | 
						"keyboard shortcuts": "atajos de teclado",
 | 
				
			||||||
	"script": "guión"
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Salir cuando la ventana se cierre",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Preguntar dónde guardar los archivos antes de descargar",
 | 
				
			||||||
 | 
						"Services": "Servicios",
 | 
				
			||||||
 | 
						"Hide": "Ocultar",
 | 
				
			||||||
 | 
						"Hide Others": "Ocultar otros",
 | 
				
			||||||
 | 
						"Unhide": "Dejar de ocultar",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "Idioma de la aplicación (requiere reinicio)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reinicia la aplicación, borrando todas las organizaciones, cuentas y certificados conectados.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "En macOS se utiliza la verificación ortográfica del sistema operativo.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Modifica el idioma en Preferencias del sistema → Teclado → Texto → Ortografía.",
 | 
				
			||||||
 | 
						"Copy Link": "Copiar enlace",
 | 
				
			||||||
 | 
						"Copy Image": "Copiar imagen",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copiar URL de la imagen",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No se encontró ninguna sugerencia",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "Puedes elegir un máximo de 3 idiomas para la verificación ortográfica.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Idiomas de verificación ortográfica",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Añadir al diccionario",
 | 
				
			||||||
 | 
						"Look Up": "Consultar"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								app/translations/fa.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "درباره Zulip ",
 | 
				
			||||||
 | 
						"Actual Size": "اندازه واقعی",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "اضافه کردن مجوز دلخواه",
 | 
				
			||||||
 | 
						"Add Organization": "اضافه کردن سازمان",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "اضافه کردن سازمان Zulip",
 | 
				
			||||||
 | 
						"Add custom CSS": "اضافه کردن CSS دلخواه",
 | 
				
			||||||
 | 
						"Advanced": "پیشرفته",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "همه سازمانهای متصل شده اینجا نمایش داده میشوند",
 | 
				
			||||||
 | 
						"Always start minimized": "همواره به صورت کوچک شده اجرا شو",
 | 
				
			||||||
 | 
						"App Updates": "بهروزرسانیهای برنامه",
 | 
				
			||||||
 | 
						"Appearance": "شمایل",
 | 
				
			||||||
 | 
						"Application Shortcuts": "میانبرهای برنامه",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "آیا از قطع ارتباط از سازمان اطمینان دارید؟",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "مخفیسازی خودکار نوار منو",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "مخفیسازی خودکار نوار منو (برای نمایش دکمه Alt را بزنید)",
 | 
				
			||||||
 | 
						"Back": "عقب",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "فایل مجوز",
 | 
				
			||||||
 | 
						"Change": "تغییر دادن",
 | 
				
			||||||
 | 
						"Check for Updates": "بررسی برای بهروزرسانی",
 | 
				
			||||||
 | 
						"Close": "بستن",
 | 
				
			||||||
 | 
						"Connect": "اتصال",
 | 
				
			||||||
 | 
						"Connect to another organization": "اتصال به یک سازمان دیگر",
 | 
				
			||||||
 | 
						"Connected organizations": "سازمانهای وصل شده",
 | 
				
			||||||
 | 
						"Copy": "رونوشت",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "رونوشت از Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "ایجاد سازمان جدید",
 | 
				
			||||||
 | 
						"Cut": "بریدن",
 | 
				
			||||||
 | 
						"Default download location": "محل پیشفرض دانلود",
 | 
				
			||||||
 | 
						"Delete": "حذف",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop App Settings",
 | 
				
			||||||
 | 
						"Desktop Notifications": "اطلاعرسانیهای دسکتاپ",
 | 
				
			||||||
 | 
						"Desktop Settings": "تنظیمات دسکتاپ",
 | 
				
			||||||
 | 
						"Disconnect": "Disconnect",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "ویرایش",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "ویرایش میانبرها",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "فایل",
 | 
				
			||||||
 | 
						"Find accounts": "پیدا کردن حساب های کاربری ",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "عمومی",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "کمک",
 | 
				
			||||||
 | 
						"Help Center": "مرکز کمک",
 | 
				
			||||||
 | 
						"History": "تاریخچه ",
 | 
				
			||||||
 | 
						"History Shortcuts": "تاریخچه میانبرها",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "میانبرهای صفحهکلید",
 | 
				
			||||||
 | 
						"Log Out": "خروج",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "یا",
 | 
				
			||||||
 | 
						"Organization URL": "URL سازمان",
 | 
				
			||||||
 | 
						"Organizations": "Organizations",
 | 
				
			||||||
 | 
						"Paste": "Paste",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Quit",
 | 
				
			||||||
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "ذخیره",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "تنظیمات",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										134
									
								
								app/translations/fi.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "Tietoa Zulipista",
 | 
				
			||||||
 | 
						"Actual Size": "Varsinainen koko",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Lisää omia sertifikaatteja",
 | 
				
			||||||
 | 
						"Add Organization": "Lisää organisaatio",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "Lisää Zulip-organisaatio",
 | 
				
			||||||
 | 
						"Add custom CSS": "Lisää oma CSS",
 | 
				
			||||||
 | 
						"Advanced": "Edistynyt",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "All the connected organizations will appear here",
 | 
				
			||||||
 | 
						"Always start minimized": "Aloita aina pienennettynä",
 | 
				
			||||||
 | 
						"App Updates": "Sovellspäivitykset",
 | 
				
			||||||
 | 
						"Appearance": "Ulkonäkö",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Sovellusoikotiet",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Auto hide Menu bar",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
				
			||||||
 | 
						"Back": "Back",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certificate file",
 | 
				
			||||||
 | 
						"Change": "Muuta",
 | 
				
			||||||
 | 
						"Check for Updates": "Check for Updates",
 | 
				
			||||||
 | 
						"Close": "Sulje",
 | 
				
			||||||
 | 
						"Connect": "Yhdistä",
 | 
				
			||||||
 | 
						"Connect to another organization": "Connect to another organization",
 | 
				
			||||||
 | 
						"Connected organizations": "Connected organizations",
 | 
				
			||||||
 | 
						"Copy": "Kopioi",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "Kopioi Zulip-URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Luo uusi organisaatio",
 | 
				
			||||||
 | 
						"Cut": "Leikkaa",
 | 
				
			||||||
 | 
						"Default download location": "Default download location",
 | 
				
			||||||
 | 
						"Delete": "Poista",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop App Settings",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop Notifications",
 | 
				
			||||||
 | 
						"Desktop Settings": "Desktop Settings",
 | 
				
			||||||
 | 
						"Disconnect": "Katkaise",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "Muokkaa",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Edit Shortcuts",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "Tiedosto",
 | 
				
			||||||
 | 
						"Find accounts": "Löydä tilit",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Ohje",
 | 
				
			||||||
 | 
						"Help Center": "Tukikeskus",
 | 
				
			||||||
 | 
						"History": "History",
 | 
				
			||||||
 | 
						"History Shortcuts": "History Shortcuts",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "EI",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "TAI",
 | 
				
			||||||
 | 
						"Organization URL": "Organisaation URL",
 | 
				
			||||||
 | 
						"Organizations": "Organisaatiot",
 | 
				
			||||||
 | 
						"Paste": "Liitä",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Liitä ja täsmää tyylit",
 | 
				
			||||||
 | 
						"Proxy": "Välipalvelin",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Lopeta",
 | 
				
			||||||
 | 
						"Quit Zulip": "Lopeta Zulip",
 | 
				
			||||||
 | 
						"Redo": "Toista",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Lataa uudelleen",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Tallenna",
 | 
				
			||||||
 | 
						"Select All": "Valitse kaikki",
 | 
				
			||||||
 | 
						"Settings": "Asetukset",
 | 
				
			||||||
 | 
						"Shortcuts": "Oikopolut",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Vinkki",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Työkalut",
 | 
				
			||||||
 | 
						"Undo": "Peru",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "Näytä",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Ikkuna",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "KYLLÄ",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,117 +1,134 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	"About Zulip": "À propos de Zulip",
 | 
						"About Zulip": "À propos de Zulip",
 | 
				
			||||||
	"Actual Size": "Taille actuelle",
 | 
						"Actual Size": "Taille actuelle",
 | 
				
			||||||
	"Add Custom Certificates": "Ajouter des certificats personnalisés",
 | 
						"Add Custom Certificates": "Ajouter un certificat spécifique",
 | 
				
			||||||
	"Add Organization": "Ajouter une organisation",
 | 
						"Add Organization": "Ajouter une organisation",
 | 
				
			||||||
	"Add a Zulip organization": "Ajouter une organisation Zulip",
 | 
						"Add a Zulip organization": "Ajouter une organisation Zulip",
 | 
				
			||||||
	"Add custom CSS": "Ajouter un CSS personnalisé",
 | 
						"Add custom CSS": "Ajouter un CSS spécifique",
 | 
				
			||||||
	"Advanced": "Avancée",
 | 
						"Advanced": "Avancé",
 | 
				
			||||||
	"All the connected organizations will appear here": "Toutes les organisations connectées apparaîtront ici",
 | 
						"All the connected organizations will appear here": "Toutes les organisations connectées apparaîtront ici",
 | 
				
			||||||
	"Always start minimized": "Toujours commencer minimisé",
 | 
						"Always start minimized": "Toujours démarrer minimisé",
 | 
				
			||||||
	"App Updates": "Mises à jour de l'application",
 | 
						"App Updates": "Mise à jour de l'application",
 | 
				
			||||||
	"Appearance": "Apparence",
 | 
						"Appearance": "Apparence",
 | 
				
			||||||
	"Application Shortcuts": "Raccourcis d'application",
 | 
						"Application Shortcuts": "Raccourcis de l'application",
 | 
				
			||||||
	"Are you sure you want to disconnect this organization?": "Êtes-vous sûr de vouloir déconnecter cette organisation?",
 | 
						"Are you sure you want to disconnect this organization?": "Êtes-vous certain de vouloir déconnecter cette organisation?",
 | 
				
			||||||
	"Auto hide Menu bar": "Masquer automatiquement la barre de menus",
 | 
						"Auto hide Menu bar": "Cacher automatiquement la barre de menu",
 | 
				
			||||||
	"Auto hide menu bar (Press Alt key to display)": "Masquer automatiquement la barre de menu (appuyez sur la touche Alt pour afficher)",
 | 
						"Auto hide menu bar (Press Alt key to display)": "Cacher automatiquement la barre de menu (Appuyez sur la touche Alt pour l'afficher)",
 | 
				
			||||||
	"Back": "Retour",
 | 
						"Back": "Précédent",
 | 
				
			||||||
	"Bounce dock on new private message": "Bounce Dock sur un nouveau message privé",
 | 
						"Bounce dock on new private message": "Animer l'horloge à la réception d'un nouveau message privé",
 | 
				
			||||||
	"Certificate file": "Dossier de certificat",
 | 
						"Certificate file": "Fichier de certificat",
 | 
				
			||||||
	"Change": "Changement",
 | 
						"Change": "Changer",
 | 
				
			||||||
	"Check for Updates": "Vérifier les mises à jour",
 | 
						"Check for Updates": "Vérifier les mises à jour",
 | 
				
			||||||
	"Close": "Fermer",
 | 
						"Close": "Fermer",
 | 
				
			||||||
	"Connect": "Relier",
 | 
						"Connect": "Connecter",
 | 
				
			||||||
	"Connect to another organization": "Se connecter à une autre organisation",
 | 
						"Connect to another organization": "Se connecter à une autre organisation",
 | 
				
			||||||
	"Connected organizations": "Organisations connectées",
 | 
						"Connected organizations": "Organisations connectées",
 | 
				
			||||||
	"Copy": "Copie",
 | 
						"Copy": "Copier",
 | 
				
			||||||
	"Copy Zulip URL": "Copier l'URL Zulip",
 | 
						"Copy Zulip URL": "Copier l'URL de Zulip",
 | 
				
			||||||
	"Create a new organization": "Créer une nouvelle organisation",
 | 
						"Create a new organization": "Créer une nouvelle organisation",
 | 
				
			||||||
	"Cut": "Couper",
 | 
						"Cut": "Couper",
 | 
				
			||||||
	"Default download location": "Emplacement de téléchargement par défaut",
 | 
						"Default download location": "Destination de téléchargement par défaut",
 | 
				
			||||||
	"Delete": "Effacer",
 | 
						"Delete": "Supprimer",
 | 
				
			||||||
	"Desktop App Settings": "Paramètres de l'application de bureau",
 | 
						"Desktop App Settings": "Paramètres de l'application de bureau",
 | 
				
			||||||
	"Desktop Notifications": "Notifications de bureau",
 | 
						"Desktop Notifications": "Notifications de bureau",
 | 
				
			||||||
	"Desktop Settings": "Paramètres du bureau",
 | 
						"Desktop Settings": "Paramètres de bureau",
 | 
				
			||||||
	"Disconnect": "Déconnecter",
 | 
						"Disconnect": "Déconnecter",
 | 
				
			||||||
	"Download App Logs": "Télécharger les journaux d'application",
 | 
						"Download App Logs": "Télécharger le journal de l'application",
 | 
				
			||||||
	"Edit": "modifier",
 | 
						"Edit": "Modifier",
 | 
				
			||||||
	"Edit Shortcuts": "Modifier les raccourcis",
 | 
						"Edit Shortcuts": "Modifier les raccourcis",
 | 
				
			||||||
	"Enable auto updates": "Activer les mises à jour automatiques",
 | 
						"Enable auto updates": "Activer la mise à jour automatique",
 | 
				
			||||||
	"Enable error reporting (requires restart)": "Activer le signalement des erreurs (nécessite un redémarrage)",
 | 
						"Enable error reporting (requires restart)": "Activer le rapport d'erreur (redémarrage nécessaire)",
 | 
				
			||||||
	"Enable spellchecker (requires restart)": "Activer le correcteur orthographique (nécessite un redémarrage)",
 | 
						"Enable spellchecker (requires restart)": "Activer le correcteur orthographique (redémarrage nécessaire)",
 | 
				
			||||||
	"Factory Reset": "Retour aux paramètres d'usine",
 | 
						"Factory Reset": "Réinitialiser aux paramètres par défaut",
 | 
				
			||||||
	"File": "Fichier",
 | 
						"File": "Fichier",
 | 
				
			||||||
	"Find accounts": "Trouver des comptes",
 | 
						"Find accounts": "Rechercher un compte",
 | 
				
			||||||
	"Find accounts by email": "Trouver des comptes par email",
 | 
						"Find accounts by email": "Rechercher un compte par adresse courriel",
 | 
				
			||||||
	"Flash taskbar on new message": "Barre de tâches Flash sur un nouveau message",
 | 
						"Flash taskbar on new message": "Clignoter la barre de tâche lors d'un nouveau message",
 | 
				
			||||||
	"Forward": "Vers l'avant",
 | 
						"Forward": "Suivant",
 | 
				
			||||||
	"Functionality": "La fonctionnalité",
 | 
						"Functionality": "Fonctionnalités",
 | 
				
			||||||
	"General": "Général",
 | 
						"General": "Général",
 | 
				
			||||||
	"Get beta updates": "Obtenir les mises à jour bêta",
 | 
						"Get beta updates": "Recevoir les mises à jour Beta",
 | 
				
			||||||
	"Hard Reload": "Rechargement dur",
 | 
						"Hard Reload": "Forcer un rechargement",
 | 
				
			||||||
	"Help": "Aidez-moi",
 | 
						"Help": "Aide",
 | 
				
			||||||
	"Help Center": "Centre d'aide",
 | 
						"Help Center": "Centre d'aide",
 | 
				
			||||||
	"History": "L'histoire",
 | 
						"History": "Historique",
 | 
				
			||||||
	"History Shortcuts": "Raccourcis Histoire",
 | 
						"History Shortcuts": "Historique des raccourcis",
 | 
				
			||||||
	"Keyboard Shortcuts": "Raccourcis clavier",
 | 
						"Keyboard Shortcuts": "Raccourcis clavier",
 | 
				
			||||||
	"Log Out": "Connectez - Out",
 | 
						"Log Out": "Se déconnecter",
 | 
				
			||||||
	"Log Out of Organization": "Déconnexion de l'organisation",
 | 
						"Log Out of Organization": "Se déconnecter de l'organisation",
 | 
				
			||||||
	"Manual proxy configuration": "Configuration manuelle du proxy",
 | 
						"Manual proxy configuration": "Configuration manuelle du proxy",
 | 
				
			||||||
	"Minimize": "Minimiser",
 | 
						"Minimize": "Minimiser",
 | 
				
			||||||
	"Mute all sounds from Zulip": "Couper tous les sons de Zulip",
 | 
						"Mute all sounds from Zulip": "Couper tous les sons de Zulip",
 | 
				
			||||||
	"NO": "NON",
 | 
						"NO": "Non",
 | 
				
			||||||
	"Network": "Réseau",
 | 
						"Network": "Réseau",
 | 
				
			||||||
	"OR": "OU",
 | 
						"OR": "OU",
 | 
				
			||||||
	"Organization URL": "URL de l'organisation",
 | 
						"Organization URL": "URL de l'organisation",
 | 
				
			||||||
	"Organizations": "Les organisations",
 | 
						"Organizations": "Organisations",
 | 
				
			||||||
	"Paste": "Coller",
 | 
						"Paste": "Coller",
 | 
				
			||||||
	"Paste and Match Style": "Coller et assortir le style",
 | 
						"Paste and Match Style": "Coller et Conserver le style",
 | 
				
			||||||
	"Proxy": "Procuration",
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
	"Proxy bypass rules": "Règles de contournement des procurations",
 | 
						"Proxy bypass rules": "Règles de contournement du proxy",
 | 
				
			||||||
	"Proxy rules": "Règles de procuration",
 | 
						"Proxy rules": "Règles du proxy",
 | 
				
			||||||
	"Quit": "Quitter",
 | 
						"Quit": "Quitter",
 | 
				
			||||||
	"Quit Zulip": "Quittez Zulip",
 | 
						"Quit Zulip": "Quitter Zulip",
 | 
				
			||||||
	"Redo": "Refaire",
 | 
						"Redo": "Refaire",
 | 
				
			||||||
	"Release Notes": "Notes de version",
 | 
						"Release Notes": "Notes sur la version",
 | 
				
			||||||
	"Reload": "Recharger",
 | 
						"Reload": "Recharger",
 | 
				
			||||||
	"Report an Issue": "Signaler un problème",
 | 
						"Report an Issue": "Rapporter un problème",
 | 
				
			||||||
	"Reset App Data": "Réinitialiser les données de l'application",
 | 
						"Save": "Sauvegarder",
 | 
				
			||||||
	"Reset App Settings": "Réinitialiser les paramètres de l'application",
 | 
						"Select All": "Sélectionner tout",
 | 
				
			||||||
	"Reset Application Data": "Réinitialiser les données d'application",
 | 
						"Settings": "Paramètres",
 | 
				
			||||||
	"Save": "sauvegarder",
 | 
					 | 
				
			||||||
	"Select All": "Tout sélectionner",
 | 
					 | 
				
			||||||
	"Settings": "Réglages",
 | 
					 | 
				
			||||||
	"Shortcuts": "Raccourcis",
 | 
						"Shortcuts": "Raccourcis",
 | 
				
			||||||
	"Show App Logs": "Afficher les journaux d'application",
 | 
						"Show App Logs": "Afficher le journal de l'application",
 | 
				
			||||||
	"Show app icon in system tray": "Afficher l'icône de l'application dans la barre d'état système",
 | 
						"Show app icon in system tray": "Afficher l'icone de l'application dans la barre d'état",
 | 
				
			||||||
	"Show app unread badge": "Afficher le badge non lu de l'application",
 | 
						"Show app unread badge": "Afficher un badge lors d'un message non lu",
 | 
				
			||||||
	"Show desktop notifications": "Afficher les notifications du bureau",
 | 
						"Show desktop notifications": "Afficher les notifications sur le bureau",
 | 
				
			||||||
	"Show downloaded files in file manager": "Afficher les fichiers téléchargés dans le gestionnaire de fichiers",
 | 
						"Show downloaded files in file manager": "Afficher les fichiers téléchargés dans le gestionnaire de fichiers",
 | 
				
			||||||
	"Show sidebar": "Afficher la barre latérale",
 | 
						"Show sidebar": "Afficher la barre latérale",
 | 
				
			||||||
	"Start app at login": "Lancer l'application à la connexion",
 | 
						"Start app at login": "Démarrer l'application à l'ouverture",
 | 
				
			||||||
	"Switch to Next Organization": "Passer à l'organisation suivante",
 | 
						"Switch to Next Organization": "Basculer à l'organisation suivante",
 | 
				
			||||||
	"Switch to Previous Organization": "Passer à l'organisation précédente",
 | 
						"Switch to Previous Organization": "Basculer à l'organisation précédente",
 | 
				
			||||||
	"These desktop app shortcuts extend the Zulip webapp's": "Ces raccourcis d’applications de bureau étendent les applications Web de Zulip.",
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "Ces raccourcis d'application vont au-delà de l'application web Zulip",
 | 
				
			||||||
	"This will delete all application data including all added accounts and preferences": "Cela supprimera toutes les données de l'application, y compris tous les comptes et préférences ajoutés.",
 | 
						"This will delete all application data including all added accounts and preferences": "Ceci supprimera toutes les données de l'application, incluant les comptes et préférences",
 | 
				
			||||||
	"Tip": "Pointe",
 | 
						"Tip": "Conseil",
 | 
				
			||||||
	"Toggle DevTools for Active Tab": "Basculer DevTools pour l'onglet actif",
 | 
						"Toggle DevTools for Active Tab": "Activer les outils de développement dans l'onglet actif",
 | 
				
			||||||
	"Toggle DevTools for Zulip App": "Basculer DevTools pour Zulip App",
 | 
						"Toggle DevTools for Zulip App": "Activer les outils de développement dans l'application Zulip",
 | 
				
			||||||
	"Toggle Do Not Disturb": "Basculer ne pas déranger",
 | 
						"Toggle Do Not Disturb": "Activer le mode Ne Pas Déranger",
 | 
				
			||||||
	"Toggle Full Screen": "Basculer en plein écran",
 | 
						"Toggle Full Screen": "Activer le mode Plein Écran",
 | 
				
			||||||
	"Toggle Sidebar": "Basculer la barre latérale",
 | 
						"Toggle Sidebar": "Activer la barre latérale",
 | 
				
			||||||
	"Toggle Tray Icon": "Basculer l'icône du plateau",
 | 
						"Toggle Tray Icon": "Activer l'icone dans la barre d'état",
 | 
				
			||||||
	"Tools": "Outils",
 | 
						"Tools": "Outils",
 | 
				
			||||||
	"Undo": "annuler",
 | 
						"Undo": "Annuler",
 | 
				
			||||||
	"Upload": "Télécharger",
 | 
						"Upload": "Envoyer",
 | 
				
			||||||
	"Use system proxy settings (requires restart)": "Utiliser les paramètres proxy du système (nécessite un redémarrage)",
 | 
						"Use system proxy settings (requires restart)": "Utiliser les paramètres de proxy du système (exige un redémarrage)",
 | 
				
			||||||
	"View": "Vue",
 | 
						"View": "Affichage",
 | 
				
			||||||
	"View Shortcuts": "Afficher les raccourcis",
 | 
						"View Shortcuts": "Voir les raccourcis",
 | 
				
			||||||
	"Window": "La fenêtre",
 | 
						"Window": "Fenêtre",
 | 
				
			||||||
	"Window Shortcuts": "Raccourcis de la fenêtre",
 | 
						"Window Shortcuts": "Raccourcis fenêtre",
 | 
				
			||||||
	"YES": "OUI",
 | 
						"YES": "Oui",
 | 
				
			||||||
	"Zoom In": "Agrandir",
 | 
						"Zoom In": "Zoom avant",
 | 
				
			||||||
	"Zoom Out": "Dézoomer",
 | 
						"Zoom Out": "Zoom arrière",
 | 
				
			||||||
	"Zulip Help": "Aide Zulip",
 | 
						"Zulip Help": "Aide sur Zulip",
 | 
				
			||||||
	"keyboard shortcuts": "Raccourcis clavier",
 | 
						"keyboard shortcuts": "Raccourcis clavier",
 | 
				
			||||||
	"script": "scénario"
 | 
						"script": "Script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quitter l'application lors de la fermeture de la fenêtre",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Demander où sauvegarder les fichiers avant de télécharger",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										134
									
								
								app/translations/gl.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "About Zulip",
 | 
				
			||||||
 | 
						"Actual Size": "Actual Size",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Add Custom Certificates",
 | 
				
			||||||
 | 
						"Add Organization": "Add Organization",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "Add a Zulip organization",
 | 
				
			||||||
 | 
						"Add custom CSS": "Add custom CSS",
 | 
				
			||||||
 | 
						"Advanced": "Advanced",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "All the connected organizations will appear here",
 | 
				
			||||||
 | 
						"Always start minimized": "Always start minimized",
 | 
				
			||||||
 | 
						"App Updates": "App Updates",
 | 
				
			||||||
 | 
						"Appearance": "Appearance",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Application Shortcuts",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Auto hide Menu bar",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
				
			||||||
 | 
						"Back": "Back",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certificate file",
 | 
				
			||||||
 | 
						"Change": "Change",
 | 
				
			||||||
 | 
						"Check for Updates": "Check for Updates",
 | 
				
			||||||
 | 
						"Close": "Close",
 | 
				
			||||||
 | 
						"Connect": "Connect",
 | 
				
			||||||
 | 
						"Connect to another organization": "Connect to another organization",
 | 
				
			||||||
 | 
						"Connected organizations": "Connected organizations",
 | 
				
			||||||
 | 
						"Copy": "Copy",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "Copy Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Create a new organization",
 | 
				
			||||||
 | 
						"Cut": "Cut",
 | 
				
			||||||
 | 
						"Default download location": "Default download location",
 | 
				
			||||||
 | 
						"Delete": "Delete",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop App Settings",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop Notifications",
 | 
				
			||||||
 | 
						"Desktop Settings": "Desktop Settings",
 | 
				
			||||||
 | 
						"Disconnect": "Disconnect",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "Editar",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Edit Shortcuts",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "File",
 | 
				
			||||||
 | 
						"Find accounts": "Find accounts",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Help",
 | 
				
			||||||
 | 
						"Help Center": "Help Center",
 | 
				
			||||||
 | 
						"History": "History",
 | 
				
			||||||
 | 
						"History Shortcuts": "History Shortcuts",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "OR",
 | 
				
			||||||
 | 
						"Organization URL": "Organization URL",
 | 
				
			||||||
 | 
						"Organizations": "Organizations",
 | 
				
			||||||
 | 
						"Paste": "Paste",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Quit",
 | 
				
			||||||
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Gardar",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Configuración",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -75,9 +75,6 @@
 | 
				
			|||||||
	"Release Notes": "रिलीज नोट्स",
 | 
						"Release Notes": "रिलीज नोट्स",
 | 
				
			||||||
	"Reload": "सीमा से अधिक लादना",
 | 
						"Reload": "सीमा से अधिक लादना",
 | 
				
			||||||
	"Report an Issue": "मामले की रिपोर्ट करें",
 | 
						"Report an Issue": "मामले की रिपोर्ट करें",
 | 
				
			||||||
	"Reset App Data": "ऐप डेटा रीसेट करें",
 | 
					 | 
				
			||||||
	"Reset App Settings": "ऐप सेटिंग रीसेट करें",
 | 
					 | 
				
			||||||
	"Reset Application Data": "एप्लिकेशन डेटा रीसेट करें",
 | 
					 | 
				
			||||||
	"Save": "बचाना",
 | 
						"Save": "बचाना",
 | 
				
			||||||
	"Select All": "सभी का चयन करे",
 | 
						"Select All": "सभी का चयन करे",
 | 
				
			||||||
	"Settings": "सेटिंग्स",
 | 
						"Settings": "सेटिंग्स",
 | 
				
			||||||
@@ -114,5 +111,24 @@
 | 
				
			|||||||
	"Zulip Help": "Zulip मदद",
 | 
						"Zulip Help": "Zulip मदद",
 | 
				
			||||||
	"keyboard shortcuts": "कुंजीपटल अल्प मार्ग",
 | 
						"keyboard shortcuts": "कुंजीपटल अल्प मार्ग",
 | 
				
			||||||
	"script": "लिपि",
 | 
						"script": "लिपि",
 | 
				
			||||||
	"AddServer": "AddServer"
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										134
									
								
								app/translations/hr.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"About Zulip": "About Zulip",
 | 
				
			||||||
 | 
						"Actual Size": "Actual Size",
 | 
				
			||||||
 | 
						"Add Custom Certificates": "Add Custom Certificates",
 | 
				
			||||||
 | 
						"Add Organization": "Add Organization",
 | 
				
			||||||
 | 
						"Add a Zulip organization": "Add a Zulip organization",
 | 
				
			||||||
 | 
						"Add custom CSS": "Add custom CSS",
 | 
				
			||||||
 | 
						"Advanced": "Advanced",
 | 
				
			||||||
 | 
						"All the connected organizations will appear here": "All the connected organizations will appear here",
 | 
				
			||||||
 | 
						"Always start minimized": "Always start minimized",
 | 
				
			||||||
 | 
						"App Updates": "App Updates",
 | 
				
			||||||
 | 
						"Appearance": "Appearance",
 | 
				
			||||||
 | 
						"Application Shortcuts": "Application Shortcuts",
 | 
				
			||||||
 | 
						"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
 | 
				
			||||||
 | 
						"Auto hide Menu bar": "Auto hide Menu bar",
 | 
				
			||||||
 | 
						"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
 | 
				
			||||||
 | 
						"Back": "Back",
 | 
				
			||||||
 | 
						"Bounce dock on new private message": "Bounce dock on new private message",
 | 
				
			||||||
 | 
						"Certificate file": "Certificate file",
 | 
				
			||||||
 | 
						"Change": "Change",
 | 
				
			||||||
 | 
						"Check for Updates": "Check for Updates",
 | 
				
			||||||
 | 
						"Close": "Close",
 | 
				
			||||||
 | 
						"Connect": "Connect",
 | 
				
			||||||
 | 
						"Connect to another organization": "Connect to another organization",
 | 
				
			||||||
 | 
						"Connected organizations": "Connected organizations",
 | 
				
			||||||
 | 
						"Copy": "Copy",
 | 
				
			||||||
 | 
						"Copy Zulip URL": "Copy Zulip URL",
 | 
				
			||||||
 | 
						"Create a new organization": "Create a new organization",
 | 
				
			||||||
 | 
						"Cut": "Cut",
 | 
				
			||||||
 | 
						"Default download location": "Default download location",
 | 
				
			||||||
 | 
						"Delete": "Delete",
 | 
				
			||||||
 | 
						"Desktop App Settings": "Desktop App Settings",
 | 
				
			||||||
 | 
						"Desktop Notifications": "Desktop Notifications",
 | 
				
			||||||
 | 
						"Desktop Settings": "Desktop Settings",
 | 
				
			||||||
 | 
						"Disconnect": "Disconnect",
 | 
				
			||||||
 | 
						"Download App Logs": "Download App Logs",
 | 
				
			||||||
 | 
						"Edit": "Edit",
 | 
				
			||||||
 | 
						"Edit Shortcuts": "Edit Shortcuts",
 | 
				
			||||||
 | 
						"Enable auto updates": "Enable auto updates",
 | 
				
			||||||
 | 
						"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
 | 
				
			||||||
 | 
						"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset": "Factory Reset",
 | 
				
			||||||
 | 
						"File": "File",
 | 
				
			||||||
 | 
						"Find accounts": "Find accounts",
 | 
				
			||||||
 | 
						"Find accounts by email": "Find accounts by email",
 | 
				
			||||||
 | 
						"Flash taskbar on new message": "Flash taskbar on new message",
 | 
				
			||||||
 | 
						"Forward": "Forward",
 | 
				
			||||||
 | 
						"Functionality": "Functionality",
 | 
				
			||||||
 | 
						"General": "General",
 | 
				
			||||||
 | 
						"Get beta updates": "Get beta updates",
 | 
				
			||||||
 | 
						"Hard Reload": "Hard Reload",
 | 
				
			||||||
 | 
						"Help": "Help",
 | 
				
			||||||
 | 
						"Help Center": "Help Center",
 | 
				
			||||||
 | 
						"History": "History",
 | 
				
			||||||
 | 
						"History Shortcuts": "History Shortcuts",
 | 
				
			||||||
 | 
						"Keyboard Shortcuts": "Keyboard Shortcuts",
 | 
				
			||||||
 | 
						"Log Out": "Log Out",
 | 
				
			||||||
 | 
						"Log Out of Organization": "Log Out of Organization",
 | 
				
			||||||
 | 
						"Manual proxy configuration": "Manual proxy configuration",
 | 
				
			||||||
 | 
						"Minimize": "Minimize",
 | 
				
			||||||
 | 
						"Mute all sounds from Zulip": "Mute all sounds from Zulip",
 | 
				
			||||||
 | 
						"NO": "NO",
 | 
				
			||||||
 | 
						"Network": "Network",
 | 
				
			||||||
 | 
						"OR": "OR",
 | 
				
			||||||
 | 
						"Organization URL": "Organization URL",
 | 
				
			||||||
 | 
						"Organizations": "Organizations",
 | 
				
			||||||
 | 
						"Paste": "Paste",
 | 
				
			||||||
 | 
						"Paste and Match Style": "Paste and Match Style",
 | 
				
			||||||
 | 
						"Proxy": "Proxy",
 | 
				
			||||||
 | 
						"Proxy bypass rules": "Proxy bypass rules",
 | 
				
			||||||
 | 
						"Proxy rules": "Proxy rules",
 | 
				
			||||||
 | 
						"Quit": "Quit",
 | 
				
			||||||
 | 
						"Quit Zulip": "Quit Zulip",
 | 
				
			||||||
 | 
						"Redo": "Redo",
 | 
				
			||||||
 | 
						"Release Notes": "Release Notes",
 | 
				
			||||||
 | 
						"Reload": "Reload",
 | 
				
			||||||
 | 
						"Report an Issue": "Report an Issue",
 | 
				
			||||||
 | 
						"Save": "Save",
 | 
				
			||||||
 | 
						"Select All": "Select All",
 | 
				
			||||||
 | 
						"Settings": "Settings",
 | 
				
			||||||
 | 
						"Shortcuts": "Shortcuts",
 | 
				
			||||||
 | 
						"Show App Logs": "Show App Logs",
 | 
				
			||||||
 | 
						"Show app icon in system tray": "Show app icon in system tray",
 | 
				
			||||||
 | 
						"Show app unread badge": "Show app unread badge",
 | 
				
			||||||
 | 
						"Show desktop notifications": "Show desktop notifications",
 | 
				
			||||||
 | 
						"Show downloaded files in file manager": "Show downloaded files in file manager",
 | 
				
			||||||
 | 
						"Show sidebar": "Show sidebar",
 | 
				
			||||||
 | 
						"Start app at login": "Start app at login",
 | 
				
			||||||
 | 
						"Switch to Next Organization": "Switch to Next Organization",
 | 
				
			||||||
 | 
						"Switch to Previous Organization": "Switch to Previous Organization",
 | 
				
			||||||
 | 
						"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
 | 
				
			||||||
 | 
						"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
 | 
				
			||||||
 | 
						"Tip": "Tip",
 | 
				
			||||||
 | 
						"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
 | 
				
			||||||
 | 
						"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
 | 
				
			||||||
 | 
						"Toggle Do Not Disturb": "Toggle Do Not Disturb",
 | 
				
			||||||
 | 
						"Toggle Full Screen": "Toggle Full Screen",
 | 
				
			||||||
 | 
						"Toggle Sidebar": "Toggle Sidebar",
 | 
				
			||||||
 | 
						"Toggle Tray Icon": "Toggle Tray Icon",
 | 
				
			||||||
 | 
						"Tools": "Tools",
 | 
				
			||||||
 | 
						"Undo": "Undo",
 | 
				
			||||||
 | 
						"Upload": "Upload",
 | 
				
			||||||
 | 
						"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
 | 
				
			||||||
 | 
						"View": "View",
 | 
				
			||||||
 | 
						"View Shortcuts": "View Shortcuts",
 | 
				
			||||||
 | 
						"Window": "Window",
 | 
				
			||||||
 | 
						"Window Shortcuts": "Window Shortcuts",
 | 
				
			||||||
 | 
						"YES": "YES",
 | 
				
			||||||
 | 
						"Zoom In": "Zoom In",
 | 
				
			||||||
 | 
						"Zoom Out": "Zoom Out",
 | 
				
			||||||
 | 
						"Zulip Help": "Zulip Help",
 | 
				
			||||||
 | 
						"keyboard shortcuts": "keyboard shortcuts",
 | 
				
			||||||
 | 
						"script": "script",
 | 
				
			||||||
 | 
						"Quit when the window is closed": "Quit when the window is closed",
 | 
				
			||||||
 | 
						"Ask where to save files before downloading": "Ask where to save files before downloading",
 | 
				
			||||||
 | 
						"Services": "Services",
 | 
				
			||||||
 | 
						"Hide": "Hide",
 | 
				
			||||||
 | 
						"Hide Others": "Hide Others",
 | 
				
			||||||
 | 
						"Unhide": "Unhide",
 | 
				
			||||||
 | 
						"AddServer": "AddServer",
 | 
				
			||||||
 | 
						"App language (requires restart)": "App language (requires restart)",
 | 
				
			||||||
 | 
						"Factory Reset Data": "Factory Reset Data",
 | 
				
			||||||
 | 
						"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
 | 
				
			||||||
 | 
						"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
 | 
				
			||||||
 | 
						"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
 | 
				
			||||||
 | 
						"Copy Link": "Copy Link",
 | 
				
			||||||
 | 
						"Copy Image": "Copy Image",
 | 
				
			||||||
 | 
						"Copy Image URL": "Copy Image URL",
 | 
				
			||||||
 | 
						"No Suggestion Found": "No Suggestion Found",
 | 
				
			||||||
 | 
						"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
 | 
				
			||||||
 | 
						"Spellchecker Languages": "Spellchecker Languages",
 | 
				
			||||||
 | 
						"Add to Dictionary": "Add to Dictionary",
 | 
				
			||||||
 | 
						"Look Up": "Look Up"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||