diff --git a/packages/i18n/README.md b/packages/i18n/README.md new file mode 100644 index 0000000000..f8ad64372a --- /dev/null +++ b/packages/i18n/README.md @@ -0,0 +1,55 @@ +# i18n + +## Usages + +- Update missing translations into the base resources, a.k.a the `src/resources/en.json` +- Replace literal text with translation keys + +```tsx +import { useTranslation } from '@affine/i18n'; + +// src/resources/en.json +// { +// 'Text': 'some text', +// 'Switch to language': 'Switch to {{language}}', // <- you can interpolation by curly brackets +// }; + +const App = () => { + const { t } = useTranslation(); + + const changeLanguage = (language: string) => { + i18n.changeLanguage(language); + }; + + return ( +
+
{t('Text')}
+ + + +
+ ); +}; +``` + +## How to sync translations manually + +- Set token as environment variable + +```shell +export TOLGEE_API_KEY=tgpak_XXXXXXX +``` + +- Run the `sync-languages:check` to check all languages +- Run the `sync-languages` script to add new keys to the tolgee platform + +## References + +- [AFFiNE | Tolgee](https://i18n.affine.pro/) +- [Tolgee Documentation](https://tolgee.io/docs/) +- [i18next](https://www.i18next.com/) +- [react-i18next](https://react.i18next.com/) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index cc559528e3..eb6c30a1a1 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -10,7 +10,10 @@ ".": "./dist/src/index.js" }, "scripts": { - "build": "tsc --project ./tsconfig.json" + "build": "tsc --project ./tsconfig.json", + "sync-languages": "NODE_OPTIONS=--experimental-fetch ts-node-esm src/scripts/sync.ts", + "sync-languages:check": "pnpm run sync-languages --check", + "download-resources": "NODE_OPTIONS=--experimental-fetch ts-node-esm src/scripts/download.ts" }, "keywords": [], "repository": { @@ -24,6 +27,7 @@ }, "devDependencies": { "@types/prettier": "^2.7.2", + "ts-node": "^10.9.1", "typescript": "^4.8.4" } } diff --git a/packages/i18n/src/resources/bn.json b/packages/i18n/src/resources/bn.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/packages/i18n/src/resources/bn.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/i18n/src/resources/en.json b/packages/i18n/src/resources/en.json index 98f2d4507a..2831a8aafe 100644 --- a/packages/i18n/src/resources/en.json +++ b/packages/i18n/src/resources/en.json @@ -1,11 +1,8 @@ { "Quick search": "Quick search", - "Quick search placeholder": "Quick Search...", - "Quick search placeholder2": "Search in {{workspace}}", "All pages": "All pages", "Favourites": "Favourites", "No item": "No item", - "Settings": "Settings", "Import": "Import", "Trash": "Trash", "New Page": "New Page", @@ -14,8 +11,6 @@ "Find results": "Find {{number}} results", "Collapse sidebar": "Collapse sidebar", "Expand sidebar": "Expand sidebar", - "Removed from Favourites": "Removed from Favourites", - "Remove from favourites": "Remove from favourites", "Added to Favourites": "Added to Favourites", "Add to favourites": "Add to favourites", "Paper": "Paper", @@ -37,7 +32,6 @@ "Delete page?": "Delete page?", "Delete permanently?": "Delete permanently?", "will be moved to Trash": "{{title}} will be moved to Trash", - "Once deleted, you can't undo this action.": "Once deleted, you can't undo this action.", "Moved to Trash": "Moved to Trash", "Permanently deleted": "Permanently deleted", "restored": "{{title}} restored", @@ -57,7 +51,6 @@ "Strikethrough": "Strikethrough", "Inline code": "Inline code", "Code block": "Code block", - "Link": "Hyperlink (with selected text)", "Body text": "Body text", "Heading": "Heading {{number}}", "Increase indent": "Increase indent", @@ -65,6 +58,11 @@ "Markdown Syntax": "Markdown Syntax", "Divider": "Divider", "404 - Page Not Found": "404 - Page Not Found", + "Once deleted, you can't undo this action": { + "": "Once deleted, you can't undo this action." + }, + "Remove from favourites": "Remove from favourites", + "Removed from Favourites": "Removed from Favourites", "New Workspace": "New Workspace", "Workspace description": "Workspace is your virtual space to capture, create and plan as just one person or together as a team.", "Create": "Create", @@ -79,6 +77,10 @@ "TrashButtonGroupTitle": "Permanently delete", "TrashButtonGroupDescription": "Once deleted, you can't undo this action. Do you confirm?", "Delete permanently": "Delete permanently", + "Link": "Hyperlink (with selected text)", + "Quick search placeholder": "Quick Search...", + "Quick search placeholder2": "Search in {{workspace}}", + "Settings": "Settings", "recommendBrowser": " We recommend the <1>Chrome browser for optimal experience.", "upgradeBrowser": "Please upgrade to the latest version of Chrome for the best experience.", "Invite Members": "Invite Members", @@ -104,19 +106,15 @@ "Create Or Import": "Create Or Import", "Tips": "Tips: ", "login success": "Login success", - "Sign in": "Sign in AFFiNE Cloud", "Sign out": "Sign out of AFFiNE Cloud", "Delete Workspace": "Delete Workspace", - "Delete Workspace Description": "Deleting (<1>{{workspace}}) cannot be undone, please proceed with caution. along with all its content.", "Delete Workspace Description2": "Deleting (<1>{{workspace}}) will delete both local and cloud data, this operation cannot be undone, please proceed with caution.", "Delete Workspace placeholder": "Please type “Delete” to confirm", "Leave Workspace": "Leave Workspace", - "Leave Workspace Description": "After you leave, you will not be able to access all the contents of this workspace.", "Leave": "Leave", "Workspace Icon": "Workspace Icon", "Workspace Name": "Workspace Name", "Workspace Type": "Workspace Type", - "Export Workspace": "Export Workspace <1>{{workspace}} Is Coming", "Users": "Users", "Access level": "Access level", "Pending": "Pending", @@ -132,12 +130,16 @@ "Publishing Description": "After publishing to the web, everyone can view the content of this workspace through the link.", "Stop publishing": "Stop publishing", "Publish to web": "Publish to web", - "Sync Description": "{{workspaceName}} is Local Workspace. All data is stored on the current device. You can enable AFFiNE Cloud for this workspace to keep data in sync with the cloud.", - "Sync Description2": "<1>{{workspaceName}} is Cloud Workspace. All data will be synchronized and saved to the AFFiNE", "Download data to device": "Download {{CoreOrAll}} data to device", "General": "General", "Sync": "Sync", "Collaboration": "Collaboration", "Publish": "Publish", - "Workspace Settings": "Workspace Settings" + "Workspace Settings": "Workspace Settings", + "Export Workspace": "Export Workspace <1>{{workspace}} is coming soon", + "Leave Workspace Description": "After you leave, you will no longer be able to access the contents of this workspace.", + "Sign in": "Sign in to AFFiNE Cloud", + "Sync Description": "{{workspaceName}} is a Local Workspace. All data is stored on the current device. You can enable AFFiNE Cloud for this workspace to keep data in sync with the cloud.", + "Sync Description2": "<1>{{workspaceName}} is a Cloud Workspace. All data will be synchronised and saved to AFFiNE Cloud.", + "Delete Workspace Description": "Deleting (<1>{{workspace}}) cannot be undone, please proceed with caution. All contents will be lost." } diff --git a/packages/i18n/src/resources/fr.json b/packages/i18n/src/resources/fr.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/packages/i18n/src/resources/fr.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/i18n/src/resources/index.ts b/packages/i18n/src/resources/index.ts index 329fa9c679..2081ad8aaf 100644 --- a/packages/i18n/src/resources/index.ts +++ b/packages/i18n/src/resources/index.ts @@ -1,16 +1,11 @@ // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. // Run `pnpm run download-resources` to regenerate. -// To overwrite this, please overwrite download.ts +// To overwrite this, please overwrite download.ts script. import en from './en.json'; -import zh_Hans from './zh-Hans.json'; -import zh_Hant from './zh-Hant.json'; -import sr from './sr.json'; -import fr from './fr.json'; -import bn from './bn.json'; export const LOCALES = [ { - id: 1000016008, + id: 1000040001, name: 'English', tag: 'en', originalName: 'English', @@ -19,54 +14,4 @@ export const LOCALES = [ completeRate: 1, res: en, }, - { - id: 1000016009, - name: 'Simplified Chinese', - tag: 'zh-Hans', - originalName: '简体中文', - flagEmoji: '🇨🇳', - base: false, - completeRate: 1, - res: zh_Hans, - }, - { - id: 1000016012, - name: 'Traditional Chinese', - tag: 'zh-Hant', - originalName: '繁體中文', - flagEmoji: '🇭🇰', - base: false, - completeRate: 1, - res: zh_Hant, - }, - { - id: 1000034005, - name: 'Serbian', - tag: 'sr', - originalName: 'српски', - flagEmoji: '🇷🇸', - base: false, - completeRate: 0.9166666666666666, - res: sr, - }, - { - id: 1000034008, - name: 'French', - tag: 'fr', - originalName: 'français', - flagEmoji: '🇫🇷', - base: false, - completeRate: 1, - res: fr, - }, - { - id: 1000034010, - name: 'Bangla', - tag: 'bn', - originalName: 'বাংলা', - flagEmoji: '🇧🇩', - base: false, - completeRate: 0.7083333333333334, - res: bn, - }, ] as const; diff --git a/packages/i18n/src/resources/sr.json b/packages/i18n/src/resources/sr.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/packages/i18n/src/resources/sr.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/i18n/src/resources/zh-Hans.json b/packages/i18n/src/resources/zh-Hans.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/packages/i18n/src/resources/zh-Hans.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/i18n/src/resources/zh-Hant.json b/packages/i18n/src/resources/zh-Hant.json deleted file mode 100644 index 0967ef424b..0000000000 --- a/packages/i18n/src/resources/zh-Hant.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/packages/i18n/src/script/api.ts b/packages/i18n/src/scripts/api.ts similarity index 99% rename from packages/i18n/src/script/api.ts rename to packages/i18n/src/scripts/api.ts index ae286fe584..657e3c245c 100644 --- a/packages/i18n/src/script/api.ts +++ b/packages/i18n/src/scripts/api.ts @@ -1,5 +1,5 @@ // cSpell:ignore Tolgee -import { fetchTolgee } from './request'; +import { fetchTolgee } from './request.js'; /** * Returns all project languages diff --git a/packages/i18n/src/script/download.ts b/packages/i18n/src/scripts/download.ts similarity index 95% rename from packages/i18n/src/script/download.ts rename to packages/i18n/src/scripts/download.ts index a43e84bf22..7aa0740c6e 100644 --- a/packages/i18n/src/script/download.ts +++ b/packages/i18n/src/scripts/download.ts @@ -2,8 +2,8 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { format } from 'prettier'; -import { getAllProjectLanguages, getRemoteTranslations } from './api'; -import type { TranslationRes } from './utils'; +import { getAllProjectLanguages, getRemoteTranslations } from './api.js'; +import type { TranslationRes } from './utils.js'; const RES_DIR = path.resolve(process.cwd(), 'src', 'resources'); @@ -66,7 +66,7 @@ const main = async () => { ); const availableLanguages = languagesWithTranslations.filter( - language => language.completeRate > 0 + language => language.completeRate === 1 ); availableLanguages @@ -90,7 +90,7 @@ const main = async () => { console.log('Generating meta data...'); const code = `// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. // Run \`pnpm run download-resources\` to regenerate. - // To overwrite this, please overwrite ${path.basename(__filename)} + // To overwrite this, please overwrite download.ts script. ${availableLanguages .map( language => diff --git a/packages/i18n/src/script/request.ts b/packages/i18n/src/scripts/request.ts similarity index 100% rename from packages/i18n/src/script/request.ts rename to packages/i18n/src/scripts/request.ts diff --git a/packages/i18n/src/script/sync.ts b/packages/i18n/src/scripts/sync.ts similarity index 97% rename from packages/i18n/src/script/sync.ts rename to packages/i18n/src/scripts/sync.ts index 80b16a5024..b89734e4c4 100644 --- a/packages/i18n/src/script/sync.ts +++ b/packages/i18n/src/scripts/sync.ts @@ -1,8 +1,8 @@ // cSpell:ignore Tolgee import { readFile } from 'fs/promises'; import path from 'path'; -import { createsNewKey, getRemoteTranslations } from './api'; -import type { TranslationRes } from './utils'; +import { createsNewKey, getRemoteTranslations } from './api.js'; +import type { TranslationRes } from './utils.js'; const BASE_JSON_PATH = path.resolve( process.cwd(), diff --git a/packages/i18n/src/script/utils.ts b/packages/i18n/src/scripts/utils.ts similarity index 100% rename from packages/i18n/src/script/utils.ts rename to packages/i18n/src/scripts/utils.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 722b006877..a6a2417511 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -164,6 +164,7 @@ importers: i18next: ^21.9.1 prettier: ^2.7.1 react-i18next: ^11.18.4 + ts-node: ^10.9.1 typescript: ^4.8.4 dependencies: i18next: 21.10.0 @@ -171,6 +172,7 @@ importers: react-i18next: 11.18.6_i18next@21.10.0 devDependencies: '@types/prettier': 2.7.2 + ts-node: 10.9.1_typescript@4.9.3 typescript: 4.9.3 packages/logger: @@ -1711,6 +1713,13 @@ packages: prettier: 2.7.1 dev: true + /@cspotcode/source-map-support/0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@emotion/babel-plugin/11.10.2: resolution: {integrity: sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==} peerDependencies: @@ -2481,6 +2490,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@jridgewell/trace-mapping/0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + /@lit/reactive-element/1.4.1: resolution: {integrity: sha512-qDv4851VFSaBWzpS02cXHclo40jsbAjRXnebNXpm0uVg32kCneZPo9RYVQtrTNICtZ+1wAYHu1ZtxWSWMbKrBw==} dev: false @@ -3280,6 +3296,22 @@ packages: resolution: {integrity: sha512-GiS5Df3CzXY/fPBFcM0CKFERZfI4Cg1X33VPZX+NLo7Fwm/h9zu/aU24N1mG75Q9LuMnwKm7woxKr8BiUXGYCg==} dev: false + /@tsconfig/node10/1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12/1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14/1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16/1.0.3: + resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} + dev: true + /@types/debug/4.1.7: resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} dependencies: @@ -3616,9 +3648,15 @@ packages: acorn: 8.8.0 dev: true + /acorn-walk/8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + /acorn/8.8.0: resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} engines: {node: '>=0.4.0'} + hasBin: true dev: true /aggregate-error/3.1.0: @@ -3693,6 +3731,10 @@ packages: engines: {node: '>=12'} dev: true + /arg/4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + /argparse/1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: @@ -4189,6 +4231,10 @@ packages: yaml: 1.10.2 dev: false + /create-require/1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + /cross-spawn/5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -4368,6 +4414,11 @@ packages: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dev: false + /diff/4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + /dir-glob/3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -6294,6 +6345,10 @@ packages: semver: 6.3.0 dev: true + /make-error/1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + /map-obj/1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -7906,6 +7961,36 @@ packages: engines: {node: '>=8'} dev: true + /ts-node/10.9.1_typescript@4.9.3: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + acorn: 8.8.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + /tsconfig-paths/3.14.1: resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} dependencies: @@ -8151,6 +8236,10 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false + /v8-compile-cache-lib/3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + /v8-compile-cache/2.3.0: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true @@ -8601,6 +8690,11 @@ packages: lib0: 0.2.52 dev: false + /yn/3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'}