implement language also in node land (menus, etc)

snyk-fix-1c00065eb8541253ee2ceeb462d92a2c
Mikael Finstad 2021-03-29 00:12:58 +07:00
rodzic 6ab880509f
commit 5830a44d99
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 25AB36E3E81CBC26
7 zmienionych plików z 146 dodań i 90 usunięć

Wyświetl plik

@ -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",

Wyświetl plik

@ -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 });

Wyświetl plik

@ -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 };

27
public/i18n.js 100644
Wyświetl plik

@ -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;

Wyświetl plik

@ -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); },
},
],

Wyświetl plik

@ -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

Wyświetl plik

@ -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: {