├── .npmrc
├── bin
└── index.js
├── .npmignore
├── .gitignore
├── gulp
├── util.js
├── filter.js
├── sorter.js
├── post.js
├── pick-empty.js
├── compare.js
├── download.js
├── translate.js
├── chs-to-cht.js
├── chs-to-cht-parallel.js
└── md5.js
├── index.js
├── tools
└── publish.js
├── .circleci
└── config.yml
├── package.json
├── keys
├── create-organization2.json
├── create-organization.json
├── permissions.json
└── teambition.json
├── util.js
├── README.MD
├── gulpfile.js
└── test.js
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/bin/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var gulp = require('gulp')
4 |
5 | require('../gulpfile')
6 |
7 | if (gulp.tasks['ci:i18n']) {
8 | gulp.start('ci:i18n')
9 | }
10 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *.seed
2 | *.log
3 | *.csv
4 | *.dat
5 | *.out
6 | *.pid
7 | *.gz
8 | *.bat
9 | *~
10 | .idea
11 | *.iml
12 | .vscode
13 | .DS_Store
14 |
15 | node_modules
16 | test.js
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.seed
2 | *.log
3 | *.csv
4 | *.dat
5 | *.out
6 | *.pid
7 | *.gz
8 | *.bat
9 | *~
10 | .idea
11 | *.iml
12 | .vscode
13 | .DS_Store
14 |
15 | node_modules
16 | tmp
17 | config
18 |
--------------------------------------------------------------------------------
/gulp/util.js:
--------------------------------------------------------------------------------
1 |
2 | var _ = require('lodash')
3 |
4 | var ONESKY_CONFIG = {
5 | fileName: 'zh.json',
6 | secret: process.env.ONESKY_SECRET,
7 | apiKey: process.env.ONESKY_API_KEY,
8 | projectId: '',
9 | format: 'HIERARCHICAL_JSON',
10 | keepStrings: true
11 | }
12 | var LANGUAGE_MAP = {
13 | 'zh': 'zh-CN',
14 | 'zh_tw': 'zh-TW'
15 | }
16 |
17 | exports.getHttpOptions = function (lang, options) {
18 | return _.extend({}, ONESKY_CONFIG, {
19 | language: LANGUAGE_MAP[lang] || lang
20 | }, options)
21 | }
22 |
--------------------------------------------------------------------------------
/gulp/filter.js:
--------------------------------------------------------------------------------
1 |
2 | var through = require('through2')
3 | var path = require('path')
4 |
5 | // exporting the plugin main function
6 | module.exports = function (description, descriptionAs) {
7 | return through.obj(function (file, ence, next) {
8 | if (!file.isBuffer()) return next()
9 |
10 | var extname = path.extname(file.path)
11 | var lang = path.basename(file.path, extname)
12 | var json = JSON.parse(file.contents.toString())
13 | Object.keys(description).forEach(function (key) {
14 | if (!json[key] && lang === descriptionAs) {
15 | json[key] = description[key]
16 | }
17 | })
18 |
19 | file.contents = new Buffer(JSON.stringify(json, null, 2))
20 | this.push(file)
21 | next()
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | var loaderUtils = require('loader-utils')
3 | var util = require('./util')
4 |
5 | var configLanguages = ['zh', 'zh_tw', 'en', 'ja', 'ko']
6 |
7 | module.exports = function (content) {
8 | if (this.cacheable) this.cacheable()
9 |
10 | var query = loaderUtils.parseQuery(this.query)
11 | var descriptionAs = query.descriptionAs
12 | var languages = query.languages || configLanguages
13 | var description = util.parseDescription(content)
14 |
15 | var results = ['var i18n = require(\'tb-i18n\');']
16 | for (var i = 0, len = languages.length; i < len; i++) {
17 | var lang = languages[i]
18 | var result
19 | if (lang === descriptionAs) {
20 | result = util.translate(lang, description)
21 | } else {
22 | result = util.translateLocales(lang, Object.keys(description))
23 | }
24 | results.push(result)
25 | }
26 | results.push('module.exports = i18n;')
27 | return results.join('\n')
28 | }
29 |
--------------------------------------------------------------------------------
/gulp/sorter.js:
--------------------------------------------------------------------------------
1 |
2 | var through = require('through2')
3 |
4 | function sortKeys (str) {
5 | var data = JSON.parse(str)
6 | var keys = Object.keys(data)
7 | keys = keys.sort(function (left, right) {
8 | if (~left.indexOf('.') && !~right.indexOf('.')) {
9 | return 1
10 | } else if (!~left.indexOf('.') && ~right.indexOf('.')) {
11 | return -1
12 | }
13 | if (left > right) {
14 | return 1
15 | } else if (left < right) {
16 | return -1
17 | }
18 | return 0
19 | })
20 |
21 | var result = {}
22 | for (var i = 0; i < keys.length; i++) {
23 | var key = keys[i]
24 | result[key] = data[key]
25 | }
26 | return JSON.stringify(result, null, 2)
27 | }
28 |
29 | // exporting the plugin main function
30 | module.exports = function () {
31 | return through.obj(function (file, ence, next) {
32 | if (!file.isBuffer()) return next()
33 |
34 | var contents = file.contents.toString()
35 | contents = sortKeys(contents)
36 | file.contents = new Buffer(contents)
37 | this.push(file)
38 | next()
39 | })
40 | }
41 |
--------------------------------------------------------------------------------
/gulp/post.js:
--------------------------------------------------------------------------------
1 |
2 | var _ = require('lodash')
3 | var util = require('./util')
4 | var through = require('through2')
5 | var gutil = require('gulp-util')
6 | var onesky = require('onesky-utils')
7 | var loaderUtil = require('../util')
8 |
9 | var col = gutil.colors
10 | var PluginError = gutil.PluginError
11 | var PLUGIN_NAME = 'gulp-i18n-post'
12 |
13 | module.exports = function (lang, extOptions) {
14 | var json = {}
15 | return through.obj(function (file, ence, next) {
16 | if (!file.isBuffer()) return next()
17 |
18 | var obj = loaderUtil.parseDescription(file.contents.toString())
19 | _.extend(json, obj)
20 |
21 | this.push(file)
22 | return next()
23 | }, function (next) {
24 | var options = util.getHttpOptions(lang, extOptions)
25 | options.content = JSON.stringify(json)
26 |
27 | gutil.log('Upload \'' + col.cyan(lang + '.json') + '\' to OneSky ...')
28 | onesky.postFile(options).then(function (content) {
29 | gutil.log('Upload finished')
30 | }).catch(function (err) {
31 | throw new PluginError(PLUGIN_NAME, err.message)
32 | })
33 | return next()
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/gulp/pick-empty.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var through = require('through2')
4 | var gutil = require('gulp-util')
5 | var path = require('path')
6 |
7 | function compare (base, current) {
8 | var result = {}
9 | Object.keys(current).forEach(function (key) {
10 | if (current[key] === base[key] && !/^[a-zA-Z0-9?><;,{}[\]\-_+=!@#$%\^&*|']*$/.test(current[key])) {
11 | result[key] = ''
12 | }
13 | })
14 | return result
15 | }
16 |
17 | module.exports = function (baseLang) {
18 | var set = {}
19 |
20 | var outputStream = through.obj(function (file, enc, next) {
21 | if (!file.isBuffer()) return next()
22 |
23 | var extname = path.extname(file.path)
24 | var lang = path.basename(file.path, extname)
25 | set[lang] = JSON.parse(file.contents.toString())
26 | return next()
27 | }, function (next) {
28 | Object.keys(set).forEach(function (lang) {
29 | if (lang !== baseLang) {
30 | var diff = compare(set[baseLang], set[lang])
31 | outputStream.push(new gutil.File({
32 | path: lang + '.json',
33 | contents: new Buffer(JSON.stringify(diff, null, 2))
34 | }))
35 | }
36 | })
37 | next()
38 | })
39 | return outputStream
40 | }
41 |
--------------------------------------------------------------------------------
/tools/publish.js:
--------------------------------------------------------------------------------
1 | const shelljs = require('shelljs')
2 |
3 | const tagRE = /^[0-9]+(\.[0-9]+)*(-(alpha|beta)\.[0-9]+)?/
4 |
5 | const prepare = () => {
6 | const { stderr, stdout } = shelljs.exec('git log -1 --pretty=%B')
7 |
8 | if (stderr) {
9 | return Promise.reject(stderr)
10 | }
11 |
12 | if (!tagRE.test(stdout)) {
13 | return Promise.reject(-1)
14 | }
15 |
16 | return Promise.resolve(stdout)
17 | }
18 |
19 | const getTag = (version) => {
20 | if (version.indexOf('alpha') > -1) {
21 | return 'prerelease'
22 | }
23 |
24 | if (version.indexOf('beta') > -1) {
25 | return 'next'
26 | }
27 |
28 | return 'latest'
29 | }
30 |
31 | const publish = (version) => {
32 | const tag = getTag(version)
33 |
34 | const { code } = shelljs.exec(`npm publish --tag ${tag}`)
35 |
36 | if (code !== 0) {
37 | return Promise.reject()
38 | }
39 |
40 | return Promise.resolve()
41 | }
42 |
43 | prepare()
44 | .then((version) => publish(version))
45 | .then(() => {
46 | console.info('Published.')
47 | process.exit(0)
48 | })
49 | .catch((err) => {
50 | if (err === -1) {
51 | console.info('Not a release commit.')
52 | process.exit(0)
53 | return
54 | }
55 | console.error(err)
56 | process.exit(1)
57 | })
58 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | defaults: &defaults
2 | working_directory: ~/tb-i18n-loader
3 | docker:
4 | - image: circleci/node:8
5 |
6 | version: 2
7 | jobs:
8 | build:
9 | <<: *defaults
10 | steps:
11 | - checkout
12 | - run: ls -l ~/tb-i18n-loader
13 | - run: echo 'export PATH=${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin' >> $BASH_ENV
14 | - restore_cache:
15 | key: dependency-cache-{{ checksum "package.json" }}
16 | - run:
17 | name: yarn
18 | command: |
19 | yarn
20 | - save_cache:
21 | key: dependency-cache-{{ checksum "package.json" }}
22 | paths:
23 | - ~/.cache/yarn
24 | - persist_to_workspace:
25 | root: ~/tb-i18n-loader
26 | paths:
27 | - ./*
28 | deploy:
29 | <<: *defaults
30 | steps:
31 | - attach_workspace:
32 | at: ~/tb-i18n-loader
33 | - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/tb-i18n-loader/.npmrc
34 | - run: npm run publish-it
35 |
36 | workflows:
37 | version: 2
38 | release:
39 | jobs:
40 | - build
41 | - deploy:
42 | requires:
43 | - build
44 | filters:
45 | tags:
46 | only: /.*/
47 | branches:
48 | only: master
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tb-i18n-loader",
3 | "version": "11.11.1",
4 | "description": "Webpack loader for teambition i18n",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "standard && mocha test",
8 | "cht": "gulp download && gulp cache && gulp chs-to-cht && gulp post-cht",
9 | "publish-it": "node ./tools/publish.js",
10 | "download": "gulp download"
11 | },
12 | "bin": {
13 | "tb-i18n": "./bin/index.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git@github.com:teambition/tb-i18n-loader.git"
18 | },
19 | "keywords": [
20 | "i18n",
21 | "i18n-loader"
22 | ],
23 | "author": "suyu34",
24 | "bugs": {
25 | "url": "https://github.com/teambition/tb-i18n-loader/issues"
26 | },
27 | "homepage": "https://github.com/teambition/tb-i18n-loader#readme",
28 | "devDependencies": {
29 | "config": "^1.26.1",
30 | "gulp": "^3.9.1",
31 | "mocha": "^2.4.5",
32 | "shelljs": "^0.8.2",
33 | "should": "^8.2.2",
34 | "standard": "^6.0.7"
35 | },
36 | "license": "MIT",
37 | "dependencies": {
38 | "gulp-sequence": "^1.0.0",
39 | "gulp-util": "^3.0.7",
40 | "inquirer": "^5.2.0",
41 | "isomorphic-fetch": "^2.2.1",
42 | "loader-utils": "^0.2.12",
43 | "lodash": "^4.8.2",
44 | "minimist": "^1.2.0",
45 | "natives": "1.1.6",
46 | "onesky-utils": "0.0.3",
47 | "through2": "^2.0.1",
48 | "tslib": "^1.7.0",
49 | "urllib": "^2.8.0"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/keys/create-organization2.json:
--------------------------------------------------------------------------------
1 | @namespace: apps.create-organization.
2 |
3 | {
4 | "title": "创建组织",
5 | "introContent": "协作之上,是更有意义的企业管理。Teambition 组织帮助你更好地管理团队、成员,并拥有无限项目数量。可以免费使用14天。",
6 |
7 | "tabTitleCreate": "( 1/2 ) 免费试用",
8 | "tabTitleContact": "( 2/2 ) 联络方式",
9 |
10 | "tabInfoCreateGuide": "现在创建组织,14天免费试用",
11 | "tabInfoCreateGuideBusinessLink": "Teambition企业版",
12 | "tabInfoOrgNamePlaceholder": "企业名称",
13 |
14 | "tabContactPhoneGuide": "验证一下创建者的联络方式
如果遇到问题,请联络 400-060-5576",
15 | "tabContactName": "联系人",
16 | "tabContactPhoneNumber": "手机号",
17 | "tabContactRetryGetVerifyCode": "重新获取",
18 | "tabContactRetryGetVerifyCodeCD": "重新获取 (%s)",
19 | "tabContactNotValidVerifyCode": "验证码错误",
20 | "tabContactSendingVerifyCode": "正在发送中...",
21 | "tabContactSMSVerifyCode": "短信验证码",
22 | "tabContactPhoneBound": "该号码已通过验证",
23 | "tabContactNoSMSNeeded": "该号码无需验证",
24 |
25 | "tabFooterDone": "完成",
26 | "tabFooterStepNext": "下一步",
27 | "tabFooterCreatingOrg": "企业创建中",
28 | "tabFooterGetSMSVerify": "获取短信验证码",
29 |
30 | "countrycode": "国际区号",
31 | "country_chn": "中国",
32 | "country_hk": "中国香港",
33 | "country_tw": "台湾",
34 | "country_jp": "日本",
35 | "country_us": "美国",
36 | "country_other": "其他",
37 |
38 | "captchaTitle": "请点击下图中的 %s",
39 | "captchaInvalided": "验证失败",
40 | "captchaValided": "验证成功",
41 | "captchaTooFrequency": "图片验证码错误次数过多, 请明天再试",
42 | "captchaTips": "请先选择图片验证码",
43 | "captchaNotNeeded": "无需验证",
44 | "captchaRefresh": "刷新验证码",
45 | "captchaSetupErr": "获取验证码出错, 请刷新重试"
46 | }
47 |
--------------------------------------------------------------------------------
/gulp/compare.js:
--------------------------------------------------------------------------------
1 | var gutil = require('gulp-util')
2 | var through = require('through2')
3 | var path = require('path')
4 | var cache = require('../cache/zh.json')
5 |
6 | var col = gutil.colors
7 |
8 | function compare(data) {
9 | var added = []
10 | var modified = []
11 | var deleted = []
12 | var cacheKeys = Object.keys(cache)
13 | var dataKeys = Object.keys(data)
14 | var allKeys = Object.keys(Object.assign({}, cache, data))
15 |
16 | while (!!allKeys.length) {
17 | var last = allKeys[allKeys.length - 1]
18 | if (!!data[last] && !!cache[last] && data[last] !== cache[last]) {
19 | modified.push({
20 | key: last,
21 | oldValue: cache[last],
22 | newValue: data[last],
23 | })
24 | }
25 | if (!!data[last] && !cache[last]) {
26 | added.push({
27 | key: last,
28 | value: data[last],
29 | })
30 | }
31 | if (!data[last] && !!cache[last]) {
32 | deleted.push({
33 | key: last,
34 | value: cache[last],
35 | })
36 | }
37 | allKeys.pop()
38 | }
39 |
40 | var result = {
41 | added: added,
42 | modified: modified,
43 | deleted: deleted,
44 | }
45 |
46 | gutil.log(col.green('Done'))
47 | return JSON.stringify(result, null, 2)
48 | }
49 |
50 | // exporting the plugin main function
51 | module.exports = function () {
52 | return through.obj(function (file, enc, next) {
53 | if (!file.isBuffer()) return next()
54 |
55 | gutil.log('Compare Origin with OneSky ...')
56 |
57 | var contents = JSON.parse(file.contents.toString())
58 | var newFile = file
59 | newFile.path = path.join(__dirname, '../cache/diff.json')
60 | newFile.contents = new Buffer(compare(contents))
61 | this.push(file)
62 | next()
63 | })
64 | }
65 |
--------------------------------------------------------------------------------
/gulp/download.js:
--------------------------------------------------------------------------------
1 |
2 | var util = require('./util')
3 | var through = require('through2')
4 | var gutil = require('gulp-util')
5 | var onesky = require('onesky-utils')
6 | var sysPath = require('path')
7 | var fs = require('fs')
8 | var rootDir = sysPath.resolve(__dirname, '../')
9 |
10 | var col = gutil.colors
11 | var PluginError = gutil.PluginError
12 | var PLUGIN_NAME = 'gulp-i18n-download'
13 |
14 | function readOldJson (lang, useLocalLocales) {
15 | var path = sysPath.resolve((useLocalLocales ? process.cwd() : rootDir), 'locales', lang + '.json')
16 | var contents = fs.readFileSync(path, 'utf-8')
17 | return JSON.parse(contents)
18 | }
19 |
20 | module.exports = function (languages, options, useLocalLocales) {
21 | useLocalLocales = typeof useLocalLocales === 'undefined' ? false : useLocalLocales
22 | var outputStream = through.obj(function (file, enc, next) {
23 | this.push(file)
24 | return next()
25 | })
26 |
27 | if (languages && languages.length) {
28 | var count = languages.length
29 | languages.map(function (lang) {
30 | var oldJson = readOldJson(lang, useLocalLocales)
31 | gutil.log('Download \'' + col.cyan(lang + '.json') + '\' from OneSky ...')
32 | onesky.getFile(util.getHttpOptions(lang, options)).then(function (content) {
33 | var json = JSON.parse(content)
34 | for (var key in oldJson) {
35 | if (oldJson[key] && !json[key]) {
36 | json[key] = oldJson[key]
37 | }
38 | }
39 |
40 | outputStream.push(new gutil.File({
41 | path: lang + '.json',
42 | contents: Buffer.from(JSON.stringify(json, null, 2))
43 | }))
44 |
45 | if (--count <= 0) {
46 | outputStream.emit('end')
47 | }
48 | }).catch(function (err) {
49 | throw new PluginError(PLUGIN_NAME, err.message)
50 | })
51 | })
52 | } else {
53 | outputStream.emit('end')
54 | }
55 | return outputStream
56 | }
57 |
--------------------------------------------------------------------------------
/util.js:
--------------------------------------------------------------------------------
1 |
2 | var localesJA = require('./locales/ja.json')
3 | var localesEN = require('./locales/en.json')
4 | var localesZH = require('./locales/zh.json')
5 | var localesZH_TW = require('./locales/zh_tw.json')
6 | var localesKO = require('./locales/ko.json')
7 |
8 | exports.getLocales = getLocales
9 | function getLocales (language) {
10 | switch (language) {
11 | case 'ja':
12 | return localesJA
13 | case 'en':
14 | return localesEN
15 | case 'zh':
16 | return localesZH
17 | case 'zh_tw':
18 | return localesZH_TW
19 | case 'ko':
20 | return localesKO
21 | }
22 | return {}
23 | }
24 |
25 | exports.translateLocales = translateLocales
26 | function translateLocales (language, keys) {
27 | var originLocales = getLocales(language)
28 | var locales = {}
29 | keys.forEach(function (key) {
30 | locales[key] = originLocales[key] || ''
31 | })
32 | return translate(language, locales)
33 | }
34 |
35 | exports.mergeLocales = mergeLocales
36 | function mergeLocales (language, description) {
37 | var originLocales = getLocales(language)
38 | var keys = Object.keys(description)
39 | var locales = {}
40 | keys.forEach(function (key) {
41 | locales[key] = originLocales[key] || description[key] || ''
42 | })
43 | return translate(language, locales)
44 | }
45 |
46 | exports.translate = translate
47 | function translate (language, locales) {
48 | return 'i18n.setLocales(\'' + language + '\', ' + JSON.stringify(locales, null, 2) + ');'
49 | }
50 |
51 | var NAMESPACE_MARK = '@namespace:'
52 | exports.parseDescription = parseDescription
53 | function parseDescription (content) {
54 | var set = parseDescriptionSet(content)
55 | var namespace = set.namespace
56 | var description = set.description
57 | if (namespace) {
58 | var result = {}
59 | var keys = Object.keys(description)
60 | for (var i = 0, len = keys.length; i < len; i++) {
61 | var key = keys[i]
62 | result[namespace + key] = description[key]
63 | }
64 | return result
65 | } else {
66 | return description
67 | }
68 | }
69 |
70 | exports.parseDescriptionSet = parseDescriptionSet
71 | function parseDescriptionSet (content) {
72 | var namespace = ''
73 | var lines = content.trim().split('\n')
74 | var first = (lines[0] || '').trim()
75 |
76 | if (first.indexOf(NAMESPACE_MARK) === 0) {
77 | namespace = first.slice(NAMESPACE_MARK.length).trim()
78 | lines.shift()
79 | }
80 |
81 | var description = JSON.parse(lines.join('\n'))
82 | return {namespace: namespace, description: description}
83 | }
84 |
--------------------------------------------------------------------------------
/gulp/translate.js:
--------------------------------------------------------------------------------
1 |
2 | var urllib = require('urllib')
3 | var through = require('through2')
4 | var gutil = require('gulp-util')
5 | var path = require('path')
6 | var MD5 = require('./md5')
7 | var defaults = require('../locales/zh.json')
8 |
9 | var col = gutil.colors
10 | var PluginError = gutil.PluginError
11 | var PLUGIN_NAME = 'gulp-i18n-translate'
12 |
13 | // 文档页面 http://developer.baidu.com/ms/translate
14 | // 使用百度翻译,每小时1000次请求,每月200万字符,超过会收费的,注意频率
15 | var TRANSLATE_ID = '20151130000006954'
16 | var TRANSLATE_KEY = process.env.TRANSLATE_KEY
17 | var TRANSLATE_URL = 'http://api.fanyi.baidu.com/api/trans/vip/translate'
18 | var TRANSLATE_MAP = {
19 | 'ja': 'jp',
20 | 'ko': 'kor',
21 | 'zh_tw': 'cht'
22 | }
23 |
24 | function translate (contents, from, to, callback) {
25 | var optionsData = {
26 | from: from,
27 | to: TRANSLATE_MAP[to] || to,
28 | appid: TRANSLATE_ID
29 | }
30 | var data = JSON.parse(contents)
31 | var keys = Object.keys(data).filter(function (key) {
32 | return !data[key] && defaults[key]
33 | })
34 | function getNext () {
35 | var key = keys.pop()
36 | var value = defaults[key] || ''
37 | if (key) return {key: key, value: value}
38 | else return null
39 | }
40 | function transNext () {
41 | var next = getNext()
42 | if (!next) return callback(JSON.stringify(data, null, 2))
43 |
44 | var time = new Date().valueOf()
45 | gutil.log('Translate \'' + col.cyan(next.key) + '\' to ' + to + ' ...')
46 | optionsData.salt = time
47 | optionsData.q = next.value
48 | optionsData.sign = MD5(TRANSLATE_ID + next.value + time + TRANSLATE_KEY)
49 |
50 | urllib.request(TRANSLATE_URL, {data: optionsData}, function (err, result, res) {
51 | if (err) {
52 | throw new PluginError(PLUGIN_NAME, err.toString())
53 | }
54 | result = JSON.parse(result.toString())
55 | data[next.key] = result.trans_result[0].dst
56 | transNext()
57 | })
58 | }
59 | transNext()
60 | }
61 |
62 | module.exports = function (options) {
63 | options = options || {}
64 | var fromLang = options.from || 'zh'
65 |
66 | var outputStream = through.obj(function (file, enc, next) {
67 | if (!file.isBuffer()) return next()
68 |
69 | var extname = path.extname(file.path)
70 | var lang = path.basename(file.path, extname)
71 | if (lang === fromLang) { // 不翻译原文
72 | this.push(file)
73 | return next()
74 | } else {
75 | translate(file.contents.toString(), fromLang, lang, function (result) {
76 | file.contents = new Buffer(result)
77 | outputStream.push(file)
78 | next()
79 | })
80 | }
81 | })
82 | return outputStream
83 | }
84 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # i18n loader of teambition web for webpack
2 |
3 | ## Config
4 | ```
5 | mkdir config && touch config/default.json
6 | {
7 | "LANGUAGES": ["zh", "zh_tw", "en", "ja", "ko"],
8 | // use for gulp notice
9 | "TOKEN": "Your Teambition AccessToken",
10 | "TASKLIST_ID": "5809ebecad0e08685f6e49a8",
11 | "TEAM_ID": "5763667798cb0609458bacdd"
12 | }
13 | ```
14 |
15 | ## Usage
16 |
17 | [Documentation: Using loaders](http://webpack.github.io/docs/using-loaders.html)
18 |
19 | [tb-i18n](https://github.com/teambition/tb-i18n)
20 |
21 | Translate keys to register locales in `tb-i18n`
22 |
23 | ``` javascript
24 | var i18n = require("locales.i18n?languages[]=en&languages[]=zh&descriptionAs=zh");
25 | // register locales to i18n and return i18n object
26 | ```
27 |
28 | Translate from
29 |
30 | ```
31 | @namespace: example
32 | {
33 | "key1": "description1",
34 | "key2": "description2",
35 | "key3": "description3"
36 | }
37 | ```
38 |
39 | to
40 |
41 | ```javascript
42 | var i18n = require('tb-i18n');
43 | i18n.setLocales('en', {
44 | "example.key1": "en.value1",
45 | "example.key2": "en.value2",
46 | "example.key3": "en.value3"
47 | })
48 | i18n.setLocales('zh', {
49 | "example.key1": "zh.value1",
50 | "example.key2": "zh.value2",
51 | "example.key3": "zh.value3"
52 | })
53 | ```
54 |
55 | ## query
56 |
57 | ### languages: []string
58 | The string array of languages those to register.
59 |
60 | ### desciptionAs: string = ''
61 | Set the desciption as default value of the specified language.
62 |
63 |
64 | ## Attention
65 | Set the namespace at the first line!!!
66 |
67 | ## Develop
68 | export ONESKY_SECRET='OneSky Secret'
69 |
70 | export ONESKY_API_KEY='OneSky ApiKey'
71 |
72 | export TRANSLATE_KEY='Baidu Translate Key'
73 |
74 | ## 简体中文 翻译为 繁体中文
75 | 调用百度翻译 API 翻译 `/locales/zh.json` 并覆盖 `/locales/zh_tw.json`
76 | 将当前 简体中文 文档缓存(可省,但需保证 cache 内文档与 最新文档 不同)
77 |
78 | `$ gulp cache`
79 |
80 | 下载 最新文档 并比对与 cache 下版本的不同
81 |
82 | `$ gulp download`
83 |
84 | 翻译 - 参数:`-a | --all` 全量翻译; `-f | --force` 不执行 确认修改 直接覆盖
85 |
86 | `$ gulp chs-to-cht`
87 |
88 | 上传
89 |
90 | `$ gulp post-cht`
91 |
92 | ### chs-to-cht 遇到 54003(请求过于频繁)报错时
93 |
94 | 可以尝试 `--queriesPerSecond [qps]` 选项,如:
95 |
96 | `$ gulp chs-to-cht --queriesPerSecond 0.6`
97 |
98 | 指定了我们以 0.6 qps 为请求频率使用百度翻译服务,即:约 1.7秒 发一个请求。关于百度服务的层级,相关信息见:([链接1](http://api.fanyi.baidu.com/api/trans/product/prodinfo#0),[链接2](https://fanyiapp.cdn.bcebos.com/api/doc/%E7%99%BE%E5%BA%A6%E7%BF%BB%E8%AF%91%E5%BC%80%E6%94%BE%E5%B9%B3%E5%8F%B0%E9%80%9A%E7%94%A8%E7%BF%BB%E8%AF%91API%E6%9C%8D%E5%8A%A1%E5%8D%87%E7%BA%A7%E8%AF%B4%E6%98%8E.pdf))
99 |
100 | ## License
101 |
102 | MIT (http://www.opensource.org/licenses/mit-license.php)
103 |
--------------------------------------------------------------------------------
/keys/create-organization.json:
--------------------------------------------------------------------------------
1 | @namespace: apps.create-organization.
2 |
3 | {
4 | "title": "创建组织",
5 | "introContent": "协作之上,是更有意义的企业管理。Teambition 组织帮助你更好地管理团队、成员,并拥有无限项目数量。可以免费使用14天。",
6 | "tabInfo": "完善企业信息",
7 | "tabInfoOrgName": "企业名称",
8 | "tabInfoOrgNamePlace": "名称",
9 | "tabInfoOrgDesc": "描述",
10 | "tabInfoOrgDescPlace": "一句话描述",
11 | "tabInfoOrgIndustry": "行业",
12 | "tabInfoOrgIndustryPlace": "请选择所属行业",
13 | "tabInfoOrgIndustryFood": "餐饮食品",
14 | "tabInfoOrgIndustryTelecommunication": "电信",
15 | "tabInfoOrgIndustryLaw": "法律实践",
16 | "tabInfoOrgIndustryRealEstate": "房地产",
17 | "tabInfoOrgIndustryApparel": "服装",
18 | "tabInfoOrgIndustryManagement": "管理咨询",
19 | "tabInfoOrgIndustryAdvertising": "广告",
20 | "tabInfoOrgIndustryInternationalTrade": "国际贸易",
21 | "tabInfoOrgIndustryInternet": "互联网",
22 | "tabInfoOrgIndustryChemistry": "化学",
23 | "tabInfoOrgIndustryEventServices": "会展服务",
24 | "tabInfoOrgIndustryManufacture": "制造业",
25 | "tabInfoOrgIndustryFurnitures": "家具建材",
26 | "tabInfoOrgIndustryFinancial": "金融服务",
27 | "tabInfoOrgIndustryEnergy": "能源",
28 | "tabInfoOrgIndustryAutomotive": "汽车",
29 | "tabInfoOrgIndustryBiotechnology": "生物技术",
30 | "tabInfoOrgIndustryDesign": "设计",
31 | "tabInfoOrgIndustryLuxury": "奢侈品",
32 | "tabInfoOrgIndustryInformation": "信息技术与服务",
33 | "tabInfoOrgIndustryHealthCare": "医疗健康",
34 | "tabInfoOrgIndustryEntertainment": "文化",
35 | "tabInfoOrgIndustryTransportation": "运输",
36 | "tabInfoOrgIndustryCoaching": "职业培训",
37 | "tabContact": "填写联系人信息",
38 | "tabContactName": "联系人",
39 | "tabContactPhone": "手机",
40 | "tabContactPhoneBound": "该手机号已被绑定",
41 | "tabContactVerifyCodeSend": "发送验证码",
42 | "tabContactVerifyCodeSending": "正在发送中",
43 | "tabContactVerifyCodeRetry": "重新发送",
44 | "tabContactVerifyCodeRetryCD": "重新发送 (%s)",
45 | "tabContactVerifyCodeError": "验证码错误",
46 | "tabContactService": "顾问服务",
47 | "tabContactServiceTip": "请为我提供顾问服务(免费)",
48 | "tabMembers": "添加企业成员",
49 | "tabMembersBlankTip": "新成员的邮箱",
50 | "tabMembersErrorEmail": "邮箱格式错误",
51 | "tabMembersErrorRepeat": "该邮箱已是企业成员",
52 | "tabMembersOrgInfoTips": "当前企业成员上限为%s人,",
53 | "tabMembersOrgInfoTips2": "还可免费试用14天。您可以随时升级您的方案。",
54 | "tabMembersPriceLink": "查看所有方案",
55 | "tabMembersUpperLimitTip": "成员数超过上限",
56 | "tabMembersRemove": "移除成员",
57 | "tabMembersImportEmail": "批量输入邮箱",
58 | "tabMembersImportEmailTip": "将邮箱粘帖至输入框即可添加,不同邮箱间用换行分割",
59 | "tabMembersImportWorked": "导入合作过的成员",
60 | "tabMembersImportWorkedNoContent": "没有找到合作过的成员",
61 | "membersCountAddExceed": "已经超出 %s 人",
62 | "membersCountAddRemain": "还可以添加 %s 人",
63 | "membersCountUpperLimit": "企业成员达到上限",
64 | "membersCountUpdatePlan": "升级方案",
65 | "comma": ",",
66 | "modalEnter": "确定",
67 | "orgCreate": "创建企业",
68 | "orgCreating": "创建企业中",
69 | "stepPrev": "上一步",
70 | "stepNext": "下一步",
71 | "stepLast": "完成,并进入企业",
72 | "countrycode": "国家号",
73 | "china": "中国",
74 | "hongkong": "中国香港",
75 | "taiwan": "台湾",
76 | "japan": "日本",
77 | "unitedstates": "美国",
78 | "other": "其他",
79 | "tabContactVerifyCode": "验证码",
80 | "tabContactVerifyImage": "图片验证码",
81 | "captchaTitle": "请点击下图中的 %s",
82 | "captchaInvalided": "验证失败",
83 | "captchaValided": "验证成功",
84 | "captchaValidedFreq": "图片验证码错误次数过多, 请明天再试",
85 | "captchaTips": "请先选择图片验证码",
86 | "captchaOff": "无需验证",
87 | "captchaRefresh": "刷新验证码",
88 | "captchaSetupErr": "获取验证码出错"
89 | }
90 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 |
2 | var gulp = require('gulp')
3 | var sequence = require('gulp-sequence')
4 | var config = require('config')
5 | var minimist = require('minimist')
6 | var download = require('./gulp/download')
7 | var post = require('./gulp/post')
8 | var filter = require('./gulp/filter')
9 | var sorter = require('./gulp/sorter')
10 | var compare = require('./gulp/compare')
11 | var translate = require('./gulp/translate')
12 | var chsToCht = require('./gulp/chs-to-cht-parallel')
13 | var pickEmpty = require('./gulp/pick-empty')
14 | var util = require('./util')
15 | var fs = require('fs')
16 | var path = require('path')
17 |
18 | require('isomorphic-fetch')
19 |
20 | var i18nCacheDir = path.join(__dirname, './cache')
21 |
22 | var ONESKY_OPTIONS = {
23 | projectId: 153977
24 | }
25 |
26 | var params = minimist(process.argv.slice(3))
27 | var chsToChtOptions = {
28 | all: false,
29 | force: false,
30 | exec: false
31 | }
32 |
33 | if (process.argv[2] && process.argv[2] === 'chs-to-cht') {
34 | chsToChtOptions.all = params.a || params.all
35 | chsToChtOptions.force = params.f || params.force
36 | chsToChtOptions.queriesPerSecond = params.queriesPerSecond
37 | }
38 |
39 | if (params.y || params.exec) {
40 | chsToChtOptions.exec = true
41 | }
42 |
43 | if (params.queriesPerSecond) {
44 | chsToChtOptions.queriesPerSecond = params.queriesPerSecond
45 | }
46 |
47 | var localesDir = path.join(__dirname, 'locales')
48 | var getLocalesJson = function(file) {
49 | file = file || 'zh.json'
50 | return path.join(localesDir, file)
51 | }
52 |
53 | function readDescription () {
54 | var result = {}
55 | var dir = path.resolve(__dirname, './keys')
56 | fs.readdirSync(dir).forEach(function (fileName) {
57 | var filePath = path.resolve(dir, fileName)
58 | var contents = fs.readFileSync(filePath, 'utf-8')
59 | var description = util.parseDescription(contents)
60 | var keys = Object.keys(description)
61 |
62 | for (var i = 0, len = keys.length; i < len; i++) {
63 | result[keys[i]] = description[keys[i]]
64 | }
65 | })
66 | return result
67 | }
68 |
69 | gulp.task('translate', function () {
70 | var tmpDir = path.join(__dirname, 'tmp', '*.json')
71 | var translatedDir = path.join(__dirname, 'translated')
72 |
73 | return gulp.src(tmpDir)
74 | .pipe(translate({from: 'zh'}))
75 | .pipe(gulp.dest(translatedDir))
76 | })
77 |
78 | gulp.task('chs-to-cht', function () {
79 | return gulp.src(getLocalesJson())
80 | .pipe(chsToCht(chsToChtOptions))
81 | .pipe(gulp.dest(localesDir))
82 | })
83 |
84 | gulp.task('cache', function () {
85 | return gulp.src(getLocalesJson())
86 | .pipe(gulp.dest(i18nCacheDir))
87 | })
88 |
89 | gulp.task('raw-download', function () {
90 | return download(config.LANGUAGES, ONESKY_OPTIONS)
91 | .pipe(filter(readDescription(), 'zh'))
92 | .pipe(sorter())
93 | .pipe(gulp.dest(localesDir))
94 | })
95 |
96 | gulp.task('compare', ['raw-download'], function () {
97 | return gulp.src(getLocalesJson())
98 | .pipe(compare())
99 | .pipe(gulp.dest(i18nCacheDir))
100 | })
101 |
102 | gulp.task('download', ['compare'])
103 |
104 | gulp.task('pick-empty', function () {
105 | return gulp.src(getLocalesJson('*.json'))
106 | .pipe(pickEmpty('zh'))
107 | .pipe(gulp.dest('tmp'))
108 | })
109 |
110 | gulp.task('post', function () {
111 | return gulp.src(getLocalesJson())
112 | .pipe(post('zh', ONESKY_OPTIONS))
113 | })
114 |
115 | gulp.task('post-cht', ['cache'], function () {
116 | return gulp.src(getLocalesJson('zh_tw.json'))
117 | .pipe(post('zh_tw', ONESKY_OPTIONS))
118 | })
119 |
120 | gulp.task('ci:i18n', sequence('download', 'chs-to-cht', 'post-cht'))
121 |
122 | gulp.task('default', ['download'])
123 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 |
2 | require('should')
3 | var translate = require('./index')
4 | var describe = global.describe
5 | var it = global.it
6 |
7 | var localesJA = require('./locales/ja.json')
8 | var localesEN = require('./locales/en.json')
9 | var localesZH = require('./locales/zh.json')
10 | var localesZH_TW = require('./locales/zh_tw.json')
11 | var localesKO = require('./locales/ko.json')
12 |
13 | describe('test tb-i18n-loader', function () {
14 | it('translate keys', function () {
15 | var query = '?languages[]=en'
16 | var contextTranslate = translate.bind({query: query})
17 |
18 | var desciption = `
19 | {
20 | "FRI": "FRI",
21 | "MON": "MON",
22 | "key1": "key1"
23 | }
24 | `
25 | var expects = [
26 | 'var i18n = require(\'tb-i18n\');',
27 | 'i18n.setLocales(\'en\', {',
28 | ` "FRI": "${localesEN['FRI'] || ''}",`,
29 | ` "MON": "${localesEN['MON'] || ''}",`,
30 | ` "key1": "${localesEN['key1'] || ''}"`,
31 | '});',
32 | 'module.exports = i18n;'
33 | ]
34 | contextTranslate(desciption).should.eql(expects.join('\n'))
35 | })
36 |
37 | it('default languages', function () {
38 | var query = ''
39 | var contextTranslate = translate.bind({query: query})
40 |
41 | var desciption = `
42 | {
43 | "FRI": "FRI",
44 | "MON": "MON",
45 | "key1": "key1"
46 | }
47 | `
48 | var expects = [
49 | 'var i18n = require(\'tb-i18n\');',
50 | 'i18n.setLocales(\'zh\', {',
51 | ` "FRI": "${localesZH['FRI'] || ''}",`,
52 | ` "MON": "${localesZH['MON'] || ''}",`,
53 | ` "key1": "${localesZH['key1'] || ''}"`,
54 | '});',
55 | 'i18n.setLocales(\'zh_tw\', {',
56 | ` "FRI": "${localesZH_TW['FRI'] || ''}",`,
57 | ` "MON": "${localesZH_TW['MON'] || ''}",`,
58 | ` "key1": "${localesZH_TW['key1'] || ''}"`,
59 | '});',
60 | 'i18n.setLocales(\'en\', {',
61 | ` "FRI": "${localesEN['FRI'] || ''}",`,
62 | ` "MON": "${localesEN['MON'] || ''}",`,
63 | ` "key1": "${localesEN['key1'] || ''}"`,
64 | '});',
65 | 'i18n.setLocales(\'ja\', {',
66 | ` "FRI": "${localesJA['FRI'] || ''}",`,
67 | ` "MON": "${localesJA['MON'] || ''}",`,
68 | ` "key1": "${localesJA['key1'] || ''}"`,
69 | '});',
70 | 'i18n.setLocales(\'ko\', {',
71 | ` "FRI": "${localesKO['FRI'] || ''}",`,
72 | ` "MON": "${localesKO['MON'] || ''}",`,
73 | ` "key1": "${localesKO['key1'] || ''}"`,
74 | '});',
75 | 'module.exports = i18n;'
76 | ]
77 | contextTranslate(desciption).should.eql(expects.join('\n'))
78 | })
79 |
80 | it('with namespace', function () {
81 | var query = '?languages[]=en'
82 | var contextTranslate = translate.bind({query: query})
83 |
84 | var desciption = `
85 | @namespace: all.
86 | {
87 | "member": "member",
88 | "project": "project",
89 | "key1": "key1"
90 | }
91 | `
92 | var expects = [
93 | 'var i18n = require(\'tb-i18n\');',
94 | 'i18n.setLocales(\'en\', {',
95 | ` "all.member": "${localesEN['all.member'] || ''}",`,
96 | ` "all.project": "${localesEN['all.project'] || ''}",`,
97 | ` "all.key1": "${localesEN['all.key1'] || ''}"`,
98 | '});',
99 | 'module.exports = i18n;'
100 | ]
101 | contextTranslate(desciption).should.eql(expects.join('\n'))
102 | })
103 |
104 | it('desciption as', function () {
105 | var query = '?languages[]=en&&descriptionAs=en'
106 | var contextTranslate = translate.bind({query: query})
107 |
108 | var desciption = {
109 | 'FRI': 'desciption FRI',
110 | 'MON': 'desciption MON',
111 | 'key1': 'desciption key1'
112 | }
113 |
114 | var expects = [
115 | 'var i18n = require(\'tb-i18n\');',
116 | 'i18n.setLocales(\'en\', {',
117 | ` "FRI": "${desciption['FRI']}",`,
118 | ` "MON": "${desciption['MON']}",`,
119 | ` "key1": "${desciption['key1']}"`,
120 | '});',
121 | 'module.exports = i18n;'
122 | ]
123 | contextTranslate(JSON.stringify(desciption)).should.eql(expects.join('\n'))
124 | })
125 | })
126 |
--------------------------------------------------------------------------------
/gulp/chs-to-cht.js:
--------------------------------------------------------------------------------
1 | // 串行调用接口
2 | var urllib = require('urllib')
3 | var through = require('through2')
4 | var inquirer = require('inquirer')
5 | var gutil = require('gulp-util')
6 | var path = require('path')
7 | var fs = require('fs')
8 | var isEmpty = require('lodash').isEmpty
9 | var MD5 = require('./md5')
10 | var defaults = require('../locales/zh.json')
11 | var defaultsTW = require('../locales/zh_tw.json')
12 |
13 | var col = gutil.colors
14 | var PluginError = gutil.PluginError
15 | var PLUGIN_NAME = 'gulp-i18n-translate'
16 |
17 | // 文档页面 http://developer.baidu.com/ms/translate
18 | // 使用百度翻译,每小时1000次请求,每月200万字符,超过会收费的,注意频率
19 | var TRANSLATE_ID = process.env.TRANSLATE_ID
20 | var TRANSLATE_KEY = process.env.TRANSLATE_KEY
21 | var TRANSLATE_URL = 'http://api.fanyi.baidu.com/api/trans/vip/translate'
22 | var TRANSLATE_MAP = {
23 | 'ja': 'jp',
24 | 'ko': 'kor',
25 | 'zh_tw': 'cht'
26 | }
27 |
28 | function translate(contents, from, to, callback) {
29 | var results = defaultsTW
30 | var optionsData = {
31 | from: from,
32 | to: TRANSLATE_MAP[to] || to,
33 | appid: TRANSLATE_ID
34 | }
35 | var data = JSON.parse(contents)
36 | var keys = Object.keys(data)
37 | var errorMessages = []
38 |
39 | var poppedKey = keys[keys.length - 1]
40 | function getNext() {
41 | var key
42 | if (data[poppedKey] !== null) {
43 | poppedKey = keys[keys.length - 1]
44 | key = keys.pop()
45 | } else {
46 | key = poppedKey
47 | }
48 | var value = defaults[key] || ''
49 | if (key) {
50 | return { key: key, value: value }
51 | }
52 | else {
53 | if (!isEmpty(errorMessages)) {
54 | gutil.log('---------------------------- ' + col.red('Errors During Translation Process') + ' ----------------------------')
55 | gutil.log(errorMessages)
56 | }
57 | return null
58 | }
59 | }
60 |
61 | function transNext() {
62 | var next = getNext()
63 | if (!next) return callback(JSON.stringify(results, null, 2))
64 |
65 | if (next.key && next.value === '') {
66 | data[next.key] = ''
67 | delete results[next.key]
68 | transNext()
69 | } else {
70 | var time = new Date().valueOf()
71 | gutil.log('Translate \'' + col.cyan(next.key) + '\' to ' + to + ' ...')
72 | optionsData.salt = time
73 | optionsData.q = next.value
74 | optionsData.sign = MD5(TRANSLATE_ID + next.value + time + TRANSLATE_KEY)
75 |
76 | urllib.request(TRANSLATE_URL, {data: optionsData}, function (err, result, res) {
77 | if (err) {
78 | throw new PluginError(PLUGIN_NAME, err.toString())
79 | }
80 | result = JSON.parse(result.toString())
81 | if (result.trans_result && result.trans_result[0]) {
82 | // 翻译成功加入列表
83 | data[next.key] = result.trans_result[0].dst
84 | results[next.key] = result.trans_result[0].dst
85 | } else {
86 | // 翻译不成功,将错误保存,置空当前字段,经过上面的逻辑重试这个字段
87 | errorMessages.push({ key: next.key, result: result })
88 | data[next.key] = null
89 | }
90 | transNext()
91 | })
92 | }
93 |
94 | }
95 |
96 | transNext()
97 | }
98 |
99 | module.exports = function (options) {
100 | options = options || {}
101 | var fromLang = 'zh'
102 | var lang = 'zh_tw'
103 | var diffFile = fs.readFileSync(path.join(__dirname, '../cache/diff.json'), 'utf-8')
104 | var diff = JSON.parse(diffFile)
105 |
106 | var outputStream = through.obj(function (file, enc, next) {
107 | if (!file.isBuffer()) return next()
108 |
109 | var contents = JSON.stringify(defaults, null, 2)
110 | if (!options.all) {
111 | var flatten = {}
112 | diff.added.forEach((added) => {
113 | flatten[added.key] = added.value
114 | })
115 | diff.modified.forEach((modified) => {
116 | flatten[modified.key] = modified.newValue
117 | })
118 | diff.deleted.forEach((deleted) => {
119 | flatten[deleted.key] = ''
120 | })
121 | contents = JSON.stringify(flatten, null, 2)
122 | }
123 |
124 | function printDiff() {
125 | gutil.log('---------------------------- ' + col.cyan('Diffs') + ' ----------------------------')
126 | if (!diff.added.length && !diff.modified.length && !diff.deleted.length) {
127 | gutil.log('None.')
128 | } else {
129 | diff.added.forEach((added) => {
130 | gutil.log(col.green('Added') + ' --> ' + added.key + ': ' + added.value)
131 | })
132 | diff.modified.forEach((modified) => {
133 | gutil.log(col.cyan('Modified') + ' --> ' + modified.key + ': ' + modified.oldValue + col.cyan(' -> ') + modified.newValue)
134 | })
135 | diff.deleted.forEach((deleted) => {
136 | gutil.log(col.red('Deleted') + ' --> ' + deleted.key + ': ' + deleted.value)
137 | })
138 | }
139 | }
140 |
141 | function execute() {
142 | return translate(contents, fromLang, lang, function (result) {
143 | var newFile = file
144 | newFile.path = path.dirname(file.path) + '/' + lang + '.json'
145 | newFile.contents = new Buffer(result)
146 | outputStream.push(newFile)
147 | return next()
148 | })
149 | }
150 |
151 | if (!options.force) {
152 | printDiff()
153 | inquirer.prompt({
154 | type: 'confirm',
155 | name: 'confirmTranslate',
156 | message: 'Execute Translating ?',
157 | }).then(answers => {
158 | if (answers.confirmTranslate) {
159 | execute()
160 | } else {
161 | return next()
162 | }
163 | })
164 | } else {
165 | execute()
166 | }
167 | })
168 |
169 | return outputStream
170 | }
171 |
--------------------------------------------------------------------------------
/keys/permissions.json:
--------------------------------------------------------------------------------
1 | @namespace: apps.permissions.
2 |
3 | {
4 | "title": "权限设置",
5 | "header.serviceTel": "官方客服",
6 | "default": "默认",
7 | "default.tip": "默认权限适用于该企业的所有项目",
8 | "app.pms.succPost": "成功添加角色",
9 | "app.pms.succPut": "成功更新角色",
10 | "app.pms.succDel": "成功删除角色",
11 | "app.pms.hasRoleManager": "已有正在编辑的角色",
12 | "app.pms.noPermission": "没有该企业的权限",
13 | "app.pms.noName": "角色名称不能为空",
14 | "info.defaultName": "Teambition 默认权限表",
15 | "org.name": "企业权限",
16 | "project.name": "项目权限",
17 | "project.addRole": "自定义权限",
18 | "role.guest": "访客",
19 | "role.member": "成员",
20 | "role.admin": "管理员",
21 | "role.owner": "拥有者",
22 | "role.new": "角色名称",
23 | "role.manager.default": "设为默认角色",
24 | "role.manager.edit": "编辑角色",
25 | "role.manager.del": "删除角色",
26 | "role.manager.cancel": "取消",
27 | "role.manager.save": "保存",
28 | "group.project": "项目",
29 | "group.activity": "动态",
30 | "group.object": "关联与点赞",
31 | "group.tasklist": "任务列表",
32 | "group.stage|stagetemplate": "任务阶段",
33 | "group.task": "任务",
34 | "group.subtask": "子任务",
35 | "group.post": "分享墙",
36 | "group.event": "日程",
37 | "group.collection": "文件库",
38 | "group.work": "文件操作",
39 | "group.tag": "标签",
40 | "group.entry|bookkeeping": "记账",
41 | "pms.organization.get.info": "查看企业基本信息",
42 | "pms.organization.put.info": "更新企业基本信息",
43 | "pms.organization.post.member": "添加企业成员",
44 | "pms.organization.put.member": "更新企业/企业项目
成员权限",
45 | "pms.organization.del.member": "删除企业成员",
46 | "pms.organization.put.transferIn": "迁入项目",
47 | "pms.organization.put.transferOut": "迁出项目",
48 | "pms.organization.put.dividers": "更新企业项目分组",
49 | "pms.organization.put.defaultrole": "设置默认角色",
50 | "pms.organization.del": "删除企业",
51 | "pms.organization.post.project": "创建企业项目",
52 | "pms.organization.del.project": "删除企业项目",
53 | "pms.team.post": "添加团队",
54 | "pms.team.del": "删除团队",
55 | "pms.role.post": "添加自定义角色",
56 | "pms.role.del": "移除自定义角色",
57 | "pms.role.put": "更新自定义角色",
58 | "pms.project.put.info": "编辑项目信息",
59 | "pms.project.put.archive": "归档项目",
60 | "pms.project.del": "删除项目",
61 | "pms.project.post.member": "添加项目成员",
62 | "pms.project.put.member": "设置成员权限",
63 | "pms.project.del.member": "删除成员",
64 | "pms.project.put.navigation": "更新项目导航",
65 | "pms.project.put.transfer": "移交项目",
66 | "pms.project.get.subscribe": "订阅项目日程/任务",
67 | "pms.project.put.progress": "更新项目进展",
68 | "pms.project.put.fork": "复制项目",
69 | "pms.project.put.defaultrole": "设置默认角色",
70 | "pms.project.put.dividerindex": "修改项目分组",
71 | "pms.project.put.integration": "切换插件开关",
72 | "pms.activity.post": "发布回复",
73 | "pms.activity.put": "修改回复",
74 | "pms.activity.del": "删除回复",
75 | "pms.objectlink.post": "添加关联",
76 | "pms.objectlink.del": "移除关联",
77 | "pms.object.put.tag": "标注标签 ",
78 | "pms.object.del.tag": "撤销标签",
79 | "pms.object.put.like": "点赞/取消点赞",
80 | "pms.object.put.involvers": "更新参与者",
81 | "pms.object.del.involvers": "移除参与者",
82 | "pms.object.put.visible": "更新参与者可见",
83 | "pms.tasklist.post": "创建任务分组",
84 | "pms.tasklist.put": "编辑任务分组",
85 | "pms.tasklist.put.archive": "归档任务分组",
86 | "pms.tasklist.put.mode": "更新分组模式",
87 | "pms.tasklist.put.stageSort": "更新分组阶段排序",
88 | "pms.tasklist.del": "删除任务分组",
89 | "pms.tasklist.get.dumpTasks": "导出分组任务",
90 | "pms.stage.post": "新增任务阶段",
91 | "pms.stage.del": "删除任务阶段",
92 | "pms.stage.put": "更新任务阶段",
93 | "pms.stagetemplate.post": "保存阶段模版",
94 | "pms.task.post": "创建任务",
95 | "pms.task.put": "编辑任务",
96 | "pms.task.put.executor": "更新任务执行者",
97 | "pms.task.put.dueDate": "更新任务截止时间",
98 | "pms.task.put.status": "更新任务状态",
99 | "pms.task.put.note": "更新任务备忘",
100 | "pms.task.put.fork": "复制任务",
101 | "pms.task.put.move": "移动任务",
102 | "pms.task.put.archive": "归档任务",
103 | "pms.task.del": "删除任务",
104 | "pms.subtask.post": "添加子任务",
105 | "pms.subtask.put": "编辑子任务",
106 | "pms.subtask.del": "删除子任务",
107 | "pms.subtask.put.transform": "子任务转换成任务",
108 | "pms.subtask.put.status": "更新子任务状态",
109 | "pms.subtask.put.executor": "更新子任务执行者",
110 | "pms.subtask.put.dueDate": "更新子任务截止时间",
111 | "pms.post.post": "创建分享",
112 | "pms.post.put": "编辑分享",
113 | "pms.post.put.pin": "置顶分享",
114 | "pms.post.put.archive": "归档分享",
115 | "pms.post.del": "删除分享",
116 | "pms.event.post": "创建日程",
117 | "pms.event.put": "编辑日程",
118 | "pms.event.put.content": "编辑日程备忘",
119 | "pms.event.put.archive": "归档日程",
120 | "pms.event.del": "删除日程",
121 | "pms.collection.post": "创建文件集",
122 | "pms.collection.put": "编辑文件集",
123 | "pms.collection.put.move": "移动文件集",
124 | "pms.collection.put.archive": "归档文件集",
125 | "pms.collection.del": "删除文件集",
126 | "pms.work.post": "上传文件",
127 | "pms.work.get.download": "下载文件",
128 | "pms.work.put.move": "移动文件",
129 | "pms.work.put.version": "更新文件版本",
130 | "pms.work.put": "编辑文件",
131 | "pms.work.put.archive": "归档文件",
132 | "pms.work.del": "删除文件",
133 | "pms.tag.post": "新建标签",
134 | "pms.tag.del": "删除标签",
135 | "pms.tag.put.archive": "归档标签",
136 | "pms.tag.put": "编辑标签",
137 | "pms.entry.post": "记录收入/支出",
138 | "pms.entry.put": "编辑收入/支出",
139 | "pms.entry.put.approve": "审批账目",
140 | "pms.entry.put.archive": "归档账目",
141 | "pms.entry.del": "删除账目",
142 | "pms.bookkeeping.put.approvers": "账薄设定审批者",
143 | "pms.bookkeeping.put.fields": "账薄显示字段设置",
144 | "pms.bookkeeping.put.categories": "账薄分类设置",
145 | "group.hook": "Webhook",
146 | "pms.project.post.progress": "发布进展",
147 | "pms.entrycategory.del": "删除账目分类",
148 | "pms.entrycategory.post": "创建账目分类",
149 | "pms.entrycategory.put": "修改账目分类",
150 | "pms.hook.del": "删除webhook",
151 | "pms.hook.post": "创建webhook",
152 | "pms.hook.put": "更新webhook",
153 | "pms.stage.put.archive": "归档阶段",
154 | "pms.tasklist.put.move": "移动任务分组"
155 | }
156 |
--------------------------------------------------------------------------------
/gulp/chs-to-cht-parallel.js:
--------------------------------------------------------------------------------
1 | // 并发调用接口
2 | var urllib = require('urllib')
3 | var through = require('through2')
4 | var inquirer = require('inquirer')
5 | var gutil = require('gulp-util')
6 | var path = require('path')
7 | var fs = require('fs')
8 | var _ = require('lodash')
9 | var MD5 = require('./md5')
10 | var defaults = require('../locales/zh.json')
11 | var defaultsTW = require('../locales/zh_tw.json')
12 |
13 | var col = gutil.colors
14 | var PluginError = gutil.PluginError
15 | var PLUGIN_NAME = 'gulp-i18n-translate'
16 |
17 | // 文档页面 http://developer.baidu.com/ms/translate
18 | // 使用百度翻译,每小时1000次请求,每月200万字符,超过会收费的,注意频率
19 | var TRANSLATE_ID = process.env.TRANSLATE_ID
20 | var TRANSLATE_KEY = process.env.TRANSLATE_KEY
21 | var TRANSLATE_URL = 'http://api.fanyi.baidu.com/api/trans/vip/translate'
22 | var TRANSLATE_MAP = {
23 | 'ja': 'jp',
24 | 'ko': 'kor',
25 | 'zh_tw': 'cht'
26 | }
27 |
28 | function translate(contents, from, to, callback, options) {
29 | var results = defaultsTW
30 | var optionsData = {
31 | from: from,
32 | to: TRANSLATE_MAP[to] || to,
33 | appid: TRANSLATE_ID
34 | }
35 | var data = JSON.parse(contents)
36 | var keys = Object.keys(data)
37 | var errorMessages = []
38 |
39 | function requestTranslate(key, value) {
40 | var time = new Date().valueOf()
41 | optionsData.salt = time
42 | optionsData.q = value
43 | optionsData.sign = MD5(TRANSLATE_ID + value + time + TRANSLATE_KEY)
44 |
45 | gutil.log('Translate \'' + col.cyan(key) + '\' to ' + to + ' ...')
46 | return urllib.request(TRANSLATE_URL, { data: optionsData })
47 | }
48 |
49 | function processBatch(batch) {
50 | var promiseList = []
51 | batch.forEach((key) => {
52 | if (key && data[key] === '') {
53 | delete results[key]
54 | data[key] = ''
55 | } else {
56 | promiseList.push(
57 | requestTranslate(key, data[key])
58 | .then((res) => {
59 | var result = JSON.parse(res.data.toString())
60 | if (result.trans_result && result.trans_result[0]) {
61 | console.log('success')
62 | // 翻译成功加入列表
63 | data[key] = result.trans_result[0].dst
64 | results[key] = result.trans_result[0].dst
65 | } else {
66 | // 翻译不成功,将错误保存,置空当前字段,经过上面的逻辑重试这个字段
67 | errorMessages.push({ key: key, result: result })
68 | if (result.error_code === '52001' || result.error_code === '52002') {
69 | return requestTranslate(key, data[key])
70 | } else {
71 | throw new PluginError(PLUGIN_NAME, result.toString())
72 | }
73 | }
74 | })
75 | .catch((err) => {
76 | console.error(err)
77 | })
78 | )
79 | }
80 | })
81 | return Promise.all(promiseList)
82 | }
83 |
84 | queriesPerSecond = options.queriesPerSecond || 5
85 | batchSize = Math.ceil(queriesPerSecond)
86 | var batches = _.chunk(keys, batchSize)
87 | allBatchesComplete = batches.reduce((resolvedBatches, batch) => {
88 | return resolvedBatches.then(() => {
89 | var ms = 1000
90 | if (queriesPerSecond < 1) {
91 | ms = ms * 1 / queriesPerSecond
92 | }
93 | return Promise.all([
94 | processBatch(batch),
95 | new Promise((resolve) => setTimeout(resolve, ms))
96 | ])
97 | })
98 | }, Promise.resolve())
99 |
100 | allBatchesComplete
101 | .then(() => {
102 | if (!_.isEmpty(errorMessages)) {
103 | gutil.log('---------------------------- ' + col.red('Errors During Translation Process') + ' ----------------------------')
104 | gutil.log(errorMessages)
105 | }
106 | return callback(JSON.stringify(results, null, 2))
107 | })
108 | }
109 |
110 | module.exports = function (options) {
111 | options = options || {}
112 | var fromLang = 'zh'
113 | var lang = 'zh_tw'
114 | var diffFile = fs.readFileSync(path.join(__dirname, '../cache/diff.json'), 'utf-8')
115 | var diff = JSON.parse(diffFile)
116 |
117 | var outputStream = through.obj(function (file, enc, next) {
118 | if (!file.isBuffer()) return next()
119 |
120 | var contents = JSON.stringify(defaults, null, 2)
121 | if (!options.all) {
122 | var flatten = {}
123 | diff.added.forEach((added) => {
124 | flatten[added.key] = added.value
125 | })
126 | diff.modified.forEach((modified) => {
127 | flatten[modified.key] = modified.newValue
128 | })
129 | diff.deleted.forEach((deleted) => {
130 | flatten[deleted.key] = ''
131 | })
132 | contents = JSON.stringify(flatten, null, 2)
133 | }
134 |
135 | function printDiff() {
136 | gutil.log('---------------------------- ' + col.cyan('Diffs') + ' ----------------------------')
137 | if (_.isEmpty(diff.added) && _.isEmpty(diff.modified) && _.isEmpty(diff.deleted)) {
138 | gutil.log('None.')
139 | } else {
140 | diff.added.forEach((added) => {
141 | gutil.log(col.green('Added') + ' --> ' + added.key + ': ' + added.value)
142 | })
143 | diff.modified.forEach((modified) => {
144 | gutil.log(col.cyan('Modified') + ' --> ' + modified.key + ': ' + modified.oldValue + col.cyan(' -> ') + modified.newValue)
145 | })
146 | diff.deleted.forEach((deleted) => {
147 | gutil.log(col.red('Deleted') + ' --> ' + deleted.key + ': ' + deleted.value)
148 | })
149 | }
150 | }
151 |
152 | function execute() {
153 | return translate(contents, fromLang, lang, function (result) {
154 | var newFile = file
155 | newFile.path = path.dirname(file.path) + '/' + lang + '.json'
156 | newFile.contents = new Buffer(result)
157 | outputStream.push(newFile)
158 | return next()
159 | }, {
160 | queriesPerSecond: options.queriesPerSecond
161 | })
162 | }
163 |
164 | if (!options.force) {
165 | printDiff()
166 | if (!options.exec) {
167 | inquirer.prompt({
168 | type: 'confirm',
169 | name: 'confirmTranslate',
170 | message: 'Execute Translating ?',
171 | }).then(answers => {
172 | if (answers.confirmTranslate) {
173 | execute()
174 | } else {
175 | return next()
176 | }
177 | })
178 | } else {
179 | execute()
180 | }
181 | } else {
182 | execute()
183 | }
184 | })
185 |
186 | return outputStream
187 | }
188 |
--------------------------------------------------------------------------------
/gulp/md5.js:
--------------------------------------------------------------------------------
1 | // MD5 for translate
2 | module.exports = function (string) {
3 | function RotateLeft (lValue, iShiftBits) {
4 | return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits))
5 | }
6 |
7 | function AddUnsigned (lX, lY) {
8 | var lX4, lY4, lX8, lY8, lResult
9 | lX8 = (lX & 0x80000000)
10 | lY8 = (lY & 0x80000000)
11 | lX4 = (lX & 0x40000000)
12 | lY4 = (lY & 0x40000000)
13 | lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF)
14 | if (lX4 & lY4) {
15 | return (lResult ^ 0x80000000 ^ lX8 ^ lY8)
16 | }
17 | if (lX4 | lY4) {
18 | if (lResult & 0x40000000) {
19 | return (lResult ^ 0xC0000000 ^ lX8 ^ lY8)
20 | } else {
21 | return (lResult ^ 0x40000000 ^ lX8 ^ lY8)
22 | }
23 | } else {
24 | return (lResult ^ lX8 ^ lY8)
25 | }
26 | }
27 |
28 | function F (x, y, z) { return (x & y) | ((~x) & z) }
29 | function G (x, y, z) { return (x & z) | (y & (~z)) }
30 | function H (x, y, z) { return (x ^ y ^ z) }
31 | function I (x, y, z) { return (y ^ (x | (~z))) }
32 |
33 | function FF (a, b, c, d, x, s, ac) {
34 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac))
35 | return AddUnsigned(RotateLeft(a, s), b)
36 | }
37 |
38 | function GG (a, b, c, d, x, s, ac) {
39 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac))
40 | return AddUnsigned(RotateLeft(a, s), b)
41 | }
42 |
43 | function HH (a, b, c, d, x, s, ac) {
44 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac))
45 | return AddUnsigned(RotateLeft(a, s), b)
46 | }
47 |
48 | function II (a, b, c, d, x, s, ac) {
49 | a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac))
50 | return AddUnsigned(RotateLeft(a, s), b)
51 | }
52 |
53 | function ConvertToWordArray (string) {
54 | var lWordCount
55 | var lMessageLength = string.length
56 | var lNumberOfWords_temp1 = lMessageLength + 8
57 | var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64
58 | var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16
59 | var lWordArray = Array(lNumberOfWords - 1)
60 | var lBytePosition = 0
61 | var lByteCount = 0
62 | while (lByteCount < lMessageLength) {
63 | lWordCount = (lByteCount - (lByteCount % 4)) / 4
64 | lBytePosition = (lByteCount % 4) * 8
65 | lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition))
66 | lByteCount++
67 | }
68 | lWordCount = (lByteCount - (lByteCount % 4)) / 4
69 | lBytePosition = (lByteCount % 4) * 8
70 | lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition)
71 | lWordArray[lNumberOfWords - 2] = lMessageLength << 3
72 | lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29
73 | return lWordArray
74 | }
75 |
76 | function WordToHex (lValue) {
77 | var lByte, lCount
78 | var WordToHexValue = ''
79 | var WordToHexValue_temp = ''
80 | for (lCount = 0; lCount <= 3; lCount++) {
81 | lByte = (lValue >>> (lCount * 8)) & 255
82 | WordToHexValue_temp = '0' + lByte.toString(16)
83 | WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2)
84 | }
85 | return WordToHexValue
86 | }
87 |
88 | function Utf8Encode (string) {
89 | string = string.replace(/\r\n/g, '\n')
90 | var utftext = ''
91 |
92 | for (var n = 0; n < string.length; n++) {
93 | var c = string.charCodeAt(n)
94 |
95 | if (c < 128) {
96 | utftext += String.fromCharCode(c)
97 | } else if ((c > 127) && (c < 2048)) {
98 | utftext += String.fromCharCode((c >> 6) | 192)
99 | utftext += String.fromCharCode((c & 63) | 128)
100 | } else {
101 | utftext += String.fromCharCode((c >> 12) | 224)
102 | utftext += String.fromCharCode(((c >> 6) & 63) | 128)
103 | utftext += String.fromCharCode((c & 63) | 128)
104 | }
105 | }
106 | return utftext
107 | }
108 |
109 | var x = []
110 | var k, AA, BB, CC, DD, a, b, c, d
111 | var S11 = 7
112 | var S12 = 12
113 | var S13 = 17
114 | var S14 = 22
115 | var S21 = 5
116 | var S22 = 9
117 | var S23 = 14
118 | var S24 = 20
119 | var S31 = 4
120 | var S32 = 11
121 | var S33 = 16
122 | var S34 = 23
123 | var S41 = 6
124 | var S42 = 10
125 | var S43 = 15
126 | var S44 = 21
127 |
128 | string = Utf8Encode(string)
129 |
130 | x = ConvertToWordArray(string)
131 |
132 | a = 0x67452301
133 | b = 0xEFCDAB89
134 | c = 0x98BADCFE
135 | d = 0x10325476
136 |
137 | for (k = 0; k < x.length; k += 16) {
138 | AA = a; BB = b; CC = c; DD = d
139 | a = FF(a, b, c, d, x[k + 0], S11, 0xD76AA478)
140 | d = FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756)
141 | c = FF(c, d, a, b, x[k + 2], S13, 0x242070DB)
142 | b = FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE)
143 | a = FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF)
144 | d = FF(d, a, b, c, x[k + 5], S12, 0x4787C62A)
145 | c = FF(c, d, a, b, x[k + 6], S13, 0xA8304613)
146 | b = FF(b, c, d, a, x[k + 7], S14, 0xFD469501)
147 | a = FF(a, b, c, d, x[k + 8], S11, 0x698098D8)
148 | d = FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF)
149 | c = FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1)
150 | b = FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE)
151 | a = FF(a, b, c, d, x[k + 12], S11, 0x6B901122)
152 | d = FF(d, a, b, c, x[k + 13], S12, 0xFD987193)
153 | c = FF(c, d, a, b, x[k + 14], S13, 0xA679438E)
154 | b = FF(b, c, d, a, x[k + 15], S14, 0x49B40821)
155 | a = GG(a, b, c, d, x[k + 1], S21, 0xF61E2562)
156 | d = GG(d, a, b, c, x[k + 6], S22, 0xC040B340)
157 | c = GG(c, d, a, b, x[k + 11], S23, 0x265E5A51)
158 | b = GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA)
159 | a = GG(a, b, c, d, x[k + 5], S21, 0xD62F105D)
160 | d = GG(d, a, b, c, x[k + 10], S22, 0x2441453)
161 | c = GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681)
162 | b = GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8)
163 | a = GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6)
164 | d = GG(d, a, b, c, x[k + 14], S22, 0xC33707D6)
165 | c = GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87)
166 | b = GG(b, c, d, a, x[k + 8], S24, 0x455A14ED)
167 | a = GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905)
168 | d = GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8)
169 | c = GG(c, d, a, b, x[k + 7], S23, 0x676F02D9)
170 | b = GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A)
171 | a = HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942)
172 | d = HH(d, a, b, c, x[k + 8], S32, 0x8771F681)
173 | c = HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122)
174 | b = HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C)
175 | a = HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44)
176 | d = HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9)
177 | c = HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60)
178 | b = HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70)
179 | a = HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6)
180 | d = HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA)
181 | c = HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085)
182 | b = HH(b, c, d, a, x[k + 6], S34, 0x4881D05)
183 | a = HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039)
184 | d = HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5)
185 | c = HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8)
186 | b = HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665)
187 | a = II(a, b, c, d, x[k + 0], S41, 0xF4292244)
188 | d = II(d, a, b, c, x[k + 7], S42, 0x432AFF97)
189 | c = II(c, d, a, b, x[k + 14], S43, 0xAB9423A7)
190 | b = II(b, c, d, a, x[k + 5], S44, 0xFC93A039)
191 | a = II(a, b, c, d, x[k + 12], S41, 0x655B59C3)
192 | d = II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92)
193 | c = II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D)
194 | b = II(b, c, d, a, x[k + 1], S44, 0x85845DD1)
195 | a = II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F)
196 | d = II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0)
197 | c = II(c, d, a, b, x[k + 6], S43, 0xA3014314)
198 | b = II(b, c, d, a, x[k + 13], S44, 0x4E0811A1)
199 | a = II(a, b, c, d, x[k + 4], S41, 0xF7537E82)
200 | d = II(d, a, b, c, x[k + 11], S42, 0xBD3AF235)
201 | c = II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB)
202 | b = II(b, c, d, a, x[k + 9], S44, 0xEB86D391)
203 | a = AddUnsigned(a, AA)
204 | b = AddUnsigned(b, BB)
205 | c = AddUnsigned(c, CC)
206 | d = AddUnsigned(d, DD)
207 | }
208 |
209 | var temp = WordToHex(a) + WordToHex(b) + WordToHex(c) + WordToHex(d)
210 |
211 | return temp.toLowerCase()
212 | }
213 |
--------------------------------------------------------------------------------
/keys/teambition.json:
--------------------------------------------------------------------------------
1 | {
2 | "FRI": "星期五",
3 | "MON": "星期一",
4 | "SAT": "星期六",
5 | "SUN": "星期日",
6 | "THU": "星期四",
7 | "TUE": "星期二",
8 | "WED": "星期三",
9 | "add": "添加",
10 | "added": "已添加",
11 | "all": "所有分享",
12 | "archive": "归档",
13 | "back": "返回",
14 | "birthday": "生日",
15 | "bookkeeping": "记账",
16 | "calendar": "日历",
17 | "cancel": "取消",
18 | "clickToEdit": "点击即可编辑",
19 | "collapse": "收起",
20 | "collection": "文件夹",
21 | "colon": ":",
22 | "comma": ",",
23 | "confirm": "确定",
24 | "connected": "网络已连上,欢迎回来",
25 | "copyAs": "复制为",
26 | "copyTo": "复制到",
27 | "create": "创建",
28 | "creator": "创建者",
29 | "date": "",
30 | "day": "天",
31 | "delete": "删除",
32 | "disconnected": "网络有异常,请检查网络后再试",
33 | "done": "完成",
34 | "draft": "检测到有未保存内容,是否恢复?",
35 | "dropFile": "",
36 | "edit": "编辑",
37 | "email": "邮箱",
38 | "entry": "账目",
39 | "error": "对不起,出错啦!",
40 | "event": "日程",
41 | "events": "",
42 | "executor": "执行者",
43 | "expand": "展开",
44 | "favorite": "收藏",
45 | "file": "文件",
46 | "files": "文件",
47 | "groupchat": "群聊",
48 | "guest": "访客",
49 | "hide": "隐藏",
50 | "inbox": "收件箱",
51 | "language": "",
52 | "location": "地址",
53 | "login": "登录",
54 | "logout": "退出登录",
55 | "manager": "管理员",
56 | "me": "我",
57 | "member": "成员",
58 | "members": "成员",
59 | "mobile": "电话",
60 | "month": "月",
61 | "more": "更多",
62 | "moveTo": "移动到",
63 | "my": "我的",
64 | "noRight": "您不能修改",
65 | "organization": "企业",
66 | "owner": "拥有者",
67 | "paynow": "立即付款",
68 | "post": "分享",
69 | "posts": "",
70 | "project": "项目",
71 | "projects": "项目",
72 | "public": "公开",
73 | "register": "注册",
74 | "remove": "移除",
75 | "repeat": "重复",
76 | "reply": "回复",
77 | "rmb": "元",
78 | "save": "保存",
79 | "search": "搜索",
80 | "setting": "设置",
81 | "show": "显示",
82 | "stage": "阶段",
83 | "tags": "标签",
84 | "task": "任务",
85 | "tasklist": "任务分组",
86 | "tasks": "任务",
87 | "time": "",
88 | "today": "今天",
89 | "unarchive": "取消归档",
90 | "unfavorite": "取消收藏",
91 | "unknown": "未知",
92 | "unknownDate": "未知日期",
93 | "upload": "上传",
94 | "website": "网址",
95 | "week": "周",
96 | "year": "年",
97 | "acitivity.timeline.notif.none.tip": "没有人会收到通知",
98 | "acitivity.timeline.notif.tip": "人将会收到通知",
99 | "activity.card.body.checkMore": "查看更多",
100 | "activity.card.create.headerTips": "创建了项目",
101 | "activity.card.error.notFound": "没有找到这个&0,或许已经被删掉了。",
102 | "activity.card.project.title": "任务统计",
103 | "activity.card.table.doneTasks": "已完成",
104 | "activity.card.table.placeholder": "最近项目进展情况",
105 | "activity.card.table.title": "项目进展",
106 | "activity.card.table.todoTasks": "未完成",
107 | "activity.card.today.title": "今天的事",
108 | "activity.card.unassigned.title": "待认领的任务",
109 | "activity.destroyer.confirm.delete": "确认希望永久删除这条动态?",
110 | "activity.destroyer.delete": "删除动态",
111 | "activity.destroyer.delete.fail": "删除失败,请重试",
112 | "activity.destroyer.delete.success": "删除成功",
113 | "activity.download.all.attachments": "下载附件",
114 | "activity.editor.error": "修改评论失败了",
115 | "activity.editor.place.holder": "编辑评论...",
116 | "activity.editor.success": "成功修改了评论",
117 | "activity.editor.title": "编辑评论",
118 | "activity.invisible.not.involved.tip1": "该内容为仅参与者可见,&0 还不是参与者,将他 添加为参与者 后,他将在&0有更新时收到通知。",
119 | "activity.invisible.not.involved.tip2": "该内容为仅参与者可见,&0 还不是参与者,将他们 添加为参与者 后,他将在&0有更新时收到通知。",
120 | "activity.moreAttachments": "等",
121 | "activity.moreFiles": "个文件",
122 | "activity.panel.checkAll": "查看所有项目动态",
123 | "activity.panel.filter.allMember": "所有成员",
124 | "activity.panel.filter.allType": "所有类型",
125 | "activity.panel.filter.date": "时间",
126 | "activity.panel.filter.member": "成员",
127 | "activity.panel.filter.system": "系统消息",
128 | "activity.panel.filter.type": "类型",
129 | "activity.panel.invite": "邀请新成员",
130 | "activity.panel.menu.calendar": "订阅项目内容至第三方日历",
131 | "activity.panel.menu.checkArchive": "查看归档",
132 | "activity.panel.menu.copy": "复制项目",
133 | "activity.panel.menu.enterOrg": "进入企业",
134 | "activity.panel.menu.share": "分享项目",
135 | "activity.panel.placeholder.searchMember": "查找成员",
136 | "activity.panel.title.activities": "项目动态",
137 | "activity.panel.title.all": "项目菜单",
138 | "activity.panel.weeker.all": "全部",
139 | "activity.timeline.add.attachment": " 添加附件",
140 | "activity.timeline.add.emoji": "添加表情",
141 | "activity.timeline.comment": "@提及他人,按 Enter 快速发布",
142 | "activity.timeline.comment.ghost": "登录或注册后可参与讨论",
143 | "activity.timeline.like": "点赞",
144 | "activity.timeline.like.tips.long": "&0人赞了",
145 | "activity.timeline.like.tips.short": "点个赞",
146 | "activity.timeline.only.attachment": "仅附件",
147 | "activity.timeline.only.comment": "仅评论",
148 | "activity.timeline.remove.like": "取消点赞",
149 | "activity.timeline.reply": "发布",
150 | "activity.timeline.reply.enter": "按 Enter 发送消息",
151 | "activity.timeline.reply.quickly.placeholder": "@提及他人,按 Ctrl+Enter 快速发布",
152 | "activity.timeline.reply.shortcut": "按 Ctrl+Enter 发送消息",
153 | "activity.visible.not.involved.tip1": "你提及的 &0 还不是参与者,将他 添加为参与者 后,他将在&0有更新时收到通知。",
154 | "activity.visible.not.involved.tip2": "你提及的 &0 等&0人还不是参与者,将他们 添加为参与者 后,他们将在&0有更新时收到通知。",
155 | "activity.visible.not.involved.tip3": "你还不是参与者,将你 添加为参与者 后,你将在&0有更新时收到通知。",
156 | "activity.visible.not.involved.tip4": "你和 &0 还不是参与者,将你们 添加为参与者 后,你们将在&0有更新时收到通知。",
157 | "add.members.by.cooperated": "导入合作过的成员",
158 | "add.members.by.cooperated.placeholder": "没有找到合作过的成员",
159 | "add.members.by.emails": "批量输入邮箱",
160 | "add.members.by.emails.tips": "将邮箱粘帖至输入框即可添加,不同邮箱间用换行分割,单次最多添加20个。",
161 | "add.members.error.limit": "超过限制了,最多一次添加20个邮箱。",
162 | "add.members.tips.add.exceed": "已经超出 &0 人",
163 | "add.members.tips.add.remian": "还可以添加 &0 人",
164 | "add.org.filter.placeholder": "搜索成员或团队",
165 | "add.org.members.import.team": "导入团队成员",
166 | "add.org.members.imported.team": "已导入",
167 | "add.org.members.join.project": "加入项目",
168 | "add.org.members.joined.project": "已加入项目",
169 | "add.org.members.teams": "企业团队列表",
170 | "add.org.members.title": "企业成员列表",
171 | "add.success": "添加成功",
172 | "advanced.settings": "高级设置",
173 | "all.member": "所有成员",
174 | "all.project": "所有项目",
175 | "app.download": "下载应用",
176 | "archive.event": "归档日程",
177 | "archive.file": "归档文件",
178 | "archive.folder": "归档文件夹",
179 | "archive.post": "归档分享",
180 | "archive.tag": "归档标签",
181 | "archive.task": "归档任务",
182 | "archive.taskgroup": "归档任务分组",
183 | "archive.title": "查看归档",
184 | "archived.event.success": "已成功归档了日程",
185 | "archived.file.success": "已成功归档了文件",
186 | "archived.folder.success": "已成功归档了文件夹",
187 | "archived.post.success": "已成功归档了分享",
188 | "archived.projects": "已归档的项目",
189 | "archived.tag.success": "已成功归档了标签",
190 | "archived.task.success": "已成功归档了任务",
191 | "archived.taskgroup.success": "已成功归档了任务分组",
192 | "at.not.involved": "(不在参与者列表)",
193 | "bookkeeping.archived": "账目已归档,不可修改",
194 | "bookkeeping.home.fields.amount": "金额",
195 | "bookkeeping.home.fields.category": "类型",
196 | "bookkeeping.home.fields.content": "内容",
197 | "bookkeeping.home.fields.creator": "创建者",
198 | "bookkeeping.home.fields.date": "时间",
199 | "bookkeeping.home.fields.tags": "标签",
200 | "bookkeeping.home.header.filter.toggler.title": "筛选和设置",
201 | "bookkeeping.home.header.filtering": "(已筛选)",
202 | "bookkeeping.home.tips.header.approve": "当前账目还未设置审批人,只有审批人才能对账目进行审批,设置审批人",
203 | "bookkeeping.home.tips.placeholder": "轻松记录每一笔开支",
204 | "bookkeeping.home.tips.placeholder.approve": "(只有审批人可以对账目进行审批,记账开始请先设置审批人)",
205 | "bookkeeping.home.total.expense": "总支出",
206 | "bookkeeping.home.total.expense.filtered": "筛选后支出",
207 | "bookkeeping.home.total.income": "总收入",
208 | "bookkeeping.home.total.income.filtered": "筛选后收入",
209 | "bookkeeping.panel.filter.end.date": "结束时间",
210 | "bookkeeping.panel.filter.hide.approved": "隐藏已经审核的账目",
211 | "bookkeeping.panel.filter.start.date": "开始时间",
212 | "bookkeeping.panel.input.placeholder": "查找相关账目",
213 | "bookkeeping.panel.title.filter": "筛选",
214 | "bookkeeping.panel.title.filter.clear": "清空筛选",
215 | "bookkeeping.panel.title.settings": "记账设置",
216 | "bookkeeping.settings.approver.member": "审批人",
217 | "bookkeeping.settings.approver.tips": "只有审批人可以审批账目,您可以设置多个审批人",
218 | "bookkeeping.settings.approver.title": "审批人设置",
219 | "bookkeeping.settings.category.edit.placeholder": "分类名称",
220 | "bookkeeping.settings.category.title": "管理分类",
221 | "bookkeeping.settings.expense.tips": "支出",
222 | "bookkeeping.settings.fields.in": "现有字段",
223 | "bookkeeping.settings.fields.out": "未添加字段",
224 | "bookkeeping.settings.fields.title": "账目字段设置",
225 | "bookkeeping.settings.income.tips": "收入",
226 | "calendar.invisivle.event.tip1": "&0等&0人已有安排",
227 | "calendar.invisivle.event.tip2": "&0已有安排",
228 | "calendar.search.no.result": "抱歉,没有搜索结果",
229 | "calendar.time": "点",
230 | "chat.room.add.emoji": "添加表情",
231 | "chat.room.chat": "聊天",
232 | "chat.room.chat.create": "新聊天",
233 | "chat.room.chat.empty1": "暂无私信消息",
234 | "chat.room.chat.empty2": "在这里你会收到新私信的消息提示",
235 | "chat.room.cloesd.room": "群聊已关闭",
236 | "chat.room.cloesd.room.placeholder": "群聊应用已关闭",
237 | "chat.room.destroyer.confirm.delete": "确认希望永久删除这条消息?",
238 | "chat.room.destroyer.delete": "删除消息",
239 | "chat.room.destroyer.delete.fail": "删除失败,请重试",
240 | "chat.room.destroyer.delete.success": "删除成功",
241 | "chat.room.editor.error": "修改消息失败了",
242 | "chat.room.editor.place.holder": "编辑消息...",
243 | "chat.room.editor.success": "成功修改了消息",
244 | "chat.room.editor.title": "编辑消息",
245 | "chat.room.group.empty1": "还没有群聊消息",
246 | "chat.room.group.empty2": "项目中的成员都可以在这里参与群聊",
247 | "chat.room.member.empty1": "暂无成员",
248 | "chat.room.member.empty2": "邀请你的同伴开始协作吧!",
249 | "chat.room.new.private.msg": "私信",
250 | "chat.room.new.project.msg": "群聊",
251 | "chat.room.placeholder": "说点什么...",
252 | "chat.room.send": "发送",
253 | "chat.room.send.error": "发送私信失败,请重新尝试",
254 | "chat.room.send.success": "发送私信成功,您可以在收件箱私信列表查看",
255 | "chat.room.topbar.add": "点这里添加更多项目成员,同他们一块聊天",
256 | "check.archive": "查看归档",
257 | "collection.creator.collection.title": "文件夹名称",
258 | "collection.creator.create.success": "文件夹创建成功",
259 | "collection.creator.duplication.title": "文件夹名称已存在!",
260 | "collection.destroyer.confirm.delete": "确认要将 &0 永久删除?",
261 | "collection.destroyer.delete.fail": "删除失败,点击重试",
262 | "collection.destroyer.delete.success": "删除成功",
263 | "collection.destroyer.deleting": "正在删除",
264 | "collection.destroyer.title": "确认删除",
265 | "collection.edit.duplication.title": "文件夹名称已存在!",
266 | "collection.menu.confirm.delete": "您确定要永远删除这个文件夹吗",
267 | "collection.menu.delete.title": "删除文件夹",
268 | "collection.menu.title": "文件夹菜单",
269 | "collection.move.message": "移动文件夹 &0 至",
270 | "collection.move.title": "移动文件夹",
271 | "collection.picker.creator.input.tips": "文件夹名称",
272 | "collection.picker.creator.tips": "创建文件夹",
273 | "collection.picker.select.collection": "请选择目标文件夹",
274 | "collection.rename.title": "重命名",
275 | "confirm.delete": "删除",
276 | "confirm.unarchive.tip": "取消归档后将移动至根目录,确认取消归档?",
277 | "copy.link": "复制链接",
278 | "copy.link.failed": "复制失败",
279 | "copy.link.success": "已复制",
280 | "create.event": "创建日程",
281 | "create.event.success": "成功创建日程:",
282 | "create.post.success": "成功创建分享:",
283 | "create.task": "创建任务",
284 | "create.task.success": "成功创建任务:",
285 | "delete.entry": "删除记账",
286 | "delete.event": "删除日程",
287 | "delete.file": "删除文件",
288 | "delete.folder": "删除文件夹",
289 | "delete.post": "删除分享",
290 | "delete.success": "删除成功",
291 | "delete.tag": "删除标签",
292 | "delete.task": "删除任务",
293 | "delete.taskgroup": "删除任务分组",
294 | "desktop.notification": "桌面通知",
295 | "desktop.notification.desc": "「桌面通知」可以让您更方便地使用通知中心, 提高协作效率",
296 | "desktop.notification.disable": "关闭桌面通知",
297 | "desktop.notification.enable": "启用桌面通知",
298 | "draft.discard": "忽略",
299 | "draft.restore": "恢复",
300 | "due.date": "截止时间",
301 | "emoji.recommend.tips": "推荐 Teambition 即可获取更多表情",
302 | "emoji.recommend.wannaDo": "我要推荐",
303 | "entry.amount.unit": "¥",
304 | "entry.archives.success.unarchive": "成功取消账目的归档",
305 | "entry.category.title.null": "没有类型",
306 | "entry.creator.expense.title": "记录支出",
307 | "entry.creator.income.title": "记录收入",
308 | "entry.creator.other.add.placeholder": "添加类型",
309 | "entry.creator.other.category": "更多",
310 | "entry.creator.placeholder.amount": "金额",
311 | "entry.creator.placeholder.content": "内容",
312 | "entry.creator.placeholder.note": "添加备忘",
313 | "entry.detail.cost": "成本",
314 | "entry.detail.profit": "利润",
315 | "entry.list.action.approve": "审核",
316 | "entry.list.action.revoke": "撤销",
317 | "entry.list.status.approved": "已审核",
318 | "entry.menu.action.approve": "批准账目",
319 | "entry.menu.action.archive": "归档账目",
320 | "entry.menu.action.delete": "删除账目",
321 | "entry.menu.action.edit": "编辑账目",
322 | "entry.menu.action.revoke": "撤销账目",
323 | "entry.menu.confirm.delete": "确认删除该项目吗?",
324 | "entry.menu.delete.tips": "确定要删除这个账目吗?",
325 | "entry.menu.success.approve": "成功审核了账目",
326 | "entry.menu.success.archive": "成功归档了账目",
327 | "entry.menu.success.revoke": "成功撤销了账目",
328 | "entry.menu.success.unarchive": "成功取消归档了账目",
329 | "entry.menu.title": "记账菜单",
330 | "entry.menu.title.delete": "删除账目",
331 | "event.all.day.tips": "(全天)",
332 | "event.confirm.delete.all": "所有日程,此日程极其相关的所有日程都会被删除",
333 | "event.confirm.delete.following": "后续日程,此日程及其相关的所有后续日程均会被删除",
334 | "event.confirm.delete.single": "仅此日程,该系列中的其他所有日程都将保持不变",
335 | "event.confirm.update.following": "后续日程,此日程及其相关的所有后续日程均会发生更改",
336 | "event.confirm.update.single": "仅此日程,该系列中的其他所有日程都将保持不变",
337 | "event.creator.all.day": "全天",
338 | "event.creator.edit.tips": "编辑日程",
339 | "event.creator.edit.title": "编辑日程",
340 | "event.creator.end.time": "结束时间",
341 | "event.creator.end.time.error": "时间格式不正确",
342 | "event.creator.location.placeholder": "地点",
343 | "event.creator.save.after": "更新今后",
344 | "event.creator.save.single": "更新当前",
345 | "event.creator.start.time": "开始时间",
346 | "event.creator.tips_1": "添加日程到 ",
347 | "event.creator.tips_2": "∅",
348 | "event.creator.title.placeholder": "日程标题",
349 | "event.detail.appearin.tips": "进入 Appearin 视频会议(最多 8 人)",
350 | "event.detail.archived": "日程已归档,不可修改",
351 | "event.detail.end.date.tips": "结束时间",
352 | "event.detail.start.date.tips": "开始时间",
353 | "event.menu.confirm.delete": "确认删除日程?",
354 | "event.menu.delete.title": "删除日程",
355 | "event.menu.edit": "编辑日程",
356 | "event.menu.title": "日程菜单",
357 | "event.placeholder": "目前还没有日程",
358 | "event.recurrence.delete.title": "删除重复日程",
359 | "event.time.picker.placeholder": "输入时间",
360 | "events.add": "添加日程",
361 | "events.no.event": "无日程安排",
362 | "events.past": "过去的日程",
363 | "events.placeholder": "添加新日程,安排会议或其他活动,添加参与者即可通知其他成员参加。",
364 | "favorites.all": "所有收藏",
365 | "favorites.deleted": " 已删除",
366 | "favorites.invisible": "不可见",
367 | "favorites.new": "已更新",
368 | "favorites.not.visible": "无法打开,已删除或没有权限",
369 | "favorites.placeholder": "当前还没有收藏
你可以收藏有价值的任务、日程、分享、文件和账目。",
370 | "favorites.search": "搜索我的收藏",
371 | "favorites.unfavorite.success": "取消收藏成功",
372 | "favorites.update.success": "更新成功",
373 | "favorites.updated": "已有新内容,点击更新",
374 | "filter.project.or.member": "搜索项目或成员",
375 | "flagged.projects": "星标项目",
376 | "future.events": "未来的日程",
377 | "future.tasks": "",
378 | "globalGuide.navigation.header": "诚邀新功能体验",
379 | "globalGuide.navigation.text1": "更精简的导航,更优雅的操作体验",
380 | "globalGuide.navigation.text2": "增加任务分组搜索,编辑分组入口位置优化",
381 | "globalGuide.tips.joinBrVersion": "走,去体验",
382 | "globalGuide.tips.stayInCurrentVersion": "不,谢谢",
383 | "globalGuide.version.newyear.changelog": "阅读更新日志",
384 | "globalGuide.version.newyear.content1": "全新的导航栏,更优雅简洁",
385 | "globalGuide.version.newyear.content2": "全新的项目菜单,精简项目动态",
386 | "globalGuide.version.newyear.content3": "全新的侧栏,更高效的搜索和添加成员",
387 | "globalGuide.version.newyear.content4": "优化新用户引导,跟TB小超人快速上手",
388 | "globalGuide.version.newyear.content5": "新增日程自定义重复,如每周二周四重复",
389 | "globalGuide.version.newyear.content6": "优化归档功能,归档的内容可以查看了",
390 | "globalGuide.version.newyear.content7": "新增全局搜索动效,让搜索更愉悦",
391 | "globalGuide.version.newyear.enter": "即刻进入",
392 | "globalGuide.version.newyear.title": "猴年首次更新",
393 | "guide.create.project1.note": "想要快速开启全新的工作方式?
点击「创建新项目」按钮。",
394 | "guide.create.project1.title": "欢迎使用 Teambition",
395 | "guide.create.project2.note": "你还可以为项目选一个喜欢的封面,点击「创建」按钮进入下一步。",
396 | "guide.create.project2.title": "为新项目起一个名字",
397 | "guide.create.project3.note": " 选择合适的方式邀请你的小伙伴,
点击「进入项目」完成项目的创建。",
398 | "guide.create.project3.title": "添加项目成员",
399 | "guide.create.project4.note": "在这里输入任务名称,然后
点击「创建」按钮。",
400 | "guide.create.project4.title": "创建第一个任务",
401 | "guide.create.project5.btn": "我知道了",
402 | "guide.create.project5.note": "在这里你可以进入文件库、分享墙
和日程列表等。",
403 | "guide.create.project5.title": "项目导航条",
404 | "guide.create.project6.btn": "我知道了",
405 | "guide.create.project6.note": "在这里你可以查看项目动态,
更改项目设置。",
406 | "guide.create.project6.title": "项目菜单",
407 | "guide.create.project7.btn": "好的",
408 | "guide.create.project7.note": "在后续的使用中,我们会继续在你需要时
提供必要的提示。",
409 | "guide.create.project7.title": "恭喜你已成功完成创建",
410 | "helper.shortcuts": "快捷键",
411 | "helper.shortcuts.for.board": "任务板的快捷键",
412 | "helper.shortcuts.for.board.new": "创建新任务",
413 | "helper.shortcuts.for.event": "日程表的快捷键",
414 | "helper.shortcuts.for.event.new": "创建新日程",
415 | "helper.shortcuts.for.global": "全局快捷键",
416 | "helper.shortcuts.for.global.inbox": "打开收件箱",
417 | "helper.shortcuts.for.global.myboard": "进入我的控制台",
418 | "helper.shortcuts.for.global.portal": "回到所有项目",
419 | "helper.shortcuts.for.global.search": "打开搜索",
420 | "helper.shortcuts.for.inbox": "收件箱的快捷键",
421 | "helper.shortcuts.for.inbox.next": "下一条信息",
422 | "helper.shortcuts.for.inbox.previous": "上一条信息",
423 | "helper.shortcuts.for.markdown": "打开Markdown语法帮助",
424 | "helper.shortcuts.for.taskgroup": "打开任务分组面板",
425 | "helper.shortcuts.for.wall": "分享墙的快捷键",
426 | "helper.shortcuts.for.wall.new": "创建新分享",
427 | "html5audio.not.support": "您的浏览器不支持该音频播放",
428 | "html5video.not.support": "您的浏览器不支持该视频播放",
429 | "iCalendar.howto": "查看如何使用",
430 | "iCalendar.msg1": "你可以在手机上通过扫描左侧的二维码,获取「&0」的订阅链接",
431 | "iCalendar.msg2": "你可以在电脑上点击以下链接直接订阅「&0」,或者将链接复制到日历软件手动订阅",
432 | "iCalendar.reset": "重新生成订阅链接",
433 | "iCalendar.title": "订阅「&0」",
434 | "inbox.at.me": "[@我]",
435 | "inbox.ated.empty.tip1": "项目内容更新时",
436 | "inbox.ated.empty.tip2": "你就会在这里收到@你的通知提醒",
437 | "inbox.clear.messages": "清空已读消息",
438 | "inbox.clear.messages.desc": "确定要清空所有已读消息吗?",
439 | "inbox.delete.message": "删除信息",
440 | "inbox.go.to.entry": "前往记账",
441 | "inbox.go.to.event": "前往日程表",
442 | "inbox.go.to.groupchat": "前往项目群聊",
443 | "inbox.go.to.organization": "前往企业:",
444 | "inbox.go.to.post": "前往分享墙",
445 | "inbox.go.to.project": "前往项目:",
446 | "inbox.go.to.task": "前往任务板",
447 | "inbox.later.empty.tip1": "项目内容更新时",
448 | "inbox.later.empty.tip2": "你就会在这里收到稍后处理的通知提醒",
449 | "inbox.later.message": "稍后处理",
450 | "inbox.later.undo.message": "已处理",
451 | "inbox.my.mention": "仅@我的消息",
452 | "inbox.my.message": "通知",
453 | "inbox.normal.empty.tip1": "项目内容更新时",
454 | "inbox.normal.empty.tip2": "你就会在这里收到通知提醒",
455 | "inbox.private.empty.tip1": "暂无私信消息",
456 | "inbox.private.empty.tip2": "在这里你会收到新私信的消息提示",
457 | "inbox.read.messages": "标记全部已读",
458 | "language.label": "语言",
459 | "leave.page.alert": "你所编辑的内容尚未保存",
460 | "library.and": "和 ",
461 | "library.author": "创建者",
462 | "library.collections.count": "&0 个文件夹",
463 | "library.create.folder": "创建文件夹",
464 | "library.default.collection": "讨论的附件",
465 | "library.download": "下载",
466 | "library.moreFiles": "个文件",
467 | "library.moreObjects": "等",
468 | "library.move": "移动",
469 | "library.move.to": "移动 &0 至",
470 | "library.name": "名称",
471 | "library.placeholder": "文件夹中还没有内容哦",
472 | "library.placeholder.no.create": "你可以创建文件夹或上传文件",
473 | "library.size": "大小",
474 | "library.success.moveTo": "成功移动 &1 至 &2",
475 | "library.title": "文件库",
476 | "library.update": "更新时间",
477 | "library.upload.file": "上传文件",
478 | "library.works.count": "&0 个文件",
479 | "link.add": "关联内容",
480 | "link.confirm.delete": "确认希望永久删除这个关联?",
481 | "link.delete": "取消关联",
482 | "link.delete.fail": "删除失败,请重试",
483 | "link.delete.success": "删除成功",
484 | "link.menu.title": "关联菜单",
485 | "link.type.event": "",
486 | "link.type.file": "",
487 | "link.type.post": "",
488 | "link.type.task": "",
489 | "markdown.helper.A": "链接",
490 | "markdown.helper.H1": "一级标题",
491 | "markdown.helper.H2": "二级标题",
492 | "markdown.helper.H3": "三级标题",
493 | "markdown.helper.H4": "四级标题",
494 | "markdown.helper.blank.spaces": "4个空格",
495 | "markdown.helper.blockquote": "区块引用",
496 | "markdown.helper.code": "行内代码",
497 | "markdown.helper.em": "斜体",
498 | "markdown.helper.hr": "分割线",
499 | "markdown.helper.img": "图片",
500 | "markdown.helper.indentation": "缩进",
501 | "markdown.helper.more.syntax": "· 更多语法请参考:Markdown语法说明",
502 | "markdown.helper.ol1": "有序列标记1",
503 | "markdown.helper.ol2": "有序列标记2",
504 | "markdown.helper.ol3": "有序列标记3",
505 | "markdown.helper.placeholder": "输入Markdown文档",
506 | "markdown.helper.pre": "形成代码区块",
507 | "markdown.helper.preview": "预览",
508 | "markdown.helper.strong": "粗体",
509 | "markdown.helper.syntax": "语法",
510 | "markdown.helper.title": "Markdown",
511 | "markdown.helper.try": " · 试一试 Markdown!",
512 | "markdown.helper.ul1": "无序列标记1",
513 | "markdown.helper.ul2": "无序列标记2",
514 | "member.bar.input": "搜索成员",
515 | "member.bar.title": "项目成员",
516 | "member.card.birthday": "生日",
517 | "member.card.email": "邮箱",
518 | "member.card.location": "地址",
519 | "member.card.mobile": "电话",
520 | "member.card.not.visited": "",
521 | "member.card.website": "网站",
522 | "member.finder.unregistered.phone": "该手机号未被注册",
523 | "member.involve": "参与者",
524 | "member.involve.add": "添加参与者",
525 | "member.involve.none": "暂无参与者",
526 | "member.management": "成员管理",
527 | "member.menu.all": "所有成员",
528 | "member.menu.confirm.remove": "确认移除该成员?",
529 | "member.menu.remove": "",
530 | "member.menu.remove.from.org": "",
531 | "member.menu.remove.from.team": "从团队中移除",
532 | "member.menu.remove.title": "",
533 | "member.menu.search": "查找成员",
534 | "member.menu.set.manager": "",
535 | "member.menu.set.member": "",
536 | "member.menu.set.owner": "",
537 | "member.menu.title": "",
538 | "member.not.visited.project": "该成员从未访问过",
539 | "member.tasks.future.tasks": "未来的任务",
540 | "member.tasks.recent.tasks": "近期的任务",
541 | "member.tasks.recently.done.tasks": "最近完成的任务",
542 | "member.tasks.today.tasks": "今天的任务",
543 | "memberlatest.actived": "最后登录于 ",
544 | "members.manager.add.member": "添加成员",
545 | "members.manager.all.members": "所有成员",
546 | "members.manager.new.members": "新成员",
547 | "members.manager.remove.description": "确认移除该成员?",
548 | "members.manager.remove.title": "移除成员",
549 | "members.manager.search": "搜索企业成员",
550 | "members.manager.search.no.result": "无搜索结果",
551 | "moment.MMMMYYYY": "YYYY年MM月",
552 | "more.operations": "更多操作",
553 | "my.btn.personal": "编辑个人资料",
554 | "my.btn.settings": "设置",
555 | "my.events": "日程",
556 | "my.favorites": "收藏",
557 | "my.notes": "笔记",
558 | "my.post": "我的分享",
559 | "my.projects": "个人项目",
560 | "my.recent": "近期的事",
561 | "my.recent.today": "今天的事",
562 | "my.recent.tomorrow": "明天的事",
563 | "my.recent.week": "最近5天的事",
564 | "my.task.created": "创建的任务",
565 | "my.task.dateByAll": "全部",
566 | "my.task.dateByLastMonth": "最近一个月",
567 | "my.task.dateByLastTwoWeek": "最近两个星期",
568 | "my.task.dateByLastWeek": "最近一个星期",
569 | "my.task.dateByToday": "今天",
570 | "my.task.dateByYesterday": "昨天",
571 | "my.task.done": "完成的任务",
572 | "my.task.joined": "参与的任务",
573 | "my.task.sortByDueDate": "按截止时间排序",
574 | "my.task.sortByPriority": "按时间优先排序",
575 | "my.task.sortByProject": "按项目名称排序",
576 | "my.task.undone": "未完成的任务",
577 | "my.tasks": "任务",
578 | "nav.menu.filter": "筛选",
579 | "nav.menu.menu": "菜单",
580 | "navigation.account.personalCenter": "账号设置",
581 | "navigation.create.organization": "创建企业",
582 | "navigation.enterBBS": "进入社区",
583 | "navigation.event": "日程",
584 | "navigation.file": "文件",
585 | "navigation.organization": "企业",
586 | "navigation.project": "项目",
587 | "navigation.sharing": "分享",
588 | "navigation.task": "任务",
589 | "navigation.version.br": "",
590 | "navigation.version.release": "",
591 | "new.member": "新成员",
592 | "new.ribbon": "新功能",
593 | "noRight.view": "您没有权限查看链接的对象",
594 | "note.delete.confirm": "一旦删除笔记,将无法恢复",
595 | "note.delete.confirm.title": "确认删除笔记",
596 | "note.placeholder": "当前还没有笔记
我的笔记内的内容,只有你自己才能看到。你可以在这里记录内容。",
597 | "notes.input.note": "内容...",
598 | "notes.input.title": "标题...",
599 | "object.picker.event.creator.tips": "创建新日程",
600 | "object.picker.event.title": "待参加的日程",
601 | "object.picker.exceed.number": "一次最多选择 10 个关联对象!",
602 | "object.picker.file.uploader.tips": "上传新文件",
603 | "object.picker.post.creator.tips": "创建新分享",
604 | "object.picker.search.all": "按「回车」搜索全部",
605 | "object.picker.task.creator.tips": "创建新任务",
606 | "org.guide.created.success": "企业创建成功 现在邀请伙伴一起加入吧",
607 | "org.guide.enter": "进入企业",
608 | "org.guide.next": "下一步",
609 | "org.guide.previous": "上一步",
610 | "org.guide.skip": "跳过",
611 | "org.guide.team.add.member": "添加成员",
612 | "org.guide.team.create": "新建团队",
613 | "org.guide.team.create.tip": "成功新建团队:",
614 | "org.guide.team.desc": "分团队进行工作,这样有利于提高效率、控制权限",
615 | "org.guide.team.destroy.tip": "成功删除团队:",
616 | "org.guide.team.hr": "人力资源",
617 | "org.guide.team.manager": "管理团队",
618 | "org.guide.team.product": "产品研发",
619 | "org.guide.team.tip": "选择或创建您的团队",
620 | "org.guide.transfer.projects": "迁入项目到企业",
621 | "org.reports.all.tips.create": "新增任务数量",
622 | "org.reports.all.tips.delay": "逾期任务数量",
623 | "org.reports.all.tips.done": "完成任务数量",
624 | "org.reports.all.title": "企业整体报告",
625 | "org.reports.arrowLeft": "上一周",
626 | "org.reports.arrowRight": "下一周",
627 | "org.reports.currentWeek": "本周",
628 | "org.reports.rank.tips.delay": "逾期任务数量排行",
629 | "org.reports.rank.tips.delayResult": "&0,有&0项任务逾期",
630 | "org.reports.rank.tips.done": "任务完成数量排行",
631 | "org.reports.rank.tips.doneResult": "&0,完成了&0项任务",
632 | "org.reports.states.tips.title": "项目的状态更新",
633 | "org.reports.tasks.tips.delay": "逾期的任务",
634 | "org.reports.tasks.tips.done": "完成的任务",
635 | "organization.advanced.settings": "高级设置",
636 | "organization.avatarCrop.choose": "选择图片",
637 | "organization.avatarCrop.edit.organization.avatar": "编辑组织头像",
638 | "organization.avatarCrop.hint": "最佳尺寸64×64,可以上传高质量图片进行裁剪",
639 | "organization.avatarCrop.preview": "图片预览",
640 | "organization.business": "企业版",
641 | "organization.contact.us": "咨询电话
400-060-5576",
642 | "organization.desc": "企业简介",
643 | "organization.dueday": "还有&0天到期",
644 | "organization.expiring.content": "的企业版即将到期,请及时续费",
645 | "organization.expiring.go.pay": "前往支付中心",
646 | "organization.expiring.help": "怎么更好地使用企业版",
647 | "organization.expiring.title": "到期提示",
648 | "organization.name": "企业名称",
649 | "organization.nav.members": "成员",
650 | "organization.nav.projects": "项目",
651 | "organization.nav.reports": "报告",
652 | "organization.nav.statistics": "统计",
653 | "organization.nav.teams": "团队",
654 | "organization.overdue": "已过期",
655 | "organization.overdue.contact.owner": "联系企业拥有者付费",
656 | "organization.overdue.message": "该企业已经到期,请尽快续费",
657 | "organization.overdue.message1": "该企业「&0」已经到期,请尽快续费",
658 | "organization.overdue.message2": "该项目所在企业「&0」已经到期,请尽快续费",
659 | "organization.overdue.title": "企业已到期",
660 | "organization.projects": "企业项目",
661 | "organization.setting.permission": "企业公开性",
662 | "organization.setting.private": "私有企业(仅企业成员可见)",
663 | "organization.setting.public": "公开企业(访客可以查看企业中的公开项目和成员)",
664 | "organization.settings.add.cooperated.members": "导入合作过的成员",
665 | "organization.settings.add.member.header": "邀请成员",
666 | "organization.settings.add.member.placeholder": "输入邮箱/手机号 回车确认",
667 | "organization.settings.add.members.by.emails": "批量邀请",
668 | "organization.settings.add.members.success": "成功添加 &0 位成员,已向他们发送邀请信",
669 | "organization.settings.app": "移动应用企业",
670 | "organization.settings.avatar.upload.failed": "更换组织头像失败",
671 | "organization.settings.avatar.upload.success": "成功更换组织头像",
672 | "organization.settings.background": "背景封面",
673 | "organization.settings.delete": "删除企业",
674 | "organization.settings.delete.msg": "一旦你删除了企业,企业内所有项目、团队、成员,项目中所有内容等都将会被永久删除。这是一个不可恢复的操作,请谨慎对待!",
675 | "organization.settings.delete.title": "删除企业",
676 | "organization.settings.design": "设计企业",
677 | "organization.settings.education": "教育企业",
678 | "organization.settings.info": "企业信息",
679 | "organization.settings.internet": "互联网企业",
680 | "organization.settings.join.organizaiton.repetitive": "&0 已是企业成员",
681 | "organization.settings.leave": "退出企业",
682 | "organization.settings.leave.msg": "一旦你退出了该企业,你将不能查看任何关于该企业的信息。退出企业后,如果想重新加入,请联系企业的拥有者或者管理员。",
683 | "organization.settings.leave.success": "已成功退出企业, 即将返回首页",
684 | "organization.settings.leave.title": "退出企业",
685 | "organization.settings.leaving": "正在退出企业",
686 | "organization.settings.media": "媒体企业",
687 | "organization.settings.member.list": "企业成员列表",
688 | "organization.settings.other": "其他企业",
689 | "organization.settings.personalized": "个性化",
690 | "organization.settings.placeholder": "请输入“DELETE”以确认",
691 | "organization.settings.roles.and.permissions": "查看权限表",
692 | "organization.settings.type": "企业类别",
693 | "organization.settings.update.plan": "升级方案",
694 | "organization.settings.upperLimit": "企业成员达到上限",
695 | "organization.settings.upperLimit.msg": "企业成员数超过上限",
696 | "organization.settings.using": "使用中",
697 | "organization.statistics.activeness": "活跃度",
698 | "organization.statistics.chartMembers": "成员总数",
699 | "organization.statistics.chartProjects": "项目总数",
700 | "organization.statistics.count": "个",
701 | "organization.statistics.going": "将到期",
702 | "organization.statistics.membersAmount": "个成员",
703 | "organization.statistics.membertab.all": "所有成员",
704 | "organization.statistics.membertab.byDelayRate": "按任务逾期率",
705 | "organization.statistics.membertab.byDoneRate": "按任务完成率",
706 | "organization.statistics.membertab.done.undo": "已完成/总任务数",
707 | "organization.statistics.membertab.name": "姓名",
708 | "organization.statistics.membertab.new": "新成员",
709 | "organization.statistics.membertab.noContent": "没有成员统计信息",
710 | "organization.statistics.membertab.projectsJoined": "参与项目数",
711 | "organization.statistics.membertab.title": "成员统计",
712 | "organization.statistics.membertab.undergoing": "进行中的任务",
713 | "organization.statistics.month": "月",
714 | "organization.statistics.monthShow": "月",
715 | "organization.statistics.none": "无",
716 | "organization.statistics.overdue": "已逾期",
717 | "organization.statistics.project.access.deny": "无权限进入项目详情",
718 | "organization.statistics.projectsAmount": "个项目",
719 | "organization.statistics.projecttab.all": "所有项目分组",
720 | "organization.statistics.projecttab.byActiveness": "按项目活跃度",
721 | "organization.statistics.projecttab.byDelayRate": "按任务逾期率",
722 | "organization.statistics.projecttab.byDoneRate": "按任务完成率",
723 | "organization.statistics.projecttab.doneUndo": "已完成/总任务数",
724 | "organization.statistics.projecttab.member": "成员",
725 | "organization.statistics.projecttab.noContent": "无项目统计信息",
726 | "organization.statistics.projecttab.project": "项目",
727 | "organization.statistics.projecttab.title": "项目统计",
728 | "organization.statistics.projecttab.undergoings": "进行中的任务",
729 | "organization.statistics.projecttab.weeklyActiveness": "周活跃度",
730 | "organization.statistics.projecttab.weeklyActivenessHint": "项目成员每周在项目中产生的内容",
731 | "organization.statistics.searchMember": "搜索成员",
732 | "organization.statistics.searchProject": "搜索项目",
733 | "organization.statistics.searchResult": "搜索结果",
734 | "organization.statistics.sorting": "排序",
735 | "organization.statistics.undergoing": "进行中",
736 | "organization.statistics.weeklyActiveness": "企业周活跃度",
737 | "organization.statistics.weeklyActiveness.hint": "企业成员每周在所有项目产生的内容",
738 | "organization.statistics.year": "年",
739 | "organization.switcher.placeholder": "你还没有加入任何企业。
你可以创建企业,来进行数据统计和团队管理。",
740 | "organization.visible": "企业可见",
741 | "payment.center": "支付中心",
742 | "popover.search.current": "在当前项目里搜索 “&0”",
743 | "popover.search.whole": "在所有项目里搜索 “&0”",
744 | "portal.create.project": "创建新项目",
745 | "portal.enter.organizaiton.home.page": "企业首页",
746 | "portal.project.notification": "这个项目有新的动态",
747 | "portal.project.star.bookmark.hint": "请进入项目并使用&1來添加",
748 | "portal.project.star.bookmark.hint.project": "请使用&1添加",
749 | "portal.project.star.success": "星标成功,方便以后快速访问项目,可以加入浏览器书签",
750 | "portal.project.un.star.success": "取消星标成功",
751 | "portal.unarchive.error": "抱歉,我们未能重新激活该项目,请稍后重试.",
752 | "portal.unarchive.project.description": "重新激活项目后,您就可以正常使用该项目了",
753 | "portal.unarchive.project.title": "重新激活项目",
754 | "portal.unarchive.success": "项目已经成功重新激活.",
755 | "post.creator.markdown.tips": "该文档使用 Markdown,语法帮助",
756 | "post.creator.tips_1": "创建分享至 ",
757 | "post.creator.tips_2": "∅",
758 | "post.creator.title.placeholder": "输入文档标题",
759 | "post.detail.archived": "分享已归档,不可修改",
760 | "post.menu.confirm.delete": "确认删除分享?",
761 | "post.menu.delete.title": "删除分享",
762 | "post.menu.pin.title": "置顶分享",
763 | "post.menu.title": "分享菜单",
764 | "post.menu.unpin.title": "取消置顶",
765 | "post.placeholder": "目前还没有分享",
766 | "post.un.title": "无标题",
767 | "post.update": "更新分享",
768 | "previewer.archived": "文件已归档,不可修改",
769 | "previewer.confirm.delete": "确定要永远删除这个文件吗?",
770 | "previewer.delete.file": "删除文件",
771 | "previewer.download": "下载",
772 | "previewer.download.file": "下载文件",
773 | "previewer.edit": "编辑",
774 | "previewer.enter.collection": "前往文件夹",
775 | "previewer.file.deleted": "原文件已删除",
776 | "previewer.file.translateFail": "文档转换失败了,无法直接预览",
777 | "previewer.header.folder.tips": "所属文件夹",
778 | "previewer.header.root.tips": "根目录",
779 | "previewer.header.temp.tips": "临时目录",
780 | "previewer.img.turn.left": "向左旋转",
781 | "previewer.img.turn.right": "向右旋转",
782 | "previewer.menu.confirm.delete": "确认永久删除该文件?",
783 | "previewer.menu.delete.title": "删除文件",
784 | "previewer.menu.title": "文件菜单",
785 | "previewer.move": "移动",
786 | "previewer.open.link": "打开链接",
787 | "previewer.update": "更新",
788 | "previewer.updating": "正在更新",
789 | "priority.high": "紧急",
790 | "priority.none": "普通",
791 | "priority.urgent": "非常紧急",
792 | "project.create.section": "点击添加一个分组",
793 | "project.creator.add.section": "添加项目分组",
794 | "project.creator.app": "移动应用项目",
795 | "project.creator.cancel": "取消",
796 | "project.creator.create": "创建",
797 | "project.creator.create.project": "创建项目",
798 | "project.creator.creating": "创建中...",
799 | "project.creator.design": "设计项目",
800 | "project.creator.education": "教育项目",
801 | "project.creator.internet": "互联网项目",
802 | "project.creator.logo": "自定义图片",
803 | "project.creator.me": "我",
804 | "project.creator.media": "媒体项目",
805 | "project.creator.owner": "拥有者",
806 | "project.creator.porject.desc.placeholder": "项目简介(可选)",
807 | "project.creator.porject.name.placeholder": "项目名称",
808 | "project.creator.project.category": "项目类型",
809 | "project.creator.project.info": "项目信息",
810 | "project.creator.section": "项目分组",
811 | "project.creator.section.placeholder": "选择或添加项目分组(可选)",
812 | "project.desc": "项目简介",
813 | "project.grouping": "项目分组",
814 | "project.guide.enter": "进入项目",
815 | "project.guide.skip": "跳过",
816 | "project.guide.success": "项目创建成功 现在邀请伙伴一起加入吧",
817 | "project.logoCrop.best.size.hint": "裁剪尺寸600×200, 可以上传高质量图片进行裁剪",
818 | "project.logoCrop.choose": "选择图片",
819 | "project.logoCrop.desktop.logo": "桌面端项目封面",
820 | "project.logoCrop.mobile.logo": "移动端项目封面",
821 | "project.logoCrop.mobile.starred.logo": "移动端星标项目封面",
822 | "project.logoCrop.previews": "图片预览",
823 | "project.logoCrop.set.project.logo": "编辑项目封面",
824 | "project.manager.add.new.project": "开始你全新的项目管理",
825 | "project.manager.enter": "查看项目",
826 | "project.manager.filter": "筛选和排序",
827 | "project.manager.join": "马上加入",
828 | "project.manager.joining": "加入中...",
829 | "project.manager.no.project": "还没有项目",
830 | "project.manager.not.member": "你不是这个项目的成员",
831 | "project.manager.sort.by.name": "按名称排序",
832 | "project.manager.success.join": "成功加入",
833 | "project.menu.all": "所有项目",
834 | "project.menu.calendar": "订阅项目",
835 | "project.menu.copy": "复制项目",
836 | "project.menu.copy.fail": "项目复制失败",
837 | "project.menu.copy.success": "成功复制项目:",
838 | "project.menu.enter.org": "进入企业",
839 | "project.menu.mail.content": "&0 邀请你查看来自 Teambition 的公开项目「&0」。链接是:&0",
840 | "project.menu.mail.content.desc": "&0 邀请你查看来自 Teambition 的公开项目「&0」。这个项目是关于 “&0”。链接是:&0",
841 | "project.menu.mail.title": "分享公开项目 “&0”",
842 | "project.menu.share": "分享项目",
843 | "project.menu.share.link": "分享链接",
844 | "project.menu.share.to": "分享到",
845 | "project.menu.share.to.mail": "通过邮件分享",
846 | "project.menu.share.to.weibo": "分享到微博",
847 | "project.menu.title": "项目菜单",
848 | "project.menu.weibo.content": "我在 Teambition 上发现了一个有用的公开项目「&0」。",
849 | "project.menu.weibo.content.desc": "我在 Teambition 上发现了一个有用的公开项目「&0」。这个项目是关于 “&0”。",
850 | "project.name": "项目名称",
851 | "project.none.section": "未选择分组",
852 | "project.section.down": "下移分组",
853 | "project.section.name": "新分组名称",
854 | "project.section.remove": "取消分组",
855 | "project.section.up": "上移分组",
856 | "project.setting.add.app": "",
857 | "project.setting.defaultrole.help": "你可以到 权限设置 页面添加更多角色",
858 | "project.setting.defaultrole.tips": "每添加一个项目成员,该成员权限默认被设置为:",
859 | "project.setting.defaultrole.title": "项目成员默认权限",
860 | "project.setting.hide.app": "",
861 | "project.setting.in.nav": "",
862 | "project.setting.nav.setting": "项目导航设置",
863 | "project.setting.not.in.nav": "",
864 | "project.setting.notification.accept": "推送动态提醒(显示标记并以桌面通知的方式进行推送)",
865 | "project.setting.notification.reject": "不推送动态提醒(只显示标记)",
866 | "project.setting.notification.saveSuccess": "已保存",
867 | "project.setting.notification.tips": "当该项目里有新动态时,以标记或通知的方式提醒,仅对个人",
868 | "project.setting.notification.title": "项目动态提醒",
869 | "project.setting.other.setting": "其它设置",
870 | "project.setting.reset.error": "重置链接失败,请重试",
871 | "project.setting.reset.invite": "重置邀请链接后,原链接失效",
872 | "project.setting.transfer.and.quit": "您需要指定一名新的拥有者才能退出该项目",
873 | "project.setting.update.cover": "更新封面",
874 | "project.setting.visibility": "项目公开性",
875 | "project.setting.visibility.all": "公开项目",
876 | "project.setting.visibility.info.all": "所有人都可通过链接访问,仅项目成员可编辑",
877 | "project.setting.visibility.info.organization": "所有 Teambition 企业成员可见,仅项目成员可编辑",
878 | "project.setting.visibility.info.project": "仅项目成员可查看和编辑",
879 | "project.setting.visibility.organization": "企业项目",
880 | "project.setting.visibility.project": "私有项目",
881 | "project.settings": "项目设置",
882 | "project.settings.add.cooperated.members": "导入合作过的成员",
883 | "project.settings.add.member.header": "邀请成员",
884 | "project.settings.add.member.placeholder": "输入邮箱/手机号 回车确认",
885 | "project.settings.add.member.repetitive": "&0 已是项目成员",
886 | "project.settings.add.member.success": "成功添加成员,已向 &0 发送邀请信",
887 | "project.settings.add.member.through.url.or.qrcode": "扫描二维码加入项目或分享项目链接邀请成员",
888 | "project.settings.add.member.through.url.or.tip1": "有效日期:",
889 | "project.settings.add.members.by.emails": "批量邀请",
890 | "project.settings.add.members.by.qrcode": "二维码邀请",
891 | "project.settings.add.members.success": "成功添加 &0 位成员,已向他们发送邀请信",
892 | "project.settings.app": "移动应用项目",
893 | "project.settings.archive": "归档",
894 | "project.settings.archive.error": "未能归档项目,请稍后再试",
895 | "project.settings.archive.msg": "如果项目已经完成或是暂时中止,你可以先将项目归档。归档的项目可以很容易地再次激活,并且不会有数据丢失",
896 | "project.settings.archive.success": "已成功归档项目,你可以在首页的底部找到所有已归档的项目",
897 | "project.settings.create.org.and.transfer": "创建企业并移交项目",
898 | "project.settings.delete": "删除",
899 | "project.settings.delete.msg": "一旦你删除了项目,所有与项目有关的信息将会被永久删除。这是一个不可恢复的操作,请谨慎对待!",
900 | "project.settings.delete.success": "已成功删除项目",
901 | "project.settings.desc.placeholder": "介绍一下这个项目",
902 | "project.settings.design": "设计项目",
903 | "project.settings.education": "教育项目",
904 | "project.settings.email.sent30": "邮件已发送(30秒后可以重新发送)",
905 | "project.settings.group.alert.success": "项目分组更改成功",
906 | "project.settings.import.member": "导入企业成员",
907 | "project.settings.inactive.init.password": "待激活,初始密码:",
908 | "project.settings.info": "项目信息",
909 | "project.settings.internet": "互联网项目",
910 | "project.settings.leave": "退出",
911 | "project.settings.leave.msg": "一旦你退出了该项目,你将不能查看任何关于该项目的信息。退出项目后,如果想重新加入,请联系项目管理员。",
912 | "project.settings.leave.success": "已成功退出项目",
913 | "project.settings.logo.crop.image.size.too.large": "图片文件过大,请选择4MB以内的文件!",
914 | "project.settings.logo.upload.failed": "更换项目封面失败",
915 | "project.settings.logo.upload.success": "成功更换项目封面",
916 | "project.settings.media": "媒体项目",
917 | "project.settings.member.list": "项目成员列表",
918 | "project.settings.members.count": "位成员",
919 | "project.settings.no.exist.warn": "该项目未能成功转移, 请尝试刷新网页然后重试",
920 | "project.settings.no.right.warn": "您没有转移该项目的权限",
921 | "project.settings.other": "其他项目",
922 | "project.settings.over.plan.warn": "请升级新拥有者的项目套餐然后再重试",
923 | "project.settings.placeholder": "请输入“DELETE”以确认",
924 | "project.settings.remove.member": "移除成员",
925 | "project.settings.remove.team.confirm": "一旦移除了团队,该团队下的所有成员将会被移除",
926 | "project.settings.remove.team.confirm.title": "确认移除团队",
927 | "project.settings.removing.member": "正在移除",
928 | "project.settings.resend": "重发邀请信",
929 | "project.settings.select.org": "选择已有企业",
930 | "project.settings.select.owner": "请选择新的拥有者",
931 | "project.settings.send.time": "邮件已发送(&0秒后可以重新发送)",
932 | "project.settings.title.placeholder": "项目名称",
933 | "project.settings.transfer": "移交",
934 | "project.settings.transfer.confirm": "确认移交",
935 | "project.settings.transfer.current": "移交当前项目",
936 | "project.settings.transfer.msg": "请注意, 当你移交项目后, 您将不再是项目的拥有者。只有你可以执行这个操作。",
937 | "project.settings.transfer.no.org": "没有企业",
938 | "project.settings.transfer.or": "或",
939 | "project.settings.transfer.success": "已成功移交项目",
940 | "project.settings.transfer.tips": "将个人项目移交到企业后,你就可以使用自定义权限功能。",
941 | "project.settings.transfer.tipsHasPerm": "将个人项目移交到企业后,你就可以使用自定义权限功能。",
942 | "project.settings.type": "项目类别",
943 | "project.settings.view.all.team.members": "查看团队所有成员",
944 | "project.switcher.no.result": "抱歉,没有找到该项目",
945 | "project.switcher.placeholder": "查找项目",
946 | "project.transfer.success": "迁入成功",
947 | "project.transferring": "正在迁入",
948 | "projects.export": "迁出项目",
949 | "projects.import": "迁入项目",
950 | "projects.manager.all.project": "所有项目",
951 | "projects.manager.create.project": "添加新项目,",
952 | "projects.transferIn": "迁入项目",
953 | "projects.transferIn.btn": "迁入",
954 | "projects.transferIn.to": "迁入到 :",
955 | "projects.transferOut": "迁出项目",
956 | "projects.transferOut.btn": "迁出",
957 | "projects.transferOut.to": "迁出到 :",
958 | "remind.add": "添加提醒",
959 | "remind.ago": "&0前",
960 | "remind.close": "关闭提醒",
961 | "remind.happened": "开始时提醒",
962 | "remove.success": "移除成功",
963 | "repeat.add": "添加重复",
964 | "repeat.description.daily": "每&1天重复",
965 | "repeat.description.monthly": "每&1月 &2 日重复",
966 | "repeat.description.weekly": "每&1周 &2 重复",
967 | "repeat.freq.CUSTOM": "自定义重复",
968 | "repeat.freq.DAILY": "每天重复",
969 | "repeat.freq.MONTHLY": "每月重复",
970 | "repeat.freq.NO": "不重复",
971 | "repeat.freq.WEEKLY": "每周重复",
972 | "repeat.freq.WORKDAY": "工作日重复",
973 | "repeat.freq.YEARLY": "每年重复",
974 | "repeat.interval": "间隔",
975 | "resend.success": "发送成功",
976 | "scrum.filter.clear": "清除筛选",
977 | "scrum.filter.hide.done": "隐藏已完成任务",
978 | "scrum.filter.search": "查找相关任务",
979 | "scrum.filter.title": "任务筛选",
980 | "scrum.filter.today.update": "今日更新",
981 | "search.archived": "已归档",
982 | "search.no.content": "抱歉,没有更多",
983 | "search.no.result": "抱歉,没有找到与 “&0” 相关的内容",
984 | "search.placeholder": "快速搜索,查找你所需的内容",
985 | "search.project": "所属项目",
986 | "search.result.all.type": "所有类型",
987 | "set.success": "设置成功",
988 | "show.event": "日程",
989 | "show.task": "任务",
990 | "simditor.alignCenter": "居中",
991 | "simditor.alignLeft": "居左",
992 | "simditor.alignRight": "居右",
993 | "simditor.alignment": "水平对齐",
994 | "simditor.blockquote": "引用",
995 | "simditor.bold": "加粗文字",
996 | "simditor.code": "插入代码",
997 | "simditor.color": "文字颜色",
998 | "simditor.coloredText": "彩色文字",
999 | "simditor.deleteColumn": "删除列",
1000 | "simditor.deleteRow": "删除行",
1001 | "simditor.deleteTable": "删除表格",
1002 | "simditor.externalImage": "外链图片",
1003 | "simditor.hr": "分隔线",
1004 | "simditor.image": "插入图片",
1005 | "simditor.imageAlt": "图片描述",
1006 | "simditor.imageSize": "图片尺寸",
1007 | "simditor.imageUrl": "图片地址",
1008 | "simditor.indent": "向右缩进",
1009 | "simditor.insertColumnLeft": "在左边插入列",
1010 | "simditor.insertColumnRight": "在右边插入列",
1011 | "simditor.insertRowAbove": "在上面插入行",
1012 | "simditor.insertRowBelow": "在下面插入行",
1013 | "simditor.italic": "斜体文字",
1014 | "simditor.link": "插入链接",
1015 | "simditor.linkText": "链接文字",
1016 | "simditor.linkUrl": "地址",
1017 | "simditor.normalText": "普通文本",
1018 | "simditor.ol": "有序列表",
1019 | "simditor.outdent": "向左缩进",
1020 | "simditor.removeLink": "移除链接",
1021 | "simditor.restoreImageSize": "还原图片尺寸",
1022 | "simditor.selectLanguage": "选择程序语言",
1023 | "simditor.strikethrough": "删除线文字",
1024 | "simditor.table": "表格",
1025 | "simditor.text": "文本",
1026 | "simditor.title": "标题",
1027 | "simditor.ul": "无序列表",
1028 | "simditor.underline": "下划线文字",
1029 | "simditor.upload": "上传",
1030 | "simditor.uploadError": "上传出错了",
1031 | "simditor.uploadFailed": "上传失败了",
1032 | "simditor.uploadImage": "上传图片",
1033 | "simditor.uploading": "正在上传",
1034 | "stage.add.tip": "新建任务阶段...",
1035 | "stage.edit.title": "添加或编辑任务阶段",
1036 | "stage.menu.add.next.stage.title": "在此后添加新阶段",
1037 | "stage.menu.add.stage.hint": "新阶段将被添加在当前阶段之后",
1038 | "stage.menu.add.title": "添加阶段",
1039 | "stage.menu.archive.tasks.title": "归档本阶段所有任务",
1040 | "stage.menu.cannot.delete": "请先清空此阶段上的任务,然后再删除这个阶段",
1041 | "stage.menu.confirm.archive.tasks": "您确定要归档这个阶段下的所有任务吗",
1042 | "stage.menu.confirm.delete": "您确定要永远删除这个阶段吗",
1043 | "stage.menu.date.picker": "设置本阶段所有任务截止时间",
1044 | "stage.menu.date.picker.success": "成功设置截止时间",
1045 | "stage.menu.date.picker.title": "选择截止时间",
1046 | "stage.menu.delete.title": "删除阶段",
1047 | "stage.menu.edit.title": "编辑阶段",
1048 | "stage.menu.lock.stage": "切换至流程模式",
1049 | "stage.menu.lock.stage.msg": "切换至流程模式后,该阶段的任务会变成已完成状态,其它阶段已完成的任务也会自动进入该阶段,即形成了任务流程模式",
1050 | "stage.menu.move.tasks.success": "移动任务成功",
1051 | "stage.menu.move.tasks.title": "移动本阶段所有任务",
1052 | "stage.menu.stage.name": "阶段名称",
1053 | "stage.menu.title": "阶段菜单",
1054 | "stage.menu.toggle.stage.tasks.executor": "设置本阶段所有任务执行者",
1055 | "stage.menu.toggle.stage.tasks.executor.success": "成功设置执行者",
1056 | "stage.menu.toggle.stage.tasks.executor.title": "选择执行者",
1057 | "stage.menu.unlock.stage": "切换至自由模式",
1058 | "stage.menu.unlock.stage.msg": "切换至自由模式后,该阶段后面可以新增加阶段,各阶段相互独立,即退出了任务流程模式",
1059 | "stage.selector.tips": "你可以在任务板中添加和修改任务分组及任务阶段。",
1060 | "stage.selector.title": "选择位置",
1061 | "submit.post": "立即发布",
1062 | "submit.ticket.email": "请输入你的常用邮箱",
1063 | "submit.ticket.menu.support": "帮助中心",
1064 | "submit.ticket.menu.video": "使用视频",
1065 | "submit.ticket.other": "其它意见",
1066 | "submit.ticket.pl.anything": "其他想说的话…",
1067 | "submit.ticket.pl.help": "输入反馈内容",
1068 | "submit.ticket.pl.suggestion": "你有什么建议吗?",
1069 | "submit.ticket.question": "向 Teambition 反馈问题",
1070 | "submit.ticket.send": "发送",
1071 | "submit.ticket.success": "谢谢您的反馈,我们会通过 &0 与您联系",
1072 | "submit.ticket.suggestion": "向 Teambition 提出建议",
1073 | "submit.ticket.title": "帮助",
1074 | "subtask.card.create.input.placeholder": "输入子任务内容...",
1075 | "subtask.card.create.placeholder": "添加子任务",
1076 | "subtask.menu.confirm.delete": "确认删除该子任务?",
1077 | "subtask.menu.confirm.transform": "确定转换该子任务为任务?",
1078 | "subtask.menu.delete.title": "删除子任务",
1079 | "subtask.menu.title": "子任务菜单",
1080 | "subtask.menu.transform.title": "转换为任务",
1081 | "tag.creator.placeholder": "标签名称",
1082 | "tag.creator.title": "新建标签",
1083 | "tag.menu.confirm.delete": "确认删除标签?",
1084 | "tag.menu.delete.title": "删除标签",
1085 | "tag.menu.edit.title": "编辑标签",
1086 | "tag.menu.title": "标签菜单",
1087 | "tag.picker.add": "添加标签",
1088 | "tag.picker.none": "暂无标签",
1089 | "tag.view.all": "所有标签",
1090 | "tag.view.create": "新标签",
1091 | "tag.view.entry.info": "&0 个账目",
1092 | "tag.view.event.info": "&0 个日程",
1093 | "tag.view.file.info": "&0 个文件",
1094 | "tag.view.placeholder.info": "你可以通过“新标签”按钮来创建标签。此后,就可以在在这里按照标签来查看项目内容了。",
1095 | "tag.view.placeholder.infoLink": "我们准备了一些实用的标签用法,立即查看。",
1096 | "tag.view.placeholder.title": "项目中还没有标签",
1097 | "tag.view.post.info": "&0 篇分享",
1098 | "tag.view.task.info": "&0 项任务",
1099 | "task.creator.add": "添加任务",
1100 | "task.creator.content": "任务内容",
1101 | "task.creator.multiple": "创建多条任务",
1102 | "task.creator.multiple.all": "创建 &0 条",
1103 | "task.creator.multiple.desc": "系统检测到你输入了 &0 行内容,你是想创建多条任务吗?",
1104 | "task.creator.multiple.one": "创建一条",
1105 | "task.creator.multiple.too.many": "内容行数太多了",
1106 | "task.creator.multiple.too.many.confirm": "好吧,我知道了",
1107 | "task.creator.multiple.too.many.desc": "内容最多为 &0 行,你当前输入了 &0 行。",
1108 | "task.creator.tips": "创建新任务至",
1109 | "task.detail.add.note": "添加备忘",
1110 | "task.detail.archived": "任务已归档,不可修改",
1111 | "task.detail.complete": "完成任务",
1112 | "task.detail.due.date": "截止时间",
1113 | "task.detail.due.date.info": "选择截止时间",
1114 | "task.detail.executor": "执行者",
1115 | "task.detail.no.executor": "待认领",
1116 | "task.detail.priority": "优先级",
1117 | "task.detail.priority.info": "选择优先级",
1118 | "task.detail.redo": "重做此任务",
1119 | "task.detail.remind": "提醒",
1120 | "task.detail.repeat": "重复",
1121 | "task.detail.repeat.info": "选择重复周期",
1122 | "task.detail.repeat.recurrence.desc": "一旦确定操作,此日程及其相关的所有后续日程均会发生更改",
1123 | "task.detail.repeat.recurrence.title": "确定更新日程重复规则?",
1124 | "task.detail.stage": "任务阶段",
1125 | "task.detail.stage.unset": "未设置",
1126 | "task.detail.tasklist": "任务分组",
1127 | "task.group": "分组",
1128 | "task.group2": "任务分组",
1129 | "task.list.belong.to": "属于任务:&0",
1130 | "task.list.no.task": "本周无任务",
1131 | "task.menu.confirm.delete": "确认删除任务?",
1132 | "task.menu.copy": "复制任务",
1133 | "task.menu.copy.check.link": "原任务接收新任务的更新提醒",
1134 | "task.menu.copy.check.linked": "新任务接收原任务的更新提醒",
1135 | "task.menu.copy.fail": "任务复制失败",
1136 | "task.menu.copy.success": "成功复制任务:",
1137 | "task.menu.copy.tips": "标题、子任务、备忘将被复制",
1138 | "task.menu.delete.title": "删除任务",
1139 | "task.menu.move": "移动任务",
1140 | "task.menu.move.fail": "任务移动失败",
1141 | "task.menu.move.success": "成功移动任务:",
1142 | "task.menu.move.tips": "跨项目移动时,任务附件、部分参与者信息不会保留",
1143 | "task.menu.search.project": "查找项目",
1144 | "task.menu.select.group": "选择任务分组",
1145 | "task.menu.select.project": "选择项目",
1146 | "task.menu.select.stage": "选择阶段",
1147 | "task.menu.title": "任务菜单",
1148 | "task.menu.warning": "请先选择项目分组和阶段",
1149 | "task.placeholder": "目前还没有任务",
1150 | "task.recent.placeholder": "你还没有任务和日程",
1151 | "task.viewl.authority.msg": "由执行者&0设置任务是否完成",
1152 | "tasklist.bar.add.group": "添加任务分组",
1153 | "tasklist.bar.done.tasks": "已完成的任务",
1154 | "tasklist.bar.member.tasks": "成员的任务",
1155 | "tasklist.bar.memberOf.tasks": "&1的任务",
1156 | "tasklist.bar.pin": "固定任务分组栏",
1157 | "tasklist.bar.search.group": "搜索分组",
1158 | "tasklist.bar.smart.group": "智能分组",
1159 | "tasklist.bar.today.tasks": "今天的任务",
1160 | "tasklist.bar.total.tasks": "未完成的任务",
1161 | "tasklist.bar.unassigned.tasks": "待认领的任务",
1162 | "tasklist.bar.unpin": "取消固定任务分组栏",
1163 | "tasklist.creator.new": "新建",
1164 | "tasklist.creator.submit": "创建",
1165 | "tasklist.creator.tasklist.desc": "任务分组描述",
1166 | "tasklist.creator.tasklist.title": "任务分组名称",
1167 | "tasklist.creator.temp": "模板",
1168 | "tasklist.menu.add.templates": "保存为任务分组模板",
1169 | "tasklist.menu.add.templates.desc": "你可以在(任务分组 > 添加 > 模板)中选择使用",
1170 | "tasklist.menu.add.templates.detail": "将当前任务阶段保存为模板",
1171 | "tasklist.menu.add.templates.success": "添加 &0 成功",
1172 | "tasklist.menu.confirm.archive": "您确定要归档这个任务分组吗",
1173 | "tasklist.menu.confirm.delete": "您确定要永远删除这个任务分组吗",
1174 | "tasklist.menu.delete.title": "删除任务分组",
1175 | "tasklist.menu.edit.title": "编辑任务分组",
1176 | "tasklist.menu.export.title": "导出分组内所有任务",
1177 | "tasklist.menu.last.cannot.edit": "抱歉,您不能归档或删除最后一个任务分组",
1178 | "tasklist.menu.tasklist.desc": "任务分组描述",
1179 | "tasklist.menu.tasklist.title": "任务分组名称",
1180 | "tasklist.menu.templates": "自定义模板",
1181 | "tasklist.menu.title": "分组菜单",
1182 | "tasklist.tab.status.done": "已完成",
1183 | "tasklist.tab.status.normal": "近期任务",
1184 | "tasklist.tab.status.overdue": "已逾期",
1185 | "tasklist.tab.status.upcoming": "将到期",
1186 | "tasklist.temp1stage1": "策划",
1187 | "tasklist.temp1stage2": "原型图",
1188 | "tasklist.temp1stage3": "效果图",
1189 | "tasklist.temp1stage4": "审核中",
1190 | "tasklist.temp1stage5": "完成",
1191 | "tasklist.temp1title": "设计",
1192 | "tasklist.temp2stage1": "需求点",
1193 | "tasklist.temp2stage2": "设计中",
1194 | "tasklist.temp2stage3": "研发中",
1195 | "tasklist.temp2stage4": "测试中",
1196 | "tasklist.temp2stage5": "已发布",
1197 | "tasklist.temp2title": "研发",
1198 | "tasklist.temp3stage1": "用户问题",
1199 | "tasklist.temp3stage2": "解决中",
1200 | "tasklist.temp3stage3": "已解决",
1201 | "tasklist.temp3stage4": "反馈用户&问题解决",
1202 | "tasklist.temp3title": "用户支持",
1203 | "team.creator.cancel": "取消",
1204 | "team.creator.create": "创建",
1205 | "team.creator.create.team": "新建团队",
1206 | "team.creator.creating": "创建中...",
1207 | "team.creator.team.name.placeholder": "团队名称",
1208 | "team.mananger.remove": "移除",
1209 | "team.manger.events": "日程安排",
1210 | "team.manger.no.member": "还没有成员加入",
1211 | "team.manger.no.project": "还没有参与任何项目",
1212 | "team.manger.project.confirm.remove": "确认移除该项目?",
1213 | "team.manger.project.confirm.title": "移除团队项目",
1214 | "team.manger.show.member.tasks": "查看全部任务",
1215 | "team.manger.tasks": "任务安排",
1216 | "team.members.add": "添加团队成员",
1217 | "team.members.add.search": "搜索成员",
1218 | "team.members.add.title": "添加团队成员",
1219 | "team.members.search": "搜索成员",
1220 | "team.menu.confirm.delete": "您确定要永远删除这个团队吗",
1221 | "team.menu.confirm.quit": "您确定退出这个团队吗",
1222 | "team.menu.delete.title": "删除团队",
1223 | "team.menu.edit.title": "编辑团队名称",
1224 | "team.menu.quit": "退出",
1225 | "team.menu.quit.title": "退出团队",
1226 | "team.menu.title": "团队菜单",
1227 | "team.projects.add": "添加项目",
1228 | "team.projects.add.search": "搜索项目",
1229 | "team.projects.add.title": "将团队加入项目",
1230 | "team.projects.search": "搜索团队项目",
1231 | "team.subnav.events": "日程",
1232 | "team.subnav.members": "成员",
1233 | "team.subnav.projects": "项目",
1234 | "team.subnav.tasks": "任务",
1235 | "teambition.title": "Teambition · 最好用的团队协作平台",
1236 | "teams.manager.create.team": "新建团队",
1237 | "teams.manager.members": "成员",
1238 | "teams.manager.not.in.team": "你不是该团队成员",
1239 | "teams.manager.projects": "项目",
1240 | "this.stage": "这个阶段",
1241 | "this.task": "这项任务",
1242 | "time.format.error": "时间格式不正确",
1243 | "time.input.placeholder": "输入时间",
1244 | "today.events": "今天的日程",
1245 | "today.tasks": "",
1246 | "token.expired": "会话过期,请重新",
1247 | "total.events.and.tasks": "共 &0 项",
1248 | "unarchived.entry.success": "已成功解档了记账",
1249 | "unarchived.event.success": "已成功解档了日程",
1250 | "unarchived.file.success": "已成功解档了文件",
1251 | "unarchived.folder.success": "已成功解档了文件夹",
1252 | "unarchived.post.success": "已成功解档了分享",
1253 | "unarchived.tag.success": "已成功解档了标签",
1254 | "unarchived.task.success": "已成功解档了任务",
1255 | "unarchived.taskgroup.success": "已成功解档了任务分组",
1256 | "uploader.error": "文件上传失败",
1257 | "uploader.file.exceed.size": "文件 &0 过大,请选择 1GB 以内的文件!",
1258 | "uploader.files.duplicate": "该文件已上传!",
1259 | "uploader.files.exceed.number": "最多一次性上传 50 个文件!",
1260 | "uploader.not.accept": "不支持该文件类型或者文件大小为 0",
1261 | "uploader.type.no.allowed": "不支持该文件类型(.&0),请压缩后上传",
1262 | "user.settings.comment.notice": "评论提醒",
1263 | "user.settings.daily.mail.notice": "每日工作事项邮件提醒",
1264 | "user.settings.emojiFormatBefore": "有效期至",
1265 | "user.settings.getEmoji": "获取表情特权",
1266 | "user.settings.mobile": "移动端",
1267 | "user.settings.monthly.mail.notice": "月刊订阅",
1268 | "user.settings.myEmoji": "我的表情特权",
1269 | "user.settings.notification.setting": "提醒设置",
1270 | "user.settings.participant.notice": "被添加参与者提醒",
1271 | "user.settings.preference.usePanelTitle": "在任务板中打开任务详情采用侧栏方式",
1272 | "user.settings.receive.event.notice": "收到『新日程』提醒",
1273 | "user.settings.receive.file.notice": "收到『新文件』提醒",
1274 | "user.settings.receive.post.notice": "收到『新分享』提醒",
1275 | "user.settings.receive.task.notice": "收到『新任务』提醒",
1276 | "user.settings.setting": "偏好设置",
1277 | "user.settings.task.file.update.notice": "任务,文件内容更新时提醒",
1278 | "visible.close": "已关闭",
1279 | "visible.mode": "隐私模式",
1280 | "visible.open": "已开启",
1281 | "visible.to.involve": "仅参与者可见",
1282 | "visible.to.member": "所有成员可见",
1283 | "wall.view.create.post": "添加分享",
1284 | "wall.view.html.post": "普通文档",
1285 | "wall.view.markdown.post": "Markdown 文档",
1286 | "wall.view.post.creator.info": "&0发布于",
1287 | "wall.view.post.order.create.time": "按发布时间排序",
1288 | "wall.view.post.order.reply.time": "按回复时间排序",
1289 | "wall.view.shimo.post": "石墨文档",
1290 | "wall.view.shimo.update": "刷新文档",
1291 | "wall.view.yiqixie.post": "一起写文档",
1292 | "wall.yiqixie.default.title": "一起写文档",
1293 | "week.selector.search": "搜索日期",
1294 | "work.creator.info": "&0上传于",
1295 | "work.creator.tips": "上传文件到 ",
1296 | "work.edit.title": "文件名",
1297 | "work.menu.confirm.delete": "确定要永远删除这个文件吗",
1298 | "work.menu.delete.title": "删除文件",
1299 | "work.menu.title": "文件菜单",
1300 | "work.move.message": "移动文件 &0 至",
1301 | "work.move.title": "移动文件",
1302 | "work.picker.from.dropbox": "从Dropbox选择",
1303 | "work.picker.from.library": "从文件库选择",
1304 | "work.picker.from.link": "粘贴下载链接...",
1305 | "work.picker.title": "添加附件",
1306 | "work.picker.uploader": "直接上传",
1307 | "work.rename.title": "重命名",
1308 | "work.versions.current": "当前版本",
1309 | "work.versions.enter.edit": "编辑版本的描述信息",
1310 | "work.versions.title": "历史版本",
1311 | "works.background.uploader.close.confirm": "您有文件正在上传,确认关闭么?",
1312 | "works.background.uploader.close.confirm.title": "确认关闭",
1313 | "works.background.uploader.error": "文件上传失败",
1314 | "works.background.uploader.uploaded": "上传完成",
1315 | "works.background.uploader.uploading": "正在上传 &0/&0",
1316 | "works.uploader.uploading.total": "&0个文件正在上传..."
1317 | }
1318 |
--------------------------------------------------------------------------------