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:
commit
3ac25104c3
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -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})`;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>',
|
||||
},
|
||||
|
@ -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(/{{> /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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
144
yarn.lock
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user