Mark more strings for translation.

Fixes #1128 among many other things.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2025-07-31 16:22:38 -07:00
committed by Anders Kaseorg
parent 11e2635aa0
commit 9dd5fd2aa5
14 changed files with 98 additions and 47 deletions

View File

@@ -3,7 +3,8 @@ import fs from "node:fs";
import os from "node:os"; import os from "node:os";
import path from "node:path"; import path from "node:path";
import {html} from "./html.ts"; import {Html, html} from "./html.ts";
import * as t from "./translation-util.ts";
export async function openBrowser(url: URL): Promise<void> { export async function openBrowser(url: URL): Promise<void> {
if (["http:", "https:", "mailto:"].includes(url.protocol)) { if (["http:", "https:", "mailto:"].includes(url.protocol)) {
@@ -21,7 +22,7 @@ export async function openBrowser(url: URL): Promise<void> {
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="Refresh" content="0; url=${url.href}" /> <meta http-equiv="Refresh" content="0; url=${url.href}" />
<title>Redirecting</title> <title>${t.__("Redirecting")}</title>
<style> <style>
html { html {
font-family: menu, "Helvetica Neue", sans-serif; font-family: menu, "Helvetica Neue", sans-serif;
@@ -29,7 +30,13 @@ export async function openBrowser(url: URL): Promise<void> {
</style> </style>
</head> </head>
<body> <body>
<p>Opening <a href="${url.href}">${url.href}</a>…</p> <p>
${new Html({
html: t.__("Opening {{{link}}}…", {
link: html`<a href="${url.href}">${url.href}</a>`.html,
}),
})}
</p>
</body> </body>
</html> </html>
`.html, `.html,

View File

@@ -1,3 +1,5 @@
import * as t from "./translation-util.ts";
type DialogBoxError = { type DialogBoxError = {
title: string; title: string;
content: string; content: string;
@@ -13,26 +15,24 @@ export function invalidZulipServerError(domain: string): string {
https://zulip.readthedocs.io/en/stable/production/ssl-certificates.html`; https://zulip.readthedocs.io/en/stable/production/ssl-certificates.html`;
} }
export function enterpriseOrgError( export function enterpriseOrgError(domains: string[]): DialogBoxError {
length: number,
domains: string[],
): DialogBoxError {
let domainList = ""; let domainList = "";
for (const domain of domains) { for (const domain of domains) {
domainList += `${domain}\n`; domainList += `${domain}\n`;
} }
return { return {
title: `Could not add the following ${ title: t.__mf(
length === 1 ? "organization" : "organizations" "{number, plural, one {Could not add # organization} other {Could not add # organizations}}",
}`, {number: domains.length},
content: `${domainList}\nPlease contact your system administrator.`, ),
content: `${domainList}\n${t.__("Please contact your system administrator.")}`,
}; };
} }
export function orgRemovalError(url: string): DialogBoxError { export function orgRemovalError(url: string): DialogBoxError {
return { return {
title: `Removing ${url} is a restricted operation.`, title: t.__("Removing {{{url}}} is a restricted operation.", {url}),
content: "Please contact your system administrator.", content: t.__("Please contact your system administrator."),
}; };
} }

View File

@@ -13,4 +13,4 @@ i18n.configure({
/* Fetches the current appLocale from settings.json */ /* Fetches the current appLocale from settings.json */
i18n.setLocale(ConfigUtil.getConfigItem("appLanguage", "en") ?? "en"); i18n.setLocale(ConfigUtil.getConfigItem("appLanguage", "en") ?? "en");
export {__} from "i18n"; export {__, __mf} from "i18n";

View File

@@ -11,6 +11,7 @@ import path from "node:path";
import * as ConfigUtil from "../common/config-util.ts"; import * as ConfigUtil from "../common/config-util.ts";
import * as LinkUtil from "../common/link-util.ts"; import * as LinkUtil from "../common/link-util.ts";
import * as t from "../common/translation-util.ts";
import {send} from "./typed-ipc-main.ts"; import {send} from "./typed-ipc-main.ts";
@@ -125,8 +126,8 @@ export default function handleExternalLink(
downloadPath, downloadPath,
async completed(filePath: string, fileName: string) { async completed(filePath: string, fileName: string) {
const downloadNotification = new Notification({ const downloadNotification = new Notification({
title: "Download Complete", title: t.__("Download Complete"),
body: `Click to show ${fileName} in folder`, body: t.__("Click to show {{{fileName}}} in folder", {fileName}),
silent: true, // We'll play our own sound - ding.ogg silent: true, // We'll play our own sound - ding.ogg
}); });
downloadNotification.on("click", () => { downloadNotification.on("click", () => {
@@ -149,8 +150,8 @@ export default function handleExternalLink(
if (state !== "cancelled") { if (state !== "cancelled") {
if (ConfigUtil.getConfigItem("promptDownload", false)) { if (ConfigUtil.getConfigItem("promptDownload", false)) {
new Notification({ new Notification({
title: "Download Complete", title: t.__("Download Complete"),
body: "Download failed", body: t.__("Download failed"),
}).show(); }).show();
} else { } else {
contents.downloadURL(url.href); contents.downloadURL(url.href);

View File

@@ -5,6 +5,7 @@ import {z} from "zod";
import * as ConfigUtil from "../common/config-util.ts"; import * as ConfigUtil from "../common/config-util.ts";
import Logger from "../common/logger-util.ts"; import Logger from "../common/logger-util.ts";
import * as t from "../common/translation-util.ts";
import * as LinuxUpdateUtil from "./linux-update-util.ts"; import * as LinuxUpdateUtil from "./linux-update-util.ts";
@@ -34,8 +35,11 @@ export async function linuxUpdateNotification(session: Session): Promise<void> {
const notified = LinuxUpdateUtil.getUpdateItem(latestVersion); const notified = LinuxUpdateUtil.getUpdateItem(latestVersion);
if (notified === null) { if (notified === null) {
new Notification({ new Notification({
title: "Zulip Update", title: t.__("Zulip Update"),
body: `A new version ${latestVersion} is available. Please update using your package manager.`, body: t.__(
"A new version {{{version}}} is available. Please update using your package manager.",
{version: latestVersion},
),
}).show(); }).show();
LinuxUpdateUtil.setUpdateItem(latestVersion, true); LinuxUpdateUtil.setUpdateItem(latestVersion, true);
} }

View File

@@ -80,7 +80,6 @@ export class ServerManagerView {
$dndTooltip: HTMLElement; $dndTooltip: HTMLElement;
$sidebar: Element; $sidebar: Element;
$fullscreenPopup: Element; $fullscreenPopup: Element;
$fullscreenEscapeKey: string;
loading: Set<string>; loading: Set<string>;
activeTabIndex: number; activeTabIndex: number;
tabs: ServerOrFunctionalTab[]; tabs: ServerOrFunctionalTab[];
@@ -121,8 +120,10 @@ export class ServerManagerView {
this.$sidebar = document.querySelector("#sidebar")!; this.$sidebar = document.querySelector("#sidebar")!;
this.$fullscreenPopup = document.querySelector("#fullscreen-popup")!; this.$fullscreenPopup = document.querySelector("#fullscreen-popup")!;
this.$fullscreenEscapeKey = process.platform === "darwin" ? "^⌘F" : "F11"; this.$fullscreenPopup.textContent = t.__(
this.$fullscreenPopup.textContent = `Press ${this.$fullscreenEscapeKey} to exit full screen`; "Press {{{exitKey}}} to exit full screen",
{exitKey: process.platform === "darwin" ? "^⌘F" : "F11"},
);
this.loading = new Set(); this.loading = new Set();
this.activeTabIndex = -1; this.activeTabIndex = -1;
@@ -261,7 +262,10 @@ export class ServerManagerView {
} catch (error: unknown) { } catch (error: unknown) {
logger.error(error); logger.error(error);
logger.error( logger.error(
`Could not add ${domain}. Please contact your system administrator.`, t.__(
"Could not add {{{domain}}}. Please contact your system administrator.",
{domain},
),
); );
return false; return false;
} }
@@ -311,10 +315,7 @@ export class ServerManagerView {
failedDomains.push(org); failedDomains.push(org);
} }
const {title, content} = Messages.enterpriseOrgError( const {title, content} = Messages.enterpriseOrgError(failedDomains);
domainsAdded.length,
failedDomains,
);
dialog.showErrorBox(title, content); dialog.showErrorBox(title, content);
if (DomainUtil.getDomains().length === 0) { if (DomainUtil.getDomains().length === 0) {
// No orgs present, stop showing loading gif // No orgs present, stop showing loading gif
@@ -795,8 +796,9 @@ export class ServerManagerView {
// Toggles the dnd button icon. // Toggles the dnd button icon.
toggleDndButton(alert: boolean): void { toggleDndButton(alert: boolean): void {
this.$dndTooltip.textContent = this.$dndTooltip.textContent = alert
(alert ? "Disable" : "Enable") + " Do Not Disturb"; ? t.__("Disable Do Not Disturb")
: t.__("Enable Do Not Disturb");
const $dndIcon = this.$dndButton.querySelector("i")!; const $dndIcon = this.$dndButton.querySelector("i")!;
$dndIcon.textContent = alert ? "notifications_off" : "notifications"; $dndIcon.textContent = alert ? "notifications_off" : "notifications";

View File

@@ -1,4 +1,5 @@
import {type Html, html} from "../../../../common/html.ts"; import {type Html, html} from "../../../../common/html.ts";
import * as t from "../../../../common/translation-util.ts";
import {generateNodeFromHtml} from "../../components/base.ts"; import {generateNodeFromHtml} from "../../components/base.ts";
import {ipcRenderer} from "../../typed-ipc-renderer.ts"; import {ipcRenderer} from "../../typed-ipc-renderer.ts";
@@ -31,7 +32,7 @@ export function generateOptionHtml(
const labelHtml = disabled const labelHtml = disabled
? html`<label ? html`<label
class="disallowed" class="disallowed"
title="Setting locked by system administrator." title="${t.__("Setting locked by system administrator.")}"
></label>` ></label>`
: html`<label></label>`; : html`<label></label>`;
if (settingOption) { if (settingOption) {

View File

@@ -561,8 +561,9 @@ export function initGeneralSection({$root}: GeneralSectionProperties): void {
} }
async function factoryResetSettings(): Promise<void> { async function factoryResetSettings(): Promise<void> {
const clearAppDataMessage = const clearAppDataMessage = t.__(
"When the application restarts, it will be as if you have just downloaded Zulip app."; "When the application restarts, it will be as if you have just downloaded the Zulip app.",
);
const getAppPath = path.join(app.getPath("appData"), app.name); const getAppPath = path.join(app.getPath("appData"), app.name);
const {response} = await dialog.showMessageBox({ const {response} = await dialog.showMessageBox({
@@ -609,7 +610,7 @@ export function initGeneralSection({$root}: GeneralSectionProperties): void {
spellDiv.innerHTML += html` spellDiv.innerHTML += html`
<div class="setting-description">${t.__("Spellchecker Languages")}</div> <div class="setting-description">${t.__("Spellchecker Languages")}</div>
<div id="spellcheck-langs-value"> <div id="spellcheck-langs-value">
<input name="spellcheck" placeholder="Enter Languages" /> <input name="spellcheck" placeholder="${t.__("Enter Languages")}" />
</div> </div>
`.html; `.html;

View File

@@ -28,7 +28,7 @@ export function initNetworkSection({$root}: NetworkSectionProperties): void {
</div> </div>
<div class="manual-proxy-block"> <div class="manual-proxy-block">
<div class="setting-row" id="proxy-pac-option"> <div class="setting-row" id="proxy-pac-option">
<span class="setting-input-key">PAC ${t.__("script")}</span> <span class="setting-input-key">${t.__("PAC script")}</span>
<input <input
class="setting-input-value" class="setting-input-value"
placeholder="e.g. foobar.com/pacfile.js" placeholder="e.g. foobar.com/pacfile.js"

View File

@@ -23,7 +23,9 @@ export function initNewServerForm({
<input <input
class="setting-input-value" class="setting-input-value"
autofocus autofocus
placeholder="your-organization.zulipchat.com or zulip.your-organization.com" placeholder="${t.__(
"your-organization.zulipchat.com or zulip.your-organization.com",
)}"
/> />
</div> </div>
<div class="server-center"> <div class="server-center">
@@ -60,12 +62,12 @@ export function initNewServerForm({
)!; )!;
async function submitFormHandler(): Promise<void> { async function submitFormHandler(): Promise<void> {
$saveServerButton.textContent = "Connecting..."; $saveServerButton.textContent = t.__("Connecting…");
let serverConfig; let serverConfig;
try { try {
serverConfig = await DomainUtil.checkDomain($newServerUrl.value.trim()); serverConfig = await DomainUtil.checkDomain($newServerUrl.value.trim());
} catch (error: unknown) { } catch (error: unknown) {
$saveServerButton.textContent = "Connect"; $saveServerButton.textContent = t.__("Connect");
await dialog.showMessageBox({ await dialog.showMessageBox({
type: "error", type: "error",
message: message:

View File

@@ -7,6 +7,7 @@ import {BrowserWindow, Menu, Tray} from "@electron/remote";
import * as ConfigUtil from "../../common/config-util.ts"; import * as ConfigUtil from "../../common/config-util.ts";
import {publicPath} from "../../common/paths.ts"; import {publicPath} from "../../common/paths.ts";
import * as t from "../../common/translation-util.ts";
import type {RendererMessage} from "../../common/typed-ipc.ts"; import type {RendererMessage} from "../../common/typed-ipc.ts";
import type {ServerManagerView} from "./main.ts"; import type {ServerManagerView} from "./main.ts";
@@ -147,13 +148,13 @@ function sendAction<Channel extends keyof RendererMessage>(
const createTray = function (): void { const createTray = function (): void {
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ {
label: "Zulip", label: t.__("Zulip"),
click() { click() {
ipcRenderer.send("focus-app"); ipcRenderer.send("focus-app");
}, },
}, },
{ {
label: "Settings", label: t.__("Settings"),
click() { click() {
ipcRenderer.send("focus-app"); ipcRenderer.send("focus-app");
sendAction("open-settings"); sendAction("open-settings");
@@ -163,7 +164,7 @@ const createTray = function (): void {
type: "separator", type: "separator",
}, },
{ {
label: "Quit", label: t.__("Quit"),
click() { click() {
ipcRenderer.send("quit-app"); ipcRenderer.send("quit-app");
}, },
@@ -202,12 +203,17 @@ export function initializeTray(serverManagerView: ServerManagerView) {
if (argument === 0) { if (argument === 0) {
unread = argument; unread = argument;
tray.setImage(iconPath()); tray.setImage(iconPath());
tray.setToolTip("No unread messages"); tray.setToolTip(t.__("No unread messages"));
} else { } else {
unread = argument; unread = argument;
const image = renderNativeImage(argument); const image = renderNativeImage(argument);
tray.setImage(image); tray.setImage(image);
tray.setToolTip(`${argument} unread messages`); tray.setToolTip(
t.__mf(
"{number, plural, one {# unread message} other {# unread messages}}",
{number: `${argument}`},
),
);
} }
} }
}); });

View File

@@ -2,6 +2,7 @@ import * as backoff from "backoff";
import {html} from "../../../common/html.ts"; import {html} from "../../../common/html.ts";
import Logger from "../../../common/logger-util.ts"; import Logger from "../../../common/logger-util.ts";
import * as t from "../../../common/translation-util.ts";
import type WebView from "../components/webview.ts"; import type WebView from "../components/webview.ts";
import {ipcRenderer} from "../typed-ipc-renderer.ts"; import {ipcRenderer} from "../typed-ipc-renderer.ts";
@@ -55,8 +56,10 @@ export default class ReconnectUtil {
const errorMessageHolder = document.querySelector("#description"); const errorMessageHolder = document.querySelector("#description");
if (errorMessageHolder) { if (errorMessageHolder) {
errorMessageHolder.innerHTML = html` errorMessageHolder.innerHTML = html`
<div>Your internet connection doesn't seem to work properly!</div> <div>
<div>Verify that it works and then click try again.</div> ${t.__("Your internet connection doesn't seem to work properly!")}
</div>
<div>${t.__("Verify that it works and then click Reconnect.")}</div>
`.html; `.html;
} }

View File

@@ -8,7 +8,7 @@ const config: UserConfig = {
input: ["app/**/*.ts"], input: ["app/**/*.ts"],
keySeparator: false, keySeparator: false,
lexers: { lexers: {
ts: [{lexer: "JavascriptLexer", functions: ["t.__"]}], ts: [{lexer: "JavascriptLexer", functions: ["t.__", "t.__mf"]}],
}, },
locales: ["en"], locales: ["en"],
namespaceSeparator: false, namespaceSeparator: false,

View File

@@ -1,5 +1,6 @@
{ {
"A new update {{{version}}} has been downloaded.": "A new update {{{version}}} has been downloaded.", "A new update {{{version}}} has been downloaded.": "A new update {{{version}}} has been downloaded.",
"A new version {{{version}}} is available. Please update using your package manager.": "A new version {{{version}}} is available. Please update using your package manager.",
"A new version {{{version}}} of Zulip Desktop is available.": "A new version {{{version}}} of Zulip Desktop is available.", "A new version {{{version}}} of Zulip Desktop is available.": "A new version {{{version}}} of Zulip Desktop is available.",
"About": "About", "About": "About",
"About Zulip": "About Zulip", "About Zulip": "About Zulip",
@@ -29,16 +30,19 @@
"Change": "Change", "Change": "Change",
"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.", "Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
"Check for Updates": "Check for Updates", "Check for Updates": "Check for Updates",
"Click to show {{{fileName}}} in folder": "Click to show {{{fileName}}} in folder",
"Close": "Close", "Close": "Close",
"Connect": "Connect", "Connect": "Connect",
"Connect to another organization": "Connect to another organization", "Connect to another organization": "Connect to another organization",
"Connected organizations": "Connected organizations", "Connected organizations": "Connected organizations",
"Connecting…": "Connecting…",
"Copy": "Copy", "Copy": "Copy",
"Copy Email Address": "Copy Email Address", "Copy Email Address": "Copy Email Address",
"Copy Image": "Copy Image", "Copy Image": "Copy Image",
"Copy Image URL": "Copy Image URL", "Copy Image URL": "Copy Image URL",
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
"Copy Zulip URL": "Copy Zulip URL", "Copy Zulip URL": "Copy Zulip URL",
"Could not add {{{domain}}}. Please contact your system administrator.": "Could not add {{{domain}}}. Please contact your system administrator.",
"Create a new organization": "Create a new organization", "Create a new organization": "Create a new organization",
"Custom CSS file deleted": "Custom CSS file deleted", "Custom CSS file deleted": "Custom CSS file deleted",
"Cut": "Cut", "Cut": "Cut",
@@ -46,17 +50,22 @@
"Delete": "Delete", "Delete": "Delete",
"Desktop Notifications": "Desktop Notifications", "Desktop Notifications": "Desktop Notifications",
"Desktop Settings": "Desktop Settings", "Desktop Settings": "Desktop Settings",
"Disable Do Not Disturb": "Disable Do Not Disturb",
"Disconnect": "Disconnect", "Disconnect": "Disconnect",
"Disconnect organization": "Disconnect organization", "Disconnect organization": "Disconnect organization",
"Do Not Disturb": "Do Not Disturb", "Do Not Disturb": "Do Not Disturb",
"Download App Logs": "Download App Logs", "Download App Logs": "Download App Logs",
"Download Complete": "Download Complete",
"Download failed": "Download failed",
"Edit": "Edit", "Edit": "Edit",
"Edit Shortcuts": "Edit Shortcuts", "Edit Shortcuts": "Edit Shortcuts",
"Emoji & Symbols": "Emoji & Symbols", "Emoji & Symbols": "Emoji & Symbols",
"Enable Do Not Disturb": "Enable Do Not Disturb",
"Enable auto updates": "Enable auto updates", "Enable auto updates": "Enable auto updates",
"Enable error reporting (requires restart)": "Enable error reporting (requires restart)", "Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)", "Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
"Enter Full Screen": "Enter Full Screen", "Enter Full Screen": "Enter Full Screen",
"Enter Languages": "Enter Languages",
"Error saving new organization": "Error saving new organization", "Error saving new organization": "Error saving new organization",
"Error saving update notifications": "Error saving update notifications", "Error saving update notifications": "Error saving update notifications",
"Error: {{{error}}}\n\nThe latest version of Zulip Desktop is available at:\n{{{link}}}\nCurrent version: {{{version}}}": "Error: {{{error}}}\n\nThe latest version of Zulip Desktop is available at:\n{{{link}}}\nCurrent version: {{{version}}}", "Error: {{{error}}}\n\nThe latest version of Zulip Desktop is available at:\n{{{link}}}\nCurrent version: {{{version}}}": "Error: {{{error}}}\n\nThe latest version of Zulip Desktop is available at:\n{{{link}}}\nCurrent version: {{{version}}}",
@@ -98,15 +107,20 @@
"New servers added. Reload app now?": "New servers added. Reload app now?", "New servers added. Reload app now?": "New servers added. Reload app now?",
"No": "No", "No": "No",
"No Suggestion Found": "No Suggestion Found", "No Suggestion Found": "No Suggestion Found",
"No unread messages": "No unread messages",
"No updates available.": "No updates available.", "No updates available.": "No updates available.",
"Notification settings": "Notification settings", "Notification settings": "Notification settings",
"OK": "OK", "OK": "OK",
"OR": "OR", "OR": "OR",
"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.", "On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
"Opening {{{link}}}…": "Opening {{{link}}}…",
"Organization URL": "Organization URL", "Organization URL": "Organization URL",
"Organizations": "Organizations", "Organizations": "Organizations",
"PAC script": "PAC script",
"Paste": "Paste", "Paste": "Paste",
"Paste and Match Style": "Paste and Match Style", "Paste and Match Style": "Paste and Match Style",
"Please contact your system administrator.": "Please contact your system administrator.",
"Press {{{exitKey}}} to exit full screen": "Press {{{exitKey}}} to exit full screen",
"Proxy": "Proxy", "Proxy": "Proxy",
"Proxy bypass rules": "Proxy bypass rules", "Proxy bypass rules": "Proxy bypass rules",
"Proxy rules": "Proxy rules", "Proxy rules": "Proxy rules",
@@ -114,9 +128,11 @@
"Quit": "Quit", "Quit": "Quit",
"Quit Zulip": "Quit Zulip", "Quit Zulip": "Quit Zulip",
"Quit when the window is closed": "Quit when the window is closed", "Quit when the window is closed": "Quit when the window is closed",
"Redirecting": "Redirecting",
"Redo": "Redo", "Redo": "Redo",
"Release Notes": "Release Notes", "Release Notes": "Release Notes",
"Reload": "Reload", "Reload": "Reload",
"Removing {{{url}}} is a restricted operation.": "Removing {{{url}}} is a restricted operation.",
"Report an Issue": "Report an Issue", "Report an Issue": "Report an Issue",
"Reset App Settings": "Reset App Settings", "Reset App Settings": "Reset App Settings",
"Reset the application, thus deleting all the connected organizations and accounts.": "Reset the application, thus deleting all the connected organizations and accounts.", "Reset the application, thus deleting all the connected organizations and accounts.": "Reset the application, thus deleting all the connected organizations and accounts.",
@@ -125,6 +141,7 @@
"Select Download Location": "Select Download Location", "Select Download Location": "Select Download Location",
"Select file": "Select file", "Select file": "Select file",
"Services": "Services", "Services": "Services",
"Setting locked by system administrator.": "Setting locked by system administrator.",
"Settings": "Settings", "Settings": "Settings",
"Shortcuts": "Shortcuts", "Shortcuts": "Shortcuts",
"Show app icon in system tray": "Show app icon in system tray", "Show app icon in system tray": "Show app icon in system tray",
@@ -155,17 +172,24 @@
"Unknown error": "Unknown error", "Unknown error": "Unknown error",
"Upload": "Upload", "Upload": "Upload",
"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)", "Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
"Verify that it works and then click Reconnect.": "Verify that it works and then click Reconnect.",
"View": "View", "View": "View",
"View Shortcuts": "View Shortcuts", "View Shortcuts": "View Shortcuts",
"We encountered an error while saving the update notifications.": "We encountered an error while saving the update notifications.", "We encountered an error while saving the update notifications.": "We encountered an error while saving the update notifications.",
"When the application restarts, it will be as if you have just downloaded the Zulip app.": "When the application restarts, it will be as if you have just downloaded the Zulip app.",
"Window": "Window", "Window": "Window",
"Window Shortcuts": "Window Shortcuts", "Window Shortcuts": "Window Shortcuts",
"Yes": "Yes", "Yes": "Yes",
"You are running the latest version of Zulip Desktop.\nVersion: {{{version}}}": "You are running the latest version of Zulip Desktop.\nVersion: {{{version}}}", "You are running the latest version of Zulip Desktop.\nVersion: {{{version}}}": "You are running the latest version of Zulip Desktop.\nVersion: {{{version}}}",
"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.", "You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
"Your internet connection doesn't seem to work properly!": "Your internet connection doesn't seem to work properly!",
"Zoom In": "Zoom In", "Zoom In": "Zoom In",
"Zoom Out": "Zoom Out", "Zoom Out": "Zoom Out",
"Zulip": "Zulip",
"Zulip Update": "Zulip Update",
"keyboard shortcuts": "keyboard shortcuts", "keyboard shortcuts": "keyboard shortcuts",
"script": "script", "your-organization.zulipchat.com or zulip.your-organization.com": "your-organization.zulipchat.com or zulip.your-organization.com",
"{number, plural, one {# unread message} other {# unread messages}}": "{number, plural, one {# unread message} other {# unread messages}}",
"{number, plural, one {Could not add # organization} other {Could not add # organizations}}": "{number, plural, one {Could not add # organization} other {Could not add # organizations}}",
"{{{server}}} runs an outdated Zulip Server version {{{version}}}. It may not fully work in this app.": "{{{server}}} runs an outdated Zulip Server version {{{version}}}. It may not fully work in this app." "{{{server}}} runs an outdated Zulip Server version {{{version}}}. It may not fully work in this app.": "{{{server}}} runs an outdated Zulip Server version {{{version}}}. It may not fully work in this app."
} }