kopia lustrzana https://github.com/mifi/lossless-cut
implement language also in node land (menus, etc)
rodzic
6ab880509f
commit
5830a44d99
|
@ -58,7 +58,6 @@
|
|||
"file-url": "^3.0.0",
|
||||
"framer-motion": "^1.8.4",
|
||||
"hammerjs": "^2.0.8",
|
||||
"i18next": "^19.3.2",
|
||||
"i18next-scanner": "^3.0.0",
|
||||
"icon-gen": "^2.0.0",
|
||||
"lodash": "^4.17.19",
|
||||
|
@ -101,6 +100,7 @@
|
|||
"file-type": "^12.4.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"github-api": "^3.2.2",
|
||||
"i18next": "^19.3.2",
|
||||
"i18next-electron-language-detector": "^0.0.10",
|
||||
"i18next-fs-backend": "^1.0.1",
|
||||
"json5": "^2.1.3",
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
const electron = require('electron'); // eslint-disable-line
|
||||
const isDev = require('electron-is-dev');
|
||||
const unhandled = require('electron-unhandled');
|
||||
const i18n = require('i18next');
|
||||
|
||||
const menu = require('./menu');
|
||||
const configStore = require('./configStore');
|
||||
|
||||
const { checkNewVersion } = require('./update-checker');
|
||||
|
||||
require('./i18n');
|
||||
|
||||
const { app } = electron;
|
||||
const { BrowserWindow } = electron;
|
||||
|
||||
|
@ -23,6 +26,7 @@ let mainWindow;
|
|||
|
||||
let askBeforeClose = false;
|
||||
let rendererReady = false;
|
||||
let newVersion;
|
||||
|
||||
const openFiles = (paths) => mainWindow.webContents.send('file-opened', paths);
|
||||
const openFile = (path) => openFiles([path]);
|
||||
|
@ -67,8 +71,8 @@ function createWindow() {
|
|||
const choice = electron.dialog.showMessageBoxSync(mainWindow, {
|
||||
type: 'question',
|
||||
buttons: ['Yes', 'No'],
|
||||
title: 'Confirm quit',
|
||||
message: 'Are you sure you want to quit?',
|
||||
title: i18n.t('Confirm quit'),
|
||||
message: i18n.t('Are you sure you want to quit?'),
|
||||
});
|
||||
if (choice === 1) {
|
||||
e.preventDefault();
|
||||
|
@ -76,6 +80,10 @@ function createWindow() {
|
|||
});
|
||||
}
|
||||
|
||||
function updateMenu() {
|
||||
menu(app, mainWindow, newVersion);
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
|
@ -83,13 +91,12 @@ app.on('ready', async () => {
|
|||
await configStore.init();
|
||||
|
||||
createWindow();
|
||||
menu(app, mainWindow);
|
||||
updateMenu();
|
||||
|
||||
if (!process.windowsStore && !process.mas) {
|
||||
const newVersion = await checkNewVersion();
|
||||
if (newVersion) {
|
||||
menu(app, mainWindow, newVersion);
|
||||
}
|
||||
newVersion = await checkNewVersion();
|
||||
// newVersion = '1.2.3';
|
||||
if (newVersion) updateMenu();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -136,6 +143,10 @@ electron.ipcMain.on('setAskBeforeClose', (e, val) => {
|
|||
askBeforeClose = val;
|
||||
});
|
||||
|
||||
electron.ipcMain.on('setLanguage', (e, language) => {
|
||||
i18n.changeLanguage(language).then(() => updateMenu()).catch(console.error);
|
||||
});
|
||||
|
||||
function focusWindow() {
|
||||
try {
|
||||
app.focus({ steal: true });
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// const LanguageDetector = window.require('i18next-electron-language-detector');
|
||||
const isDev = require('electron-is-dev');
|
||||
|
||||
const { app } = require('electron');
|
||||
|
||||
const { join } = require('path');
|
||||
|
||||
const getLangPath = (subPath) => (isDev ? join('public', subPath) : join(app.getAppPath(), 'build', subPath));
|
||||
|
||||
// Weblate hardcodes different lang codes than electron
|
||||
// https://www.electronjs.org/docs/api/locales
|
||||
const mapLang = (lng) => ({
|
||||
nb: 'nb_NO',
|
||||
no: 'nb_NO',
|
||||
nn: 'nb_NO',
|
||||
zh: 'zh_Hans',
|
||||
'zh-CN': 'zh_Hans',
|
||||
'zh-TW': 'zh_Hans',
|
||||
fr: 'fr',
|
||||
'fr-CA': 'fr',
|
||||
'fr-CH': 'fr',
|
||||
'fr-FR': 'fr',
|
||||
it: 'it',
|
||||
'it-CH': 'it',
|
||||
'it-IT': 'it',
|
||||
'ru-RU': 'ru',
|
||||
}[lng] || lng);
|
||||
|
||||
const fallbackLng = 'en';
|
||||
|
||||
const commonI18nOptions = {
|
||||
fallbackLng,
|
||||
// debug: isDev,
|
||||
// saveMissing: isDev,
|
||||
// updateMissing: isDev,
|
||||
// saveMissingTo: 'all',
|
||||
|
||||
// TODO improve keys?
|
||||
// Maybe do something like this: https://stackoverflow.com/a/19405314/6519037
|
||||
// https://www.i18next.com/translation-function/context
|
||||
keySeparator: false,
|
||||
nsSeparator: false,
|
||||
pluralSeparator: false,
|
||||
contextSeparator: false,
|
||||
};
|
||||
|
||||
const loadPath = (lng, ns) => getLangPath(`locales/${mapLang(lng)}/${ns}.json`);
|
||||
const addPath = (lng, ns) => getLangPath(`locales/${mapLang(lng)}/${ns}.missing.json`);
|
||||
|
||||
module.exports = { fallbackLng, loadPath, addPath, commonI18nOptions };
|
|
@ -0,0 +1,27 @@
|
|||
const i18n = require('i18next');
|
||||
const Backend = require('i18next-fs-backend');
|
||||
|
||||
const { commonI18nOptions, loadPath, addPath } = require('./i18n-common');
|
||||
|
||||
// See also renderer
|
||||
|
||||
// https://github.com/i18next/i18next/issues/869
|
||||
i18n
|
||||
.use(Backend)
|
||||
// detect user language
|
||||
// learn more: https://github.com/i18next/i18next-browser-languageDetector
|
||||
// TODO disabled for now because translations need more reviewing https://github.com/mifi/lossless-cut/issues/346
|
||||
// .use(LanguageDetector)
|
||||
// init i18next
|
||||
// for all options read: https://www.i18next.com/overview/configuration-options
|
||||
// See also i18next-scanner.config.js
|
||||
.init({
|
||||
...commonI18nOptions,
|
||||
|
||||
backend: {
|
||||
loadPath,
|
||||
addPath,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = i18n;
|
|
@ -1,4 +1,5 @@
|
|||
const electron = require('electron'); // eslint-disable-line
|
||||
const i18n = require('i18next');
|
||||
|
||||
const { Menu } = electron;
|
||||
const { dialog } = electron;
|
||||
|
@ -10,10 +11,10 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
...(process.platform === 'darwin' ? [{ role: 'appMenu' }] : []),
|
||||
|
||||
{
|
||||
label: 'File',
|
||||
label: i18n.t('File'),
|
||||
submenu: [
|
||||
{
|
||||
label: 'Open',
|
||||
label: i18n.t('Open'),
|
||||
accelerator: 'CmdOrCtrl+O',
|
||||
async click() {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile', 'multiSelections'] });
|
||||
|
@ -22,7 +23,7 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
},
|
||||
},
|
||||
{
|
||||
label: 'Close',
|
||||
label: i18n.t('Close'),
|
||||
accelerator: 'CmdOrCtrl+W',
|
||||
async click() {
|
||||
mainWindow.webContents.send('close-file');
|
||||
|
@ -30,46 +31,46 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Load project (CSV)',
|
||||
label: i18n.t('Load project (CSV)'),
|
||||
click() {
|
||||
mainWindow.webContents.send('importEdlFile', 'csv');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Save project (CSV)',
|
||||
label: i18n.t('Save project (CSV)'),
|
||||
click() {
|
||||
mainWindow.webContents.send('exportEdlFile', 'csv');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Import project',
|
||||
label: i18n.t('Import project'),
|
||||
submenu: [
|
||||
{
|
||||
label: 'EDL (MPlayer)',
|
||||
label: i18n.t('EDL (MPlayer)'),
|
||||
click() {
|
||||
mainWindow.webContents.send('importEdlFile', 'mplayer');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Text chapters / YouTube',
|
||||
label: i18n.t('Text chapters / YouTube'),
|
||||
click() {
|
||||
mainWindow.webContents.send('importEdlFile', 'youtube');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DaVinci Resolve / Final Cut Pro XML',
|
||||
label: i18n.t('DaVinci Resolve / Final Cut Pro XML'),
|
||||
click() {
|
||||
mainWindow.webContents.send('importEdlFile', 'xmeml');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'CUE sheet file',
|
||||
label: i18n.t('CUE sheet file'),
|
||||
click() {
|
||||
mainWindow.webContents.send('importEdlFile', 'cue');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'PotPlayer Bookmarks (.pbf)',
|
||||
label: i18n.t('PotPlayer Bookmarks (.pbf)'),
|
||||
click() {
|
||||
mainWindow.webContents.send('importEdlFile', 'pbf');
|
||||
},
|
||||
|
@ -77,22 +78,22 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
],
|
||||
},
|
||||
{
|
||||
label: 'Export project',
|
||||
label: i18n.t('Export project'),
|
||||
submenu: [
|
||||
{
|
||||
label: 'Timestamps (CSV)',
|
||||
label: i18n.t('Timestamps (CSV)'),
|
||||
click() {
|
||||
mainWindow.webContents.send('exportEdlFile', 'csv-human');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Timestamps (TSV/TXT)',
|
||||
label: i18n.t('Timestamps (TSV/TXT)'),
|
||||
click() {
|
||||
mainWindow.webContents.send('exportEdlFile', 'tsv-human');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Start times as YouTube Chapters',
|
||||
label: i18n.t('Start times as YouTube Chapters'),
|
||||
click() {
|
||||
mainWindow.webContents.send('exportEdlYouTube');
|
||||
},
|
||||
|
@ -101,13 +102,13 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Convert to supported format',
|
||||
label: i18n.t('Convert to supported format'),
|
||||
click() {
|
||||
mainWindow.webContents.send('html5ify');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Fix incorrect duration',
|
||||
label: i18n.t('Fix incorrect duration'),
|
||||
click() {
|
||||
mainWindow.webContents.send('fixInvalidDuration');
|
||||
},
|
||||
|
@ -116,14 +117,14 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Settings',
|
||||
label: i18n.t('Settings'),
|
||||
click() {
|
||||
mainWindow.webContents.send('openSettings');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Exit',
|
||||
label: i18n.t('Exit'),
|
||||
click() {
|
||||
app.quit();
|
||||
},
|
||||
|
@ -132,7 +133,7 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
},
|
||||
|
||||
{
|
||||
label: 'Edit',
|
||||
label: i18n.t('Edit'),
|
||||
submenu: [
|
||||
{ role: 'undo' },
|
||||
{ role: 'redo' },
|
||||
|
@ -143,28 +144,28 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
{ role: 'selectall' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Segments',
|
||||
label: i18n.t('Segments'),
|
||||
submenu: [
|
||||
{
|
||||
label: 'Clear all segments',
|
||||
label: i18n.t('Clear all segments'),
|
||||
click() {
|
||||
mainWindow.webContents.send('clearSegments');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Reorder segments by start time',
|
||||
label: i18n.t('Reorder segments by start time'),
|
||||
click() {
|
||||
mainWindow.webContents.send('reorderSegsByStartTime');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Create num segments',
|
||||
label: i18n.t('Create num segments'),
|
||||
click() {
|
||||
mainWindow.webContents.send('createNumSegments');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Create fixed duration segments',
|
||||
label: i18n.t('Create fixed duration segments'),
|
||||
click() {
|
||||
mainWindow.webContents.send('createFixedDurationSegments');
|
||||
},
|
||||
|
@ -172,16 +173,16 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
],
|
||||
},
|
||||
{
|
||||
label: 'Tracks',
|
||||
label: i18n.t('Tracks'),
|
||||
submenu: [
|
||||
{
|
||||
label: 'Extract all tracks',
|
||||
label: i18n.t('Extract all tracks'),
|
||||
click() {
|
||||
mainWindow.webContents.send('extract-all-streams');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Edit tracks / metadata tags',
|
||||
label: i18n.t('Edit tracks / metadata tags'),
|
||||
click() {
|
||||
mainWindow.webContents.send('showStreamsSelector');
|
||||
},
|
||||
|
@ -192,7 +193,7 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
},
|
||||
|
||||
{
|
||||
label: 'View',
|
||||
label: i18n.t('View'),
|
||||
submenu: [
|
||||
{ role: 'togglefullscreen' },
|
||||
],
|
||||
|
@ -202,28 +203,28 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
...(process.platform === 'darwin'
|
||||
? [{ role: 'windowMenu' }]
|
||||
: [{
|
||||
label: 'Window',
|
||||
label: i18n.t('Window'),
|
||||
submenu: [{ role: 'minimize' }],
|
||||
}]
|
||||
),
|
||||
|
||||
{
|
||||
label: 'Tools',
|
||||
label: i18n.t('Tools'),
|
||||
submenu: [
|
||||
{
|
||||
label: 'Merge files',
|
||||
label: i18n.t('Merge files'),
|
||||
click() {
|
||||
mainWindow.webContents.send('show-merge-dialog', true);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Batch convert to supported format',
|
||||
label: i18n.t('Batch convert to supported format'),
|
||||
click() {
|
||||
mainWindow.webContents.send('batchConvertFriendlyFormat');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Set custom start offset/timecode',
|
||||
label: i18n.t('Set custom start offset/timecode'),
|
||||
click() {
|
||||
mainWindow.webContents.send('set-start-offset', true);
|
||||
},
|
||||
|
@ -235,23 +236,23 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Help and shortcuts',
|
||||
label: i18n.t('Help and shortcuts'),
|
||||
click() {
|
||||
mainWindow.webContents.send('openHelp');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'About',
|
||||
label: i18n.t('About'),
|
||||
click() {
|
||||
mainWindow.webContents.send('openAbout');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Learn More',
|
||||
label: i18n.t('Learn More'),
|
||||
click() { electron.shell.openExternal(homepage); },
|
||||
},
|
||||
{
|
||||
label: 'Report an error',
|
||||
label: i18n.t('Report an error'),
|
||||
click() { mainWindow.webContents.send('openSendReportDialog'); },
|
||||
},
|
||||
],
|
||||
|
@ -260,10 +261,10 @@ module.exports = (app, mainWindow, newVersion) => {
|
|||
|
||||
if (newVersion) {
|
||||
menu.push({
|
||||
label: 'New version!',
|
||||
label: i18n.t('New version!'),
|
||||
submenu: [
|
||||
{
|
||||
label: `Download ${newVersion}`,
|
||||
label: i18n.t('Download {{version}}', { version: newVersion }),
|
||||
click() { electron.shell.openExternal(releasesPage); },
|
||||
},
|
||||
],
|
||||
|
|
|
@ -157,7 +157,9 @@ const App = memo(() => {
|
|||
const outSegTemplateOrDefault = outSegTemplate || defaultOutSegTemplate;
|
||||
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(language || fallbackLng).catch(console.error);
|
||||
const l = language || fallbackLng;
|
||||
i18n.changeLanguage(l).catch(console.error);
|
||||
electron.ipcRenderer.send('setLanguage', l);
|
||||
}, [language]);
|
||||
|
||||
// Global state
|
||||
|
|
47
src/i18n.js
47
src/i18n.js
|
@ -1,36 +1,13 @@
|
|||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
// const LanguageDetector = window.require('i18next-electron-language-detector');
|
||||
const Backend = window.require('i18next-fs-backend');
|
||||
const isDev = window.require('electron-is-dev');
|
||||
|
||||
const { app } = window.require('electron').remote;
|
||||
const electron = window.require('electron'); // eslint-disable-line
|
||||
|
||||
const { join } = require('path');
|
||||
const { commonI18nOptions, fallbackLng, loadPath, addPath } = electron.remote.require('./i18n-common');
|
||||
|
||||
const getLangPath = (subPath) => (isDev ? join('public', subPath) : join(app.getAppPath(), 'build', subPath));
|
||||
|
||||
// Weblate hardcodes different lang codes than electron
|
||||
// https://www.electronjs.org/docs/api/locales
|
||||
const mapLang = (lng) => ({
|
||||
nb: 'nb_NO',
|
||||
no: 'nb_NO',
|
||||
nn: 'nb_NO',
|
||||
zh: 'zh_Hans',
|
||||
'zh-CN': 'zh_Hans',
|
||||
'zh-TW': 'zh_Hans',
|
||||
fr: 'fr',
|
||||
'fr-CA': 'fr',
|
||||
'fr-CH': 'fr',
|
||||
'fr-FR': 'fr',
|
||||
it: 'it',
|
||||
'it-CH': 'it',
|
||||
'it-IT': 'it',
|
||||
'ru-RU': 'ru',
|
||||
}[lng] || lng);
|
||||
|
||||
export const fallbackLng = 'en';
|
||||
export { fallbackLng };
|
||||
|
||||
// https://github.com/i18next/i18next/issues/869
|
||||
i18n
|
||||
|
@ -45,23 +22,11 @@ i18n
|
|||
// for all options read: https://www.i18next.com/overview/configuration-options
|
||||
// See also i18next-scanner.config.js
|
||||
.init({
|
||||
fallbackLng,
|
||||
// debug: isDev,
|
||||
// saveMissing: isDev,
|
||||
// updateMissing: isDev,
|
||||
// saveMissingTo: 'all',
|
||||
|
||||
// TODO improve keys?
|
||||
// Maybe do something like this: https://stackoverflow.com/a/19405314/6519037
|
||||
// https://www.i18next.com/translation-function/context
|
||||
keySeparator: false,
|
||||
nsSeparator: false,
|
||||
pluralSeparator: false,
|
||||
contextSeparator: false,
|
||||
...commonI18nOptions,
|
||||
|
||||
backend: {
|
||||
loadPath: (lng, ns) => getLangPath(`locales/${mapLang(lng)}/${ns}.json`),
|
||||
addPath: (lng, ns) => getLangPath(`locales/${mapLang(lng)}/${ns}.missing.json`),
|
||||
loadPath,
|
||||
addPath,
|
||||
},
|
||||
|
||||
interpolation: {
|
||||
|
|
Ładowanie…
Reference in New Issue