Skip to content
Merged
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
7 changes: 7 additions & 0 deletions src/apps/main/config/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ipcMain } from 'electron';
import { isLanguage } from '../../shared/Locale/Language';

import { getConfigKey, setConfigKey } from './service';

Expand All @@ -7,3 +8,9 @@ ipcMain.handle('get-config-key', (_, key) => getConfigKey(key));
ipcMain.on('set-config-key', (_, { key, value }) => {
setConfigKey({ key, value });
});

ipcMain.on('set-prefered-language', (_, language) => {
if (!isLanguage(language)) return;

setConfigKey({ key: 'preferedLanguage', value: language });
});
9 changes: 7 additions & 2 deletions src/apps/main/config/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { electronStore } from '../config';
import { broadcastTheme } from '../../../core/theme';
import { broadcastLanguage } from './language';
import type { SetConfigKeyProps, StoredValues } from './service.types';
import { setMainI18nLanguage } from '../localize/i18n.service';

export const getConfigKey = <T extends StoredValues>(key: T): AppStore[T] => {
return electronStore.get(key);
Expand All @@ -14,6 +15,10 @@ export const setConfigKey = ({ key, value }: SetConfigKeyProps): void => {

electronStore.set(key, value);

if (key === 'preferedLanguage') broadcastLanguage();
else if (key === 'preferedTheme') broadcastTheme();
if (key === 'preferedLanguage') {
broadcastLanguage();
void setMainI18nLanguage({ language: value });
} else if (key === 'preferedTheme') {
broadcastTheme();
}
};
1 change: 1 addition & 0 deletions src/apps/main/interface.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { UserNotification } from '../../infra/drive-server/out/dto';
export interface IElectronAPI {
getConfigKey<T extends StoredValues>(key: T): Promise<AppStore[T]>;
setConfigKey<T extends StoredValues>(key: T, value: AppStore[T]): void;
setPreferedLanguage(language: AppStore['preferedLanguage']): void;
listenToConfigKeyChange<T>(key: StoredValues, fn: (value: T) => void): () => void;
toggleDarkMode(mode: ConfigTheme): Promise<void>;
getPreferredAppLanguage(): Promise<Array<string>>;
Expand Down
105 changes: 105 additions & 0 deletions src/apps/main/localize/i18n.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import i18next from 'i18next';
import { logger } from '@internxt/drive-desktop-core/build/backend';
import enTranslations from '../../renderer/localize/locales/en.json';
import esTranslations from '../../renderer/localize/locales/es.json';
import frTranslations from '../../renderer/localize/locales/fr.json';
import { DEFAULT_LANGUAGE, Language, isLanguage } from '../../shared/Locale/Language';

type TranslationOptions = {
[key: string]: unknown;
};

type TranslateMainPops = {
key: string;
defaultValue: string;
options?: TranslationOptions;
};

type SetMainI18nLanguagePops = {
language: Language;
};

type SetupMainI18nPops = {
language?: string;
};

const languageChangeListeners = new Set<() => void>();
let isMainI18nInitialized = false;

function notifyMainLanguageChanged() {
for (const listener of languageChangeListeners) {
listener();
}
}

export async function setupMainI18n({ language }: SetupMainI18nPops = {}) {
if (isMainI18nInitialized) return;

const initialLanguage = language && isLanguage(language) ? language : DEFAULT_LANGUAGE;

try {
await i18next.init({
resources: {
en: {
translation: enTranslations,
},
es: {
translation: esTranslations,
},
fr: {
translation: frTranslations,
},
},
lng: initialLanguage,
fallbackLng: DEFAULT_LANGUAGE,
defaultNS: 'translation',
ns: ['translation'],
debug: false,
interpolation: {
escapeValue: false,
},
});

i18next.on('languageChanged', notifyMainLanguageChanged);
isMainI18nInitialized = true;
} catch (exc) {

Check warning on line 65 in src/apps/main/localize/i18n.service.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The catch parameter `exc` should be named `error_`.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ6-HJ-nixFPtcnb9usg&open=AZ6-HJ-nixFPtcnb9usg&pullRequest=389
logger.error({
msg: 'Unable to initialize i18next for Main process',
exc,
language: initialLanguage,
});
}
}

export async function setMainI18nLanguage({ language }: SetMainI18nLanguagePops) {
await setupMainI18n();

if (!isMainI18nInitialized || i18next.language === language) return;

try {
await i18next.changeLanguage(language);
} catch (exc) {

Check warning on line 81 in src/apps/main/localize/i18n.service.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The catch parameter `exc` should be named `error_`.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ6-HJ-nixFPtcnb9ush&open=AZ6-HJ-nixFPtcnb9ush&pullRequest=389
logger.error({
msg: 'Unable to change i18next language for Main process',
exc,
language,
});
}
}

export function translateMain({ key, defaultValue, options }: TranslateMainPops) {
if (!isMainI18nInitialized) return defaultValue;

return i18next.t(key, {
defaultValue,
...(options || {}),

Check warning on line 95 in src/apps/main/localize/i18n.service.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

The empty object is useless.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ6-HJ-nixFPtcnb9usi&open=AZ6-HJ-nixFPtcnb9usi&pullRequest=389
});
}

export function onMainI18nLanguageChange(listener: () => void) {
languageChangeListeners.add(listener);

return () => {
languageChangeListeners.delete(listener);
};
}
2 changes: 2 additions & 0 deletions src/apps/main/preload.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ declare interface Window {

setConfigKey: typeof import('./config/service').setConfigKey;

setPreferedLanguage: (language: import('../shared/Locale/Language').Language) => void;

pathChanged(path: string): void;

getGeneralIssues: () => Promise<import('../../shared/issues/AppIssue').AppIssue[]>;
Expand Down
3 changes: 3 additions & 0 deletions src/apps/main/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ contextBridge.exposeInMainWorld('electron', {
setConfigKey(key, value) {
return ipcRenderer.send('set-config-key', { key, value });
},
setPreferedLanguage(language) {
return ipcRenderer.send('set-prefered-language', language);
},
listenToConfigKeyChange(key, fn) {
const eventName = `${key}-updated`;
const callback = (_, v) => fn(v);
Expand Down
75 changes: 54 additions & 21 deletions src/apps/main/tray/tray-menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Menu, nativeImage, Tray } from 'electron';
import path from 'node:path';
import PackageJson from '../../../../package.json';
import { TrayMenuState } from './types';
import { translateMain } from '../localize/i18n.service';

export class TrayMenu {
private readonly tray: Tray;
Expand All @@ -21,22 +22,7 @@ export class TrayMenu {
this.tray = new Tray(trayIcon);

this.setState('LOADING');

const contextMenu = Menu.buildFromTemplate([
{
label: `Internxt ${PackageJson.version}`,
click: () => {
this.onClick();
},
},
{
label: 'Quit',
click: () => {
this.onQuit();
},
},
]);
this.tray.setContextMenu(contextMenu);
this.setContextMenu();
}

getIconPath(state: TrayMenuState) {
Expand All @@ -53,17 +39,64 @@ export class TrayMenu {
this.setTooltip(state);
}

refreshTranslations() {
this.setContextMenu();

if (this.currentState) {
this.setTooltip(this.currentState);
}
}

setImage(imagePath: string) {
const image = nativeImage.createFromPath(imagePath);
this.tray.setImage(image);
}

setContextMenu() {
const contextMenu = Menu.buildFromTemplate([
{
label: translateMain({
key: 'main.tray.menu.open-app',
defaultValue: `Internxt ${PackageJson.version}`,
options: { version: PackageJson.version },
}),
click: () => {
void this.onClick();
},
},
{
label: translateMain({
key: 'main.tray.menu.quit',
defaultValue: 'Quit',
}),
click: () => {
this.onQuit();
},
},
]);

this.tray.setContextMenu(contextMenu);
}

setTooltip(state: TrayMenuState) {
const messages: Record<TrayMenuState, string> = {
SYNCING: 'Sync in process',
IDLE: `Internxt ${PackageJson.version}`,
ALERT: 'There are some issues with your sync',
LOADING: 'Loading Internxt...',
const messages = {
SYNCING: translateMain({
key: 'main.tray.tooltip.syncing',
defaultValue: 'Sync in process',
}),
IDLE: translateMain({
key: 'main.tray.tooltip.idle',
defaultValue: `Internxt ${PackageJson.version}`,
options: { version: PackageJson.version },
}),
ALERT: translateMain({
key: 'main.tray.tooltip.alert',
defaultValue: 'There are some issues with your sync',
}),
LOADING: translateMain({
key: 'main.tray.tooltip.loading',
defaultValue: 'Loading Internxt...',
}),
};

const message = messages[state];
Expand Down
8 changes: 8 additions & 0 deletions src/apps/main/tray/tray-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import { getAuthWindow } from '../windows/auth';
import { TrayMenuState } from './types';
import { PATHS } from '../../../core/electron/paths';
import { onMainI18nLanguageChange } from '../localize/i18n.service';

let tray: TrayMenu | null = null;
let removeMainI18nListener: (() => void) | null = null;

// v.2.6.0
// Esteban Galvis Triana
Expand Down Expand Up @@ -64,5 +66,11 @@

tray = new TrayMenu(iconsPath, onTrayClick, onQuitClick);

if (!removeMainI18nListener) {
removeMainI18nListener = onMainI18nLanguageChange(() => {
tray?.refreshTranslations();
});
}

Check warning on line 73 in src/apps/main/tray/tray-setup.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using nullish coalescing operator (`??=`) instead of an assignment expression, as it is simpler to read.

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ6-HJ8GixFPtcnb9usf&open=AZ6-HJ8GixFPtcnb9usf&pullRequest=389

return tray;
}
14 changes: 14 additions & 0 deletions src/apps/renderer/localize/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,5 +433,19 @@
},
"ctaUpgrade": "Upgrade plan",
"plan": "{{planName}} -> up to {{planMaxFileSize}}"
},
"main": {
"tray": {
"menu": {
"open-app": "Internxt {{version}}",
"quit": "Quit"
},
"tooltip": {
"syncing": "Sync in process",
"idle": "Internxt {{version}}",
"alert": "There are some issues with your sync",
"loading": "Loading Internxt..."
}
}
}
}
14 changes: 14 additions & 0 deletions src/apps/renderer/localize/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,5 +433,19 @@
},
"ctaUpgrade": "Mejorar plan",
"plan": "{{planName}} -> hasta {{planMaxFileSize}}"
},
"main": {
"tray": {
"menu": {
"open-app": "Internxt {{version}}",
"quit": "Salir"
},
"tooltip": {
"syncing": "Sincronizacion en curso",
"idle": "Internxt {{version}}",
"alert": "Hay algunos problemas con tu sincronizacion",
"loading": "Cargando Internxt..."
}
}
}
}
14 changes: 14 additions & 0 deletions src/apps/renderer/localize/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,5 +433,19 @@
},
"ctaUpgrade": "Mettre à jour le plan",
"plan": "{{planName}} -> jusqu'à {{planMaxFileSize}}"
},
"main": {
"tray": {
"menu": {
"open-app": "Internxt {{version}}",
"quit": "Fermer"
},
"tooltip": {
"syncing": "Synchronisation en cours",
"idle": "Internxt {{version}}",
"alert": "Il y a des problemes avec votre synchronisation",
"loading": "Chargement d'Internxt..."
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function LanguagePicker(): JSX.Element {
const lang = value;
i18next.changeLanguage(lang);
dayjs.locale(DayJsLocales[lang]);
window.electron.setConfigKey('preferedLanguage', lang);
window.electron.setPreferedLanguage(lang);
refreshPreferedLanguage();
};

Expand Down
3 changes: 3 additions & 0 deletions src/core/bootstrap/register-app-ready-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { setupAppImageDeeplink } from '../../apps/main/auth/deeplink/setup-appim
import { INTERNXT_VERSION } from '../utils/utils';
import { checkForUpdates } from '../../apps/main/auto-update/check-for-updates';
import { setPendingUpdateInfo } from './bootstrap-runtime-state';
import { setupMainI18n } from '../../apps/main/localize/i18n.service';
import { getLanguage } from '../../apps/main/config/language';

export function registerAppReadyFlow() {
app
Expand All @@ -32,6 +34,7 @@ export function registerAppReadyFlow() {
* while the exact behavior of the context menu options is being determined.
*/
// await installNautilusExtension();
await setupMainI18n({ language: getLanguage() });
setupThemeListener();
setupTrayIcon();

Expand Down
Loading