Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions main/common/localization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {settings} from './settings';

const simplifiedChinese: Record<string, string> = {
'Preferences…': '偏好设置…',
'About Kap': '关于 Kap',
'Open Video…': '打开视频…',
Videos: '视频',
'Export History': '导出历史',
'Send Feedback…': '发送反馈…',
Help: '帮助',
File: '文件',
Edit: '编辑',
Window: '窗口',
Plugins: '插件',
Microphone: '麦克风',
None: '无',
'System Default': '系统默认',
Stop: '停止',
Pause: '暂停',
Resume: '继续',
Preferences: '偏好设置',
Exports: '导出记录',
'Save Original…': '保存原文件…',
Discard: '丢弃',
Close: '关闭',
Minimize: '最小化',
Zoom: '缩放',
'Bring All to Front': '全部置于顶层',
'Open System Preferences': '打开系统设置',
Cancel: '取消',
Continue: '继续',
'Are you sure that you want to discard this recording?': '确定要丢弃此录制吗?',
'You will no longer be able to edit and export the original recording.': '丢弃后将无法再编辑或导出原始录制。',
'Kap cannot access the microphone.': 'Kap 无法访问麦克风。',
'Kap requires microphone access to be able to record audio. You can grant this in the System Preferences. Afterwards, launch Kap for the changes to take effect.': 'Kap 需要麦克风权限才能录制音频。请在系统设置中授予权限,然后重新启动 Kap。',
'Kap cannot record the screen.': 'Kap 无法录制屏幕。',
'Kap requires screen capture access to be able to record the screen. You can grant this in the System Preferences. Afterwards, launch Kap for the changes to take effect.': 'Kap 需要屏幕录制权限。请在系统设置中授予权限,然后重新启动 Kap。',
'Audio recording is enabled but Kap does not have access to the microphone. Continue without audio or grant Kap access to the microphone the System Preferences.': '已启用音频录制,但 Kap 没有麦克风权限。你可以继续进行无声录制,或前往系统设置授予麦克风权限。'
};

export const t = (text: string) => {
if (settings.get('language') !== 'zh-CN') {
return text;
}

const systemDefaultMatch = /^System Default(\s*\(.+\))?$/.exec(text);
if (systemDefaultMatch) {
return `系统默认${systemDefaultMatch[1] ?? ''}`;
}

return simplifiedChinese[text] ?? text;
};
9 changes: 9 additions & 0 deletions main/common/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const shortcutSchema = {
};

interface Settings {
language: 'en' | 'zh-CN';
kapturesDir: string;
allowAnalytics: boolean;
showCursor: boolean;
Expand All @@ -42,6 +43,14 @@ interface Settings {

export const settings = new Store<Settings>({
schema: {
language: {
type: 'string',
enum: [
'en',
'zh-CN'
],
default: 'en'
},
kapturesDir: {
type: 'string',
default: `${homedir()}/Movies/Kaptures`
Expand Down
8 changes: 4 additions & 4 deletions main/common/system-permissions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {systemPreferences, shell, dialog, app} from 'electron';
import {t} from './localization';
const {hasScreenCapturePermission, hasPromptedForPermission} = require('mac-screen-capture-permissions');
const {ensureDockIsShowing} = require('../utils/dock');

Expand All @@ -13,10 +14,10 @@ const promptSystemPreferences = (options: {message: string; detail: string; syst
await ensureDockIsShowing(async () => {
const {response} = await dialog.showMessageBox({
type: 'warning',
buttons: ['Open System Preferences', 'Cancel'],
buttons: [t('Open System Preferences'), t('Cancel')],
defaultId: 0,
message: options.message,
detail: options.detail,
message: t(options.message),
detail: t(options.detail),
cancelId: 1
});
isDialogShowing = false;
Expand Down Expand Up @@ -86,4 +87,3 @@ export const ensureScreenCapturePermissions = (fallback = screenCaptureFallback)
};

export const hasScreenCaptureAccess = () => hasScreenCapturePermission();

6 changes: 5 additions & 1 deletion main/menus/application.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {appMenu} from 'electron-util';
import {getAboutMenuItem, getExportHistoryMenuItem, getOpenFileMenuItem, getPreferencesMenuItem, getSendFeedbackMenuItem} from './common';
import {MenuItemId, MenuOptions} from './utils';
import {t} from '../common/localization';

const getAppMenuItem = () => {
const appMenuItem = appMenu([getPreferencesMenuItem()]);
Expand All @@ -15,6 +16,7 @@ export const defaultApplicationMenu = (): MenuOptions => [
getAppMenuItem(),
{
role: 'fileMenu',
label: t('File'),
id: MenuItemId.file,
submenu: [
getOpenFileMenuItem(),
Expand All @@ -28,10 +30,12 @@ export const defaultApplicationMenu = (): MenuOptions => [
},
{
role: 'editMenu',
label: t('Edit'),
id: MenuItemId.edit
},
{
role: 'windowMenu',
label: t('Window'),
id: MenuItemId.window,
submenu: [
{
Expand All @@ -54,7 +58,7 @@ export const defaultApplicationMenu = (): MenuOptions => [
},
{
id: MenuItemId.help,
label: 'Help',
label: t('Help'),
role: 'help',
submenu: [getSendFeedbackMenuItem()]
}
Expand Down
9 changes: 5 additions & 4 deletions main/menus/cog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {getAudioDevices, getDefaultInputDevice} from '../utils/devices';
import {settings} from '../common/settings';
import {defaultInputDeviceId} from '../common/constants';
import {hasMicrophoneAccess} from '../common/system-permissions';
import {t} from '../common/localization';

const getCogMenuTemplate = async (): Promise<MenuOptions> => [
getAboutMenuItem(),
Expand Down Expand Up @@ -48,7 +49,7 @@ const getPluginsItem = (): MenuOptions[number] => {

return {
id: MenuItemId.plugins,
label: 'Plugins',
label: t('Plugins'),
submenu: items,
visible: items.length > 0
};
Expand All @@ -67,18 +68,18 @@ const getMicrophoneItem = async (): Promise<MenuOptions[number]> => {

return {
id: MenuItemId.audioDevices,
label: 'Microphone',
label: t('Microphone'),
submenu: [
{
label: 'None',
label: t('None'),
type: 'checkbox',
checked: !isRecordAudioEnabled,
click: () => {
settings.set('recordAudio', false);
}
},
...[
{name: `System Default${currentDefaultDevice ? ` (${currentDefaultDevice.name})` : ''}`, id: defaultInputDeviceId},
{name: t(`System Default${currentDefaultDevice ? ` (${currentDefaultDevice.name})` : ''}`), id: defaultInputDeviceId},
...devices
].map(device => ({
label: device.name,
Expand Down
14 changes: 7 additions & 7 deletions main/menus/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import {supportedVideoExtensions} from '../common/constants';
import {getCurrentMenuItem, MenuItemId} from './utils';
import {openFiles} from '../utils/open-files';
import {windowManager} from '../windows/manager';
import {t} from '../common/localization';

export const getPreferencesMenuItem = () => ({
id: MenuItemId.preferences,
label: 'Preferences…',
label: t('Preferences…'),
accelerator: 'Command+,',
click: () => windowManager.preferences?.open()
});

export const getAboutMenuItem = () => ({
id: MenuItemId.about,
label: `About ${app.name}`,
label: t(`About ${app.name}`),
click: () => {
windowManager.cropper?.close();
app.focus();
Expand All @@ -26,7 +27,7 @@ export const getAboutMenuItem = () => ({

export const getOpenFileMenuItem = () => ({
id: MenuItemId.openVideo,
label: 'Open Video…',
label: t('Open Video…'),
accelerator: 'Command+O',
click: async () => {
windowManager.cropper?.close();
Expand All @@ -35,7 +36,7 @@ export const getOpenFileMenuItem = () => ({

app.focus();
const {canceled, filePaths} = await dialog.showOpenDialog({
filters: [{name: 'Videos', extensions: supportedVideoExtensions}],
filters: [{name: t('Videos'), extensions: supportedVideoExtensions}],
properties: ['openFile', 'multiSelections']
});

Expand All @@ -46,15 +47,15 @@ export const getOpenFileMenuItem = () => ({
});

export const getExportHistoryMenuItem = () => ({
label: 'Export History',
label: t('Export History'),
click: () => windowManager.exports?.open(),
enabled: getCurrentMenuItem(MenuItemId.exportHistory)?.enabled ?? false,
id: MenuItemId.exportHistory
});

export const getSendFeedbackMenuItem = () => ({
id: MenuItemId.sendFeedback,
label: 'Send Feedback…',
label: t('Send Feedback…'),
click() {
openNewGitHubIssue({
user: 'wulkano',
Expand Down Expand Up @@ -91,4 +92,3 @@ Workaround: A workaround for the issue if you've found on. (this will

<!-- If you have additional information, enter it below. -->
`;

7 changes: 4 additions & 3 deletions main/menus/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {MenuItemId, MenuOptions} from './utils';
import {pauseRecording, resumeRecording, stopRecording} from '../aperture';
import formatTime from '../utils/format-time';
import {getCurrentDurationStart, getOverallDuration} from '../utils/track-duration';
import {t} from '../common/localization';

const getDurationLabel = () => {
if (getCurrentDurationStart() <= 0) {
Expand All @@ -20,19 +21,19 @@ const getDurationMenuItem = () => ({

const getStopRecordingMenuItem = () => ({
id: MenuItemId.stopRecording,
label: 'Stop',
label: t('Stop'),
click: stopRecording
});

const getPauseRecordingMenuItem = () => ({
id: MenuItemId.pauseRecording,
label: 'Pause',
label: t('Pause'),
click: pauseRecording
});

const getResumeRecordingMenuItem = () => ({
id: MenuItemId.resumeRecording,
label: 'Resume',
label: t('Resume'),
click: resumeRecording
});

Expand Down
7 changes: 4 additions & 3 deletions main/windows/cropper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import delay from 'delay';

import {settings} from '../common/settings';
import {hasMicrophoneAccess, ensureMicrophonePermissions, openSystemPreferences, ensureScreenCapturePermissions} from '../common/system-permissions';
import {t} from '../common/localization';
import {loadRoute} from '../utils/routes';
import {MacWindow} from '../utils/windows';

Expand Down Expand Up @@ -100,10 +101,10 @@ const openCropperWindow = async () => {
const granted = await ensureMicrophonePermissions(async () => {
const {response} = await dialog.showMessageBox({
type: 'warning',
buttons: ['Open System Preferences', 'Continue'],
buttons: [t('Open System Preferences'), t('Continue')],
defaultId: 1,
message: 'Kap cannot access the microphone.',
detail: 'Audio recording is enabled but Kap does not have access to the microphone. Continue without audio or grant Kap access to the microphone the System Preferences.',
message: t('Kap cannot access the microphone.'),
detail: t('Audio recording is enabled but Kap does not have access to the microphone. Continue without audio or grant Kap access to the microphone the System Preferences.'),
cancelId: 2
});

Expand Down
11 changes: 6 additions & 5 deletions main/windows/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {is} from 'electron-util';
import fs from 'fs';
import {saveSnapshot} from '../utils/image-preview';
import {windowManager} from './manager';
import {t} from '../common/localization';

const pify = require('pify');

Expand Down Expand Up @@ -68,7 +69,7 @@ const open = async (video: Video) => {
submenu.splice(index + 1, 0, {
type: 'separator'
}, {
label: 'Save Original…',
label: t('Save Original…'),
id: MenuItemId.saveOriginal,
accelerator: 'Command+S',
click: async () => saveOriginal(video)
Expand All @@ -89,13 +90,13 @@ const open = async (video: Video) => {
const buttonIndex = dialog.showMessageBoxSync(editorWindow, {
type: 'question',
buttons: [
'Discard',
'Cancel'
t('Discard'),
t('Cancel')
],
defaultId: 0,
cancelId: 1,
message: 'Are you sure that you want to discard this recording?',
detail: 'You will no longer be able to edit and export the original recording.'
message: t('Are you sure that you want to discard this recording?'),
detail: t('You will no longer be able to edit and export the original recording.')
});

if (buttonIndex === 1) {
Expand Down
3 changes: 2 additions & 1 deletion main/windows/exports.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import KapWindow from './kap-window';
import {windowManager} from './manager';
import {t} from '../common/localization';

let exportsKapWindow: KapWindow | undefined;

Expand All @@ -8,7 +9,7 @@ const openExportsWindow = async () => {
exportsKapWindow.browserWindow.focus();
} else {
exportsKapWindow = new KapWindow({
title: 'Exports',
title: t('Exports'),
width: 320,
height: 360,
resizable: false,
Expand Down
3 changes: 2 additions & 1 deletion main/windows/preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {ipcMain as ipc} from 'electron-better-ipc';
import {loadRoute} from '../utils/routes';
import {track} from '../common/analytics';
import {windowManager} from './manager';
import {t} from '../common/localization';

let prefsWindow: BrowserWindow | undefined;

Expand All @@ -25,7 +26,7 @@ const openPrefsWindow = async (options?: PreferencesWindowOptions) => {
}

prefsWindow = new BrowserWindow({
title: 'Preferences',
title: t('Preferences'),
width: 480,
height: 480,
resizable: false,
Expand Down
9 changes: 5 additions & 4 deletions renderer/components/editor/conversion/title-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ import {ExportStatus} from '../../../common/types';
import {useMemo} from 'react';
import {template} from 'lodash';
import IconMenu from '../../icon-menu';
import {t} from '../../../utils/localization';

const TitleBar = ({conversion, cancel, copy, retry, showInFolder}: {conversion: UseConversionState; cancel: () => any; copy: () => any; retry: () => any; showInFolder: () => void}) => {
const {api} = require('electron-util');
const shouldClose = async () => {
if (conversion.status === ExportStatus.inProgress && !flags.get('backgroundEditorConversion')) {
await api.dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'info',
message: 'Your export will continue in the background. You can access it through the Export History window.',
buttons: ['Ok'],
message: t('Your export will continue in the background. You can access it through the Export History window.'),
buttons: [t('OK')],
defaultId: 0
});
flags.set('backgroundEditorConversion', true);
Expand All @@ -29,7 +30,7 @@ const TitleBar = ({conversion, cancel, copy, retry, showInFolder}: {conversion:

if (conversion?.canCopy) {
template.push({
label: 'Copy',
label: t('Copy'),
click: () => copy()
}, {
type: 'separator'
Expand All @@ -38,7 +39,7 @@ const TitleBar = ({conversion, cancel, copy, retry, showInFolder}: {conversion:

if (conversion?.status === ExportStatus.completed) {
template.push({
label: 'Show in Finder',
label: t('Show in Finder'),
click: () => showInFolder()
});
}
Expand Down
Loading