diff --git a/public/menu.js b/public/menu.js index 30ea5b7b..b5a3c3ae 100644 --- a/public/menu.js +++ b/public/menu.js @@ -1,6 +1,10 @@ const electron = require('electron'); const i18n = require('i18next'); +// menu-safe i18n.t: +// https://github.com/mifi/lossless-cut/issues/1456 +const t = (key) => i18n.t(key).replace(/&/g, '&&'); + const { Menu } = electron; const { homepage, getReleaseUrl, licensesPage } = require('./constants'); @@ -10,88 +14,88 @@ module.exports = (app, mainWindow, newVersion) => { ...(process.platform === 'darwin' ? [{ role: 'appMenu' }] : []), { - label: i18n.t('File'), + label: t('File'), submenu: [ { - label: i18n.t('Open'), + label: t('Open'), accelerator: 'CmdOrCtrl+O', async click() { mainWindow.webContents.send('openFilesDialog'); }, }, { - label: i18n.t('Close'), + label: t('Close'), accelerator: 'CmdOrCtrl+W', async click() { mainWindow.webContents.send('closeCurrentFile'); }, }, { - label: i18n.t('Close batch'), + label: t('Close batch'), async click() { mainWindow.webContents.send('closeBatchFiles'); }, }, { type: 'separator' }, { - label: i18n.t('Import project (LLC)...'), + label: t('Import project (LLC)...'), click() { mainWindow.webContents.send('importEdlFile', 'llc'); }, }, { - label: i18n.t('Export project (LLC)...'), + label: t('Export project (LLC)...'), click() { mainWindow.webContents.send('exportEdlFile', 'llc'); }, }, { - label: i18n.t('Import project'), + label: t('Import project'), submenu: [ { - label: i18n.t('Times in seconds (CSV)'), + label: t('Times in seconds (CSV)'), click() { mainWindow.webContents.send('importEdlFile', 'csv'); }, }, { - label: i18n.t('Frame numbers (CSV)'), + label: t('Frame numbers (CSV)'), click() { mainWindow.webContents.send('importEdlFile', 'csv-frames'); }, }, { - label: i18n.t('EDL (MPlayer)'), + label: t('EDL (MPlayer)'), click() { mainWindow.webContents.send('importEdlFile', 'mplayer'); }, }, { - label: i18n.t('Text chapters / YouTube'), + label: t('Text chapters / YouTube'), click() { mainWindow.webContents.send('importEdlFile', 'youtube'); }, }, { - label: i18n.t('DaVinci Resolve / Final Cut Pro XML'), + label: t('DaVinci Resolve / Final Cut Pro XML'), click() { mainWindow.webContents.send('importEdlFile', 'xmeml'); }, }, { - label: i18n.t('Final Cut Pro FCPX / FCPXML'), + label: t('Final Cut Pro FCPX / FCPXML'), click() { mainWindow.webContents.send('importEdlFile', 'fcpxml'); }, }, { - label: i18n.t('CUE sheet file'), + label: t('CUE sheet file'), click() { mainWindow.webContents.send('importEdlFile', 'cue'); }, }, { - label: i18n.t('PotPlayer Bookmarks (.pbf)'), + label: t('PotPlayer Bookmarks (.pbf)'), click() { mainWindow.webContents.send('importEdlFile', 'pbf'); }, @@ -99,34 +103,34 @@ module.exports = (app, mainWindow, newVersion) => { ], }, { - label: i18n.t('Export project'), + label: t('Export project'), submenu: [ { - label: i18n.t('Times in seconds (CSV)'), + label: t('Times in seconds (CSV)'), click() { mainWindow.webContents.send('exportEdlFile', 'csv'); }, }, { - label: i18n.t('Timestamps (CSV)'), + label: t('Timestamps (CSV)'), click() { mainWindow.webContents.send('exportEdlFile', 'csv-human'); }, }, { - label: i18n.t('Frame numbers (CSV)'), + label: t('Frame numbers (CSV)'), click() { mainWindow.webContents.send('exportEdlFile', 'csv-frames'); }, }, { - label: i18n.t('Timestamps (TSV/TXT)'), + label: t('Timestamps (TSV/TXT)'), click() { mainWindow.webContents.send('exportEdlFile', 'tsv-human'); }, }, { - label: i18n.t('Start times as YouTube Chapters'), + label: t('Start times as YouTube Chapters'), click() { mainWindow.webContents.send('exportEdlYouTube'); }, @@ -135,13 +139,13 @@ module.exports = (app, mainWindow, newVersion) => { }, { type: 'separator' }, { - label: i18n.t('Convert to supported format'), + label: t('Convert to supported format'), click() { mainWindow.webContents.send('html5ify'); }, }, { - label: i18n.t('Fix incorrect duration'), + label: t('Fix incorrect duration'), click() { mainWindow.webContents.send('fixInvalidDuration'); }, @@ -150,7 +154,7 @@ module.exports = (app, mainWindow, newVersion) => { { type: 'separator' }, { - label: i18n.t('Settings'), + label: t('Settings'), accelerator: 'CmdOrCtrl+,', click() { mainWindow.webContents.send('toggleSettings'); @@ -161,7 +165,7 @@ module.exports = (app, mainWindow, newVersion) => { ...(process.platform !== 'darwin' ? [ { type: 'separator' }, { - label: i18n.t('Exit'), + label: t('Exit'), click() { app.quit(); }, @@ -171,83 +175,83 @@ module.exports = (app, mainWindow, newVersion) => { }, { - label: i18n.t('Edit'), + label: t('Edit'), submenu: [ // https://github.com/mifi/lossless-cut/issues/610 // https://github.com/mifi/lossless-cut/issues/1183 - { role: 'undo', label: i18n.t('Undo') }, - { role: 'redo', label: i18n.t('Redo') }, + { role: 'undo', label: t('Undo') }, + { role: 'redo', label: t('Redo') }, { type: 'separator' }, - { role: 'cut', label: i18n.t('Cut') }, - { role: 'copy', label: i18n.t('Copy') }, - { role: 'paste', label: i18n.t('Paste') }, - { role: 'selectall', label: i18n.t('Select All') }, + { role: 'cut', label: t('Cut') }, + { role: 'copy', label: t('Copy') }, + { role: 'paste', label: t('Paste') }, + { role: 'selectall', label: t('Select All') }, { type: 'separator' }, { - label: i18n.t('Segments'), + label: t('Segments'), submenu: [ { - label: i18n.t('Clear all segments'), + label: t('Clear all segments'), click() { mainWindow.webContents.send('clearSegments'); }, }, { - label: i18n.t('Reorder segments by start time'), + label: t('Reorder segments by start time'), click() { mainWindow.webContents.send('reorderSegsByStartTime'); }, }, { - label: i18n.t('Create num segments'), + label: t('Create num segments'), click() { mainWindow.webContents.send('createNumSegments'); }, }, { - label: i18n.t('Create fixed duration segments'), + label: t('Create fixed duration segments'), click() { mainWindow.webContents.send('createFixedDurationSegments'); }, }, { - label: i18n.t('Create random segments'), + label: t('Create random segments'), click() { mainWindow.webContents.send('createRandomSegments'); }, }, { - label: i18n.t('Invert all segments on timeline'), + label: t('Invert all segments on timeline'), click() { mainWindow.webContents.send('invertAllSegments'); }, }, { - label: i18n.t('Fill gaps between segments'), + label: t('Fill gaps between segments'), click() { mainWindow.webContents.send('fillSegmentsGaps'); }, }, { - label: i18n.t('Combine overlapping segments'), + label: t('Combine overlapping segments'), click() { mainWindow.webContents.send('combineOverlappingSegments'); }, }, { - label: i18n.t('Shuffle segments order'), + label: t('Shuffle segments order'), click() { mainWindow.webContents.send('shuffleSegments'); }, }, { - label: i18n.t('Shift all segments on timeline'), + label: t('Shift all segments on timeline'), click() { mainWindow.webContents.send('shiftAllSegmentTimes'); }, }, { - label: i18n.t('Align segment times to keyframes'), + label: t('Align segment times to keyframes'), click() { mainWindow.webContents.send('alignSegmentTimesToKeyframes'); }, @@ -255,16 +259,16 @@ module.exports = (app, mainWindow, newVersion) => { ], }, { - label: i18n.t('Tracks'), + label: t('Tracks'), submenu: [ { - label: i18n.t('Extract all tracks'), + label: t('Extract all tracks'), click() { mainWindow.webContents.send('extractAllStreams'); }, }, { - label: i18n.t('Edit tracks / metadata tags'), + label: t('Edit tracks / metadata tags'), click() { mainWindow.webContents.send('showStreamsSelector'); }, @@ -275,105 +279,105 @@ module.exports = (app, mainWindow, newVersion) => { }, { - label: i18n.t('View'), + label: t('View'), submenu: [ - { role: 'togglefullscreen', label: i18n.t('Toggle Full Screen') }, + { role: 'togglefullscreen', label: t('Toggle Full Screen') }, ], }, // On Windows the windowMenu has a close Ctrl+W which clashes with File->Close shortcut ...(process.platform === 'darwin' - ? [{ role: 'windowMenu', label: i18n.t('Window') }] + ? [{ role: 'windowMenu', label: t('Window') }] : [{ - label: i18n.t('Window'), - submenu: [{ role: 'minimize', label: i18n.t('Minimize') }], + label: t('Window'), + submenu: [{ role: 'minimize', label: t('Minimize') }], }] ), { - label: i18n.t('Tools'), + label: t('Tools'), submenu: [ { - label: i18n.t('Merge/concatenate files'), + label: t('Merge/concatenate files'), click() { mainWindow.webContents.send('concatCurrentBatch'); }, }, { - label: i18n.t('Set custom start offset/timecode'), + label: t('Set custom start offset/timecode'), click() { mainWindow.webContents.send('askSetStartTimeOffset'); }, }, { - label: i18n.t('Detect black scenes'), + label: t('Detect black scenes'), click() { mainWindow.webContents.send('detectBlackScenes'); }, }, { - label: i18n.t('Detect silent scenes'), + label: t('Detect silent scenes'), click() { mainWindow.webContents.send('detectSilentScenes'); }, }, { - label: i18n.t('Detect scene changes'), + label: t('Detect scene changes'), click() { mainWindow.webContents.send('detectSceneChanges'); }, }, { - label: i18n.t('Create segments from keyframes'), + label: t('Create segments from keyframes'), click() { mainWindow.webContents.send('createSegmentsFromKeyframes'); }, }, { - label: i18n.t('Last ffmpeg commands'), + label: t('Last ffmpeg commands'), click() { mainWindow.webContents.send('toggleLastCommands'); }, }, { type: 'separator' }, - { role: 'toggleDevTools', label: i18n.t('Toggle Developer Tools') }, + { role: 'toggleDevTools', label: t('Toggle Developer Tools') }, ], }, { role: 'help', - label: i18n.t('Help'), + label: t('Help'), submenu: [ { - label: i18n.t('How to use'), + label: t('How to use'), click() { electron.shell.openExternal('https://mifi.no/losslesscut/usage'); }, }, { - label: i18n.t('FAQ'), + label: t('FAQ'), click() { electron.shell.openExternal('https://mifi.no/losslesscut/faq'); }, }, { - label: i18n.t('Troubleshooting'), + label: t('Troubleshooting'), click() { electron.shell.openExternal('https://mifi.no/losslesscut/troubleshooting'); }, }, { - label: i18n.t('Learn More'), + label: t('Learn More'), click() { electron.shell.openExternal(homepage); }, }, { - label: i18n.t('Licenses'), + label: t('Licenses'), click() { electron.shell.openExternal(licensesPage); }, }, { type: 'separator' }, { - label: i18n.t('Keyboard & mouse shortcuts'.replace(/&/g, '&&')), + label: t('Keyboard & mouse shortcuts'), click() { mainWindow.webContents.send('toggleKeyboardShortcuts'); }, }, { - label: i18n.t('Report an error'), + label: t('Report an error'), click() { mainWindow.webContents.send('openSendReportDialog'); }, }, { - label: i18n.t('Version'), + label: t('Version'), click() { mainWindow.webContents.send('openAbout'); }, }, ], @@ -382,10 +386,10 @@ module.exports = (app, mainWindow, newVersion) => { if (newVersion) { menu.push({ - label: i18n.t('New version!'), + label: t('New version!'), submenu: [ { - label: i18n.t('Download {{version}}', { version: newVersion }), + label: t('Download {{version}}', { version: newVersion }), click() { electron.shell.openExternal(getReleaseUrl(newVersion)); }, }, ],