Merge branch 'chore/master-i18n' into feat/datacenter-i18n

This commit is contained in:
JimmFly
2023-01-12 18:30:34 +08:00
16 changed files with 321 additions and 12 deletions

62
packages/i18n/README.md Normal file
View File

@@ -0,0 +1,62 @@
# 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 (
<div>
<div>{t('Text')}</div>
<button onClick={() => changeLanguage('en')}>
{t('Switch to language', { language: 'en' })}
</button>
<button onClick={() => changeLanguage('zh-Hans')}>
{t('Switch to language', { language: 'zh-Hans' })}
</button>
</div>
);
};
```
## How the i18n workflow works?
- When the `src/resources/en.json`(base language) updated and merged to the develop branch, will trigger the `languages-sync` action.
- The `languages-sync` action will check the base language and add missing translations to the Tolgee platform.
- This way, partners from the community can update the translations.
## 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
- Run the `download-resources` script to download the latest full-translation translation resources from 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/)

View File

@@ -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"
}
}

View File

@@ -15,7 +15,6 @@
"Add to favourites": "Add to favourites",
"Paper": "Paper",
"Edgeless": "Edgeless",
"Jump to": "Jump to",
"Convert to ": "Convert to ",
"Page": "Page",
"Export": "Export",
@@ -57,6 +56,12 @@
"Reduce indent": "Reduce indent",
"Markdown Syntax": "Markdown Syntax",
"Divider": "Divider",
"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",
"Jump to": "Jump to",
"404 - Page Not Found": "404 - Page Not Found",
"Once deleted, you can't undo this action": {
"": "Once deleted, you can't undo this action."
@@ -141,5 +146,7 @@
"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}}</1> is a Cloud Workspace. All data will be synchronised and saved to AFFiNE Cloud.",
"Delete Workspace Description": "Deleting (<1>{{workspace}}</1>) cannot be undone, please proceed with caution. All contents will be lost."
"Delete Workspace Description": "Deleting (<1>{{workspace}}</1>) cannot be undone, please proceed with caution. All contents will be lost.",
"core": "core",
"all": "all"
}

View File

@@ -2,6 +2,7 @@
// Run `pnpm run download-resources` to regenerate.
// To overwrite this, please overwrite download.ts script.
import en from './en.json';
import zh_Hans from './zh-Hans.json';
export const LOCALES = [
{
@@ -14,4 +15,14 @@ export const LOCALES = [
completeRate: 1,
res: en,
},
{
id: 1000040004,
name: 'Simplified Chinese',
tag: 'zh-Hans',
originalName: '简体中文',
flagEmoji: '🇨🇳',
base: false,
completeRate: 1,
res: zh_Hans,
},
] as const;

View File

@@ -0,0 +1,148 @@
{
"// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.": "",
"Quick search": "快速搜索",
"All pages": "全部页面",
"Favourites": "收藏夹",
"No item": "无项目",
"Import": "导入",
"Trash": "垃圾箱",
"New Page": "新建页面",
"New Keyword Page": "新建 “{{query}}“ 为标题的页面 ",
"Find 0 result": "找到 0 个结果",
"Find results": "找到 {{number}} 个结果",
"Collapse sidebar": "折叠侧边栏",
"Expand sidebar": "展开侧边栏",
"Added to Favourites": "已收藏",
"Add to favourites": "加入收藏",
"Paper": "文档",
"Edgeless": "无界",
"Convert to ": "转换为",
"Page": "页面",
"Export": "导出",
"Export to HTML": "导出为 HTML",
"Export to Markdown": "导出为 Markdown",
"Delete": "删除",
"Title": "标题",
"Untitled": "未命名",
"Created": "已创建",
"Updated": "已更新",
"Open in new tab": "在新标签页打开",
"Favourite": "收藏",
"Favourited": "已收藏",
"Delete page?": "确定要删除页面?",
"Delete permanently?": "是否永久删除?",
"will be moved to Trash": "{{title}} 将被移到垃圾箱",
"Once deleted, you can't undo this action": {
"": "一旦删除,将无法撤销!"
},
"Moved to Trash": "已移到垃圾箱",
"Permanently deleted": "已永久删除",
"restored": "{{title}} 已恢复",
"Cancel": "取消",
"Keyboard Shortcuts": "键盘快捷键",
"Contact Us": "联系我们",
"Official Website": "官网",
"Get in touch!": "保持联络!",
"AFFiNE Community": "AFFiNE 社区",
"How is AFFiNE Alpha different?": "AFFiNE Alpha有何不同",
"Shortcuts": "快捷键",
"Undo": "撤销",
"Redo": "重做",
"Bold": "粗体",
"Italic": "斜体",
"Underline": "下划线",
"Strikethrough": "删除线",
"Inline code": "行内代码",
"Code block": "代码块",
"Link": "超链接(选定文本)",
"Body text": "正文",
"Heading": "标题 {{number}}",
"Increase indent": "增加缩进",
"Reduce indent": "减少缩进",
"Markdown Syntax": "Markdown 语法",
"Divider": "分割线",
"404 - Page Not Found": "404 - 页面不见了",
"Remove from favourites": "从收藏中移除",
"Removed from Favourites": "已从收藏中移除",
"New Workspace": "新建工作区",
"Workspace description": "工作区是为个人和团队进行引用、创建和规划的虚拟空间。",
"Create": "创建",
"Select": "选择",
"Text": "文本(即将上线)",
"Shape": "图形",
"Sticky": "便利贴(即将上线)",
"Pen": "笔(即将上线)",
"Connector": "链接(即将上线)",
"Upload": "上传",
"Restore it": "恢复TA",
"TrashButtonGroupTitle": "永久删除",
"TrashButtonGroupDescription": "一旦删除,将无法撤消此操作。确定吗?",
"Delete permanently": "永久删除",
"Quick search placeholder": "快速搜索...",
"Quick search placeholder2": "在{{workspace}} 中搜索",
"Settings": "设置",
"recommendBrowser": "建议使用 <1>Chrome</1> 浏览器以获得最佳体验。",
"upgradeBrowser": "请升级到最新版本的 Chrome 以获得最佳体验。",
"Invite Members": "邀请成员",
"Invite placeholder": "搜索邮件仅支持Gmail",
"Non-Gmail": "不支持非Gmail邮箱",
"Invite": "邀请",
"Loading": "加载中...",
"NotLoggedIn": "当前未登录",
"ClearData": "清除本地数据",
"Continue with Google": "谷歌登录以继续",
"Set up an AFFiNE account to sync data": "设置AFFiNE帐户以同步数据",
"Stay logged out": "保持登出状态",
"All changes are saved locally": "所有改动已保存到本地",
"Ooops!": "啊哦!",
"mobile device": "貌似你正在移动设备上浏览。",
"mobile device description": "我们仍在进行移动端的支持工作,建议使用桌面设备。",
"Got it": "知道了",
"emptyAllPages": "此工作区为空。创建新页面并开始编辑。",
"emptyFavourite": "单击“添加到收藏夹”,页面将显示在此处。",
"emptyTrash": "单击“添加到垃圾箱”,页面将显示在此处。",
"still designed": "(此页面仍在设计中。)",
"My Workspaces": "我的工作区",
"Create Or Import": "创建或导入",
"Tips": "提示:",
"login success": "登录成功",
"Sign in": "登录 AFFiNE 云",
"Sign out": "登出 AFFiNE 云",
"Delete Workspace": "删除工作空间",
"Delete Workspace Description": "正在删除 (<1>{{workspace}}</1>) ,此操作无法撤销,所有内容将会丢失。",
"Delete Workspace Description2": "正在删除(<1>{{workspace}}</1>),将同时删除本地和云端数据。此操作无法撤消,请谨慎操作。",
"Delete Workspace placeholder": "请输入”Delete“以确认",
"Leave Workspace": "退出工作区",
"Leave Workspace Description": "退出后,您将无法再访问此工作区的内容。",
"Jump to": "跳转到",
"Leave": "退出",
"Workspace Icon": "工作区图标",
"Workspace Name": "工作区名称",
"Workspace Type": "工作区类型",
"Export Workspace": "导出工作区 <1>{{workspace}}</1> 即将上线",
"Users": "用户",
"Access level": "访问权限",
"Pending": "待定",
"Collaboration Description": "与其他成员协作需要AFFiNE云服务支持。",
"Enable AFFiNE Cloud": "启用 AFFiNE 云服务",
"Enable AFFiNE Cloud Description": "如启用此工作区中的数据将通过AFFiNE Cloud进行备份和同步。",
"Enable": "启动",
"Sign in and Enable": "登录并启用",
"Skip": "跳过",
"Publishing": "发布到web需要AFFiNE云服务。",
"Share with link": "通过链接分享",
"Copy Link": "复制链接",
"Publishing Description": "发布到web后所有人都可以通过链接查看此工作区的内容。",
"Stop publishing": "中止发布",
"Publish to web": "发布到web",
"Sync Description": "{{workspaceName}}是本地工作区所有数据都存储在当前设备上。您可以为此工作区启用AFFiNE Cloud以使数据与云端保持同步。",
"Sync Description2": "<1>{{workspaceName}}</1>是云端工作区。所有数据将同步并保存到AFFiNE Cloud。",
"Download data to device": "下载{{CoreOrAll}}数据到设备",
"General": "常规",
"Sync": "同步",
"Collaboration": "协作",
"Publish": "发布",
"Workspace Settings": "工作区设置",
"core": "核心",
"all": "全部"
}

View File

@@ -1,5 +1,5 @@
// cSpell:ignore Tolgee
import { fetchTolgee } from './request';
import { fetchTolgee } from './request.js';
/**
* Returns all project languages

View File

@@ -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 =>

View File

@@ -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(),