From bd268e3d0e2f69cb95723a3a87f2b651964ec64d Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Thu, 13 Feb 2020 12:45:24 +0100 Subject: [PATCH] Add gettext-based translations Signed-off-by: Christoph Wurst --- .eslintrc.js | 3 +- .github/workflows/l10n.yml | 19 ++++ .github/workflows/transifex.yml | 21 ++++ .npmignore | 4 +- build/extract-l10n.js | 23 ++++ l10n/messages.pot | 48 ++++++++ package-lock.json | 104 ++++++++++++++++++ package.json | 4 + .../AppNavigationSettings.vue | 4 +- src/components/AppSidebar/AppSidebar.vue | 4 +- src/components/ColorPicker/ColorPicker.vue | 4 +- src/components/Modal/Modal.vue | 12 +- src/components/Multiselect/Multiselect.vue | 5 +- .../MultiselectTags/MultiselectTags.vue | 13 ++- src/l10n.js | 9 ++ src/mixins/l10n.js | 8 ++ tests/setup.js | 16 +-- webpack.common.js | 30 ++++- 18 files changed, 294 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/l10n.yml create mode 100644 .github/workflows/transifex.yml create mode 100644 build/extract-l10n.js create mode 100644 l10n/messages.pot create mode 100644 src/l10n.js create mode 100644 src/mixins/l10n.js diff --git a/.eslintrc.js b/.eslintrc.js index 10d03feb57..e0bb82afc0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,7 +7,8 @@ module.exports = { jest: true }, globals: { - SCOPE_VERSION: true + SCOPE_VERSION: true, + TRANSLATIONS: true }, parserOptions: { parser: 'babel-eslint', diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml new file mode 100644 index 0000000000..8a8abebf22 --- /dev/null +++ b/.github/workflows/l10n.yml @@ -0,0 +1,19 @@ +name: L10n +on: pull_request + +jobs: + l10n-extract-check: + runs-on: ubuntu-latest + name: Pot check + steps: + - uses: actions/checkout@master + - name: Set up Node + uses: actions/setup-node@v1 + with: + node-version: 12.x + - name: npm install + run: npm ci + - name: extract l10n files + run: npm run l10n:extract + - name: Check l10n file changes + run: bash -c "[[ ! \"`git status --porcelain l10n`\" ]] || ( echo 'Uncommited l10n changes. Run `npm l10n:extract`.' && git status && exit 1 )" diff --git a/.github/workflows/transifex.yml b/.github/workflows/transifex.yml new file mode 100644 index 0000000000..abc14feafd --- /dev/null +++ b/.github/workflows/transifex.yml @@ -0,0 +1,21 @@ +name: Transifex +on: pull_request + +jobs: + approve: + runs-on: ubuntu-latest + name: Approve + steps: + - uses: hmarr/auto-approve-action@v2.0.0 + if: github.actor == 'Transifex-localization-platform[bot]' || github.actor == 'transifex-integration[bot]' + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + automerge: + runs-on: ubuntu-latest + name: Auto-merge + steps: + - uses: "pascalgn/automerge-action@ecb16453ce68e85b1e23596c8caa7e7499698a84" + if: github.actor == 'Transifex-localization-platform[bot]' || github.actor == 'transifex-integration[bot]' + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MERGE_LABELS: "" \ No newline at end of file diff --git a/.npmignore b/.npmignore index b512c09d47..11cf6267fd 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,3 @@ -node_modules \ No newline at end of file +/build +/l10n +/node_modules diff --git a/build/extract-l10n.js b/build/extract-l10n.js new file mode 100644 index 0000000000..dfba88c703 --- /dev/null +++ b/build/extract-l10n.js @@ -0,0 +1,23 @@ +const { GettextExtractor, JsExtractors } = require('gettext-extractor'); + +let extractor = new GettextExtractor(); + +extractor + .createJsParser([ + JsExtractors.callExpression('t', { + arguments: { + text: 0, + } + }), + JsExtractors.callExpression('n', { + arguments: { + text: 1, + textPlural: 2, + } + }), + ]) + .parseFilesGlob('./src/**/*.@(ts|js|vue)'); + +extractor.savePotFile('./l10n/messages.pot'); + +extractor.printStats(); diff --git a/l10n/messages.pot b/l10n/messages.pot new file mode 100644 index 0000000000..147d5bb643 --- /dev/null +++ b/l10n/messages.pot @@ -0,0 +1,48 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" + +#: src/components/MultiselectTags/MultiselectTags.vue:169 +msgid "{tag} (invisible)" +msgstr "" + +#: src/components/MultiselectTags/MultiselectTags.vue:172 +msgid "{tag} (restricted)" +msgstr "" + +#: src/components/ColorPicker/ColorPicker.vue:145 +msgid "Choose" +msgstr "" + +#: src/components/Modal/Modal.vue:109 +msgid "Close" +msgstr "" + +#: src/components/Modal/Modal.vue:154 +msgid "Next" +msgstr "" + +#: src/components/Multiselect/Multiselect.vue:169 +#: src/components/MultiselectTags/MultiselectTags.vue:78 +msgid "No results" +msgstr "" + +#: src/components/Modal/Modal.vue:290 +msgid "Pause slideshow" +msgstr "" + +#: src/components/Modal/Modal.vue:134 +msgid "Previous" +msgstr "" + +#: src/components/MultiselectTags/MultiselectTags.vue:100 +msgid "Select a tag" +msgstr "" + +#: src/components/AppNavigationSettings/AppNavigationSettings.vue:53 +msgid "Settings" +msgstr "" + +#: src/components/Modal/Modal.vue:290 +msgid "Start slideshow" +msgstr "" diff --git a/package-lock.json b/package-lock.json index 6221a749f6..b6ebf2fd1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3022,6 +3022,15 @@ } } }, + "@nextcloud/l10n": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@nextcloud/l10n/-/l10n-1.1.0.tgz", + "integrity": "sha512-MywbaSb31JH5LNUsC98RrMwzHdsjDELf+nL5BVtHBQWq2r0cDP0nPd7Ve+knRVdGMegnigXW+F2VXbxFBLb6mQ==", + "requires": { + "core-js": "3.6.4", + "node-gettext": "^2.0.0" + } + }, "@nextcloud/router": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-1.0.0.tgz", @@ -3186,6 +3195,12 @@ "integrity": "sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==", "dev": true }, + "@types/parse5": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.2.tgz", + "integrity": "sha512-BOl+6KDs4ItndUWUFchy3aEqGdHhw0BC4Uu+qoDonN/f0rbUnJbm71Ulj8Tt9jLFRaAxPLKvdS1bBLfx1qXR9g==", + "dev": true + }, "@types/semver": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.0.tgz", @@ -5970,6 +5985,12 @@ } } }, + "css-selector-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.3.0.tgz", + "integrity": "sha1-XxrUPi2O77/cME/NOaUhZklD4+s=", + "dev": true + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -6545,6 +6566,15 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -8640,6 +8670,61 @@ "assert-plus": "^1.0.0" } }, + "gettext-extractor": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/gettext-extractor/-/gettext-extractor-3.5.2.tgz", + "integrity": "sha512-4fJViJvAkWBUV8BHwAaY2T1oirsIEAYgYfYm/+x/gF2xWxOXgn4f7Cjsdq+xuuoA9HZga+fx2PIDP+07zbmP6g==", + "dev": true, + "requires": { + "@types/glob": "5 - 7", + "@types/parse5": "^5", + "css-selector-parser": "^1.3", + "glob": "5 - 7", + "parse5": "^5", + "pofile": "1.0.x", + "typescript": "2 - 3" + } + }, + "gettext-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-4.0.2.tgz", + "integrity": "sha512-JPCBpGzm01te+nTenJwWqKDzixYPY4pInedixpcMl4GPEJeia/cH2TJCh32IggDrrLYrzqA8OitXZLpBdrx4Gg==", + "dev": true, + "requires": { + "content-type": "^1.0.4", + "encoding": "^0.1.12", + "readable-stream": "^3.4.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", + "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + } + } + }, "github-slugger": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.1.tgz", @@ -12780,6 +12865,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -13589,6 +13679,14 @@ "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", "dev": true }, + "node-gettext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-gettext/-/node-gettext-2.0.0.tgz", + "integrity": "sha1-8dwSN83FRvUVk9o0AwS4vrpbhSU=", + "requires": { + "lodash.get": "^4.4.2" + } + }, "node-gyp": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", @@ -14702,6 +14800,12 @@ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", "dev": true }, + "pofile": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pofile/-/pofile-1.0.11.tgz", + "integrity": "sha512-Vy9eH1dRD9wHjYt/QqXcTz+RnX/zg53xK+KljFSX30PvdDMb2z+c6uDUeblUGqqJgz3QFsdlA0IJvHziPmWtQg==", + "dev": true + }, "popper.js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", diff --git a/package.json b/package.json index 8003bdf278..978a30583b 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dev": "webpack --config webpack.dev.js", "watch": "webpack --progress --watch --config webpack.dev.js", "build": "NODE_ENV=production webpack --progress --hide-modules --config webpack.prod.js", + "l10n:extract": "node build/extract-l10n.js", "lint": "eslint --ext .js,.vue src", "lint:fix": "eslint --ext .js,.vue src --fix", "test": "jest --verbose", @@ -33,6 +34,7 @@ ], "dependencies": { "@nextcloud/axios": "^1.1.0", + "@nextcloud/l10n": "^1.1.0", "@nextcloud/router": "^1.0.0", "core-js": "^3.4.4", "escape-html": "^1.0.3", @@ -70,6 +72,8 @@ "eslint-plugin-standard": "^4.0.0", "eslint-plugin-vue": "^6.0.1", "file-loader": "^5.0.2", + "gettext-extractor": "^3.5.2", + "gettext-parser": "^4.0.2", "iconfont-plugin-webpack": "^1.1.4", "jest": "^25.1.0", "jest-environment-jsdom-sixteen": "^1.0.0", diff --git a/src/components/AppNavigationSettings/AppNavigationSettings.vue b/src/components/AppNavigationSettings/AppNavigationSettings.vue index 2ee9c434ca..fc1b0d086a 100644 --- a/src/components/AppNavigationSettings/AppNavigationSettings.vue +++ b/src/components/AppNavigationSettings/AppNavigationSettings.vue @@ -40,6 +40,7 @@