├── .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 | --------------------------------------------------------------------------------