1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Merge branch 'dev' of github.com:laurent22/joplin into dev

This commit is contained in:
Laurent Cozic 2023-10-11 13:05:30 +03:00
commit 3ac25104c3
23 changed files with 283 additions and 144 deletions

View File

@ -79,7 +79,7 @@
"eslint-plugin-react": "7.33.2",
"execa": "5.1.1",
"fs-extra": "11.1.1",
"glob": "10.3.4",
"glob": "10.3.5",
"gulp": "4.0.2",
"husky": "3.1.0",
"lerna": "3.22.1",

View File

@ -57,7 +57,7 @@
"proper-lockfile": "4.1.2",
"read-chunk": "2.1.0",
"server-destroy": "1.0.1",
"sharp": "0.32.5",
"sharp": "0.32.6",
"sprintf-js": "1.1.3",
"sqlite3": "5.1.6",
"string-padding": "1.0.2",
@ -73,7 +73,7 @@
"@joplin/tools": "~2.13",
"@types/fs-extra": "11.0.2",
"@types/jest": "29.5.4",
"@types/node": "18.17.17",
"@types/node": "18.17.18",
"@types/proper-lockfile": "^4.1.2",
"gulp": "4.0.2",
"jest": "29.6.4",

View File

@ -2,17 +2,16 @@
import { ContextMenuEvent, ContextMenuParams } from 'electron';
import { useEffect, RefObject } from 'react';
import { _ } from '@joplin/lib/locale';
import Setting from '@joplin/lib/models/Setting';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { MenuItemLocation } from '@joplin/lib/services/plugins/api/types';
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import CommandService from '@joplin/lib/services/CommandService';
import convertToScreenCoordinates from '../../../../utils/convertToScreenCoordinates';
import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerService';
import { EditContextMenuFilterObject } from '@joplin/lib/services/plugins/api/JoplinWorkspace';
import type CodeMirrorControl from '@joplin/editor/CodeMirror/CodeMirrorControl';
import eventManager from '@joplin/lib/eventManager';
import bridge from '../../../../../services/bridge';
import Setting from '@joplin/lib/models/Setting';
const Menu = bridge().Menu;
const MenuItem = bridge().MenuItem;
@ -35,7 +34,7 @@ const useContextMenu = (props: ContextMenuProps) => {
// It might be buggy, refer to the below issue
// https://github.com/laurent22/joplin/pull/3974#issuecomment-718936703
useEffect(() => {
const isAncestorOfCodeMirrorEditor = (elem: HTMLElement) => {
const isAncestorOfCodeMirrorEditor = (elem: Element) => {
for (; elem.parentElement; elem = elem.parentElement) {
if (elem.classList.contains(props.editorClassName)) {
return true;
@ -45,14 +44,9 @@ const useContextMenu = (props: ContextMenuProps) => {
return false;
};
let lastInCodeMirrorContextMenuTimestamp = 0;
// The browser's contextmenu event provides additional information about the
// target of the event, not provided by the Electron context-menu event.
const onBrowserContextMenu = (event: Event) => {
if (isAncestorOfCodeMirrorEditor(event.target as HTMLElement)) {
lastInCodeMirrorContextMenuTimestamp = Date.now();
}
const convertFromScreenCoordinates = (zoomPercent: number, screenXY: number) => {
const zoomFraction = zoomPercent / 100;
return screenXY / zoomFraction;
};
function pointerInsideEditor(params: ContextMenuParams) {
@ -64,13 +58,15 @@ const useContextMenu = (props: ContextMenuProps) => {
// params.inputFieldType is "plainText". Thus, such a check would be inconsistent.
if (!elements.length || !isEditable) return false;
const maximumMsSinceBrowserEvent = 100;
if (Date.now() - lastInCodeMirrorContextMenuTimestamp > maximumMsSinceBrowserEvent) {
return false;
}
const rect = convertToScreenCoordinates(Setting.value('windowContentZoomFactor'), elements[0].getBoundingClientRect());
return rect.x < x && rect.y < y && rect.right > x && rect.bottom > y;
// Checks whether the element the pointer clicked on is inside the editor.
// This logic will need to be changed if the editor is eventually wrapped
// in an iframe, as elementFromPoint will return the iframe container (and not
// a child of the editor).
const zoom = Setting.value('windowContentZoomFactor');
const xScreen = convertFromScreenCoordinates(zoom, x);
const yScreen = convertFromScreenCoordinates(zoom, y);
const intersectingElement = document.elementFromPoint(xScreen, yScreen);
return intersectingElement && isAncestorOfCodeMirrorEditor(intersectingElement);
}
async function onContextMenu(event: ContextMenuEvent, params: ContextMenuParams) {
@ -160,11 +156,8 @@ const useContextMenu = (props: ContextMenuProps) => {
// the listener that shows the default menu.
bridge().window().webContents.prependListener('context-menu', onContextMenu);
window.addEventListener('contextmenu', onBrowserContextMenu);
return () => {
bridge().window().webContents.off('context-menu', onContextMenu);
window.removeEventListener('contextmenu', onBrowserContextMenu);
};
}, [
props.plugins, props.editorClassName, editorRef,

View File

@ -118,13 +118,13 @@
"@joplin/tools": "~2.13",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.5.4",
"@types/node": "18.17.17",
"@types/react": "18.2.21",
"@types/node": "18.17.18",
"@types/react": "18.2.22",
"@types/react-redux": "7.1.26",
"@types/styled-components": "5.1.27",
"electron": "25.8.1",
"electron-builder": "24.4.0",
"glob": "10.3.4",
"glob": "10.3.5",
"gulp": "4.0.2",
"jest": "29.6.4",
"jest-environment-jsdom": "29.6.4",
@ -173,7 +173,7 @@
"react-datetime": "3.2.0",
"react-dom": "18.2.0",
"react-redux": "8.1.2",
"react-select": "5.7.4",
"react-select": "5.7.5",
"react-toggle-button": "2.2.0",
"react-tooltip": "4.5.1",
"redux": "4.2.1",

View File

@ -58,20 +58,20 @@
"react-native-image-picker": "5.6.1",
"react-native-localize": "3.0.2",
"react-native-modal-datetime-picker": "17.1.0",
"react-native-paper": "5.10.4",
"react-native-paper": "5.10.6",
"react-native-popup-menu": "0.16.1",
"react-native-quick-actions": "0.3.13",
"react-native-rsa-native": "2.0.5",
"react-native-safe-area-context": "4.7.2",
"react-native-securerandom": "1.0.1",
"react-native-share": "9.2.4",
"react-native-share": "9.4.0",
"react-native-side-menu-updated": "1.3.2",
"react-native-sqlite-storage": "6.0.1",
"react-native-url-polyfill": "2.0.0",
"react-native-vector-icons": "10.0.0",
"react-native-version-info": "1.1.1",
"react-native-vosk": "0.1.12",
"react-native-webview": "13.4.0",
"react-native-webview": "13.5.1",
"react-native-zip-archive": "6.0.9",
"react-redux": "8.1.2",
"redux": "4.2.1",
@ -95,7 +95,7 @@
"@tsconfig/react-native": "2.0.2",
"@types/fs-extra": "11.0.2",
"@types/jest": "29.5.4",
"@types/react": "18.2.21",
"@types/react": "18.2.22",
"@types/react-native": "0.70.6",
"@types/react-redux": "7.1.26",
"@types/tar-stream": "2.2.3",

View File

@ -18,7 +18,7 @@
"@joplin/lib": "~2.13",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.5.4",
"@types/react": "18.0.24",
"@types/react": "18.2.22",
"@types/react-redux": "7.1.26",
"@types/styled-components": "5.1.27",
"jest": "29.6.3",

View File

@ -46,7 +46,7 @@
},
"devDependencies": {
"@types/jest": "29.5.4",
"@types/node": "18.17.17",
"@types/node": "18.17.18",
"@typescript-eslint/eslint-plugin": "6.0.0",
"@typescript-eslint/parser": "6.0.0",
"coveralls": "3.1.1",

View File

@ -1,4 +1,4 @@
import { closestSupportedLocale } from './locale';
import { closestSupportedLocale, parsePluralForm, setLocale, _n } from './locale';
describe('locale', () => {
@ -15,4 +15,80 @@ describe('locale', () => {
}
});
it('should translate plurals - en_GB', () => {
setLocale('en_GB');
expect(_n('Copy Shareable Link', 'Copy Shareable Links', 1)).toBe('Copy Shareable Link');
expect(_n('Copy Shareable Link', 'Copy Shareable Links', 2)).toBe('Copy Shareable Links');
});
it('should translate plurals - fr_FR', () => {
setLocale('fr_FR');
expect(_n('Copy Shareable Link', 'Copy Shareable Links', 1)).toBe('Copier lien partageable');
expect(_n('Copy Shareable Link', 'Copy Shareable Links', 2)).toBe('Copier liens partageables');
});
it('should translate plurals - pl_PL', () => {
setLocale('pl_PL');
// Not the best test since 5 is the same as 2, but it's all I could find
expect(_n('Copy Shareable Link', 'Copy Shareable Links', 1)).toBe('Kopiuj udostępnialny link');
expect(_n('Copy Shareable Link', 'Copy Shareable Links', 2)).toBe('Kopiuj udostępnialne linki');
expect(_n('Copy Shareable Link', 'Copy Shareable Links', 5)).toBe('Kopiuj udostępnialne linki');
});
it('should parse the plural form', async () => {
const pluralForms = [
'nplurals=1; plural=0;',
'nplurals=2; plural=(n != 0);',
'nplurals=2; plural=(n != 1);',
'nplurals=2; plural=(n > 1);',
'nplurals=2; plural=(n%10!=1 || n%100==11);',
'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);',
'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);',
'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);',
'nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2);',
'nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);',
'nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);',
'nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;',
'nplurals=3; plural=(n==1) ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;',
'nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0);',
'nplurals=4; plural=(n==1 ? 0 : n==0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3);',
'nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;',
'nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;',
'nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3;',
'nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : (n>2 && n<7) ? 2 :(n>6 && n<11) ? 3 : 4;',
'nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5);',
];
const pluralValues = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1],
[2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1],
[2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1],
[2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2],
[0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2],
[2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[2, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2],
[0, 1, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3],
[3, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[2, 0, 1, 2, 2, 2, 2, 2, 3, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[3, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[4, 0, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
];
for (let index = 0; index < pluralForms.length; index++) {
const form = pluralForms[index];
const pluralFn = parsePluralForm(form);
for (let i = 0; i < 128; ++i) {
expect(pluralValues[index][i]).toBe(pluralFn(i));
}
}
});
});

View File

@ -7,6 +7,7 @@ interface StringToStringMap {
interface CodeToCountryMap {
[key: string]: string[];
}
type ParsePluralFormFunction = (n: number)=> number;
const codeToLanguageE_: StringToStringMap = {};
codeToLanguageE_['aa'] = 'Afar';
@ -436,12 +437,51 @@ const codeToCountry_: CodeToCountryMap = {
let supportedLocales_: any = null;
let localeStats_: any = null;
const loadedLocales_: any = {};
const loadedLocales_: Record<string, Record<string, string[]>> = {};
const pluralFunctions_: Record<string, ParsePluralFormFunction> = {};
const defaultLocale_ = 'en_GB';
let currentLocale_ = defaultLocale_;
// Copied from https://github.com/eugeny-dementev/parse-gettext-plural-form
// along with the tests
export const parsePluralForm = (form: string): ParsePluralFormFunction => {
const pluralFormRegex = /^(\s*nplurals\s*=\s*[0-9]+\s*;\s*plural\s*=\s*(?:\s|[-?|&=!<>+*/%:;a-zA-Z0-9_()])+)$/m;
if (!pluralFormRegex.test(form)) throw new Error(`Plural-Forms is invalid: ${form}`);
if (!/;\s*$/.test(form)) {
form += ';';
}
const code = [
'var plural;',
'var nplurals;',
form,
'return (plural === true ? 1 : plural ? plural : 0);',
].join('\n');
// eslint-disable-next-line no-new-func -- There's a regex to check the form but it's still slighlty unsafe, eventually we should automatically generate all the functions in advance in build-translations.ts
return (new Function('n', code)) as ParsePluralFormFunction;
};
const getPluralFunction = (lang: string) => {
if (!(lang in pluralFunctions_)) {
const locale = closestSupportedLocale(lang);
const stats = localeStats()[locale];
if (!stats.pluralForms) {
pluralFunctions_[lang] = null;
} else {
pluralFunctions_[lang] = parsePluralForm(stats.pluralForms);
}
}
return pluralFunctions_[lang];
};
function defaultLocale() {
return defaultLocale_;
}
@ -589,18 +629,45 @@ function _(s: string, ...args: any[]): string {
}
function _n(singular: string, plural: string, n: number, ...args: any[]) {
if (n > 1) return _(plural, ...args);
return _(singular, ...args);
if (['en_GB', 'en_US'].includes(currentLocale_)) {
if (n > 1) return _(plural, ...args);
return _(singular, ...args);
} else {
const pluralFn = getPluralFunction(currentLocale_);
const stringIndex = pluralFn ? pluralFn(n) : 0;
const strings = localeStrings(currentLocale_);
const result = strings[singular];
let translatedString = '';
if (result === undefined || !result.join('')) {
translatedString = singular;
} else {
translatedString = stringIndex < result.length ? result[stringIndex] : result[0];
}
try {
return sprintf(translatedString, ...args);
} catch (error) {
return `${translatedString} ${args.join(', ')} (Translation error: ${error.message})`;
}
}
}
const stringByLocale = (locale: string, s: string, ...args: any[]): string => {
const strings = localeStrings(locale);
let result = strings[s];
if (result === '' || result === undefined) result = s;
const result = strings[s];
let translatedString = '';
if (result === undefined || !result.join('')) {
translatedString = s;
} else {
translatedString = result[0];
}
try {
return sprintf(result, ...args);
return sprintf(translatedString, ...args);
} catch (error) {
return `${result} ${args.join(', ')} (Translation error: ${error.message})`;
return `${translatedString} ${args.join(', ')} (Translation error: ${error.message})`;
}
};

View File

@ -19,13 +19,13 @@
"@types/fs-extra": "11.0.2",
"@types/jest": "29.5.4",
"@types/js-yaml": "4.0.6",
"@types/node": "18.17.17",
"@types/node": "18.17.18",
"@types/node-rsa": "1.1.1",
"@types/react": "18.2.21",
"@types/react": "18.2.22",
"@types/uuid": "9.0.4",
"clean-html": "1.5.0",
"jest": "29.6.4",
"sharp": "0.32.5",
"sharp": "0.32.6",
"typescript": "5.1.6"
},
"dependencies": {
@ -52,7 +52,7 @@
"es6-promise-pool": "2.5.0",
"fast-deep-equal": "3.1.3",
"fast-xml-parser": "3.21.1",
"follow-redirects": "1.15.2",
"follow-redirects": "1.15.3",
"form-data": "4.0.0",
"fs-extra": "11.1.1",
"hpagent": "1.2.0",

View File

@ -21,7 +21,7 @@
"devDependencies": {
"@types/jest": "29.5.4",
"@types/pdfjs-dist": "2.10.378",
"@types/react": "18.2.21",
"@types/react": "18.2.22",
"@types/react-dom": "18.2.7",
"@types/styled-components": "5.1.27",
"babel-jest": "29.6.4",

View File

@ -30,7 +30,7 @@
"devDependencies": {
"@types/fs-extra": "11.0.2",
"@types/jest": "29.5.4",
"@types/node": "18.17.17",
"@types/node": "18.17.18",
"jest": "29.6.4",
"source-map-loader": "4.0.1",
"typescript": "5.1.6",

View File

@ -20,7 +20,7 @@
"license": "AGPL-3.0-or-later",
"devDependencies": {
"@types/jest": "29.5.4",
"@types/node": "18.17.17",
"@types/node": "18.17.18",
"jest": "29.6.4",
"jest-environment-jsdom": "29.6.4",
"ts-jest": "29.1.1",

View File

@ -32,7 +32,7 @@
"bulma": "0.9.4",
"bulma-prefers-dark": "0.1.0-beta.1",
"compare-versions": "6.1.0",
"dayjs": "1.11.9",
"dayjs": "1.11.10",
"formidable": "3.5.1",
"fs-extra": "11.1.1",
"html-entities": "1.4.0",

View File

@ -12,7 +12,7 @@ import { countryDisplayName, countryCodeOnly } from '@joplin/lib/locale';
import { readdirSync, writeFileSync } from 'fs';
import { readFile } from 'fs/promises';
import { copy, mkdirpSync, remove } from 'fs-extra';
const { GettextExtractor, JsExtractors } = require('gettext-extractor');
import { GettextExtractor, JsExtractors } from 'gettext-extractor';
const rootDir = `${__dirname}/../..`;
const localesDir = `${__dirname}/locales`;
@ -20,7 +20,7 @@ const libDir = `${rootDir}/packages/lib`;
function serializeTranslation(translation: string) {
const output = parseTranslations(translation);
return JSON.stringify(output, Object.keys(output).sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : +1), ' ');
return JSON.stringify(output, Object.keys(output).sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : +1), '\t');
}
function saveToFile(filePath: string, data: string) {
@ -31,6 +31,7 @@ async function buildLocale(inputFile: string, outputFile: string) {
const r = await parsePoFile(inputFile);
const translation = serializeTranslation(r);
saveToFile(outputFile, translation);
return { headers: r.headers };
}
async function createPotFile(potFilePath: string) {
@ -391,9 +392,10 @@ async function main() {
const poFilePäth = `${localesDir}/${locale}.po`;
const jsonFilePath = `${jsonLocalesDir}/${locale}.json`;
if (locale !== defaultLocale) await mergePotToPo(potFilePath, poFilePäth);
await buildLocale(poFilePäth, jsonFilePath);
const { headers } = await buildLocale(poFilePäth, jsonFilePath);
const stat = await translationStatus(defaultLocale === locale, poFilePäth);
stat.pluralForms = headers['Plural-Forms'];
stat.locale = locale;
stat.languageName = countryDisplayName(locale);
stats.push(stat);

View File

@ -24,11 +24,11 @@
"@joplin/renderer": "~2.13",
"@joplin/utils": "~2.13",
"compare-versions": "6.1.0",
"dayjs": "1.11.9",
"dayjs": "1.11.10",
"execa": "4.1.0",
"fs-extra": "11.1.1",
"gettext-parser": "7.0.1",
"glob": "10.3.4",
"glob": "10.3.5",
"markdown-it": "13.0.1",
"md5-file": "5.0.0",
"moment": "2.29.4",
@ -36,7 +36,7 @@
"node-fetch": "2.6.7",
"relative": "3.0.2",
"request": "2.88.2",
"sharp": "0.32.5",
"sharp": "0.32.6",
"source-map-support": "0.5.21",
"uri-template": "2.0.0",
"yargs": "17.7.2"
@ -48,7 +48,7 @@
"@types/jest": "29.5.4",
"@types/markdown-it": "13.0.1",
"@types/mustache": "4.2.2",
"@types/node": "18.17.17",
"@types/node": "18.17.18",
"@types/node-fetch": "2.6.5",
"@types/yargs": "17.0.24",
"gettext-extractor": "3.8.0",

View File

@ -8,9 +8,10 @@ export interface TranslationStatus {
translatorName: string;
percentDone: number;
untranslatedCount: number;
pluralForms?: string;
}
export type Translations = Record<string, string>;
export type Translations = Record<string, string[]>;
export const removePoHeaderDate = async (filePath: string) => {
let sedPrefix = 'sed -i';
@ -67,14 +68,14 @@ export const parseTranslations = (gettextTranslations: any) => {
if (!translations.hasOwnProperty(n)) continue;
if (n === '') continue;
const t = translations[n];
let translated = '';
let translated: string[] = [];
if (t.comments && t.comments.flag && t.comments.flag.indexOf('fuzzy') >= 0) {
// Don't include fuzzy translations
} else {
translated = t['msgstr'][0];
translated = t['msgstr'];
}
if (translated) output[n] = translated;
if (translated.length) output[n] = translated;
}
}

View File

@ -7,7 +7,7 @@ describe('applyTranslations', () => {
{
html: '<div><span translate>Translate me</span></div>',
translations: {
'Translate me': 'Traduis moi',
'Translate me': ['Traduis moi'],
},
htmlTranslated: '<div>\n<span translate>\nTraduis moi\n</span>\n</div>',
},
@ -19,14 +19,14 @@ describe('applyTranslations', () => {
{
html: '<h1 translate class="text-center">\nFree your <span class="frame-bg frame-bg-blue">notes</span>\n</h1>',
translations: {
'Free your <span class="frame-bg frame-bg-blue">notes</span>': 'Libérez vos <span class="frame-bg frame-bg-blue">notes</span>',
'Free your <span class="frame-bg frame-bg-blue">notes</span>': ['Libérez vos <span class="frame-bg frame-bg-blue">notes</span>'],
},
htmlTranslated: '<h1 translate class="text-center">\nLibérez vos <span class="frame-bg frame-bg-blue">notes</span>\n</h1>',
},
{
html: '<div translate>Save <span class="frame-bg frame-bg-blue">web pages</span> <br />as notes</div>',
translations: {
'Save <span class="frame-bg frame-bg-blue">web pages</span> <br>as notes': 'Sauvegardez vos <span class="frame-bg frame-bg-blue">pages web</span> <br>en notes',
'Save <span class="frame-bg frame-bg-blue">web pages</span> <br>as notes': ['Sauvegardez vos <span class="frame-bg frame-bg-blue">pages web</span> <br>en notes'],
},
htmlTranslated: '<div translate>\nSauvegardez vos <span class="frame-bg frame-bg-blue">pages web</span> <br>en notes\n</div>',
},

View File

@ -1,5 +1,6 @@
import { unique } from '@joplin/lib/ArrayUtils';
import { attributesHtml, isSelfClosingTag } from '@joplin/renderer/htmlUtils';
import { Translations } from '../../utils/translation';
const Entities = require('html-entities').AllHtmlEntities;
const htmlentities = new Entities().encode;
const htmlparser2 = require('@joplin/fork-htmlparser2');
@ -15,7 +16,7 @@ const trimHtml = (content: string) => {
.replace(/\t+$/, '');
};
const findTranslation = (englishString: string, translations: Record<string, string>): string => {
const findTranslation = (englishString: string, translations: Translations): string => {
const stringsToTry = unique([
englishString,
englishString.replace(/<br\/>/gi, '<br>'),
@ -26,7 +27,8 @@ const findTranslation = (englishString: string, translations: Record<string, str
]) as string[];
for (const stringToTry of stringsToTry) {
if (translations[stringToTry]) return translations[stringToTry];
// Note that we don't currently support plural forms for the website
if (translations[stringToTry] && translations[stringToTry].length) return translations[stringToTry][0];
}
return englishString;
@ -38,7 +40,7 @@ const encodeHtml = (decodedText: string): string => {
.replace(/{{&gt; /gi, '{{> '); // Don't break Mustache partials
};
export default (html: string, _languageCode: string, translations: Record<string, string>) => {
export default (html: string, _languageCode: string, translations: Translations) => {
const output: string[] = [];
interface State {

View File

@ -1,8 +1,9 @@
import { mkdirp, readFile, writeFile } from 'fs-extra';
import { dirname } from 'path';
import { Translations } from '../../utils/translation';
import applyTranslations from './applyTranslations';
export default async (englishFilePath: string, translatedFilePath: string, languageCode: string, translations: Record<string, string>) => {
export default async (englishFilePath: string, translatedFilePath: string, languageCode: string, translations: Translations) => {
let content = await readFile(englishFilePath, 'utf8');
content = content.replace('<html lang="en-gb">', `<html lang="${languageCode}">`);
const translatedContent = await applyTranslations(content, languageCode, translations);

View File

@ -1,5 +1,6 @@
import { Plan, StripePublicConfig } from '@joplin/lib/utils/joplinCloud';
import { Sponsors } from '../../utils/loadSponsors';
import { Translations } from '../../utils/translation';
import { OpenGraphTags } from './openGraph';
export enum Env {
@ -8,7 +9,7 @@ export enum Env {
}
export interface Locale {
htmlTranslations: Record<string, string>;
htmlTranslations: Translations;
lang: string;
pathPrefix: string;
}

View File

@ -29,7 +29,7 @@
"async-mutex": "0.4.0",
"execa": "5.1.1",
"fs-extra": "11.1.1",
"glob": "10.3.4",
"glob": "10.3.5",
"html-entities": "1.4.0",
"moment": "2.29.4",
"node-fetch": "2.6.7",

144
yarn.lock
View File

@ -4664,8 +4664,8 @@ __metadata:
"@testing-library/react-hooks": 8.0.1
"@types/jest": 29.5.4
"@types/mustache": 4.2.2
"@types/node": 18.17.17
"@types/react": 18.2.21
"@types/node": 18.17.18
"@types/react": 18.2.22
"@types/react-redux": 7.1.26
"@types/styled-components": 5.1.27
async-mutex: 0.4.0
@ -4679,7 +4679,7 @@ __metadata:
electron-window-state: 5.0.3
formatcoords: 1.1.3
fs-extra: 11.1.1
glob: 10.3.4
glob: 10.3.5
gulp: 4.0.2
highlight.js: 11.8.0
immer: 7.0.15
@ -4701,7 +4701,7 @@ __metadata:
react-datetime: 3.2.0
react-dom: 18.2.0
react-redux: 8.1.2
react-select: 5.7.4
react-select: 5.7.5
react-test-renderer: 18.2.0
react-toggle-button: 2.2.0
react-tooltip: 4.5.1
@ -4753,7 +4753,7 @@ __metadata:
"@tsconfig/react-native": 2.0.2
"@types/fs-extra": 11.0.2
"@types/jest": 29.5.4
"@types/react": 18.2.21
"@types/react": 18.2.22
"@types/react-native": 0.70.6
"@types/react-redux": 7.1.26
"@types/tar-stream": 2.2.3
@ -4797,20 +4797,20 @@ __metadata:
react-native-image-picker: 5.6.1
react-native-localize: 3.0.2
react-native-modal-datetime-picker: 17.1.0
react-native-paper: 5.10.4
react-native-paper: 5.10.6
react-native-popup-menu: 0.16.1
react-native-quick-actions: 0.3.13
react-native-rsa-native: 2.0.5
react-native-safe-area-context: 4.7.2
react-native-securerandom: 1.0.1
react-native-share: 9.2.4
react-native-share: 9.4.0
react-native-side-menu-updated: 1.3.2
react-native-sqlite-storage: 6.0.1
react-native-url-polyfill: 2.0.0
react-native-vector-icons: 10.0.0
react-native-version-info: 1.1.1
react-native-vosk: 0.1.12
react-native-webview: 13.4.0
react-native-webview: 13.5.1
react-native-zip-archive: 6.0.9
react-redux: 8.1.2
react-test-renderer: 18.2.0
@ -4855,7 +4855,7 @@ __metadata:
"@replit/codemirror-vim": 6.0.14
"@testing-library/react-hooks": 8.0.1
"@types/jest": 29.5.4
"@types/react": 18.0.24
"@types/react": 18.2.22
"@types/react-redux": 7.1.26
"@types/styled-components": 5.1.27
jest: 29.6.3
@ -4870,7 +4870,7 @@ __metadata:
resolution: "@joplin/fork-htmlparser2@workspace:packages/fork-htmlparser2"
dependencies:
"@types/jest": 29.5.4
"@types/node": 18.17.17
"@types/node": 18.17.18
"@typescript-eslint/eslint-plugin": 6.0.0
"@typescript-eslint/parser": 6.0.0
coveralls: 3.1.1
@ -4936,9 +4936,9 @@ __metadata:
"@types/jest": 29.5.4
"@types/js-yaml": 4.0.6
"@types/nanoid": 3.0.0
"@types/node": 18.17.17
"@types/node": 18.17.18
"@types/node-rsa": 1.1.1
"@types/react": 18.2.21
"@types/react": 18.2.22
"@types/uuid": 9.0.4
async-mutex: 0.4.0
base-64: 1.0.0
@ -4953,7 +4953,7 @@ __metadata:
es6-promise-pool: 2.5.0
fast-deep-equal: 3.1.3
fast-xml-parser: 3.21.1
follow-redirects: 1.15.2
follow-redirects: 1.15.3
form-data: 4.0.0
fs-extra: 11.1.1
hpagent: 1.2.0
@ -4984,7 +4984,7 @@ __metadata:
relative: 3.0.2
reselect: 4.1.8
server-destroy: 1.0.1
sharp: 0.32.5
sharp: 0.32.6
sprintf-js: 1.1.3
sqlite3: 5.1.6
string-padding: 1.0.2
@ -5010,7 +5010,7 @@ __metadata:
"@joplin/lib": ~2.13
"@types/jest": 29.5.4
"@types/pdfjs-dist": 2.10.378
"@types/react": 18.2.21
"@types/react": 18.2.22
"@types/react-dom": 18.2.7
"@types/styled-components": 5.1.27
async-mutex: 0.4.0
@ -5040,7 +5040,7 @@ __metadata:
"@joplin/utils": ~2.13
"@types/fs-extra": 11.0.2
"@types/jest": 29.5.4
"@types/node": 18.17.17
"@types/node": 18.17.18
fs-extra: 11.1.1
gh-release-assets: 2.0.1
jest: 29.6.4
@ -5094,7 +5094,7 @@ __metadata:
"@joplin/fork-uslug": ^1.0.11
"@joplin/utils": ~2.13
"@types/jest": 29.5.4
"@types/node": 18.17.17
"@types/node": 18.17.18
font-awesome-filetypes: 2.1.0
fs-extra: 11.1.1
highlight.js: 11.8.0
@ -5151,7 +5151,7 @@ __metadata:
bulma: 0.9.4
bulma-prefers-dark: 0.1.0-beta.1
compare-versions: 6.1.0
dayjs: 1.11.9
dayjs: 1.11.10
formidable: 3.5.1
fs-extra: 11.1.1
gulp: 4.0.2
@ -5198,16 +5198,16 @@ __metadata:
"@types/jest": 29.5.4
"@types/markdown-it": 13.0.1
"@types/mustache": 4.2.2
"@types/node": 18.17.17
"@types/node": 18.17.18
"@types/node-fetch": 2.6.5
"@types/yargs": 17.0.24
compare-versions: 6.1.0
dayjs: 1.11.9
dayjs: 1.11.10
execa: 4.1.0
fs-extra: 11.1.1
gettext-extractor: 3.8.0
gettext-parser: 7.0.1
glob: 10.3.4
glob: 10.3.5
gulp: 4.0.2
html-entities: 1.4.0
jest: 29.6.4
@ -5220,7 +5220,7 @@ __metadata:
request: 2.88.2
rss: 1.2.2
sass: 1.66.1
sharp: 0.32.5
sharp: 0.32.6
source-map-support: 0.5.21
sqlite3: 5.1.6
typescript: 5.1.6
@ -5283,7 +5283,7 @@ __metadata:
async-mutex: 0.4.0
execa: 5.1.1
fs-extra: 11.1.1
glob: 10.3.4
glob: 10.3.5
html-entities: 1.4.0
jest: 29.6.4
moment: 2.29.4
@ -8231,10 +8231,10 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:18.17.17":
version: 18.17.17
resolution: "@types/node@npm:18.17.17"
checksum: ff28f347c77723780836f9bb2ffa6db0cd72490bfd7604397c03db31db34f1f2899e82f0aaf3e825efeb09c15bd94d076ea9aca19a1407e1b56cb4603318936c
"@types/node@npm:18.17.18":
version: 18.17.18
resolution: "@types/node@npm:18.17.18"
checksum: 59cbd906363d37017fe9ba0c08c1446e440d4d977459609c5f90b8fb7eb41f273ce8af30c5a5b5d599d7de934c1b3702bc9fc27caf8d2270e5cdb659c5232991
languageName: node
linkType: hard
@ -8383,25 +8383,14 @@ __metadata:
languageName: node
linkType: hard
"@types/react@npm:18.0.24":
version: 18.0.24
resolution: "@types/react@npm:18.0.24"
"@types/react@npm:18.2.22":
version: 18.2.22
resolution: "@types/react@npm:18.2.22"
dependencies:
"@types/prop-types": "*"
"@types/scheduler": "*"
csstype: ^3.0.2
checksum: 7d06125bac61e1c6661e5dfbeeeb56d5b6d1d4c743292faebaa6b0f30f8414c7af3cadf674923fd86e4ca14e82566ff9156cd40c56786be024600c31b97d6c03
languageName: node
linkType: hard
"@types/react@npm:18.2.21":
version: 18.2.21
resolution: "@types/react@npm:18.2.21"
dependencies:
"@types/prop-types": "*"
"@types/scheduler": "*"
csstype: ^3.0.2
checksum: ffed203bfe7aad772b8286f7953305c9181ac3a8f27d3f5400fbbc2a8e27ca8e5bbff818ee014f39ca0d19d2b3bb154e5bdbec7e232c6f80b59069375aa78349
checksum: 44289523dabaadcd3fd85689abb98f9ebcc8492d7e978348d1c986138acef4801030b279e89a19e38a6319e294bcea77559e37e0c803e4bacf2b8ae3a56ba587
languageName: node
linkType: hard
@ -14067,7 +14056,14 @@ __metadata:
languageName: node
linkType: hard
"dayjs@npm:1.11.9, dayjs@npm:^1.11.7":
"dayjs@npm:1.11.10":
version: 1.11.10
resolution: "dayjs@npm:1.11.10"
checksum: a6b5a3813b8884f5cd557e2e6b7fa569f4c5d0c97aca9558e38534af4f2d60daafd3ff8c2000fed3435cfcec9e805bcebd99f90130c6d1c5ef524084ced588c4
languageName: node
linkType: hard
"dayjs@npm:^1.11.7":
version: 1.11.9
resolution: "dayjs@npm:1.11.9"
checksum: a4844d83dc87f921348bb9b1b93af851c51e6f71fa259604809cfe1b49d1230e6b0212dab44d1cb01994c096ad3a77ea1cf18fa55154da6efcc9d3610526ac38
@ -17415,13 +17411,13 @@ __metadata:
languageName: node
linkType: hard
"follow-redirects@npm:1.15.2":
version: 1.15.2
resolution: "follow-redirects@npm:1.15.2"
"follow-redirects@npm:1.15.3":
version: 1.15.3
resolution: "follow-redirects@npm:1.15.3"
peerDependenciesMeta:
debug:
optional: true
checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190
checksum: 584da22ec5420c837bd096559ebfb8fe69d82512d5585004e36a3b4a6ef6d5905780e0c74508c7b72f907d1fa2b7bd339e613859e9c304d0dc96af2027fd0231
languageName: node
linkType: hard
@ -18344,9 +18340,9 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:10.3.4":
version: 10.3.4
resolution: "glob@npm:10.3.4"
"glob@npm:10.3.5":
version: 10.3.5
resolution: "glob@npm:10.3.5"
dependencies:
foreground-child: ^3.1.0
jackspeak: ^2.0.3
@ -18355,7 +18351,7 @@ __metadata:
path-scurry: ^1.10.1
bin:
glob: dist/cjs/src/bin.js
checksum: 176b97c124414401cb51329a93d2ba112cef8814adbed10348481916b9521b677773eee2691cb6b24d66632d8c8bb8913533f5ac4bfb2d0ef5454a1856082361
checksum: 564f4799cae48c0bcc841c88a20b539b5701c27ed5596f8623f588b3c523262d3fc20eb1ea89cab9c75b0912faf40ca5501fc835f982225d0d0599282b09e97a
languageName: node
linkType: hard
@ -22172,7 +22168,7 @@ __metadata:
"@joplin/utils": ~2.13
"@types/fs-extra": 11.0.2
"@types/jest": 29.5.4
"@types/node": 18.17.17
"@types/node": 18.17.18
"@types/proper-lockfile": ^4.1.2
aws-sdk: 2.1340.0
chalk: 4.1.2
@ -22189,7 +22185,7 @@ __metadata:
proper-lockfile: 4.1.2
read-chunk: 2.1.0
server-destroy: 1.0.1
sharp: 0.32.5
sharp: 0.32.6
sprintf-js: 1.1.3
sqlite3: 5.1.6
string-padding: 1.0.2
@ -29412,9 +29408,9 @@ __metadata:
languageName: node
linkType: hard
"react-native-paper@npm:5.10.4":
version: 5.10.4
resolution: "react-native-paper@npm:5.10.4"
"react-native-paper@npm:5.10.6":
version: 5.10.6
resolution: "react-native-paper@npm:5.10.6"
dependencies:
"@callstack/react-theme-provider": ^3.0.9
color: ^3.1.2
@ -29424,7 +29420,7 @@ __metadata:
react-native: "*"
react-native-safe-area-context: "*"
react-native-vector-icons: "*"
checksum: 28662d669b728601e7998be3a9488e3b8cf81b0a8cbe4cdc499bb88eac3de872ed5f05f94b14c905d56f3db3e9316eee696b317860e168cd0a52b90f38025cb2
checksum: 6793a0ad54ad64894fc6b3d8a2f86c06a4cd553d38d1d22776f04cc9991e33cab83cc01c2ecd600356cd5f0ea7b847660ca1487432a09f772adadc33b0fa4edf
languageName: node
linkType: hard
@ -29470,10 +29466,10 @@ __metadata:
languageName: node
linkType: hard
"react-native-share@npm:9.2.4":
version: 9.2.4
resolution: "react-native-share@npm:9.2.4"
checksum: 2d6062c5de2202afb2cf360c82283d7314033e3920980a1dc40c24e5d9208960f1225ee9a02727ff8174a290a95ede3120ce6084f5a60d0e40e138aa03683db7
"react-native-share@npm:9.4.0":
version: 9.4.0
resolution: "react-native-share@npm:9.4.0"
checksum: 0ca3afca7d352b0c107af326b63f4024fe307134eb1d922fd3e870900d3dded04aa4f3e2d581c313b4e100bc3ed83b85538a3fb05ae6de91c2797ee2c5f238b3
languageName: node
linkType: hard
@ -29550,16 +29546,16 @@ __metadata:
languageName: node
linkType: hard
"react-native-webview@npm:13.4.0":
version: 13.4.0
resolution: "react-native-webview@npm:13.4.0"
"react-native-webview@npm:13.5.1":
version: 13.5.1
resolution: "react-native-webview@npm:13.5.1"
dependencies:
escape-string-regexp: 2.0.0
invariant: 2.2.4
peerDependencies:
react: "*"
react-native: "*"
checksum: fb2b6f936d4f29eeda44e8d1187833c9ffe5a16183600e7b160a9d30052e8eaa8d604182b5e778b99c60d6247160c86dc2ee878b2073f1f775efdec89a058b1d
checksum: f7536d0832c401d75c6f92bb997daeb7fe9de82471bf50bbc895421b82cd355da476dc393b186e9256d5759c345f5c97f2f8185fd079d3cb6fc174ca8ee70ba5
languageName: node
linkType: hard
@ -29761,9 +29757,9 @@ __metadata:
languageName: node
linkType: hard
"react-select@npm:5.7.4":
version: 5.7.4
resolution: "react-select@npm:5.7.4"
"react-select@npm:5.7.5":
version: 5.7.5
resolution: "react-select@npm:5.7.5"
dependencies:
"@babel/runtime": ^7.12.0
"@emotion/cache": ^11.4.0
@ -29777,7 +29773,7 @@ __metadata:
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
checksum: ca72941ad1d2c578ec04c09ed3deb7e373f987e589f403fadedc6fcc3e29935b5425ec4d2628f0fe58c21319bcaf153c0d0172432e09fc6423da869d848de757
checksum: 88f2d94c4a6778df525a9fb5d7acac1bf34821f6efcfdc5927ec608f5f933cf3f47e1c4e4fd3b92d7b2ba1d91e44595d45ac4e2fd7528ba420086008ac5a81cf
languageName: node
linkType: hard
@ -30971,7 +30967,7 @@ __metadata:
eslint-plugin-react: 7.33.2
execa: 5.1.1
fs-extra: 11.1.1
glob: 10.3.4
glob: 10.3.5
gulp: 4.0.2
http-server: 14.1.1
husky: 3.1.0
@ -31503,9 +31499,9 @@ __metadata:
languageName: node
linkType: hard
"sharp@npm:0.32.5":
version: 0.32.5
resolution: "sharp@npm:0.32.5"
"sharp@npm:0.32.6":
version: 0.32.6
resolution: "sharp@npm:0.32.6"
dependencies:
color: ^4.2.3
detect-libc: ^2.0.2
@ -31516,7 +31512,7 @@ __metadata:
simple-get: ^4.0.1
tar-fs: ^3.0.4
tunnel-agent: ^0.6.0
checksum: 3cd6dc037c9ba126a30af90ac94043c4418bbb4228e15fd446638ff43fc9b14eabb553037988e484162c318f7baff21d896a5bef7dcc453f608e247d468f41e0
checksum: 0cca1d16b1920800c0e22d27bc6305f4c67c9ebe44f67daceb30bf645ae39e7fb7dfbd7f5d6cd9f9eebfddd87ac3f7e2695f4eb906d19b7a775286238e6a29fc
languageName: node
linkType: hard