提交 e3a9a78f 编写于 作者: N ninecents

清除代码

上级 b27a1553
name: Build Ctool
on: [ push ]
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 16.x ]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Initialize / Install Dependencies
run: |
mkdir temp_release
npm install
- name: Build web
run: |
npm run build -adapter=web
cp -r ./dist ./dist_gh_pages
cd ./dist
zip -r -q ../temp_release/web.zip ./*
cd ../
unzip -l ./temp_release/web.zip
- name: Build chrome
if: startsWith(github.ref, 'refs/tags/')
run: |
npm run build -adapter=chrome
cd ./dist
zip -r -q ../temp_release/chrome.zip ./*
cd ../
- name: Build utools
if: startsWith(github.ref, 'refs/tags/')
run: |
npm run build -adapter=utools
cd ./dist
zip -r -q ../temp_release/utools.zip ./*
cd ../
- name: Build edge
if: startsWith(github.ref, 'refs/tags/')
run: |
npm run build -adapter=edge
cd ./dist
zip -r -q ../temp_release/edge.zip ./*
cd ../
- name: Build firefox
if: startsWith(github.ref, 'refs/tags/')
run: |
npm run build -adapter=firefox
cd ./dist
zip -r -q ../temp_release/firefox.zip ./*
cd ../
- name: Upload build to github
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
prerelease: true
files: |
./temp_release/web.zip
./temp_release/chrome.zip
./temp_release/utools.zip
./temp_release/edge.zip
./temp_release/firefox.zip
- name: Deploy web to gh-pages
uses: peaceiris/actions-gh-pages@v3
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
publish_dir: ./dist_gh_pages
.DS_Store
/node_modules
/temp_release
/desktop/node_modules
/dist
/public/manifest.json
/public/plugin.json
/public/README.md
/id_rsa.pem
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
test.js
/public/background.js
MIT License
Copyright 2017 baiy
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
# 程序开发常用工具
使用过程中的任何问题或者需要新的工具欢迎提交`Issue`,新工具如果可以提供实现代码就完美了O(∩_∩)O
## 先睹为快
![](https://cdn.jsdelivr.net/gh/baiy/Ctool@master/images/v2_1.png)
## 在线使用
<https://baiy.github.io/Ctool/>
## chrome 安装
-[Chrome 应用商店](https://chrome.google.com/webstore/detail/ipfcebkfhpkjeikaammlkcnalknjahmh) 安装
## 微软 Edge 安装
-[微软 Edge 应用商店](https://microsoftedge.microsoft.com/addons/detail/cihekagpnnadjjplgljkmkpcfiopfplc) 安装
## 火狐 Firefox 安装
-[火狐 Firefox 应用商店](https://addons.mozilla.org/zh-CN/firefox/addon/ctool/) 安装
## utools 安装
- [utools](https://u.tools/) 插件中心 搜索 `ctool`
## 开发
```
# 安装依赖
npm install
# 调试
npm run serve -adapter=[chrome|edge|utools|firefox|web]
# 编译
npm run build -adapter=[chrome|edge|utools|firefox|web]
// 编译输出目录: `/dist/`
```
## 功能列表
| 功能 | 说明 |离线使用|
|--------------|-------------------------------------------------------------------------------------------------------------------------------|---|
| 哈希 | `md5`, `sha1`, `sha256`, `sha512`,`sm3` |√|
| 加密/解密 | `AES`,`DES`,`RC4`,`Rabbit`,`TripleDes`,`sm2`,`sm4` |√|
| BASE64编码 | `加密`,`解密`,`支持文件` |√|
| URL编码 | `编码`,`解码` |√|
| 时间戳 | `双向转换`,`毫秒` |√|
| 二维码 | `生成`,`解析` |√|
| 条形码 | `生成` |√|
| 汉字转拼音 | `声调`,`首字母`,`分隔符` |√|
| IP地址查询 | `运营商`,`城市` |×|
| 代码格式化 | `js`, `ts`, `html`, `css`, `less`, `scss`, `graphql`, `vue`, `angular`, `markdown`, `json5`, `xml`, `yaml`, `sql`, `压缩` |√|
| Unicode | `双向转换`,`emoji`,`html 实体`,`css 实体` |√|
| 进制转换 | `2-64进制` |√|
| 正则表达式 | `匹配`,`查找`,`替换` |√|
| 随机字符生成器 | `批量`,`特殊字符` |√|
| 序列化转换 | `json`, `xml`, `yaml`, `phpArray`, `phpSerialize`, `properties` |√|
| 文本差异化对比 | `行`,`单词`,`css` |√|
| crontab校验 | `Crontab`,`规则`,`校验`,`例子` |√|
| websocket调试 | `websocket`,`在线调试` |×|
| 单位换算 | `长度`,`面积`,`体积`,`质量`,`温度`,`压力`,`功率`,`功`,`密度`,`力`,`时间`,`速度`,`数据存储`,`角度` |√|
| 时间计算器 | - |√|
| JSON工具 | `格式化`,`校验`,`压缩`,`转义`,`去除转义`,`Unicode转中文`,`中文转Unicode`,`转GET参数`,`Java`, `C#`, `Go`, `Dart`,`csv`,`table`,`Protobuf`,`jsonpath` |√|
| UUID | `在线生成uuid` |√|
| ascii编码转换 | `十进制`, `十六进制`, `八进制`, `二进制`, `字符串` |√|
| 变量名格式转换 | `Var Name`, `var-name`, `VAR_NAME`, `VarName`, `varName`, `var_name`, `var name` |√|
| jwt解码 | `header`, `payload` |√|
| Hex/String转换 | `hex to string`, `string to hex`, `十六进制转字符串`, `字符串转十六进制` |√|
| Hex/Base64转换 | `hex to Base64`, `Base64 to hex` |√|
| 文本处理 | `大小写转换`, `中英文标点转换`, `简繁转换`, `替换`, `字符统计`, `行去重`, `添加行号`, `行排序`, `过滤行首尾不可见字符`,`过滤空行` |√|
| html编码 | - |√|
| 原码/反码/补码 | `生成` |√|
| ARM/HEX | `互转` |×|
| Bcrypt | `加密`,`验证` |√|
| IP网络计算器 | `子网掩码各个进制表示换算,IP地址进制表示换算` |√|
## 功能列表-yeahmao
| 功能 | 说明 |离线使用|
|--------------|-------------------------------------------------------------------------------------------------------------------------------|---|
| 字节转码 | `x64dbg拷贝的汇编代码`, `美化后`, `字符串格式`, `字节数组格式`,`` |√|
| 常量查看器 | `winerror.h`, `ntstatus.h` |√|
## 功能列表-代码生成器
| 功能 | 说明 |离线使用|
|--------------|-------------------------------------------------------------------------------------------------------------------------------|---|
| GenJson | `k-v字符串列表转换为json`, `excel表中数据转json` |√|
| GenJava | `超长字符串报错`,`StringBuilder ==> str` |√|
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}
{
"name": "c-tool",
"version": "1.11.2",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --port 8081",
"dev": "vue-cli-service serve --port 8081",
"build": "vue-cli-service build --no-module",
"lint": "vue-cli-service lint",
"report": "vue-cli-service build --report"
},
"dependencies": {
"@babel/parser": "^7.17.0",
"@prettier/plugin-php": "^0.17.6",
"@typescript-eslint/typescript-estree": "^5.10.2",
"angular-html-parser": "^1.8.0",
"axios": "^0.21.4",
"babel-runtime": "^6.26.0",
"bcryptjs": "^2.4.3",
"bignumber.js": "^9.0.2",
"camel-case": "^4.1.2",
"chinese-simple2traditional": "^1.0.1",
"code-formatter": "0.0.1",
"codemirror": "^5.65.1",
"codemirror-graphql": "^1.2.11",
"core-js": "^3.8.3",
"cosmiconfig": "^7.0.1",
"cron-parser": "^2.16.3",
"cronstrue": "^1.123.0",
"crypto-js": "^3.3.0",
"csv-parse": "^5.0.4",
"csv-stringify": "^6.0.5",
"darkreader": "^4.9.44",
"diff-match-patch": "^1.0.5",
"file": "^0.2.2",
"file-type": "^16.5.3",
"graphql": "15.7.2",
"hexy": "^0.3.4",
"ipinyinjs": "^1.0.0",
"jimp": "^0.16.1",
"js-base64": "^3.7.2",
"js-htmlencode": "^0.3.0",
"js-yaml": "^3.14.1",
"jsbarcode": "^3.11.5",
"json-to-properties": "^1.1.3",
"json5": "^2.2.0",
"jsonlint": "^1.6.3",
"jsonpath-plus": "^6.0.1",
"jsrsasign": "^10.5.1",
"jsrsasign-util": "^1.0.5",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"mime-types": "^2.1.34",
"moment": "^2.29.1",
"netmask": "^2.0.2",
"pascal-case": "^3.1.2",
"php-array-reader": "^1.3.2",
"phparr": "^0.2.0",
"pluralize": "^8.0.0",
"postcss": "^8.4.6",
"postcss-less": "^5.0.0",
"postcss-scss": "^4.0.3",
"prettier": "^2.5.1",
"prettier-plugin-sql": "^0.3.0",
"properties-to-json": "^0.1.7",
"qrcode": "^1.5.0",
"qrcode-parser": "^1.2.0",
"qs": "^6.10.3",
"serialize-php": "^1.1.2",
"sm-crypto": "^0.3.7",
"system": "^2.0.1",
"typescript": "^4.5.5",
"uglify-js": "3.14.3",
"uuid": "^8.3.2",
"view-design": "^4.7.0",
"vue": "^2.6.14",
"vue-i18n": "^8.27.0",
"vue-router": "^3.5.3",
"vuedraggable": "^2.24.3",
"x2js": "git+https://gitee.com/ltlwill/x2js.git",
"xml-formatter": "^2.6.1"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"less": "^2.7.0",
"less-loader": "^6.0.0",
"node-polyfill-webpack-plugin": "^1.1.4",
"utools-api-types": "^2.3.0",
"vue-cli-plugin-iview": "~2.0.0",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true,
"webextensions": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {
"no-console": "off",
"vue/no-parsing-error": [
2,
{
"x-invalid-end-tag": false
}
]
},
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"globals": {
"__": "writable"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
# preview.yml
autoOpen: false # 打开工作空间时是否自动开启所有应用的预览
apps:
- port: 8081 # 应用的端口
run: npm i && npm run dev # 应用的启动命
command: # 使用此命令启动服务,且不执行run
root: ./ # 应用的启动目录
name: front_end__ctool # 应用名称
description: 我的第一个 App。 # 应用描述
autoOpen: true # 打开工作空间时是否自动开启预览(优先级高于根级 autoOpen
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>加载中...</title>
<style>
* {
box-sizing: border-box;
}
body{
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="page" style="width: 800px;height: 580px;margin: 0 auto;">
<div style="text-align: center;padding-top: 200px;font-size: 16px;color: #666">加载中...</div>
</div>
<script src=index.js></script>
</body>
</html>
\ No newline at end of file
setTimeout(function () {
window.location.href = "tool.html"
}, 1);
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Ctool 程序开发常用工具</title>
</head>
<body>
<div id="page" style="width: 800px;height: 550px;margin: 0 auto;padding: 0 10px;overflow-y:auto;">
<div id="app"></div>
</div>
<div id="clipboard"></div>
</body>
</html>
{
"name": "__MSG_main_manifest_name__",
"description": "__MSG_main_manifest_description__",
"version": "##version##",
"author": "wo@baiy.org",
"offline_enabled": true,
"homepage_url": "https://github.com/baiy/Ctool",
"manifest_version": 2,
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"browser_action": {
"default_icon": "img/icon_chrome.png",
"default_title": "__MSG_main_manifest_default_title__",
"default_popup": "index.html"
},
"background": {
"scripts": ["background.js"]
},
"icons": {
"16": "img/icon_chrome.png",
"48": "img/icon_chrome.png",
"128": "img/icon_chrome.png"
},
"permissions": [
"clipboardWrite",
"clipboardRead",
"*://get.geojs.io/*",
"*://*.baiy.org/*"
],
"commands": {
"panel": {
"description": "__MSG_main_manifest_commands_panel_description__",
"global": true
}
},
"update_url": "http://clients2.google.com/service/update2/crx"
}
let windowId = null;
// 打开独立窗口
const panel = {
create() {
chrome.windows.create({
url: chrome.runtime.getURL("tool.html"),
type: "popup",
width: 810,
left: 200,
top: 200,
height: 610,
}, (w) => {
windowId = w.id
})
},
open() {
if (windowId === null) {
this.create()
} else {
chrome.windows.get(windowId, (w) => {
if (!w) {
this.create()
} else {
chrome.windows.update(windowId, {focused: true})
}
})
}
},
onRemoved(id) {
if (id === windowId) {
windowId = null;
}
}
}
// 注册快捷键
chrome.commands.onCommand.addListener((command) => {
switch (command) {
case "panel":
panel.open()
break;
default:
return;
}
})
// 窗口关闭事件
chrome.windows.onRemoved.addListener((id) => {
panel.onRemoved(id);
})
export const openUrl = (url) => {
// return chrome.tabs.create();
// return chrome.windows.create();
if (url.indexOf('chrome://') === 0){
return chrome.tabs.create({url:url});
}
return window.open(url);
}
\ No newline at end of file
import lsCache from './lscache'
export default {
get(key, def = null) {
let data = lsCache.get(key)
return data ? data : def
},
set(key, value, expiry = 0) {
return lsCache.set(key, value, expiry / 60)
},
remove(key) {
return lsCache.remove(key)
},
// 清理过期
clear() {
return lsCache.flushExpired()
},
getAllKey() {
return lsCache.getAllKey();
}
}
\ No newline at end of file
/**
* lscache library
* Copyright (c) 2011, Pamela Fox
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://github.com/pamelafox/lscache
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* jshint undef:true, browser:true, node:true */
// Prefix for all lscache keys
var CACHE_PREFIX = 'lscache-';
// Suffix for the key name on the expiration items in localStorage
var CACHE_SUFFIX = '-cacheexpiration';
// expiration date radix (set to Base-36 for most space savings)
var EXPIRY_RADIX = 10;
// time resolution in milliseconds
var expiryMilliseconds = 60 * 1000;
// ECMAScript max Date (epoch + 1e8 days)
var maxDate = calculateMaxDate(expiryMilliseconds);
var cachedStorage;
var cachedJSON;
var cacheBucket = '';
var warnings = false;
// Determines if localStorage is supported in the browser;
// result is cached for better performance instead of being run each time.
// Feature detection is based on how Modernizr does it;
// it's not straightforward due to FF4 issues.
// It's not run at parse-time as it takes 200ms in Android.
function supportsStorage() {
var key = '__lscachetest__';
var value = key;
if (cachedStorage !== undefined) {
return cachedStorage;
}
// some browsers will throw an error if you try to access local storage (e.g. brave browser)
// hence check is inside a try/catch
try {
if (!localStorage) {
return false;
}
} catch (ex) {
return false;
}
try {
setItem(key, value);
removeItem(key);
cachedStorage = true;
} catch (e) {
// If we hit the limit, and we don't have an empty localStorage then it means we have support
if (isOutOfSpace(e) && localStorage.length) {
cachedStorage = true; // just maxed it out and even the set test failed.
} else {
cachedStorage = false;
}
}
return cachedStorage;
}
// Check to set if the error is us dealing with being out of space
function isOutOfSpace(e) {
return e && (
e.name === 'QUOTA_EXCEEDED_ERR' ||
e.name === 'NS_ERROR_DOM_QUOTA_REACHED' ||
e.name === 'QuotaExceededError'
);
}
// Determines if native JSON (de-)serialization is supported in the browser.
function supportsJSON() {
/*jshint eqnull:true */
if (cachedJSON === undefined) {
cachedJSON = (window.JSON != null);
}
return cachedJSON;
}
/**
* Returns a string where all RegExp special characters are escaped with a \.
* @param {String} text
* @return {string}
*/
function escapeRegExpSpecialCharacters(text) {
return text.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&');
}
/**
* Returns the full string for the localStorage expiration item.
* @param {String} key
* @return {string}
*/
function expirationKey(key) {
return key + CACHE_SUFFIX;
}
/**
* Returns the number of minutes since the epoch.
* @return {number}
*/
function currentTime() {
return Math.floor((new Date().getTime()) / expiryMilliseconds);
}
/**
* Wrapper functions for localStorage methods
*/
function getItem(key) {
return localStorage.getItem(CACHE_PREFIX + cacheBucket + key);
}
function setItem(key, value) {
// Fix for iPad issue - sometimes throws QUOTA_EXCEEDED_ERR on setItem.
localStorage.removeItem(CACHE_PREFIX + cacheBucket + key);
localStorage.setItem(CACHE_PREFIX + cacheBucket + key, value);
}
function removeItem(key) {
localStorage.removeItem(CACHE_PREFIX + cacheBucket + key);
}
function eachKey(fn) {
var prefixRegExp = new RegExp('^' + CACHE_PREFIX + escapeRegExpSpecialCharacters(cacheBucket) + '(.*)');
// Loop in reverse as removing items will change indices of tail
for (var i = localStorage.length - 1; i >= 0; --i) {
var key = localStorage.key(i);
key = key && key.match(prefixRegExp);
key = key && key[1];
if (key && key.indexOf(CACHE_SUFFIX) < 0) {
fn(key, expirationKey(key));
}
}
}
function flushItem(key) {
var exprKey = expirationKey(key);
removeItem(key);
removeItem(exprKey);
}
function flushExpiredItem(key) {
var exprKey = expirationKey(key);
var expr = getItem(exprKey);
if (expr) {
var expirationTime = parseInt(expr, EXPIRY_RADIX);
// Check if we should actually kick item out of storage
if (currentTime() >= expirationTime) {
removeItem(key);
removeItem(exprKey);
return true;
}
}
}
function warn(message, err) {
if (!warnings) return;
if (!('console' in window) || typeof window.console.warn !== 'function') return;
window.console.warn("lscache - " + message);
if (err) window.console.warn("lscache - The error was: " + err.message);
}
function calculateMaxDate(expiryMilliseconds) {
return Math.floor(8.64e15 / expiryMilliseconds);
}
let lscache = {
/**
* Stores the value in localStorage. Expires after specified number of minutes.
* @param {string} key
* @param {Object|string} value
* @param {number} time
* @return true if the value was inserted successfully
*/
set: function (key, value, time) {
if (!supportsStorage()) return false;
// If we don't get a string value, try to stringify
// In future, localStorage may properly support storing non-strings
// and this can be removed.
if (!supportsJSON()) return false;
try {
value = JSON.stringify(value);
} catch (e) {
// Sometimes we can't stringify due to circular refs
// in complex objects, so we won't bother storing then.
return false;
}
try {
setItem(key, value);
} catch (e) {
if (isOutOfSpace(e)) {
// If we exceeded the quota, then we will sort
// by the expire time, and then remove the N oldest
var storedKeys = [];
var storedKey;
eachKey(function (key, exprKey) {
var expiration = getItem(exprKey);
if (expiration) {
expiration = parseInt(expiration, EXPIRY_RADIX);
} else {
// TODO: Store date added for non-expiring items for smarter removal
expiration = maxDate;
}
storedKeys.push({
key: key,
size: (getItem(key) || '').length,
expiration: expiration
});
});
// Sorts the keys with oldest expiration time last
storedKeys.sort(function (a, b) {
return (b.expiration - a.expiration);
});
var targetSize = (value || '').length;
while (storedKeys.length && targetSize > 0) {
storedKey = storedKeys.pop();
warn("Cache is full, removing item with key '" + key + "'");
flushItem(storedKey.key);
targetSize -= storedKey.size;
}
try {
setItem(key, value);
} catch (e) {
// value may be larger than total quota
warn("Could not add item with key '" + key + "', perhaps it's too big?", e);
return false;
}
} else {
// If it was some other error, just give up.
warn("Could not add item with key '" + key + "'", e);
return false;
}
}
// If a time is specified, store expiration info in localStorage
if (time) {
setItem(expirationKey(key), (currentTime() + time).toString(EXPIRY_RADIX));
} else {
// In case they previously set a time, remove that info from localStorage.
removeItem(expirationKey(key));
}
return true;
},
/**
* Retrieves specified value from localStorage, if not expired.
* @param {string} key
* @return {string|Object}
*/
get: function (key) {
if (!supportsStorage()) return null;
// Return the de-serialized item if not expired
if (flushExpiredItem(key)) {
return null;
}
// Tries to de-serialize stored value if its an object, and returns the normal value otherwise.
var value = getItem(key);
if (!value || !supportsJSON()) {
return value;
}
try {
// We can't tell if its JSON or a string, so we try to parse
return JSON.parse(value);
} catch (e) {
// If we can't parse, it's probably because it isn't an object
return value;
}
},
/**
* Removes a value from localStorage.
* Equivalent to 'delete' in memcache, but that's a keyword in JS.
* @param {string} key
*/
remove: function (key) {
if (!supportsStorage()) return;
flushItem(key);
},
/**
* Returns whether local storage is supported.
* Currently exposed for testing purposes.
* @return {boolean}
*/
supported: function () {
return supportsStorage();
},
/**
* Flushes all lscache items and expiry markers without affecting rest of localStorage
*/
flush: function () {
if (!supportsStorage()) return;
eachKey(function (key) {
flushItem(key);
});
},
/**
* Flushes expired lscache items and expiry markers without affecting rest of localStorage
*/
flushExpired: function () {
if (!supportsStorage()) return;
eachKey(function (key) {
flushExpiredItem(key);
});
},
/**
* Appends CACHE_PREFIX so lscache will partition data in to different buckets.
* @param {string} bucket
*/
setBucket: function (bucket) {
cacheBucket = bucket;
},
/**
* Resets the string being appended to CACHE_PREFIX so lscache will use the default storage behavior.
*/
resetBucket: function () {
cacheBucket = '';
},
/**
* @returns {number} The currently set number of milliseconds each time unit represents in
* the set() function's "time" argument.
*/
getExpiryMilliseconds: function () {
return expiryMilliseconds;
},
/**
* Sets the number of milliseconds each time unit represents in the set() function's
* "time" argument.
* Sample values:
* 1: each time unit = 1 millisecond
* 1000: each time unit = 1 second
* 60000: each time unit = 1 minute (Default value)
* 360000: each time unit = 1 hour
* @param {number} milliseconds
*/
setExpiryMilliseconds: function (milliseconds) {
expiryMilliseconds = milliseconds;
maxDate = calculateMaxDate(expiryMilliseconds);
},
/**
* Sets whether to display warnings when an item is removed from the cache or not.
*/
enableWarnings: function (enabled) {
warnings = enabled;
},
getAllKey() {
let all = []
eachKey((key) => {
all.push(key)
})
return all;
}
};
export default lscache;
{
"name": "__MSG_main_manifest_name__",
"description": "__MSG_main_manifest_description__",
"version": "##version##",
"author": "wo@baiy.org",
"offline_enabled": true,
"homepage_url": "https://github.com/baiy/Ctool",
"manifest_version": 2,
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"browser_action": {
"default_icon": "img/icon_chrome.png",
"default_title": "__MSG_main_manifest_default_title__",
"default_popup": "index.html"
},
"background": {
"scripts": ["background.js"]
},
"icons": {
"16": "img/icon_chrome.png",
"48": "img/icon_chrome.png",
"128": "img/icon_chrome.png"
},
"permissions": [
"clipboardWrite",
"clipboardRead",
"*://get.geojs.io/*",
"*://*.baiy.org/*"
],
"commands": {
"panel": {
"description": "__MSG_main_manifest_commands_panel_description__",
"global": true
}
}
}
let windowId = null;
// 打开独立窗口
const panel = {
create() {
browser.windows.create({
url: browser.runtime.getURL("tool.html"),
type: "popup",
}, (w) => {
browser.windows.update(w.id, {
width: 810,
height: 610
});
windowId = w.id
})
},
open() {
if (windowId === null) {
this.create()
} else {
browser.windows.get(windowId, (w) => {
if (!w) {
this.create()
} else {
browser.windows.update(windowId, {focused: true})
}
})
}
},
onRemoved(id) {
if (id === windowId) {
windowId = null;
}
}
}
// 注册快捷键
browser.commands.onCommand.addListener((command) => {
switch (command) {
case "panel":
panel.open()
break;
default:
return;
}
})
// 窗口关闭事件
browser.windows.onRemoved.addListener((id) => {
panel.onRemoved(id);
})
export const openUrl = (url) => {
return browser.tabs.create({url:url});
}
export const getMessage = (messageName, values = {}, placeholders = []) => {
let substitutions = []
placeholders.forEach((key) => {
substitutions.push((key in values) ? values[key] : "")
})
return browser.i18n.getMessage(messageName, substitutions);
}
{
"name": "__MSG_main_manifest_name__",
"description": "__MSG_main_manifest_description__",
"version": "##version##",
"author": "wo@baiy.org",
"homepage_url": "https://github.com/baiy/Ctool",
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"manifest_version": 2,
"browser_action": {
"default_icon": "img/icon_chrome.png",
"default_title": "__MSG_main_manifest_default_title__",
"default_popup": "index.html"
},
"default_locale": "zh_CN",
"background": {
"scripts": ["background.js"]
},
"icons": {
"16": "img/icon_chrome.png",
"48": "img/icon_chrome.png",
"128": "img/icon_chrome.png"
},
"permissions": [
"clipboardWrite",
"clipboardRead",
"*://get.geojs.io/*",
"*://*.baiy.org/*"
],
"commands": {
"_execute_browser_action": {},
"panel": {
"description": "__MSG_main_manifest_commands_panel_description__"
}
}
}
import _ from "lodash"
const PREFIX = "_system_"
const timestamp = () => {
return Math.ceil((Date.parse(new Date()) / 1000))
}
const encode = (value, expiry = 0) => {
return {
v: value,
e: expiry ? expiry + timestamp() : 0,
}
}
const decode = (data) => {
if (
!_.isObject(data)
|| !("v" in data)
|| !("e" in data)
) {
return null;
}
return data
}
const keyName = (key) => {
return `${PREFIX}${key}`
}
export default {
get(key, def = null) {
let data = decode(window.utools.dbStorage.getItem(keyName(key)));
// 不存在/过期
if (data === null || (data.e !== 0 && data.e < timestamp())) {
// 移除过期
if (data !== null) {
this.remove(key)
}
return def;
}
return data.v
},
set(key, value, expiry = 0) {
return window.utools.dbStorage.setItem(
keyName(key),
encode(value, _.toInteger(expiry))
)
},
remove(key) {
return window.utools.dbStorage.removeItem(keyName(key))
},
// 清理过期
clear() {
for (let {_id} of window.utools.db.allDocs(PREFIX)) {
let key = _id.replace(PREFIX, "");
if (key) {
// 获取一次 过期会自动删除
this.get(key)
}
}
return true;
},
getAllKey() {
return window.utools.db.allDocs(PREFIX).map(({_id}) => {
return _id.replace(PREFIX, "");
})
}
}
\ No newline at end of file
export const openUrl = (url) => {
if ("utools" in window && "shellOpenExternal" in window.utools) {
return window.utools.shellOpenExternal(url)
}
return window.open(url);
}
\ No newline at end of file
{
"pluginName": "Ctool",
"description": "Ctool 程序开发常用工具",
"author": "baiy",
"homepage": "https://github.com/baiy/Ctool",
"main": "tool.html",
"version": "##version##",
"logo": "img/icon_utools.png",
"pluginSetting": {
"single": false
},
"development": {
"main": "http://localhost:8081/tool.html"
},
"features": "##features##"
}
\ No newline at end of file
<template>
<div>
<div class="ctool-bottom">
<div class="ctool-bottom-right" style="width: 210px">
<Tooltip class="ctool-bottom-tooltip" transfer :content="$t('main_tool_'+currentTool) + ' -' + $t('main_history')" placement="top">
<Badge v-if="historyLength>0" dot :offset="[6,10]">
<Icon type="md-time" :size="22" @click="historyShow= true"/>
</Badge>
<template v-else>
<Icon type="md-time" :size="22" @click="historyShow= true"/>
</template>
</Tooltip>
<Tooltip class="ctool-bottom-tooltip" transfer content="Github" placement="top">
<Icon type="logo-github" :size="22" @click="openUrl('https://github.com/baiy/Ctool')"/>
</Tooltip>
<Tooltip class="ctool-bottom-tooltip" transfer :content="$t('main_ui_setting')" placement="top">
<Icon type="md-settings" :size="22" @click="settingShow = true"/>
</Tooltip>
<Tooltip class="ctool-bottom-tooltip" transfer :content="$t('main_ui_issues')" placement="top">
<Icon type="md-help-circle" :size="22" @click="openUrl('https://github.com/baiy/Ctool/issues')"/>
</Tooltip>
<Tooltip class="ctool-bottom-tooltip" transfer :content="$t('main_ui_open_full')" placement="top">
<Icon type="md-expand" :size="22" @click="openUrl('_new')" v-if="!isUtools"/>
</Tooltip>
</div>
<div class="ctool-bottom-block" style="margin-right: 210px">
<notice-block/>
</div>
</div>
<Drawer :title="$t('main_ui_setting')" v-model="settingShow" :width="400">
<setting-block v-if="settingShow"></setting-block>
</Drawer>
<Drawer :title="$t('main_tool_'+currentTool)+' - '+$t('main_history')" v-model="historyShow" :width="100">
<History v-if="historyShow" @close="historyShow = false"/>
</Drawer>
</div>
</template>
<script>
import Notice from './notice'
import History from './history'
import {isUtools, openUrl} from '../helper'
import settingBlock from "../views/setting/block"
import model from "../tool/model";
import historyFactory from "../tool/history";
export default {
name: "bottom",
components: {
"notice-block": Notice,
"setting-block": settingBlock,
History
},
data() {
return {
isUtools,
settingShow: false,
historyShow: false,
currentTool: model.getCurrentTool(),
currentToolSetInterval: null,
}
},
computed: {
historyLength() {
return historyFactory(this.currentTool).length()
}
},
created() {
this.currentToolSetInterval = setInterval(() => {
this.currentTool = model.getCurrentTool()
}, 2000);
},
methods: {
openUrl(url) {
openUrl(url === '_new' ? window.location.href : url)
}
},
destroyed() {
if (this.currentToolSetInterval !== null) {
clearInterval(this.currentToolSetInterval);
this.currentToolSetInterval = null
}
},
}
</script>
<style>
.ctool-bottom {
width: 100%;
position: fixed;
bottom: 0;
left: 0;
height: 33px;
padding-top: 2px;
overflow: hidden;
border-top: 1px solid #dcdee2;
background: #FFF;
}
.ctool-bottom-block {
height: 30px;
line-height: 30px;
overflow: hidden;
}
.ctool-bottom-right {
line-height: 30px;
float: right;
text-align: right;
}
.ctool-bottom-right .ivu-icon {
margin-right: 15px;
cursor: pointer;
}
.ctool-bottom-right .ivu-icon:hover {
color: #1abc9c;
}
</style>
<template>
<div>
<Table ref="historyTable" border :columns="columns" :data="lists" :height="tableHeight">
<template slot-scope="{ row }" slot="_value">
<div>{{ stringify(row.value) }}}</div>
</template>
<template slot-scope="{ index }" slot="_op">
<Button type="primary" size="small" @click="view(index)">{{ $t('main_ui_views') }}</Button>
<Button type="primary" style="margin-left: 5px" @click="load(index)" size="small">
{{ $t('main_ui_load') }}
</Button>
</template>
</Table>
<div class="drawer-footer">
<Button type="primary" @click="clear">{{ $t('main_history_clear') }}</Button>
</div>
</div>
</template>
<script>
import model from '../tool/model'
import historyFactory, {setForceLoadHistoryIndex} from "../tool/history";
export default {
name: "history",
data() {
return {
lists: [],
columns: [
{
title: this.$t('main_history_time'),
key: 'time',
width: 180
},
{
title: this.$t('main_history_data'),
slot: '_value',
ellipsis: true,
},
{
title: this.$t('main_history_op'),
slot: '_op',
width: 160
}
],
}
},
computed: {
tableHeight() {
// 设置表格高度
return window.innerHeight - 140
}
},
created() {
let history = historyFactory(this.currentTool())
if (history.length() < 1) {
return this.$Message.error(this.$t('main_history_null').toString())
}
this.lists = history.all()
},
methods: {
currentTool() {
return model.getCurrentTool()
},
stringify(value) {
return JSON.stringify(value)
},
view(index) {
this.$Modal.info({
render: (h) => {
return h('Input', {
props: {
type: "textarea",
rows: 10,
value: JSON.stringify(historyFactory(this.currentTool()).get(index), null, "\t"),
}
})
},
width: 700,
okText: this.$t('main_ui_close')
})
},
load(index) {
setForceLoadHistoryIndex(index)
this.close()
this.$router.push({
path: this.$router.currentRoute.fullPath,
query: {
t: Date.now(),
},
});
},
clear() {
historyFactory(model.getCurrentTool()).clear()
this.close()
},
close(){
this.$emit('close')
}
}
};
</script>
<style scoped>
.drawer-footer {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
border-top: 1px solid #e8e8e8;
padding: 10px 16px;
text-align: right;
background: #fff;
}
</style>
<template>
<div style="position: relative;">
<slot></slot>
<div :style="extraStyle">
<slot name="extra">
<Button v-if="text" :type="type" size="small" @click="buttonClick">{{ text }}</Button>
</slot>
</div>
</div>
</template>
<script>
export default {
name: 'inputBlock',
props: {
text: {
type: String,
default: ""
},
bottom: {
type: String,
default: "4px"
},
top: {
type: String,
default: ""
},
left: {
type: String,
default: ""
},
right: {
type: String,
default: "4px"
},
type:{
type: String,
default: "primary"
}
},
computed:{
extraStyle(){
let css = {
fixed:"position: absolute",
zindex:"z-index:100",
vertical:this.top !== "" ? `top:${this.top}` : `bottom:${this.bottom}`,
horizontal:this.left !== "" ? `left:${this.left}` : `right:${this.right}`,
};
return Object.values(css).join(";")
}
},
methods: {
buttonClick() {
this.$emit('on-default-right-bottom-click');
}
}
};
</script>
<template>
<div class="ctool-notice-block">
<ul class="ctool-notice-ul" :class="{'ctool-notice-animate-up': animateUp}">
<li v-for="(item, index) in listData" :key="index">
<Icon v-if="item.icon.length > 0" :type="item.icon"/>
<span v-html="item.text" @click="open(item)"></span>
</li>
</ul>
</div>
</template>
<script>
import {env, openUrl} from "../helper";
import instance from '../tool/instance'
import cache from "../tool/cache";
import user from "../tool/user";
import axios from "axios";
import _ from "lodash";
import {dispatchWindowResize} from '../tool/event'
import {getCurrentLocale} from "../i18n/index";
const CACHE_NAME = 'notice_item';
const CACHE_EXPIRY = 3600 * 24;
const NOTICE_TYPE = ['info', 'ad', 'hidden'];
const NOTICE_URL_TYPE = ['tool', 'web'];
const handleNoticeItems = ({code, data, info}) => {
if (code !== 0) {
throw new Error(info)
}
let items = [];
for (let item of data) {
if (
!('type' in item)
|| !('text' in item)
|| !('url' in item)
|| !('type' in item.url)
|| !('value' in item.url)
|| !NOTICE_TYPE.includes(item.type)
|| !NOTICE_URL_TYPE.includes(item.url.type)
|| item.text.trim().length < 1
) {
continue;
}
items.push({
type: item.type,
icon: 'icon' in item ? item.icon : "",
text: item.text.trim(),
url: {
type: item.url.type,
value: item.url.value,
},
})
}
return items;
}
const logError = (e) => {
console.log(e)
}
export default {
name: "notice",
data() {
return {
animateUp: false,
// {icon,text,url:{type,value}},
listData: [],
timer: null
}
},
created() {
// 初始化数据
this.load();
},
methods: {
load() {
try {
// 加载缓存
let items = cache.get(CACHE_NAME)
if (items !== null) {
return this.init(items)
}
try {
// 远程加载
axios({
url: 'https://www.baiy.org/chrome_tool/notice/',
responseType: 'json',
params: {
i: getCurrentLocale(),
v: env('version'),
p: env('platform'),
u: user.uid(),
r: Math.random()
}
}).then(({data}) => {
let notices = handleNoticeItems(data);
if (notices.length > 0) {
cache.set(CACHE_NAME, notices, CACHE_EXPIRY)
this.init(notices);
}
}).catch((error) => {
logError(error)
});
} catch (e) {
logError(e)
}
} catch (e) {
logError(e)
}
},
init(notices) {
this.listData = _.cloneDeep(notices.filter((item) => {
return item.type !== "hidden"
}))
if (this.listData.length > 1) {
this.timer = setInterval(this.scrollAnimate, 6000);
}
this.$nextTick(()=>{
dispatchWindowResize()
})
},
scrollAnimate() {
this.animateUp = true
setTimeout(() => {
this.listData.push(this.listData[0])
this.listData.shift()
this.animateUp = false
}, 500)
},
open(item) {
switch (item.url.type) {
case "tool":
instance.enter(item.url.value)
break;
case "web":
openUrl(item.url.value)
break;
}
}
},
destroyed() {
if (this.timer !== null) {
clearInterval(this.timer)
}
}
};
</script>
<style scoped>
.ctool-notice-block {
padding-left: 20px;
height: 28px;
border-radius: 20px;
margin: 0 auto;
overflow: hidden;
}
.ctool-notice-block .ctool-notice-ul li {
width: 100%;
height: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
list-style: none;
line-height: 28px;
/*text-align: right;*/
font-size: 14px;
color: #515a6e;
}
.ctool-notice-animate-up {
transition: all 0.5s ease-in-out;
transform: translateY(-28px);
}
.ctool-notice-block .ctool-notice-ul li span:hover {
font-weight: 400;
text-decoration: underline;
cursor: pointer;
}
.ctool-notice-block .ctool-notice-ul li i {
margin-right: 5px;
}
</style>
<template>
<Form inline :style="style" class="option-block">
<slot></slot>
</Form>
</template>
<script>
export default {
name: 'optionBlock',
props: {
center: {
type: Boolean,
default: false
},
disablePadding: {
type: Boolean,
default: false
}
},
computed: {
style() {
let css = {
padding: !this.disablePadding ? "10px 0" : "0",
};
if (this.center) {
css.textAlign = "center"
}
return css;
}
}
};
</script>
<style>
.option-block .ivu-form-item {
margin-bottom: 0;
}
</style>
// 工具缓存数据过期时间(秒)
const TOOL_DATA_EXPIRY = 3600 * 24
// 徽章过期时间(天)
const BADGE_EXPIRY = 5
// 分类徽章
const BADGE_CATEGORY = []
// 工具徽章
const BADGE_TOOL = []
// 默认常用工具
const DEFAULT_COMMON_TOOL = [
'hash', 'encrypt', 'json', 'base64', 'url', 'timestamp',
'qrCode', 'pinyin', 'ip', 'code', 'unicode',
'text', 'randomString', 'diffs',
]
const category = [
{'name': 'common'},
{'name': 'encryption'},
{'name': 'conversion'},
{'name': 'encoder_decoder'},
{'name': 'check'},
{'name': 'generate'},
{'name': 'other'},
{'name': 'yeahmao'},
{'name': 'code_gen'},
]
const tool = [
{
'name': 'hash',
'cat': ['encryption']
},
{
'name': 'encrypt',
'cat': ['encryption']
},
{'name': 'sign', 'cat': ['encryption', 'check']},
{'name': 'base64', 'cat': ['encryption','encoder_decoder']},
{'name': 'json', 'cat': ['conversion']},
{'name': 'url', 'cat': ['encoder_decoder']},
{'name': 'timestamp', 'cat': ['conversion']},
{'name': 'qrCode', 'cat': ['generate']},
{'name': 'barcode', 'cat': ['generate']},
{'name': 'pinyin', 'cat': ['conversion']},
{'name': 'ip', 'cat': ['other']},
{'name': 'code', 'cat': ['other']},
{'name': 'unicode', 'cat': ['encoder_decoder']},
{'name': 'decimalConvert', 'cat': ['conversion']},
{'name': 'regex', 'cat': ['check']},
{'name': 'randomString', 'cat': ['generate']},
{'name': 'serializeConversion', 'cat': ['conversion']},
{'name': 'diffs', 'cat': ['check']},
{'name': 'crontab', 'cat': ['check']},
{'name': 'websocket', 'cat': ['other']},
{'name': 'unit', 'cat': ['other']},
{'name': 'time', 'cat': ['other']},
{'name': 'uuid', 'cat': ['generate']},
{'name': 'ascii', 'cat': ['conversion']},
{'name': 'variableConversion', 'cat': ['conversion']},
{'name': 'jwt', 'cat': ['encoder_decoder']},
{'name': 'hexString', 'cat': ['conversion']},
{'name': 'hex2base64', 'cat': ['conversion']},
{'name': 'text', 'cat': ['other']},
{'name': 'html', 'cat': ['encoder_decoder']},
{'name': 'binary', 'cat': ['generate']},
{'name': 'armConverter', 'cat': ['conversion']},
{'name': 'bcrypt', 'cat': ['encryption','check']},
{'name': 'ipcalc', 'cat': ['generate']},
// yeahmao
{'name': 'hexConvert', 'cat': ['yeahmao']},
{'name': 'constFinder', 'cat': ['yeahmao']},
// code_gen
{'name': 'codeGenJson', 'cat': ['code_gen']},
{'name': 'codeGenJava', 'cat': ['code_gen']},
]
// 工具类功能配置
const feature = {
qrCode: [
{name: "generate", title: '生成'},
{name: "reader", title: '解析'}
]
}
const utools = {
keyword: {
hash: ['md5', 'sha1', 'sha256', 'sha512', 'sm3'],
encrypt: ['AES', 'DES', 'RC4', 'Rabbit', 'TripleDes', 'sm2', 'sm4'],
jwt: ['jwtDecode'],
hexString: ['hex to string', 'string to hex', '十六进制转字符串', '字符串转十六机制'],
text: ['文本处理', '大小写转换', '中英文标点转换', '简繁转换', '字符替换', '字符统计', '行去重', '添加行号', '行排序', '过滤行首尾不可见字符', '过滤空行'],
sign: ['签名', '验签', 'rsa'],
binary: ['原码', '补码', '反码'],
armConverter: ['ARM', 'HEX'],
serializeConversion:["json","xml","yaml","php array","php serialize","properties"]
},
cmds: {
timestamp: [
{
"type": "regex",
// "label": "", //程序自动根据tool title填充
"match": "/(^\\d{10}(?:\\d{3})?$)|(^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?$)/i",
"minLength": 10,
"maxLength": 25
}
],
qrCode: [
{
"type": "over",
"minLength": 1,
"feature": 'generate' // 适配工具内功能
},
{
"type": "regex",
"match": "/[a-zA-z]+://[^\\s]*/i",
"minLength": 8,
"feature": 'reader' // 适配工具内功能
}
],
ip: [
{
"type": "regex",
"match": "/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/i",
"minLength": 7,
"maxLength": 15
}
],
unicode: [
{
"type": "regex",
"match": "/\\\\u[0-9a-f]{4}/i",
"minLength": 6
}
]
}
}
module.exports = {
category,
tool,
feature,
utools,
toolDataExpiry: TOOL_DATA_EXPIRY,
badgeExpiry: BADGE_EXPIRY,
badgeCategory: BADGE_CATEGORY,
badgeTool: BADGE_TOOL,
defaultCommonTool: DEFAULT_COMMON_TOOL
}
import {v4 as uuidV4} from 'uuid';
import {openUrl as chromiumOpenUrl} from './adapter/chromium/helper';
import {openUrl as utoolsOpenUrl} from './adapter/utools/helper';
import {openUrl as firefoxOpenUrl} from './adapter/firefox/helper';
export const env = (key) => {
return process['ctool'][key] ? process['ctool'][key] : "";
};
export const isChrome = !!env('isChrome')
export const isEdge = !!env('isEdge')
export const isFirefox = !!env('isFirefox')
export const isChromium = !!env('isChromium')
export const isWeb = !!env('isWeb')
export const isUtools = !!env('isUtools')
export const uuid = () => {
return uuidV4().toLowerCase();
}
export const openUrl = (url) => {
if (isChromium) {
return chromiumOpenUrl(url)
}
if (isUtools) {
return utoolsOpenUrl(url)
}
if (isFirefox) {
return firefoxOpenUrl(url)
}
return window.open(url);
};
export const version = env('version').trim()
export const setDisplayMode = (mode) => {
mode = ['light', 'dark', 'auto'].includes(mode) ? mode : 'light'
console.log(`set display mode:${mode}`)
document.getElementsByTagName('html')[0].setAttribute('theme-mode', mode);
}
// 编译语言包
const path = require('path');
const fs = require('fs');
const JSON5 = require('json5')
const _ = require('lodash')
const LOCAL_LISTS = [
{code: '_default', name: "默认(default)"},
{code: "en", name: "English"},
{code: 'zh_CN', name: "简体中文"}
]
// 默认地区
const DEFAULT_LOCALE = 'zh_CN'
// 为空展示地区
const DEFAULT_SHOW_LOCALE = 'en'
const codeToLocale = (code) => {
return code === "_default" ? DEFAULT_LOCALE : code;
}
const placeholder = (message) => {
let placeholders = [];
const result = message.match(new RegExp('{.+?}', 'g'));
if (result !== null) {
result.forEach((item) => {
item = item.replace('{', '').replace('}', '')
if (!placeholders.includes(item)) {
placeholders.push(item)
}
})
}
return placeholders
}
const getLocale = (code) => {
const localeDir = path.join(__dirname, "locales/" + code)
if (!(fs.existsSync(localeDir) && fs.statSync(localeDir).isDirectory())) {
return null;
}
let locale = {}
fs.readdirSync(path.resolve(localeDir)).forEach((file) => {
if (file.indexOf('.i18n.json5') !== -1) {
let type = file.replace('.i18n.json5', '');
let config = JSON5.parse(fs.readFileSync(path.join(__dirname, `locales/${code}/${file}`), 'utf-8'));
// 写入区域
if (type === "main") {
locale[`${type}_locale`] = {message: code}
}
Object.keys(config).forEach(function (key) {
let placeholders = placeholder(config[key])
locale[`${type}_${key}`] = {
message: config[key],
}
if (placeholders.length > 0) {
locale[`${type}_${key}`]["placeholders"] = placeholders
}
});
}
})
return locale;
}
const getAllLocale = () => {
// 所有语言包
let locales = {
lists: LOCAL_LISTS,
default_locale: DEFAULT_LOCALE,
default_show_locale: DEFAULT_SHOW_LOCALE,
detail: {}
}
fs.readdirSync(path.resolve(path.join(__dirname, "locales"))).forEach((code) => {
let locale = getLocale(code)
if (locale !== null) {
locales['detail'][code] = locale;
}
})
checkAllLocale(locales)
return locales
}
const checkAllLocale = (locales) => {
/**
* 检查语言包是否完备
* 确保中文/英文语言包是否一致
*/
const zhKeys = Object.keys(locales['detail']['zh_CN'])
const enKeys = Object.keys(locales['detail']['en'])
const zhDiff = _.difference(zhKeys, enKeys);
if (zhDiff.length > 0) {
throw new Error(`中/英文语言包存在差异[key](英文缺失):${zhDiff.join(',')}`)
}
const enDiff = _.difference(enKeys, zhKeys);
if (enDiff.length > 0) {
throw new Error(`中/英文语言包存在差异[key](中文缺失):${enDiff.join(',')}`)
}
}
const ALL_LOCALE = getAllLocale()
module.exports = {
getLocales() {
return ALL_LOCALE;
},
// 生成运行时语言包
generate() {
fs.writeFileSync(path.join(__dirname, 'locales/build.js'), `export default ${JSON.stringify(ALL_LOCALE, null, 4)}`);
},
getMessage(code, key) {
let locales = ALL_LOCALE['detail']
let locale = codeToLocale(code)
let text = key;
if ((locale in locales) && (key in locales[locale])) {
text = locales[locale][key]['message']
} else if (locale !== DEFAULT_SHOW_LOCALE) {
// 获取默认语言
text = this.getMessage(DEFAULT_SHOW_LOCALE, key)
}
return text;
},
translate(key, values = {}, code = DEFAULT_LOCALE) {
if (!key) return '';
const text = this.getMessage(code, key)
const matchRge = new RegExp('{.+?}', 'g')
const matchString = text.match(matchRge);
const replaceHash = {};
// 获取语言包对应翻译内容
let result = text;
if (matchString) {
matchString.forEach((wildcard) => {
let key = wildcard.replace("{", "").replace("}", '')
if ((key in values) && !(wildcard in replaceHash)) {
replaceHash[wildcard] = values[key]
}
});
}
result = result.replace(matchRge, (replacer) => {
return replacer in replaceHash ? replaceHash[replacer] : replacer;
});
return result;
}
}
import VueI18n from 'vue-i18n'
import Vue from 'vue'
import {getMessage as chromiumGetMessage} from "../adapter/chromium/helper"
import {getMessage as firefoxGetMessage} from "../adapter/firefox/helper"
import {isChromium,isFirefox} from "../helper";
import locales from "./locales/build.js";
Vue.use(VueI18n)
// 区域列表
export const LOCALE_LISTS = locales.lists
export const LOCALE_DETAIL = locales.detail
export const DEFAULT_LOCALE = locales.default_locale
export const DEFAULT_SHOW_LOCALE = locales.default_show_locale
let currentLocale = "";
// 设置当前地区
export const setCurrentLocale = (locale) => {
currentLocale = locale;
}
const getMessage = (code, key) => {
let locale = code === "_default" ? DEFAULT_LOCALE : code;
let text = key;
if ((locale in LOCALE_DETAIL) && (key in LOCALE_DETAIL[locale])) {
text = LOCALE_DETAIL[locale][key]['message']
} else if (locale !== DEFAULT_SHOW_LOCALE) {
// 获取默认语言
text = getMessage(DEFAULT_SHOW_LOCALE, key)
}
return text;
}
const translate = (code, key, values = {}) => {
if (!key) return '';
let locale = code === "_default" ? DEFAULT_LOCALE : code;
if (code === "_default" && (isChromium || isFirefox) ) {
let placeholders = []
if (
(locale in LOCALE_DETAIL)
&& (key in LOCALE_DETAIL[locale])
&& ('placeholders' in LOCALE_DETAIL[locale][key])
) {
placeholders = LOCALE_DETAIL[locale][key]['placeholders']
}
if (isChromium){
return chromiumGetMessage(key, values, placeholders)
}
if (isFirefox){
return firefoxGetMessage(key, values, placeholders)
}
}
let text = getMessage(code, key);
const matchRge = new RegExp('{.+?}', 'g')
const matchString = text.match(matchRge);
const replaceHash = {};
// 获取语言包对应翻译内容
let result = text;
if (matchString) {
matchString.forEach((wildcard) => {
let key = wildcard.replace("{", "").replace("}", '')
if ((key in values) && !(wildcard in replaceHash)) {
replaceHash[wildcard] = values[key]
}
});
}
result = result.replace(matchRge, (replacer) => {
return replacer in replaceHash ? replaceHash[replacer] : replacer;
});
return result;
}
window["__"] = (key, values = {}, locale = null) => {
locale = !locale ? currentLocale : locale
// values 必须是对象
return translate(
(!locale ? currentLocale : locale),
key,
values
)
}
// 获取当前真实区域
export const getCurrentLocale = () => {
return __('main_locale').toString();
}
export const i18n = new VueI18n({
locale: 'zh_CN',
missing: (locale, key, vm, values) => {
if (Array.isArray(values) && values.length === 1) {
if (Array.isArray(values[0])) {
// 数组转对象表示法
let temp = {}
for (let i = 0; i < values[0].length; i++) {
temp[i] = values[0][i]
}
return __(key, temp)
}
return __(key, values[0])
}
return __(key)
}
})
{
"convert": "Convert",
"stringEscape": "stringEscape",
"codeGenFrida": "codeGenFrida",
"codeGenX64dbgIdaVtable": "X64dbgIdaVtable",
}
{
"info_source": "Information Source: ",
"convert": "Convert",
"output": "Output",
"error": "error: {0}"
}
{
"convent": "Convent",
"input_prompt": "Multiple characters are separated by space",
"input_10": "DEC",
"input_16": "HEX",
"input_8": "OCT",
"input_2": "BIN",
"input_string": "String",
"clear": "Clear",
"code_table": "ASCII table",
"yes": "Y",
"no": "N",
"input_null_prompt": "Please enter the corresponding code",
"convent_error": "Conversion exception: {0}",
"is_show": "Is Show",
"description": "Description",
"code_nul": "Null char",
"code_soh": "Start of Heading",
"code_stx": "Start of Text",
"code_etx": "End of Text",
"code_eot": "End of Transmission",
"code_enq": "Enquiry",
"code_ack": "Acknowledgment",
"code_bel": "Bell",
"code_bs": "Back Space",
"code_tab": "Horizontal Tab",
"code_lf": "Line Feed",
"code_vt": "Vertical Tab",
"code_ff": "Form Feed",
"code_cr": "Carriage Return",
"code_so": "Shift Out / X-On",
"code_si": "Shift In / X-Off",
"code_dle": "Data Line Escape",
"code_dc1": "Device Control 1 (oft. XON)",
"code_dc2": "Device Control 2",
"code_dc3": "Device Control 3 (oft. XOFF)",
"code_dc4": "Device Control 4",
"code_nak": "Negative Acknowledgement",
"code_syn": "Synchronous Idle",
"code_etb": "End of Transmit Block",
"code_can": "Cancel",
"code_em": "End of Medium",
"code_sub": "Substitute",
"code_esc": "Escape",
"code_fs": "File Separator",
"code_gs": "Group Separator",
"code_rs": "Record Separator",
"code_us": "Unit Separator",
"code_del": "Delete",
"code_space": "Space",
}
{
"content": "Content",
"background": "Background",
"line_color": "Line Color",
"bar_width": "Bar Width",
"height": "Height",
"margin": "Margin",
"show_text": "Show Text",
"hide": "Hide",
"top": "Top",
"bottom": "Bottom",
"text_align": "Text Align",
"left": "Left",
"center": "Center",
"right": "Right",
"font": "Font",
"bold": "Bold",
"italic": "Italic",
"font_size": "Size",
"text_margin": "Margin",
"invalid_content": "Invalid content"
}
{
"input": "Input",
"encode": "Encode",
"decode": "Decode",
"url_safe": "Url Safe",
"output": "Output",
"error": "error: {0}",
"hex_dump": "Hex Dump",
"setting": "Setting",
"hex_dump_show_mode": "Show Mode",
"hex_dump_format": "Format",
"hex_dump_caps": "Caps Lock",
"hex_dump_caps_lower": "Lower",
"hex_dump_caps_upper": "Upper",
"hex_dump_width": "Width",
"hex_dump_show_mode_hex": "Hex",
"hex_dump_show_mode_text": "Text",
"hex_dump_setting": "Hex Dump Setting",
"hex_dump_format_twos": "Twos",
"hex_dump_format_fours": "Fours",
"hex_dump_format_eights": "Eights",
"hex_dump_format_sixteens": "Sixteens",
"hex_dump_format_none": "None",
}
{
"generate": "Generate",
"password": "String",
"hash_password": "Hash",
"rounds": "Rounds",
"generate_submit": "Generate",
"check": "check",
"check_submit": "Check",
"check_result_success": "Right",
"check_result_error": "Wrong",
"rounds_range": "Rounds range [{0} - {1}]"
}
{
"input": "Input",
"length": "{0} bit",
"true_form": "trueForm",
"inverse": "Inverse",
"complement": "Complement",
"error": "error: {0}"
}
{
"more": "More Languages",
"indent": "Code Indent",
"indent_width": "Indent Space {0}",
"compress": "Compress",
"complete": "Operation Complete",
"error_prompt": "Error Prompt"
}
{
"expression": "Expression",
"execute_time": "Execute Time",
"example": "Example",
"format": "Format",
"execute_time_list": "Execute Time List",
"no": "{0}: {1}",
"symbol": "Symbol",
"description": "Description",
"symbol_description_1": "any value",
"symbol_description_2": "value list separator",
"symbol_description_3": "range of values",
"symbol_description_4": "step values",
}
{
"input_placeholder": "Please enter the number to be converted",
"input": "Input",
"input_type_common": "Common",
"input_type_other": "Other",
"base": "Base-{0}",
"result": "Result {0}",
"alphabet": "64 Alphabet",
"reset": "Reset Default",
"alphabet_length_error": "The conversion alphabet must be 64 bits in length"
}
{
"more": "More Languages",
"collapse":"Collapse Identical",
"beautify":"Beautify"
}
{
"input": "Input",
"password":"Password/Secret Key",
"encrypt": "Encrypt",
"decrypt":"Decrypt",
"generate_secret_key": "Generate Secret Key",
"output":"Output",
"public_key": "Public Key:",
"private_key":"Private Key:",
"secret_key_prompt": "Please save the key pair in time, the current key data cannot be restored after closing the dialog",
"close":"Close",
"failed": "Operation failed with error:{0}"
}
{
"content": "Content",
"uppercase": "Uppercase",
}
{
"input": "assemble",
"output": "Output",
"uppercase": "Uppercase",
}
{
"input": "Input",
"encode":"Encode",
"decode": "Decode",
"output":"Output"
}
{
"input": "Please enter IP address",
"query":"Query",
"local": "Local IP",
"info_source":"IP information source",
"ok": "Query Success",
"error":"Query Fail:{0}"
}
{
ip: "IP Address",
format: "Input Format",
mask: "Subnet Mask",
ip_info: "IP Detail",
ip_info_long: "Long IP",
ip_info_ip8: "Octal IP",
ip_info_ip10: "Decimal IP",
ip_info_ip16: "Hexadecimal IP",
ip_info_ip2: "Binary IP",
mask_info: "Mask Detail",
mask_info_mask: "Subnet Mask",
mask_info_long: "Long Mask",
mask_info_opposite: "Opposite Mask",
mask_info_mask8: "Octal Mask",
mask_info_mask16: "Hexadecimal Mask",
mask_info_mask2: "Binary Mask",
network_info: "Network Detail",
network_export: "Export Available IP",
network_info_available: "Available IP Size",
network_info_size: "All IP Size",
network_info_base: "Base",
network_info_first: "First IP",
network_info_last: "Last IP",
network_info_broadcast: "Broadcast Address",
mask_set_title: "Calculate mask by number of available IP"
}
{
"input": "Input",
"output": "Output",
"json_input_empty": "Please Enter Json Content",
"error": "Exception:{0}",
"format": "Format",
"compress": "Compress",
"escape": "Escape",
"clear_escape": "Unescape",
"add_escape":"Add Escape",
"unicode":"Unicode",
"unicode_to_zh": "Unicode->Zh",
"zh_to_unicode": "Zh->Unicode",
"complete": "Processing is complete",
"get": "Get Query",
"object":"To Object",
"csv": "CSV",
"table":"Table",
"path":"Path",
"add_quote":"Add Quote",
"column_name":"Column Name",
"json_type_json": "Json",
"json_type_keyed": "Keyed",
"json_type_array": "Array",
"json_type_column": "Column Array",
"inline": "Inline"
}
{
"input": "Input",
"decode":"Decode",
"output": "Output",
"decode_fail":"Decode Fail:{0}"
}
{
// ui
"ui_setting": "Settings",
"ui_reset": "Reset",
"ui_views": "View",
"ui_load": "Loading",
"ui_close": "Close",
"ui_issues": "Issues Feedback",
"ui_open_full": "Open Full Screen Tab",
"ui_copy_text_ok": "Copy Success ^o^",
"ui_copy_image_ok": "Image Copied ^o^",
// views
"setting_language": "Language",
"common_tool": "Common Tool",
"unselected_tool": "Unselected Tool",
"keyboard_setting": "Setting Shortcuts",
"display_mode": "Theme",
"display_mode_light": "Light",
"display_mode_dark": "Dark",
"display_mode_auto": "Auto",
"copy_results_to_clipboard": "Copy results to clipboard",
"read_content_from_clipboard": "Read content from clipboard",
"read_clipboard_content_trim": "Read clipboard content trim",
"common_tool_setting": "Common Tool Settings",
"keyboard_firefox_1": "Need to manually set shortcuts",
"keyboard_firefox_2": "Open [Add-ons Manager(about:addons)],Click the setting button on the right side of [Manage your extension],Select [Manage Extension Shortcuts] to modify these shortcuts.",
"keyboard_firefox_3": "Operation Method",
"history": "History",
"history_clear": "Clear history",
"history_time": "Operation time",
"history_data": "Data",
"history_op": "Operation",
"history_null": "History not found",
// category
"category_common": "Common",
"category_encryption": "En/Decrypt",
"category_conversion": "Convert",
"category_encoder_decoder": "En/Decoder",
"category_check": "Validator",
"category_generate": "Generate",
"category_other": "Other",
"category_yeahmao": "yeahmao",
"category_code_gen": "code_gen",
// tool
"tool_hash": "Hash",
"tool_encrypt": "Encrypt & Decrypt",
"tool_sign": "Sign",
"tool_base64": "Base64",
"tool_json": "JSON",
"tool_url": "Url En/Decode",
"tool_timestamp": "Timestamp",
"tool_qrCode": "QR Code",
"tool_pinyin": "Chinese Pinyin",
"tool_ip": "Ip Query",
"tool_code": "Code Formatter ",
"tool_unicode": "Unicode",
"tool_decimalConvert": "Base Convert",
"tool_regex": "Regex",
"tool_randomString": "Random String",
"tool_serializeConversion": "Serialize Convert",
"tool_diffs": "Compare Text",
"tool_crontab": "Crontab",
"tool_websocket": "Websocket",
"tool_unit": "Unit Convert",
"tool_time": "Time Calculator",
"tool_uuid": "UUID",
"tool_ascii": "ASCII",
"tool_variableConversion": "Variable Name",
"tool_jwt": "JWT",
"tool_hexString": "Hex/String",
"tool_hex2base64": "Hex/Base64",
"tool_text": "Text",
"tool_html": "Html En/Decode",
"tool_binary": "trueForm/inverse/complement",
"tool_armConverter": "ARM/HEX",
"tool_bcrypt": "Bcrypt",
"tool_ipcalc": "Ipcalc",
"tool_hexConvert": "hexConvert",
"tool_codeGenJson": "codeGenJson",
"tool_codeGenJava": "codeGenJava",
"tool_constFinder": "constFinder",
// other
"css_main_category_item_style": "padding: 0 10px",
"editor_line_wrapping": "Line Wrapping",
// manifest
"manifest_name": "Ctool Commonly Used Developer Tools",
"manifest_description": "Commonly Used Developer Tools: Hash/Encrypt/Decrypt/Code Convert/Timestamp/Qrcode/IP Query/Code Formatter/Unicode/Regex/...",
"manifest_default_title": "Commonly Used Developer Tools",
"manifest_commands_panel_description": "Open Popup Tool Winodws"
}
{
"input": "Input",
"normal":"No Tone",
"tone": "Have Tone",
"abbr":"First Letter",
"output": "Output",
"delimiter_null":"No Delimiter",
"delimiter_space": "Space Delimiter",
"delimiter_1":"'-' Delimiter",
"delimiter_2": "'_' Delimiter",
"delimiter_3":"'.' Delimiter"
}
{
"generate_title": "Generate",
"generate_input": "Input",
"generate_error": "Generate Error:{0}",
"reader_title": "Reader",
"reader_input": "Please enter the QR code image url or click the button below to upload the image",
"reader_output": "Output",
"reader_error": "Reader Error:{0}",
"reader_parsing_failure": "Image parsing failure",
}
{
"length": "Length",
"amount": "Number of strings",
"delimiter": "Delimiter",
"digital": "Digital",
"lowercase": "Lowercase",
"uppercase": "Uppercase",
"symbol": "Symbol",
"unique": "Unique",
"add_quote": "Add Quote",
"generate": "Generate",
"output": "Output"
}
{
"reference": "Reference",
"expression": "Expression",
"replace_content": "Replace Content",
"replace": "Replace",
"delete": "Delete",
"input": "Input",
"global": "Global",
"ignore_case": "Ignore Case",
"output": "Output",
"output_count": "{0} Total Matches",
"output_emty": "No match result, please check expression",
"error": "Error:{0}",
}
{
"input": "Input",
"output":"output",
"error": "Error:"
}
{
"sign_data": "Sign Data/Verify data",
"verify_code": "Signature after Base64 encoding",
"sign": "Sign",
"verify": "Verify",
"generate_keypair": "Generate Keypair",
"public_key": "PEM Public Key",
"private_key": "PEM Private Key",
"keypair_type": "Type",
"keypair_length": "Length",
"generate_cancel": "Cancel",
"generate_in": "Generate in progress",
"generate": "Generate",
"error_sign_content_empty": "Sign Data/Private Key Required",
"error": "error:{0}",
"error_verify_content_empty": "Base64 Sign/Public Key Required",
"verify_fail": "Verify Fail",
"verify_ok": "Verify Success",
}
{
"content": "Text Content",
"input": "Input",
"resume": "Resume",
"case_conversion": "Case Converter",
"upper_all": "Upper",
"lower_all": "Lower",
"upper_line_start": "Upper Line Start",
"lower_line_start": "lower Line Start",
"upper_word_start": "Upper Word Start",
"lower_word_start": "Lower Word Start",
"punctuation": "Punctuation",
"cn": "CN",
"en": "EN",
"simplified_traditional": "Chinese",
"simplified": "Simplified",
"traditional": "Traditional",
"replace": "Replace",
"line_remove_duplicate": "Line Remove Duplicate",
"line_number": "Line Number",
"line_number_add": "Add",
"line_number_remove": "Remove",
"line_sort": "Line Sort",
"line_sort_asc": "Asc",
"line_sort_desc": "Desc",
"filter": "Filter",
"filter_trim": "(trim) Removes whitespace from both ends of a string",
"filter_blank_line": "Filter blank line",
"filter_all_br": "Filter newlines",
"stat": "Stat",
"stat_explain": "Explain",
"replace_search": "Search",
"replace_replace": "Replace",
"replace_regular": "Regular",
"replace_explain": "You can enter multiple lines, batch replace by line",
"cancel": "Cancel",
"submit": "Submit",
"string_length": "String",
"byte_length": "byte(utf8/gbk)",
"word_length": "Word",
"line_length": "Line",
"zh_length": "(Cn)Char/Punctuation",
"en_length": "(En)Char/Word/Punctuation",
"int_length": "(Number)Char/word",
"ok": "Complete",
"error": "Error:{0}",
"item": "Item",
"explain": "Explain",
"explain_byte_length_utf8_name": "byte utf8",
"explain_byte_length_utf8_info": "Cn Char Length:3",
"explain_byte_length_gbk_name": "byte gbk",
"explain_byte_length_gbk_info": "Cn Char Length:2",
"explain_string_length_name": "String Length",
"explain_string_length_info": "Cn/En Char Length:1 Line Break Length:0",
"explain_word_length_name": "Word",
"explain_word_length_info": "Cn+En Word+punctuation+Number word",
"explain_int_length_name": "Number Char",
"explain_int_length_info": "Number Char Count. For example:'a1024 1024' result:8",
"explain_int_word_length_name": "Number Word",
"explain_int_word_length_info": "For example:'a1024 1024' result:1",
"explain_blank_line_length_name": "Line Length",
"explain_blank_line_length_info": "Blank lines are also included in the number of lines",
"value": "value",
"stat_show": "Word:{0} UTF-8:{1} GBK:{2}"
}
{
"diff_tool": "Difference Calculator",
"and": "And",
"diff": "Diff",
"operation": "Time Operation",
"add": "Add",
"reduce": "Reduce",
"after": "After",
"is": "Is",
"error": "error:{0}",
"error_duration_length": "The year/month interval can only be an integer",
"current_time": "Current Time",
"current_date": "Current Date",
"current_month_date": "Current Month Date",
"current_year_date": "Current Year Date",
"year": "Year",
"month": "Month",
"week": "Week",
"day": "Day",
"hour": "Hour",
"minute": "Minute",
"second": "Second",
analyze: "Time Analyze",
analyze_year: "Year",
analyze_quarter: "Quarter",
analyze_month: "Month",
analyze_year_output: "{year}: Q{quarter},{weekOfYear} Week,{dayOfYear} Day,{hourOfYear} Hour,{minuteOfYear} Minute,{secondOfYear} Second",
analyze_quarter_output: "Q{quarter}:{weekOfQuarter} Week,{dayOfQuarter} Day,{hourOfQuarter} Hour,{minuteOfQuarter} Minute,{secondOfQuarter} Second",
analyze_month_output: "{month}:{weekOfMonth} Week,{hourOfMonth} Hour,{minuteOfMonth} Minute,{secondOfMonth} Second"
}
{
"input": "Input",
"get":"Get Current",
"normal_second": "Normal Second",
"normal_millisecond":"Normal Millisecond",
"unix_second": "Unix Timestamp Second",
"unix_millisecond":"Unix Timestamp Millisecond",
"output": "Output",
"copy":"Copy",
"error_format": "Input Format Error",
"error":"error:{0}",
"format": "Format",
"value":"Value"
}
{
"input": "Input",
"encode": "Encode",
"decode": "Decode",
"mode_default": "Unicode Default",
"mode_wide": "Unicode Wide",
"mode_wide_bracket": "Unicode Wide(Has Bracket)",
"mode_number": "Unicode Number",
"mode_html_10": "HTML-code(Base-10)",
"mode_html_16": "HTML-code(Base-16)",
"mode_css_16": "Css-code(Base-16)",
"ignore_ascii": "Ignore Ascii",
"output": "Output",
"error": "error:{0}"
}
{
"length": "Length",
"area": "Area",
"volume": "Volume",
"weight": "Weight",
"temperature": "Temperature",
"pressure": "Pressure",
"power": "Power",
"work": "Work",
"density": "Density",
"strength": "Strength",
"time": "Time",
"speed": "Speed",
"byte": "Byte",
"angle": "Angle",
"length_km": "kilometres",
"length_m": "metre",
"length_dm": "decimetre",
"length_cm": "centimetre",
"length_mm": "millimetre",
"length_um": "Micrometre",
"length_nm": "Nanometre",
"length_pm": "Picometre",
"length_ly": "Light year",
"length_au": "Astronomical unit",
"length_in": "Inch",
"length_ft": "Foot",
"length_yd": "Yard",
"length_mi": "Mile",
"length_nmi": "Nautical mile",
"length_fm": "Fathom",
"length_fur": "furlong",
"length_cn_li": "lǐ",
"length_cn_zhang": "zhàng",
"length_cn_chi": "chǐ",
"length_cn_cun": "cùn",
"length_cn_fen": "fēn",
"length_cn_li2": "lí",
"length_cn_hao": "háo",
"area_km_2": "Square kilometre",
"area_ha": "Hectare",
"area_are": "Hectare Are",
"area_m_2": "Square metre",
"area_dm_2": "square decimetre",
"area_cm_2": "Square centimetre",
"area_mm_2": "Square millimeter",
"area_acre": "Acre",
"area_mi_2": "Square Acre",
"area_yd_2": "Square Yard",
"area_ft_2": "Square foot",
"area_in_2": "Square inch",
"area_rd_2": "Square rod",
"area_cn_qing": "qǐng",
"area_cn_mu": "mǔ",
"area_cn_fen": "fēn",
"area_cn_chi_2": "Square chǐ",
"area_cn_cun_2": "Square cùn",
"volume_m_3": "Cubic metre",
"volume_dm_3": "cubic decimeter",
"volume_cm_3": "Cubic centimeter",
"volume_mm_3": "Cubic millimeter",
"volume_l": "Litre",
"volume_dl": "deciliter",
"volume_ml": "milliliter",
"volume_cl": "centiliter",
"volume_uL": "microliter",
"volume_hl": "hectolitre",
"volume_ft_3": "Cubic feet",
"volume_in_3": "Cubic inch",
"volume_yd_3": "Cubic yards",
"volume_acre_ft": "acre foot",
"volume_uk_gal": "Uk gallon",
"volume_us_gal": "Us gallon",
"volume_uk_oz": "Uk ounce",
"volume_us_oz": "Us ounce",
"weight_kg": "kilogram",
"weight_g": "gram",
"weight_mg": "Milligrams",
"weight_ug": "microgram",
"weight_t": "Ton",
"weight_q": "Quintal",
"weight_ct": "carat",
"weight_lb": "Pound",
"weight_oz": "ounce",
"weight_gr": "Grain",
"weight_lt": "British long ton",
"weight_st1": "US short ton",
"weight_st2": "Stone",
"weight_uk_cwt": "British long hundredweight",
"weight_us_cwt": "US short hundredweight",
"weight_dr": "Dram",
"weight_cn_dan": "dān",
"weight_cn_jin": "jīn",
"weight_cn_liang": "liǎng",
"weight_cn_qian": "qián",
"temperature_c": "Celsius",
"temperature_f": "Fahrenheit",
"temperature_k": "Kelvin",
"temperature_r": "Rankine_scale",
"temperature_re": "Réaumur_scale",
"pressure_pa": "Pascal",
"pressure_kpa": "KPa",
"pressure_hpa": "HPa",
"pressure_atm": "Standard atmospheric pressure",
"pressure_mmhg": "MmHg",
"pressure_in_hg": "Inch mercury",
"pressure_bar": "bar",
"pressure_mbar": "millibar",
"pressure_psf": "Pounds per square feet",
"pressure_psi": "Pounds per square inch",
"pressure_mmwg": "mmH2O",
"pressure_kgf_cm_2": "Kgf/square centimeter",
"pressure_kgf_m_2": "Kgf/Square meter",
"pressure_mpa": "MPa",
"power_w": "watt",
"power_kw": "kilowatt",
"power_hp": "Imperial horsepower",
"power_ps": "Metric horsepower",
"power_kg_m_s": "Kg·m/sec",
"power_kcal_s": "Kcal/s",
"power_btu_s": "British Thermal Unit/sec",
"power_ft_lb_s": "Feet·lbs/sec",
"power_j_s": "Joule/sec",
"power_n_m_s": "Newton m/s",
"work_j": "joule",
"work_kg_m": "Kg·m",
"work_ps_h": "Metric horsepower·hour",
"work_hp_h": "British horsepower·hour",
"work_kw_h": "Kilowatt hour",
"work_kw_h_": "Spend",
"work_cal": "Card",
"work_kcal": "Kcal",
"work_btu": "British Thermal Unit",
"work_ft_lb": "Foot pound",
"work_kj": "Kilojoule",
"density_kg_cm_3": "Kg/Cubic centimeter",
"density_kg_dm_3": "Kg/cubic decimeter",
"density_kg_m_3": "Kg/Cubic metre",
"density_g_cm_3": "g/Cubic centimeter",
"density_g_dm_3": "g/cubic decimeter",
"density_g_m_3": "g/Cubic metre",
"strength_n": "Newton",
"strength_kn": "Kilogram Newton",
"strength_kgf": "Kilogram force",
"strength_gf": "Keli 克力",
"strength_tf": "Metric ton force",
"strength_lbf": "Pound force",
"strength_kip": "Kilopound force",
"strength_dyn": "Dyne",
"time_yr": "year",
"time_week": "week",
"time_d": "Day",
"time_h": "Hour",
"time_min": "Minute",
"time_s": "Second",
"time_ms": "millisecond",
"time_us": "Microseconds",
"time_ns": "Nanosecond",
"speed_m_s": "metre/Sec",
"speed_km_s": "kilometer/Sec",
"speed_km_h": "kilometer/Hour",
"speed_c": "Speed of light",
"speed_mach": "Maher",
"speed_mile_h": "Mile/hour",
"speed_in_s": "Inch/Sec",
"byte_bit": "Bit",
"byte_b": "byte",
"byte_kb": "Kilobytes",
"byte_mb": "Megabyte",
"byte_gb": "Gigabytes",
"byte_tb": "Terabyte",
"byte_pb": "Petabytes",
"byte_eb": "Exabytes",
"angle_circle": "circle",
"angle_angle": "angle",
"angle_gon": "grade",
"angle_degree": "degree",
"angle_min": "Minute",
"angle_s": "Second",
"angle_rad": "radian",
"angle_mrad": "Milliradian",
"metric_system": "Metric System",
"imperial_units": "Imperial Units",
"chinese_units": "Chinese Units",
"angle_units": "Angle Units",
"radian_units": "Radian Units",
"all": "All"
}
{
"input": "Input",
"encode": "Encode",
"decode": "Decode",
"output": "Output"
}
{
"amount": "Amount",
"delimiter": "Delimiter",
"hyphens": "Hyphens(-)",
"is_upper": "Upper",
"is_add_quote": "Add Quote",
"uint8_array": "Uint8 Array",
"output": "Output"
}
{
"input": "Input",
"input_placeholder":"A line of one"
}
{
"connect": "Connect",
"close": "Close",
"send_content": "Send Content",
"log_content": "Request/Response/Log Content",
"send": "Send",
"copy": "Copy",
"clear": "Clear",
"you": "You",
"server": "Server",
"error_connect": "Ws is not connected yet, or the connection fails, please check",
"error_content": "Send content cannot be empty",
"connect_ok": "Connect Success",
"close_ok": "Close Success",
"error": "error:{0}",
"connect_start": "Connecting:{0}",
"close_start": "Closing:{0}"
}
{
"convert": "转换",
"stringEscape": "字符串转义",
"codeGenFrida": "生成frida代码",
"codeGenX64dbgIdaVtable": "X64dbg断点虚表",
}
{
"info_source": "数据来源:",
"convert": "转换",
"output": "输出",
"error": "错误: {0}"
}
{
"convent": "转换",
"input_prompt": "多个字符用空格分隔",
"input_10": "十进制",
"input_16": "十六进制",
"input_8": "八进制",
"input_2": "二进制",
"input_string": "字符串",
"clear": "清空",
"code_table": "编码表",
"yes": "是",
"no": "否",
"input_null_prompt": "请输入对应的待转换编码",
"convent_error": "转换异常: {0}",
"is_show": "是否显示",
"description": "描述",
"code_nul": "空字符(Null)",
"code_soh": "标题开始",
"code_stx": "本文开始",
"code_etx": "本文结束",
"code_eot": "传输结束",
"code_enq": "请求",
"code_ack": "确认回应",
"code_bel": "响铃",
"code_bs": "退格",
"code_tab": "水平定位符号",
"code_lf": "换行键",
"code_vt": "垂直定位符号",
"code_ff": "换页键",
"code_cr": "归位键",
"code_so": "取消变换(Shift out)",
"code_si": "启用变换(Shift in)",
"code_dle": "跳出数据通讯",
"code_dc1": "设备控制一(XON 启用软件速度控制)",
"code_dc2": "设备控制二",
"code_dc3": "设备控制三(XOFF 停用软件速度控制)",
"code_dc4": "设备控制四",
"code_nak": "确认失败回应",
"code_syn": "同步用暂停",
"code_etb": "区块传输结束",
"code_can": "取消",
"code_em": "连接介质中断",
"code_sub": "替换",
"code_esc": "跳出",
"code_fs": "文件分割符",
"code_gs": "组群分隔符",
"code_rs": "记录分隔符",
"code_us": "单元分隔符",
"code_del": "删除",
"code_space": "空格"
}
{
"content": "内容",
"background": "背景",
"line_color": "线条颜色",
"bar_width": "条码宽",
"height": "条码高",
"margin": "外边距",
"show_text": "文本显示",
"hide": "隐藏",
"top": "上方",
"bottom": "下方",
"text_align": "水平位置",
"left": "居左",
"center": "居中",
"right": "居右",
"font": "字体",
"bold": "粗体",
"italic": "斜体",
"font_size": "大小",
"text_margin": "外边距",
"invalid_content": "无效条码内容"
}
{
"input": "输入",
"encode": "编码",
"decode": "解码",
"url_safe": "Url 安全",
"output": "输出",
"error": "错误: {0}",
"hex_dump": "Hex Dump",
"setting": "配置",
"hex_dump_show_mode":"显示模式",
"hex_dump_format": "格式",
"hex_dump_caps": "大小写",
"hex_dump_caps_lower": "小写",
"hex_dump_caps_upper": "大写",
"hex_dump_width":"宽度",
"hex_dump_show_mode_hex": "十六进制",
"hex_dump_show_mode_text": "文本",
"hex_dump_setting": "十六进制显示配置",
"hex_dump_format_twos": "2位",
"hex_dump_format_fours": "4",
"hex_dump_format_eights": "8位",
"hex_dump_format_sixteens": "16位",
"hex_dump_format_none": "无分隔"
}
{
"generate": "生成",
"password": "明文(String)",
"hash_password": "哈希(Hash)",
"rounds": "Rounds",
"generate_submit": "生成",
"check": "校验",
"check_submit": "校验",
"check_result_success": "正确",
"check_result_error": "错误",
"rounds_range": "Rounds 范围 [{0} - {1}]"
}
{
"input": "输入",
"length": "{0} 位",
"true_form": "原码",
"inverse": "反码",
"complement": "补码",
"error": "错误: {0}"
}
{
"more": "更多语言",
"indent": "代码缩进",
"indent_width": "缩进 空格 {0}",
"compress": "压缩",
"complete": "操作完成",
"error_prompt": "错误提示"
}
{
"expression": "表达式",
"execute_time": "最近执行时间",
"example": "例子",
"format": "格式",
"execute_time_list": "最近10次执行时间",
"no": "第{0}次: {1}",
"symbol": "特殊符号",
"description": "描述",
"symbol_description_1": "代表任何时刻都接受的意思。举例来说,范例一内那个日、月、周都是*,就代表着不论何月、何日的礼拜几的12:00都执行后续命令的意思。",
"symbol_description_2": "代表分隔时段的意思。举例来说,如果要执行的工作是3:00与6:00时,就会是:0 3,6 * * * command时间还是有五列,不过第二列是 3,6 ,代表3与6都适用",
"symbol_description_3": "代表一段时间范围内,举例来说,8点到12点之间的每小时的20分都进行一项工作:20 8-12 * * * command仔细看到第二列变成8-12.代表 8,9,10,11,12 都适用的意思",
"symbol_description_4": "那个n代表数字,即是每隔n单位间隔的意思,例如每五分钟进行一次,则:*/5 * * * * command用*与/5来搭配,也可以写成0-59/5,意思相同",
}
{
"input_placeholder": "请输入待转换数字",
"input": "转换数字",
"input_type_common": "常用",
"input_type_other": "其他",
"base": "{0} 进制",
"result": "转换结果 {0}",
"alphabet": "64位字母表",
"reset": "恢复默认",
"alphabet_length_error": "转换字母表必须是64位长度"
}
{
"more": "更多语言",
"collapse":"折叠相同",
"beautify":"格式化"
}
{
"input": "输入",
"password":"密码/密钥",
"encrypt": "加密",
"decrypt":"解密",
"generate_secret_key": "生成密钥对",
"output":"输出",
"public_key": "公钥:",
"private_key":"私钥:",
"secret_key_prompt": "请及时保存秘钥对, 关闭对话框后无法恢复当前秘钥数据",
"close":"关闭",
"failed": "操作失败:{0}"
}
{
"content": "内容",
"uppercase": "大写字母",
}
{
"input": "输入",
"output": "输出",
"uppercase": "大写字母",
}
{
"input": "输入",
"encode":"编码",
"decode": "解码",
"output":"输出"
}
{
"input": "请输入IP地址",
"query":"查询",
"local": "本地IP",
"info_source":"IP 信息来源",
"ok": "查询成功",
"error":"查询异常:{0}"
}
{
format: "输入格式",
ip: "IP地址",
mask: "掩码",
ip_info: "IP 信息",
ip_info_long: "整型IP",
ip_info_ip8: "点分八进制IP",
ip_info_ip10: "点分十进制IP",
ip_info_ip16: "点分十六进制IP",
ip_info_ip2: "点分二进制IP",
mask_info: "掩码信息",
mask_info_mask: "子网掩码",
mask_info_long: "整型掩码",
mask_info_opposite: "反掩码",
mask_info_mask8: "点分八进制掩码",
mask_info_mask16: "点分十六进制掩码",
mask_info_mask2: "点分二进制掩码",
network_info: "网络信息",
network_export: "导出可用IP",
network_info_available: "可用数量",
network_info_size: "全部数量",
network_info_base: "网络",
network_info_first: "第一个IP",
network_info_last: "最后一个IP",
network_info_broadcast: "广播地址",
mask_set_title: "通过可用IP数量设置掩码"
}
{
"input": "输入",
"output": "输出",
"json_input_empty": "请输入JSON内容",
"error":"异常:{0}",
"format": "格式化",
"compress":"压缩",
"escape": "转义",
"clear_escape":"移除转义",
"add_escape":"添加转义",
"unicode":"Unicode",
"unicode_to_zh": "Unicode转中文",
"zh_to_unicode":"中文转Unicode",
"complete": "处理完成",
"get": "Get参数",
"object":"转实体类",
"csv": "CSV",
"table":"Table",
"path":"Path",
"add_quote":"添加引号",
"column_name":"列名",
"json_type_json": "Json",
"json_type_keyed": "关联对象",
"json_type_array": "数组",
"json_type_column": "列数组",
"inline": "内联(inline)",
}
{
"input": "输入",
"decode":"解码",
"output": "输出",
"decode_fail":"解码失败:{0}"
}
{
// ui
"ui_setting": "设置",
"ui_reset": "重置",
"ui_views": "查看",
"ui_load": "加载",
"ui_close": "关闭",
"ui_issues": "问题反馈",
"ui_open_full": "打开全屏窗口",
"ui_copy_text_ok": "复制成功 ^o^",
"ui_copy_image_ok": "图片已复制 ^o^",
// 界面
"setting_language": "语言",
"common_tool": "常用工具",
"unselected_tool": "未选择工具",
"keyboard_setting": "快捷键设置",
"display_mode": "主题",
"display_mode_light": "浅色",
"display_mode_dark": "深色",
"display_mode_auto": "自动",
"copy_results_to_clipboard": "自动复制结果到剪贴板",
"read_content_from_clipboard": "自动读取剪贴板内容",
"read_clipboard_content_trim": "读取剪贴板内容过滤首尾不可见字符",
"common_tool_setting": "常用工具设置",
"keyboard_firefox_1": "请手动设置快捷键",
"keyboard_firefox_2": "请打开附加组件管理器(about:addons),点击“管理扩展程序”右侧的设置按钮,选择“管理扩展快捷键”来修改这些快捷键。",
"keyboard_firefox_3": "操作方法",
"history": "历史记录",
"history_clear": "清空历史记录",
"history_time": "操作时间",
"history_data": "数据",
"history_op": "操作",
"history_null": "暂无历史记录",
// 分类
"category_common": "常用",
"category_encryption": "加解密",
"category_conversion": "转换",
"category_encoder_decoder": "编解码",
"category_check": "校验",
"category_generate": "生成",
"category_other": "其他",
"category_yeahmao": "夜猫逐梦",
"category_code_gen": "代码生成器",
// 工具
"tool_hash": "哈希(hash)",
"tool_encrypt": "加密/解密",
"tool_sign": "签名/验签",
"tool_base64": "BASE64编码",
"tool_json": "JSON工具",
"tool_url": "URL编码",
"tool_timestamp": "时间戳",
"tool_qrCode": "二维码",
"tool_pinyin": "汉字转拼音",
"tool_ip": "IP地址查询",
"tool_code": "代码格式化",
"tool_unicode": "Unicode",
"tool_decimalConvert": "进制转换",
"tool_regex": "正则表达式",
"tool_randomString": "随机字符生成",
"tool_serializeConversion": "序列化转换",
"tool_diffs": "文本比对",
"tool_crontab": "Crontab",
"tool_websocket": "Websocket",
"tool_unit": "单位换算",
"tool_time": "时间计算器",
"tool_uuid": "UUID生成",
"tool_ascii": "ASCII",
"tool_variableConversion": "变量名",
"tool_jwt": "JWT解码",
"tool_hexString": "Hex/String",
"tool_hex2base64": "Hex/Base64",
"tool_text": "文本处理",
"tool_html": "Html编码",
"tool_binary": "原码/反码/补码",
"tool_armConverter": "ARM/HEX",
"tool_bcrypt": "Bcrypt",
"tool_ipcalc": "IP网络计算器",
"tool_hexConvert": "字节转换",
"tool_codeGenJson": "代码生成Json",
"tool_codeGenJava": "代码生成Java",
"tool_constFinder": "常量查找",
// 其他
"css_main_category_item_style": "padding: 0 20px",
"editor_line_wrapping": "自动换行",
// manifest
"manifest_name": "Ctool 程序开发常用工具",
"manifest_description": "程序开发常用工具,哈希/加解密/编码转换/时间戳/二维码/拼音/IP查询/代码优化/Unicode/正则等...",
"manifest_default_title": "常用开发工具",
"manifest_commands_panel_description": "打开独立工具窗口",
}
{
"input": "输入",
"normal":"无声调",
"tone": "有声调",
"abbr":"首字母",
"output": "输出",
"delimiter_null":"无分隔符",
"delimiter_space": "空格分隔",
"delimiter_1":"'-'中划线分隔",
"delimiter_2": "'_'下划线分隔",
"delimiter_3":"'.'点分隔"
}
{
"generate_title": "二维码生成",
"generate_input": "输入",
"generate_error": "生成错误:{0}",
"reader_title": "二维码解析",
"reader_input": "请输入二维码图片地址或点击下方按钮上传图片",
"reader_output": "输出",
"reader_error": "解析错误:{0}",
"reader_parsing_failure": "图片解析失败",
}
{
"length": "长度",
"amount":"数量",
"delimiter": "分隔符",
"digital":"数字",
"lowercase": "小写字母",
"uppercase":"大写字母",
"symbol": "特殊符号",
"unique":"唯一",
"add_quote": "添加引号",
"generate":"生成",
"output": "输出"
}
{
"reference": "参考",
"expression": "正则表达式",
"replace_content": "替换内容",
"replace": "替换",
"delete": "删除",
"input": "输入待处理内容",
"global": "全局搜索",
"ignore_case": "忽略大小写",
"output": "输出",
"output_count": "共 {0} 个匹配项",
"output_emty": "没有匹配结果,请检查正则",
"error": "错误:{0}",
}
{
"input": "输入",
"output":"输出",
"error": "错误:{0}"
}
{
"sign_data": "待签名内容/验签数据",
"verify_code": "Base64编码后签名",
"sign": "签名",
"verify": "验签",
"generate_keypair": "生成公钥/私钥",
"public_key": "PEM格式公钥",
"private_key": "PEM格式私钥",
"keypair_type": "密钥格式",
"keypair_length": "密钥长度",
"generate_cancel": "取消",
"generate_in": "生成中",
"generate": "生成",
"error_sign_content_empty": "待签名内容/PEM格式私钥 必填",
"error": "错误:{0}",
"error_verify_content_empty": "Base64编码后签名/PEM格式公钥 必填",
"verify_fail": "验签失败",
"verify_ok": "验签成功",
}
{
"content": "文本内容",
"input": "输入",
"resume": "恢复",
"case_conversion": "大小写转换",
"upper_all": "全部大写",
"lower_all": "全部小写",
"upper_line_start": "行首大写",
"lower_line_start": "行首小写",
"upper_word_start": "词首大写",
"lower_word_start": "词首小写",
"punctuation": "中英标点转换",
"cn": "中文",
"en": "英文",
"simplified_traditional": "简繁转换",
"simplified": "简体",
"traditional": "繁体",
"replace": "替换",
"line_remove_duplicate": "行去重",
"line_number": "行号",
"line_number_add": "添加",
"line_number_remove": "移除",
"line_sort": "行排序",
"line_sort_asc": "升序",
"line_sort_desc": "降序",
"filter": "过滤",
"filter_trim": "过滤行首尾不可见字符(trim)",
"filter_blank_line": "过滤多余空行",
"filter_all_br": "过滤换行符",
"stat": "统计",
"stat_explain": "统计说明",
"replace_search": "查找",
"replace_replace": "替换",
"replace_regular": "正则",
"replace_explain": "可输入多行, 按行进行批量替换",
"cancel": "取消",
"submit": "提交",
"string_length": "字符数",
"byte_length": "字节数(utf8/gbk)",
"word_length": "字数",
"line_length": "行数",
"zh_length": "(中文)字数/标点",
"en_length": "(英文)字母/单词/标点",
"int_length": "(数字)字符/单词",
"ok": "完成",
"error": "错误:{0}",
"item": "项目",
"explain": "说明",
"explain_byte_length_utf8_name": "字节数utf8",
"explain_byte_length_utf8_info": "中文字符计3个长度",
"explain_byte_length_gbk_name": "字节数gbk",
"explain_byte_length_gbk_info": "中文字符计2个长度",
"explain_string_length_name": "字符数",
"explain_string_length_info": "中/英文字符均计1个长度 换行符不计入长度",
"explain_word_length_name": "字数",
"explain_word_length_info": "中文字数+英文单词数+中文标点数+英文标点数+数字单词数",
"explain_int_length_name": "数字字符",
"explain_int_length_info": "统计单个数字出现次数 例如:'a1024 1024' 结果为:8",
"explain_int_word_length_name": "数字单词",
"explain_int_word_length_info": "例如:'a1024 1024' 结果为:1 其中:'a1024' 为英文单词 '1024' 为数字单词",
"explain_blank_line_length_name": "行数",
"explain_blank_line_length_info": "空行也会计入行数",
"value": "值",
"stat_show": "字数:{0} UTF-8:{1} GBK:{2}",
}
{
"diff_tool": "差值计算器",
"and": "与",
"diff": "相差",
"operation": "时间操作",
"add": "添加",
"reduce": "减少",
"after": "后",
"is": "为",
"error": "错误:{0}",
"error_duration_length": "年/月间隔只能是整数",
"current_time": "当前时间",
"current_date": "当前日期",
"current_month_date": "当月日期",
"current_year_date": "当年日期",
"year": "年",
"month": "月",
"week": "周",
"day": "日",
"hour": "小时",
"minute": "分钟",
"second": "秒",
analyze: "时间分析",
analyze_year: "年",
analyze_quarter: "季度",
analyze_month: "月",
analyze_year_output: "{year}年:{quarter}季度,{weekOfYear}周,{dayOfYear}天,{hourOfYear}小时,{minuteOfYear}分钟,{secondOfYear}秒",
analyze_quarter_output: "{quarter}季度:{weekOfQuarter}周,{dayOfQuarter}天,{hourOfQuarter}小时,{minuteOfQuarter}分钟,{secondOfQuarter}秒",
analyze_month_output: "{month}月:{weekOfMonth}周,{hourOfMonth}小时,{minuteOfMonth}分钟,{secondOfMonth}秒"
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册