Doc: Add support for localisation using Crowdin

pull/9179/head
Laurent Cozic 2023-10-31 11:29:09 +00:00
parent 3fe473cdd3
commit 66fe2f9390
14 changed files with 134 additions and 25 deletions

1
.gitignore vendored
View File

@ -51,6 +51,7 @@ lerna-debug.log
.env .env
docs/**/*.mustache docs/**/*.mustache
.idea .idea
/readme/i18n
# Yarn stuff # Yarn stuff
# https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored

23
crowdin.yml Normal file
View File

@ -0,0 +1,23 @@
project_id: '624298'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
files:
- source: /readme/**/*
translation: /readme/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
ignore:
- /readme/_i18n
- /readme/i18n
- /readme/about/changelog
- /readme/about/stats.md
- /readme/api
- /readme/dev
- /readme/news
- /readme/cla.md
- /readme/connection_check.md
- /readme/privacy.md
- /**/*.yml
- /**/*.json
- /**/*.png
- /**/*.jpg

View File

@ -182,6 +182,11 @@
"docs/images/flags": true, "docs/images/flags": true,
"lerna-debug.log": true, "lerna-debug.log": true,
"node_modules/": true, "node_modules/": true,
"packages/doc-builder/build": true,
"packages/doc-builder/help": true,
"packages/doc-builder/news": true,
"packages/doc-builder/i18n": true,
"readme/i18n": true,
"packages/app-cli/**/*.*~": true, "packages/app-cli/**/*.*~": true,
"packages/app-cli/**/*.mo": true, "packages/app-cli/**/*.mo": true,
"packages/app-cli/**/build/": true, "packages/app-cli/**/build/": true,

View File

@ -12,31 +12,34 @@
"node": ">=16" "node": ">=16"
}, },
"scripts": { "scripts": {
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 --topological run build && yarn run tsc",
"buildSequential": "yarn workspaces foreach --verbose --interlaced --topological run build && yarn run tsc",
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md", "buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
"buildCommandIndex": "node packages/tools/gulp/tasks/buildCommandIndexRun.js", "buildCommandIndex": "node packages/tools/gulp/tasks/buildCommandIndexRun.js",
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 --topological run build && yarn run tsc",
"buildPluginDoc": "cd packages/generate-plugin-doc && yarn run buildPluginDoc_", "buildPluginDoc": "cd packages/generate-plugin-doc && yarn run buildPluginDoc_",
"updateMarkdownDoc": "node ./packages/tools/updateMarkdownDoc", "buildSequential": "yarn workspaces foreach --verbose --interlaced --topological run build && yarn run tsc",
"updateNews": "node ./packages/tools/website/updateNews", "buildServerDocker": "node packages/tools/buildServerDocker.js",
"postPreReleasesToForum": "node ./packages/tools/postPreReleasesToForum",
"buildSettingJsonSchema": "yarn workspace joplin start settingschema ../../../joplin-website/docs/schema/settings.json", "buildSettingJsonSchema": "yarn workspace joplin start settingschema ../../../joplin-website/docs/schema/settings.json",
"buildTranslations": "node packages/tools/build-translation.js", "buildTranslations": "node packages/tools/build-translation.js",
"buildWebsiteTranslations": "node packages/tools/website/buildTranslations.js",
"buildWebsite": "node ./packages/tools/website/processDocs.js --env prod && node ./packages/tools/website/build.js && yarn run buildPluginDoc && yarn run buildSettingJsonSchema", "buildWebsite": "node ./packages/tools/website/processDocs.js --env prod && node ./packages/tools/website/build.js && yarn run buildPluginDoc && yarn run buildSettingJsonSchema",
"checkLibPaths": "node ./packages/tools/checkLibPaths.js", "buildWebsiteTranslations": "node packages/tools/website/buildTranslations.js",
"checkIgnoredFiles": "node ./packages/tools/checkIgnoredFiles.js", "checkIgnoredFiles": "node ./packages/tools/checkIgnoredFiles.js",
"checkLibPaths": "node ./packages/tools/checkLibPaths.js",
"circularDependencyCheck": "madge --warning --circular --extensions js ./", "circularDependencyCheck": "madge --warning --circular --extensions js ./",
"clean": "npm run clean --workspaces --if-present && node packages/tools/clean && yarn cache clean", "clean": "npm run clean --workspaces --if-present && node packages/tools/clean && yarn cache clean",
"crowdin": "crowdin",
"crowdinDownload": "crowdin download",
"crowdinUpload": "crowdin upload",
"cspell": "cspell",
"dependencyTree": "madge", "dependencyTree": "madge",
"generateDatabaseTypes": "node packages/tools/generate-database-types", "generateDatabaseTypes": "node packages/tools/generate-database-types",
"linkChecker": "linkchecker https://joplinapp.org", "linkChecker": "linkchecker https://joplinapp.org",
"linter-ci": "eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx", "linter-ci": "eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter-interactive": "eslint-interactive --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter-precommit": "eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx", "linter-precommit": "eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter": "eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx", "linter": "eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter-interactive": "eslint-interactive --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"packageJsonLint": "node ./packages/tools/packageJsonLint.js", "packageJsonLint": "node ./packages/tools/packageJsonLint.js",
"postinstall": "gulp build", "postinstall": "gulp build",
"postPreReleasesToForum": "node ./packages/tools/postPreReleasesToForum",
"publishAll": "git pull && yarn run buildParallel && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll", "publishAll": "git pull && yarn run buildParallel && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
"releaseAndroid": "PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" node packages/tools/release-android.js", "releaseAndroid": "PATH=\"/usr/local/opt/openjdk@11/bin:$PATH\" node packages/tools/release-android.js",
"releaseAndroidClean": "node packages/tools/release-android.js", "releaseAndroidClean": "node packages/tools/release-android.js",
@ -47,15 +50,15 @@
"releasePluginGenerator": "node packages/tools/release-plugin-generator.js", "releasePluginGenerator": "node packages/tools/release-plugin-generator.js",
"releasePluginRepoCli": "node packages/tools/release-plugin-repo-cli.js", "releasePluginRepoCli": "node packages/tools/release-plugin-repo-cli.js",
"releaseServer": "node packages/tools/release-server.js", "releaseServer": "node packages/tools/release-server.js",
"cspell": "cspell", "setupNewRelease": "node ./packages/tools/setupNewRelease",
"spellcheck": "node packages/tools/spellcheck.js", "spellcheck": "node packages/tools/spellcheck.js",
"tagServerLatest": "node packages/tools/tagServerLatest.js", "tagServerLatest": "node packages/tools/tagServerLatest.js",
"buildServerDocker": "node packages/tools/buildServerDocker.js",
"setupNewRelease": "node ./packages/tools/setupNewRelease",
"test-ci": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test-ci", "test-ci": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test-ci",
"test": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test", "test": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test",
"tsc": "yarn workspaces foreach --parallel --verbose --interlaced run tsc", "tsc": "yarn workspaces foreach --parallel --verbose --interlaced run tsc",
"updateIgnored": "node packages/tools/gulp/tasks/updateIgnoredTypeScriptBuildRun.js", "updateIgnored": "node packages/tools/gulp/tasks/updateIgnoredTypeScriptBuildRun.js",
"updateMarkdownDoc": "node ./packages/tools/updateMarkdownDoc",
"updateNews": "node ./packages/tools/website/updateNews",
"updatePluginTypes": "./packages/generator-joplin/updateTypes.sh", "updatePluginTypes": "./packages/generator-joplin/updateTypes.sh",
"watch": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 999 run watch", "watch": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 999 run watch",
"watchWebsite": "nodemon --delay 1 --watch Assets/WebsiteAssets --watch packages/tools/website --watch packages/tools/website/utils --watch packages/doc-builder/build --ext md,ts,js,mustache,css,tsx,gif,png,svg --exec \"node packages/tools/website/build.js && http-server --port 8077 ../joplin-website/docs -a localhost\"" "watchWebsite": "nodemon --delay 1 --watch Assets/WebsiteAssets --watch packages/tools/website --watch packages/tools/website/utils --watch packages/doc-builder/build --ext md,ts,js,mustache,css,tsx,gif,png,svg --exec \"node packages/tools/website/build.js && http-server --port 8077 ../joplin-website/docs -a localhost\""
@ -66,6 +69,7 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@crowdin/cli": "3",
"@joplin/utils": "~2.12", "@joplin/utils": "~2.12",
"@seiyab/eslint-plugin-react-hooks": "4.5.1-beta.0", "@seiyab/eslint-plugin-react-hooks": "4.5.1-beta.0",
"@typescript-eslint/eslint-plugin": "6.0.0", "@typescript-eslint/eslint-plugin": "6.0.0",

View File

@ -8,11 +8,12 @@
.docusaurus .docusaurus
.cache-loader .cache-loader
# Docs are auto-generated using processDoc.js # Folders that are auto-generated using processDocs.js
/docs /docs
/help /help
/blog /blog
/news /news
/i18n
# Misc # Misc
.DS_Store .DS_Store

View File

@ -27,7 +27,7 @@ const config = {
// to replace "en" with "zh-Hans". // to replace "en" with "zh-Hans".
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: 'en',
locales: ['en'], locales: ['en', 'fr'],
}, },
plugins: [ plugins: [
@ -116,6 +116,10 @@ const config = {
className: 'navbar-custom-buttons sponsor-button', className: 'navbar-custom-buttons sponsor-button',
target: '_self', target: '_self',
}, },
{
type: 'localeDropdown',
position: 'right',
},
], ],
}, },
footer: { footer: {

View File

@ -40,11 +40,11 @@ yarn install
# to change after installation. # to change after installation.
git reset --hard git reset --hard
JOPLIN_GITHUB_OAUTH_TOKEN=$JOPLIN_GITHUB_OAUTH_TOKEN yarn run updateMarkdownDoc JOPLIN_GITHUB_OAUTH_TOKEN=$JOPLIN_GITHUB_OAUTH_TOKEN yarn updateMarkdownDoc
# Automatically update certain forum posts # Automatically update certain forum posts
yarn run updateNews $DISCOURSE_API_KEY $DISCOURSE_USERNAME yarn updateNews $DISCOURSE_API_KEY $DISCOURSE_USERNAME
yarn run postPreReleasesToForum $DISCOURSE_API_KEY $DISCOURSE_USERNAME yarn postPreReleasesToForum $DISCOURSE_API_KEY $DISCOURSE_USERNAME
# We commit and push the change. It will be a noop if nothing was actually # We commit and push the change. It will be a noop if nothing was actually
# changed # changed
@ -67,7 +67,8 @@ git checkout master
git pull --rebase git pull --rebase
cd "$JOPLIN_ROOT_DIR" cd "$JOPLIN_ROOT_DIR"
yarn run buildWebsite yarn crowdinDownload
yarn buildWebsite
cd "$JOPLIN_WEBSITE_ROOT_DIR" cd "$JOPLIN_WEBSITE_ROOT_DIR"
git add -A git add -A

View File

@ -1,5 +1,5 @@
import { readFile } from 'fs/promises'; import { readFile } from 'fs/promises';
import { processMarkdownDoc } from './processDocs'; import { processMarkdownDoc, processUrls } from './processDocs';
import { basename } from 'path'; import { basename } from 'path';
import { readdirSync } from 'fs'; import { readdirSync } from 'fs';
@ -47,4 +47,34 @@ ${actual}`);
expect(actual).toBe(expected); expect(actual).toBe(expected);
}); });
test.each([
[
'',
'',
],
[
'[synchronisation set to Joplin Cloud](https://github.com/laurent22/joplin/blob/dev/readme/apps/sync/joplin_cloud.md)',
'[synchronisation set to Joplin Cloud](/help/apps/sync/joplin_cloud)',
],
[
'The notes can be securely [synchronised](https://github.com/laurent22/joplin/blob/dev/readme/apps/sync/index.md) using [end-to-end encryption](https://github.com/laurent22/joplin/blob/dev/readme/apps/sync/e2ee.md)',
'The notes can be securely [synchronised](/help/apps/sync) using [end-to-end encryption](/help/apps/sync/e2ee)',
],
[
'The notes can be securely [synchronised](https://github.com/laurent22/joplin/blob/dev/readme/apps/sync/index.md) using [end-to-end encryption](https://github.com/laurent22/joplin/blob/dev/readme/apps/sync/e2ee.md)',
'The notes can be securely [synchronised](/help/apps/sync) using [end-to-end encryption](/help/apps/sync/e2ee)',
],
[
'[Configuration screen](https://github.com/laurent22/joplin/blob/dev/readme/apps/config_screen.md) [Configuration screen](https://github.com/laurent22/joplin/blob/dev/readme/apps/config_screen.md)',
'[Configuration screen](/help/apps/config_screen) [Configuration screen](/help/apps/config_screen)',
],
[
'In [command-line mode](https://github.com/laurent22/joplin/blob/dev/readme/apps/terminal.md#command-line-mode), type `import --format md /path/to/file.md`',
'In [command-line mode](/help/apps/terminal#command-line-mode), type `import --format md /path/to/file.md`',
],
])('should process URLs', (input, expected) => {
const actual = processUrls(input);
expect(actual).toBe(expected);
});
}); });

View File

@ -313,10 +313,10 @@ const resolveBlockQuotes = (output: string): string => {
return newOutput.join('\n'); return newOutput.join('\n');
}; };
const processUrls = (md: string) => { export const processUrls = (md: string) => {
md = md md = md
.replace(/https:\/\/github.com\/laurent22\/joplin\/blob\/dev\/readme\/(.*)\/index\.md/g, '/help/$1') .replace(/https:\/\/github.com\/laurent22\/joplin\/blob\/dev\/readme\/(.*?)\/index\.md/g, '/help/$1')
.replace(/https:\/\/github.com\/laurent22\/joplin\/blob\/dev\/readme\/(.*)\.md/g, '/help/$1'); .replace(/https:\/\/github.com\/laurent22\/joplin\/blob\/dev\/readme\/(.*?)\.md/g, '/help/$1');
return md; return md;
}; };
@ -479,6 +479,7 @@ async function main() {
await processDocFiles(readmeDir, destHelpDir, [ await processDocFiles(readmeDir, destHelpDir, [
`${readmeDir}/_i18n`, `${readmeDir}/_i18n`,
`${readmeDir}/i18n`,
`${readmeDir}/cla.md`, `${readmeDir}/cla.md`,
`${readmeDir}/download.md`, `${readmeDir}/download.md`,
`${readmeDir}/faq_joplin_cloud.md`, `${readmeDir}/faq_joplin_cloud.md`,
@ -495,6 +496,14 @@ async function main() {
await processDocFiles(`${readmeDir}/news`, newsDestDir, [], newsContext); await processDocFiles(`${readmeDir}/news`, newsDestDir, [], newsContext);
await deleteUnprocessedFiles(newsDestDir, newsContext.processedFiles); await deleteUnprocessedFiles(newsDestDir, newsContext.processedFiles);
if (await pathExists(`${readmeDir}/i18n`)) {
const localeContext: Context = { donateLinks };
await processDocFiles(`${readmeDir}/i18n`, `${docBuilderDir}/i18n`, [], localeContext);
await deleteUnprocessedFiles(`${docBuilderDir}/i18n`, localeContext.processedFiles);
} else {
console.info('i18n folder is missing - skipping it');
}
await copyFile(`${rootDir}/Assets/WebsiteAssets/images`, `${docBuilderDir}/static/images`); await copyFile(`${rootDir}/Assets/WebsiteAssets/images`, `${docBuilderDir}/static/images`);
if (config.docusaurusBuildEnabled) { if (config.docusaurusBuildEnabled) {

View File

@ -10,13 +10,13 @@ This feature is available for Pro and Teams members.
### Desktop ### Desktop
To copy your Joplin Cloud email address you will need to navigate to the [Configuration screen](https://github.com/laurent22/joplin/blob/dev/readme/apps/config_screen.md) and locate to the Joplin Cloud tab. You will need to have your [synchronisation set to Joplin Cloud](https://github.com/laurent22/joplin/blob/dev/readme/welcome/3_synchronising_your_notes.md#setting-up-joplin-cloud-synchronisation) To copy your Joplin Cloud email address you will need to navigate to the [Configuration screen](https://github.com/laurent22/joplin/blob/dev/readme/apps/config_screen.md) and locate to the Joplin Cloud tab. You will need to have your [synchronisation set to Joplin Cloud](https://github.com/laurent22/joplin/blob/dev/readme/apps/sync/joplin_cloud.md)
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/email_to_note/desktop.png" width="80%"/> <img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/email_to_note/desktop.png" width="80%"/>
### Mobile ### Mobile
To copy your Joplin Cloud email address you will need to navigate to the [Configuration screen](https://github.com/laurent22/joplin/blob/dev/readme/apps/config_screen.md) and find the Joplin Cloud section. You will need to have your [synchronisation set to Joplin Cloud](https://github.com/laurent22/joplin/blob/dev/readme/welcome/3_synchronising_your_notes.md#setting-up-joplin-cloud-synchronisation) To copy your Joplin Cloud email address you will need to navigate to the [Configuration screen](https://github.com/laurent22/joplin/blob/dev/readme/apps/config_screen.md) and find the Joplin Cloud section. You will need to have your [synchronisation set to Joplin Cloud](https://github.com/laurent22/joplin/blob/dev/readme/apps/sync/joplin_cloud.md)
<img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/email_to_note/mobile.png" width="50%"/> <img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/email_to_note/mobile.png" width="50%"/>

View File

@ -21,10 +21,13 @@ In the **terminal application**, in [command-line mode](https://github.com/laure
Joplin can import notes from plain Markdown file. You can either import a complete directory of Markdown files or individual files. Joplin can import notes from plain Markdown file. You can either import a complete directory of Markdown files or individual files.
In the **desktop application**: In the **desktop application**:
* **File import**: Go to File > Import > MD - Markdown (file) and select the Markdown file. This file will then be imported to the currently selected Notebook. * **File import**: Go to File > Import > MD - Markdown (file) and select the Markdown file. This file will then be imported to the currently selected Notebook.
* **Directory import**: Go to File > Import > MD - Markdown (directory) and select the top level of the directory that is being imported. Directory (folder) structure will be preserved in the Notebook > Subnotebook > Note structure within Joplin. * **Directory import**: Go to File > Import > MD - Markdown (directory) and select the top level of the directory that is being imported. Directory (folder) structure will be preserved in the Notebook > Subnotebook > Note structure within Joplin.
In the **terminal application**, in [command-line mode](https://github.com/laurent22/joplin/blob/dev/readme/apps/terminal.md#command-line-mode), type `import --format md /path/to/file.md` or `import --format md /path/to/directory/`. In the **terminal application**, in [command-line mode](https://github.com/laurent22/joplin/blob/dev/readme/apps/terminal.md#command-line-mode):
Type `import --format md /path/to/file.md` or `import --format md /path/to/directory/`.
### Importing from other applications ### Importing from other applications

View File

@ -0,0 +1,5 @@
# Joplin Cloud synchronisation
[Joplin Cloud](https://joplinapp.org/plans/) is a web service specifically designed for Joplin. Besides synchronising your data, it also allows you to publish a note to the internet, or share a notebook with your friends, family or colleagues. Joplin Cloud, compared to other services, also features a number of performance improvements allowing for faster synchronisation.
To use it, go to the [Configuration screen](https://github.com/laurent22/joplin/blob/dev/readme/apps/config_screen.md), then to the Synchronisation section. In the list of sync targets, select "Joplin Cloud". Enter your email and password, and you're ready to use Joplin Cloud.

View File

@ -6,7 +6,7 @@ Joplin allows you to synchronise your data using various file hosting services.
[Joplin Cloud](https://joplinapp.org/plans/) is a web service specifically designed for Joplin. Besides synchronising your data, it also allows you to publish a note to the internet, or share a notebook with your friends, family or colleagues. Joplin Cloud, compared to other services, also features a number of performance improvements allowing for faster synchronisation. [Joplin Cloud](https://joplinapp.org/plans/) is a web service specifically designed for Joplin. Besides synchronising your data, it also allows you to publish a note to the internet, or share a notebook with your friends, family or colleagues. Joplin Cloud, compared to other services, also features a number of performance improvements allowing for faster synchronisation.
To use it, go to the config screen, then to the Synchronisation section. In the list of sync target, select "Joplin Cloud". Enter your email and password, and you're ready to use Joplin Cloud. To use it, go to the config screen, then to the Synchronisation section. In the list of sync targets, select "Joplin Cloud". Enter your email and password, and you're ready to use Joplin Cloud.
## Setting up Dropbox synchronisation ## Setting up Dropbox synchronisation

View File

@ -4225,6 +4225,21 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@crowdin/cli@npm:3":
version: 3.15.0
resolution: "@crowdin/cli@npm:3.15.0"
dependencies:
command-exists-promise: ^2.0.2
node-fetch: 2.6.7
shelljs: ^0.8.4
tar: ^4.4.8
yauzl: ^2.10.0
bin:
crowdin: jdeploy-bundle/jdeploy.js
checksum: 232f455d9ba44357d8f107ed7471b642e20ae724ebe8ce28175219223225f1c6ca77d7c69b220fe45db7ce1f06774d5d9a5a46100e5aa948a89743ea0eb6b7c1
languageName: node
linkType: hard
"@cspell/cspell-bundled-dicts@npm:^5.21.2": "@cspell/cspell-bundled-dicts@npm:^5.21.2":
version: 5.21.2 version: 5.21.2
resolution: "@cspell/cspell-bundled-dicts@npm:5.21.2" resolution: "@cspell/cspell-bundled-dicts@npm:5.21.2"
@ -15501,6 +15516,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"command-exists-promise@npm:^2.0.2":
version: 2.0.2
resolution: "command-exists-promise@npm:2.0.2"
checksum: f6674abbc7d9cc704255999adb42e8b8fe165d941de207d1df8177e1ccea2afb9ff57aad7a70f9235056dd317e45b31f9726ecb2c39826f9a1f98a50f4de1b31
languageName: node
linkType: hard
"command-exists@npm:^1.2.8": "command-exists@npm:^1.2.8":
version: 1.2.9 version: 1.2.9
resolution: "command-exists@npm:1.2.9" resolution: "command-exists@npm:1.2.9"
@ -36493,6 +36515,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "root@workspace:." resolution: "root@workspace:."
dependencies: dependencies:
"@crowdin/cli": 3
"@joplin/utils": ~2.12 "@joplin/utils": ~2.12
"@seiyab/eslint-plugin-react-hooks": 4.5.1-beta.0 "@seiyab/eslint-plugin-react-hooks": 4.5.1-beta.0
"@types/fs-extra": 11.0.2 "@types/fs-extra": 11.0.2