diff --git a/.csharpierrc.json b/.csharpierrc.json
new file mode 100644
index 00000000..e7740ff1
--- /dev/null
+++ b/.csharpierrc.json
@@ -0,0 +1,3 @@
+{
+ "printWidth": 140
+}
\ No newline at end of file
diff --git a/Attachments/iconfont/demo.css b/Attachments/iconfont/demo.css
new file mode 100644
index 00000000..a67054a0
--- /dev/null
+++ b/Attachments/iconfont/demo.css
@@ -0,0 +1,539 @@
+/* Logo 字体 */
+@font-face {
+ font-family: "iconfont logo";
+ src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
+ src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
+ url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
+ url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
+ url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
+}
+
+.logo {
+ font-family: "iconfont logo";
+ font-size: 160px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+/* tabs */
+.nav-tabs {
+ position: relative;
+}
+
+.nav-tabs .nav-more {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ height: 42px;
+ line-height: 42px;
+ color: #666;
+}
+
+#tabs {
+ border-bottom: 1px solid #eee;
+}
+
+#tabs li {
+ cursor: pointer;
+ width: 100px;
+ height: 40px;
+ line-height: 40px;
+ text-align: center;
+ font-size: 16px;
+ border-bottom: 2px solid transparent;
+ position: relative;
+ z-index: 1;
+ margin-bottom: -1px;
+ color: #666;
+}
+
+
+#tabs .active {
+ border-bottom-color: #f00;
+ color: #222;
+}
+
+.tab-container .content {
+ display: none;
+}
+
+/* 页面布局 */
+.main {
+ padding: 30px 100px;
+ width: 960px;
+ margin: 0 auto;
+}
+
+.main .logo {
+ color: #333;
+ text-align: left;
+ margin-bottom: 30px;
+ line-height: 1;
+ height: 110px;
+ margin-top: -50px;
+ overflow: hidden;
+ *zoom: 1;
+}
+
+.main .logo a {
+ font-size: 160px;
+ color: #333;
+}
+
+.helps {
+ margin-top: 40px;
+}
+
+.helps pre {
+ padding: 20px;
+ margin: 10px 0;
+ border: solid 1px #e7e1cd;
+ background-color: #fffdef;
+ overflow: auto;
+}
+
+.icon_lists {
+ width: 100% !important;
+ overflow: hidden;
+ *zoom: 1;
+}
+
+.icon_lists li {
+ width: 100px;
+ margin-bottom: 10px;
+ margin-right: 20px;
+ text-align: center;
+ list-style: none !important;
+ cursor: default;
+}
+
+.icon_lists li .code-name {
+ line-height: 1.2;
+}
+
+.icon_lists .icon {
+ display: block;
+ height: 100px;
+ line-height: 100px;
+ font-size: 42px;
+ margin: 10px auto;
+ color: #333;
+ -webkit-transition: font-size 0.25s linear, width 0.25s linear;
+ -moz-transition: font-size 0.25s linear, width 0.25s linear;
+ transition: font-size 0.25s linear, width 0.25s linear;
+}
+
+.icon_lists .icon:hover {
+ font-size: 100px;
+}
+
+.icon_lists .svg-icon {
+ /* 通过设置 font-size 来改变图标大小 */
+ width: 1em;
+ /* 图标和文字相邻时,垂直对齐 */
+ vertical-align: -0.15em;
+ /* 通过设置 color 来改变 SVG 的颜色/fill */
+ fill: currentColor;
+ /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
+ normalize.css 中也包含这行 */
+ overflow: hidden;
+}
+
+.icon_lists li .name,
+.icon_lists li .code-name {
+ color: #666;
+}
+
+/* markdown 样式 */
+.markdown {
+ color: #666;
+ font-size: 14px;
+ line-height: 1.8;
+}
+
+.highlight {
+ line-height: 1.5;
+}
+
+.markdown img {
+ vertical-align: middle;
+ max-width: 100%;
+}
+
+.markdown h1 {
+ color: #404040;
+ font-weight: 500;
+ line-height: 40px;
+ margin-bottom: 24px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+ color: #404040;
+ margin: 1.6em 0 0.6em 0;
+ font-weight: 500;
+ clear: both;
+}
+
+.markdown h1 {
+ font-size: 28px;
+}
+
+.markdown h2 {
+ font-size: 22px;
+}
+
+.markdown h3 {
+ font-size: 16px;
+}
+
+.markdown h4 {
+ font-size: 14px;
+}
+
+.markdown h5 {
+ font-size: 12px;
+}
+
+.markdown h6 {
+ font-size: 12px;
+}
+
+.markdown hr {
+ height: 1px;
+ border: 0;
+ background: #e9e9e9;
+ margin: 16px 0;
+ clear: both;
+}
+
+.markdown p {
+ margin: 1em 0;
+}
+
+.markdown>p,
+.markdown>blockquote,
+.markdown>.highlight,
+.markdown>ol,
+.markdown>ul {
+ width: 80%;
+}
+
+.markdown ul>li {
+ list-style: circle;
+}
+
+.markdown>ul li,
+.markdown blockquote ul>li {
+ margin-left: 20px;
+ padding-left: 4px;
+}
+
+.markdown>ul li p,
+.markdown>ol li p {
+ margin: 0.6em 0;
+}
+
+.markdown ol>li {
+ list-style: decimal;
+}
+
+.markdown>ol li,
+.markdown blockquote ol>li {
+ margin-left: 20px;
+ padding-left: 4px;
+}
+
+.markdown code {
+ margin: 0 3px;
+ padding: 0 5px;
+ background: #eee;
+ border-radius: 3px;
+}
+
+.markdown strong,
+.markdown b {
+ font-weight: 600;
+}
+
+.markdown>table {
+ border-collapse: collapse;
+ border-spacing: 0px;
+ empty-cells: show;
+ border: 1px solid #e9e9e9;
+ width: 95%;
+ margin-bottom: 24px;
+}
+
+.markdown>table th {
+ white-space: nowrap;
+ color: #333;
+ font-weight: 600;
+}
+
+.markdown>table th,
+.markdown>table td {
+ border: 1px solid #e9e9e9;
+ padding: 8px 16px;
+ text-align: left;
+}
+
+.markdown>table th {
+ background: #F7F7F7;
+}
+
+.markdown blockquote {
+ font-size: 90%;
+ color: #999;
+ border-left: 4px solid #e9e9e9;
+ padding-left: 0.8em;
+ margin: 1em 0;
+}
+
+.markdown blockquote p {
+ margin: 0;
+}
+
+.markdown .anchor {
+ opacity: 0;
+ transition: opacity 0.3s ease;
+ margin-left: 8px;
+}
+
+.markdown .waiting {
+ color: #ccc;
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+ opacity: 1;
+ display: inline-block;
+}
+
+.markdown>br,
+.markdown>p>br {
+ clear: both;
+}
+
+
+.hljs {
+ display: block;
+ background: white;
+ padding: 0.5em;
+ color: #333333;
+ overflow-x: auto;
+}
+
+.hljs-comment,
+.hljs-meta {
+ color: #969896;
+}
+
+.hljs-string,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-strong,
+.hljs-emphasis,
+.hljs-quote {
+ color: #df5000;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-type {
+ color: #a71d5d;
+}
+
+.hljs-literal,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-attribute {
+ color: #0086b3;
+}
+
+.hljs-section,
+.hljs-name {
+ color: #63a35c;
+}
+
+.hljs-tag {
+ color: #333333;
+}
+
+.hljs-title,
+.hljs-attr,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+ color: #795da3;
+}
+
+.hljs-addition {
+ color: #55a532;
+ background-color: #eaffea;
+}
+
+.hljs-deletion {
+ color: #bd2c00;
+ background-color: #ffecec;
+}
+
+.hljs-link {
+ text-decoration: underline;
+}
+
+/* 代码高亮 */
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+ color: black;
+ background: none;
+ text-shadow: 0 1px white;
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+@media print {
+
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+ padding: 1em;
+ margin: .5em 0;
+ overflow: auto;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+ background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre)>code[class*="language-"] {
+ padding: .1em;
+ border-radius: .3em;
+ white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: slategray;
+}
+
+.token.punctuation {
+ color: #999;
+}
+
+.namespace {
+ opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+ color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+ color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #9a6e3a;
+ background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+ color: #07a;
+}
+
+.token.function,
+.token.class-name {
+ color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+ color: #e90;
+}
+
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
diff --git a/Attachments/iconfont/demo_index.html b/Attachments/iconfont/demo_index.html
new file mode 100644
index 00000000..7331559e
--- /dev/null
+++ b/Attachments/iconfont/demo_index.html
@@ -0,0 +1,1292 @@
+
+
+
+
+ iconfont Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Unicode
+ - Font class
+ - Symbol
+
+
+
查看项目
+
+
+
+
+
+
+ -
+
+
二维码
+ 
+
+
+ -
+
+
二维码
+ 
+
+
+ -
+
+
二维码
+ 
+
+
+ -
+
+
鼠标划词
+ 
+
+
+ -
+
+
表单文本框
+ 
+
+
+ -
+
+
划词翻译
+ 
+
+
+ -
+
+
鼠标
+ 
+
+
+ -
+
+
清理
+ 
+
+
+ -
+
+
刷新
+ 
+
+
+ -
+
+
backspace-outline
+ 
+
+
+ -
+
+
space
+ 
+
+
+ -
+
+
3
+ 
+
+
+ -
+
+
文本换行
+ 
+
+
+ -
+
+
文字
+ 
+
+
+ -
+
+
系统首选项
+ 
+
+
+ -
+
+
对勾
+ 
+
+
+ -
+
+
禁用
+ 
+
+
+ -
+
+
交换
+ 
+
+
+ -
+
+
icon-基础设置
+ 
+
+
+ -
+
+
可拖拽
+ 
+
+
+ -
+
+
退出
+ 
+
+
+ -
+
+
界面
+ 
+
+
+ -
+
+
符号-单行输入框
+ 
+
+
+ -
+
+
表单组件-输入框
+ 
+
+
+ -
+
+
文本识别
+ 
+
+
+ -
+
+
多行输入
+ 
+
+
+ -
+
+
14C截图
+ 
+
+
+ -
+
+
文字识别
+ 
+
+
+ -
+
+
Maximize-3
+ 
+
+
+ -
+
+
删除
+ 
+
+
+ -
+
+
保存
+ 
+
+
+ -
+
+
历史记录
+ 
+
+
+ -
+
+
常用示例
+ 
+
+
+ -
+
+
服务器
+ 
+
+
+ -
+
+
关于
+ 
+
+
+ -
+
+
收藏
+ 
+
+
+ -
+
+
关闭26
+ 
+
+
+ -
+
+
最大化
+ 
+
+
+ -
+
+
最小化
+ 
+
+
+ -
+
+
图钉
+ 
+
+
+ -
+
+
固定,图钉
+ 
+
+
+ -
+
+
复制
+ 
+
+
+ -
+
+
下拉
+ 
+
+
+ -
+
+
调整,半圆,亮度
+ 
+
+
+ -
+
+
snake
+ 
+
+
+ -
+
+
copy_large_hump
+ 
+
+
+ -
+
+
copy_c
+ 
+
+
+ -
+
+
喇叭
+ 
+
+
+
+
+
Unicode 引用
+
+
+
Unicode 是字体在网页端最原始的应用方式,特点是:
+
+ - 支持按字体的方式去动态调整图标大小,颜色等等。
+ - 默认情况下不支持多色,直接添加多色图标会自动去色。
+
+
+ 注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)
+
+
Unicode 使用步骤如下:
+
第一步:拷贝项目下面生成的 @font-face
+
@font-face {
+ font-family: 'iconfont';
+ src: url('iconfont.woff2?t=1704174060960') format('woff2'),
+ url('iconfont.woff?t=1704174060960') format('woff'),
+ url('iconfont.ttf?t=1704174060960') format('truetype');
+}
+
+
第二步:定义使用 iconfont 的样式
+
.iconfont {
+ font-family: "iconfont" !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+
第三步:挑选相应图标并获取字体编码,应用于页面
+
+<span class="iconfont">3</span>
+
+
+ "iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
+
+
+
+
+
+
+ -
+
+
+ 二维码
+
+ .icon-erweima
+
+
+
+ -
+
+
+ 二维码
+
+ .icon-erweima1
+
+
+
+ -
+
+
+ 二维码
+
+ .icon-erweima3
+
+
+
+ -
+
+
+ 鼠标划词
+
+ .icon-a-biaodanzujian-shurukuang-svg6
+
+
+
+ -
+
+
+ 表单文本框
+
+ .icon-textBox
+
+
+
+ -
+
+
+ 划词翻译
+
+ .icon-a-biaodanzujian-shurukuang-svg5
+
+
+
+ -
+
+
+ 鼠标
+
+ .icon-shubiao
+
+
+
+ -
+
+
+ 清理
+
+ .icon-qingli
+
+
+
+ -
+
+
+ 刷新
+
+ .icon-refresh-1-copy
+
+
+
+ -
+
+
+ backspace-outline
+
+ .icon-backspace-outline
+
+
+
+ -
+
+
+ space
+
+ .icon-space
+
+
+
+ -
+
+
+ 3
+
+ .icon-icon-test
+
+
+
+ -
+
+
+ 文本换行
+
+ .icon-wenbenhuanhang
+
+
+
+ -
+
+
+ 文字
+
+ .icon-wenzi
+
+
+
+ -
+
+
+ 系统首选项
+
+ .icon-xitongshouxuanxiang
+
+
+
+ -
+
+
+ 对勾
+
+ .icon-duigoux
+
+
+
+ -
+
+
+ 禁用
+
+ .icon-jinyong
+
+
+
+ -
+
+
+ 交换
+
+ .icon-jiaohuan
+
+
+
+ -
+
+
+ icon-基础设置
+
+ .icon-icon-jichushezhi
+
+
+
+ -
+
+
+ 可拖拽
+
+ .icon-ketuozhuai
+
+
+
+ -
+
+
+ 退出
+
+ .icon-tuichu
+
+
+
+ -
+
+
+ 界面
+
+ .icon-jiemian
+
+
+
+ -
+
+
+ 符号-单行输入框
+
+ .icon-danhangshurukuang
+
+
+
+ -
+
+
+ 表单组件-输入框
+
+ .icon-biaodanzujian-shurukuang
+
+
+
+ -
+
+
+ 文本识别
+
+ .icon-wenbenshibie
+
+
+
+ -
+
+
+ 多行输入
+
+ .icon-duohangshuru
+
+
+
+ -
+
+
+ 14C截图
+
+ .icon-a-14Cjietu
+
+
+
+ -
+
+
+ 文字识别
+
+ .icon-wenzishibie
+
+
+
+ -
+
+
+ Maximize-3
+
+ .icon-3zuidahua-3
+
+
+
+ -
+
+
+ 删除
+
+ .icon-shanchu
+
+
+
+ -
+
+
+ 保存
+
+ .icon-baocun
+
+
+
+ -
+
+
+ 历史记录
+
+ .icon-lishijilu
+
+
+
+ -
+
+
+ 常用示例
+
+ .icon-changyongshili
+
+
+
+ -
+
+
+ 服务器
+
+ .icon-fuwuqi
+
+
+
+ -
+
+
+ 关于
+
+ .icon-guanyu
+
+
+
+ -
+
+
+ 收藏
+
+ .icon-shoucang
+
+
+
+ -
+
+
+ 关闭26
+
+ .icon-guanbi
+
+
+
+ -
+
+
+ 最大化
+
+ .icon-zuidahua1
+
+
+
+ -
+
+
+ 最小化
+
+ .icon-zuixiaohua
+
+
+
+ -
+
+
+ 图钉
+
+ .icon-tuding
+
+
+
+ -
+
+
+ 固定,图钉
+
+ .icon-relieve-full
+
+
+
+ -
+
+
+ 复制
+
+ .icon-fuzhi
+
+
+
+ -
+
+
+ 下拉
+
+ .icon-xiala
+
+
+
+ -
+
+
+ 调整,半圆,亮度
+
+ .icon-adjust-full
+
+
+
+ -
+
+
+ snake
+
+ .icon-snake
+
+
+
+ -
+
+
+ copy_large_hump
+
+ .icon-copy_large_hump
+
+
+
+ -
+
+
+ copy_c
+
+ .icon-copy_c
+
+
+
+ -
+
+
+ 喇叭
+
+ .icon-laba1
+
+
+
+
+
+
font-class 引用
+
+
+
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
+
与 Unicode 使用方式相比,具有如下特点:
+
+ - 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
+ - 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
+
+
使用步骤如下:
+
第一步:引入项目下面生成的 fontclass 代码:
+
<link rel="stylesheet" href="./iconfont.css">
+
+
第二步:挑选相应图标并获取类名,应用于页面:
+
<span class="iconfont icon-xxx"></span>
+
+
+ "
+ iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
+
+
+
+
+
+
+ -
+
+
二维码
+ #icon-erweima
+
+
+ -
+
+
二维码
+ #icon-erweima1
+
+
+ -
+
+
二维码
+ #icon-erweima3
+
+
+ -
+
+
鼠标划词
+ #icon-a-biaodanzujian-shurukuang-svg6
+
+
+ -
+
+
表单文本框
+ #icon-textBox
+
+
+ -
+
+
划词翻译
+ #icon-a-biaodanzujian-shurukuang-svg5
+
+
+ -
+
+
鼠标
+ #icon-shubiao
+
+
+ -
+
+
清理
+ #icon-qingli
+
+
+ -
+
+
刷新
+ #icon-refresh-1-copy
+
+
+ -
+
+
backspace-outline
+ #icon-backspace-outline
+
+
+ -
+
+
space
+ #icon-space
+
+
+ -
+
+
3
+ #icon-icon-test
+
+
+ -
+
+
文本换行
+ #icon-wenbenhuanhang
+
+
+ -
+
+
文字
+ #icon-wenzi
+
+
+ -
+
+
系统首选项
+ #icon-xitongshouxuanxiang
+
+
+ -
+
+
对勾
+ #icon-duigoux
+
+
+ -
+
+
禁用
+ #icon-jinyong
+
+
+ -
+
+
交换
+ #icon-jiaohuan
+
+
+ -
+
+
icon-基础设置
+ #icon-icon-jichushezhi
+
+
+ -
+
+
可拖拽
+ #icon-ketuozhuai
+
+
+ -
+
+
退出
+ #icon-tuichu
+
+
+ -
+
+
界面
+ #icon-jiemian
+
+
+ -
+
+
符号-单行输入框
+ #icon-danhangshurukuang
+
+
+ -
+
+
表单组件-输入框
+ #icon-biaodanzujian-shurukuang
+
+
+ -
+
+
文本识别
+ #icon-wenbenshibie
+
+
+ -
+
+
多行输入
+ #icon-duohangshuru
+
+
+ -
+
+
14C截图
+ #icon-a-14Cjietu
+
+
+ -
+
+
文字识别
+ #icon-wenzishibie
+
+
+ -
+
+
Maximize-3
+ #icon-3zuidahua-3
+
+
+ -
+
+
删除
+ #icon-shanchu
+
+
+ -
+
+
保存
+ #icon-baocun
+
+
+ -
+
+
历史记录
+ #icon-lishijilu
+
+
+ -
+
+
常用示例
+ #icon-changyongshili
+
+
+ -
+
+
服务器
+ #icon-fuwuqi
+
+
+ -
+
+
关于
+ #icon-guanyu
+
+
+ -
+
+
收藏
+ #icon-shoucang
+
+
+ -
+
+
关闭26
+ #icon-guanbi
+
+
+ -
+
+
最大化
+ #icon-zuidahua1
+
+
+ -
+
+
最小化
+ #icon-zuixiaohua
+
+
+ -
+
+
图钉
+ #icon-tuding
+
+
+ -
+
+
固定,图钉
+ #icon-relieve-full
+
+
+ -
+
+
复制
+ #icon-fuzhi
+
+
+ -
+
+
下拉
+ #icon-xiala
+
+
+ -
+
+
调整,半圆,亮度
+ #icon-adjust-full
+
+
+ -
+
+
snake
+ #icon-snake
+
+
+ -
+
+
copy_large_hump
+ #icon-copy_large_hump
+
+
+ -
+
+
copy_c
+ #icon-copy_c
+
+
+ -
+
+
喇叭
+ #icon-laba1
+
+
+
+
+
Symbol 引用
+
+
+
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章
+ 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
+
+ - 支持多色图标了,不再受单色限制。
+ - 通过一些技巧,支持像字体那样,通过
font-size
, color
来调整样式。
+ - 兼容性较差,支持 IE9+,及现代浏览器。
+ - 浏览器渲染 SVG 的性能一般,还不如 png。
+
+
使用步骤如下:
+
第一步:引入项目下面生成的 symbol 代码:
+
<script src="./iconfont.js"></script>
+
+
第二步:加入通用 CSS 代码(引入一次就行):
+
<style>
+.icon {
+ width: 1em;
+ height: 1em;
+ vertical-align: -0.15em;
+ fill: currentColor;
+ overflow: hidden;
+}
+</style>
+
+
第三步:挑选相应图标并获取类名,应用于页面:
+
<svg class="icon" aria-hidden="true">
+ <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+
+
+
+
+
+
diff --git a/Attachments/iconfont/iconfont.css b/Attachments/iconfont/iconfont.css
new file mode 100644
index 00000000..673c85e0
--- /dev/null
+++ b/Attachments/iconfont/iconfont.css
@@ -0,0 +1,207 @@
+@font-face {
+ font-family: "iconfont"; /* Project id 4294789 */
+ src: url('iconfont.woff2?t=1704174060960') format('woff2'),
+ url('iconfont.woff?t=1704174060960') format('woff'),
+ url('iconfont.ttf?t=1704174060960') format('truetype');
+}
+
+.iconfont {
+ font-family: "iconfont" !important;
+ font-size: 16px;
+ font-style: normal;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-erweima:before {
+ content: "\e607";
+}
+
+.icon-erweima1:before {
+ content: "\e642";
+}
+
+.icon-erweima3:before {
+ content: "\e619";
+}
+
+.icon-a-biaodanzujian-shurukuang-svg6:before {
+ content: "\e606";
+}
+
+.icon-textBox:before {
+ content: "\e648";
+}
+
+.icon-a-biaodanzujian-shurukuang-svg5:before {
+ content: "\e605";
+}
+
+.icon-shubiao:before {
+ content: "\e650";
+}
+
+.icon-qingli:before {
+ content: "\e6cb";
+}
+
+.icon-refresh-1-copy:before {
+ content: "\e60f";
+}
+
+.icon-backspace-outline:before {
+ content: "\e64e";
+}
+
+.icon-space:before {
+ content: "\e6ab";
+}
+
+.icon-icon-test:before {
+ content: "\e603";
+}
+
+.icon-wenbenhuanhang:before {
+ content: "\e6b2";
+}
+
+.icon-wenzi:before {
+ content: "\e613";
+}
+
+.icon-xitongshouxuanxiang:before {
+ content: "\e656";
+}
+
+.icon-duigoux:before {
+ content: "\ec9e";
+}
+
+.icon-jinyong:before {
+ content: "\e615";
+}
+
+.icon-jiaohuan:before {
+ content: "\e665";
+}
+
+.icon-icon-jichushezhi:before {
+ content: "\e604";
+}
+
+.icon-ketuozhuai:before {
+ content: "\e617";
+}
+
+.icon-tuichu:before {
+ content: "\e66b";
+}
+
+.icon-jiemian:before {
+ content: "\e65b";
+}
+
+.icon-danhangshurukuang:before {
+ content: "\e694";
+}
+
+.icon-biaodanzujian-shurukuang:before {
+ content: "\eb94";
+}
+
+.icon-wenbenshibie:before {
+ content: "\e861";
+}
+
+.icon-duohangshuru:before {
+ content: "\e64a";
+}
+
+.icon-a-14Cjietu:before {
+ content: "\e679";
+}
+
+.icon-wenzishibie:before {
+ content: "\e9ce";
+}
+
+.icon-3zuidahua-3:before {
+ content: "\e693";
+}
+
+.icon-shanchu:before {
+ content: "\e74b";
+}
+
+.icon-baocun:before {
+ content: "\e63b";
+}
+
+.icon-lishijilu:before {
+ content: "\e63f";
+}
+
+.icon-changyongshili:before {
+ content: "\e640";
+}
+
+.icon-fuwuqi:before {
+ content: "\e726";
+}
+
+.icon-guanyu:before {
+ content: "\e60b";
+}
+
+.icon-shoucang:before {
+ content: "\e8b9";
+}
+
+.icon-guanbi:before {
+ content: "\e64d";
+}
+
+.icon-zuidahua1:before {
+ content: "\e651";
+}
+
+.icon-zuixiaohua:before {
+ content: "\e676";
+}
+
+.icon-tuding:before {
+ content: "\e637";
+}
+
+.icon-relieve-full:before {
+ content: "\e9ba";
+}
+
+.icon-fuzhi:before {
+ content: "\e692";
+}
+
+.icon-xiala:before {
+ content: "\e61d";
+}
+
+.icon-adjust-full:before {
+ content: "\e994";
+}
+
+.icon-snake:before {
+ content: "\e600";
+}
+
+.icon-copy_large_hump:before {
+ content: "\e601";
+}
+
+.icon-copy_c:before {
+ content: "\e602";
+}
+
+.icon-laba1:before {
+ content: "\e610";
+}
+
diff --git a/Attachments/iconfont/iconfont.js b/Attachments/iconfont/iconfont.js
new file mode 100644
index 00000000..96290a4e
--- /dev/null
+++ b/Attachments/iconfont/iconfont.js
@@ -0,0 +1 @@
+window._iconfont_svg_string_4294789='',function(l){var c=(c=document.getElementsByTagName("script"))[c.length-1],a=c.getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,v,p=function(c,a){a.parentNode.insertBefore(c,a)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}h=function(){var c,a=document.createElement("div");a.innerHTML=l._iconfont_svg_string_4294789,(a=a.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",a=a,(c=document.body).firstChild?p(a,c.firstChild):c.appendChild(a))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=l.document,v=!1,d(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,s())})}function s(){v||(v=!0,i())}function d(){try{o.documentElement.doScroll("left")}catch(c){return void setTimeout(d,50)}s()}}(window);
\ No newline at end of file
diff --git a/Attachments/iconfont/iconfont.json b/Attachments/iconfont/iconfont.json
new file mode 100644
index 00000000..e568b868
--- /dev/null
+++ b/Attachments/iconfont/iconfont.json
@@ -0,0 +1,345 @@
+{
+ "id": "4294789",
+ "name": "STranslate 2.0",
+ "font_family": "iconfont",
+ "css_prefix_text": "icon-",
+ "description": "基于.Net 6 开发的新款翻译工具",
+ "glyphs": [
+ {
+ "icon_id": "77191",
+ "name": "二维码",
+ "font_class": "erweima",
+ "unicode": "e607",
+ "unicode_decimal": 58887
+ },
+ {
+ "icon_id": "627566",
+ "name": "二维码",
+ "font_class": "erweima1",
+ "unicode": "e642",
+ "unicode_decimal": 58946
+ },
+ {
+ "icon_id": "11937102",
+ "name": "二维码",
+ "font_class": "erweima3",
+ "unicode": "e619",
+ "unicode_decimal": 58905
+ },
+ {
+ "icon_id": "38439764",
+ "name": "鼠标划词",
+ "font_class": "a-biaodanzujian-shurukuang-svg6",
+ "unicode": "e606",
+ "unicode_decimal": 58886
+ },
+ {
+ "icon_id": "16859931",
+ "name": "表单文本框",
+ "font_class": "textBox",
+ "unicode": "e648",
+ "unicode_decimal": 58952
+ },
+ {
+ "icon_id": "38439761",
+ "name": "划词翻译",
+ "font_class": "a-biaodanzujian-shurukuang-svg5",
+ "unicode": "e605",
+ "unicode_decimal": 58885
+ },
+ {
+ "icon_id": "6528456",
+ "name": "鼠标",
+ "font_class": "shubiao",
+ "unicode": "e650",
+ "unicode_decimal": 58960
+ },
+ {
+ "icon_id": "8763657",
+ "name": "清理",
+ "font_class": "qingli",
+ "unicode": "e6cb",
+ "unicode_decimal": 59083
+ },
+ {
+ "icon_id": "1029208",
+ "name": "刷新",
+ "font_class": "refresh-1-copy",
+ "unicode": "e60f",
+ "unicode_decimal": 58895
+ },
+ {
+ "icon_id": "6867447",
+ "name": "backspace-outline",
+ "font_class": "backspace-outline",
+ "unicode": "e64e",
+ "unicode_decimal": 58958
+ },
+ {
+ "icon_id": "19899588",
+ "name": "space",
+ "font_class": "space",
+ "unicode": "e6ab",
+ "unicode_decimal": 59051
+ },
+ {
+ "icon_id": "10754078",
+ "name": "3",
+ "font_class": "icon-test",
+ "unicode": "e603",
+ "unicode_decimal": 58883
+ },
+ {
+ "icon_id": "29570627",
+ "name": "文本换行",
+ "font_class": "wenbenhuanhang",
+ "unicode": "e6b2",
+ "unicode_decimal": 59058
+ },
+ {
+ "icon_id": "18941518",
+ "name": "文字",
+ "font_class": "wenzi",
+ "unicode": "e613",
+ "unicode_decimal": 58899
+ },
+ {
+ "icon_id": "13868185",
+ "name": "系统首选项",
+ "font_class": "xitongshouxuanxiang",
+ "unicode": "e656",
+ "unicode_decimal": 58966
+ },
+ {
+ "icon_id": "6616926",
+ "name": "对勾",
+ "font_class": "duigoux",
+ "unicode": "ec9e",
+ "unicode_decimal": 60574
+ },
+ {
+ "icon_id": "4942647",
+ "name": "禁用",
+ "font_class": "jinyong",
+ "unicode": "e615",
+ "unicode_decimal": 58901
+ },
+ {
+ "icon_id": "1630751",
+ "name": "交换",
+ "font_class": "jiaohuan",
+ "unicode": "e665",
+ "unicode_decimal": 58981
+ },
+ {
+ "icon_id": "4935751",
+ "name": "icon-基础设置",
+ "font_class": "icon-jichushezhi",
+ "unicode": "e604",
+ "unicode_decimal": 58884
+ },
+ {
+ "icon_id": "521494",
+ "name": "可拖拽",
+ "font_class": "ketuozhuai",
+ "unicode": "e617",
+ "unicode_decimal": 58903
+ },
+ {
+ "icon_id": "9454025",
+ "name": "退出",
+ "font_class": "tuichu",
+ "unicode": "e66b",
+ "unicode_decimal": 58987
+ },
+ {
+ "icon_id": "36590127",
+ "name": "界面",
+ "font_class": "jiemian",
+ "unicode": "e65b",
+ "unicode_decimal": 58971
+ },
+ {
+ "icon_id": "1766474",
+ "name": "符号-单行输入框",
+ "font_class": "danhangshurukuang",
+ "unicode": "e694",
+ "unicode_decimal": 59028
+ },
+ {
+ "icon_id": "4354240",
+ "name": "表单组件-输入框",
+ "font_class": "biaodanzujian-shurukuang",
+ "unicode": "eb94",
+ "unicode_decimal": 60308
+ },
+ {
+ "icon_id": "16399025",
+ "name": "文本识别",
+ "font_class": "wenbenshibie",
+ "unicode": "e861",
+ "unicode_decimal": 59489
+ },
+ {
+ "icon_id": "23626694",
+ "name": "多行输入",
+ "font_class": "duohangshuru",
+ "unicode": "e64a",
+ "unicode_decimal": 58954
+ },
+ {
+ "icon_id": "29522620",
+ "name": "14C截图",
+ "font_class": "a-14Cjietu",
+ "unicode": "e679",
+ "unicode_decimal": 59001
+ },
+ {
+ "icon_id": "32538045",
+ "name": "文字识别",
+ "font_class": "wenzishibie",
+ "unicode": "e9ce",
+ "unicode_decimal": 59854
+ },
+ {
+ "icon_id": "1306794",
+ "name": "Maximize-3",
+ "font_class": "3zuidahua-3",
+ "unicode": "e693",
+ "unicode_decimal": 59027
+ },
+ {
+ "icon_id": "577357",
+ "name": "删除",
+ "font_class": "shanchu",
+ "unicode": "e74b",
+ "unicode_decimal": 59211
+ },
+ {
+ "icon_id": "1305399",
+ "name": "保存",
+ "font_class": "baocun",
+ "unicode": "e63b",
+ "unicode_decimal": 58939
+ },
+ {
+ "icon_id": "1305475",
+ "name": "历史记录",
+ "font_class": "lishijilu",
+ "unicode": "e63f",
+ "unicode_decimal": 58943
+ },
+ {
+ "icon_id": "2678581",
+ "name": "常用示例",
+ "font_class": "changyongshili",
+ "unicode": "e640",
+ "unicode_decimal": 58944
+ },
+ {
+ "icon_id": "4933316",
+ "name": "服务器",
+ "font_class": "fuwuqi",
+ "unicode": "e726",
+ "unicode_decimal": 59174
+ },
+ {
+ "icon_id": "8219242",
+ "name": "关于",
+ "font_class": "guanyu",
+ "unicode": "e60b",
+ "unicode_decimal": 58891
+ },
+ {
+ "icon_id": "11372701",
+ "name": "收藏",
+ "font_class": "shoucang",
+ "unicode": "e8b9",
+ "unicode_decimal": 59577
+ },
+ {
+ "icon_id": "2939196",
+ "name": "关闭26",
+ "font_class": "guanbi",
+ "unicode": "e64d",
+ "unicode_decimal": 58957
+ },
+ {
+ "icon_id": "11490920",
+ "name": "最大化",
+ "font_class": "zuidahua1",
+ "unicode": "e651",
+ "unicode_decimal": 58961
+ },
+ {
+ "icon_id": "36077732",
+ "name": "最小化",
+ "font_class": "zuixiaohua",
+ "unicode": "e676",
+ "unicode_decimal": 58998
+ },
+ {
+ "icon_id": "386507",
+ "name": "图钉",
+ "font_class": "tuding",
+ "unicode": "e637",
+ "unicode_decimal": 58935
+ },
+ {
+ "icon_id": "18170426",
+ "name": "固定,图钉",
+ "font_class": "relieve-full",
+ "unicode": "e9ba",
+ "unicode_decimal": 59834
+ },
+ {
+ "icon_id": "16365853",
+ "name": "复制",
+ "font_class": "fuzhi",
+ "unicode": "e692",
+ "unicode_decimal": 59026
+ },
+ {
+ "icon_id": "10031200",
+ "name": "下拉",
+ "font_class": "xiala",
+ "unicode": "e61d",
+ "unicode_decimal": 58909
+ },
+ {
+ "icon_id": "18170245",
+ "name": "调整,半圆,亮度",
+ "font_class": "adjust-full",
+ "unicode": "e994",
+ "unicode_decimal": 59796
+ },
+ {
+ "icon_id": "33564238",
+ "name": "snake",
+ "font_class": "snake",
+ "unicode": "e600",
+ "unicode_decimal": 58880
+ },
+ {
+ "icon_id": "33564325",
+ "name": "copy_large_hump",
+ "font_class": "copy_large_hump",
+ "unicode": "e601",
+ "unicode_decimal": 58881
+ },
+ {
+ "icon_id": "33564377",
+ "name": "copy_c",
+ "font_class": "copy_c",
+ "unicode": "e602",
+ "unicode_decimal": 58882
+ },
+ {
+ "icon_id": "10109104",
+ "name": "喇叭",
+ "font_class": "laba1",
+ "unicode": "e610",
+ "unicode_decimal": 58896
+ }
+ ]
+}
diff --git a/Attachments/iconfont/iconfont.ttf b/Attachments/iconfont/iconfont.ttf
new file mode 100644
index 00000000..d89ca8ee
Binary files /dev/null and b/Attachments/iconfont/iconfont.ttf differ
diff --git a/Attachments/iconfont/iconfont.woff b/Attachments/iconfont/iconfont.woff
new file mode 100644
index 00000000..e90b5517
Binary files /dev/null and b/Attachments/iconfont/iconfont.woff differ
diff --git a/Attachments/iconfont/iconfont.woff2 b/Attachments/iconfont/iconfont.woff2
new file mode 100644
index 00000000..0fd49609
Binary files /dev/null and b/Attachments/iconfont/iconfont.woff2 differ
diff --git a/STranslate/ClearCache.bat b/ClearCache.bat
similarity index 100%
rename from STranslate/ClearCache.bat
rename to ClearCache.bat
diff --git a/README.md b/README.md
index e2b159a9..25825729 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+
@@ -20,48 +20,17 @@
STranslate
WPF 开发的一款即开即用、即用即走的翻译工具
-
中文 | English
-## Preview 2.0
-
-- [x] 支持多服务同时翻译
-- [x] 支持完全离线OCR,实用效果ok
-- [x] 支持历史记录,方便回溯查找
-- [ ] 后期收尾,即将更新
-
-
-
-
-
-![](./preview_ocr.png)
-
-![](./preview_preference.png)
-
-
-## 功能
-
-- [x] 添加 DeepL API
-- [x] 添加划词翻译
-- [x] 添加复制结果蛇形、大小驼峰
-- [x] 软件层面识别语种(中英文)
-- [x] 添加开机自启
-- [x] 添加明/暗主题
-- [x] 添加 UI 设置缓存(用户目录下 `AppData\Local\STranslate`)
-- [x] 添加离线语音合成
-- [x] 添加离线截图文字识别(支持英文, 中文数据包过大且体验不好)
-- [x] 添加检查更新
-- [x] 添加翻译记录缓存功能
-
## 安装
下载最新 [Release](https://github.com/ZGGSONG/STranslate/releases) 版本后解压即可使用
## 使用
-![previews](./example.png)
-
-![previews_dark](./example_dark.png)
+| 亮色 | 暗色 |
+| :-- | :-- |
+| ![](./light.png) | ![](./dark.png) |
打开软件后会静默在后台,等待调用
@@ -69,9 +38,11 @@
- `Alt` + `A` 打开软件界面,输入内容按回车翻译
- `Alt` + `D` 复制当前鼠标选中内容并翻译
- `Alt` + `S` 截图选中区域内容并翻译
-- `Alt` + `G` 打开窗口(不清空内容)
+- `Alt` + `G` 打开主界面
+- `Alt` + `Shift` + `S` 完全离线文字识别(基于PaddleOCR)
+- `Alt` + `Shift` + `D` 打开监听鼠标划词,鼠标滑动选中文字立即翻译
-2. 软件内快捷键
+1. 软件内快捷键
- `ESC` 隐藏界面
- `Ctrl+Shift+Q` 退出程序
- `Ctrl+Shift+R` 切换主题
@@ -79,21 +50,21 @@
点击软件外部任意处即自动隐藏到后台——即用即走。
-
-> STranslate依赖于.NET Framework 4.8 运行环境,如果程序启动时提示“This application requires *** .NETFramework,Version=v4.8”,请点击以下链接下载并安装.NET Framework 4.8 运行环境。
-> [适用于 Windows 的 Microsoft .NET Framework 4.8 脱机安装程序下载](https://download.visualstudio.microsoft.com/download/pr/2d6bb6b2-226a-4baa-bdec-798822606ff1/8494001c276a4b96804cde7829c04d7f/ndp48-x86-x64-allos-enu.exe) | [Microsoft Support](https://support.microsoft.com/zh-cn/topic/%E9%80%82%E7%94%A8%E4%BA%8E-windows-%E7%9A%84-microsoft-net-framework-4-8-%E8%84%B1%E6%9C%BA%E5%AE%89%E8%A3%85%E7%A8%8B%E5%BA%8F-9d23f658-3b97-68ab-d013-aa3c3e7495e0)
-
## 卸载
-1. 删除软件运行目录
-2. 打开 cmd 运行下面的命令即可
-
+1. 打开 cmd 运行下面的命令即可
+> 或者双击运行目录下的`ClearCache.bat`文件
```shell
rd /s /q "%localappdata%\stranslate"
```
+2. 删除软件运行目录
+
+
## 开发历史
+- 2024-01-04 1.0.0.104 全新开发(新更新程序变动较大,`1.*`开始需要全新安装一次)
+
- 2023-03-02 0.25 添加复制提醒动画
- 2023-02-28 0.24 添加 deepl 接口(已经安装的cmd运行 `del %localappdata%\stranslate\stranslate.json` 后打开即可更新接口)
@@ -108,45 +79,20 @@ rd /s /q "%localappdata%\stranslate"
- 2023-01-10 0.15 添加离线 OCR 功能,其使用 [tesseract](https://github.com/tesseract-ocr/tesseract) 目前仅支持英文
-
- 自修改提示
-
-有经验者可自行下载 [语言包](https://github.com/tesseract-ocr/tessdata) 至 `tessdata` 目录后修改 `Util`中`TesseractGetText`方法即可
-
-```C#
-public static string TesseractGetText(Bitmap bmp)
-{
- try
- {
- using (var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default))
- //using (var engine = new TesseractEngine(@"./tessdata", "chi_sim", EngineMode.Default))
- {
- using(var pix = PixConverter.ToPix(bmp))
- {
- using (var page = engine.Process(pix))
- {
- return page.GetText();
- }
- }
- }
- }
- catch (Exception ex)
- {
- throw ex;
- }
-}
-```
-
-
- 2023-12-28 0.10 添加明暗主题切换功能
- 2022-12-27 0.08 版本添加开机启动
-## 如果接口失效
+## 免费接口
当请求人数较多时,远端接口可能暂时失效,可自行运行翻译接口程序
-1. **【推荐】** 下载对应平台可 [执行文件](https://github.com/ZGGSONG/STranslate/releases/tag/0.01),随后在软件右上角选择 `local` 接口即可
-2. 【进阶】 下载 [docker镜像](https://hub.docker.com/r/zggsong/translate),关闭软件 - cmd 运行 `start %localappdata%\stranslate\stranslate.json` - 修改接口地址 - 重启软件即可
+1. 下载对应平台可 [执行文件](https://github.com/ZGGSONG/STranslate/releases/tag/0.01),随后在软件右上角选择 `local` 接口即可
+2. 下载 [docker镜像](https://hub.docker.com/r/zggsong/translate),关闭软件 - cmd 运行 `start %localappdata%\stranslate\stranslate.json` - 修改接口地址 - 重启软件即可
+3. [https://github.com/OwO-Network/DeepLX](https://github.com/OwO-Network/DeepLX)
+
+## 付费接口
+
+1. 支持[百度翻译API](https://fanyi-api.baidu.com/product/11)
## Author 作者
diff --git a/README_EN.md b/README_EN.md
deleted file mode 100644
index 4771dd4f..00000000
--- a/README_EN.md
+++ /dev/null
@@ -1,142 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-STranslate
-
-
-A ready-to-use, ready-to-go translation tool developed by WPF
-
中文 | English
-
-
-
-
-## Function
-
-- [x] Add DeepL API
-- [x] Add Crossword translation
-- [x] Add replication results serpentine, large and small humps
-- [x] Software level language recognition (Chinese and English)
-- [x] Add Boot Self Start
-- [x] Add a light/dark theme
-- [x] Add UI settings cache (`AppData\Local\STranslate` in user directory)
-- [x] Add offline voice synthesis
-- [x] Add offline screenshot text recognition (supports English, Chinese data packets are too large and the experience is not good)
-- [x] Add Check for Updates
-- [x] Add translation record cache
-
-## Install
-
-Download the latest [Release](https://github.com/ZGGSONG/STranslate/releases) version and unzip it to use
-
-## Useage
-
-![previews](./example.png)
-
-![previews_dark](./example_dark.png)
-
-After opening the software it will be silent in the background, waiting for the call
-
-1. Global Listening Shortcuts
-- `Alt` + `A` Open the software interface, enter the content and press enter to translate
-- `Alt` + `D` Copy the current mouse selection and translate it
-- `Alt` + `S` Screenshot the content of the selected area and translate it
-- `Alt` + `G` Open window (without emptying the contents)
-
-2. In-software shortcuts
-- `ESC` Hide interface
-- `Ctrl+Shift+Q` Exit program
-- `Ctrl+Shift+R` Switch Theme
-- `Ctrl+Shift+T` Top/Cancel Top
-
-Click anywhere outside of the software to automatically hide it in the background
-
-
-> NET Framework 4.8 runtime environment, if the application starts with the message "This application requires *** .NETFramework,Version=v4.8", please click on the following link NET Framework 4.8 Runtime Environment.
-> [NET Framework 4.8 for Windows Offline Installer Download](https://download.visualstudio.microsoft.com/download/pr/2d6bb6b2-226a-4baa-bdec-798822606ff1/8494001c276a4b96804cde7829c04d7f/ndp48-x86-x64-allos-enu.exe) | [Microsoft Support](https://support.microsoft.com/zh-cn/topic/%E9%80%82%E7%94%A8%E4%BA%8E-windows-%E7%9A%84-microsoft-net-framework-4-8-%E8%84%B1%E6%9C%BA%E5%AE%89%E8%A3%85%E7%A8%8B%E5%BA%8F-9d23f658-3b97-68ab-d013-aa3c3e7495e0)
-
-## Uninstall
-
-1. Delete the software run directory
-2. Just open cmd and run the following command
-
-```shell
-rd /s /q "%localappdata%\stranslate"
-```
-
-## Development History
-
-- 2023-03-02 0.25 Add copy alert animation
-
-- 2023-02-28 0.24 Add Deepl interface(If you have already installed cmd, run `del %localappdata%\stranslate\stranslate.json ` and open it to update the interface)
-
-- 2023-02-24 0.22 Optimize the problem of blurred tray icon when resolution switching
-
-- 2023-01-17 0.20 Add translation record caching function, repeat translations are obtained from local database, and the upper limit of local records can be adjusted
-
-- 2023-01-12 0.18 Optimized GC background silent running memory footprint remains around 4MB
-
-- 2023-01-12 0.17 Add check update function
-
-- 2023-01-10 0.15 Add offline OCR functionality, which uses [tesseract](https://github.com/tesseract-ocr/tesseract) currently only supports English
-
-
- Self-modification tips
-If you are experienced, you can download the [language package](https://github.com/tesseract-ocr/tessdata) to the tessdata directory and modify the TesseractGetText method in the Util.
-
-```C#
-public static string TesseractGetText(Bitmap bmp)
-{
- try
- {
- using (var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.Default))
- //using (var engine = new TesseractEngine(@"./tessdata", "chi_sim", EngineMode.Default))
- {
- using(var pix = PixConverter.ToPix(bmp))
- {
- using (var page = engine.Process(pix))
- {
- return page.GetText();
- }
- }
- }
- }
- catch (Exception ex)
- {
- throw ex;
- }
-}
-```
-
-
-- 2023-12-28 0.10 Add light and dark theme switching function
-
-- 2022-12-27 0.08 Versions add boot up
-
-## If the interface fails
-
-When the number of requests is large, the remote interface may temporarily fail, so you can run the translation interface program yourself
-1. **【Recommend】** Download the [executable file](https://github.com/ZGGSONG/STranslate/releases/tag/0.01) for the corresponding platform and then select the local interface in the upper right corner of the software.
-2. **【Advanced】** Download the [docker image](https://hub.docker.com/r/zggsong/translate), close the software - cmd run `start %localappdata%\stranslate\stranslate.json` - change the interface address - restart the software
-
-## Author
-
-**STranslate** © [zggsong](https://github.com/zggsong), Released under the [MIT](https://github.com/ZGGSONG/STranslate/blob/main/LICENSE) License.
-Authored and maintained by zggsong with help from other open source projects [WpfTool](https://github.com/NPCDW/WpfTool) and [Tai](https://github.com/Planshit/Tai).
-
-> Website [Blog](https://www.zggsong.com) · GitHub [@zggsong](https://github.com/zggsong)
diff --git a/STranslate.Log/BaseLogger.cs b/STranslate.Log/BaseLogger.cs
new file mode 100644
index 00000000..e54e3225
--- /dev/null
+++ b/STranslate.Log/BaseLogger.cs
@@ -0,0 +1,56 @@
+using System;
+
+namespace STranslate.Log
+{
+ public class BaseLogger : ILogger
+ {
+ public virtual void Debug(string message)
+ {
+ WriteLine("DBG", message);
+ }
+
+ public virtual void Info(string message)
+ {
+ WriteLine("INF", message);
+ }
+
+ public virtual void Warn(string message)
+ {
+ WriteLine("WRN", message);
+ }
+
+ public virtual void Error(string message)
+ {
+ WriteLine("ERR", message);
+ }
+
+ public virtual void Error(string message, Exception ex)
+ {
+ WriteLine("ERR", message, ex);
+ }
+
+ public virtual void Fatal(string message)
+ {
+ WriteLine("FTL", message);
+ }
+
+ public virtual void Fatal(string message, Exception ex)
+ {
+ WriteLine("FTL", message, ex);
+ }
+
+ public virtual void Dispose()
+ {
+ WriteLine("DBG", $"{nameof(BaseLogger)} Dispose");
+ }
+
+ internal static void WriteLine(string type, string message)
+ {
+ System.Diagnostics.Debug.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{type}] {message}");
+ }
+ internal static void WriteLine(string type, string message, Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{type}] {message}, Exception: {ex}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/STranslate.Log/ILogger.cs b/STranslate.Log/ILogger.cs
new file mode 100644
index 00000000..ecd821a5
--- /dev/null
+++ b/STranslate.Log/ILogger.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace STranslate.Log
+{
+ public interface ILogger : IDisposable
+ {
+ void Debug(string message);
+
+ void Info(string message);
+
+ void Warn(string message);
+
+ void Error(string message);
+
+ void Error(string message, Exception ex);
+
+ void Fatal(string message);
+
+ void Fatal(string message, Exception ex);
+ }
+}
\ No newline at end of file
diff --git a/STranslate.Log/LogLevel.cs b/STranslate.Log/LogLevel.cs
new file mode 100644
index 00000000..46c4f05d
--- /dev/null
+++ b/STranslate.Log/LogLevel.cs
@@ -0,0 +1,35 @@
+namespace STranslate.Log
+{
+ public enum LogLevel
+ {
+ ///
+ /// Most verbose level. Used for development and seldom enabled in production
+ ///
+ Trace = 0,
+
+ ///
+ /// Debugging the application behavior from internal events of interest
+ ///
+ Debug,
+
+ ///
+ /// Information that highlights progress or application lifetime events
+ ///
+ Info,
+
+ ///
+ /// Warnings about validation issues or temporary failures that can be recovered.
+ ///
+ Warn,
+
+ ///
+ /// Errors where functionality has failed or have been caught.
+ ///
+ Error,
+
+ ///
+ /// Most critical level. Application is about to abort.
+ ///
+ Fatal
+ }
+}
diff --git a/STranslate.Log/LogService.cs b/STranslate.Log/LogService.cs
new file mode 100644
index 00000000..5bf2288a
--- /dev/null
+++ b/STranslate.Log/LogService.cs
@@ -0,0 +1,36 @@
+namespace STranslate.Log
+{
+ public class LogService
+ {
+#if true
+
+ public static void Register(string name = "", LogLevel minLevel = LogLevel.Debug)
+ {
+ _logger = name.ToLower() switch
+ {
+ "serilog" => new SerilogLogger(minLevel),
+ //"nlog" => new NLogLogger(level),
+ _ => new SerilogLogger(minLevel),
+ };
+ }
+
+ public static void UnRegister()
+ {
+ _logger?.Dispose();
+ }
+
+ private static ILogger? _logger;
+ public static ILogger Logger { get => _logger!; set => _logger = value; }
+
+#else
+
+ private static readonly Lazy _logger = new(() => new SerilogLogger());
+ public static ILogger Logger => _logger.Value;
+
+ public static void UnRegister()
+ {
+ _logger.Value.Dispose();
+ }
+#endif
+ }
+}
diff --git a/STranslate.Log/NLogLogger.cs b/STranslate.Log/NLogLogger.cs
new file mode 100644
index 00000000..62e50bd7
--- /dev/null
+++ b/STranslate.Log/NLogLogger.cs
@@ -0,0 +1,89 @@
+//using NLog;
+//using System;
+
+//namespace STranslate.Log
+//{
+// public class NLogLogger : BaseLogger
+// {
+// private readonly Logger _logger;
+
+// public NLogLogger(LogLevel minLevel = LogLevel.Debug)
+// {
+// var logFileName = $"logs/log{DateTime.Now:yyyyMMdd}.log";
+// var convLevel = ToNLogLevel(minLevel);
+
+// var conf = new NLog.Config.LoggingConfiguration();
+// conf.AddRule(
+// convLevel,
+// NLog.LogLevel.Fatal,
+// new NLog.Targets.FileTarget("logfile") { FileName = logFileName }
+// );
+// LogManager.Configuration = conf;
+// _logger = LogManager.GetCurrentClassLogger();
+// }
+
+// ///
+// /// 辅助方法将自定义的LogLevel转换为NLog的NLogLevel
+// ///
+// ///
+// ///
+// private NLog.LogLevel ToNLogLevel(LogLevel level) => level switch
+// {
+// LogLevel.Trace => NLog.LogLevel.Trace,
+// LogLevel.Debug => NLog.LogLevel.Debug,
+// LogLevel.Info => NLog.LogLevel.Info,
+// LogLevel.Warn => NLog.LogLevel.Warn,
+// LogLevel.Error => NLog.LogLevel.Error,
+// LogLevel.Fatal => NLog.LogLevel.Fatal,
+// _ => NLog.LogLevel.Debug,
+// };
+
+// public override void Debug(string message)
+// {
+// base.Debug(message);
+// _logger.Debug(message);
+// }
+
+// public override void Info(string message)
+// {
+// base.Info(message);
+// _logger.Info(message);
+// }
+
+// public override void Warn(string message)
+// {
+// base.Warn(message);
+// _logger.Warn(message);
+// }
+
+// public override void Error(string message)
+// {
+// base.Error(message);
+// _logger.Error(message);
+// }
+
+// public override void Error(string message, Exception ex)
+// {
+// base.Error(message);
+// _logger.Error(ex, message);
+// }
+
+// public override void Fatal(string message)
+// {
+// base.Fatal(message);
+// _logger.Fatal(message);
+// }
+
+// public override void Fatal(string message, Exception ex)
+// {
+// base.Fatal(message);
+// _logger.Fatal(ex, message);
+// }
+
+// public override void Dispose()
+// {
+// base.Dispose();
+// LogManager.Shutdown();
+// }
+// }
+//}
diff --git a/STranslate.Log/STranslate.Log.csproj b/STranslate.Log/STranslate.Log.csproj
new file mode 100644
index 00000000..0d56b5aa
--- /dev/null
+++ b/STranslate.Log/STranslate.Log.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net8.0-windows
+ enable
+ true
+
+
+
+ none
+
+
+
+
+
+
+
+
diff --git a/STranslate.Log/SerilogLogger.cs b/STranslate.Log/SerilogLogger.cs
new file mode 100644
index 00000000..daf2e5c0
--- /dev/null
+++ b/STranslate.Log/SerilogLogger.cs
@@ -0,0 +1,91 @@
+using Serilog;
+using Serilog.Core;
+using Serilog.Events;
+using System;
+
+namespace STranslate.Log
+{
+ public class SerilogLogger : BaseLogger
+ {
+ private readonly Logger _logger;
+
+ public SerilogLogger(LogLevel minLevel = LogLevel.Debug)
+ {
+ var logConfiguration = new LoggerConfiguration()
+ .WriteTo.File(
+ "logs/log.log",
+ rollingInterval: RollingInterval.Day,
+ restrictedToMinimumLevel: LogEventLevel.Verbose,
+ outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
+ )
+ .MinimumLevel.Is(ConvertToSerilogLevel(minLevel));
+
+ _logger = logConfiguration.CreateLogger();
+ }
+
+ ///
+ /// 将自定义的LogLevel转换为Serilog的LogEventLevel
+ ///
+ ///
+ ///
+ private LogEventLevel ConvertToSerilogLevel(LogLevel level) =>
+ level switch
+ {
+ LogLevel.Trace => LogEventLevel.Verbose,
+ LogLevel.Debug => LogEventLevel.Debug,
+ LogLevel.Info => LogEventLevel.Information,
+ LogLevel.Warn => LogEventLevel.Warning,
+ LogLevel.Error => LogEventLevel.Error,
+ LogLevel.Fatal => LogEventLevel.Fatal,
+ _ => LogEventLevel.Debug,
+ };
+
+ public override void Debug(string message)
+ {
+ base.Debug(message);
+ _logger.Debug(message);
+ }
+
+ public override void Info(string message)
+ {
+ base.Info(message);
+ _logger.Information(message);
+ }
+
+ public override void Warn(string message)
+ {
+ base.Warn(message);
+ _logger.Warning(message);
+ }
+
+ public override void Error(string message)
+ {
+ base.Error(message);
+ _logger.Error(message);
+ }
+
+ public override void Error(string message, Exception ex)
+ {
+ base.Error(message);
+ _logger.Error(ex, message);
+ }
+
+ public override void Fatal(string message)
+ {
+ base.Fatal(message);
+ _logger.Fatal(message);
+ }
+
+ public override void Fatal(string message, Exception ex)
+ {
+ base.Fatal(message);
+ _logger.Fatal(ex, message);
+ }
+
+ public override void Dispose()
+ {
+ base.Dispose();
+ _logger.Dispose();
+ }
+ }
+}
diff --git a/STranslate.Model/BaiduModel.cs b/STranslate.Model/BaiduModel.cs
deleted file mode 100644
index 82908cb8..00000000
--- a/STranslate.Model/BaiduModel.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Newtonsoft.Json;
-
-namespace STranslate.Model
-{
- public class BaiduModel
- {
- public string Text { get; set; }
- public string From { get; set; }
- public string TO { get; set; }
- public string AppId { get; set; }
- public string Salt { get; set; }
- public string Sign { get; set; }
- }
-
- public class BaiduResp
- {
- [JsonProperty("from")]
- public string From { get; set; }
-
- [JsonProperty("to")]
- public string To { get; set; }
-
- [JsonProperty("trans_result")]
- public TransResult[] TransResult { get; set; }
- }
-
- public class TransResult
- {
- [JsonProperty("src")]
- public string Src { get; set; }
-
- [JsonProperty("dst")]
- public string Dst { get; set; }
- }
-}
\ No newline at end of file
diff --git a/STranslate.Model/ConfigModel.cs b/STranslate.Model/ConfigModel.cs
index f363bc61..a2379ade 100644
--- a/STranslate.Model/ConfigModel.cs
+++ b/STranslate.Model/ConfigModel.cs
@@ -1,183 +1,86 @@
-using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Newtonsoft.Json;
+using System.ComponentModel;
+using System.Windows;
namespace STranslate.Model
{
-
- public class Hotkeys
+ public partial class ConfigModel : ObservableObject
{
- [JsonProperty("inputTranslate")]
- public InputTranslate InputTranslate { get; set; }
-
- [JsonProperty("crosswordTranslate")]
- public CrosswordTranslate CrosswordTranslate { get; set; }
-
- [JsonProperty("screenShotTranslate")]
- public ScreenShotTranslate ScreenShotTranslate { get; set; }
+ ///
+ /// 开机自启动
+ ///
+ public bool IsStartup { get; set; }
- [JsonProperty("openMainWindow")]
- public OpenMainWindow OpenMainWindow { get; set; }
- }
- public class InputTranslate
- {
- public byte Modifiers { get; set; }
- public int Key { get; set; }
- public String Text { get; set; }
- public bool Conflict { get; set; }
- }
- public class CrosswordTranslate
- {
- public byte Modifiers { get; set; }
- public int Key { get; set; }
- public String Text { get; set; }
- public bool Conflict { get; set; }
- }
- public class ScreenShotTranslate
- {
- public byte Modifiers { get; set; }
- public int Key { get; set; }
- public String Text { get; set; }
- public bool Conflict { get; set; }
- }
- public class OpenMainWindow
- {
- public byte Modifiers { get; set; }
- public int Key { get; set; }
- public String Text { get; set; }
- public bool Conflict { get; set; }
- }
- public class Server
- {
- [JsonProperty("name")]
- public string Name { get; set; }
+ ///
+ /// 是否管理员启动
+ ///
+ public bool NeedAdministrator { get; set; }
- [JsonProperty("api")]
- public string Api { get; set; }
- }
- public class ConfigModel
- {
///
- /// 最大历史记录数量
+ /// 历史记录大小
///
- [JsonProperty("maxHistoryCount")]
- public int MaxHistoryCount { get; set; }
+ public long HistorySize { get; set; }
+
///
/// 自动识别语种标度
///
- [JsonProperty("autoScale")]
public double AutoScale { get; set; }
- ///
- /// 取词间隔
- ///
- [JsonProperty("wordPickupInterval")]
- public double WordPickupInterval { get; set; }
+
///
/// 是否亮色模式
///
- [JsonProperty("isBright")]
public bool IsBright { get; set; }
- [JsonProperty("sourceLanguage")]
- public string SourceLanguage { get; set; }
+ ///
+ /// 是否跟随鼠标
+ ///
+ public bool IsFollowMouse { get; set; }
+
+ ///
+ /// OCR结果翻译关闭OCR界面
+ ///
+ public bool CloseUIOcrRetTranslate { get; set; }
+
+ ///
+ /// 截图出现问题尝试一下
+ ///
+ public bool UnconventionalScreen { get; set; }
+
+ ///
+ /// OCR时是否自动复制文本
+ ///
+ public bool IsOcrAutoCopyText { get; set; }
+
+ ///
+ /// 是否调整完语句后翻译
+ ///
+ public bool IsAdjustContentTranslate { get; set; }
+
+ ///
+ /// 取词时移除换行
+ ///
+ public bool IsRemoveLineBreakGettingWords { get; set; }
+
+ ///
+ /// 鼠标双击托盘程序功能
+ ///
+ public DoubleTapFuncEnum DoubleTapTrayFunc { get; set; } = DoubleTapFuncEnum.InputFunc;
- [JsonProperty("targetLanguage")]
- public string TargetLanguage { get; set; }
+ public string SourceLanguage { get; set; } = string.Empty;
- [JsonProperty("selectServer")]
- public int SelectServer { get; set; }
+ public string TargetLanguage { get; set; } = string.Empty;
///
/// 服务
///
- [JsonProperty("servers")]
- public Server[] Servers { get; set; }
+ [JsonIgnore]
+ [ObservableProperty]
+ public BindingList? _services;
///
/// 热键
///
- [JsonProperty("hotkeys")]
- public Hotkeys Hotkeys { get; set; }
-
-
- public ConfigModel()
- {
- }
-
- public ConfigModel InitialConfig()
- {
- return new ConfigModel
- {
- MaxHistoryCount = 100,
- AutoScale = 0.8,
- WordPickupInterval = 200,
- IsBright = true,
- SourceLanguage = LanguageEnum.AUTO.GetDescription(),
- TargetLanguage = LanguageEnum.AUTO.GetDescription(),
- SelectServer = 0,
- Servers = new Server[]
- {
- new Server
- {
- Name = "zggsong",
- Api = "https://dfree.deno.dev/translate"
- },
- new Server
- {
- Name = "iciba",
- Api = "https://iciba.deno.dev/translate"
- },
- new Server
- {
- Name = "google",
- Api = "https://ggtranslate.deno.dev/translate"
- },
- new Server
- {
- Name = "zu1k",
- Api = "https://deepl.deno.dev/translate"
- },
- new Server
- {
- Name = "local",
- Api = "http://127.0.0.1:8000/translate"
- }
- },
- Hotkeys = new Hotkeys
- {
- InputTranslate = new InputTranslate
- {
- Modifiers = 1,
- Key = 65,
- Text = "Alt + A",
- Conflict = false,
- },
- CrosswordTranslate = new CrosswordTranslate
- {
- Modifiers = 1,
- Key = 68,
- Text = "Alt + D",
- Conflict = false,
- },
- ScreenShotTranslate = new ScreenShotTranslate
- {
- Modifiers = 1,
- Key = 83,
- Text = "Alt + S",
- Conflict = false,
- },
- OpenMainWindow = new OpenMainWindow
- {
- Modifiers = 1,
- Key = 71,
- Text = "Alt + G",
- Conflict = false,
- },
- }
- };
- }
+ public Hotkeys? Hotkeys { get; set; }
}
}
diff --git a/STranslate.Model/ConstStr.cs b/STranslate.Model/ConstStr.cs
new file mode 100644
index 00000000..fbbc6b4e
--- /dev/null
+++ b/STranslate.Model/ConstStr.cs
@@ -0,0 +1,31 @@
+
+namespace STranslate.Model
+{
+ public static class ConstStr
+ {
+ public const string THEMELIGHT = "pack://application:,,,/STranslate.Style;component/Styles/Themes/ColorLight.xaml";
+ public const string THEMEDARK = "pack://application:,,,/STranslate.Style;component/Styles/Themes/ColorDark.xaml";
+
+ public static System.Uri LIGHTURI = new(THEMELIGHT);
+ public static System.Uri DARKURI = new(THEMEDARK);
+
+ public const string ICON = "pack://application:,,,/STranslate.Style;component/Resources/favicon.ico";
+ public const string ICONFORBIDDEN = "pack://application:,,,/STranslate.Style;component/Resources/forbidden.ico";
+
+ public const string TAGTRUE = "True";
+ public const string TAGFALSE = "False";
+
+ public const string TOPMOSTCONTENT = "\xe637";
+ public const string UNTOPMOSTCONTENT = "\xe9ba";
+
+ public const string MAXIMIZECONTENT = "\xe651";
+ public const string MAXIMIZEBACKCONTENT = "\xe693";
+
+ public const string DEFAULTINPUTHOTKEY = "Alt + A";
+ public const string DEFAULTCROSSWORDHOTKEY = "Alt + D";
+ public const string DEFAULTSCREENSHOTHOTKEY = "Alt + S";
+ public const string DEFAULTOPENHOTKEY = "Alt + G";
+ public const string DEFAULTMOUSEHOOKHOTKEY = "Alt + Shift + D";
+ public const string DEFAULTOCRHOTKEY = "Alt + Shift + S";
+ }
+}
\ No newline at end of file
diff --git a/STranslate.Model/DeeplModel.cs b/STranslate.Model/DeeplModel.cs
deleted file mode 100644
index d28b6776..00000000
--- a/STranslate.Model/DeeplModel.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Newtonsoft.Json;
-
-namespace STranslate.Model
-{
- public class DeeplReq
- {
- [JsonProperty("text")]
- public string Text { get; set; }
-
- [JsonProperty("source_lang")]
- public string SourceLang { get; set; }
-
- [JsonProperty("target_lang")]
- public string TargetLang { get; set; }
- }
-
- public class DeeplResp
- {
- [JsonProperty("code")]
- public int Code { get; set; }
-
- [JsonProperty("data")]
- public string Data { get; set; }
- }
-}
\ No newline at end of file
diff --git a/STranslate.Model/Enums.cs b/STranslate.Model/Enums.cs
new file mode 100644
index 00000000..e522abc1
--- /dev/null
+++ b/STranslate.Model/Enums.cs
@@ -0,0 +1,222 @@
+using System;
+using System.ComponentModel;
+
+namespace STranslate.Model
+{
+ public enum LanguageEnum
+ {
+ [Description("自动选择")]
+ AUTO, //自动
+
+ [Description("中文")]
+ ZH, //中文
+
+ [Description("英语")]
+ EN, //英语
+
+ [Description("德语")]
+ DE, //德语
+
+ [Description("西班牙语")]
+ ES, //西班牙语
+
+ [Description("法语")]
+ FR, //法语
+
+ [Description("意大利语")]
+ IT, //意大利语
+
+ [Description("日语")]
+ JA, //日语
+
+ [Description("荷兰语")]
+ NL, //荷兰语
+
+ [Description("波兰语")]
+ PL, //波兰语
+
+ [Description("葡萄牙语")]
+ PT, //葡萄牙语
+
+ [Description("俄语")]
+ RU, //俄语
+
+ [Description("保加利亚语")]
+ BG, //保加利亚语
+
+ [Description("捷克语")]
+ CS, //捷克语
+
+ [Description("丹麦语")]
+ DA, //丹麦语
+
+ [Description("希腊语")]
+ EL, //希腊语
+
+ [Description("爱沙尼亚语")]
+ ET, //爱沙尼亚语
+
+ [Description("芬兰语")]
+ FI, //芬兰语
+
+ [Description("匈牙利语")]
+ HU, //匈牙利语
+
+ [Description("立陶宛语")]
+ LT, //立陶宛语
+
+ [Description("拉脱维亚语")]
+ LV, //拉脱维亚语
+
+ [Description("罗马尼亚语")]
+ RO, //罗马尼亚语
+
+ [Description("斯洛伐克语")]
+ SK, //斯洛伐克语
+
+ [Description("斯洛文尼亚语")]
+ SL, //斯洛文尼亚语
+
+ [Description("瑞典语")]
+ SV, //瑞典语
+
+ [Description("土耳其语")]
+ TR, //土耳其语
+ }
+
+ ///
+ /// ServiceView 重置选中项类型
+ ///
+ public enum ActionType
+ {
+ Initialize,
+ Delete,
+ Add
+ }
+
+ ///
+ /// 服务类型
+ ///
+ public enum ServiceType
+ {
+ ApiService = 0,
+ CloudService
+ }
+
+ ///
+ /// 请求方式
+ ///
+ public enum RequestMode
+ {
+ GET = 0,
+ POST
+ }
+
+ ///
+ /// 图标类型
+ ///
+ public enum IconType
+ {
+ STranslate,
+ DeepL,
+ Baidu,
+ Google,
+ Iciba,
+ Youdao,
+ }
+
+ ///
+ /// 快捷键修饰键
+ ///
+ public enum KeyModifiers : byte
+ {
+ MOD_NONE = 0x0,
+ MOD_ALT = 0x1,
+ MOD_CTRL = 0x2,
+ MOD_SHIFT = 0x4,
+ MOD_WIN = 0x8
+ }
+
+ public enum KeyCodes
+ {
+ None = 0,
+ A = 65,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ I,
+ J,
+ K,
+ L,
+ M,
+ N,
+ O,
+ P,
+ Q,
+ R,
+ S,
+ T,
+ U,
+ V,
+ W,
+ X,
+ Y,
+ Z
+ }
+
+ public enum OCRType
+ {
+ Chinese,
+ English
+ }
+
+ ///
+ /// 窗体类型-用于通知窗口
+ ///
+ public enum WindowType
+ {
+ Main,
+ Preference,
+ OCR
+ }
+
+ ///
+ /// 托盘功能枚举
+ ///
+ public enum DoubleTapFuncEnum
+ {
+ [Description("输入翻译")]
+ InputFunc,
+ [Description("截图翻译")]
+ ScreenFunc,
+ [Description("鼠标划词")]
+ MouseHookFunc,
+ [Description("文字识别")]
+ OCRFunc,
+ [Description("显示界面")]
+ ShowViewFunc,
+ [Description("偏好设置")]
+ PreferenceFunc,
+ [Description("禁用热键")]
+ ForbidShortcutFunc,
+ [Description("退出程序")]
+ ExitFunc
+ }
+
+ ///
+ /// 获取Description
+ ///
+ public static class EnumExtensions
+ {
+ public static string GetDescription(this Enum val)
+ {
+ var field = val.GetType().GetField(val.ToString());
+ var customAttribute = Attribute.GetCustomAttribute(field!, typeof(DescriptionAttribute));
+ return customAttribute == null ? val.ToString() : ((DescriptionAttribute)customAttribute).Description;
+ }
+ }
+}
\ No newline at end of file
diff --git a/STranslate.Model/HistoryModel.cs b/STranslate.Model/HistoryModel.cs
new file mode 100644
index 00000000..97d93ac4
--- /dev/null
+++ b/STranslate.Model/HistoryModel.cs
@@ -0,0 +1,48 @@
+using Dapper.Contrib.Extensions;
+using System;
+using System.Windows.Documents;
+
+namespace STranslate.Model
+{
+ [Table("History")]
+ public class HistoryModel
+ {
+ [Key]
+ public int Id { get; set; }
+
+ ///
+ /// 记录时间
+ ///
+ public DateTime Time { get; set; }
+
+ ///
+ /// 源语言
+ ///
+ public string SourceLang { get; set; } = "";
+
+ ///
+ /// 目标语言
+ ///
+ public string TargetLang { get; set; } = "";
+
+ ///
+ /// 需翻译内容
+ ///
+ public string SourceText { get; set; } = "";
+
+ ///
+ /// 收藏
+ ///
+ public bool Favorite { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string Remark { get; set; } = "";
+
+ ///
+ /// 服务
+ ///
+ public string Data { get; set; } = "";
+ }
+}
diff --git a/STranslate.Model/Hotkeys.cs b/STranslate.Model/Hotkeys.cs
new file mode 100644
index 00000000..b5093d62
--- /dev/null
+++ b/STranslate.Model/Hotkeys.cs
@@ -0,0 +1,53 @@
+namespace STranslate.Model
+{
+ public class Hotkeys
+ {
+ public InputTranslate InputTranslate { get; set; } = new InputTranslate();
+
+ public CrosswordTranslate CrosswordTranslate { get; set; } = new CrosswordTranslate();
+
+ public ScreenShotTranslate ScreenShotTranslate { get; set; } = new ScreenShotTranslate();
+
+ public OpenMainWindow OpenMainWindow { get; set; } = new OpenMainWindow();
+
+ public MousehookTranslate MousehookTranslate { get; set; } = new MousehookTranslate();
+
+ public OCR OCR { get; set; } = new OCR();
+ }
+
+ public class InputTranslate : HotkeyBase { }
+
+ public class CrosswordTranslate : HotkeyBase { }
+
+ public class ScreenShotTranslate : HotkeyBase { }
+
+ public class OpenMainWindow : HotkeyBase { }
+
+ public class MousehookTranslate : HotkeyBase { }
+
+ public class OCR : HotkeyBase { }
+
+ public class HotkeyBase
+ {
+ public KeyModifiers Modifiers { get; set; }
+
+ public KeyCodes Key { get; set; }
+
+ public string? Text { get; set; }
+
+ public bool Conflict { get; set; }
+ }
+
+ public static class HotkeyExtensions
+ {
+ public static T Update(this T t, KeyModifiers modifiers, KeyCodes key, string? text, bool conflict = false)
+ where T : HotkeyBase
+ {
+ t.Modifiers = modifiers;
+ t.Key = key;
+ t.Text = text;
+ t.Conflict = conflict;
+ return t;
+ }
+ }
+}
diff --git a/STranslate.Model/ITranslator.cs b/STranslate.Model/ITranslator.cs
new file mode 100644
index 00000000..7a11ee6c
--- /dev/null
+++ b/STranslate.Model/ITranslator.cs
@@ -0,0 +1,31 @@
+using System.Threading.Tasks;
+using System.Threading;
+using System;
+
+namespace STranslate.Model
+{
+ public interface ITranslator
+ {
+ Guid Identify { get; set; }
+
+ ServiceType Type { get; set; }
+
+ public bool IsEnabled { get; set; }
+
+ IconType Icon { get; set; }
+
+ string Name { get; set; }
+
+ string Url { get; set; }
+
+ object Data { get; set; }
+
+ RequestMode HttpMode { get; set; }
+
+ string AppID { get; set; }
+
+ string AppKey { get; set; }
+
+ Task