From 5ead846e65030c42d0cbb4ce343be7266d91db48 Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 10:54:44 -0500 Subject: [PATCH 01/10] Translation: desktop-electron --- packages/desktop-electron/menu.ts | 41 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 4c44c30ce2..82e7a54a4b 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -5,18 +5,21 @@ import { app, shell, } from 'electron'; +import { useTranslation } from 'react-i18next'; export function getMenu( isDev: boolean, createWindow: () => Promise, budgetId: string | undefined = undefined, ) { + const { t } = useTranslation('myNamespace'); + const template: MenuItemConstructorOptions[] = [ { - label: 'File', + label: t('File'), submenu: [ { - label: 'Load Backup...', + label: t('Load Backup...'), enabled: false, click(_item, focusedWindow) { if (focusedWindow && budgetId) { @@ -32,7 +35,7 @@ export function getMenu( type: 'separator', }, { - label: 'Manage files...', + label: t('Manage files...'), accelerator: 'CmdOrCtrl+O', click(_item, focusedWindow) { if (focusedWindow) { @@ -52,10 +55,10 @@ export function getMenu( ], }, { - label: 'Edit', + label: t('Edit'), submenu: [ { - label: 'Undo', + label: t('Undo'), enabled: false, accelerator: 'CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -68,7 +71,7 @@ export function getMenu( }, }, { - label: 'Redo', + label: t('Redo'), enabled: false, accelerator: 'Shift+CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -104,11 +107,11 @@ export function getMenu( ], }, { - label: 'View', + label: t('View'), submenu: [ isDev ? { - label: 'Reload', + label: t('Reload'), accelerator: 'CmdOrCtrl+R', click(_item, focusedWindow) { if (focusedWindow) focusedWindow.reload(); @@ -116,7 +119,7 @@ export function getMenu( } : { label: 'hidden', visible: false }, { - label: 'Toggle Developer Tools', + label: t('Toggle Developer Tools'), accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', click(_item, focusedWindow) { @@ -146,10 +149,10 @@ export function getMenu( ], }, { - label: 'Tools', + label: t('Tools'), submenu: [ { - label: 'Find schedules', + label: t('Find schedules'), enabled: false, click: function (_menuItem, focusedWin) { if (focusedWin) { @@ -173,7 +176,7 @@ export function getMenu( role: 'help', submenu: [ { - label: 'Keyboard Shortcuts Reference', + label: t('Keyboard Shortcuts Reference'), accelerator: '?', enabled: !!budgetId, click: function (_menuItem, focusedWin) { @@ -188,7 +191,7 @@ export function getMenu( type: 'separator', }, { - label: 'Learn More', + label: t('Learn More'), click() { shell.openExternal('https://actualbudget.org/docs/'); }, @@ -204,7 +207,7 @@ export function getMenu( submenu: [ isDev ? { - label: 'Screenshot', + label: t('Screenshot'), click() { ipcMain.emit('screenshot'); }, @@ -244,7 +247,7 @@ export function getMenu( type: 'separator', }, { - label: 'Speech', + label: t('Speech'), submenu: [ { role: 'startSpeaking', @@ -259,24 +262,24 @@ export function getMenu( const windowIdx = template.findIndex(t => t.role === 'window'); template[windowIdx].submenu = [ { - label: 'Close', + label: t('Close'), accelerator: 'CmdOrCtrl+W', role: 'close', }, { - label: 'Minimize', + label: t('Minimize'), accelerator: 'CmdOrCtrl+M', role: 'minimize', }, { - label: 'Zoom', + label: t('Zoom'), role: 'zoom', }, { type: 'separator', }, { - label: 'Bring All to Front', + label: t('Bring All to Front'), role: 'front', }, ]; From a803e1e60ad2a776dd5f3745fd67ca2393a5ee22 Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 10:58:29 -0500 Subject: [PATCH 02/10] add release note --- upcoming-release-notes/3267.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 upcoming-release-notes/3267.md diff --git a/upcoming-release-notes/3267.md b/upcoming-release-notes/3267.md new file mode 100644 index 0000000000..e221cb634f --- /dev/null +++ b/upcoming-release-notes/3267.md @@ -0,0 +1,6 @@ +--- +category: Enhancements +authors: [psybers] +--- + +Support translations in desktop-electron. From bec104d96aaaf1802d72deee2ae9f8ead06afa6a Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 10:59:23 -0500 Subject: [PATCH 03/10] fix lint --- packages/desktop-electron/menu.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 82e7a54a4b..092f2c1dd4 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -1,3 +1,5 @@ +import { useTranslation } from 'react-i18next'; + import { MenuItemConstructorOptions, Menu, @@ -5,7 +7,6 @@ import { app, shell, } from 'electron'; -import { useTranslation } from 'react-i18next'; export function getMenu( isDev: boolean, From dac930dcbe803f84310871e27cb60d08102db081 Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 11:08:35 -0500 Subject: [PATCH 04/10] switch to bare i18n function --- .../desktop-client/i18next-parser.config.js | 2 +- packages/desktop-electron/i18n.ts | 24 +++++++++++ packages/desktop-electron/menu.ts | 42 +++++++++---------- packages/desktop-electron/package.json | 2 + yarn.lock | 11 +++++ 5 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 packages/desktop-electron/i18n.ts diff --git a/packages/desktop-client/i18next-parser.config.js b/packages/desktop-client/i18next-parser.config.js index eaab75dae0..fc636053ae 100644 --- a/packages/desktop-client/i18next-parser.config.js +++ b/packages/desktop-client/i18next-parser.config.js @@ -1,5 +1,5 @@ module.exports = { - input: ['src/**/*.{js,jsx,ts,tsx}', '../loot-core/src/**/*.{js,jsx,ts,tsx}'], + input: ['src/**/*.{js,jsx,ts,tsx}', '../loot-core/src/**/*.{js,jsx,ts,tsx}', '../desktop-electron/*.{js,ts}'], output: 'src/locale/$LOCALE.json', locales: ['en'], sort: true, diff --git a/packages/desktop-electron/i18n.ts b/packages/desktop-electron/i18n.ts new file mode 100644 index 0000000000..26e82fb9be --- /dev/null +++ b/packages/desktop-electron/i18n.ts @@ -0,0 +1,24 @@ +import i18n from 'i18next'; +import resourcesToBackend from 'i18next-resources-to-backend'; + +const loadLanguage = (language: string) => { + return import(`../desktop-client/locale/${language}.json`); +}; + +i18n + .use(resourcesToBackend(loadLanguage)) + .init({ + // While we mark all strings for translations, one can test + // it by setting the language in localStorage to their choice. + // Set this to 'cimode' to see the exact keys without interpolation. + lng: localStorage.getItem('language') || 'en', + + // allow keys to be phrases having `:`, `.` + nsSeparator: false, + keySeparator: false, + // do not load a fallback + fallbackLng: false, + interpolation: { + escapeValue: false, + }, + }); diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 092f2c1dd4..97a426c2b6 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -1,4 +1,4 @@ -import { useTranslation } from 'react-i18next'; +import i18n from 'i18next'; import { MenuItemConstructorOptions, @@ -13,14 +13,12 @@ export function getMenu( createWindow: () => Promise, budgetId: string | undefined = undefined, ) { - const { t } = useTranslation('myNamespace'); - const template: MenuItemConstructorOptions[] = [ { - label: t('File'), + label: i18n.t('File'), submenu: [ { - label: t('Load Backup...'), + label: i18n.t('Load Backup...'), enabled: false, click(_item, focusedWindow) { if (focusedWindow && budgetId) { @@ -36,7 +34,7 @@ export function getMenu( type: 'separator', }, { - label: t('Manage files...'), + label: i18n.t('Manage files...'), accelerator: 'CmdOrCtrl+O', click(_item, focusedWindow) { if (focusedWindow) { @@ -56,10 +54,10 @@ export function getMenu( ], }, { - label: t('Edit'), + label: i18n.t('Edit'), submenu: [ { - label: t('Undo'), + label: i18n.t('Undo'), enabled: false, accelerator: 'CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -72,7 +70,7 @@ export function getMenu( }, }, { - label: t('Redo'), + label: i18n.t('Redo'), enabled: false, accelerator: 'Shift+CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -108,11 +106,11 @@ export function getMenu( ], }, { - label: t('View'), + label: i18n.t('View'), submenu: [ isDev ? { - label: t('Reload'), + label: i18n.t('Reload'), accelerator: 'CmdOrCtrl+R', click(_item, focusedWindow) { if (focusedWindow) focusedWindow.reload(); @@ -120,7 +118,7 @@ export function getMenu( } : { label: 'hidden', visible: false }, { - label: t('Toggle Developer Tools'), + label: i18n.t('Toggle Developer Tools'), accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', click(_item, focusedWindow) { @@ -150,10 +148,10 @@ export function getMenu( ], }, { - label: t('Tools'), + label: i18n.t('Tools'), submenu: [ { - label: t('Find schedules'), + label: i18n.t('Find schedules'), enabled: false, click: function (_menuItem, focusedWin) { if (focusedWin) { @@ -177,7 +175,7 @@ export function getMenu( role: 'help', submenu: [ { - label: t('Keyboard Shortcuts Reference'), + label: i18n.t('Keyboard Shortcuts Reference'), accelerator: '?', enabled: !!budgetId, click: function (_menuItem, focusedWin) { @@ -192,7 +190,7 @@ export function getMenu( type: 'separator', }, { - label: t('Learn More'), + label: i18n.t('Learn More'), click() { shell.openExternal('https://actualbudget.org/docs/'); }, @@ -208,7 +206,7 @@ export function getMenu( submenu: [ isDev ? { - label: t('Screenshot'), + label: i18n.t('Screenshot'), click() { ipcMain.emit('screenshot'); }, @@ -248,7 +246,7 @@ export function getMenu( type: 'separator', }, { - label: t('Speech'), + label: i18n.t('Speech'), submenu: [ { role: 'startSpeaking', @@ -263,24 +261,24 @@ export function getMenu( const windowIdx = template.findIndex(t => t.role === 'window'); template[windowIdx].submenu = [ { - label: t('Close'), + label: i18n.t('Close'), accelerator: 'CmdOrCtrl+W', role: 'close', }, { - label: t('Minimize'), + label: i18n.t('Minimize'), accelerator: 'CmdOrCtrl+M', role: 'minimize', }, { - label: t('Zoom'), + label: i18n.t('Zoom'), role: 'zoom', }, { type: 'separator', }, { - label: t('Bring All to Front'), + label: i18n.t('Bring All to Front'), role: 'front', }, ]; diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index 327f1990f8..c9fd34732a 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -65,6 +65,8 @@ "dependencies": { "electron-is-dev": "2.0.0", "electron-log": "4.4.8", + "i18next": "^23.12.6", + "i18next-resources-to-backend": "^1.2.1", "loot-core": "*", "node-fetch": "^2.7.0", "promise-retry": "^2.0.1" diff --git a/yarn.lock b/yarn.lock index 947d418bec..2414e62773 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8669,6 +8669,8 @@ __metadata: electron-builder: "npm:24.13.3" electron-is-dev: "npm:2.0.0" electron-log: "npm:4.4.8" + i18next: "npm:^23.12.6" + i18next-resources-to-backend: "npm:^1.2.1" loot-core: "npm:*" node-fetch: "npm:^2.7.0" promise-retry: "npm:^2.0.1" @@ -11316,6 +11318,15 @@ __metadata: languageName: node linkType: hard +"i18next@npm:^23.12.6": + version: 23.12.6 + resolution: "i18next@npm:23.12.6" + dependencies: + "@babel/runtime": "npm:^7.23.2" + checksum: 10/6bb7faa2f1645dd7fc8d04c485707221edcf14911756d94faa8f5b7ffb5507995e00dca01f3c516ced9e986effb0073b06e9c55c2d762d400841cc557002abb1 + languageName: node + linkType: hard + "iconv-corefoundation@npm:^1.1.7": version: 1.1.7 resolution: "iconv-corefoundation@npm:1.1.7" From 4861a73fa5f335c39e411a878c6c3b8fbdb0b9bb Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 11:11:09 -0500 Subject: [PATCH 05/10] fix linter --- .../desktop-client/i18next-parser.config.js | 6 +++- packages/desktop-electron/i18n.ts | 30 +++++++++---------- packages/desktop-electron/menu.ts | 3 +- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/desktop-client/i18next-parser.config.js b/packages/desktop-client/i18next-parser.config.js index fc636053ae..55acd37d9f 100644 --- a/packages/desktop-client/i18next-parser.config.js +++ b/packages/desktop-client/i18next-parser.config.js @@ -1,5 +1,9 @@ module.exports = { - input: ['src/**/*.{js,jsx,ts,tsx}', '../loot-core/src/**/*.{js,jsx,ts,tsx}', '../desktop-electron/*.{js,ts}'], + input: [ + 'src/**/*.{js,jsx,ts,tsx}', + '../loot-core/src/**/*.{js,jsx,ts,tsx}', + '../desktop-electron/*.{js,ts}', + ], output: 'src/locale/$LOCALE.json', locales: ['en'], sort: true, diff --git a/packages/desktop-electron/i18n.ts b/packages/desktop-electron/i18n.ts index 26e82fb9be..906f6e7368 100644 --- a/packages/desktop-electron/i18n.ts +++ b/packages/desktop-electron/i18n.ts @@ -5,20 +5,18 @@ const loadLanguage = (language: string) => { return import(`../desktop-client/locale/${language}.json`); }; -i18n - .use(resourcesToBackend(loadLanguage)) - .init({ - // While we mark all strings for translations, one can test - // it by setting the language in localStorage to their choice. - // Set this to 'cimode' to see the exact keys without interpolation. - lng: localStorage.getItem('language') || 'en', +i18n.use(resourcesToBackend(loadLanguage)).init({ + // While we mark all strings for translations, one can test + // it by setting the language in localStorage to their choice. + // Set this to 'cimode' to see the exact keys without interpolation. + lng: localStorage.getItem('language') || 'en', - // allow keys to be phrases having `:`, `.` - nsSeparator: false, - keySeparator: false, - // do not load a fallback - fallbackLng: false, - interpolation: { - escapeValue: false, - }, - }); + // allow keys to be phrases having `:`, `.` + nsSeparator: false, + keySeparator: false, + // do not load a fallback + fallbackLng: false, + interpolation: { + escapeValue: false, + }, +}); diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 97a426c2b6..2c810505c1 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -1,5 +1,3 @@ -import i18n from 'i18next'; - import { MenuItemConstructorOptions, Menu, @@ -7,6 +5,7 @@ import { app, shell, } from 'electron'; +import i18n from 'i18next'; export function getMenu( isDev: boolean, From 0f6c011eb1dbe2e55b453e65c50d0c3fc29136bc Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 11:26:32 -0500 Subject: [PATCH 06/10] i18n -> i18next --- packages/desktop-electron/menu.ts | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 2c810505c1..99e39d4a34 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -5,7 +5,7 @@ import { app, shell, } from 'electron'; -import i18n from 'i18next'; +import i18next from 'i18next'; export function getMenu( isDev: boolean, @@ -14,10 +14,10 @@ export function getMenu( ) { const template: MenuItemConstructorOptions[] = [ { - label: i18n.t('File'), + label: i18next.t('File'), submenu: [ { - label: i18n.t('Load Backup...'), + label: i18next.t('Load Backup...'), enabled: false, click(_item, focusedWindow) { if (focusedWindow && budgetId) { @@ -33,7 +33,7 @@ export function getMenu( type: 'separator', }, { - label: i18n.t('Manage files...'), + label: i18next.t('Manage files...'), accelerator: 'CmdOrCtrl+O', click(_item, focusedWindow) { if (focusedWindow) { @@ -53,10 +53,10 @@ export function getMenu( ], }, { - label: i18n.t('Edit'), + label: i18next.t('Edit'), submenu: [ { - label: i18n.t('Undo'), + label: i18next.t('Undo'), enabled: false, accelerator: 'CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -69,7 +69,7 @@ export function getMenu( }, }, { - label: i18n.t('Redo'), + label: i18next.t('Redo'), enabled: false, accelerator: 'Shift+CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -105,11 +105,11 @@ export function getMenu( ], }, { - label: i18n.t('View'), + label: i18next.t('View'), submenu: [ isDev ? { - label: i18n.t('Reload'), + label: i18next.t('Reload'), accelerator: 'CmdOrCtrl+R', click(_item, focusedWindow) { if (focusedWindow) focusedWindow.reload(); @@ -117,7 +117,7 @@ export function getMenu( } : { label: 'hidden', visible: false }, { - label: i18n.t('Toggle Developer Tools'), + label: i18next.t('Toggle Developer Tools'), accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', click(_item, focusedWindow) { @@ -147,10 +147,10 @@ export function getMenu( ], }, { - label: i18n.t('Tools'), + label: i18next.t('Tools'), submenu: [ { - label: i18n.t('Find schedules'), + label: i18next.t('Find schedules'), enabled: false, click: function (_menuItem, focusedWin) { if (focusedWin) { @@ -174,7 +174,7 @@ export function getMenu( role: 'help', submenu: [ { - label: i18n.t('Keyboard Shortcuts Reference'), + label: i18next.t('Keyboard Shortcuts Reference'), accelerator: '?', enabled: !!budgetId, click: function (_menuItem, focusedWin) { @@ -189,7 +189,7 @@ export function getMenu( type: 'separator', }, { - label: i18n.t('Learn More'), + label: i18next.t('Learn More'), click() { shell.openExternal('https://actualbudget.org/docs/'); }, @@ -205,7 +205,7 @@ export function getMenu( submenu: [ isDev ? { - label: i18n.t('Screenshot'), + label: i18next.t('Screenshot'), click() { ipcMain.emit('screenshot'); }, @@ -245,7 +245,7 @@ export function getMenu( type: 'separator', }, { - label: i18n.t('Speech'), + label: i18next.t('Speech'), submenu: [ { role: 'startSpeaking', @@ -260,24 +260,24 @@ export function getMenu( const windowIdx = template.findIndex(t => t.role === 'window'); template[windowIdx].submenu = [ { - label: i18n.t('Close'), + label: i18next.t('Close'), accelerator: 'CmdOrCtrl+W', role: 'close', }, { - label: i18n.t('Minimize'), + label: i18next.t('Minimize'), accelerator: 'CmdOrCtrl+M', role: 'minimize', }, { - label: i18n.t('Zoom'), + label: i18next.t('Zoom'), role: 'zoom', }, { type: 'separator', }, { - label: i18n.t('Bring All to Front'), + label: i18next.t('Bring All to Front'), role: 'front', }, ]; From 03940076a84e87afe4c82147ea5fc02afe06b41f Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 11:36:16 -0500 Subject: [PATCH 07/10] fix error --- packages/desktop-electron/menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 99e39d4a34..4000f274d2 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -239,7 +239,7 @@ export function getMenu( ], }); // Edit menu. - const editIdx = template.findIndex(t => t.label === 'Edit'); + const editIdx = template.findIndex(t => t.label === i18next.t('Edit')); (template[editIdx].submenu as MenuItemConstructorOptions[]).push( { type: 'separator', From 4fd11dd9dd4e68c5542c907689078f6c8dcb261c Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 13:51:42 -0500 Subject: [PATCH 08/10] try another approach --- packages/desktop-electron/i18n.ts | 43 +++++++++++++++----------- packages/desktop-electron/index.ts | 17 +++++++--- packages/desktop-electron/menu.ts | 42 ++++++++++++------------- packages/desktop-electron/package.json | 3 ++ packages/desktop-electron/preload.ts | 3 ++ yarn.lock | 20 ++++++++++++ 6 files changed, 84 insertions(+), 44 deletions(-) diff --git a/packages/desktop-electron/i18n.ts b/packages/desktop-electron/i18n.ts index 906f6e7368..55af735f03 100644 --- a/packages/desktop-electron/i18n.ts +++ b/packages/desktop-electron/i18n.ts @@ -1,22 +1,29 @@ import i18n from 'i18next'; -import resourcesToBackend from 'i18next-resources-to-backend'; +import backend from "i18next-electron-fs-backend"; -const loadLanguage = (language: string) => { - return import(`../desktop-client/locale/${language}.json`); -}; +i18n + .use(backend) + .init({ + backend: { + loadPath: "../desktop-client/locale/{{lng}}/{{ns}}.json", + addPath: "../desktop-client/locale/{{lng}}/{{ns}}.missing.json", + contextBridgeApiKey: "Actual", + }, -i18n.use(resourcesToBackend(loadLanguage)).init({ - // While we mark all strings for translations, one can test - // it by setting the language in localStorage to their choice. - // Set this to 'cimode' to see the exact keys without interpolation. - lng: localStorage.getItem('language') || 'en', + // While we mark all strings for translations, one can test + // it by setting the language in localStorage to their choice. + // Set this to 'cimode' to see the exact keys without interpolation. + lng: 'en', // FIXME localStorage undefined + // lng: localStorage.getItem('language') || 'en', - // allow keys to be phrases having `:`, `.` - nsSeparator: false, - keySeparator: false, - // do not load a fallback - fallbackLng: false, - interpolation: { - escapeValue: false, - }, -}); + // allow keys to be phrases having `:`, `.` + nsSeparator: false, + keySeparator: false, + // do not load a fallback + fallbackLng: false, + interpolation: { + escapeValue: false, + }, + }); + +export default i18n; diff --git a/packages/desktop-electron/index.ts b/packages/desktop-electron/index.ts index d5767d200f..017b7528f0 100644 --- a/packages/desktop-electron/index.ts +++ b/packages/desktop-electron/index.ts @@ -26,6 +26,9 @@ import { get as getWindowState, listen as listenToWindowState, } from './window-state'; +const backend = require("i18next-electron-fs-backend"); + +import i18n from './i18n'; import './security'; @@ -113,6 +116,8 @@ async function createWindow() { }); win.setBackgroundColor('#E8ECF0'); + backend.mainBindings(ipcMain, win, fs); + if (isDev) { win.webContents.openDevTools(); } @@ -186,23 +191,23 @@ function isExternalUrl(url: string) { function updateMenu(budgetId?: string) { const isBudgetOpen = !!budgetId; const menu = getMenu(isDev, createWindow, budgetId); - const file = menu.items.filter(item => item.label === 'File')[0]; + const file = menu.items.filter(item => item.label === i18n.t('File'))[0]; const fileItems = file.submenu?.items || []; fileItems - .filter(item => item.label === 'Load Backup...') + .filter(item => item.label === i18n.t('Load Backup...')) .forEach(item => { item.enabled = isBudgetOpen; }); - const tools = menu.items.filter(item => item.label === 'Tools')[0]; + const tools = menu.items.filter(item => item.label === i18n.t('Tools'))[0]; tools.submenu?.items.forEach(item => { item.enabled = isBudgetOpen; }); - const edit = menu.items.filter(item => item.label === 'Edit')[0]; + const edit = menu.items.filter(item => item.label === i18n.t('Edit'))[0]; const editItems = edit.submenu?.items || []; editItems - .filter(item => item.label === 'Undo' || item.label === 'Redo') + .filter(item => item.label === i18n.t('Undo') || item.label === i18n.t('Redo')) .map(item => (item.enabled = isBudgetOpen)); if (process.platform === 'win32') { @@ -267,6 +272,8 @@ app.on('window-all-closed', () => { // On macOS, closing all windows shouldn't exit the process if (process.platform !== 'darwin') { app.quit(); + } else { + backend.clearMainBindings(ipcMain); } }); diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 4000f274d2..97af4d155b 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -5,7 +5,7 @@ import { app, shell, } from 'electron'; -import i18next from 'i18next'; +import i18n from './i18n'; export function getMenu( isDev: boolean, @@ -14,10 +14,10 @@ export function getMenu( ) { const template: MenuItemConstructorOptions[] = [ { - label: i18next.t('File'), + label: i18n.t('File'), submenu: [ { - label: i18next.t('Load Backup...'), + label: i18n.t('Load Backup...'), enabled: false, click(_item, focusedWindow) { if (focusedWindow && budgetId) { @@ -33,7 +33,7 @@ export function getMenu( type: 'separator', }, { - label: i18next.t('Manage files...'), + label: i18n.t('Manage files...'), accelerator: 'CmdOrCtrl+O', click(_item, focusedWindow) { if (focusedWindow) { @@ -53,10 +53,10 @@ export function getMenu( ], }, { - label: i18next.t('Edit'), + label: i18n.t('Edit'), submenu: [ { - label: i18next.t('Undo'), + label: i18n.t('Undo'), enabled: false, accelerator: 'CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -69,7 +69,7 @@ export function getMenu( }, }, { - label: i18next.t('Redo'), + label: i18n.t('Redo'), enabled: false, accelerator: 'Shift+CmdOrCtrl+Z', click: function (_menuItem, focusedWin) { @@ -105,11 +105,11 @@ export function getMenu( ], }, { - label: i18next.t('View'), + label: i18n.t('View'), submenu: [ isDev ? { - label: i18next.t('Reload'), + label: i18n.t('Reload'), accelerator: 'CmdOrCtrl+R', click(_item, focusedWindow) { if (focusedWindow) focusedWindow.reload(); @@ -117,7 +117,7 @@ export function getMenu( } : { label: 'hidden', visible: false }, { - label: i18next.t('Toggle Developer Tools'), + label: i18n.t('Toggle Developer Tools'), accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', click(_item, focusedWindow) { @@ -147,10 +147,10 @@ export function getMenu( ], }, { - label: i18next.t('Tools'), + label: i18n.t('Tools'), submenu: [ { - label: i18next.t('Find schedules'), + label: i18n.t('Find schedules'), enabled: false, click: function (_menuItem, focusedWin) { if (focusedWin) { @@ -174,7 +174,7 @@ export function getMenu( role: 'help', submenu: [ { - label: i18next.t('Keyboard Shortcuts Reference'), + label: i18n.t('Keyboard Shortcuts Reference'), accelerator: '?', enabled: !!budgetId, click: function (_menuItem, focusedWin) { @@ -189,7 +189,7 @@ export function getMenu( type: 'separator', }, { - label: i18next.t('Learn More'), + label: i18n.t('Learn More'), click() { shell.openExternal('https://actualbudget.org/docs/'); }, @@ -205,7 +205,7 @@ export function getMenu( submenu: [ isDev ? { - label: i18next.t('Screenshot'), + label: i18n.t('Screenshot'), click() { ipcMain.emit('screenshot'); }, @@ -239,13 +239,13 @@ export function getMenu( ], }); // Edit menu. - const editIdx = template.findIndex(t => t.label === i18next.t('Edit')); + const editIdx = template.findIndex(t => t.label === i18n.t('Edit')); (template[editIdx].submenu as MenuItemConstructorOptions[]).push( { type: 'separator', }, { - label: i18next.t('Speech'), + label: i18n.t('Speech'), submenu: [ { role: 'startSpeaking', @@ -260,24 +260,24 @@ export function getMenu( const windowIdx = template.findIndex(t => t.role === 'window'); template[windowIdx].submenu = [ { - label: i18next.t('Close'), + label: i18n.t('Close'), accelerator: 'CmdOrCtrl+W', role: 'close', }, { - label: i18next.t('Minimize'), + label: i18n.t('Minimize'), accelerator: 'CmdOrCtrl+M', role: 'minimize', }, { - label: i18next.t('Zoom'), + label: i18n.t('Zoom'), role: 'zoom', }, { type: 'separator', }, { - label: i18next.t('Bring All to Front'), + label: i18n.t('Bring All to Front'), role: 'front', }, ]; diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index c9fd34732a..8d923844ad 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -66,7 +66,9 @@ "electron-is-dev": "2.0.0", "electron-log": "4.4.8", "i18next": "^23.12.6", + "i18next-electron-fs-backend": "^3.0.2", "i18next-resources-to-backend": "^1.2.1", + "lodash": "^4.17.21", "loot-core": "*", "node-fetch": "^2.7.0", "promise-retry": "^2.0.1" @@ -75,6 +77,7 @@ "@electron/notarize": "2.2.0", "@electron/rebuild": "3.6.0", "@types/copyfiles": "^2", + "@types/lodash": "^4", "copyfiles": "^2.4.1", "cross-env": "^7.0.3", "electron": "30.0.6", diff --git a/packages/desktop-electron/preload.ts b/packages/desktop-electron/preload.ts index 832bd6a505..11f1a1b83c 100644 --- a/packages/desktop-electron/preload.ts +++ b/packages/desktop-electron/preload.ts @@ -5,6 +5,7 @@ import { OpenFileDialogPayload, SaveFileDialogPayload, } from './index'; +const backend = require("i18next-electron-fs-backend"); const { version: VERSION, isDev: IS_DEV }: GetBootstrapDataPayload = ipcRenderer.sendSync('get-bootstrap-data'); @@ -14,6 +15,8 @@ contextBridge.exposeInMainWorld('Actual', { ACTUAL_VERSION: VERSION, logToTerminal: console.log, + i18nextElectronBackend: backend.preloadBindings(ipcRenderer, process), + ipcConnect: ( func: (payload: { on: IpcRenderer['on']; diff --git a/yarn.lock b/yarn.lock index 2414e62773..50bf9f2a46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8663,6 +8663,7 @@ __metadata: "@electron/notarize": "npm:2.2.0" "@electron/rebuild": "npm:3.6.0" "@types/copyfiles": "npm:^2" + "@types/lodash": "npm:^4" copyfiles: "npm:^2.4.1" cross-env: "npm:^7.0.3" electron: "npm:30.0.6" @@ -8670,7 +8671,9 @@ __metadata: electron-is-dev: "npm:2.0.0" electron-log: "npm:4.4.8" i18next: "npm:^23.12.6" + i18next-electron-fs-backend: "npm:^3.0.2" i18next-resources-to-backend: "npm:^1.2.1" + lodash: "npm:^4.17.21" loot-core: "npm:*" node-fetch: "npm:^2.7.0" promise-retry: "npm:^2.0.1" @@ -11273,6 +11276,16 @@ __metadata: languageName: node linkType: hard +"i18next-electron-fs-backend@npm:^3.0.2": + version: 3.0.2 + resolution: "i18next-electron-fs-backend@npm:3.0.2" + dependencies: + lodash.clonedeep: "npm:^4.5.0" + lodash.merge: "npm:^4.6.2" + checksum: 10/43227421724382ba3cea544e03d0ca3d159c73dad57abc85ba216fd6ae425b7f5cd1937205123e75d405b4db2488f654fbf6214145d2970316cd43572f789cbd + languageName: node + linkType: hard + "i18next-parser@npm:^9.0.0": version: 9.0.1 resolution: "i18next-parser@npm:9.0.1" @@ -13301,6 +13314,13 @@ __metadata: languageName: node linkType: hard +"lodash.clonedeep@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeep@npm:4.5.0" + checksum: 10/957ed243f84ba6791d4992d5c222ffffca339a3b79dbe81d2eaf0c90504160b500641c5a0f56e27630030b18b8e971ea10b44f928a977d5ced3c8948841b555f + languageName: node + linkType: hard + "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" From 58e5ae5e71f9c6386b0a267919b1a708d06b2c9d Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 14:03:06 -0500 Subject: [PATCH 09/10] fix path --- packages/desktop-electron/i18n.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/desktop-electron/i18n.ts b/packages/desktop-electron/i18n.ts index 55af735f03..4b232746f8 100644 --- a/packages/desktop-electron/i18n.ts +++ b/packages/desktop-electron/i18n.ts @@ -5,8 +5,8 @@ i18n .use(backend) .init({ backend: { - loadPath: "../desktop-client/locale/{{lng}}/{{ns}}.json", - addPath: "../desktop-client/locale/{{lng}}/{{ns}}.missing.json", + loadPath: "../desktop-client/locale/{{lng}}.json", + addPath: "../desktop-client/locale/{{lng}}.missing.json", contextBridgeApiKey: "Actual", }, From 4823db30f49d13b0cec3d38f5ba6b14af7a266f0 Mon Sep 17 00:00:00 2001 From: Robert Dyer Date: Thu, 15 Aug 2024 14:23:05 -0500 Subject: [PATCH 10/10] cleanup --- .eslintignore | 1 + packages/desktop-electron/i18n.ts | 44 ++++++++++++-------------- packages/desktop-electron/index.ts | 9 +++--- packages/desktop-electron/menu.ts | 1 + packages/desktop-electron/package.json | 1 - packages/desktop-electron/preload.ts | 3 +- yarn.lock | 1 - 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.eslintignore b/.eslintignore index 2487b54410..6bb987b25a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -17,6 +17,7 @@ packages/desktop-client/test-results/ packages/desktop-electron/client-build/ packages/desktop-electron/dist/ +packages/desktop-electron/build/ packages/import-ynab4/**/node_modules/* diff --git a/packages/desktop-electron/i18n.ts b/packages/desktop-electron/i18n.ts index 4b232746f8..3b591594b5 100644 --- a/packages/desktop-electron/i18n.ts +++ b/packages/desktop-electron/i18n.ts @@ -1,29 +1,27 @@ import i18n from 'i18next'; -import backend from "i18next-electron-fs-backend"; +import backend from 'i18next-electron-fs-backend'; -i18n - .use(backend) - .init({ - backend: { - loadPath: "../desktop-client/locale/{{lng}}.json", - addPath: "../desktop-client/locale/{{lng}}.missing.json", - contextBridgeApiKey: "Actual", - }, +i18n.use(backend).init({ + backend: { + loadPath: '../desktop-client/locale/{{lng}}.json', + addPath: '../desktop-client/locale/{{lng}}.missing.json', + contextBridgeApiKey: 'Actual', + }, - // While we mark all strings for translations, one can test - // it by setting the language in localStorage to their choice. - // Set this to 'cimode' to see the exact keys without interpolation. - lng: 'en', // FIXME localStorage undefined - // lng: localStorage.getItem('language') || 'en', + // While we mark all strings for translations, one can test + // it by setting the language in localStorage to their choice. + // Set this to 'cimode' to see the exact keys without interpolation. + lng: 'en', // FIXME localStorage undefined + // lng: localStorage.getItem('language') || 'en', - // allow keys to be phrases having `:`, `.` - nsSeparator: false, - keySeparator: false, - // do not load a fallback - fallbackLng: false, - interpolation: { - escapeValue: false, - }, - }); + // allow keys to be phrases having `:`, `.` + nsSeparator: false, + keySeparator: false, + // do not load a fallback + fallbackLng: false, + interpolation: { + escapeValue: false, + }, +}); export default i18n; diff --git a/packages/desktop-electron/index.ts b/packages/desktop-electron/index.ts index 017b7528f0..7c12b90e7f 100644 --- a/packages/desktop-electron/index.ts +++ b/packages/desktop-electron/index.ts @@ -21,16 +21,15 @@ import isDev from 'electron-is-dev'; import fetch from 'node-fetch'; import promiseRetry from 'promise-retry'; +import i18n from './i18n'; import { getMenu } from './menu'; import { get as getWindowState, listen as listenToWindowState, } from './window-state'; -const backend = require("i18next-electron-fs-backend"); - -import i18n from './i18n'; import './security'; +const backend = require('i18next-electron-fs-backend'); Module.globalPaths.push(__dirname + '/..'); @@ -207,7 +206,9 @@ function updateMenu(budgetId?: string) { const edit = menu.items.filter(item => item.label === i18n.t('Edit'))[0]; const editItems = edit.submenu?.items || []; editItems - .filter(item => item.label === i18n.t('Undo') || item.label === i18n.t('Redo')) + .filter( + item => item.label === i18n.t('Undo') || item.label === i18n.t('Redo'), + ) .map(item => (item.enabled = isBudgetOpen)); if (process.platform === 'win32') { diff --git a/packages/desktop-electron/menu.ts b/packages/desktop-electron/menu.ts index 97af4d155b..270e2de818 100644 --- a/packages/desktop-electron/menu.ts +++ b/packages/desktop-electron/menu.ts @@ -5,6 +5,7 @@ import { app, shell, } from 'electron'; + import i18n from './i18n'; export function getMenu( diff --git a/packages/desktop-electron/package.json b/packages/desktop-electron/package.json index 8d923844ad..b7f120a873 100644 --- a/packages/desktop-electron/package.json +++ b/packages/desktop-electron/package.json @@ -67,7 +67,6 @@ "electron-log": "4.4.8", "i18next": "^23.12.6", "i18next-electron-fs-backend": "^3.0.2", - "i18next-resources-to-backend": "^1.2.1", "lodash": "^4.17.21", "loot-core": "*", "node-fetch": "^2.7.0", diff --git a/packages/desktop-electron/preload.ts b/packages/desktop-electron/preload.ts index 11f1a1b83c..137a20becb 100644 --- a/packages/desktop-electron/preload.ts +++ b/packages/desktop-electron/preload.ts @@ -5,7 +5,8 @@ import { OpenFileDialogPayload, SaveFileDialogPayload, } from './index'; -const backend = require("i18next-electron-fs-backend"); + +const backend = require('i18next-electron-fs-backend'); const { version: VERSION, isDev: IS_DEV }: GetBootstrapDataPayload = ipcRenderer.sendSync('get-bootstrap-data'); diff --git a/yarn.lock b/yarn.lock index 50bf9f2a46..115cf4e5f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8672,7 +8672,6 @@ __metadata: electron-log: "npm:4.4.8" i18next: "npm:^23.12.6" i18next-electron-fs-backend: "npm:^3.0.2" - i18next-resources-to-backend: "npm:^1.2.1" lodash: "npm:^4.17.21" loot-core: "npm:*" node-fetch: "npm:^2.7.0"