├── .eslintrc.js
├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── LICENSE
├── README.md
├── client
├── .eslintignore
├── README.md
├── package-lock.json
├── package.json
├── src
│ ├── config
│ │ └── ConfigManager.ts
│ ├── extension.ts
│ ├── lib
│ │ ├── constant.ts
│ │ ├── intl
│ │ │ ├── IntlConfigManager.ts
│ │ │ ├── IntlConfigVisitor.ts
│ │ │ ├── template
│ │ │ │ ├── generator.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── template_config.json
│ │ │ └── utils.ts
│ │ ├── translate
│ │ │ ├── TranslateManager.ts
│ │ │ ├── baidu.ts
│ │ │ └── util.ts
│ │ ├── ui
│ │ │ └── VSCodeUIManager.ts
│ │ └── util
│ │ │ ├── index.ts
│ │ │ └── validator.ts
│ └── middleware
│ │ ├── disable-file.ts
│ │ ├── disable-line.ts
│ │ └── extract.ts
├── tsconfig.json
└── yarn.lock
├── package-lock.json
├── package.json
├── react-intl-linter-2.0.1.vsix
├── react-intl-linter.icon.png
├── server
├── .eslintignore
├── index.d.ts
├── package-lock.json
├── package.json
├── src
│ ├── lib
│ │ ├── command
│ │ │ └── index.ts
│ │ ├── comment
│ │ │ ├── CommentManager.ts
│ │ │ ├── rules.ts
│ │ │ └── utils.ts
│ │ ├── util.ts
│ │ └── validator.ts
│ └── server.ts
├── tsconfig.json
└── yarn.lock
├── test_files
├── .vscode
│ └── settings.json
├── TypescriptReact.tsx
├── plaintext.txt
├── src
│ ├── i18n
│ │ ├── en_US.json
│ │ ├── index.ts
│ │ └── zh_CN.json
│ └── intl
│ │ ├── en_US.json
│ │ ├── index.ts
│ │ └── zh_CN.json
└── typescript.ts
├── tsconfig.base.json
├── tsconfig.json
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /**@type {import('eslint').Linter.Config} */
2 | // eslint-disable-next-line no-undef
3 | module.exports = {
4 | root: true,
5 | parser: "@typescript-eslint/parser",
6 | plugins: ["@typescript-eslint"],
7 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
8 | rules: {
9 | "@typescript-eslint/no-unused-vars": 0,
10 | "@typescript-eslint/no-explicit-any": 0,
11 | "@typescript-eslint/explicit-module-boundary-types": 0,
12 | "@typescript-eslint/no-non-null-assertion": 0,
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | client/out
3 | client/node_modules
4 | server/out
5 | server/node_modules
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | {
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "type": "extensionHost",
7 | "request": "launch",
8 | "name": "Launch Client",
9 | "runtimeExecutable": "${execPath}",
10 | "args": ["--extensionDevelopmentPath=${workspaceFolder}" ],
11 | "stopOnEntry": false,
12 | "sourceMaps": true,
13 | "outFiles": ["${workspaceFolder}/client/out/**/*.js"],
14 | "preLaunchTask": "npm: watch"
15 | },
16 | {
17 | "type": "node",
18 | "request": "attach",
19 | "name": "Attach to Server",
20 | "address": "localhost",
21 | "protocol": "inspector",
22 | "port": 6011,
23 | "sourceMaps": true,
24 | "outFiles": ["${workspaceFolder}/server/out/**/*.js"]
25 | }
26 | ],
27 | "compounds": [
28 | {
29 | "name": "Client + Server",
30 | "configurations": ["Launch Client", "Attach to Server"]
31 | }
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "out": false
5 | },
6 | "search.exclude": {
7 | "out": true,
8 | "server": true
9 | },
10 | "files.trimTrailingWhitespace": true,
11 | "editor.insertSpaces": false,
12 | "editor.tabSize": 4,
13 | "typescript.tsdk": "./node_modules/typescript/lib",
14 | "typescript.tsc.autoDetect": "off",
15 | "editor.codeActionsOnSave": {
16 | "source.fixAll.eslint": true
17 | },
18 | "eslint.workingDirectories": [
19 | "./client",
20 | "./server"
21 | ],
22 | "eslint.trace.server": "off",
23 | "eslint.lintTask.enable": false,
24 | "eslint.format.enable": false,
25 | "editor.detectIndentation": false,
26 | "javascript.format.insertSpaceBeforeAndAfterBinaryOperators": true,
27 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
28 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": true,
29 | "css.lint.important": "warning",
30 | "typescript.format.placeOpenBraceOnNewLineForControlBlocks": true,
31 | "typescript.format.placeOpenBraceOnNewLineForFunctions": true,
32 | "javascript.format.placeOpenBraceOnNewLineForControlBlocks": true,
33 | "javascript.format.placeOpenBraceOnNewLineForFunctions": true,
34 | "eslint.validate": [
35 | "javascript",
36 | "javascriptreact",
37 | "typescript",
38 | "typescriptreact"
39 | ],
40 | "editor.formatOnSave": true,
41 | "editor.defaultFormatter": "vscode.typescript-language-features", // https://github.com/prettier/prettier-vscode#default-formatter
42 | "[typescript]": {
43 | "editor.defaultFormatter": "vscode.typescript-language-features"
44 | },
45 | "[typescriptreact]": {
46 | "editor.defaultFormatter": "vscode.typescript-language-features"
47 | },
48 | "editor.wordWrap": "on",
49 | "editor.renderWhitespace": "all",
50 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "type": "npm",
8 | "script": "watch",
9 | "isBackground": true,
10 | "group": {
11 | "kind": "build",
12 | "isDefault": true
13 | },
14 | "presentation": {
15 | "reveal": "never",
16 | "panel": "dedicated"
17 | },
18 | "problemMatcher": [
19 | "$tsc-watch"
20 | ]
21 | },
22 | {
23 | "type": "npm",
24 | "script": "compile",
25 | "isBackground": false,
26 | "group": "build",
27 | "presentation": {
28 | "reveal": "never",
29 | "panel": "dedicated"
30 | },
31 | "problemMatcher": [
32 | "$tsc"
33 | ]
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
react intl linter
8 |
9 | 


10 |
11 | 自动替换中文字符串为 react-intl 代码的 VS Code 插件,Market 👉 [Link](https://marketplace.visualstudio.com/items?itemName=styx11.react-intl-linter)
12 |
13 |
14 |
15 | ## 目录
16 |
17 | * [介绍](#介绍)
18 | * [背景](#背景)
19 | * [工作流程](#工作流程)
20 | * [文本已存在现有配置](#文本已存在现有配置)
21 | * [文本不存在已有配置](#文本不存在已有配置)
22 | * [使用](#使用)
23 | * [基本使用](#基本使用)
24 | * [Simple Argument](#Simple-Argument)
25 | * [Disabling Rules](#Disabling-Rules)
26 | * [注意](#注意)
27 | * [多语言支持](#多语言支持)
28 | * [语言代码](#语言代码)
29 | * [例子](#例子)
30 | * [配置](#配置)
31 | * [Sponsor](#Sponsor)
32 | * [Structure](#Structure)
33 | * [Debug](#Debug)
34 | * [License](#License)
35 |
36 | ## 介绍
37 |
38 | 该插件可以自动检测代码中的**中文字符串**,通过 CodeAction 翻译并生成对应的 intl 代码,另外插件会自动更新国际化配置文件。
39 |
40 | ### 背景
41 | 我在开发公司项目的时候会有国际化的需求,但是这个过程**异常繁琐且痛苦**,尤其是当一个 UI 里面含有大量文本时🤮。因为我们需要收集所有中文文本将他们翻译为英文,然后还要有一个配置文件对应的 intl id(也就是 `intl.formatMessage(id: ...)` 的 `id` 属性),最后再将它们一个一个填入配置文件中(为了规范化我们甚至还要对配置文件按照名称去排序😓)。所以我认为有必要开发一个自动化插件帮助我们解决以上所有问题!
42 |
43 | ### 工作流程
44 |
45 | 本插件作为 VSCode 语言类插件,工作流程如下:
46 | 1. 检测中文、特殊字符串并提示
47 | 2. 查看现有国际化配置是否已包含目标文本
48 | 3. 翻译文本至目标语言,用户可以选择或自定义 intl id 内容
49 | 4. 插件自动更新对应的国际化配置文件(配置会按照 `id` 名称进行排序)
50 | 5. 替换字符串为`intl.formatMessage({ id: ... })`代码
51 |
52 | 你可以直接将本插件应用到现有的前端项目中,也可以从一开始就将它加入到项目的 Bootstrap 里,插件会优先检查本地文本是否已经存在于已有的国际化配置文件中,如果没有,插件会自动为你生成所需的配置。它甚至可以根据本地语言和国际语言生成 react-intl 所需的 `index` 文件!
53 |
54 | ### 文本已存在现有配置
55 | 插件会优先检查工作区中是否存在该文本对应的国际化配置,若有会直接替换为对应 react-intl 代码
56 | 
57 |
58 | ### 文本不存在已有配置
59 | 当一个中文文本既不存在已有国际化配置时,该插件会翻译文本生成对应的的 react-intl 代码,并自动修改国际化配置
60 | 
61 |
62 | ## 使用
63 | *注意:此插件默认在工作区`src/intl`目录下存放国际化配置文件,并默认用户使用的本地语言为**中文**,目标的国际语言为**英文***
64 |
65 | 🌐本插件提供了**多语言**支持并将其设计得足够灵活,如果你想自定义本地语言和目标国际语言,可以参考 [多语言支持](#多语言支持)
66 |
67 | 本插件默认工作区采用以下形式的 react-intl 国际化配置
68 |
69 | 1. 供 `IntlProvider` 使用的 `index` 文件(插件也可以自动生成)
70 | ```ts
71 | // src/intl/index.ts
72 | import zh_CN from "./zh_CN.json";
73 | import en_US from "./en_US.json";
74 |
75 | export type ILocales = 'en-US' | 'zh-CN'
76 |
77 | export function getLocales(lang: ILocales)
78 | {
79 | switch (lang)
80 | {
81 | case ('en-US'):
82 | return en_US
83 | case ('zh-CN'):
84 | return zh_CN
85 | default:
86 | return en_US
87 | }
88 | }
89 |
90 | export default {
91 | "en-US": en_US,
92 | "zh-CN": zh_CN
93 | }
94 | ```
95 | 2. 对应的国际化 json 文件
96 | ```json
97 | // src/intl/zh_CN.json
98 | {
99 | "HELLO": "你好",
100 | }
101 |
102 | // src/intl/en_US.json
103 | {
104 | "HELLO": "Hello",
105 | }
106 | ```
107 |
108 | ### 基本使用
109 | 在 React 中最基本的使用是识别一个中文字符串
110 | ```ts
111 | const intl = useIntl()
112 |
113 | const message = '你好'
114 | // 👇
115 | const message = intl.formatMessage({ id: 'HELLO' })
116 | ```
117 | 另外也可以使用`react-intl=`或`$=`标识符表示这段文本**含有特殊字符串**,或者这是一段除了中文外的**其他语言的文本**(isChinese 无法判断含有特殊字符的文本为中文字符)
118 | ```ts
119 | const intl = useIntl()
120 |
121 | const message = '$=Hello'
122 | // 👇
123 | const message = intl.formatMessage({ id: 'HELLO' })
124 | ```
125 |
126 | 当然在 JSX 元素内部你需要给这个字符串加上大括号表示这是一段 JS 代码
127 | ```ts
128 | import React from 'react'
129 | import { useIntl } from 'react-intl'
130 |
131 | const Element = () =>
132 | {
133 | const intl = useIntl()
134 |
135 | return
136 | {'你好'}
137 | // 👇
138 | {intl.formatMessage({ id: 'HELLO' })}
139 |
140 | }
141 | ```
142 |
143 | ### Simple Argument
144 | 本插件支持 react-intl 简单的参数语法 [Message Syntax](https://formatjs.io/docs/core-concepts/icu-syntax/)
145 |
146 | 关于本插件对该功能的实现可以参考 issue [#4](https://github.com/Styx11/react-intl-linter/issues/4)
147 |
148 | 
149 |
150 | 其中我们使用普通的对象字面量来声明句子中的参数:
151 |
152 | ```ts
153 | // 目标特殊文本
154 | const message = 'react-intl=你好,{name: "约翰"}'
155 | // 👇
156 | // 替换后的 react-intl 代码
157 | const intl = intl.formatMessage({ id: "HELLO_NAME" }, {name: "约翰"})
158 |
159 | // 拿去翻译的结果,真正的内容由参数传递
160 | const transTarget = '你好,{name}'
161 |
162 | // 处理后供选择的 intlId
163 | const intlId = 'HELLO_NAME'
164 | // 翻译后的中文结果
165 | const transZHResult = '你好,{name}'
166 | // 翻译后的英文结果
167 | const transENResult = 'Hello, {name}'
168 |
169 | ```
170 |
171 | ### Disabling Rules
172 | 类似于 ESLint,本插件也允许你在文件中使用注释来临时禁止规则出现警告:
173 | ```ts
174 | // ri-lint-disable
175 |
176 | const message = '这段话被禁止抛出警告'
177 |
178 | // ri-lint-enable
179 | ```
180 |
181 | 可以在你的文件中使用以下格式的行注释或块注释在某一特定的行上禁用所有规则:
182 | ```ts
183 | const message = '这段话被禁止抛出警告' // ri-lint-disable-line
184 |
185 | // ri-lint-disable-next-line
186 | const message_01 = '这段话被禁止抛出警告'
187 |
188 | /* ri-lint-disable-next-line */
189 | const message_02 = '这段话被禁止抛出警告'
190 |
191 | const message_03 = '这段话被禁止抛出警告' /* ri-lint-disable-line */
192 | ```
193 |
194 | 注释规则遵循 **就近原则**,也就是说,一个句子之前如果有多个注释,那么它会应用离他最近的那个注释(包括句子那一行的)
195 | ```ts
196 | // ri-lint-disable
197 |
198 | // ri-lint-enable-next-line
199 | const messsage = '这段话被禁止抛出警告' // ri-disable-line
200 | ```
201 |
202 | 你可以通过 CodeAction 方便地为句子添加规则注释:
203 | [](https://imgtu.com/i/HtwECT)
204 |
205 | 以下是本插件目前支持的所有规则注释:
206 | ```ts
207 | /* ri-lint-enable */ // 解析该注释之后的中文时,插件发出警告
208 | /* ri-lint-disable */ // 解析该注释之后的中文时,插件不发出警告
209 | /* ri-lint-enable-line */ // 解析该注释当前行的中文时,插件发出警告
210 | /* ri-lint-disable-line */ // 解析该注释当前行的中文时,插件不发出警告
211 | /* ri-lint-enable-next-line */ // 解析该注释后下一行的中文时,插件发出警告
212 | /* ri-lint-disable-next-line */ // 解析该注释下一行的中文时,插件不发出警告
213 | ```
214 |
215 | ### 注意
216 | ⚠️因为百度翻译会将驼峰字符串转化为普通的字符串形式
217 | ```ts
218 | 'totalPage'
219 | // 👇
220 | 'totalpage'
221 | ```
222 | 所以我们会统一将驼峰命名形式的参数转为下划线命名的形式
223 | ```ts
224 | // 目标特殊文本
225 | const message = '$=我说:{rawMessage: "你好"}'
226 | // 👇
227 | // 替换后的 react-intl 代码
228 | const intl = intl.formatMessage({ id: "I_SAID_RAWMESSAGE" }, {raw_message: "你好"})
229 |
230 | // 对应配置
231 | // en_US
232 | {
233 | "I_SAID_RAWMESSAGE": "I said: {raw_message}"
234 | }
235 | // zh_CN
236 | {
237 | "I_SAID_RAWMESSAGE": "我说: {raw_message}"
238 | }
239 | ```
240 |
241 | ## 多语言支持
242 | *关于多语言的实现可参考 [issue#11](https://github.com/Styx11/react-intl-linter/issues/11)*
243 |
244 | 本插件将语言分为了两个概念:一是**本地语言**,二是**国际语言**。
245 |
246 | 本地语言表示用户使用的语言,它代表了本地文本的语言类型;国际语言表示用户想要覆盖到的所有外国语言类型,比如我们的本地语言为中文,我想要支持中英文的国际化,那么英文就是国际语言。
247 |
248 | 两种类型会影响我们对文本的翻译,因为我们要基于它们构建一对多的翻译 Token 并得到对应它们的翻译结果。
249 |
250 | 举个例子🌰,如果我们的本地语言是中文,国际语言包括英文、日文和繁体中文,那么它们的关系会是这样的:
251 |
252 |
253 |
254 | 用户使用本插件时可以灵活地组合本地语言和国际语言,插件会根据你的设置对这些国际化配置文件进行管理
255 |
256 |
257 |
258 | ### 语言代码
259 |
260 | 下面是我们现在支持的所有语言以及它们对应的语言代码,用户可以使用这些语言代码进行相应的配置:
261 |
262 | 语言名称|代码
263 | ------|----
264 | 中文|zh
265 | 英语|en
266 | 日语|jp
267 | 繁体中文|cht
268 |
269 | 用户可以配置`"localLanguage"`和`"localLanguageConfigName"`来分别表示`本地语言`和`本地语言配置文件名`;
270 |
271 | 配置`"intlLanguage"`和`"intlLanguageConfigName"`来表示`国际语言数组`和`国际语言配置文件名数组`
272 |
273 | 需要注意的是:用户配置的`"intlLanguage"`数组和`"intlLanguageConfigName"`数组要**严格地一一对应**,否则插件无法正确的将文本写入对应的配置文件中。
274 |
275 | ### 例子
276 |
277 | 举个例子🌰,我们有以下配置文件:
278 | ```json
279 | // .vscode/settings.json
280 | {
281 | "reactIntlLinter.localLanguage": "en", // 本地语言为英语
282 | "reactIntlLinter.localLanguageConfigName": "en_US", // 本地语言配置文件名
283 | "reactIntlLinter.intlLanguage": [
284 | "zh", // 国际语言有中文和繁体中文
285 | "cht"
286 | ],
287 | "reactIntlLinter.intlLanguageConfigName": [
288 | "zh_CN", // 国际语言配置对应的文件名
289 | "zh_TW"
290 | ],
291 | "reactIntlLinter.intlConfigPath": "src/intl",
292 | }
293 | ```
294 | 以上的配置表明我们的本地语言为**英文**,我们想要覆盖到**中文**和**繁体中文**的国际化,插件会自动将翻译文本写入到对应文件中,效果会是下面这样的:
295 | 
296 |
297 | ## 配置
298 | 本插件提供了诸如国际化配置文件夹路径、国际化配置文件名称和不同框架下的国际化代码等配置项可供用户在 `settings.json` 文件下自定义:
299 |
300 | 配置项|类型|默认值|描述
301 | -----|-----|------|----
302 | localLanguage|`string`|`'zh'`|本地语言代码 [语言代码](#语言代码)
303 | localLanguageConfigName|`string`|`'zh_CN'`|本地语言配置 json 文件名(不包括后缀)
304 | intlLanguage|`array`|`['en']`|国际语言代码数组 [语言代码](#语言代码)
305 | intlLanguageConfigName|`array`|`['en_US']`|国际语言配置 json 文件名数组(不包括后缀)
306 | intlConfigPath|`string`|`src/intl`|国际化配置文件夹路径名(相对于工作区根路径)
307 | intlCode|`react-intl`\|`vue-i18n`|`react-intl`|目标国际化框架,支持 `react-intl`,`vue-i18n`。`react-intl` 对应代码为 `intl.formatMessage({id: ...})` ,`vueI18n` 对应代码为 `$t('id')`
308 |
309 | 其中,因为国际化代码还需要适配简单Message Syntax参数的使用(即使以上两种国际化框架使用的是同一参数语法),所以目前无法通过代码字符串的方式进行配置
310 |
311 | ***修改配置后,你需要重启 VS Code***
312 |
313 | ## Sponsor
314 | 由于我使用的翻译接口是需要收费的😅,所以如果这个插件你用的爽的话或许可以 Buy me a Coffee☕️
315 |
316 |  
317 |
318 | ## Structure
319 |
320 | ```
321 | .
322 | ├── client // Language Client
323 | │ ├── src
324 | │ │ └── extension.ts // Language Client entry point
325 | ├── package.json // The extension manifest.
326 | └── server // Language Server
327 | └── src
328 | └── server.ts // Language Server entry point
329 | ```
330 |
331 | ## Debug
332 |
333 | - Run `npm install` in this folder. This installs all necessary npm modules in both the client and server folder
334 | - Open VS Code on this folder.
335 | - Press Ctrl+Shift+B to compile the client and server.
336 | - Switch to the Debug viewlet.
337 | - Select `Launch Client` from the drop down.
338 | - Run the launch config.
339 | - If you want to debug the server as well use the launch configuration `Attach to Server`
340 | - In the [Extension Development Host] instance of VSCode, open a document in 'plain text' language mode.
341 | - Activate code action on the error on the first line.
342 |
343 | ## License
344 | Apache License 2.0
--------------------------------------------------------------------------------
/client/.eslintignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # react-intl-linter💡
4 | 自动替换中文字符串为 react-intl 代码的 VS Code 插件
5 |
6 |
7 |
8 |
9 | ## 功能
10 |
11 | 这个插件可以自动检测打开的文件中的包裹在单/双引号之间的**中文文本**,并提供用户一个提示,用户可以点击提示进行中文文本翻译并选择写入的 intl 内容,插件会自动更新配置文件
12 | - 中文字符串提示
13 | - 自动检测已有国际化配置是否已包含目标文本
14 | - 翻译目标文本至英文,用户可以选择或自定义 intl id 内容
15 | - 替换中文字符串为`intl.formatMessage({ id: ... })`
16 |
17 |
18 | ## Structure
19 |
20 | ```
21 | .
22 | ├── client // Language Client
23 | │ ├── src
24 | │ │ └── extension.ts // Language Client entry point
25 | ├── package.json // The extension manifest.
26 | └── server // Language Server
27 | └── src
28 | └── server.ts // Language Server entry point
29 | ```
30 |
31 | ## Debug
32 |
33 | - Run `npm install` in this folder. This installs all necessary npm modules in both the client and server folder
34 | - Open VS Code on this folder.
35 | - Press Ctrl+Shift+B to compile the client and server.
36 | - Switch to the Debug viewlet.
37 | - Select `Launch Client` from the drop down.
38 | - Run the launch config.
39 | - If you want to debug the server as well use the launch configuration `Attach to Server`
40 | - In the [Extension Development Host] instance of VSCode, open a document in 'plain text' language mode.
41 | - Activate code action on the error on the first line.
42 |
43 | ## License
44 | Apache License 2.0
--------------------------------------------------------------------------------
/client/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.0.4",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/blueimp-md5": {
8 | "version": "2.18.0",
9 | "resolved": "https://registry.npmjs.org/@types/blueimp-md5/-/blueimp-md5-2.18.0.tgz",
10 | "integrity": "sha512-f4A+++lGZGJvVSgeyMkqA7BEf2BVQli6F+qEykKb49c5ieWQBkfpn6CP5c1IZr2Yi2Ofl6Fj+v0e1fN18Z8Cnw==",
11 | "dev": true
12 | },
13 | "@types/uuid": {
14 | "version": "8.3.1",
15 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz",
16 | "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==",
17 | "dev": true
18 | },
19 | "@types/vscode": {
20 | "version": "1.52.0",
21 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.52.0.tgz",
22 | "integrity": "sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA==",
23 | "dev": true
24 | },
25 | "axios": {
26 | "version": "0.21.4",
27 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
28 | "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
29 | "requires": {
30 | "follow-redirects": "^1.14.0"
31 | }
32 | },
33 | "balanced-match": {
34 | "version": "1.0.0",
35 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
36 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
37 | },
38 | "blueimp-md5": {
39 | "version": "2.18.0",
40 | "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.18.0.tgz",
41 | "integrity": "sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q=="
42 | },
43 | "brace-expansion": {
44 | "version": "1.1.11",
45 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
46 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
47 | "requires": {
48 | "balanced-match": "^1.0.0",
49 | "concat-map": "0.0.1"
50 | }
51 | },
52 | "concat-map": {
53 | "version": "0.0.1",
54 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
55 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
56 | },
57 | "follow-redirects": {
58 | "version": "1.14.3",
59 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.3.tgz",
60 | "integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw=="
61 | },
62 | "lru-cache": {
63 | "version": "6.0.0",
64 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
65 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
66 | "requires": {
67 | "yallist": "^4.0.0"
68 | }
69 | },
70 | "minimatch": {
71 | "version": "3.0.4",
72 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
73 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
74 | "requires": {
75 | "brace-expansion": "^1.1.7"
76 | }
77 | },
78 | "semver": {
79 | "version": "7.3.4",
80 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
81 | "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
82 | "requires": {
83 | "lru-cache": "^6.0.0"
84 | }
85 | },
86 | "uuid": {
87 | "version": "8.3.2",
88 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
89 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
90 | },
91 | "vscode-jsonrpc": {
92 | "version": "6.0.0",
93 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
94 | "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg=="
95 | },
96 | "vscode-languageclient": {
97 | "version": "7.0.0",
98 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz",
99 | "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==",
100 | "requires": {
101 | "minimatch": "^3.0.4",
102 | "semver": "^7.3.4",
103 | "vscode-languageserver-protocol": "3.16.0"
104 | }
105 | },
106 | "vscode-languageserver-protocol": {
107 | "version": "3.16.0",
108 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz",
109 | "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==",
110 | "requires": {
111 | "vscode-jsonrpc": "6.0.0",
112 | "vscode-languageserver-types": "3.16.0"
113 | }
114 | },
115 | "vscode-languageserver-types": {
116 | "version": "3.16.0",
117 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
118 | "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA=="
119 | },
120 | "yallist": {
121 | "version": "4.0.0",
122 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
123 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "displayName": "React-Intl-Linter - Client",
4 | "version": "2.0.1",
5 | "description": "vscode linter extension for react-intl",
6 | "repository": "git@github.com:Styx11/react-intl-linter.git",
7 | "author": "styx11 <1654630248@qq.com>",
8 | "license": "Apache-2.0 License",
9 | "private": true,
10 | "bugs": {
11 | "url": "https://github.com/Styx11/react-intl-linter/issues"
12 | },
13 | "engines": {
14 | "vscode": "^1.52.0"
15 | },
16 | "devDependencies": {
17 | "@types/blueimp-md5": "^2.18.0",
18 | "@types/uuid": "^8.3.1",
19 | "@types/vscode": "1.52.0"
20 | },
21 | "dependencies": {
22 | "axios": "^0.21.4",
23 | "blueimp-md5": "^2.18.0",
24 | "snake-case": "^3.0.4",
25 | "uuid": "^8.3.2",
26 | "vscode-languageclient": "^7.0.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/client/src/config/ConfigManager.ts:
--------------------------------------------------------------------------------
1 | // @desc configuration 配置管理对象
2 |
3 | import { workspace } from 'vscode'
4 | import { CONFIG_SECTION } from '../lib/constant'
5 | import { SupportLanguage } from '../lib/translate/TranslateManager'
6 |
7 | export const enum IntlCodeType
8 | {
9 | REACT_INTL = 'react-intl',
10 | VUE_I18N = 'vue-i18n',
11 | }
12 |
13 | // 配置项目,与 package.json 保持一致
14 | export const enum LinterConfigItem
15 | {
16 | localLanguage = 'localLanguage',
17 | intlLanguage = 'intlLanguage',
18 | localLanguageConfigName = 'localLanguageConfigName',
19 | intlLanguageConfigName = 'intlLanguageConfigName',
20 | intlConfigPath = 'intlConfigPath',
21 | intlCode = 'intlCode',
22 | }
23 |
24 | // 配置接口
25 | export interface LinterConfig
26 | {
27 | [LinterConfigItem.localLanguage]: SupportLanguage; // 本地语言
28 | [LinterConfigItem.intlLanguage]: SupportLanguage[]; // 目标国际化语言
29 | [LinterConfigItem.localLanguageConfigName]: string; // 本地语言配置文件名
30 | [LinterConfigItem.intlLanguageConfigName]: string[]; // 国际化语言配置文件名数组,与 intlLanguage 长度保持一致
31 | [LinterConfigItem.intlConfigPath]: string; // 国际化配置文件夹路径
32 | [LinterConfigItem.intlCode]: IntlCodeType; // 国际化代码模版
33 | }
34 |
35 | // 默认配置,与 package.json 中的默认配置保持一致
36 | const _defaultConfig: { [key in LinterConfigItem]: LinterConfig[key] } =
37 | {
38 | [LinterConfigItem.localLanguage]: SupportLanguage.ZH,
39 | [LinterConfigItem.intlLanguage]: [SupportLanguage.EN],
40 | [LinterConfigItem.localLanguageConfigName]: 'zh_CN',
41 | [LinterConfigItem.intlLanguageConfigName]: ['en_US'],
42 | [LinterConfigItem.intlConfigPath]: 'src/intl',
43 | [LinterConfigItem.intlCode]: IntlCodeType.REACT_INTL,
44 | }
45 |
46 | export default class ConfigManager
47 | {
48 | private static _instance: ConfigManager
49 |
50 | static getInstance()
51 | {
52 | if (!this._instance)
53 | {
54 | this._instance = new ConfigManager()
55 | }
56 | return this._instance
57 | }
58 |
59 | protected constructor() { }
60 |
61 | public getConfig(item: T): LinterConfig[T]
62 | {
63 | const workspaceConfig = workspace.getConfiguration().get(CONFIG_SECTION)
64 | return workspaceConfig ? workspaceConfig[item] : _defaultConfig[item]
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/client/src/extension.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import * as path from 'path';
4 | import { ExtensionContext, Uri, window as Window, workspace } from 'vscode';
5 | import { ExecuteCommandSignature, LanguageClient, LanguageClientOptions, RevealOutputChannelOn, ServerOptions, TransportKind } from 'vscode-languageclient/node';
6 |
7 | import ConfigManager, { LinterConfigItem } from './config/ConfigManager';
8 | import { LinterCommands } from './lib/constant';
9 | import { TranslationResultMap } from './lib/translate/TranslateManager';
10 | import { getDocumentSelector } from './lib/util';
11 | import DisableFileMiddleware from './middleware/disable-file';
12 | import DisableLineMiddleware from './middleware/disable-line';
13 | import ExtractMiddleware from './middleware/extract';
14 |
15 | let client: LanguageClient;
16 |
17 | export async function activate(context: ExtensionContext): Promise
18 | {
19 | // 指明语言服务器路径
20 | // 因为我们只能将 client 目录下的文件作为 extension 发布,所以需要复制 server/out 下的文件至 client/out/server 下
21 | const serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js'));
22 |
23 | // 国际化配置模版路径
24 | const intlConfigTemp = context.asAbsolutePath(path.join('client', 'src', 'lib', 'intl', 'template'))
25 |
26 | // 工作区的真实文件路径
27 | const workspaceFolderPath = workspace.workspaceFolders && workspace.workspaceFolders[0]
28 |
29 | // 工作区国际化配置文件夹路径
30 | const workspaceIntlConfigFolerPath = ConfigManager.getInstance().getConfig(LinterConfigItem.intlConfigPath)
31 | const workspaceIntlConfigPath = workspaceFolderPath && Uri.joinPath(workspaceFolderPath.uri, workspaceIntlConfigFolerPath)
32 |
33 | // 语言服务器配置
34 | let serverOptions: ServerOptions = {
35 | run: { module: serverModule, transport: TransportKind.ipc, options: { cwd: process.cwd() } },
36 | debug: { module: serverModule, transport: TransportKind.ipc, options: { execArgv: ['--nolazy', '--inspect=6011'], cwd: process.cwd() } }
37 | };
38 |
39 | // 客户端配置
40 | let clientOptions: LanguageClientOptions = {
41 | documentSelector: getDocumentSelector(),
42 | diagnosticCollectionName: 'react-intl-linter',
43 | revealOutputChannelOn: RevealOutputChannelOn.Never,
44 | progressOnInitialization: true,
45 | middleware: {
46 | // 执行从语言服务器发送的要执行的命令
47 | executeCommand: async (command: string, args: any[], next: ExecuteCommandSignature) =>
48 | {
49 | try
50 | {
51 | switch (command)
52 | {
53 | case LinterCommands.Extract:
54 | await ExtractMiddleware(intlConfigTemp, workspaceIntlConfigPath, args, next);
55 | break;
56 | case LinterCommands.DisableLine:
57 | DisableLineMiddleware(args, next);
58 | break;
59 | case LinterCommands.DisableFile:
60 | DisableFileMiddleware(args, next);
61 | break;
62 | default:
63 | return
64 | }
65 | }
66 | catch (e)
67 | {
68 | Window.showErrorMessage(`抽取文本发生错误 ${e}`);
69 | }
70 |
71 | }
72 | }
73 | };
74 |
75 | try
76 | {
77 | client = new LanguageClient('React-Intl-Linter', serverOptions, clientOptions);
78 | } catch (err)
79 | {
80 | Window.showErrorMessage(`The extension couldn't be started. See the output channel for details.`);
81 | return;
82 | }
83 | client.registerProposedFeatures();
84 |
85 | context.subscriptions.push(
86 | client.start(),
87 | );
88 | }
89 |
90 | export function deactivate(): Thenable | undefined
91 | {
92 | // 清除翻译缓存
93 | TranslationResultMap.clear()
94 |
95 | if (!client)
96 | {
97 | return undefined;
98 | }
99 | return client.stop();
100 | }
--------------------------------------------------------------------------------
/client/src/lib/constant.ts:
--------------------------------------------------------------------------------
1 |
2 | // 激活插件的语言文件,与根目录下的 package.json 中的 activationEvents 保持一致
3 | export const ActivationLanguage = [
4 | "onLanguage:typescript",
5 | "onLanguage:typescriptreact",
6 | "onLanguage:javascriptreact",
7 | "onLanguage:javascript",
8 | "onLanguage:html",
9 | "onLanguage:vue"
10 | ]
11 |
12 | // 从语言服务器发出的要执行的插件命令
13 | export enum LinterCommands
14 | {
15 | Extract = 'react-intl-linter.extract', // 抽取中文字符串为 react-intl 代码
16 | DisableLine = 'react-intl-linter.disable-line', // 在中文代码前一行添加 ri-lint-disable-nextline 注释
17 | DisableFile = 'react-intl-linter.disable-file', // 在当前文件开始行添加 ri-lint-disable 注释
18 | }
19 |
20 | export const CUSTOM_INTL_ID_REGX = /[A-Z_]+/
21 |
22 | // json 文件的缩进数
23 | export const JSON_SPACE = 4
24 |
25 | // 自定义国际化内容选项
26 | export const CUSTOM_PICK_OPTION = '自定义'
27 |
28 | // 自定义 pick option 描述信息
29 | export const CUSTOM_PICK_OPTION_DESC = '自定义 id,这会默认选择第一个翻译结果'
30 |
31 | // 选择国际化文本 placeholder
32 | export const CUSTOM_PICK_PLACEHOLDER = '请选择用来替换的国际化代码 id 内容'
33 |
34 | // 自定义国际化输入框 placeholder
35 | export const CUSTOM_INPUT_PLACEHOLDER = '请输入自定义的国际化代码 id 内容'
36 |
37 | // 自定义 intl id 校验失败信息
38 | export const INVALID_CUSTOM_ID_MESSAGE = '国际化代码 id 只能由大写字符或下划线组成'
39 |
40 | // intl id 中的非法字符
41 | export const INVALID_INTL_ID_CHARACTER = /[^A-Za-z\s]/ig
42 |
43 | // configuration 配置域名
44 | export const CONFIG_SECTION = 'reactIntlLinter'
--------------------------------------------------------------------------------
/client/src/lib/intl/IntlConfigManager.ts:
--------------------------------------------------------------------------------
1 | import { ProgressLocation, Uri, window } from "vscode"
2 |
3 | import ConfigManager, { LinterConfigItem } from "../../config/ConfigManager"
4 | import { SupportLanguage } from "../translate/TranslateManager";
5 | import IntlConfigVisitor, { IntlConfigFactory } from "./IntlConfigVisitor";
6 | import { initializeIntlIndexFile } from "./template/generator";
7 |
8 | // 此单例用于统一管理(本地语言、国际语言)国际化配置文件,包括校验用户配置是否合法,初始化配置文件,以及配置文件的写入等
9 | export default class IntlConfigManager
10 | {
11 | private static _instance: IntlConfigManager;
12 |
13 | // 用户配置的所有语言
14 | private _configLangs: SupportLanguage[];
15 |
16 | // 用户配置的所有文件名
17 | private _configNames: string[];
18 |
19 | private _configVisitor: IntlConfigVisitor[];
20 |
21 | static getInstance()
22 | {
23 | if (!this._instance)
24 | {
25 | this._instance = new IntlConfigManager()
26 | }
27 | return this._instance
28 | }
29 |
30 | protected constructor()
31 | {
32 | this._configLangs = []
33 | this._configNames = []
34 | this._configVisitor = []
35 | }
36 |
37 | /**
38 | * 校验用户配置的国际化文件名是否合法
39 | *
40 | * @private
41 | * @memberof IntlConfigManager
42 | */
43 | private validIntlConfigSetting(): void
44 | {
45 | const localLanguage = ConfigManager.getInstance().getConfig(LinterConfigItem.localLanguage)
46 | const intlLanguage = ConfigManager.getInstance().getConfig(LinterConfigItem.intlLanguage)
47 | const localLanguageConfigName = ConfigManager.getInstance().getConfig(LinterConfigItem.localLanguageConfigName)
48 | const intlLanguageConfigName = ConfigManager.getInstance().getConfig(LinterConfigItem.intlLanguageConfigName)
49 |
50 | if (
51 | !localLanguage
52 | || !localLanguageConfigName
53 | || !intlLanguageConfigName.length
54 | || intlLanguageConfigName.length !== intlLanguage.length
55 | )
56 | {
57 | throw new Error('国际化相关设置错误,请检查插件相关配置')
58 | }
59 |
60 | // 所有受支持的语言
61 | const langs = Object.values(SupportLanguage)
62 |
63 | if (langs.every((lang) => localLanguage !== lang)) throw new Error('本地语言不受支持,请检查插件相关配置')
64 |
65 | if (intlLanguage.some(intl => !langs.includes(intl))) throw new Error('国际语言不受支持,请检查插件相关配置')
66 |
67 | this._configLangs = [localLanguage, ...intlLanguage]
68 |
69 | this._configNames = [localLanguageConfigName, ...intlLanguageConfigName]
70 | }
71 |
72 | /**
73 | * 初始化所有配置文件(包括本地语言和国际语言配置文件)
74 | *
75 | * @param {Uri} workspaceIntlConfigPath - 工作区配置文件路径
76 | * @param {string} intlTempPath - 国际化配置模版路径
77 | * @return {*} {Promise}
78 | * @memberof IntlConfigManager
79 | */
80 | public async initializeIntlConfig(workspaceIntlConfigPath: Uri, intlTempPath: string): Promise
81 | {
82 | this.validIntlConfigSetting()
83 |
84 | this._configVisitor = IntlConfigFactory(workspaceIntlConfigPath, this._configNames)
85 |
86 | await Promise.all([
87 | ...this._configVisitor.map(visitor => visitor.initializeWorkplaceIntlConfig(intlTempPath)),
88 | initializeIntlIndexFile(workspaceIntlConfigPath, this._configLangs, this._configNames),
89 | ])
90 | }
91 |
92 | /**
93 | * 读取所有各语言对应的国际化配置
94 | *
95 | * @return {*} {Promise[]>}
96 | * @memberof IntlConfigManager
97 | */
98 | public async readAllConfig(): Promise[]>
99 | {
100 | return await Promise.all(
101 | this._configVisitor.map(visitor => visitor.readConfig())
102 | )
103 | }
104 |
105 | /**
106 | * 本地语言文本是否已存在配置中,若存在则返回对应的 intl id
107 | *
108 | * @param {string} localText - 想要转换的本地语言文本
109 | * @param {Record[]} configs - 所有语言配置文件
110 | * @return {*} {(string | undefined)}
111 | * @memberof IntlConfigManager
112 | */
113 | public getExistingIntlId(localText: string, configs: Record[]): string | undefined
114 | {
115 | if (!configs.length) return
116 |
117 | const _configs = configs.slice()
118 |
119 | const localConfig = _configs.shift() as Record
120 |
121 | const intlId = Object.keys(localConfig).find(key => localConfig[key] === localText)
122 |
123 | const allConfigsExisted = intlId ? _configs.every(c => intlId in c) : false
124 |
125 | return allConfigsExisted ? intlId : undefined
126 |
127 | }
128 |
129 | /**
130 | * 更新所有国际化配置文件
131 | *
132 | * @param {string} intlId - 国际化配置 id
133 | * @param {string[]} translationResults - 所有翻译结果,包括本地语言(即代码中需要替换的本地语言字符串)
134 | * @param {Record[]} configs - 所有的已有国际化配置(和翻译结果一一对应,本地语言总是位于第一个)
135 | * @return {*} {Promise}
136 | * @memberof IntlConfigManager
137 | */
138 | public async writeConfigIntoWorkSpace(intlId: string, translationResults: string[], configs: Record[]): Promise
139 | {
140 | const _result = translationResults.slice()
141 | const _config = configs.slice()
142 | await window.withProgress({
143 | cancellable: false,
144 | location: ProgressLocation.Window,
145 | title: `正在写入国际化配置...`,
146 | }, async () =>
147 | {
148 | await Promise.all(
149 | this._configVisitor.map(visior => visior.updateConfig(intlId, _result.shift() || '', _config.shift()))
150 | )
151 | })
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/client/src/lib/intl/IntlConfigVisitor.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import { Uri, workspace } from "vscode";
3 | import { JSON_SPACE } from "../constant";
4 | import { getWorkspaceIntlJsonPath } from "../util";
5 |
6 | const TEMP_CONFIG_NAME = '/template_config.json'
7 |
8 | /**
9 | * 某个语言对应的国际化配置管理类
10 | * 负责单个国际化配置的 初始化、查询和更新
11 | *
12 | * @class IntlConfigVisitor
13 | */
14 | export default class IntlConfigVisitor
15 | {
16 | // 国际化配置的工作区路径
17 | private _workspacePath: Uri;
18 |
19 | constructor(workspacePath: Uri)
20 | {
21 | this._workspacePath = workspacePath
22 | }
23 |
24 | /**
25 | * 初始化本 visitor 对应的工作区配置文件
26 | *
27 | * @param {string} intlTempPath
28 | * @return {*} {Promise}
29 | * @memberof IntlConfigVisitor
30 | */
31 | public async initializeWorkplaceIntlConfig(intlTempPath: string): Promise
32 | {
33 | try
34 | {
35 | await workspace.fs.stat(this._workspacePath)
36 | }
37 | catch (e)
38 | {
39 | await workspace.fs.copy(Uri.file(path.join(intlTempPath, TEMP_CONFIG_NAME)), this._workspacePath)
40 | }
41 | }
42 |
43 | /**
44 | * 读取本 visitor 对应的工作区配置
45 | *
46 | * @return {*} {Promise>}
47 | * @memberof IntlConfigVisitor
48 | */
49 | public async readConfig(): Promise>
50 | {
51 | const rawConfig = await workspace.fs.readFile(this._workspacePath)
52 |
53 | return await Promise.resolve>(JSON.parse(rawConfig.toString()))
54 | }
55 |
56 | /**
57 | * 将翻译内容写入现有工作区的国际化配置中
58 | * 并按 keys 排序后返回新的配置对象
59 | *
60 | * @private
61 | * @param {string} intlId - 国际化 id
62 | * @param {string} text - 写入的文本
63 | * @param {Record} [config] - 旧的国际化配置
64 | * @return {*} {Promise>}
65 | * @memberof IntlConfigVisitor
66 | */
67 | private async insertConfig(intlId: string, text: string, config?: Record): Promise>
68 | {
69 | const _config: Record = {}
70 |
71 | // 处理新的国际化配置内容
72 | // 包括写入新对象,重新将 key 排序
73 | const updatedConfig = await new Promise>((resolve, reject) =>
74 | {
75 | if (!config) return reject()
76 |
77 | config[intlId] = text
78 | Object
79 | .keys(config)
80 | .sort(new Intl.Collator('en-US').compare)
81 | .forEach(id => _config[id] = config[id])
82 |
83 | resolve(_config)
84 | })
85 |
86 | return updatedConfig
87 | }
88 |
89 | /**
90 | * 将配置文件写入工作区
91 | *
92 | * @private
93 | * @param {Record} config
94 | * @return {*} {Promise}
95 | * @memberof IntlConfigVisitor
96 | */
97 | private async writeConfig(config: Record): Promise
98 | {
99 | const wpPath = this._workspacePath
100 | await workspace.fs.writeFile(wpPath, Buffer.from(JSON.stringify(config, null, JSON_SPACE)))
101 | }
102 |
103 | /**
104 | * 更新配置文件
105 | *
106 | * @param {string} intlId - 国际化配置 id
107 | * @param {string} text - 写入配置的文本
108 | * @param {Record} [config] - 现有配置
109 | * @return {*} {Promise}
110 | * @memberof IntlConfigVisitor
111 | */
112 | public async updateConfig(intlId: string, text: string, config?: Record): Promise
113 | {
114 | const sortedConfig = await this.insertConfig(intlId, text, config)
115 | await this.writeConfig(sortedConfig)
116 | }
117 |
118 | }
119 |
120 |
121 | /**
122 | * 工厂函数,用于创建国际化配置路径对应的「管理类」数组
123 | * 每个「管理类」负责该国际化配置的各项操作
124 | *
125 | * @param {Uri} workspaceIntlConfigPath - 工作区国际化文件夹路径名
126 | * @param {string} configPathNames - 所有配置文件名数组(包括本地语言配置和国际语言配置,本地语言路径名在数组第一位)
127 | * @return {*} {IntlConfigVisitor[]}
128 | */
129 | export function IntlConfigFactory(
130 | workspaceIntlConfigPath: Uri,
131 | configPathNames: string[]
132 | ): IntlConfigVisitor[]
133 | {
134 | return configPathNames.map(name => new IntlConfigVisitor(getWorkspaceIntlJsonPath(workspaceIntlConfigPath, `${name}.json`)))
135 | }
136 |
--------------------------------------------------------------------------------
/client/src/lib/intl/template/generator.ts:
--------------------------------------------------------------------------------
1 | import { Uri, workspace } from "vscode"
2 | import { SupportLanguage } from "../../translate/TranslateManager"
3 | import { getWorkspaceIntlJsonPath } from "../../util"
4 |
5 | const LanguageCode: { [key in SupportLanguage]: string } = {
6 | [SupportLanguage.ZH]: 'zh-CN',
7 | [SupportLanguage.EN]: 'en-US',
8 | [SupportLanguage.CHT]: 'zh-TW',
9 | [SupportLanguage.JP]: 'ja',
10 | }
11 |
12 |
13 | /**
14 | * 根据 本地、国际语言以及对应的文件名称格式化 react-intl 配置的 index.ts 文件
15 | *
16 | * @param {SupportLanguage[]} languages 所有语言
17 | * @param {string[]} names 和语言一一对应的配置文件名
18 | * @return {string} index.ts 代码文本
19 | */
20 | const _generateIndexTemplate = (languages: SupportLanguage[], names: string[]): string =>
21 | {
22 | // 导入
23 | const _imports = names.map(name => `import ${name} from "./${name}.json";`).join('\n')
24 |
25 | // 类型
26 | const _types = 'export type ILocales = ' + languages.map(lang => `'${LanguageCode[lang]}'`).join(' | ')
27 |
28 | // switch 代码
29 | const _switches = languages.map((lang, i) => `case ('${LanguageCode[lang]}'):\n\t\t\treturn ${names[i]};`).join('\n\t\t')
30 |
31 | // 导出
32 | const _exports = `
33 | export default {
34 | ${languages.map((lang, i) => `"${LanguageCode[lang]}": ${names[i]},`).join('\n\t')}
35 | }
36 | `
37 |
38 | const template = `
39 | /* eslint-disable import/no-anonymous-default-export */
40 | ${_imports}
41 |
42 | ${_types}
43 |
44 | export function getLocales(lang: ILocales)
45 | {
46 | switch (lang)
47 | {
48 | ${_switches}
49 | default:
50 | return ${names[0]}
51 | }
52 | }
53 | ${_exports}
54 | `
55 |
56 | return template
57 | }
58 |
59 | /**
60 | * 生成国际化配置的 index 文件
61 | *
62 | * @param {Uri} workspaceIntlConfigPath
63 | * @param {SupportLanguage[]} languages
64 | * @param {string[]} configNames
65 | */
66 | export const initializeIntlIndexFile = async (workspaceIntlConfigPath: Uri, languages: SupportLanguage[], configNames: string[]) =>
67 | {
68 | const indexPath = getWorkspaceIntlJsonPath(workspaceIntlConfigPath, 'index.ts')
69 |
70 | try
71 | {
72 | await workspace.fs.stat(indexPath)
73 | }
74 | catch (e)
75 | {
76 | await workspace.fs.writeFile(indexPath, Buffer.from(_generateIndexTemplate(languages, configNames)))
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/client/src/lib/intl/template/index.ts:
--------------------------------------------------------------------------------
1 | // /* eslint-disable import/no-anonymous-default-export */
2 | // import zh_CN from "./zh_CN.json";
3 | // import en_US from "./en_US.json";
4 |
5 | // export type ILocales = 'en-US' | 'zh-CN'
6 |
7 | // export function getLocales(lang: ILocales)
8 | // {
9 | // switch (lang)
10 | // {
11 | // case ('en-US'):
12 | // return en_US
13 | // case ('zh-CN'):
14 | // return zh_CN
15 | // default:
16 | // return en_US
17 | // }
18 | // }
19 |
20 | // export default {
21 | // "en-US": en_US,
22 | // "zh-CN": zh_CN
23 | // }
--------------------------------------------------------------------------------
/client/src/lib/intl/template/template_config.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
--------------------------------------------------------------------------------
/client/src/lib/intl/utils.ts:
--------------------------------------------------------------------------------
1 | import ConfigManager, { IntlCodeType, LinterConfigItem } from "../../config/ConfigManager"
2 | import { INVALID_INTL_ID_CHARACTER } from "../constant"
3 | import { SpecialStringParams, specialStringParams2String } from "../util/validator"
4 |
5 | // intlCode 配置对应的代码 formatter 函数
6 | const LinterCode: { [key in IntlCodeType]: (intlId: string) => string } = {
7 | [IntlCodeType.REACT_INTL]: (intlId: string) => `intl.formatMessage({ id: '${intlId}' })`,
8 | [IntlCodeType.VUE_I18N]: (intlId: string) => `$t('${intlId}')`,
9 | }
10 |
11 | const LinterCodeWithParams: { [key in IntlCodeType]: (intlId: string, params: SpecialStringParams[]) => string } = {
12 | [IntlCodeType.REACT_INTL]:
13 | (intlId: string, params: SpecialStringParams[]) => `intl.formatMessage({ id: '${intlId}' }, ${specialStringParams2String(params)})`,
14 | [IntlCodeType.VUE_I18N]:
15 | (intlId: string, params: SpecialStringParams[]) => `$t('${intlId}', ${specialStringParams2String(params)})`,
16 | }
17 |
18 | /**
19 | * 通过数字获取用以区分相同翻译结果的 intl id
20 | * 例如 HELLO_01 和 HELLO_02
21 | *
22 | * @param {number} number
23 | * @return {*} {string}
24 | */
25 | export const getIntlIdCount = (number: number): string =>
26 | {
27 | const numStr = number.toString()
28 | if (numStr.length === 1) return `0${number}`
29 | return numStr
30 | }
31 |
32 | /**
33 | * 去掉翻译结果中的特殊字符串,只保留英文字母和空格作为 intl id
34 | *
35 | * @param {string} translationResult - 翻译结果
36 | * @return {*} {string}
37 | */
38 | export const getCleanIntlId = (translationResult: string): string =>
39 | {
40 | return translationResult
41 | .replace(INVALID_INTL_ID_CHARACTER, '')
42 | .trim()
43 | .toUpperCase()
44 | .replace(/\s+/g, '_')
45 | }
46 |
47 | /**
48 | * 获取 react-intl 代码
49 | *
50 | * @param {string} intlId
51 | * @param {SpecialStringParams[]} [params] 特殊字符串参数
52 | * @return {*}
53 | */
54 | export const getIntlMessage = (intlId: string, params?: SpecialStringParams[]) =>
55 | {
56 | const intlCodeType = ConfigManager.getInstance().getConfig(LinterConfigItem.intlCode)
57 | if (Array.isArray(params) && params.length)
58 | {
59 | return LinterCodeWithParams[intlCodeType](intlId, params)
60 | }
61 | return LinterCode[intlCodeType](intlId)
62 | }
63 |
--------------------------------------------------------------------------------
/client/src/lib/translate/TranslateManager.ts:
--------------------------------------------------------------------------------
1 |
2 | // 常见语种表可参考 baidu 翻译 api:https://fanyi-api.baidu.com/doc/21
3 |
4 | import axios from "axios"
5 | import ConfigManager, { LinterConfigItem } from "../../config/ConfigManager"
6 | import { getCleanIntlId } from "../intl/utils"
7 | import { BaiduResponse, getBaiduSource } from "./baidu"
8 | import { capitalize } from "./util"
9 |
10 | // 翻译结果 Map 缓存,将在 deactive 的时候清除
11 | export const TranslationResultMap = new Map()
12 |
13 | // 支持的所有国际化目标语言
14 | export enum SupportLanguage
15 | {
16 | ZH = 'zh', // 中文
17 | EN = 'en', // 英语
18 | CHT = 'cht', // 繁体中文
19 | JP = 'jp', // 日文
20 | }
21 |
22 | export default class TranslateManager
23 | {
24 | private static _instance: TranslateManager
25 |
26 | // 本地语言
27 | private _localLanguage: SupportLanguage
28 |
29 | // 目标国际语言
30 | private _intlLanguages: SupportLanguage[]
31 |
32 | // 用于生成 intl id 的英文文本
33 | // 来源可能为:
34 | // 1. 本地语言即为英文
35 | // 2. 国际语言中含有英文,我们在翻译结果中取值
36 | // 3. 以上情况都不成立,我们会自行翻译一次英文
37 | private _intlIdENText: string
38 |
39 | static getInstance()
40 | {
41 | if (!this._instance)
42 | {
43 | this._instance = new TranslateManager()
44 | }
45 | return this._instance
46 | }
47 |
48 | protected constructor()
49 | {
50 | const localLanguage = ConfigManager.getInstance().getConfig(LinterConfigItem.localLanguage)
51 |
52 | const intlLanguages = ConfigManager.getInstance().getConfig(LinterConfigItem.intlLanguage)
53 |
54 | if (intlLanguages.includes(localLanguage as any)) throw new Error('目标语言不能与本地语言相同,请检查您的配置文件')
55 |
56 | this._localLanguage = localLanguage
57 |
58 | this._intlLanguages = [...new Set(intlLanguages)]
59 |
60 | this._intlIdENText = ''
61 | }
62 |
63 | /**
64 | * 根据目标国际化语言生成翻译链接数组
65 | *
66 | * @private
67 | * @param {string} query - 目标文本
68 | * @return {*} {string[]} - 翻译 api 数组
69 | * @memberof TranslateManager
70 | */
71 | private getTranslateSourceQueue(query: string): string[]
72 | {
73 | return this._intlLanguages.map(il => getBaiduSource(query, this._localLanguage, il))
74 | }
75 |
76 | /**
77 | * 获取所有 本地语言到国际化语言 的翻译结果
78 | *
79 | * @param {string} query - 本地目标文本
80 | * @return {*} {Promise}
81 | * @memberof TranslateManager
82 | */
83 | public async getTranslationResult(query: string): Promise
84 | {
85 | if (!query) return []
86 |
87 | let enSource = ''
88 |
89 | // 获取 intl id 的英文文本
90 | if (this._localLanguage === SupportLanguage.EN)
91 | {
92 | this._intlIdENText = query
93 | }
94 | else if (!this._intlLanguages.includes(SupportLanguage.EN))
95 | {
96 | console.log('we fetch en text manually')
97 | enSource = getBaiduSource(query, this._localLanguage, SupportLanguage.EN)
98 | }
99 |
100 | const sources = this.getTranslateSourceQueue(query)
101 |
102 | const axiosRes = await Promise.all([
103 | ...sources.map(source => axios.get(source)),
104 | ...(enSource ? [axios.get(enSource)] : []),
105 | ])
106 |
107 | const result = axiosRes.map(res =>
108 | {
109 | const {
110 | to,
111 | trans_result,
112 | error_code,
113 | } = res.data
114 |
115 | if (res.data.error_code) throw new Error('翻译错误码' + error_code)
116 |
117 | const dst = trans_result[0] ? trans_result[0].dst : ''
118 |
119 | if (to === SupportLanguage.EN) this._intlIdENText = dst
120 |
121 | return capitalize(dst)
122 | })
123 |
124 | // 如果我们人工获取了英文文本,则需要把它从结果数组里面去除
125 | enSource ? result.pop() : result
126 |
127 | return result
128 |
129 | }
130 |
131 | public getIntlId(): string
132 | {
133 | return getCleanIntlId(this._intlIdENText)
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/client/src/lib/translate/baidu.ts:
--------------------------------------------------------------------------------
1 | import { URLSearchParams } from 'url';
2 | import { v4 as uuidv4 } from 'uuid';
3 | import md5 from 'blueimp-md5';
4 | import { SupportLanguage } from './TranslateManager';
5 |
6 | export interface BaiduResponse
7 | {
8 | from: string;
9 | to: string;
10 | trans_result: Array<{ src: string; dst: string }>;
11 | error_code?: number;
12 | }
13 |
14 | /**
15 | * this module is used to get baidu translation resource
16 | * including sentence translation and the dictionary resource
17 | *
18 | * @param {string} query - 查询字符串
19 | * @param {boolean} [isChinese] - 查询字符串是否为中文
20 | * @return {*} string - 请求链接
21 | */
22 | export const getBaiduSource = (query: string, from: SupportLanguage, to: SupportLanguage): string =>
23 | {
24 | const q = query.toLocaleLowerCase();
25 | const appid = '20191014000341469';
26 | const key = '4GEOkedoeuLlSiwypAoD';
27 | const baidu = 'http://api.fanyi.baidu.com/api/trans/vip/translate';
28 | // const from = 'auto';
29 | // const to = isChinese ? 'en' : 'zh'; // to can't be auto
30 | const salt = uuidv4(); // UUID
31 | const str = appid + q + salt + key;
32 | const dict = '1'
33 |
34 | // 获取 md5 加密后的 sign
35 | const sign = md5(str);
36 |
37 | // 构建参数
38 | // 这里也会做 URL encode 操作
39 | const search = new URLSearchParams({
40 | q,
41 | from,
42 | to,
43 | appid,
44 | salt,
45 | sign,
46 | dict,
47 | }).toString();
48 |
49 | const source = `${baidu}?${search}`;
50 |
51 | return source;
52 | };
53 |
--------------------------------------------------------------------------------
/client/src/lib/translate/util.ts:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * 讲英文字符串第一个字符转为大写
4 | *
5 | * @param {string} string
6 | * @return {*}
7 | */
8 | export const capitalize = (string: string) =>
9 | {
10 | return string.charAt(0).toUpperCase() + string.slice(1);
11 | }
12 |
--------------------------------------------------------------------------------
/client/src/lib/ui/VSCodeUIManager.ts:
--------------------------------------------------------------------------------
1 | import { window as Window, ProgressLocation, QuickPickItem } from 'vscode'
2 | import { CUSTOM_INPUT_PLACEHOLDER, CUSTOM_INTL_ID_REGX, CUSTOM_PICK_OPTION, CUSTOM_PICK_OPTION_DESC, CUSTOM_PICK_PLACEHOLDER, INVALID_CUSTOM_ID_MESSAGE } from '../constant';
3 | import { getIntlIdCount, getIntlMessage } from '../intl/utils';
4 |
5 | import TranslateManager, { TranslationResultMap } from "../translate/TranslateManager";
6 | import { SpecialStringParams } from '../util/validator';
7 |
8 | // 此单例包含所有和 VSCode UI 相关的操作
9 | export default class VSCodeUIManager
10 | {
11 | private static _instance: VSCodeUIManager;
12 |
13 | static getInstance()
14 | {
15 | if (!this._instance)
16 | {
17 | this._instance = new VSCodeUIManager()
18 | }
19 | return this._instance
20 | }
21 |
22 | /**
23 | * 获取文本翻译结果 - 通过 进度弹框 的形式
24 | *
25 | * @param {string} searchText
26 | * @return {*} {Promise}
27 | */
28 | public async getTranslateResultsWithProgress(searchText: string): Promise
29 | {
30 | // 是否有缓存
31 | const cacheResult = TranslationResultMap.get(searchText)
32 | if (Array.isArray(cacheResult)) return cacheResult
33 |
34 | try
35 | {
36 | return await Window.withProgress({
37 | cancellable: false,
38 | location: ProgressLocation.Notification,
39 | title: `正在获取“${searchText}”的翻译结果...`,
40 | }, async () =>
41 | {
42 | // 获取供选择的 Options
43 | // 这是最终的返回结果
44 | const result = await TranslateManager.getInstance().getTranslationResult(searchText)
45 | Array.isArray(result) && result.length && TranslationResultMap.set(searchText, result)
46 | return result
47 | })
48 | }
49 | catch (e: any)
50 | {
51 | Window.showErrorMessage('文本翻译错误', e)
52 | throw e
53 | }
54 | }
55 |
56 | /**
57 | * 通过 pick 选择想要的 intl.formatMessage({ id: ... }) 的 id 文本
58 | *
59 | * @param {string[]} translateResults - 翻译结果数组
60 | * @param {Record} [intlConfig] - 国际化配置(传本地国际化配置即可)
61 | * @return {*} {(Promise<[string | undefined, string | undefined]>)}
62 | */
63 | public async getIntlIdWithQuickPick(
64 | translateResults: string[],
65 | intlConfig?: Record,
66 | ): Promise
67 | {
68 | const labelDetailSuffix = (translateResults.length > 1 ? '等' : '')
69 |
70 | // 包含描述信息和细节的 quick pick
71 | // 参考 https://stackoverflow.com/questions/62312943/vscode-use-quickpick-list-items-with-description
72 | const quickPickOptions: QuickPickItem = {
73 | label: TranslateManager.getInstance().getIntlId(),
74 | detail: `对应翻译结果:${(translateResults[0] + labelDetailSuffix) || '无'}`
75 | }
76 |
77 | // 自定义选项
78 | const customPickOption: QuickPickItem = { label: CUSTOM_PICK_OPTION, description: CUSTOM_PICK_OPTION_DESC }
79 |
80 | const pickedIntlOption = await Window.showQuickPick([quickPickOptions, customPickOption], { placeHolder: CUSTOM_PICK_PLACEHOLDER })
81 |
82 | // 选中的 option 对应的 intl id
83 | let intlId = pickedIntlOption?.label
84 |
85 | // 用户选择的 id 有可能已存在,我们会给它加上数字用以区分
86 | if (intlConfig && intlId && (intlId in intlConfig) && intlId !== CUSTOM_PICK_OPTION)
87 | {
88 | let idCount = 1
89 | while (`${intlId}_${getIntlIdCount(idCount)}` in intlConfig)
90 | {
91 | idCount++
92 | }
93 | intlId = `${intlId}_${getIntlIdCount(idCount)}`
94 | }
95 |
96 | return intlId
97 | }
98 |
99 | /**
100 | * 将选择后的国际化代码附加到 onExecuteCommand 参数中返回
101 | *
102 | * @param {any[]} args - executeCommand 参数
103 | * @param {string} [selectedIntlId] - 选择的国际化 id
104 | * @param {SpecialStringParams[]} [specialStringParams] - 若该字符为包含参数的特殊字符,这个参数表示参数数组
105 | * @return {*} {(Promise<{
106 | * newArgs?: any[] | undefined;
107 | * customIntlId?: string;
108 | * }>)} - 处理后的新的参数和可选的自定义 id
109 | */
110 | public async processArgsWithSelectResult(
111 | args: any[],
112 | selectedIntlId?: string,
113 | specialStringParams?: SpecialStringParams[],
114 | ): Promise<{
115 | newArgs?: any[] | undefined;
116 | customIntlId?: string;
117 | }>
118 | {
119 | // 选择国际化代码内容
120 | switch (selectedIntlId)
121 | {
122 | case undefined:
123 | return {}
124 | case CUSTOM_PICK_OPTION:
125 | {
126 | // 自定义国际化代码(不是自定义翻译结果)
127 | const inputBoxContent = await Window.showInputBox({
128 | placeHolder: CUSTOM_INPUT_PLACEHOLDER,
129 | validateInput: (val: string) => CUSTOM_INTL_ID_REGX.test(val) ? undefined : INVALID_CUSTOM_ID_MESSAGE
130 | })
131 | if (!inputBoxContent) return {}
132 |
133 | return {
134 | newArgs: [...args, getIntlMessage(inputBoxContent, specialStringParams)],
135 | customIntlId: inputBoxContent
136 | }
137 | }
138 | default:
139 | return {
140 | newArgs: [...args, getIntlMessage(selectedIntlId, specialStringParams)],
141 | }
142 | }
143 | }
144 |
145 | }
--------------------------------------------------------------------------------
/client/src/lib/util/index.ts:
--------------------------------------------------------------------------------
1 | import path = require("path");
2 | import { Uri } from "vscode";
3 | import { DocumentSelector } from 'vscode-languageclient'
4 |
5 | import { ActivationLanguage } from '../constant'
6 |
7 | /**
8 | * 获取工作区下国际化配置文件 json 路径
9 | *
10 | * @param {Uri} workspaceIntlConfigPath 工作区根目录
11 | * @param {string} jsonPath 国际化的 json 路径
12 | * @return {*} {Uri}
13 | */
14 | export const getWorkspaceIntlJsonPath = (workspaceIntlConfigPath: Uri, jsonPath: string): Uri =>
15 | {
16 | return Uri.joinPath(workspaceIntlConfigPath, path.join('/' + jsonPath))
17 | }
18 |
19 | /**
20 | * 获取 package.json 文件配置的激活事件 activationEvents 对应的语言
21 | *
22 | * @return {*} {DocumentSelector} - 客户端支持的语言
23 | */
24 | export const getDocumentSelector = (): DocumentSelector =>
25 | {
26 | const languagePrefix = /onLanguage:/
27 | const activationEvents = ActivationLanguage
28 | return activationEvents.reduce((prevLangs: DocumentSelector, curAction: string) =>
29 | {
30 | if (!languagePrefix.test(curAction)) return prevLangs
31 | const language = curAction.replace(languagePrefix, '')
32 | return language ? prevLangs.concat({ scheme: 'file', language: language }) : prevLangs
33 | }, [])
34 | };
35 |
--------------------------------------------------------------------------------
/client/src/lib/util/validator.ts:
--------------------------------------------------------------------------------
1 | import { snakeCase } from 'snake-case'
2 |
3 |
4 | // 特殊字符串参数对象
5 | // 例如:'react-intl=你好,{name: Fred 哥}' 中的 {name: 'Fred 哥'}
6 | export type SpecialStringParams = { [key: string]: string; }
7 |
8 | // 特殊字符串里面的参数
9 | export const ParamsRegx = /\{\s*([A-Za-z_]+):\s*([^\}\n]+)\s*\}/gu
10 |
11 | /**
12 | * 获取格式化后的查询字符串和参数
13 | *
14 | * @param {string} target
15 | * @return {*} {([string, SpecialStringParams[]] | undefined)}
16 | */
17 | export const validateSpecialString = (target: string): [string, SpecialStringParams[]] =>
18 | {
19 | const params = formatSpecialStringParams(target)
20 | const searchText = formatSearchText(target)
21 |
22 | return [searchText, params]
23 | }
24 |
25 | /**
26 | * 获取特殊字符串的参数数组
27 | *
28 | * @param {string} target 特殊字符串中的文本内容
29 | * @return {*} {SpecialStringParams[]}
30 | */
31 | const formatSpecialStringParams = (target: string): SpecialStringParams[] =>
32 | {
33 |
34 | let match: RegExpExecArray | null
35 |
36 | const params: SpecialStringParams[] = []
37 |
38 | while ((match = ParamsRegx.exec(target)))
39 | {
40 | // 百度翻译会将 rawMessage -> rawmessage
41 | // 所以我们将参数名统一转为下划线命名
42 | const key = snakeCase(match[1])
43 | const value = match[2]
44 |
45 | if (params.find(p => key in p))
46 | {
47 | throw new Error(`参数的 key 值(${key})只唯一!`)
48 | }
49 |
50 | params.push({ [key]: value })
51 | }
52 |
53 | return params
54 | }
55 |
56 |
57 |
58 | /**
59 | * 获取特殊字符串中要拿去翻译的文本
60 | * 例如:'你好,{name: Fred 哥}' -> '你好,{name}'
61 | *
62 | * @param {string} target
63 | * @return {*} {string}
64 | */
65 | const formatSearchText = (target: string): string =>
66 | {
67 | // 百度翻译会将 rawMessage -> rawmessage
68 | // 所以我们将翻译文本统一转为下划线命名
69 | return target.replace(ParamsRegx, (_, p1) => `{${snakeCase(p1)}}`)
70 | }
71 |
72 |
73 | /**
74 | * 将特殊字符串内的参数对象转化为代码字符串
75 | *
76 | * @param {SpecialStringParams[]} params 参数对象数组
77 | * @return {*} {string} 合并后的字符串,例如 "{name: '约翰', title: '老师'}"
78 | */
79 | export const specialStringParams2String = (params: SpecialStringParams[]): string =>
80 | {
81 | const sourceObj = params.reduce((accObj, curParams: SpecialStringParams) =>
82 | {
83 | return { ...accObj, ...curParams }
84 | }, {})
85 |
86 | const targetStr = Object.keys(sourceObj)
87 | .map(key => `${key}: ${sourceObj[key]}`)
88 | .join(', ')
89 |
90 | return `{ ${targetStr} }`
91 | }
--------------------------------------------------------------------------------
/client/src/middleware/disable-file.ts:
--------------------------------------------------------------------------------
1 | import { ExecuteCommandSignature } from "vscode-languageclient"
2 |
3 | import { LinterCommands } from "../lib/constant"
4 |
5 | const DisableFileMiddleware = (args: any[], next: ExecuteCommandSignature) =>
6 | {
7 | next(LinterCommands.DisableFile, args)
8 | }
9 |
10 | export default DisableFileMiddleware
--------------------------------------------------------------------------------
/client/src/middleware/disable-line.ts:
--------------------------------------------------------------------------------
1 | import { ExecuteCommandSignature } from "vscode-languageclient"
2 |
3 | import { LinterCommands } from "../lib/constant"
4 |
5 | const DisableLineMiddleware = (args: any[], next: ExecuteCommandSignature) =>
6 | {
7 | next(LinterCommands.DisableLine, args)
8 | }
9 |
10 | export default DisableLineMiddleware
--------------------------------------------------------------------------------
/client/src/middleware/extract.ts:
--------------------------------------------------------------------------------
1 | import { Uri, window as Window } from 'vscode'
2 | import { ExecuteCommandSignature } from "vscode-languageclient"
3 |
4 | import { LinterCommands } from "../lib/constant"
5 | import IntlConfigManager from '../lib/intl/IntlConfigManager'
6 | import { getIntlMessage } from '../lib/intl/utils'
7 | import VSCodeUIManager from '../lib/ui/VSCodeUIManager'
8 | import { validateSpecialString } from '../lib/util/validator'
9 |
10 | /**
11 | * 命令中间件 - 执行语言服务器发出的抽取中文文本为 react-intl 代码命令
12 | *
13 | * @param {string} intlConfigTemp react-intl 国际化模版配置路径
14 | * @param {(Uri | undefined)} workspaceIntlConfigPath 工作区 react-intl 国际化配置路径
15 | * @param {any[]} args 语言服务器发出该命令时给的参数
16 | * @param {ExecuteCommandSignature} next next 调用语言服务器的 onExecuteCommand 函数
17 | * @return {*}
18 | */
19 | const ExtractMiddleware = async (intlConfigTemp: string, workspaceIntlConfigPath: Uri | undefined, args: any[], next: ExecuteCommandSignature) =>
20 | {
21 | const rawText = args[1] as string
22 |
23 | if (!rawText || !workspaceIntlConfigPath) return
24 |
25 | // 想要替换的中文文本
26 | const [searchText, specialStringParams] = validateSpecialString(rawText)
27 |
28 | // 初始化工作区国际化配置文件
29 | await IntlConfigManager.getInstance().initializeIntlConfig(workspaceIntlConfigPath, intlConfigTemp)
30 |
31 | // 获取已有所有配置文件
32 | const oldConfigs = await IntlConfigManager.getInstance().readAllConfig()
33 |
34 | // 查找工作区是否已存在对应中文文本配置
35 | const intlId = IntlConfigManager.getInstance().getExistingIntlId(searchText, oldConfigs)
36 |
37 | // 工作区已存在对应配置
38 | if (intlId)
39 | {
40 | // 传给语言服务器的 onExecuteCommand 函数
41 | return next(LinterCommands.Extract, [...args, getIntlMessage(intlId, specialStringParams)])
42 | }
43 |
44 | // 翻译结果相关进度条
45 | const translateResults = await VSCodeUIManager.getInstance().getTranslateResultsWithProgress(searchText)
46 |
47 | // picker 选择结果
48 | const selectedIntlId = await VSCodeUIManager.getInstance().getIntlIdWithQuickPick(translateResults, oldConfigs[0]);
49 |
50 | // 获得处理后的参数,用于传给语言服务器的 onExecuteCommand 函数
51 | const { newArgs, customIntlId } = await VSCodeUIManager.getInstance().processArgsWithSelectResult(args, selectedIntlId, specialStringParams)
52 |
53 | if (!newArgs || !selectedIntlId || !translateResults.length) return
54 |
55 | try
56 | {
57 | // 替换文本操作可以首先实行减少用户对延迟的感知
58 | // 文件写入错误和文本替换并不冲突,只不过需要用户重新执行一遍替换操作来执行文件写入或手动写入文件
59 | next(LinterCommands.Extract, newArgs)
60 |
61 | // 写入配置文件,包括将搜索文本写入本地配置文件(这个操作是费时的)
62 | await IntlConfigManager.getInstance().writeConfigIntoWorkSpace(
63 | customIntlId || selectedIntlId,
64 | [searchText, ...translateResults],
65 | oldConfigs
66 | )
67 | }
68 | catch (e)
69 | {
70 | Window.showErrorMessage('写入国际化文件发生错误:', String(e).toString())
71 | console.log('write config failed', e)
72 | }
73 | }
74 |
75 | export default ExtractMiddleware
--------------------------------------------------------------------------------
/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "target": "es2017",
6 | "outDir": "out",
7 | "rootDir": "src",
8 | "lib": [ "es2017" ],
9 | "sourceMap": true,
10 | "esModuleInterop": true,
11 | "resolveJsonModule": true,
12 | },
13 | "include": [
14 | "src"
15 | ],
16 | "exclude": [
17 | "node_modules",
18 | "package-lock.json",
19 | "package.json"
20 | ]
21 | }
--------------------------------------------------------------------------------
/client/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@types/blueimp-md5@^2.18.0":
6 | version "2.18.0"
7 | resolved "https://registry.yarnpkg.com/@types/blueimp-md5/-/blueimp-md5-2.18.0.tgz#a5c44fa0a61f5840e95a7965eafcec2c30d6c4df"
8 | integrity sha512-f4A+++lGZGJvVSgeyMkqA7BEf2BVQli6F+qEykKb49c5ieWQBkfpn6CP5c1IZr2Yi2Ofl6Fj+v0e1fN18Z8Cnw==
9 |
10 | "@types/uuid@^8.3.1":
11 | version "8.3.1"
12 | resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f"
13 | integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==
14 |
15 | "@types/vscode@1.52.0":
16 | version "1.52.0"
17 | resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.52.0.tgz#61917968dd403932127fc4004a21fd8d69e4f61c"
18 | integrity sha512-Kt3bvWzAvvF/WH9YEcrCICDp0Z7aHhJGhLJ1BxeyNP6yRjonWqWnAIh35/pXAjswAnWOABrYlF7SwXR9+1nnLA==
19 |
20 | axios@^0.21.4:
21 | version "0.21.4"
22 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
23 | integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
24 | dependencies:
25 | follow-redirects "^1.14.0"
26 |
27 | balanced-match@^1.0.0:
28 | version "1.0.2"
29 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
30 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
31 |
32 | blueimp-md5@^2.18.0:
33 | version "2.18.0"
34 | resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.18.0.tgz#1152be1335f0c6b3911ed9e36db54f3e6ac52935"
35 | integrity sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q==
36 |
37 | brace-expansion@^1.1.7:
38 | version "1.1.11"
39 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
40 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
41 | dependencies:
42 | balanced-match "^1.0.0"
43 | concat-map "0.0.1"
44 |
45 | concat-map@0.0.1:
46 | version "0.0.1"
47 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
48 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
49 |
50 | dot-case@^3.0.4:
51 | version "3.0.4"
52 | resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
53 | integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
54 | dependencies:
55 | no-case "^3.0.4"
56 | tslib "^2.0.3"
57 |
58 | follow-redirects@^1.14.0:
59 | version "1.14.3"
60 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e"
61 | integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==
62 |
63 | lower-case@^2.0.2:
64 | version "2.0.2"
65 | resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
66 | integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
67 | dependencies:
68 | tslib "^2.0.3"
69 |
70 | lru-cache@^6.0.0:
71 | version "6.0.0"
72 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
73 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
74 | dependencies:
75 | yallist "^4.0.0"
76 |
77 | minimatch@^3.0.4:
78 | version "3.0.4"
79 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
80 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
81 | dependencies:
82 | brace-expansion "^1.1.7"
83 |
84 | no-case@^3.0.4:
85 | version "3.0.4"
86 | resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
87 | integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
88 | dependencies:
89 | lower-case "^2.0.2"
90 | tslib "^2.0.3"
91 |
92 | semver@^7.3.4:
93 | version "7.3.5"
94 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
95 | integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
96 | dependencies:
97 | lru-cache "^6.0.0"
98 |
99 | snake-case@^3.0.4:
100 | version "3.0.4"
101 | resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
102 | integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
103 | dependencies:
104 | dot-case "^3.0.4"
105 | tslib "^2.0.3"
106 |
107 | tslib@^2.0.3:
108 | version "2.3.1"
109 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
110 | integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
111 |
112 | uuid@^8.3.2:
113 | version "8.3.2"
114 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
115 | integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
116 |
117 | vscode-jsonrpc@6.0.0:
118 | version "6.0.0"
119 | resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
120 | integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
121 |
122 | vscode-languageclient@^7.0.0:
123 | version "7.0.0"
124 | resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2"
125 | integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==
126 | dependencies:
127 | minimatch "^3.0.4"
128 | semver "^7.3.4"
129 | vscode-languageserver-protocol "3.16.0"
130 |
131 | vscode-languageserver-protocol@3.16.0:
132 | version "3.16.0"
133 | resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
134 | integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
135 | dependencies:
136 | vscode-jsonrpc "6.0.0"
137 | vscode-languageserver-types "3.16.0"
138 |
139 | vscode-languageserver-types@3.16.0:
140 | version "3.16.0"
141 | resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
142 | integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
143 |
144 | yallist@^4.0.0:
145 | version "4.0.0"
146 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
147 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
148 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-intl-linter",
3 | "version": "2.0.1",
4 | "publisher": "styx11",
5 | "description": "vscode linter extension for react-intl",
6 | "repository": "git@github.com:Styx11/react-intl-linter.git",
7 | "author": "styx11 <1654630248@qq.com>",
8 | "license": "Apache-2.0 License",
9 | "private": true,
10 | "categories": [],
11 | "engines": {
12 | "vscode": "^1.52.0"
13 | },
14 | "icon": "react-intl-linter.icon.png",
15 | "activationEvents": [
16 | "onLanguage:typescript",
17 | "onLanguage:typescriptreact",
18 | "onLanguage:javascriptreact",
19 | "onLanguage:javascript",
20 | "onLanguage:html",
21 | "onLanguage:vue"
22 | ],
23 | "contributes": {
24 | "configurationDefaults": {
25 | "[json]": {
26 | "editor.insertSpaces": true,
27 | "editor.tabSize": 4,
28 | "editor.autoIndent": "none"
29 | }
30 | },
31 | "configuration": {
32 | "type": "object",
33 | "title": "ReactIntlLinter",
34 | "properties": {
35 | "reactIntlLinter.localLanguage": {
36 | "type": "string",
37 | "default": "zh",
38 | "enum": [
39 | "zh",
40 | "en",
41 | "jp",
42 | "cht"
43 | ],
44 | "enumDescriptions": [
45 | "中文",
46 | "英文",
47 | "日文",
48 | "繁体中文"
49 | ],
50 | "markdownDescription": "本地语言,即代码中使用的本地语言"
51 | },
52 | "reactIntlLinter.localLanguageConfigName": {
53 | "type": "string",
54 | "default": "zh_CN",
55 | "markdownDescription": "本地语言配置文件名(不包括后缀)"
56 | },
57 | "reactIntlLinter.intlLanguage": {
58 | "type": "array",
59 | "default": ["en"],
60 | "items": {
61 | "type": "string",
62 | "enum": [
63 | "zh",
64 | "en",
65 | "jp",
66 | "cht"
67 | ],
68 | "enumDescriptions": [
69 | "中文",
70 | "英文",
71 | "日文",
72 | "繁体中文"
73 | ]
74 | },
75 | "markdownDescription": "国际化语言,即支持的 react-intl 国际化目标语言"
76 | },
77 | "reactIntlLinter.intlLanguageConfigName": {
78 | "type": "array",
79 | "default": ["en_US"],
80 | "items": {
81 | "type": "string"
82 | },
83 | "markdownDescription": "国际化语言配置文件名数组(不包括后缀,**必须与国际化语言数组配置一一对应**)"
84 | },
85 | "reactIntlLinter.intlConfigPath": {
86 | "type": "string",
87 | "default": "src/intl",
88 | "markdownDescription": "国际化配置文件夹路径名,例如 `src/intl`(相对于工作区跟路径)"
89 | },
90 | "reactIntlLinter.intlCode": {
91 | "type": "string",
92 | "default": "react-intl",
93 | "enum": [
94 | "react-intl",
95 | "vue-i18n"
96 | ],
97 | "markdownDescription": "期望转换的国际化框架,支持 `react-intl`,`vue-i18n`。\n`react-intl` 对应代码为 `intl.formatMessage({id: ...})` ,`vueI18n` 对应代码为 `$t('id')`"
98 | }
99 | }
100 | }
101 | },
102 | "capabilities": {
103 | "codeActionProvider": true
104 | },
105 | "main": "client/out/extension",
106 | "scripts": {
107 | "vscode:prepublish": "yarn compile",
108 | "compile": "rimraf server/out && rimraf client/out && tsc -b",
109 | "watch": "tsc -b -w",
110 | "lint": "npm run lint:client && npm run lint:server",
111 | "lint:client": "eslint --config ./client/.eslintrc.json ./client/src/*.ts",
112 | "lint:server": "eslint --config ./server/.eslintrc.json ./server/src/*.ts",
113 | "clean": "rimraf client/out && rimraf server/out",
114 | "postinstall": "cd client && npm install && cd ../server && npm install && cd .."
115 | },
116 | "devDependencies": {
117 | "@types/node": "^14.17.8",
118 | "@typescript-eslint/parser": "^4.29.0",
119 | "eslint": "^7.32.0",
120 | "rimraf": "^3.0.2",
121 | "typescript": "^4.3.5"
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/react-intl-linter-2.0.1.vsix:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Styx11/react-intl-linter/42aecdea39e1a656117bd60ee521e6d693327377/react-intl-linter-2.0.1.vsix
--------------------------------------------------------------------------------
/react-intl-linter.icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Styx11/react-intl-linter/42aecdea39e1a656117bd60ee521e6d693327377/react-intl-linter.icon.png
--------------------------------------------------------------------------------
/server/.eslintignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
--------------------------------------------------------------------------------
/server/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'is-chinese'
--------------------------------------------------------------------------------
/server/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "0.0.4",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "is-chinese": {
8 | "version": "1.4.6",
9 | "resolved": "https://registry.npmjs.org/is-chinese/-/is-chinese-1.4.6.tgz",
10 | "integrity": "sha512-ofuscVRi2kkSGe60EKbwlnrx+RFhln6vW1Ya98/87dmO60iLqYnWWk8X7Veb3k3TN0X8MddNj0h2xam4qyf2sw=="
11 | },
12 | "vscode-jsonrpc": {
13 | "version": "6.0.0",
14 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
15 | "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg=="
16 | },
17 | "vscode-languageserver": {
18 | "version": "7.0.0",
19 | "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz",
20 | "integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==",
21 | "requires": {
22 | "vscode-languageserver-protocol": "3.16.0"
23 | }
24 | },
25 | "vscode-languageserver-protocol": {
26 | "version": "3.16.0",
27 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz",
28 | "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==",
29 | "requires": {
30 | "vscode-jsonrpc": "6.0.0",
31 | "vscode-languageserver-types": "3.16.0"
32 | }
33 | },
34 | "vscode-languageserver-textdocument": {
35 | "version": "1.0.1",
36 | "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz",
37 | "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA=="
38 | },
39 | "vscode-languageserver-types": {
40 | "version": "3.16.0",
41 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
42 | "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA=="
43 | },
44 | "vscode-uri": {
45 | "version": "3.0.2",
46 | "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.2.tgz",
47 | "integrity": "sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA=="
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "2.0.1",
4 | "description": "vscode linter extension for react-intl",
5 | "repository": "git@github.com:Styx11/react-intl-linter.git",
6 | "author": "styx11 <1654630248@qq.com>",
7 | "license": "Apache-2.0 License",
8 | "private": true,
9 | "bugs": {
10 | "url": "https://github.com/Styx11/react-intl-linter/issues"
11 | },
12 | "engines": {
13 | "node": "*"
14 | },
15 | "dependencies": {
16 | "is-chinese": "^1.4.6",
17 | "vscode-languageserver": "^7.0.0",
18 | "vscode-languageserver-textdocument": "^1.0.1",
19 | "vscode-uri": "^3.0.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/server/src/lib/command/index.ts:
--------------------------------------------------------------------------------
1 | import { Diagnostic, ExecuteCommandParams, TextDocumentEdit, TextEdit, _, _Connection } from "vscode-languageserver";
2 | import { TextDocument } from "vscode-languageserver-textdocument";
3 |
4 | import { EnableComment } from "../comment/rules";
5 |
6 | const LINE_BREAK = '\n'
7 |
8 | // 从语言服务器发出的要执行的插件命令
9 | export enum LinterCommands
10 | {
11 | Extract = 'react-intl-linter.extract', // 抽取中文字符串为 react-intl 代码
12 | DisableLine = 'react-intl-linter.disable-line', // 在中文代码前一行添加 ri-lint-disable-nextline 注释
13 | DisableFile = 'react-intl-linter.disable-file', // 在当前文件开始行添加 ri-lint-disable 注释
14 | }
15 |
16 | type CommandCallback = (
17 | cnt: _Connection<_, _, _, _, _, _, _>,
18 | doc: TextDocument,
19 | params: ExecuteCommandParams,
20 | diagnostic?: Diagnostic,
21 | ) => Promise
22 |
23 | // 命令回调函数
24 | export const ExecuteCommandMap: { [key in LinterCommands]: CommandCallback } = {
25 | [LinterCommands.Extract]: async (
26 | connection: _Connection<_, _, _, _, _, _, _>,
27 | document: TextDocument,
28 | params: ExecuteCommandParams,
29 | diagnostic?: Diagnostic,
30 | ) =>
31 | {
32 | if (!Array.isArray(params.arguments) || !params.arguments.length) return
33 |
34 | const newText = typeof params.arguments[2] === 'string' && params.arguments[2]
35 |
36 | if (!newText || !diagnostic) return
37 |
38 | // 替换中文为 intl 国际化代码
39 | await connection.workspace.applyEdit({
40 | documentChanges: [
41 | TextDocumentEdit.create({ uri: document.uri, version: document.version }, [
42 | TextEdit.replace(diagnostic.range, newText)
43 | ])
44 | ]
45 | })
46 | },
47 | [LinterCommands.DisableLine]: async (
48 | connection: _Connection<_, _, _, _, _, _, _>,
49 | document: TextDocument,
50 | _,
51 | diagnostic?: Diagnostic,
52 | ) =>
53 | {
54 | if (!diagnostic) return
55 |
56 | const rawText = document.getText()
57 | // 当前中文文本所在位置
58 | const diagnosticOffset = document.offsetAt(diagnostic.range.start)
59 | // 离当前文本最近的换行符
60 | const breakOffset = rawText.slice(0, diagnosticOffset).lastIndexOf(LINE_BREAK)
61 | // 当前文本行之前 tab 的空格(保持对齐)
62 | const tabSpace = rawText.slice(breakOffset + 1, diagnosticOffset).match(/\s*/)
63 | const customTabSpace = tabSpace ? tabSpace[0] : ''
64 |
65 | // 在当前中文行之前的一行加入 ri-lint-disable-next-line 注释,并保持锁进相同
66 | await connection.workspace.applyEdit({
67 | documentChanges: [
68 | TextDocumentEdit.create({ uri: document.uri, version: document.version }, [
69 | TextEdit.insert(document.positionAt(breakOffset), LINE_BREAK + customTabSpace + '// ' + EnableComment.DISABLE_NEXT_LINE)
70 | ])
71 | ]
72 | })
73 | },
74 | [LinterCommands.DisableFile]: async (
75 | connection: _Connection<_, _, _, _, _, _, _>,
76 | document: TextDocument,
77 | ) =>
78 | {
79 | // 在文件开头首行加入 ri-lint-disable 注释
80 | await connection.workspace.applyEdit({
81 | documentChanges: [
82 | TextDocumentEdit.create({ uri: document.uri, version: document.version }, [
83 | TextEdit.insert(document.positionAt(0), '// ' + EnableComment.DISABLE + LINE_BREAK)
84 | ])
85 | ]
86 | })
87 | },
88 | }
89 |
90 | export const isCommand = (command: string): command is LinterCommands =>
91 | {
92 | return command in ExecuteCommandMap
93 | }
94 |
95 | export const LinterCodeActionMessage: { [key in LinterCommands]: string } = {
96 | [LinterCommands.Extract]: '',
97 | [LinterCommands.DisableLine]: 'Disable react-intl-linter for this line',
98 | [LinterCommands.DisableFile]: 'Disable react-intl-linter for entire file',
99 | }
100 |
--------------------------------------------------------------------------------
/server/src/lib/comment/CommentManager.ts:
--------------------------------------------------------------------------------
1 | import { EnableComment, EnableCommentRules } from "./rules";
2 | import { getEnableComment } from "./utils";
3 |
4 |
5 | // 代码原始文本中的换行符
6 | const LINE_BREAK = '\n'
7 |
8 | interface CommentContent
9 | {
10 | comment: EnableComment;
11 | line: number;
12 | }
13 |
14 | interface DocumentInfo
15 | {
16 | // 文件原始文本(包括换行符)
17 | rawContent: string;
18 | // 规则相关注释信息数组
19 | commentContent: CommentContent[]
20 | }
21 |
22 | /**
23 | * 处理注释规则的单例 Singleton,其中因为每行文本以 \n 结尾,所以 \n 的数量即为代码的行数
24 | * 参考 issue https://github.com/Styx11/react-intl-linter/issues/10
25 | *
26 | * @class CommentManager
27 | */
28 | class CommentManager
29 | {
30 | private static _instance: CommentManager;
31 |
32 | // 文件 uri 对应 DocumentInfo 的 map
33 | private _uriContentMap: { [key: string]: DocumentInfo | undefined };
34 |
35 | public static getInstance()
36 | {
37 | if (!this._instance)
38 | {
39 | this._instance = new CommentManager()
40 | }
41 | return this._instance
42 | }
43 |
44 | protected constructor()
45 | {
46 | this._uriContentMap = {}
47 | }
48 |
49 | /**
50 | * 根据文档内容获取 DoucmentInfo
51 | *
52 | * @private
53 | * @param {string} content
54 | * @memberof CommentManager
55 | */
56 | private _getDocumentInfo = (content: string): DocumentInfo =>
57 | {
58 | const _commentContent = content
59 | .split(LINE_BREAK)
60 | .reduce((acc: CommentContent[], cur: string, idx: number) =>
61 | {
62 | const comment = getEnableComment(cur)
63 | return !comment ? acc : acc.concat({
64 | comment,
65 | line: idx,
66 | })
67 | }, [])
68 | return {
69 | rawContent: content,
70 | commentContent: _commentContent,
71 | }
72 | }
73 |
74 | /**
75 | * 设置文件 uri 对应文档信息
76 | *
77 | * @param {string} uri 文档 uri
78 | * @param {string} content 文档中的原始文本
79 | * @memberof CommentManager
80 | */
81 | public setUriDocument = (uri: string, content: string) =>
82 | {
83 | if (!this._uriContentMap[uri] || this._uriContentMap[uri]?.rawContent !== content)
84 | {
85 | this._uriContentMap[uri] = this._getDocumentInfo(content)
86 | }
87 | return this
88 | }
89 |
90 | /**
91 | * 找到目标句子当前行之前最近的、且在其作用域范围内的 Enable 注释(包括当前行,比如 ri-linter-enable-line)
92 | *
93 | * @private
94 | * @param {number} line 目标字符串行数
95 | * @return {CommentContent} 最近的 EnableComment 内容(包括行数)
96 | * @memberof CommentManager
97 | */
98 | private _findNearestEnablComment = (uri: string, line: number): CommentContent | undefined =>
99 | {
100 | const commentContent = this._uriContentMap[uri]?.commentContent
101 | if (!Array.isArray(commentContent) || !commentContent.length) return
102 |
103 | const nearestComment = commentContent
104 | .filter(content => line - content.line >= 0)
105 | .sort((a, b) => b.line - a.line)
106 | .find(comment => EnableCommentRules[comment.comment].checkAvailable(line, comment.line))
107 |
108 | return nearestComment
109 | }
110 |
111 |
112 | /**
113 | * 目标字符串在 Enable Comment 规则下是否需要抛出警告
114 | *
115 | * @param {string} uri 文档对象 uri
116 | * @param {string} targetLine 目标字符串所在行数
117 | * @return {boolean} linter 是否能够抛出警告
118 | * @memberof CommentManager
119 | */
120 | public checkEnableCommentRules = (uri: string, targetLine: number): boolean =>
121 | {
122 | const enableComment = this._findNearestEnablComment(uri, targetLine)
123 | const enableRule = enableComment ? EnableCommentRules[enableComment.comment].enable : true
124 |
125 | return enableRule
126 | }
127 | }
128 |
129 | export default CommentManager
130 |
--------------------------------------------------------------------------------
/server/src/lib/comment/rules.ts:
--------------------------------------------------------------------------------
1 |
2 | // 和是否启用 linter 警告相关的注释
3 | export enum EnableComment
4 | {
5 | ENABLE = 'ri-lint-enable', // 解析该注释之后的中文时,插件发出警告
6 | DISABLE = 'ri-lint-disable', // 解析该注释之后的中文时,插件不发出警告
7 | ENABLE_LINE = 'ri-lint-enable-line', // 解析该注释当前行的中文时,插件发出警告
8 | DISABLE_LINE = 'ri-lint-disable-line', // 解析该注释当前行的中文时,插件不发出警告
9 | ENABLE_NEXT_LINE = 'ri-lint-enable-next-line', // 解析该注释后下一行的中文时,插件发出警告
10 | DISABLE_NEXT_LINE = 'ri-lint-disable-next-line', // 解析该注释下一行的中文时,插件不发出警告
11 | }
12 |
13 | // Enable Comment 规则内容
14 | // sentLine: number - 句子所在行数
15 | // commentLine: number - 注释所在行数
16 | interface EnableRule
17 | {
18 | // 检查该规则是否可用(是否在该规则作用范围内)
19 | checkAvailable: (sentLine: number, commentLine: number) => boolean;
20 | // 解析某行中文时,是否能够抛出警告;
21 | enable: boolean;
22 | }
23 |
24 | // 和是否启用 linter 警告相关的规则函数
25 | export const EnableCommentRules: { [key in EnableComment]: EnableRule } = {
26 | [EnableComment.ENABLE]: {
27 | checkAvailable: (_sl: number, _cl: number) => _sl > _cl,
28 | enable: true,
29 | },
30 | [EnableComment.DISABLE]: {
31 | checkAvailable: (_sl: number, _cl: number) => _sl > _cl,
32 | enable: false,
33 | },
34 | [EnableComment.ENABLE_LINE]: {
35 | checkAvailable: (_sl: number, _cl: number) => _sl === _cl,
36 | enable: true,
37 | },
38 | [EnableComment.DISABLE_LINE]: {
39 | checkAvailable: (_sl: number, _cl: number) => _sl === _cl,
40 | enable: false,
41 | },
42 | [EnableComment.ENABLE_NEXT_LINE]: {
43 | checkAvailable: (_sl: number, _cl: number) => _sl - 1 === _cl,
44 | enable: true,
45 | },
46 | [EnableComment.DISABLE_NEXT_LINE]: {
47 | checkAvailable: (_sl: number, _cl: number) => _sl - 1 === _cl,
48 | enable: false,
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/server/src/lib/comment/utils.ts:
--------------------------------------------------------------------------------
1 | import { EnableComment, EnableCommentRules } from "./rules";
2 |
3 | // 匹配行注释
4 | const lineCommentRegex = /(?:^|\s)\/\/(.+?)$/gms
5 |
6 | // 匹配块注释
7 | const blockCommentRegex = /\/\*(.*?)\*\//gms;
8 |
9 | // 匹配行注释和块注释
10 | const commentRegex = new RegExp(`(?:${lineCommentRegex.source})|(?:${blockCommentRegex.source})`, 'gms');
11 |
12 |
13 | /**
14 | * 获取字符串中多个注释的内容
15 | *
16 | * @param {string} target 目标字符串
17 | * @return {*} {string[]} 注释内容,可有多个,比如块注释后跟一个行注释
18 | */
19 | const getCommentContent = (target: string): string[] =>
20 | {
21 | let match: RegExpExecArray | null;
22 | const contentList: string[] = []
23 |
24 | while ((match = commentRegex.exec(target)))
25 | {
26 | const content = (match[1] || match[2] || '').replace(/\*|\n/g, '').trim()
27 | // 倒序插入内容(同一行的多个注释以最后一个注释为准)
28 | content && contentList.unshift(content)
29 | }
30 |
31 | return contentList
32 | }
33 |
34 | /**
35 | * 获取字符串中最后一个内容为 Enable Comment 的注释内容
36 | *
37 | * @param {string} target
38 | * @return {*} {(EnableComment | undefined)}
39 | */
40 | export const getEnableComment = (target: string): EnableComment | undefined =>
41 | {
42 | const commentList = getCommentContent(target)
43 | return commentList.find(comment => comment in EnableCommentRules) as EnableComment | undefined
44 | }
45 |
--------------------------------------------------------------------------------
/server/src/lib/util.ts:
--------------------------------------------------------------------------------
1 |
2 | // 匹配单引号/双引号内的字符串文本
3 | // 单个字符 . 的匹配默认是贪婪的,所以当有多个带引号的句子在一行时,匹配结果就会是从第一个引号开始到最后一个引号结束,所以要用 [^'\n]+ 和 [^"\n]+ 去匹配
4 | // 例如 const message = `${'添加'}hellllllo${'添加'}` 会匹配为 添加'}hellllllo${'添加
5 | export const StringRegx = /'([^'\n]*)'|"([^"\n]*)"/gu
6 |
7 | // 标识特殊字符串前缀,表示含有特殊字符需要 react-intl-linter 识别 'react-intl=你好,{name: 约翰}' 或 '$=你好,{name: 约翰}'
8 | export const SpecialStringRegx = /^(?:react-intl|\$)=([^\n]+)/u
9 |
10 | // 特殊字符串里面的非法参数,若有一个非法参数则不抛出 codeAction
11 | export const inValidParamsRegx = /\{\s*([A-Za-z_]+):\s*\}/u
12 |
13 | // 错误信息
14 | export const DiagnosticMessage = ' can be transferred to intl code'
15 |
16 | // debounce 文本更新回调时间 ms
17 | export const ContentChangeDelay = 600
18 |
19 | export const ExtensionSource = 'react-intl-linter'
20 |
21 | /**
22 | * 获取 CodeAction 信息,包含引号之间的中文文本
23 | *
24 | * @param {string} message
25 | * @return {*} {[string, string]} - 【codeAction 信息,引号之间的中文文本】
26 | */
27 | export const getCodeActionMessage = (message: string): [string, string] =>
28 | {
29 | const rawMessage = message.replace(DiagnosticMessage, '')
30 | const codeActionMessage = `抽取 "${rawMessage}" 为 react-intl 国际化内容`
31 |
32 | return [codeActionMessage, rawMessage]
33 | }
34 |
35 | /**
36 | * 获取 end 位置开头的字符串在 target 字符串中的第几行(行数从零开始)
37 | *
38 | * @param {string} target 原始字符串
39 | * @param {number} [end] 目标字符串开始位置
40 | * @return {*} {number} 目标字符串在原始字符串上的行数
41 | */
42 | export const getLineCount = (target: string, end?: number): number =>
43 | {
44 | return target.slice(0, end).split('\n').length - 1
45 | }
46 |
47 | /**
48 | * debounce for fn
49 | * @param fn target function
50 | * @param wait wait millisecond
51 | */
52 | // tslint:disable-next-line: ban-types
53 | export const debounce = (fn: Function, wait: number): ((...args: any[]) => void) =>
54 | {
55 | let timer: any = 0
56 | return (...args: any[]) =>
57 | {
58 | if (timer)
59 | {
60 | clearTimeout(timer)
61 | }
62 |
63 | timer = setTimeout(() =>
64 | {
65 | fn(...args)
66 | }, wait)
67 | }
68 | }
--------------------------------------------------------------------------------
/server/src/lib/validator.ts:
--------------------------------------------------------------------------------
1 | import { TextDocument } from "vscode-languageserver-textdocument"
2 | import { Diagnostic, DiagnosticSeverity } from "vscode-languageserver-types";
3 | import * as isChinese from 'is-chinese'
4 |
5 | import { StringRegx, DiagnosticMessage, SpecialStringRegx, inValidParamsRegx, ExtensionSource, getLineCount } from "./util";
6 | import CommentManager from "./comment/CommentManager";
7 |
8 | const MaxDiagnosticCount = 100
9 |
10 | /**
11 | * 在服务端校验文本,所有字符串内的中文文本会作为错误传出
12 | *
13 | * @param {TextDocument} textDocument - 文本映射对象
14 | * @return {*} {Diagnostic[]} - 错误信息数组,需要传回给客户端
15 | */
16 | export const validateMessage = (textDocument: TextDocument): Diagnostic[] =>
17 | {
18 | // 出于性能考虑,需对错误信息数量做限制
19 | let limitDiagnosticCount = 0
20 |
21 | // 校验器会检查所有的大写单词是否超过 2 个字母
22 | const text = textDocument.getText();
23 |
24 | // 单引号之间/双引号之间 的字符串文本
25 | let match: RegExpExecArray | null;
26 |
27 | const diagnostics: Diagnostic[] = [];
28 |
29 | while ((match = StringRegx.exec(text)) && limitDiagnosticCount <= MaxDiagnosticCount)
30 | {
31 | // 匹配的全部字符串
32 | const rawString = match[0]
33 |
34 | // 单双引号之间的文本内容
35 | const string = match[1] || match[2]
36 |
37 | if (!string) continue
38 |
39 | // 以 react-intl= 或 $= 开头的特殊字符串匹配
40 | const specialStringMatch = SpecialStringRegx.exec(string)
41 | const invalidParams = specialStringMatch ? !!inValidParamsRegx.exec(specialStringMatch[1].trim()) : false
42 |
43 | if (!isChinese(string) && (!specialStringMatch || invalidParams)) continue
44 |
45 | limitDiagnosticCount++
46 |
47 | // 特殊字符串内容,如:'react-intl=你好,{name: Fred 哥}' 中的 '你好,{name: Fred 哥}'
48 | const specialString = specialStringMatch ? specialStringMatch[1].trim() : undefined
49 |
50 | const targetString = specialString || string.trim()
51 |
52 | if (!CommentManager.getInstance().checkEnableCommentRules(textDocument.uri, getLineCount(text, match.index))) continue
53 |
54 | // 错误信息
55 | const diagnostic: Diagnostic = {
56 | severity: DiagnosticSeverity.Warning,
57 | range: {
58 | start: textDocument.positionAt(match.index),
59 | end: textDocument.positionAt(match.index + rawString.length)
60 | },
61 | message: `${targetString}${DiagnosticMessage}`,
62 | source: ExtensionSource,
63 |
64 | };
65 | diagnostics.push(diagnostic);
66 | }
67 |
68 | return diagnostics
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/server/src/server.ts:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import
4 | {
5 | CodeAction, CodeActionKind, Command, createConnection,
6 | TextDocuments, TextDocumentSyncKind, TextDocumentChangeEvent, ProposedFeatures, CodeActionParams, ExecuteCommandParams, Diagnostic
7 | } from 'vscode-languageserver/node';
8 | import { TextDocument } from 'vscode-languageserver-textdocument';
9 |
10 | import { validateMessage } from './lib/validator';
11 | import CommentManager from './lib/comment/CommentManager';
12 | import { getCodeActionMessage, debounce, ContentChangeDelay, ExtensionSource } from './lib/util';
13 | import { LinterCodeActionMessage, ExecuteCommandMap, isCommand, LinterCommands } from './lib/command';
14 |
15 | const connection = createConnection(ProposedFeatures.all);
16 | connection.console.info(`Sample server running in node ${process.version}`);
17 |
18 | // 关键点2: 创建文档集合对象,用于映射到实际文档
19 | const documents: TextDocuments = new TextDocuments(TextDocument);
20 | documents.listen(connection);
21 |
22 | connection.onInitialize(() =>
23 | {
24 | // 声明语言服务器支持的特性
25 | return {
26 | capabilities: {
27 | codeActionProvider: true,
28 | textDocumentSync: {
29 | openClose: true,
30 | change: TextDocumentSyncKind.Incremental
31 | },
32 | executeCommandProvider: {
33 | commands: [LinterCommands.Extract, LinterCommands.DisableLine, LinterCommands.DisableFile],
34 | }
35 | }
36 | };
37 | });
38 |
39 | // 打开一个文件时
40 | documents.onDidOpen((event: TextDocumentChangeEvent) =>
41 | {
42 | const document = event.document
43 | CommentManager.getInstance().setUriDocument(document.uri, document.getText())
44 | const diagnostics = validateMessage(document)
45 | connection.sendDiagnostics({ uri: document.uri, version: document.version, diagnostics })
46 | });
47 |
48 | // 当文件内容改变时
49 | documents.onDidChangeContent(debounce((event: TextDocumentChangeEvent) =>
50 | {
51 | const document = event.document
52 | CommentManager.getInstance().setUriDocument(document.uri, document.getText())
53 | const diagnostics = validateMessage(document)
54 | connection.sendDiagnostics({ uri: document.uri, version: document.version, diagnostics })
55 | }, ContentChangeDelay));
56 |
57 | // 正在 Code Action 中处理的错误
58 | let CodeActionDiagnostic: Diagnostic | undefined
59 |
60 | // code action 事件
61 | // 触发时机:光标变化时(点击文本或通过键盘移动光标)
62 | // 返回:如果光标所在范围包含有错误信息(diagnostic)则会在 params.context.diagnostics 中存入一个非空数组,里面就有我们需要的 diagnostic 信息
63 | connection.onCodeAction((params: CodeActionParams) =>
64 | {
65 | const { diagnostics } = params.context
66 | const textDocument = documents.get(params.textDocument.uri);
67 |
68 | // 光标所在文本不包含错误信息时返回
69 | if (!Array.isArray(diagnostics) || !diagnostics.length || !textDocument) return
70 |
71 | const diagnostic = (CodeActionDiagnostic = diagnostics.find(d => d.source === ExtensionSource))
72 |
73 | // 过滤其他插件发出的错误信息
74 | if (!diagnostic) return
75 |
76 | // 用于 CodeAction 的文本和原始引号之间的文本
77 | const [codeActionMessage, rawMessage] = getCodeActionMessage(diagnostic.message);
78 |
79 | if (!rawMessage) return
80 |
81 | // 点击 CodeAction 后发出的指令会被 client middleware 捕获并处理
82 | return [
83 | CodeAction.create(
84 | codeActionMessage,
85 | Command.create(codeActionMessage, LinterCommands.Extract, textDocument.uri, rawMessage),
86 | CodeActionKind.QuickFix,
87 | ),
88 | CodeAction.create(
89 | LinterCodeActionMessage[LinterCommands.DisableLine],
90 | Command.create(LinterCodeActionMessage[LinterCommands.DisableLine], LinterCommands.DisableLine, textDocument.uri),
91 | CodeActionKind.QuickFix,
92 | ),
93 | CodeAction.create(
94 | LinterCodeActionMessage[LinterCommands.DisableFile],
95 | Command.create(LinterCodeActionMessage[LinterCommands.DisableFile], LinterCommands.DisableFile, textDocument.uri),
96 | CodeActionKind.QuickFix,
97 | )
98 | ];
99 | });
100 |
101 | // Code Action 指令经 client middleware 处理后执行
102 | connection.onExecuteCommand(async (params: ExecuteCommandParams) =>
103 | {
104 | if (!isCommand(params.command)) return
105 |
106 | const textDocument = (Array.isArray(params.arguments) && params.arguments.length) ? documents.get(params.arguments[0]) : undefined
107 |
108 | if (!textDocument) return
109 |
110 | ExecuteCommandMap[params.command](connection, textDocument, params, CodeActionDiagnostic)
111 | .then(() => CodeActionDiagnostic = undefined)
112 | });
113 |
114 | connection.listen();
115 |
--------------------------------------------------------------------------------
/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "compilerOptions": {
4 | "target": "es2017",
5 | "module": "commonjs",
6 | "moduleResolution": "node",
7 | "sourceMap": true,
8 | "outDir": "out",
9 | "rootDir": "src",
10 | "lib": [ "es2017" ]
11 | },
12 | "include": [
13 | "src",
14 | "index.d.ts"
15 | ],
16 | "exclude": [
17 | "node_modules"
18 | ]
19 | }
--------------------------------------------------------------------------------
/server/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | is-chinese@^1.4.6:
6 | version "1.4.6"
7 | resolved "https://registry.yarnpkg.com/is-chinese/-/is-chinese-1.4.6.tgz#11e03397d4020e170798ba9edf2f55139587899e"
8 | integrity sha512-ofuscVRi2kkSGe60EKbwlnrx+RFhln6vW1Ya98/87dmO60iLqYnWWk8X7Veb3k3TN0X8MddNj0h2xam4qyf2sw==
9 |
10 | vscode-jsonrpc@6.0.0:
11 | version "6.0.0"
12 | resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e"
13 | integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==
14 |
15 | vscode-languageserver-protocol@3.16.0:
16 | version "3.16.0"
17 | resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821"
18 | integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==
19 | dependencies:
20 | vscode-jsonrpc "6.0.0"
21 | vscode-languageserver-types "3.16.0"
22 |
23 | vscode-languageserver-textdocument@^1.0.1:
24 | version "1.0.1"
25 | resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f"
26 | integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==
27 |
28 | vscode-languageserver-types@3.16.0:
29 | version "3.16.0"
30 | resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247"
31 | integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==
32 |
33 | vscode-languageserver@^7.0.0:
34 | version "7.0.0"
35 | resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0"
36 | integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==
37 | dependencies:
38 | vscode-languageserver-protocol "3.16.0"
39 |
40 | vscode-uri@^3.0.2:
41 | version "3.0.2"
42 | resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.2.tgz#ecfd1d066cb8ef4c3a208decdbab9a8c23d055d0"
43 | integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA==
44 |
--------------------------------------------------------------------------------
/test_files/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "reactIntlLinter.intlConfigPath": "src/intl",
3 | "reactIntlLinter.intlCode": "react-intl",
4 | }
--------------------------------------------------------------------------------
/test_files/TypescriptReact.tsx:
--------------------------------------------------------------------------------
1 | // ri-lint-disable
2 | "UPPERCASE TEXT"
3 |
4 | '一个文本'
5 |
6 | // ri-lint-enable
7 | "这是另一个文本"
8 |
9 | '这是一个特殊的文件'
10 |
11 | // ri-lint-disable
12 | 'react-intl=你好,{status: 无敌的}{name: Fred 哥}';
13 |
14 | '$=你好,{nameTitle: Fred 哥}' // ri-lint-enable-line
15 |
16 | '$=共 {total: 100} 条记录,第 {page: 1}/{totalPage: 10} 页'
17 |
18 | '$=添加'
19 |
20 | // ri-lint-enable
21 | '添加'
22 |
23 | const tabTest = () =>
24 | {
25 | const validMessage = true
26 | ? 'test disabling linter for line in tab case'
27 | : '这是一个测试'
28 | }
29 |
30 | // https://github.com/Styx11/react-intl-linter/issues/7
31 | const validMessage = true ? '' : '你好'; const followMessage = true ? '你好' : ''
32 |
33 | const message = `${"添加"}hellllllo${"添加"}` /* ri-lint-disable-line */
34 |
35 | const testMsg = `${'添加'}hellllllo${'一个文本'}hellllllo{"这是另一个文本"}`
36 |
37 | "English!!!"
--------------------------------------------------------------------------------
/test_files/plaintext.txt:
--------------------------------------------------------------------------------
1 | 自定义EclipseEclipseEclipseUPPERCASE ERROR
2 |
3 | '一个文本'
4 |
5 | "这是另一个文本"这段文本紧贴字符串,并且不会触发 Code Action
6 |
7 | "It's not me"
8 |
9 | 你好你好你好
10 |
11 | "你好你好吗你"
12 |
--------------------------------------------------------------------------------
/test_files/src/i18n/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-anonymous-default-export */
2 | import zh_CN from "./zh_CN.json";
3 | import en_US from "./en_US.json";
4 |
5 | export type ILocales = 'en-US' | 'zh-CN'
6 |
7 | export function getLocales(lang: ILocales)
8 | {
9 | switch (lang)
10 | {
11 | case ('en-US'):
12 | return en_US
13 | case ('zh-CN'):
14 | return zh_CN
15 | default:
16 | return en_US
17 | }
18 | }
19 |
20 | export default {
21 | "en-US": en_US,
22 | "zh-CN": zh_CN
23 | }
--------------------------------------------------------------------------------
/test_files/src/i18n/zh_CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "A_TEXT": "一个文本",
3 | "ABSOLUTE_VALUE": "绝对值",
4 | "ACCEPTING": "受理中",
5 | "ACCESS_REQUIRED_PASSWORD": "需要密码访问",
6 | "ACCOUNT_CENTER": "账户中心",
7 | "ACCOUNT_INFO": "账户信息",
8 | "ACCURATE_CALCULATION": "精准算量",
9 | "ACHIEVEMENT_FILE": "成果文件",
10 | "ACTUAL_LANDFORM": "实际地貌",
11 | "ADD": "添加",
12 | "ADD_CUSTOM": "添加自定义",
13 | "ADD_DESIGN_SCHEME": "添加基坑设计(开挖)方案",
14 | "ADD_ROLE": "添加角色",
15 | "ADDITIONAL_INFO": "补充信息",
16 | "ADDITIONAL_INFO_TIPS": "请输入要补充的描述信息",
17 | "ADDNEED_TIPS": "如以上服务内容未满足您的需求,可在下方补充描述",
18 | "ADMINISTRATOR": "管理员",
19 | "AFTER_SALES_EXPERT": "XX售后专家",
20 | "AFTER_SALES_EXPERT_DESC1": "XX已经根据服务单展开服务,其中内容无法修改,",
21 | "AFTER_SALES_EXPERT_DESC2": "如有特殊情况请联系",
22 | "AFTER_SALES_PHONE": "(0755)2692 3283 ",
23 | "AFTER_SALES_PHONE_DESC": "您可以微信扫码联系或拨打下方电话",
24 | "AFTER_SALES_QR_CODE_DESC": "XX售后服务专家",
25 | "AFTER_SALES_QR_CODE_PHONE": "TEL:XXX",
26 | "AFTER_SALES_QR_CODE_TITLE": "微信扫码联系售后服务",
27 | "ALL": "全部",
28 | "ALTER_QUESTIONNAIRE": "修改调查表",
29 | "AMOUNT_EXCAVATION": "挖方量",
30 | "AMOUNT_FILLING": "填方量",
31 | "AMOUNT_NET": "净方量",
32 | "AND": "和",
33 | "APP_MODEL": "XX测量",
34 | "APP_PANORAMA": "XX全景",
35 | "APPLICATION_NAME": "XX云平台",
36 | "APPLY_NODE": "申请节点",
37 | "APPLY_NODE_EARTHWORK_COST_OPTIMIZATION_DESIGN": "地下工程设计前",
38 | "APPLY_NODE_EARTHWORK_SURVEY_CONSTRUCTION": "土方施工过程任意时间点",
39 | "APPLY_NODE_GEOLOGICAL_SURVEY_INVESTMENT": "现场踏勘前",
40 | "APPLY_NODE_PROJECT_VISUALIZER_CONSTRUCTION": "建议开工前开通",
41 | "APPLY_NODE_SECURITY_INSPECTION_CONSTRUCTION": "土方施工过程任意时间点",
42 | "APPLY_SERVICE_LIST": "申请服务列表",
43 | "APPLY_SERVICE_TICKET": "申请服务单",
44 | "APPOINTED_DOOR_TIME": "预约上门时间",
45 | "APPOINTED_TIME": "预约时间",
46 | "AREA_LEN_TIPS": "最长10位数字并且小数点后最长两位数字",
47 | "ASSOCIATE_MISSION": "关联任务",
48 | "AUDIT": "审核",
49 | "AUDIT_APPROVED": "审核通过",
50 | "AUDIT_APPROVED_REASON": "审核通过备注",
51 | "AUDIT_REASON_RULE": "请输入至少5个字符",
52 | "AUDIT_REJECTED_REASON": "审核不通过备注",
53 | "AUDIT_TICKET": "审核服务单",
54 | "AUDIT_TIME": "审核时间",
55 | "AUDITOR": "审核人",
56 | "AUDITOR_CONTACT": "审核人联系方式",
57 | "AUTONOMOUS_COLLECTION": "自主采集",
58 | "AUTONOMOUS_COLLECTION_DESC": "由甲方自主完成数据采集操作,随后再由XX进行数据处理和交付,请在数据采集完成后通知XX售后团队进行处理。",
59 | "AUTONOMOUS_COLLECTION_DESC_VANKE": "即由公司采购无人机设备后自行进行测量外业工作,测量完成后提交测量数据至测量服务公司进行数据处理服务。对于单项目需要进行土方进度或形象进度监控、公司开发体量较大(年度飞行需求大于等于7次)且已掌握测量飞行技巧的一线公司,建议选用本模式。",
60 | "AUTONOMY": "自主采集",
61 | "AVERAGE_EFFICIENCY_LINE": "平均施工速度",
62 | "BACKGROUND_SUMMARY": "背景提要",
63 | "BAD_GATEWAY_ERROR_TEXT": "服务器正在重启,请稍后再试",
64 | "BAD_REQUEST_ERROR_TEXT": "网络请求出错",
65 | "BASE_AMOUNT": "监测前已施工量",
66 | "BIND_PROJECT": "绑定项目",
67 | "BIND_PROJECT_PLACE_HOLDER": "请选择您要绑定的项目",
68 | "BIND_PROJECT_TIP": "请选择项目",
69 | "BRING_LAST_DATA": "带入上一次数据",
70 | "BUILDING_CHECKING": "主体监测中",
71 | "BUILDING_STAGE": "主体阶段",
72 | "BUSINESS_CONTENT": "业务内容",
73 | "BUSINESS_CONTENT_EARTHWORK_COST_OPTIMIZATION_DESIGN": "1. 快速土方测量,供成本测算。
2. 辅助设计端口依据实际地形寻找合适、经济的地库及边坡设计方案,减少土方外运量。",
74 | "BUSINESS_CONTENT_EARTHWORK_SURVEY_CONSTRUCTION": "1. 适用于土方工程量较大、土方工期紧张的项目实时动态监控现场进度情况,合理安排土方工期,同时把控进度、效率,对工期进行预警。",
75 | "BUSINESS_CONTENT_GEOLOGICAL_SURVEY_INVESTMENT": "1. 不受场地管理限制,可于场外快速测量地形地貌,输出场地三维模型数据,为投资强排方案设计提供充足依据。
2. 获取地貌信息,准确预估土方成本,提高可研测算准确性,减少投资风险。",
76 | "BUSINESS_CONTENT_PROJECT_VISUALIZER_CONSTRUCTION": "1. 周期性记录项目楼栋形象进度,为结算提供依据。
2. 形象进度包含:三维模型、正射图、全景图、全周期视频。",
77 | "BUSINESS_CONTENT_SECURITY_INSPECTION_CONSTRUCTION": "利用无人机高空数据采集的优势,通过无人机自动巡航的方法,采集工地现状,巡查工地安全文明等相关隐患,并出具报告。",
78 | "CANCEL": "取消",
79 | "CHOOSE": "选择",
80 | "CLICK_FOR_DETAILS": "点击查看详情",
81 | "CLICK_TO_VIEW": "点击查看",
82 | "CLOSE": "关",
83 | "CLOSE_SHARE": "停止分享",
84 | "CLOSE_SHARE_ERROR": "该项目停止分享失败",
85 | "CLOSE_SHARE_TIP": "该项目停止分享",
86 | "CLOSURE": "关闭",
87 | "COLLAPSED": "收起",
88 | "COLLECT_NEW_MODEL": "采集新模型",
89 | "COLLECTION_TIME": "采集时间",
90 | "COLLECTION_TYPE": "采集方式",
91 | "COLLECTOR": "采集人",
92 | "COLLECTOR_AREA": "采集面积",
93 | "COLLECTOR_AREA_DESC": "请填写大约需要采集的地块面积,用于飞行专家判断携带备用电池数量",
94 | "COLLECTOR_AREA_TIPS": "请输入采集面积",
95 | "COLLECTOR_COLLECT": "采集人联系方式",
96 | "COLLECTOR_COLLECT_DESC": "在自主采集方式下采集完成后,XX售后团队在进行数据验证时可能需要联系采集的相关人员核对信息",
97 | "COLLECTOR_JOB": "职位",
98 | "COLLECTOR_JOB_TIPS": "请输入职位",
99 | "COLLECTOR_NAME": "名字",
100 | "COLLECTOR_NAME_TIPS": "请输入名字",
101 | "COLLECTOR_PHONE": "联系电话/手机",
102 | "COLLECTOR_PHONE_TIPS": "请输入联系电话/手机",
103 | "COMMENT": "批注",
104 | "COMPLETED": "已完成",
105 | "COMPLETED_TIME": "完工时间",
106 | "CONFIRM": "确定",
107 | "CONFIRM_REQUIREMENT": "确认需求",
108 | "CONSTRUCTED_ACTUAL_AMOUNT": "实际总工程量",
109 | "CONSTRUCTED_ACTUAL_AMOUNT_TIPS": "实际总工程量在工程完结时输入",
110 | "CONSTRUCTED_AMOUNT": "已施工量",
111 | "CONSTRUCTED_CURRENT_PERIOD": "本期施工量",
112 | "CONSTRUCTED_EVERY_PERIOD": "每期施工量",
113 | "CONSTRUCTED_GRAND_AMOUNT": "累计施工量",
114 | "CONSTRUCTED_GRAND_AMOUNT_TIPS": "累计施工量由每期施工量叠加所算得",
115 | "CONSTRUCTED_LATEST_AMOUNT": "最新一期施工量",
116 | "CONSTRUCTION_DAILY": "施工速度",
117 | "CONSTRUCTION_EFFICIENCY": "施工效率",
118 | "CONSTRUCTION_EXPECT_AMOUNT": "预计总工程量",
119 | "CONSTRUCTION_MONITOR_TIME": "土方监测时间",
120 | "CONSTRUCTION_REMAIN_AMOUNT": "剩余施工量",
121 | "CONSTRUCTION_SPEED": "施工速度",
122 | "CONSTRUCTION_SPEED_AVERAGE": "平均施工速度",
123 | "CONSTRUCTION_TIME": "土方施工时间",
124 | "CONTACT": "联系人",
125 | "CONTACT_INFO": "联系方式",
126 | "CONTACT_INFO_WRITE": "填写联系方式",
127 | "CONTACT_JOB": "职位",
128 | "CONTACT_JOB_PROJECT": "对接人职位",
129 | "CONTACT_JOB_TIPS": "请输入职位",
130 | "CONTACT_NAME": "名字",
131 | "CONTACT_NAME_PROJECT": "项目对接人",
132 | "CONTACT_NAME_TIPS": "请输入名字",
133 | "CONTACT_NUMBER": "联系电话",
134 | "CONTACT_PHONE": "联系电话/手机",
135 | "CONTACT_PHONE_TIPS": "请输入联系电话/手机",
136 | "CONTROL_POINT": "控制点",
137 | "CONTROL_POINT_DES": "如果您未上传【控制点信息文件】,将对成果产生以下影响:
1. 无法基于本地坐标提供【实景模型】、【高程点测量数据成果】或【算量数据成果】
2. 无法提供【 CAD 图纸与实景模型叠加】服务
若您继续提交,将统一基于【 CGCS2000 国家大地坐标系】和【 WGS84 大地高程基准】输出所有数据成果,不影响数据精度,但该坐标系可能会与当地坐标系存在一定偏差,造成无法与【用地范围红线】和【基坑设计方案】对应
",
138 | "CONTROL_POINT_FILE": "控制点信息文件",
139 | "CONTROL_POINT_TIP_TITLE": "下图为控制点(基准点)示意图,
请在您的项目现场找到以下点位并确认数量",
140 | "CONTROL_POINT_TIPS": "请上传控制点信息文件",
141 | "COORDINATE_AXIS": "坐标轴",
142 | "COORDINATE_CONVERSION_ACCURACY_TABLE": "坐标转换精度表",
143 | "COORDINATE_MODEL": "模型坐标",
144 | "COORDINATE_ORIGINAL": "原始坐标",
145 | "COPIED": "链接已复制",
146 | "COPY_LINK": "复制链接",
147 | "COPY_LINK_PASSWORD": "复制链接与密码",
148 | "COPY_TEXT": "{link} 密码:{password}",
149 | "CREATE_AT": "创建于",
150 | "CREATE_PROJECT_FOR": "为【{name}】创建新项目",
151 | "CREATE_PROJECT_QUICKLY": "快速创建项目",
152 | "CREATE_PROJECT_WITH_STAGE": "正在创建{stage}的项目",
153 | "CREATE_TIME": "创建时间",
154 | "CURRENNT_POSITION": "当前位置",
155 | "D_VALUE": "差值",
156 | "DATA_ACHIEVEMENT_NO_EXIST_TIPS": "服务单未上传数据成果,请联系售后处理(0755)2692 3283",
157 | "DATA_ACHIEVEMENTS": "数据成果",
158 | "DATA_ACHIEVEMENTS_LIST": "数据成果列表",
159 | "DATA_DETAIL": "数据详情",
160 | "DATA_DOWNLOAD": "数据下载",
161 | "DATA_FILE_DESCRIPTION": "点击查看以下数据文件说明",
162 | "DATA_REPORTS": "数据报告",
163 | "DATA_RESULTS": "数据成果",
164 | "DATA_RESULTS_VIEW": "查看数据成果",
165 | "DATA_STATISTICS": "数据统计",
166 | "DATE": "日期",
167 | "DATE_FORMAT": "YYYY.MM.DD HH:mm",
168 | "DATE_FORMATTER": "YYYY年MM月DD日 HH:mm:ss",
169 | "DAY": "天",
170 | "DAYJS_D": "日",
171 | "DAYJS_M": "月",
172 | "DAYJS_Y": "年",
173 | "DEFAULT_PACKAGE_RESULT": "默认套餐成果,可在【服务内容】更改数据标准、选择更多数据类型",
174 | "DEFAULT_STAGE_TOOLTIP": "已为您筛选出",
175 | "DELEGATION": "上门采集",
176 | "DELETE": "删除",
177 | "DELETE_CONFIRM": "你确定要删除吗?",
178 | "DELETE_ERROR": "删除出错,请重试",
179 | "DELETE_ROLE": "删除角色",
180 | "DELETE_ROLE_ERROR": "该公司下存在使用该角色的成员,所以您暂时无法删除此角色。",
181 | "DELETE_ROLE_TIP": "请确认是否删除角色【{name}】?",
182 | "DEMAND_DESIGN_SCHEME": "基坑设计(开挖)方案",
183 | "DEMAND_DESIGN_SCHEME_TITLE": "基坑设计(开挖)方案",
184 | "DEMAND_FILE_TITLE": "根据您的选择,请上传/选择以下文件",
185 | "DEMAND_LIST": "需求单",
186 | "DEMAND_TYPE": "需求类型",
187 | "DEMO_PANO_DIALOG_TITLE": "拍摄位置大图",
188 | "DESIGN_CONDITIONS": "设计条件",
189 | "DESIGN_SCHEME": "设计方案",
190 | "DESIGN_SCHEME_ADD": "添加基坑设计方案",
191 | "DESIGN_SCHEME_ADD_FROM": "您正在为({projectName})新增基坑设计(开挖)方案",
192 | "DESIGN_SCHEME_BIND_ERROR": "该基坑设计方案已被使用,无法删除",
193 | "DESIGN_SCHEME_DELETE_TIP": "请确认是否删除 【{name}】?",
194 | "DESIGN_SCHEME_DESC": "以下数据根据指定的设计条件测得(输出时间:{time})",
195 | "DESIGN_SCHEME_DESC1": "个设计方案",
196 | "DESIGN_SCHEME_EDIT_FROM": "该方案在您提交服务单后才会保存在项目中",
197 | "DESIGN_SCHEME_EMPTY_TIPS": "项目还未添加设计方案,请先添加设计方案",
198 | "DESIGN_SCHEME_FROM": "以下方案来自于",
199 | "DESIGN_SCHEME_NAME_EXIST": "新增设计方案名称重复",
200 | "DESIGN_SCHEME_NAME_EXIST_RULE": "方案名称已存在,请重新输入",
201 | "DESIGN_SCHEME_PICKER_TIPS": "已选择 {count} 个基坑方案,将对已选中的每个基坑设计方案进行土方量计算",
202 | "DESIGN_SCHEME_TIPS": "请选取本次服务采用的方案",
203 | "DETAILED_ADDRESS": "详细地址",
204 | "DETAILED_ADDRESS_EMPTY_TIP": "请输入详细地址",
205 | "DIG": "挖",
206 | "DISTRICT_INPUT_EMPTY_TIP": "省市区获取失败",
207 | "DISTRICT_INPUT_PLACEHOLDER": "省市区",
208 | "DOWNLOAD": "下载",
209 | "DRONE_NO_FLY_ZONE_BUTTON_TEXT": "点击查看无人机禁飞区相关信息",
210 | "DRONE_NO_FLY_ZONE_DIALOG_A1_1": "1. 首先点击访问链接:",
211 | "DRONE_NO_FLY_ZONE_DIALOG_A1_2": "2. 在上方选择机型为 Phantom 4 RTK;",
212 | "DRONE_NO_FLY_ZONE_DIALOG_A1_3": "3. 在地图左上角输入您项目的所在地;",
213 | "DRONE_NO_FLY_ZONE_DIALOG_A1_4": "4. 拖动地图查看项目附近是否有禁飞区、限飞区等限制无人机飞行的区域。",
214 | "DRONE_NO_FLY_ZONE_DIALOG_A1_WARING": "⚠️ 限制区域需要至少离项目范围 500 m",
215 | "DRONE_NO_FLY_ZONE_DIALOG_A2_1": "1. 下载《民用无人机飞行活动申请审批表》并填写完整;",
216 | "DRONE_NO_FLY_ZONE_DIALOG_A2_2": "2. 将《审批表》提交给表中项目所在地的相关部门审批盖章;",
217 | "DRONE_NO_FLY_ZONE_DIALOG_A2_3": "3. 将盖章后的《审批表》给到XX售后服务团队;",
218 | "DRONE_NO_FLY_ZONE_DIALOG_A2_4": "4. XX将《审批表》递交大疆官方申请解禁,通过后XX将会负责为项目当地无人机实施解禁操作。",
219 | "DRONE_NO_FLY_ZONE_DIALOG_A2_FILE_NAME": "民用无人机飞行活动申请审批表.xlsx",
220 | "DRONE_NO_FLY_ZONE_DIALOG_AFTER_SALES": "XX售后服务:(TEL-0755 2692 3283)",
221 | "DRONE_NO_FLY_ZONE_DIALOG_Q1": "如何查询您的项目是否属于无人机禁飞区?",
222 | "DRONE_NO_FLY_ZONE_DIALOG_Q2": "如何在禁飞区正常执行无人机作业任务?",
223 | "DRONE_NO_FLY_ZONE_DIALOG_Q2_DESC": "如果您的项目恰巧在禁飞区内或十分接近禁飞区,则需要向有关部门申请解禁,才能在禁飞区内使用无人机飞行。具体步骤如下:",
224 | "DRONE_NO_FLY_ZONE_DIALOG_SCAN": "请使用微信扫码",
225 | "DRONE_NO_FLY_ZONE_DIALOG_TITLE": "无人机禁飞区的相关信息",
226 | "EARTHWORK": "土方算量",
227 | "EARTHWORK_ABBR": "土方算量",
228 | "EARTHWORK_CHECKING": "土方监测中",
229 | "EARTHWORK_COST_OPTIMIZATION": "土方成本优化",
230 | "EARTHWORK_CURRENT_PROGRESS": "土方工程当前进度",
231 | "EARTHWORK_HAS_RISK": "土方工程有逾期风险",
232 | "EARTHWORK_MONITOR": "土方监测",
233 | "EARTHWORK_NO_RISK": "暂无风险",
234 | "EARTHWORK_PROGRESS_BAR": "土方工程进度条",
235 | "EARTHWORK_STAGE": "土方阶段",
236 | "EARTHWORK_SURVEY": "土方测量",
237 | "EARTHWORK_WARN_1": "土方工程有逾期风险,按照当前施工速度,将于 {expectFinishedAt} 完成土方施工,晚于期望完工时间( {expectedAt} ),超出天数 {duration} 天",
238 | "EARTHWORK_WARN_2": "土方工程有逾期风险,{startTime}至{endTime} 的净方量为 0 m³",
239 | "EARTHWORK_WARN_3": "土方工程有逾期风险,{startTime}至{endTime} 填方 {amount}",
240 | "EDIT": "编辑",
241 | "EDIT_DESIGN_SCHEME": "编辑方案",
242 | "EDIT_MODEL_3D": "编辑模型",
243 | "EDIT_PANORAMA": "编辑全景",
244 | "EDIT_PERSONAL_INFO": "编辑个人信息",
245 | "EDIT_PROJECT": "编辑项目",
246 | "EDIT_PROJECT_FOR": "正在编辑【{name}项目】",
247 | "EDIT_ROLE": "编辑角色",
248 | "ELEVATION_AVERAGE": "平均高程",
249 | "ELEVATION_MAXIMUM": "最大高程",
250 | "ELEVATION_MINIMUM": "最小高程",
251 | "ELEVATION_POINTS_NUMBER": "标高点数量",
252 | "EMAIL": "邮箱",
253 | "EMPTY": "暂无",
254 | "EMPTY_DATA": "暂无数据",
255 | "ENTERPRISE_INFO": "公司信息",
256 | "ENUM_ACCEPTING": "受理中",
257 | "ENUM_COMPLETED": "已完成",
258 | "ENUM_CUSTOM": "自定义",
259 | "ENUM_CUSTOMER_AUDIT": "审核中",
260 | "ENUM_CUSTOMER_REFUSED": "审核不通过",
261 | "ENUM_EARTHWORK_SURVEY": "土方算量",
262 | "ENUM_HEIGHT_SURVEY": "高程测量",
263 | "ENUM_LAND_EVALUATION": "投拓看地",
264 | "ENUM_MEASURE": "实景建模",
265 | "ENUM_MEASURING": "采集中",
266 | "ENUM_MODEL": "三维模型",
267 | "ENUM_PANORAMA": "全景图",
268 | "ENUM_PROCESSING": "服务中",
269 | "ENUM_REJECTED": "已退回",
270 | "ENUM_REVERTED": "已撤销",
271 | "ENUM_SECURITY_INSPECTION": "空中巡检",
272 | "ESTIMATED_COMPLETED_TIME": "预计完工时间",
273 | "ESTIMATED_PLAN": "预估方案",
274 | "ESTIMATED_PROGRESS": "预估进度",
275 | "ESTIMATED_PROGRESS_TIPS": "预估进度按最近一期施工速度推算",
276 | "EXAMPLE_3D_MODEL": "实景三维模型",
277 | "EXAMPLE_CAD_ELEVATION": "场地高程点 CAD 图",
278 | "EXAMPLE_CONTOUR_MAP": "场地等高线图",
279 | "EXAMPLE_EARTHWORK_GRID": "土方算量方格网图",
280 | "EXAMPLE_EARTHWORK_REPORT": "土方算量报告",
281 | "EXAMPLE_ELEVATION_GRID": "高程方格网图",
282 | "EXAMPLE_HD_ORTHO": "高清正射图",
283 | "EXAMPLE_SECURITY_INSPECTION": "安全文明巡检报告",
284 | "EXISTING_SPECIFICATION": "已存在相同规格的选项!",
285 | "EXPAND": "展开",
286 | "EXPECTED_COMPLETION_TIME": "期望完工时间",
287 | "FILE": "文件",
288 | "FILE_ALREADY_EXIST": "已存在文件名相同的文件",
289 | "FILE_DESC": "个文件",
290 | "FILE_NAME": "文件名称",
291 | "FILE_NUMBER_TIP": "等{count}个文件",
292 | "FILE_SIZE": "文件大小",
293 | "FILE_TIP": "根据您的选择,请上传/选择以下文件",
294 | "FILL": "填",
295 | "FILL_INFORMATION": "填写信息",
296 | "FILLED": "已填写",
297 | "FLIGHT_TYPE": "飞行类型",
298 | "FLIGHT_WAY": "飞行方式",
299 | "FORM_TIP": "注意序号前加 * 为必填项",
300 | "FOUNDATION_PIT": "基坑文件",
301 | "FOUNDATION_PIT_TIPS": "请选择至少一个基坑设计方案",
302 | "GENERAL": "通用",
303 | "GENERAL_DATA": "通用数据",
304 | "GEOLOGICAL_SURVEY": "地块勘察",
305 | "GET_ADDRESS_FAILED": "获取地址失败",
306 | "GET_DISTRICT_INFO_FAILED": "获取行政区信息失败",
307 | "GET_PROJECT_DETAIL_FAILED": "获取项目详情失败",
308 | "GET_SHARE_INFO_ERROR": "获取分享信息失败",
309 | "GO_TO_DELIVERY": "数据后台",
310 | "GRAVITY_BENCHMARK": "重力基准",
311 | "GRID_FILE": "方格网文件",
312 | "GRID_IMG": "方格网示意图",
313 | "GRID_TABLE": "方格网表格",
314 | "HEIGHT_BENCHMARK": "高程基准",
315 | "HELLO_NAMETITLE": "你好,{name_title}",
316 | "HELLO_WORLD": "你好,世界!",
317 | "HOME_COLLECTION": "上门采集",
318 | "HOME_COLLECTION_DESC": "在经过上门时间预约后,由XX提供人员完成现场数据采集、数据处理、数据交付等后续流程,完成后您可以在XX云平台查收数据成果。",
319 | "HOME_COLLECTION_DESC_VANKE": "即由无人机测量单位进行测量外业服务及测量数据处理服务。对于开发体量较小的一线公司(年度飞行需求小于7次)建议优先选择本模式。",
320 | "HOME_TIME": "上门时间",
321 | "HOME_TIME_DESC": "请填写一个初步的上门时间意向,不一定是最终上门时间",
322 | "HOME_TIME_TIPS": "请选择上门时间",
323 | "IMAGE_UPLOAD_RULE": "可以上传20张不大于10M的照片",
324 | "INFO_QUESTIONNAIRE": "地块信息调查表",
325 | "IS_SOME_SCHEMATIC_DIAGRAM": "下图为【{title}】示意图",
326 | "JOB_LEN_TIPS": "职位最长为10个字符",
327 | "LAND_EVALUATION_REPORT": "投拓看地报告",
328 | "LAND_SCOPE": "用地范围文件",
329 | "LAND_SCOPE_RED_LINE_TIPS": "请上传红线图(用地范围文件)",
330 | "LAND_SCOPE_TIPS": "请上传用地范围文件",
331 | "LATEST_DATA_DYNAMICS": "最新数据动态",
332 | "LAUNCHED_TIP": "该功能即将上线",
333 | "LICE_3D_MODEL": "实景模型",
334 | "LIMIT_EXCEEDED_TIP": "您已经上传了 {limit} 个文件,请检查是否有重复的文件。",
335 | "LINK_PASSWORD": "链接密码",
336 | "LINK_SHARE": "链接分享",
337 | "LINK_VALID_PERIOD": "链接有效期:",
338 | "LIST": "列表",
339 | "LIST_ERROR_TIP": "获取加载失败,请重试刷新",
340 | "LIST_ERROR_TIP_DATA": "数据加载失败,请重试刷新",
341 | "LIST_ERROR_TIP_RESULT": "数据成果加载失败,请重试刷新",
342 | "LIST_ERROR_TIP_SERVICE": "服务单加载失败,请重试刷新",
343 | "LIVE_3D_ORTHOPHOTO": "实景三维模型 & 正射图",
344 | "LIVE_IMAGE": "现场图片",
345 | "LOAD_MORE": "加载更多",
346 | "LOAD_MORE_ERROR": "加载失败,请重试",
347 | "LOADED_TIP": "已经到底啦",
348 | "LOADING": "加载中...",
349 | "LOG_EXCAVATION_VOLUME": "本期挖方量(m³)",
350 | "LOG_NET_VOLUME": "本期净方量(m³)",
351 | "LOG_SOIL_VOLUME": "本期堆土量(m³)",
352 | "LOGIN_BUTTON": "登录 MeshKit 账户",
353 | "LOGIN_FAIL": "登录失败",
354 | "LOGOUT": "退出",
355 | "LOGOUT_TIP": "退出登录?",
356 | "LOOSE_FACTOR": "松散系数",
357 | "MEASURE_AREA": "测区面积",
358 | "MEASURING_TIME": "测量时间",
359 | "MGMT_ORG": "管理公司",
360 | "MISSION_NAME": "任务名称",
361 | "MODEL_SOURCE": "模型来源",
362 | "MODEL_TITLE": "模型标题",
363 | "MODIFY_TICKET": "修改服务单",
364 | "MONITOR_CHART": "监测图表",
365 | "MONITOR_LATEST": "最新监测",
366 | "MONITOR_LOG": "监测日志",
367 | "MONITOR_PERIOD": "监测期数",
368 | "MONITOR_START_TIME": "开始监测",
369 | "MONITOR_TIME": "监测时间",
370 | "MONTH": "月",
371 | "MUST_PROVIDE": "必须提供",
372 | "NAME": "姓名",
373 | "NAME_LEN_TIPS": "名字最长为10个字符",
374 | "NETWORK_ERROR_TEXT": "您的网络已断开链接,请确保网络正常后再试",
375 | "NEW_ROLE": "新增角色",
376 | "NO_NEED_COLLECTION": "无需采集",
377 | "NO_PROJECT_TIPS": "请先选择项目",
378 | "NO_QUESTIONNAIRE_DATA": "您这是第一次填写地块信息调查表,没有上一次的数据",
379 | "NO_ROLE_TIP": "暂无角色可以分配,请先添加角色",
380 | "NO_SERVICE_TEXT": "很抱歉,您尚未开通企业服务,n所以无法在此页面看到数据成果,请联系我们的客服人员进行咨询",
381 | "NO_SUB_TIP": "该公司下没有子公司和项目",
382 | "NOT_FOUND_ERROR_TEXT": "请求的资源未被找到",
383 | "NOT_GRANTED": "未授予",
384 | "NOT_PERMISSION_TEXT": "很抱歉,您所在公司的管理员未给您的账号分配权限,n所以您暂时无法访问到任何数据",
385 | "NUMBER": "编号",
386 | "NUMBER_OF_CONTROL_POINTS": "已知控制点(基准点)数量",
387 | "ONE": "一种",
388 | "ONLINE": "在线查看",
389 | "OPEN": "开",
390 | "OPEN_SHARE": "开启分享",
391 | "OPEN_SHARE_ERROR": "该项目开启分享失败",
392 | "OPEN_SHARE_TIP": "该项目开启分享",
393 | "OPERATION": "操作",
394 | "ORG": "所属公司",
395 | "ORG_ADMIN": "所属公司管理员",
396 | "ORTHOPHOTO": "正射图",
397 | "OTHER_INFO": "其他信息",
398 | "OUTPUT_TIME": "输出时间",
399 | "OVERDUE_BUILDING_NAME": "主体进度滞后项目",
400 | "OVERDUE_EARTHWORK_NAME": "土方逾期风险项目",
401 | "PACKAGE_INCLUDE": "套餐内包含",
402 | "PAGE_LEAVE_TIP": "您尚未提交服务单,此时离开您修改的内容将不会被保存,确定要离开吗?",
403 | "PAGE_NOT_FOUND": "你的页面走丢了...",
404 | "PAGE_NUMBER": "第 {page}/{total} 页",
405 | "PANORAMA": "全景图",
406 | "PANORAMA_720D": "720°全景图",
407 | "PANORAMA_LOSSLESS": "无损全景图",
408 | "PASSWORD": "密码",
409 | "PASSWORD_HIDE": "●●●●●●●●",
410 | "PERMANENT": "永久",
411 | "PERMISSION_INCLUDE": "权限包括",
412 | "PERMISSION_ITEM": "权限条目",
413 | "PERMISSION_ITEM_TITLE": "权限条目(若不允许,请在权限条目尾部取消勾选)",
414 | "PERSONAL_INFO": "个人信息",
415 | "PHASE": "期数",
416 | "PHASE_NUMBER": "第{number}期",
417 | "PHONE": "手机号",
418 | "PHONE_LEN_TIPS": "电话号码最长为12个纯数字",
419 | "PHONE_RULE": "请输入正确的电话号码",
420 | "PHONE2": "电话",
421 | "PHOTOS": "照片数量",
422 | "PLAN": "方案",
423 | "PLANE_BENCHMARK": "平面基准",
424 | "PLEASE": "请",
425 | "PLEASE_CHOOSE": "请选择",
426 | "PLEASE_CHOOSE_CONTENT_RESULT": "请选择此项成果!",
427 | "PLEASE_CHOOSE_HEIGHT_SURVEY_TYPE": "选择高程测量类型(可多选)",
428 | "PLEASE_CHOOSE_SPECIFICATION": "请选择此项的规格!",
429 | "PLEASE_CONFIRM_WHETHER_TO_DELETE": "请确认是否删除项目",
430 | "PLEASE_ENTER": "请输入",
431 | "PLEASE_FILL_TIP": "请填写",
432 | "PLEASE_SELECT": "请选择",
433 | "PLEASE_SELECT_PROJECT_FIRST": "请先选择一个项目",
434 | "PLEASE_SELECT_TAGS": "请在下方点选需要查询的公司/项目",
435 | "PLOT_EARTHWORK_STAGE": "待测量地块土方阶段",
436 | "PLOT_MEASURE_AREA": "待测地块面积",
437 | "PLOT_QUESTIONNAIRE": "地块信息调查表",
438 | "POINTS_NAME": "点名称",
439 | "POSITION": "职位",
440 | "PRECISION": "精度",
441 | "PROCESSING": "服务中",
442 | "PROGRESS": "进度",
443 | "PROJECT": "项目",
444 | "PROJECT_BELONGS": "所属公司",
445 | "PROJECT_COMPLETED_AT": "工程完工于",
446 | "PROJECT_EDIT_TIP1": "项目位置编辑开关",
447 | "PROJECT_EDIT_TIP2": "开关打开时拖动将会改动项目定位,如果仅需要查看请关闭此按钮",
448 | "PROJECT_ESTIMATED_COMPLETED_AT": "预计工程完成于",
449 | "PROJECT_HAS_EARTHWORK_MONITOR": "因为项目中存在土方监测",
450 | "PROJECT_HAS_MISSIONS": "因为项目中存在已上传照片或视频的飞行任务",
451 | "PROJECT_HAS_REPORT": "因为项目中存在数据成果",
452 | "PROJECT_HAS_TICKET": "因为项目中存在不在已撤回状态的服务单",
453 | "PROJECT_IS_UNREMOVABLE": "该项目无法删除",
454 | "PROJECT_LIST": "项目列表",
455 | "PROJECT_LOCATION": "项目位置",
456 | "PROJECT_NAME": "项目名称",
457 | "PROJECT_NAME_EMPTY_TIP": "请输入项目名称",
458 | "PROJECT_NAME_LENGTH_TIP": "项目名称长度:2 - 32 字符",
459 | "PROJECT_NUM": "项目数量",
460 | "PROJECT_STAGE": "项目阶段",
461 | "PROJECT_TOTAL_NUM": "项目总数",
462 | "PROJECT_VISUALIZER": "形象进度",
463 | "PROJECT_VISUALIZER_SHARE": "分享形象进度",
464 | "PROJECT_VISUALIZER_SHARING": "形象进度分享中",
465 | "PROJECT2": "所属项目",
466 | "QUESTIONNAIRE_TITLE": "土方测量信息调查表",
467 | "RE_APPLY": "重新申请",
468 | "RECOMMENDED_SCENE": "推荐场景",
469 | "RED_LINE": "红线图",
470 | "RED_LINE_AREA": "红线面积",
471 | "RED_LINE_FILE": "红线图(用地范围文件)",
472 | "REGION_PROJECT": "所属区域/项目",
473 | "REJECTED": "审核不通过",
474 | "REQUIRED": "需要",
475 | "REQUIREMENT_AND_FILE": "需求与文件",
476 | "REQUIREMENT_TIPS": "如上述提供的数据仍无法满足您的需求,您可以在此处描述您的补充需求,我们将会在收到后回应您",
477 | "RESELECT_PROJECT": "重新选择项目",
478 | "RESET": "重置",
479 | "RESET_PASSWORD": "重置密码",
480 | "RESUBMIT": "重新提交",
481 | "RETRY": "重试",
482 | "REVERTED": "已撤销",
483 | "REVERTED_AND_MODIFY": "撤销并修改",
484 | "REVERTED_ERROR_TIP": "撤销失败!",
485 | "REVERTED_ONLY": "撤销",
486 | "REVERTED_TIP": "服务单撤销后会被标记为\"已撤销\",且需要重新提交才会受理,确定要撤销吗?",
487 | "RISK_BUILDING_OVERDUE": "主体进度滞后",
488 | "RISK_EARTHWORK_OVERDUE": "土方逾期风险",
489 | "RISK_PROJECT": "风险项目",
490 | "RISK_WARNING": "风险预警",
491 | "ROAD_AXIS": "道路中心线",
492 | "ROAD_AXIS_FILE": "道路中心线",
493 | "ROAD_AXIS_TIPS": "请上传道路中心线文件",
494 | "ROAD_SCOPE": "道路范围线",
495 | "ROAD_SCOPE_FILE": "道路范围线",
496 | "ROAD_SCOPE_TIPS": "请上传范围线文件",
497 | "ROLE": "角色",
498 | "ROLE_GRANT": "角色授予",
499 | "ROLE_GRANT_TIP": "在【角色授予】里您可以为您公司下的成员分配角色,或为您下层公司下的成员分配角色",
500 | "ROLE_GRANT_TIP1": "当前公司下有",
501 | "ROLE_GRANT_TIP2": "个成员未授予角色,他们可能无法查看任何数据",
502 | "ROLE_MANAGEMENT": "角色管理",
503 | "ROLE_MANAGEMENT_TIP": "您可以为您的公司创建角色,通过为角色制定权限然后赋予对应的成员即可实现权限管理,同时您也可以查看和修改您下层公司的角色配置。请注意,如果您不为您的公司创建角色,则意味着您公司下的成员拥有下方权限列表内的所有权限。",
504 | "ROLE_NAME": "角色名称",
505 | "ROLE_NAME_EXISTED_ERROR": "角色名称不能重复",
506 | "ROLE_NAME_LEN_TIPS": "角色名称最长为10个字符",
507 | "ROLE_NAME_TIPS": "请输入角色名称",
508 | "SAME_AS_CONTACT": "同服务单联系人",
509 | "SCHEME": "方案",
510 | "SCHEME_ADD": "新增方案",
511 | "SCHEME_DESCRIPTION": "方案详情",
512 | "SCHEME_DESCRIPTION_PLACEHOLDER": "若暂无完整的基坑设计(开挖)方案,请在这里描述土方计算的起始标高",
513 | "SCHEME_DESCRIPTION_TIPS": "请输入方案描述",
514 | "SCHEME_FILE_TIPS": "请上传文件",
515 | "SCHEME_NAME": "方案名称",
516 | "SCHEME_NAME_LEN_TIPS": "方案名称最长为32个字符",
517 | "SCHEME_NAME_TIPS": "请输入方案名称",
518 | "SCHEME_TEXT_AREA_TIPS": "无文件,请描述土方算量起始标高",
519 | "SEARCH": "查询",
520 | "SEARCH_LOCATION": "搜索位置",
521 | "SEARCH_PROJECT_EMPTY_DATA": "暂无符合条件的项目",
522 | "SEARCH_PROJECT_PLACEHOLDER": "搜索公司/项目关键字",
523 | "SEARCHING": "搜索中...",
524 | "SECURITY_INSPECTION": "安全文明巡检",
525 | "SELECT_DESIGN_SCHEME": "选择之前上传的方案",
526 | "SELECT_EXIST_DESIGN_SCHEME": "选择之前上传的基坑设计(开挖)方案",
527 | "SELECT_EXIST_DESIGN_SCHEME_TIPS": "请选择之前上传的基坑设计(开挖)方案",
528 | "SELECT_MODEL_TYPE": "选择实景模型类型(可多选)",
529 | "SELECT_PROJECT": "选择项目",
530 | "SELECT_PROJECT_OR_ENTERPRISE": "选择一个公司/项目",
531 | "SELECT_PROJECT_TIP": "请在此处选择项目",
532 | "SELECT_PROJECT_TIP2": "请先选择一个项目,选择项目后,您可以查看该项目的所有数据成果",
533 | "SELECT_PROJECT_TIP3": "查看数据成果,需要在下方选中任意一个公司或者项目,点击确定即可查看信息",
534 | "SELECTED": "已选择",
535 | "SERIAL_NUMBER": "序号",
536 | "SERVER_ERROR_TEXT": "后端服务出错,请重试",
537 | "SERVICE": "服务",
538 | "SERVICE_APPLICATION": "申请服务",
539 | "SERVICE_CONTENT": "服务内容",
540 | "SERVICE_CUSTOM": "自定义成果",
541 | "SERVICE_EARTHWORK_SURVEY": "土方算量",
542 | "SERVICE_HEIGHT_SURVEY": "高程测量",
543 | "SERVICE_LAND_EVALUATION": "投拓看地",
544 | "SERVICE_LIST": "服务单",
545 | "SERVICE_MEASURE": "实景建模",
546 | "SERVICE_PACKAGE": "服务套餐",
547 | "SERVICE_PROJECT_STAGE": "服务类型",
548 | "SERVICE_SECURITY_INSPECTION": "空中巡检",
549 | "SERVICE_TICKET_AUDIT_LIST": "待审核服务单列表",
550 | "SERVICE_TICKET_DETAIL": "服务单详情",
551 | "SERVICE_TICKET_LIST": "服务单列表",
552 | "SHARE_EFFECT_TIP": "已开启分享,链接将于 {time} 失效",
553 | "SHARE_INVALID_TIP": "该链接已失效,可停止分享后重新启用",
554 | "SHARE_OPEN_TIP": "开启后,通过链接可访问该项目下形象进度信息",
555 | "SHARE_PERMANENT_TIP": "已开启分享,链接永久有效",
556 | "SITE_ELEVATION": "场地高程数据",
557 | "SIZE_EXCEEDED_TIP": "您选择的文件大于 {size},无法上传。",
558 | "SOLUTION_DESCRIPTION": "解决方案说明",
559 | "SOURCE": "来源",
560 | "SPACE_TIME_BENCHMARK": "时空基准",
561 | "SPATIAL_DATA_SOURCE_DIALOG_TIP": "根据您的需求,您需要选择{content}",
562 | "SPATIAL_DATA_SOURCE_DIALOG_TIP_LIMITED": "根据您的需求,您只能选择{content}",
563 | "SPATIAL_DATA_SOURCE_DIALOG_TITLE": "选择已有模型",
564 | "SPATIAL_DATA_SOURCE_TYPE_AUTONOMOUS_COLLECTION_DESC": "现场采集并生成新的实景模型,可选上门采集或自主采集",
565 | "SPATIAL_DATA_SOURCE_TYPE_DATA_SOURCE_DESC": "选择本项目已生成的实景模型",
566 | "SPECIFICATION_NUMBER": "规格数值",
567 | "STAGE_CONSTRUCTION": "施工阶段",
568 | "STAGE_DESIGN": "设计阶段",
569 | "STAGE_FILTRATE": "阶段筛选:",
570 | "STAGE_INVESTMENT": "投拓阶段",
571 | "START_TIME": "开工时间",
572 | "STATUS": "状态",
573 | "STATUS_DONE": "已完成",
574 | "STATUS_PROCESSING": "处理中",
575 | "STATUS_WAIT_FOR_UPLOAD": "等待上传",
576 | "STEP_NEXT": "下一步",
577 | "STEP_PREVIOUS": "上一步",
578 | "SUBMIT": "提交",
579 | "SUBMIT_APPLICATION": "提交申请",
580 | "SUBMIT_ERROR": "提交失败,请重试",
581 | "SUBMIT_QUESTIONNAIRE": "提交调查表",
582 | "SUBMIT_QUESTIONNAIRE_ERROR_TIP": "您有未做的题,请填写完毕后再提交",
583 | "SUBMIT_QUESTIONNAIRE_FAILED": "提交调查表失败",
584 | "SUBMITTING": "提交中...",
585 | "SUPPLEMENT_DEMAND": "补充需求",
586 | "SUPPLEMENT_DEMAND_TIPS": "请上传补充需求的文件",
587 | "SUPPLEMENT_INSTRUCTION": "补充说明",
588 | "SWITCH_ENTERPRISE": "切换公司",
589 | "SWITCH_ENTERPRISE_AND_PROJECT": "切换公司/项目",
590 | "SWITCH_ORGANIZATION": "切换公司",
591 | "TABLE_CLOUM_FILE_NUM": "文件(个)",
592 | "TABLE_CLOUM_OPRATION": "操作",
593 | "TABLE_TOTAL_ITEM": "共{number}条",
594 | "TABLE_TOTAL_SELECT_ITEM": "共选择 {number} 条记录",
595 | "TABLE_VIEW_ERROR_TEXT": "数据出错",
596 | "THE_DEGREE_OF_SURFACE_CLEARING": "待测量地块清表程度",
597 | "TICKET_APPROVE_TIP": "审核通过备注如下",
598 | "TICKET_CONTACT": "服务单联系人",
599 | "TICKET_CUSTOMER_APPROVAL_REMARK": "审核通过备注",
600 | "TICKET_CUSTOMER_REFUSED_REASON": "审核不通过原因",
601 | "TICKET_CUSTOMER_REFUSED_TIP": "{cnt} 个服务单审核不通过,请及时处理。",
602 | "TICKET_FORM_DATA_ERROR": "服务单数据获取出错",
603 | "TICKET_FORM_SUBMIT_ERROR": "服务单提交失败,请重试",
604 | "TICKET_NUMBER": "服务单号",
605 | "TICKET_NUMBER2": "服务单编号",
606 | "TICKET_REJECTED_REASON": "已退回原因",
607 | "TICKET_REJECTED_TIP": "{cnt} 个服务单已退回,请及时处理。",
608 | "TICKET_REJECTED_TIP_WHIT_BUTTON": "审核不通过原因如下,请修改后「{reapply}」,若暂时不需要服务,可以「{revoke}」服务单",
609 | "TICKET_REJECTED_TIP2": "审核不通过原因如下,请修改后重新提交",
610 | "TICKET_SERVICE_CARD_TITLE": "选择服务类型",
611 | "TICKET_SERVICE_TIPS": "请选择一种服务类型",
612 | "TICKET_SERVICE_TIPS_1": "* 需要上传红线图",
613 | "TICKET_SERVICE_TIPS_2": "* 需要上传红线图、控制点文件以及设计方案",
614 | "TICKET_STATUS": "服务单状态",
615 | "TICKET_TYPE": "服务单类型",
616 | "TIME_BENCHMARK": "时间基准",
617 | "TITLE_KEYWORDS": "标题关键词",
618 | "TRANSFER_PROJECT": "转移项目",
619 | "TYPE": "类型",
620 | "UNAUTHORIZED_ERROR_TEXT": "登录信息已失效,请重新登录",
621 | "UNFILE": "无文件",
622 | "UNFILLED": "未填写",
623 | "UNIT": "单位",
624 | "UNIT_GE": "个",
625 | "UNIT_M": "米",
626 | "UNIT_ZHANG": "张",
627 | "UNREQUIRED": "不需要",
628 | "UPDATE": "更新",
629 | "UPDATE_AT": "已于{time}更新",
630 | "UPDATE_TIME": "更新时间",
631 | "UPDATED_IN": "更新于",
632 | "UPDATED_THINGS": "更新了{things}",
633 | "UPLOAD": "上传",
634 | "UPLOAD_DESIGN_CONDITION_FILE": "上传设计条件",
635 | "UPLOAD_DESIGN_CONDITION_FILE_TIPS": "(请上传CAD图纸等设计条件)",
636 | "UPLOAD_ERROR": "上传失败",
637 | "UPLOAD_FILE": "上传文件",
638 | "UPLOAD_FILE_TIPS": "控制点(基准点)、范围线(红线图)、其他文件(现场照片等)",
639 | "UPLOAD_NEW_DESIGN_SCHEME": "上传新的基坑设计(开挖)方案",
640 | "UPLOAD_NEW_DESIGN_SCHEME_TIPS": "请上传新的基坑设计(开挖)方案",
641 | "VIEW": "查看",
642 | "VIEW_DETAILS": "查看详情",
643 | "VIEW_DISTRIBUTION": "查看分布",
644 | "VIEW_FULL_PANORAMA": "查看完整全景图",
645 | "VIEW_LIVE_MODEL": "查看实景模型",
646 | "VIEW_ORG": "查看公司信息",
647 | "VIEW_ORTHOPHOTO": "查看正射图",
648 | "VIEW_PROJECT_SCREEN": "查看项目大屏",
649 | "VIEW_RESULT": "查看成果",
650 | "VISUAL_ACTIVATED_ERROR": "您选择的项目已开通了形象进度,无需再次开通",
651 | "VISUAL_DISPLAY": "实景三维模型",
652 | "VISUAL_HAS_TICKET_ERROR": "您选择的项目已提交了开通形象进度的服务单,无需再次提交",
653 | "VISUAL_STUDIO_CODE": "这是一个特殊的文件",
654 | "WEB_LINK": "网页链接",
655 | "WITHDRAW": "撤销",
656 | "WRITE_QUESTIONNAIRE": "填写调查表",
657 | "YEAR": "年"
658 | }
--------------------------------------------------------------------------------
/test_files/src/intl/en_US.json:
--------------------------------------------------------------------------------
1 | {
2 | "A_TEXT": "A text",
3 | "ABSOLUTE_VALUE": "Absolute value",
4 | "ACCEPTING": "Accepting",
5 | "ACCESS_REQUIRED_PASSWORD": "Password access required",
6 | "ACCOUNT_CENTER": "Account center",
7 | "ACCOUNT_INFO": "Account Info",
8 | "ACCURATE_CALCULATION": "Accurate calculation",
9 | "ACHIEVEMENT_FILE": "Achievement files",
10 | "ACTUAL_LANDFORM": "Actual landform",
11 | "ADD": "Add",
12 | "ADD_CUSTOM": "Add custom",
13 | "ADD_DESIGN_SCHEME": "Upload new plan",
14 | "ADD_ROLE": "Add role",
15 | "ADDITIONAL_INFO": "Additional information",
16 | "ADDITIONAL_INFO_TIPS": "Please input the description information to be added",
17 | "ADDNEED_TIPS": "If the above service content does not meet your needs,you can add a description below",
18 | "ADMINISTRATOR": "Administrator",
19 | "AFTER_SALES_EXPERT": "After sales expert",
20 | "AFTER_SALES_EXPERT_DESC1": "Please contact",
21 | "AFTER_SALES_EXPERT_DESC2": "",
22 | "AFTER_SALES_PHONE": "(0755)2692 3283 ",
23 | "AFTER_SALES_PHONE_DESC": "You can scan WeChat code contact or dial the phone below",
24 | "AFTER_SALES_QR_CODE_DESC": "XX售后服务专家",
25 | "AFTER_SALES_QR_CODE_PHONE": "TEL:XXXX",
26 | "AFTER_SALES_QR_CODE_TITLE": "微信扫码联系售后服务",
27 | "ALL": "All",
28 | "ALTER_QUESTIONNAIRE": "Modify the earthwork questionnaire",
29 | "AMOUNT_EXCAVATION": "Excavation amount",
30 | "AMOUNT_FILLING": "Filling amount",
31 | "AMOUNT_NET": "Net amount",
32 | "AND": "and",
33 | "APP_MODEL": "Mesh model",
34 | "APP_PANORAMA": "Mesh panorama",
35 | "APPLICATION_NAME": "Delivery terminal",
36 | "APPLY_NODE": "Application node",
37 | "APPLY_NODE_EARTHWORK_COST_OPTIMIZATION_DESIGN": "Before underground engineering design",
38 | "APPLY_NODE_EARTHWORK_SURVEY_CONSTRUCTION": "Any point in the earthwork construction process",
39 | "APPLY_NODE_GEOLOGICAL_SURVEY_INVESTMENT": "Before the site survey",
40 | "APPLY_NODE_PROJECT_VISUALIZER_CONSTRUCTION": "Any point in the period from start to delivery",
41 | "APPLY_NODE_SECURITY_INSPECTION_CONSTRUCTION": "Any point in the earthwork construction process",
42 | "APPLY_SERVICE_LIST": "Application Service List",
43 | "APPLY_SERVICE_TICKET": "Apply Service Ticket",
44 | "APPOINTED_DOOR_TIME": "Appointed time",
45 | "APPOINTED_TIME": "Appointed time",
46 | "AREA_LEN_TIPS": "Up to 10 digits and up to two digits after the decimal point",
47 | "ASSOCIATE_MISSION": "Associated tasks",
48 | "AUDIT": "Audit",
49 | "AUDIT_APPROVED": "Examination passed",
50 | "AUDIT_APPROVED_REASON": "Reason for approval",
51 | "AUDIT_REASON_RULE": "Please enter at least 5 characters",
52 | "AUDIT_REJECTED_REASON": "Reasons for failing the review",
53 | "AUDIT_TICKET": "Audit ticket",
54 | "AUDIT_TIME": "Review time",
55 | "AUDITOR": "Auditor",
56 | "AUDITOR_CONTACT": "Auditor contact",
57 | "AUTONOMOUS_COLLECTION": "Autonomous collection",
58 | "AUTONOMOUS_COLLECTION_DESC": "Party A will independently complete the data collection operation,and then Qizhi will process and deliver the data. Please notify the Qizhi after-sales team for processing after the data collection is completed.",
59 | "AUTONOMOUS_COLLECTION_DESC_VANKE": "That is,the company purchases UAV equipment and conducts survey field work by itself,and submits the survey data to the survey service company for data processing services after the survey is completed. It is recommended to use this mode for first-line companies that need to monitor earthwork progress or image progress for a single project,have a large company development volume (annual flight demand is greater than or equal to 7 times),and have mastered measurement flight skills.",
60 | "AUTONOMY": "Autonomy",
61 | "AVERAGE_EFFICIENCY_LINE": "Average efficiency line",
62 | "BACKGROUND_SUMMARY": "Background Summary",
63 | "BAD_GATEWAY_ERROR_TEXT": "The server is restarting,please try again later",
64 | "BAD_REQUEST_ERROR_TEXT": "Bad request",
65 | "BASE_AMOUNT": "Construction volume before monitoring",
66 | "BIND_PROJECT": "Bind Project",
67 | "BIND_PROJECT_PLACE_HOLDER": "Please select project which you want to bind",
68 | "BIND_PROJECT_TIP": "Please select your project",
69 | "BRING_LAST_DATA": "Bring in the last data",
70 | "BUILDING_CHECKING": "Subject monitoring",
71 | "BUILDING_STAGE": "Building stage",
72 | "BUSINESS_CONTENT": "Business content",
73 | "BUSINESS_CONTENT_EARTHWORK_COST_OPTIMIZATION_DESIGN": "1. Quick earthwork measurement for cost estimation.n2. The auxiliary design port finds suitable and economical basement and slope design schemes based on the actual terrain to reduce the amount of outbound earthwork.",
74 | "BUSINESS_CONTENT_EARTHWORK_SURVEY_CONSTRUCTION": "1. It is suitable for projects with a large amount of earthwork and tight earthwork schedules in real-time and dynamic monitoring of on-site progress,reasonable arrangements for earthwork schedules,while controlling progress and efficiency,and providing early warning of the construction period.",
75 | "BUSINESS_CONTENT_GEOLOGICAL_SURVEY_INVESTMENT": "1. Free from site management restrictions,it can quickly measure topography and landforms off-site,and output site three-dimensional model data to provide sufficient basis for the design of investment forcing plans.n2. Obtain landform information,accurately estimate earthwork costs,improve the accuracy of feasibility studies and reduce investment risks.",
76 | "BUSINESS_CONTENT_PROJECT_VISUALIZER_CONSTRUCTION": "1. Periodically record the image progress of the project building to provide a basis for settlement.",
77 | "BUSINESS_CONTENT_SECURITY_INSPECTION_CONSTRUCTION": "Utilizing the advantages of UAV high-altitude data collection,using the method of UAV automatic cruise,collecting the status quo of the construction site,inspecting the safety and civilization of the construction site and other related hidden dangers,and issuing a report.",
78 | "CANCEL": "Cancel",
79 | "CHOOSE": "Choose",
80 | "CLICK_FOR_DETAILS": "Click for details",
81 | "CLICK_TO_VIEW": "Click to view",
82 | "CLOSE": "close",
83 | "CLOSE_SHARE": "Close share",
84 | "CLOSE_SHARE_ERROR": "The project failed to stop sharing",
85 | "CLOSE_SHARE_TIP": "The project stopped sharing",
86 | "CLOSURE": "Closure",
87 | "COLLAPSED": "Collapsed",
88 | "COLLECT_NEW_MODEL": "Acquire new models",
89 | "COLLECTION_TIME": "Collection time",
90 | "COLLECTION_TYPE": "Collection type",
91 | "COLLECTOR": "Collector",
92 | "COLLECTOR_AREA": "Collection area",
93 | "COLLECTOR_AREA_DESC": "Please fill in the approximate area of the plot to be collected for flight experts to determine the number of spare batteries",
94 | "COLLECTOR_AREA_TIPS": "Please input collection area",
95 | "COLLECTOR_COLLECT": "Collector contact information",
96 | "COLLECTOR_COLLECT_DESC": "After the collection is completed in the independent collection mode,the Qizhi after-sales team may need to contact the relevant personnel to check the information during data verification.",
97 | "COLLECTOR_JOB": "Position",
98 | "COLLECTOR_JOB_TIPS": "Please input collector jobs",
99 | "COLLECTOR_NAME": "Name",
100 | "COLLECTOR_NAME_TIPS": "Please input collector name",
101 | "COLLECTOR_PHONE": "Contact phone/mobile phone",
102 | "COLLECTOR_PHONE_TIPS": "Please input phone/mobile phone",
103 | "COMMENT": "Comment",
104 | "COMPLETED": "Complete",
105 | "COMPLETED_TIME": "Completion Time",
106 | "CONFIRM": "Confirm",
107 | "CONFIRM_REQUIREMENT": "Confirm requirement",
108 | "CONSTRUCTED_ACTUAL_AMOUNT": "Constructed Actual",
109 | "CONSTRUCTED_ACTUAL_AMOUNT_TIPS": "The actual total engineering quantity is input when the project is completed",
110 | "CONSTRUCTED_AMOUNT": "Constructed Amount",
111 | "CONSTRUCTED_CURRENT_PERIOD": "Current construction volume",
112 | "CONSTRUCTED_EVERY_PERIOD": "Constructed every period",
113 | "CONSTRUCTED_GRAND_AMOUNT": "Constructed Grand",
114 | "CONSTRUCTED_GRAND_AMOUNT_TIPS": "The cumulative construction volume is calculated by superimposing the construction volume in each phase",
115 | "CONSTRUCTED_LATEST_AMOUNT": "Latest Construction",
116 | "CONSTRUCTION_DAILY": "Average daily construction volume",
117 | "CONSTRUCTION_EFFICIENCY": "Construction efficiency",
118 | "CONSTRUCTION_EXPECT_AMOUNT": "Construction Expect",
119 | "CONSTRUCTION_MONITOR_TIME": "Earthwork monitoring time",
120 | "CONSTRUCTION_REMAIN_AMOUNT": "Construction Remain",
121 | "CONSTRUCTION_SPEED": "Construction speed",
122 | "CONSTRUCTION_SPEED_AVERAGE": "Average construction speed",
123 | "CONSTRUCTION_TIME": "Earthwork construction time",
124 | "CONTACT": "Contact",
125 | "CONTACT_INFO": "Contact information",
126 | "CONTACT_INFO_WRITE": "Write Contact information",
127 | "CONTACT_JOB": "Position",
128 | "CONTACT_JOB_PROJECT": "Matchmaker position",
129 | "CONTACT_JOB_TIPS": "Please input position",
130 | "CONTACT_NAME": "Name",
131 | "CONTACT_NAME_PROJECT": "Project docker",
132 | "CONTACT_NAME_TIPS": "Please input name",
133 | "CONTACT_NUMBER": "Contact Number",
134 | "CONTACT_PHONE": "Contact phone/mobile phone",
135 | "CONTACT_PHONE_TIPS": "Please input phone/mobile phone",
136 | "CONTROL_POINT": "Control point information file",
137 | "CONTROL_POINT_DES": "If you do not upload the [Control Point Information File],the results will be affected as follows:
1. It is not possible to provide [Real Scene Model],[Elevation Point Measurement Data Results] or [Calculation] based on local coordinates. Data results]
2. The service of [CAD drawing and real-world model overlay] cannot be provided
If you continue to submit,it will be based on [CGCS2000 National Geodetic Coordinate System] and [WGS84 Earth Coordinate System]. Elevation reference] Output all data results without affecting the accuracy of the data,but the coordinate system may deviate from the local coordinate system to a certain extent,resulting in failure to correspond to the [Red Line of Land Use Range] and [Pit Design Plan]
",
138 | "CONTROL_POINT_FILE": "Control point information file",
139 | "CONTROL_POINT_TIP_TITLE": "The figure below is a schematic diagram of the control points (reference points).
Please find the following points on your project site and confirm the quantity",
140 | "CONTROL_POINT_TIPS": "Please upload the control point information file",
141 | "COORDINATE_AXIS": "coordinate axis",
142 | "COORDINATE_CONVERSION_ACCURACY_TABLE": "Coordinate conversion accuracy table",
143 | "COORDINATE_MODEL": "Model coordinates",
144 | "COORDINATE_ORIGINAL": "Original coordinates",
145 | "COPIED": "Link copied",
146 | "COPY_LINK": "Copy Link",
147 | "COPY_LINK_PASSWORD": "Copy link and password",
148 | "COPY_TEXT": "{link} Password:{password}",
149 | "CREATE_AT": "Create at",
150 | "CREATE_PROJECT_FOR": "Create project for [{name}]",
151 | "CREATE_PROJECT_QUICKLY": "Create Project Quickly",
152 | "CREATE_PROJECT_WITH_STAGE": "The project in {stage} is being created",
153 | "CREATE_TIME": "Create Time",
154 | "CURRENNT_POSITION": "Current position",
155 | "D_VALUE": "D_Value",
156 | "DATA_ACHIEVEMENT_NO_EXIST_TIPS": "The service order did not upload the data results,please contact the after-sales processing (0755) 2692 3283",
157 | "DATA_ACHIEVEMENTS": "Data Achievements",
158 | "DATA_ACHIEVEMENTS_LIST": "Data Achievements List",
159 | "DATA_DETAIL": "Data Detail",
160 | "DATA_DOWNLOAD": "Data Download",
161 | "DATA_FILE_DESCRIPTION": "Click to view the following data file description",
162 | "DATA_REPORTS": "Data reports",
163 | "DATA_RESULTS": "Data Results",
164 | "DATA_RESULTS_VIEW": "View data results",
165 | "DATA_STATISTICS": "Statistics",
166 | "DATE": "Date",
167 | "DATE_FORMAT": "YYYY.MM.DD HH:mm",
168 | "DATE_FORMATTER": "YYYY.MM.DD HH:mm:ss",
169 | "DAY": "Day",
170 | "DAYJS_D": "D",
171 | "DAYJS_M": "M",
172 | "DAYJS_Y": "Y",
173 | "DEFAULT_PACKAGE_RESULT": "Default package results,you can change the data standard and choose more data types in \"Service Content\"",
174 | "DEFAULT_STAGE_TOOLTIP": "Already filter out ",
175 | "DELEGATION": "Delegation",
176 | "DELETE": "Delete",
177 | "DELETE_CONFIRM": "Are you sure you want to delete?",
178 | "DELETE_ERROR": "Error deleting,please try again",
179 | "DELETE_ROLE": "Delete Role",
180 | "DELETE_ROLE_ERROR": "There are members under the organization that use this role,so you cannot delete this role for the time being.",
181 | "DELETE_ROLE_TIP": "Please confirm whether to delete the role [{name}]?",
182 | "DEMAND_DESIGN_SCHEME": "Foundation pit design (excavation) plan",
183 | "DEMAND_DESIGN_SCHEME_TITLE": "According to your choice,please upload/select the foundation pit design (excavation) plan:",
184 | "DEMAND_FILE_TITLE": "According to your choice,you need to upload at least the following files:",
185 | "DEMAND_LIST": "Demand list",
186 | "DEMAND_TYPE": "Demand type",
187 | "DEMO_PANO_DIALOG_TITLE": "Big picture of shooting location",
188 | "DESIGN_CONDITIONS": "Design Conditions",
189 | "DESIGN_SCHEME": "Design Scheme",
190 | "DESIGN_SCHEME_ADD": "Add foundation pit design plan",
191 | "DESIGN_SCHEME_ADD_FROM": "Tip: You are adding a foundation pit design (excavation) plan for ({projectName}),which will not be saved in the project until you submit the service order",
192 | "DESIGN_SCHEME_BIND_ERROR": "The foundation pit design has been used and cannot be deleted",
193 | "DESIGN_SCHEME_DELETE_TIP": "Please confirm whether to delete [{name}]?",
194 | "DESIGN_SCHEME_DESC": "The following data are measured according to the specified design conditions (output time: {time})",
195 | "DESIGN_SCHEME_DESC1": "Design Scheme",
196 | "DESIGN_SCHEME_EDIT_FROM": "The plan will not be saved in the project until you submit the service ticket",
197 | "DESIGN_SCHEME_EMPTY_TIPS": "The project has not added a design scheme,please add a design scheme first",
198 | "DESIGN_SCHEME_FROM": "The design scheme is from",
199 | "DESIGN_SCHEME_NAME_EXIST": "Duplicate name of new design scheme",
200 | "DESIGN_SCHEME_NAME_EXIST_RULE": "Project name already exists,please re-enter",
201 | "DESIGN_SCHEME_PICKER_TIPS": "{count} foundation pit schemes have been selected,and earthwork calculation will be carried out for each selected foundation pit design scheme",
202 | "DESIGN_SCHEME_TIPS": "Please select the plan used for this service",
203 | "DETAILED_ADDRESS": "Detailed Address",
204 | "DETAILED_ADDRESS_EMPTY_TIP": "Please enter the detailed address",
205 | "DIG": "Dig",
206 | "DISTRICT_INPUT_EMPTY_TIP": "Province/City/District acquisition failed",
207 | "DISTRICT_INPUT_PLACEHOLDER": "Province/City/District",
208 | "DOWNLOAD": "Download",
209 | "DRONE_NO_FLY_ZONE_BUTTON_TEXT": "Click here for information about the drone no-fly zone",
210 | "DRONE_NO_FLY_ZONE_DIALOG_A1_1": "1. 首先点击访问链接:",
211 | "DRONE_NO_FLY_ZONE_DIALOG_A1_2": "2. 在上方选择机型为 Phantom 4 RTK;",
212 | "DRONE_NO_FLY_ZONE_DIALOG_A1_3": "3. 在地图左上角输入您项目的所在地;",
213 | "DRONE_NO_FLY_ZONE_DIALOG_A1_4": "4. 拖动地图查看项目附近是否有禁飞区、限飞区等限制无人机飞行的区域。",
214 | "DRONE_NO_FLY_ZONE_DIALOG_A1_WARING": "⚠️ 限制区域需要至少离项目范围 500 m",
215 | "DRONE_NO_FLY_ZONE_DIALOG_A2_1": "1. 下载《民用无人机飞行活动申请审批表》并填写完整;",
216 | "DRONE_NO_FLY_ZONE_DIALOG_A2_2": "2. 将《审批表》提交给表中项目所在地的相关部门审批盖章;",
217 | "DRONE_NO_FLY_ZONE_DIALOG_A2_3": "3. 将盖章后的《审批表》给到XX售后服务团队;",
218 | "DRONE_NO_FLY_ZONE_DIALOG_A2_4": "4. XX将《审批表》递交大疆官方申请解禁,通过后XX将会负责为项目当地无人机实施解禁操作。",
219 | "DRONE_NO_FLY_ZONE_DIALOG_A2_FILE_NAME": "民用无人机飞行活动申请审批表.xlsx",
220 | "DRONE_NO_FLY_ZONE_DIALOG_AFTER_SALES": "XX售后服务:(TEL-0755 2692 3283)",
221 | "DRONE_NO_FLY_ZONE_DIALOG_Q1": "如何查询您的项目是否属于无人机禁飞区?",
222 | "DRONE_NO_FLY_ZONE_DIALOG_Q2": "如何在禁飞区正常执行无人机作业任务?",
223 | "DRONE_NO_FLY_ZONE_DIALOG_Q2_DESC": "如果您的项目恰巧在禁飞区内或十分接近禁飞区,则需要向有关部门申请解禁,才能在禁飞区内使用无人机飞行。具体步骤如下:",
224 | "DRONE_NO_FLY_ZONE_DIALOG_SCAN": "请使用微信扫码",
225 | "DRONE_NO_FLY_ZONE_DIALOG_TITLE": "无人机禁飞区的相关信息",
226 | "EARTHWORK": "Earthwork calculation",
227 | "EARTHWORK_ABBR": "Earthwork calculation",
228 | "EARTHWORK_CHECKING": "Earthwork monitoring",
229 | "EARTHWORK_COST_OPTIMIZATION": "Earthwork cost optimization",
230 | "EARTHWORK_CURRENT_PROGRESS": "Current progress of earthwork",
231 | "EARTHWORK_HAS_RISK": "土方工程有逾期风险",
232 | "EARTHWORK_MONITOR": "Earthwork monitoring",
233 | "EARTHWORK_NO_RISK": "暂无风险",
234 | "EARTHWORK_PROGRESS_BAR": "Earthwork progress bar",
235 | "EARTHWORK_STAGE": "Earthmoving stage",
236 | "EARTHWORK_SURVEY": "Earthwork Survey",
237 | "EARTHWORK_WARN_1": "土方工程有逾期风险,按照当前施工速度,将于 {expectFinishedAt} 完成土方施工,晚于期望完工时间( {expectedAt} ),超出天数 {duration} 天",
238 | "EARTHWORK_WARN_2": "土方工程有逾期风险,{startTime}至{endTime} 的净方量为 0 m³",
239 | "EARTHWORK_WARN_3": "土方工程有逾期风险,{startTime}至{endTime} 填方 {amount}",
240 | "EDIT": "Edit",
241 | "EDIT_DESIGN_SCHEME": "Edit scheme",
242 | "EDIT_MODEL_3D": "Edit model 3d",
243 | "EDIT_PANORAMA": "Edit panorama",
244 | "EDIT_PERSONAL_INFO": "Edit personal information",
245 | "EDIT_PROJECT": "Edit Project",
246 | "EDIT_PROJECT_FOR": "Edit project for [{name}]",
247 | "EDIT_ROLE": "Edit Role",
248 | "ELEVATION_AVERAGE": "Average elevation",
249 | "ELEVATION_MAXIMUM": "Maximum elevation",
250 | "ELEVATION_MINIMUM": "Minimum elevation",
251 | "ELEVATION_POINTS_NUMBER": "elevation points",
252 | "EMAIL": "Email",
253 | "EMPTY": "No",
254 | "EMPTY_DATA": "Empty Data",
255 | "ENTERPRISE_INFO": "Enterprise information",
256 | "ENUM_ACCEPTING": "Accepting",
257 | "ENUM_COMPLETED": "Complete",
258 | "ENUM_CUSTOM": "custom",
259 | "ENUM_CUSTOMER_AUDIT": "Auditing",
260 | "ENUM_CUSTOMER_REFUSED": "Audit not passed",
261 | "ENUM_EARTHWORK_SURVEY": "Earthwork survey",
262 | "ENUM_HEIGHT_SURVEY": "Height survey",
263 | "ENUM_LAND_EVALUATION": "Land evaluation",
264 | "ENUM_MEASURE": "Measure",
265 | "ENUM_MEASURING": "Collecting",
266 | "ENUM_MODEL": "3D Model",
267 | "ENUM_PANORAMA": "Panorama",
268 | "ENUM_PROCESSING": "Processing",
269 | "ENUM_REJECTED": "Returned",
270 | "ENUM_REVERTED": "Reverted",
271 | "ENUM_SECURITY_INSPECTION": "Security inspection",
272 | "ESTIMATED_COMPLETED_TIME": "Estimated completion time",
273 | "ESTIMATED_PLAN": "Estimated plan",
274 | "ESTIMATED_PROGRESS": "Estimated progress",
275 | "ESTIMATED_PROGRESS_TIPS": "Estimated progress is calculated based on the latest construction speed",
276 | "EXAMPLE_3D_MODEL": "Real three-dimensional mode",
277 | "EXAMPLE_CAD_ELEVATION": "CAD drawing of site elevation points",
278 | "EXAMPLE_CONTOUR_MAP": "Site contour map",
279 | "EXAMPLE_EARTHWORK_GRID": "Earthwork calculation grid",
280 | "EXAMPLE_EARTHWORK_REPORT": "Earthwork calculation report",
281 | "EXAMPLE_ELEVATION_GRID": "Elevation grid",
282 | "EXAMPLE_HD_ORTHO": "HD Orthograph",
283 | "EXAMPLE_SECURITY_INSPECTION": "Safety and Civilization Inspection Report",
284 | "EXISTING_SPECIFICATION": "Existing specification!",
285 | "EXPAND": "Expand",
286 | "EXPECTED_COMPLETION_TIME": "Expected completion time",
287 | "FILE": "File",
288 | "FILE_ALREADY_EXIST": "Already Exist Same File Name",
289 | "FILE_DESC": "File",
290 | "FILE_NAME": "File name",
291 | "FILE_NUMBER_TIP": " {count}files",
292 | "FILE_SIZE": "File size",
293 | "FILE_TIP": "According to your choice, please upload/select the following files",
294 | "FILL": "Fill",
295 | "FILL_INFORMATION": "Fill information",
296 | "FILLED": "Filled",
297 | "FLIGHT_TYPE": "Flight type",
298 | "FLIGHT_WAY": "Flight way",
299 | "FORM_TIP": "Note that the items with {asterisk} before the serial number are required",
300 | "FOUNDATION_PIT": "Foundation pit",
301 | "FOUNDATION_PIT_TIPS": "Please select at least one foundation pit design",
302 | "GENERAL": "General",
303 | "GENERAL_DATA": "General Data",
304 | "GEOLOGICAL_SURVEY": "Geological Survey",
305 | "GET_ADDRESS_FAILED": "Get address failed",
306 | "GET_DISTRICT_INFO_FAILED": "Get District info failed",
307 | "GET_PROJECT_DETAIL_FAILED": "Get project detail failed",
308 | "GET_SHARE_INFO_ERROR": "Failed to get shared information",
309 | "GO_TO_DELIVERY": "Go to delivery",
310 | "GRAVITY_BENCHMARK": "Gravity Benchmark",
311 | "GRID_FILE": "Grid file",
312 | "GRID_IMG": "Grid image",
313 | "GRID_TABLE": "Grid table",
314 | "HEIGHT_BENCHMARK": "Height Benchmark",
315 | "HELLO_WORLD": "Hello World!",
316 | "HOME_COLLECTION": "Home collection",
317 | "HOME_COLLECTION_DESC": "After the on-site appointment,the staff provided by Qizhi will complete the on-site data collection,data processing,data delivery and other follow-up procedures. After completion,you can check the data results on the Qizhi cloud platform.",
318 | "HOME_COLLECTION_DESC_VANKE": "That is,the measurement field service and measurement data processing service are provided by the drone measurement unit. For first-tier companies with a small development volume (annual flight requirements less than 7 times),it is recommended to choose this mode first.",
319 | "HOME_TIME": "Home time",
320 | "HOME_TIME_DESC": "Please fill in a preliminary intention to visit time,not necessarily the final time",
321 | "HOME_TIME_TIPS": "Please select the home time",
322 | "IMAGE_UPLOAD_RULE": "You can upload 20 photos no larger than 10M",
323 | "INFO_QUESTIONNAIRE": "Land parcel information questionnaire",
324 | "IS_SOME_SCHEMATIC_DIAGRAM": "The figure below is a schematic diagram of [{title}]",
325 | "JOB_LEN_TIPS": "The maximum length of the position is 10 characters",
326 | "LAND_EVALUATION_REPORT": "Land Evaluation Report",
327 | "LAND_SCOPE": "Land scope file",
328 | "LAND_SCOPE_RED_LINE_TIPS": "Please upload the red line map (land use scope file)",
329 | "LAND_SCOPE_TIPS": "Please upload the land scope file",
330 | "LATEST_DATA_DYNAMICS": "Latest data dynamics",
331 | "LAUNCHED_TIP": "This function will be launched soon.",
332 | "LICE_3D_MODEL": "Live 3D Model",
333 | "LIMIT_EXCEEDED_TIP": "You have uploaded 99 files. Please check if there are duplicate files.",
334 | "LINK_PASSWORD": "Link password",
335 | "LINK_SHARE": "Share link",
336 | "LINK_VALID_PERIOD": "Link validity period:",
337 | "LIST": "List",
338 | "LIST_ERROR_TIP": "Failed to get load,please retry refresh",
339 | "LIST_ERROR_TIP_DATA": "Failed to get load,please retry refresh",
340 | "LIST_ERROR_TIP_RESULT": "Failed to get load,please retry refresh",
341 | "LIST_ERROR_TIP_SERVICE": "Failed to get load,please retry refresh",
342 | "LIVE_3D_ORTHOPHOTO": "Live 3D Models & Orthophoto",
343 | "LIVE_IMAGE": "Live Images",
344 | "LOAD_MORE": "Load More",
345 | "LOAD_MORE_ERROR": "Load More Failed,Retry",
346 | "LOADED_TIP": "No More",
347 | "LOADING": "Loading...",
348 | "LOG_EXCAVATION_VOLUME": "Excavation Volume(m³)",
349 | "LOG_NET_VOLUME": "Net Volume(m³)",
350 | "LOG_SOIL_VOLUME": "Soil Volume(m³)",
351 | "LOGIN_BUTTON": "Login MeshKit Account",
352 | "LOGIN_FAIL": "Login failed",
353 | "LOGOUT": "Logout",
354 | "LOGOUT_TIP": "Log out?",
355 | "LOOSE_FACTOR": "Loose factor",
356 | "MEASURE_AREA": "Measure area",
357 | "MEASURING_TIME": "Measuring Time",
358 | "MGMT_ORG": "Management organization",
359 | "MISSION_NAME": "Mission name",
360 | "MODEL_SOURCE": "Model Source",
361 | "MODEL_TITLE": "Model Title",
362 | "MODIFY_TICKET": "Modify service list",
363 | "MONITOR_CHART": "Monitoring chart",
364 | "MONITOR_LATEST": "Latest Monitoring",
365 | "MONITOR_LOG": "Monitoring log",
366 | "MONITOR_PERIOD": "Number of monitoring periods",
367 | "MONITOR_START_TIME": "Monitoring start time",
368 | "MONITOR_TIME": "Monitoring time",
369 | "MONTH": "M",
370 | "MUST_PROVIDE": "Must provide",
371 | "NAME": "Name",
372 | "NAME_LEN_TIPS": "The name can be up to 10 characters long",
373 | "NETWORK_ERROR_TEXT": "Your network is disconnected,please make sure the network is normal before",
374 | "NEW_ROLE": "New role",
375 | "NO_NEED_COLLECTION": "No need to collect",
376 | "NO_PROJECT_TIPS": "Please select project first",
377 | "NO_QUESTIONNAIRE_DATA": "This is the first time you have filled out the land parcel information survey form,and there is no data from the last time",
378 | "NO_ROLE_TIP": "No role can be assigned,please add a role first",
379 | "NO_SERVICE_TEXT": "Sorry,you have not yet subscribed to the enterprise service,
so you cannot see the data results on this page,please contact our customer service staff for consultation",
380 | "NO_SUB_TIP": "No subsidiary and project under the company",
381 | "NOT_FOUND_ERROR_TEXT": "Resource not found",
382 | "NOT_GRANTED": "Not Granted",
383 | "NOT_PERMISSION_TEXT": "Sorry,the administrator of your organization has not assigned permissions to your account,
so you cannot access any data temporarily",
384 | "NUMBER": "Num",
385 | "NUMBER_OF_CONTROL_POINTS": "Number Of Known Control Points",
386 | "ONE": "one",
387 | "ONLINE": "Online",
388 | "OPEN": "open",
389 | "OPEN_SHARE": "Open share",
390 | "OPEN_SHARE_ERROR": "The project failed to open and share",
391 | "OPEN_SHARE_TIP": "The project is open for sharing",
392 | "OPERATION": "Operation",
393 | "ORG": "Organization",
394 | "ORG_ADMIN": "Owning Organization Administrator",
395 | "ORTHOPHOTO": "Orthophoto",
396 | "OTHER_INFO": "Other Information",
397 | "OUTPUT_TIME": "Output time",
398 | "OVERDUE_BUILDING_NAME": "The main project is behind schedule",
399 | "OVERDUE_EARTHWORK_NAME": "Earthwork overdue risk project",
400 | "PACKAGE_INCLUDE": "Package Include",
401 | "PAGE_LEAVE_TIP": "You have not submitted the service ticket. If you leave at this time,your modified content will not be saved. Are you sure you want to leave?",
402 | "PAGE_NOT_FOUND": "Page Not Found",
403 | "PAGE_NUMBER": "The {page}/{total} page",
404 | "PANORAMA": "Panorama",
405 | "PANORAMA_720D": "720° Panorama",
406 | "PANORAMA_LOSSLESS": "Lossless panorama",
407 | "PASSWORD": "Password",
408 | "PASSWORD_HIDE": "●●●●●●●●",
409 | "PERMANENT": "permanent",
410 | "PERMISSION_INCLUDE": "Permission Include",
411 | "PERMISSION_ITEM": "Permission Item",
412 | "PERMISSION_ITEM_TITLE": "Permission entries (if not allowed,uncheck at the end of permission entries)",
413 | "PERSONAL_INFO": "Personal information",
414 | "PHASE": "Phase",
415 | "PHASE_NUMBER": "Phase {number}",
416 | "PHONE": "Phone number",
417 | "PHONE_LEN_TIPS": "The phone number can be up to 12 pure numbers",
418 | "PHONE_RULE": "Please enter the correct phone number",
419 | "PHONE2": "Phone",
420 | "PHOTOS": "Photos",
421 | "PLAN": "Plan ",
422 | "PLANE_BENCHMARK": "Plane Benchmark",
423 | "PLEASE": "Please",
424 | "PLEASE_CHOOSE": "Please choose",
425 | "PLEASE_CHOOSE_CONTENT_RESULT": "Please choose current result",
426 | "PLEASE_CHOOSE_HEIGHT_SURVEY_TYPE": "Select elevation measurement type (multiple options)",
427 | "PLEASE_CHOOSE_SPECIFICATION": "Please choose a specification",
428 | "PLEASE_CONFIRM_WHETHER_TO_DELETE": "Please confirm whether to delete",
429 | "PLEASE_ENTER": "Please enter",
430 | "PLEASE_FILL_TIP": "Please fill out",
431 | "PLEASE_SELECT": "Please select",
432 | "PLEASE_SELECT_PROJECT_FIRST": "Please select a project first",
433 | "PLEASE_SELECT_TAGS": "Please select the tags below",
434 | "PLOT_EARTHWORK_STAGE": "Earthwork stage of plot",
435 | "PLOT_MEASURE_AREA": "Plot Measure Area",
436 | "PLOT_QUESTIONNAIRE": "Plot Information Questionnaire",
437 | "POINTS_NAME": "Name",
438 | "POSITION": "Position",
439 | "PRECISION": "Precision",
440 | "PROCESSING": "Processing",
441 | "PROGRESS": "Progress",
442 | "PROJECT": "Project",
443 | "PROJECT_BELONGS": "Project Company",
444 | "PROJECT_COMPLETED_AT": "Project Completed At",
445 | "PROJECT_EDIT_TIP1": "Project location edit switch",
446 | "PROJECT_EDIT_TIP2": "Open to edit location,if only view please close",
447 | "PROJECT_ESTIMATED_COMPLETED_AT": "The project is expected to be completed by",
448 | "PROJECT_HAS_EARTHWORK_MONITOR": "because the earthwork monitor exists in the project",
449 | "PROJECT_HAS_MISSIONS": "because the flight mission exists in the project",
450 | "PROJECT_HAS_REPORT": "because the report exists in the project",
451 | "PROJECT_HAS_TICKET": "because the ticket exists in the project",
452 | "PROJECT_IS_UNREMOVABLE": "Project is unremovable",
453 | "PROJECT_LIST": "Project List",
454 | "PROJECT_LOCATION": "Project Location",
455 | "PROJECT_NAME": "Project Name",
456 | "PROJECT_NAME_EMPTY_TIP": "Please enter the project name",
457 | "PROJECT_NAME_LENGTH_TIP": "Project name length: 2-32 characters",
458 | "PROJECT_NUM": "Number of projects",
459 | "PROJECT_STAGE": "Project Stage",
460 | "PROJECT_TOTAL_NUM": "Total number of items",
461 | "PROJECT_VISUALIZER": "Project Visualizer",
462 | "PROJECT_VISUALIZER_SHARE": "Share Project Visualizer",
463 | "PROJECT_VISUALIZER_SHARING": "Project Visualizer Sharing",
464 | "PROJECT2": "Project",
465 | "QUESTIONNAIRE_TITLE": "Survey form for earthwork measurement information",
466 | "RE_APPLY": "Re-apply",
467 | "RECOMMENDED_SCENE": "Recommended Scene",
468 | "RED_LINE": "Redline",
469 | "RED_LINE_AREA": "Redline area",
470 | "RED_LINE_FILE": "Red line map (land use scope document)",
471 | "REGION_PROJECT": "Region/Project",
472 | "REJECTED": "Rejected",
473 | "REQUIRED": "Required",
474 | "REQUIREMENT_AND_FILE": "Requirements and files",
475 | "REQUIREMENT_TIPS": "If the data provided above still cannot meet your needs,you can describe your supplementary needs here,and we will respond to you after receiving it",
476 | "RESELECT_PROJECT": "Reselect Project",
477 | "RESET": "Reset",
478 | "RESET_PASSWORD": "Reset password",
479 | "RESUBMIT": "Resubmit",
480 | "RETRY": "RETRY",
481 | "REVERTED": "Reverted",
482 | "REVERTED_AND_MODIFY": "Revocation and modification",
483 | "REVERTED_ERROR_TIP": "Revocation failed!",
484 | "REVERTED_ONLY": "Reverted",
485 | "REVERTED_TIP": "The service order will be marked as \"cancelled\" after it is withdrawn,and it will need to be resubmitted before it can be processed. Are you sure you want to cancel?",
486 | "RISK_BUILDING_OVERDUE": "Subject progress lag",
487 | "RISK_EARTHWORK_OVERDUE": "Earthwork overdue risk",
488 | "RISK_PROJECT": "Risk project",
489 | "RISK_WARNING": "Risk Warning",
490 | "ROAD_AXIS": "Road axis",
491 | "ROAD_AXIS_FILE": "Road axis",
492 | "ROAD_AXIS_TIPS": "Please upload the road centerline file",
493 | "ROAD_SCOPE": "Road scope",
494 | "ROAD_SCOPE_FILE": "Road scope",
495 | "ROAD_SCOPE_TIPS": "Please upload the range line file",
496 | "ROLE": "Role",
497 | "ROLE_GRANT": "Role Grant",
498 | "ROLE_GRANT_TIP": "In Role Grant you can assign roles to members of your organization or to members of your lower organization",
499 | "ROLE_GRANT_TIP1": "There is",
500 | "ROLE_GRANT_TIP2": "member under the current organization that has not been granted a role,and they may not be able to view any data",
501 | "ROLE_MANAGEMENT": "Role Management",
502 | "ROLE_MANAGEMENT_TIP": "You can create roles for your organization,manage permissions by assigning permissions to roles and then assigning corresponding members,and view and modify role configurations for your underlying organization. Note that if you do not create roles for your organization,it means that the members of your organization have all the permissions in the list below.",
503 | "ROLE_NAME": "Role Name",
504 | "ROLE_NAME_EXISTED_ERROR": "Role name cannot be repeated",
505 | "ROLE_NAME_LEN_TIPS": "The role name can be up to 10 characters long",
506 | "ROLE_NAME_TIPS": "Please enter role name",
507 | "SAME_AS_CONTACT": "Same as contact",
508 | "SCHEME": "Scheme",
509 | "SCHEME_ADD": "New scheme",
510 | "SCHEME_DESCRIPTION": "Please enter the design description",
511 | "SCHEME_DESCRIPTION_PLACEHOLDER": "If there is no complete foundation pit design (excavation) plan,please describe the starting elevation of earthwork calculation here",
512 | "SCHEME_DESCRIPTION_TIPS": "Please enter a plan description",
513 | "SCHEME_FILE_TIPS": "Please upload files",
514 | "SCHEME_NAME": "Please enter the name of the design",
515 | "SCHEME_NAME_LEN_TIPS": "The project name can be up to 32 characters long",
516 | "SCHEME_NAME_TIPS": "Please enter the project name",
517 | "SEARCH": "Search",
518 | "SEARCH_LOCATION": "Search Location",
519 | "SEARCH_PROJECT_EMPTY_DATA": "No eligible project",
520 | "SEARCH_PROJECT_PLACEHOLDER": "Search company project keywords",
521 | "SEARCHING": "Searching...",
522 | "SECURITY_INSPECTION": "security inspection",
523 | "SELECT_DESIGN_SCHEME": "Choose the previously uploaded plan",
524 | "SELECT_EXIST_DESIGN_SCHEME": "Select the previously uploaded foundation pit design (excavation) plan",
525 | "SELECT_EXIST_DESIGN_SCHEME_TIPS": "Please select the previously uploaded foundation pit design (excavation) plan",
526 | "SELECT_MODEL_TYPE": "Select the type of dioramas (multiple)",
527 | "SELECT_PROJECT": "Select project",
528 | "SELECT_PROJECT_OR_ENTERPRISE": "Select company/project",
529 | "SELECT_PROJECT_TIP": "Please select the project here",
530 | "SELECT_PROJECT_TIP2": "Please select a project first. After selecting a project,you can view all the data results of the project",
531 | "SELECT_PROJECT_TIP3": "To view the data results,select any company or project below and click OK to view the information",
532 | "SELECTED": "Selected",
533 | "SERIAL_NUMBER": "Serial Number",
534 | "SERVER_ERROR_TEXT": "Server error,please try again",
535 | "SERVICE": "Service",
536 | "SERVICE_APPLICATION": "Service application",
537 | "SERVICE_CONTENT": "Service content",
538 | "SERVICE_CUSTOM": "Custom Results",
539 | "SERVICE_EARTHWORK_SURVEY": "Earthwork cost optimization",
540 | "SERVICE_HEIGHT_SURVEY": "Earthwork survey",
541 | "SERVICE_LAND_EVALUATION": "Land survey",
542 | "SERVICE_LIST": "Service list",
543 | "SERVICE_MEASURE": "Image progress",
544 | "SERVICE_PACKAGE": "Service package",
545 | "SERVICE_PROJECT_STAGE": "Service type",
546 | "SERVICE_SECURITY_INSPECTION": "security inspection",
547 | "SERVICE_TICKET_AUDIT_LIST": "Audit Service list",
548 | "SERVICE_TICKET_DETAIL": "Service Ticket Detail",
549 | "SERVICE_TICKET_LIST": "Service list",
550 | "SHARE_EFFECT_TIP": "Sharing is turned on,the link will expire at {time}",
551 | "SHARE_INVALID_TIP": "The link has expired,you can stop sharing and re-enable it",
552 | "SHARE_OPEN_TIP": "After opening,you can access the results of the project through the link",
553 | "SHARE_PERMANENT_TIP": "Sharing is turned on, the link is permanent",
554 | "SITE_ELEVATION": "Site elevation data",
555 | "SIZE_EXCEEDED_TIP": "The file you selected is larger than 1GB and cannot be uploaded.",
556 | "SOLUTION_DESCRIPTION": "Soluction Description",
557 | "SOURCE": "Source",
558 | "SPACE_TIME_BENCHMARK": "Space Time Benchmark",
559 | "SPATIAL_DATA_SOURCE_DIALOG_TIP": "According to your needs,you need to choose {content}",
560 | "SPATIAL_DATA_SOURCE_DIALOG_TIP_LIMITED": "According to your needs,you can only choose {content}",
561 | "SPATIAL_DATA_SOURCE_DIALOG_TITLE": "Choose an existing model",
562 | "SPATIAL_DATA_SOURCE_TYPE_AUTONOMOUS_COLLECTION_DESC": "Site collection and generate a new real - scene model,can be selected on - site collection or independent collection",
563 | "SPATIAL_DATA_SOURCE_TYPE_DATA_SOURCE_DESC": "Select the generated real world model for this project",
564 | "SPECIFICATION_NUMBER": "Specification Number",
565 | "STAGE_CONSTRUCTION": "Construction Stage",
566 | "STAGE_DESIGN": "Design Stage",
567 | "STAGE_FILTRATE": "Stage Filtrate: ",
568 | "STAGE_INVESTMENT": "Investment Stage",
569 | "START_TIME": "Start time",
570 | "STATUS": "Status",
571 | "STATUS_DONE": "done",
572 | "STATUS_PROCESSING": "progress",
573 | "STATUS_WAIT_FOR_UPLOAD": "wait for upload",
574 | "STEP_NEXT": "Next",
575 | "STEP_PREVIOUS": "Previous",
576 | "SUBMIT": "Submit",
577 | "SUBMIT_APPLICATION": "Submit application",
578 | "SUBMIT_ERROR": "Submission failed,please try again",
579 | "SUBMIT_QUESTIONNAIRE": "Submit Questionnaire",
580 | "SUBMIT_QUESTIONNAIRE_ERROR_TIP": "If you have any unfinished questions,please fill them out before submitting them",
581 | "SUBMIT_QUESTIONNAIRE_FAILED": "Submit Questionnaire Failed",
582 | "SUBMITTING": "Submitting...",
583 | "SUPPLEMENT_DEMAND": "Supplementary demand",
584 | "SUPPLEMENT_DEMAND_TIPS": "Please upload files for supplementary requirements",
585 | "SUPPLEMENT_INSTRUCTION": "Supplementary instruction",
586 | "SWITCH_ENTERPRISE": "Switch organization",
587 | "SWITCH_ENTERPRISE_AND_PROJECT": "Switch company/project",
588 | "SWITCH_ORGANIZATION": "Switch organization",
589 | "TABLE_TOTAL_ITEM": "Total {number} items",
590 | "TABLE_TOTAL_SELECT_ITEM": "Total {number} items",
591 | "TABLE_VIEW_ERROR_TEXT": "Data Error",
592 | "THE_DEGREE_OF_SURFACE_CLEARING": "The degree of surface clearing of the plot to be measured",
593 | "TICKET_APPROVE_TIP": "The approved remarks are as follows",
594 | "TICKET_CONTACT": "Ticket Contact",
595 | "TICKET_CUSTOMER_APPROVAL_REMARK": "Approved notes",
596 | "TICKET_CUSTOMER_REFUSED_REASON": "Reasons for not passing the audit",
597 | "TICKET_CUSTOMER_REFUSED_TIP": "{cnt} Ticket were not approved",
598 | "TICKET_FORM_DATA_ERROR": "Request order data acquisition error",
599 | "TICKET_FORM_SUBMIT_ERROR": "Request order submission failed,please try again",
600 | "TICKET_NUMBER": "Ticket Number",
601 | "TICKET_NUMBER2": "Ticket Number",
602 | "TICKET_REJECTED_REASON": "Reason for returned",
603 | "TICKET_REJECTED_TIP": "{cnt} Ticket were returned",
604 | "TICKET_REJECTED_TIP_WHIT_BUTTON": "The reasons for not passing the review are as follows. Please modify and \"{reapply}\". If you do not need service temporarily,you can \"{revoke}\" the service order",
605 | "TICKET_REJECTED_TIP2": "The reasons for not passing the audit are as follows. Please revise and resubmit.",
606 | "TICKET_SERVICE_CARD_TITLE": "Choose service type",
607 | "TICKET_SERVICE_TIPS": "Please select a service type",
608 | "TICKET_SERVICE_TIPS_1": "* Need to upload a red line map",
609 | "TICKET_SERVICE_TIPS_2": "* Need to upload red line map,control point file and design plan",
610 | "TICKET_STATUS": "Ticket Status",
611 | "TICKET_TYPE": "Ticket Type",
612 | "TIME_BENCHMARK": "Time Benchmark",
613 | "TITLE_KEYWORDS": "Title Keywords",
614 | "TRANSFER_PROJECT": "Transfer project",
615 | "TYPE": "Type",
616 | "UNAUTHORIZED_ERROR_TEXT": "Unauthorized",
617 | "UNFILE": "No file",
618 | "UNFILLED": "Unfilled",
619 | "UNIT": "Unit",
620 | "UNIT_GE": "",
621 | "UNIT_M": "m",
622 | "UNIT_ZHANG": "",
623 | "UNREQUIRED": "Unrequired",
624 | "UPDATE": "Update",
625 | "UPDATE_AT": "Update At {time}",
626 | "UPDATE_TIME": "Update Time",
627 | "UPDATED_IN": "Updated in",
628 | "UPDATED_THINGS": "Updated {things}",
629 | "UPLOAD": "Upload",
630 | "UPLOAD_DESIGN_CONDITION_FILE": "Upload design conditions",
631 | "UPLOAD_DESIGN_CONDITION_FILE_TIPS": "Please upload design conditions such as CAD drawings",
632 | "UPLOAD_ERROR": "Upload failed",
633 | "UPLOAD_FILE": "Upload file",
634 | "UPLOAD_FILE_TIPS": "Control points (reference points),range lines (red line graph),other documents (on-site photos,etc.)",
635 | "UPLOAD_NEW_DESIGN_SCHEME": "Upload new foundation pit design (excavation) plan",
636 | "UPLOAD_NEW_DESIGN_SCHEME_TIPS": "Please upload new foundation pit design (excavation) plan",
637 | "VIEW": "View",
638 | "VIEW_DETAILS": "View Details",
639 | "VIEW_DISTRIBUTION": "View distribution",
640 | "VIEW_FULL_PANORAMA": "View panorama",
641 | "VIEW_LIVE_MODEL": "View live model",
642 | "VIEW_ORG": "View organization information",
643 | "VIEW_ORTHOPHOTO": "View orthophoto",
644 | "VIEW_PROJECT_SCREEN": "View project screen",
645 | "VIEW_RESULT": "View Result",
646 | "VISUAL_ACTIVATED_ERROR": "The image progress of the project you have selected has been activated,and there is no need to activate it again",
647 | "VISUAL_DISPLAY": "Real three-dimensional mode",
648 | "VISUAL_HAS_TICKET_ERROR": "The project you have selected has already submitted a service order for the image activation progress,and there is no need to submit it again",
649 | "VISUAL_STUDIO_CODE": "Visual Studio",
650 | "WEB_LINK": "Website link",
651 | "WITHDRAW": "Withdraw",
652 | "WRITE_QUESTIONNAIRE": "Fill the earthwork questionnaire",
653 | "YEAR": "Y"
654 | }
--------------------------------------------------------------------------------
/test_files/src/intl/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-anonymous-default-export */
2 | import zh_CN from "./zh_CN.json";
3 | import en_US from "./en_US.json";
4 |
5 | export type ILocales = 'en-US' | 'zh-CN'
6 |
7 | export function getLocales(lang: ILocales)
8 | {
9 | switch (lang)
10 | {
11 | case ('en-US'):
12 | return en_US
13 | case ('zh-CN'):
14 | return zh_CN
15 | default:
16 | return en_US
17 | }
18 | }
19 |
20 | export default {
21 | "en-US": en_US,
22 | "zh-CN": zh_CN
23 | }
--------------------------------------------------------------------------------
/test_files/src/intl/zh_CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "A_TEXT": "一个文本",
3 | "ABSOLUTE_VALUE": "绝对值",
4 | "ACCEPTING": "受理中",
5 | "ACCESS_REQUIRED_PASSWORD": "需要密码访问",
6 | "ACCOUNT_CENTER": "账户中心",
7 | "ACCOUNT_INFO": "账户信息",
8 | "ACCURATE_CALCULATION": "精准算量",
9 | "ACHIEVEMENT_FILE": "成果文件",
10 | "ACTUAL_LANDFORM": "实际地貌",
11 | "ADD": "添加",
12 | "ADD_CUSTOM": "添加自定义",
13 | "ADD_DESIGN_SCHEME": "添加基坑设计(开挖)方案",
14 | "ADD_ROLE": "添加角色",
15 | "ADDITIONAL_INFO": "补充信息",
16 | "ADDITIONAL_INFO_TIPS": "请输入要补充的描述信息",
17 | "ADDNEED_TIPS": "如以上服务内容未满足您的需求,可在下方补充描述",
18 | "ADMINISTRATOR": "管理员",
19 | "AFTER_SALES_EXPERT": "XX售后专家",
20 | "AFTER_SALES_EXPERT_DESC1": "XX已经根据服务单展开服务,其中内容无法修改,",
21 | "AFTER_SALES_EXPERT_DESC2": "如有特殊情况请联系",
22 | "AFTER_SALES_PHONE": "(0755)2692 3283 ",
23 | "AFTER_SALES_PHONE_DESC": "您可以微信扫码联系或拨打下方电话",
24 | "AFTER_SALES_QR_CODE_DESC": "XX售后服务专家",
25 | "AFTER_SALES_QR_CODE_PHONE": "TEL:XXX",
26 | "AFTER_SALES_QR_CODE_TITLE": "微信扫码联系售后服务",
27 | "ALL": "全部",
28 | "ALTER_QUESTIONNAIRE": "修改调查表",
29 | "AMOUNT_EXCAVATION": "挖方量",
30 | "AMOUNT_FILLING": "填方量",
31 | "AMOUNT_NET": "净方量",
32 | "AND": "和",
33 | "APP_MODEL": "XX测量",
34 | "APP_PANORAMA": "XX全景",
35 | "APPLICATION_NAME": "XX云平台",
36 | "APPLY_NODE": "申请节点",
37 | "APPLY_NODE_EARTHWORK_COST_OPTIMIZATION_DESIGN": "地下工程设计前",
38 | "APPLY_NODE_EARTHWORK_SURVEY_CONSTRUCTION": "土方施工过程任意时间点",
39 | "APPLY_NODE_GEOLOGICAL_SURVEY_INVESTMENT": "现场踏勘前",
40 | "APPLY_NODE_PROJECT_VISUALIZER_CONSTRUCTION": "建议开工前开通",
41 | "APPLY_NODE_SECURITY_INSPECTION_CONSTRUCTION": "土方施工过程任意时间点",
42 | "APPLY_SERVICE_LIST": "申请服务列表",
43 | "APPLY_SERVICE_TICKET": "申请服务单",
44 | "APPOINTED_DOOR_TIME": "预约上门时间",
45 | "APPOINTED_TIME": "预约时间",
46 | "AREA_LEN_TIPS": "最长10位数字并且小数点后最长两位数字",
47 | "ASSOCIATE_MISSION": "关联任务",
48 | "AUDIT": "审核",
49 | "AUDIT_APPROVED": "审核通过",
50 | "AUDIT_APPROVED_REASON": "审核通过备注",
51 | "AUDIT_REASON_RULE": "请输入至少5个字符",
52 | "AUDIT_REJECTED_REASON": "审核不通过备注",
53 | "AUDIT_TICKET": "审核服务单",
54 | "AUDIT_TIME": "审核时间",
55 | "AUDITOR": "审核人",
56 | "AUDITOR_CONTACT": "审核人联系方式",
57 | "AUTONOMOUS_COLLECTION": "自主采集",
58 | "AUTONOMOUS_COLLECTION_DESC": "由甲方自主完成数据采集操作,随后再由XX进行数据处理和交付,请在数据采集完成后通知XX售后团队进行处理。",
59 | "AUTONOMOUS_COLLECTION_DESC_VANKE": "即由公司采购无人机设备后自行进行测量外业工作,测量完成后提交测量数据至测量服务公司进行数据处理服务。对于单项目需要进行土方进度或形象进度监控、公司开发体量较大(年度飞行需求大于等于7次)且已掌握测量飞行技巧的一线公司,建议选用本模式。",
60 | "AUTONOMY": "自主采集",
61 | "AVERAGE_EFFICIENCY_LINE": "平均施工速度",
62 | "BACKGROUND_SUMMARY": "背景提要",
63 | "BAD_GATEWAY_ERROR_TEXT": "服务器正在重启,请稍后再试",
64 | "BAD_REQUEST_ERROR_TEXT": "网络请求出错",
65 | "BASE_AMOUNT": "监测前已施工量",
66 | "BIND_PROJECT": "绑定项目",
67 | "BIND_PROJECT_PLACE_HOLDER": "请选择您要绑定的项目",
68 | "BIND_PROJECT_TIP": "请选择项目",
69 | "BRING_LAST_DATA": "带入上一次数据",
70 | "BUILDING_CHECKING": "主体监测中",
71 | "BUILDING_STAGE": "主体阶段",
72 | "BUSINESS_CONTENT": "业务内容",
73 | "BUSINESS_CONTENT_EARTHWORK_COST_OPTIMIZATION_DESIGN": "1. 快速土方测量,供成本测算。
2. 辅助设计端口依据实际地形寻找合适、经济的地库及边坡设计方案,减少土方外运量。",
74 | "BUSINESS_CONTENT_EARTHWORK_SURVEY_CONSTRUCTION": "1. 适用于土方工程量较大、土方工期紧张的项目实时动态监控现场进度情况,合理安排土方工期,同时把控进度、效率,对工期进行预警。",
75 | "BUSINESS_CONTENT_GEOLOGICAL_SURVEY_INVESTMENT": "1. 不受场地管理限制,可于场外快速测量地形地貌,输出场地三维模型数据,为投资强排方案设计提供充足依据。
2. 获取地貌信息,准确预估土方成本,提高可研测算准确性,减少投资风险。",
76 | "BUSINESS_CONTENT_PROJECT_VISUALIZER_CONSTRUCTION": "1. 周期性记录项目楼栋形象进度,为结算提供依据。
2. 形象进度包含:三维模型、正射图、全景图、全周期视频。",
77 | "BUSINESS_CONTENT_SECURITY_INSPECTION_CONSTRUCTION": "利用无人机高空数据采集的优势,通过无人机自动巡航的方法,采集工地现状,巡查工地安全文明等相关隐患,并出具报告。",
78 | "CANCEL": "取消",
79 | "CHOOSE": "选择",
80 | "CLICK_FOR_DETAILS": "点击查看详情",
81 | "CLICK_TO_VIEW": "点击查看",
82 | "CLOSE": "关",
83 | "CLOSE_SHARE": "停止分享",
84 | "CLOSE_SHARE_ERROR": "该项目停止分享失败",
85 | "CLOSE_SHARE_TIP": "该项目停止分享",
86 | "CLOSURE": "关闭",
87 | "COLLAPSED": "收起",
88 | "COLLECT_NEW_MODEL": "采集新模型",
89 | "COLLECTION_TIME": "采集时间",
90 | "COLLECTION_TYPE": "采集方式",
91 | "COLLECTOR": "采集人",
92 | "COLLECTOR_AREA": "采集面积",
93 | "COLLECTOR_AREA_DESC": "请填写大约需要采集的地块面积,用于飞行专家判断携带备用电池数量",
94 | "COLLECTOR_AREA_TIPS": "请输入采集面积",
95 | "COLLECTOR_COLLECT": "采集人联系方式",
96 | "COLLECTOR_COLLECT_DESC": "在自主采集方式下采集完成后,XX售后团队在进行数据验证时可能需要联系采集的相关人员核对信息",
97 | "COLLECTOR_JOB": "职位",
98 | "COLLECTOR_JOB_TIPS": "请输入职位",
99 | "COLLECTOR_NAME": "名字",
100 | "COLLECTOR_NAME_TIPS": "请输入名字",
101 | "COLLECTOR_PHONE": "联系电话/手机",
102 | "COLLECTOR_PHONE_TIPS": "请输入联系电话/手机",
103 | "COMMENT": "批注",
104 | "COMPLETED": "已完成",
105 | "COMPLETED_TIME": "完工时间",
106 | "CONFIRM": "确定",
107 | "CONFIRM_REQUIREMENT": "确认需求",
108 | "CONSTRUCTED_ACTUAL_AMOUNT": "实际总工程量",
109 | "CONSTRUCTED_ACTUAL_AMOUNT_TIPS": "实际总工程量在工程完结时输入",
110 | "CONSTRUCTED_AMOUNT": "已施工量",
111 | "CONSTRUCTED_CURRENT_PERIOD": "本期施工量",
112 | "CONSTRUCTED_EVERY_PERIOD": "每期施工量",
113 | "CONSTRUCTED_GRAND_AMOUNT": "累计施工量",
114 | "CONSTRUCTED_GRAND_AMOUNT_TIPS": "累计施工量由每期施工量叠加所算得",
115 | "CONSTRUCTED_LATEST_AMOUNT": "最新一期施工量",
116 | "CONSTRUCTION_DAILY": "施工速度",
117 | "CONSTRUCTION_EFFICIENCY": "施工效率",
118 | "CONSTRUCTION_EXPECT_AMOUNT": "预计总工程量",
119 | "CONSTRUCTION_MONITOR_TIME": "土方监测时间",
120 | "CONSTRUCTION_REMAIN_AMOUNT": "剩余施工量",
121 | "CONSTRUCTION_SPEED": "施工速度",
122 | "CONSTRUCTION_SPEED_AVERAGE": "平均施工速度",
123 | "CONSTRUCTION_TIME": "土方施工时间",
124 | "CONTACT": "联系人",
125 | "CONTACT_INFO": "联系方式",
126 | "CONTACT_INFO_WRITE": "填写联系方式",
127 | "CONTACT_JOB": "职位",
128 | "CONTACT_JOB_PROJECT": "对接人职位",
129 | "CONTACT_JOB_TIPS": "请输入职位",
130 | "CONTACT_NAME": "名字",
131 | "CONTACT_NAME_PROJECT": "项目对接人",
132 | "CONTACT_NAME_TIPS": "请输入名字",
133 | "CONTACT_NUMBER": "联系电话",
134 | "CONTACT_PHONE": "联系电话/手机",
135 | "CONTACT_PHONE_TIPS": "请输入联系电话/手机",
136 | "CONTROL_POINT": "控制点",
137 | "CONTROL_POINT_DES": "如果您未上传【控制点信息文件】,将对成果产生以下影响:
1. 无法基于本地坐标提供【实景模型】、【高程点测量数据成果】或【算量数据成果】
2. 无法提供【 CAD 图纸与实景模型叠加】服务
若您继续提交,将统一基于【 CGCS2000 国家大地坐标系】和【 WGS84 大地高程基准】输出所有数据成果,不影响数据精度,但该坐标系可能会与当地坐标系存在一定偏差,造成无法与【用地范围红线】和【基坑设计方案】对应
",
138 | "CONTROL_POINT_FILE": "控制点信息文件",
139 | "CONTROL_POINT_TIP_TITLE": "下图为控制点(基准点)示意图,
请在您的项目现场找到以下点位并确认数量",
140 | "CONTROL_POINT_TIPS": "请上传控制点信息文件",
141 | "COORDINATE_AXIS": "坐标轴",
142 | "COORDINATE_CONVERSION_ACCURACY_TABLE": "坐标转换精度表",
143 | "COORDINATE_MODEL": "模型坐标",
144 | "COORDINATE_ORIGINAL": "原始坐标",
145 | "COPIED": "链接已复制",
146 | "COPY_LINK": "复制链接",
147 | "COPY_LINK_PASSWORD": "复制链接与密码",
148 | "COPY_TEXT": "{link} 密码:{password}",
149 | "CREATE_AT": "创建于",
150 | "CREATE_PROJECT_FOR": "为【{name}】创建新项目",
151 | "CREATE_PROJECT_QUICKLY": "快速创建项目",
152 | "CREATE_PROJECT_WITH_STAGE": "正在创建{stage}的项目",
153 | "CREATE_TIME": "创建时间",
154 | "CURRENNT_POSITION": "当前位置",
155 | "D_VALUE": "差值",
156 | "DATA_ACHIEVEMENT_NO_EXIST_TIPS": "服务单未上传数据成果,请联系售后处理(0755)2692 3283",
157 | "DATA_ACHIEVEMENTS": "数据成果",
158 | "DATA_ACHIEVEMENTS_LIST": "数据成果列表",
159 | "DATA_DETAIL": "数据详情",
160 | "DATA_DOWNLOAD": "数据下载",
161 | "DATA_FILE_DESCRIPTION": "点击查看以下数据文件说明",
162 | "DATA_REPORTS": "数据报告",
163 | "DATA_RESULTS": "数据成果",
164 | "DATA_RESULTS_VIEW": "查看数据成果",
165 | "DATA_STATISTICS": "数据统计",
166 | "DATE": "日期",
167 | "DATE_FORMAT": "YYYY.MM.DD HH:mm",
168 | "DATE_FORMATTER": "YYYY年MM月DD日 HH:mm:ss",
169 | "DAY": "天",
170 | "DAYJS_D": "日",
171 | "DAYJS_M": "月",
172 | "DAYJS_Y": "年",
173 | "DEFAULT_PACKAGE_RESULT": "默认套餐成果,可在【服务内容】更改数据标准、选择更多数据类型",
174 | "DEFAULT_STAGE_TOOLTIP": "已为您筛选出",
175 | "DELEGATION": "上门采集",
176 | "DELETE": "删除",
177 | "DELETE_CONFIRM": "你确定要删除吗?",
178 | "DELETE_ERROR": "删除出错,请重试",
179 | "DELETE_ROLE": "删除角色",
180 | "DELETE_ROLE_ERROR": "该公司下存在使用该角色的成员,所以您暂时无法删除此角色。",
181 | "DELETE_ROLE_TIP": "请确认是否删除角色【{name}】?",
182 | "DEMAND_DESIGN_SCHEME": "基坑设计(开挖)方案",
183 | "DEMAND_DESIGN_SCHEME_TITLE": "基坑设计(开挖)方案",
184 | "DEMAND_FILE_TITLE": "根据您的选择,请上传/选择以下文件",
185 | "DEMAND_LIST": "需求单",
186 | "DEMAND_TYPE": "需求类型",
187 | "DEMO_PANO_DIALOG_TITLE": "拍摄位置大图",
188 | "DESIGN_CONDITIONS": "设计条件",
189 | "DESIGN_SCHEME": "设计方案",
190 | "DESIGN_SCHEME_ADD": "添加基坑设计方案",
191 | "DESIGN_SCHEME_ADD_FROM": "您正在为({projectName})新增基坑设计(开挖)方案",
192 | "DESIGN_SCHEME_BIND_ERROR": "该基坑设计方案已被使用,无法删除",
193 | "DESIGN_SCHEME_DELETE_TIP": "请确认是否删除 【{name}】?",
194 | "DESIGN_SCHEME_DESC": "以下数据根据指定的设计条件测得(输出时间:{time})",
195 | "DESIGN_SCHEME_DESC1": "个设计方案",
196 | "DESIGN_SCHEME_EDIT_FROM": "该方案在您提交服务单后才会保存在项目中",
197 | "DESIGN_SCHEME_EMPTY_TIPS": "项目还未添加设计方案,请先添加设计方案",
198 | "DESIGN_SCHEME_FROM": "以下方案来自于",
199 | "DESIGN_SCHEME_NAME_EXIST": "新增设计方案名称重复",
200 | "DESIGN_SCHEME_NAME_EXIST_RULE": "方案名称已存在,请重新输入",
201 | "DESIGN_SCHEME_PICKER_TIPS": "已选择 {count} 个基坑方案,将对已选中的每个基坑设计方案进行土方量计算",
202 | "DESIGN_SCHEME_TIPS": "请选取本次服务采用的方案",
203 | "DETAILED_ADDRESS": "详细地址",
204 | "DETAILED_ADDRESS_EMPTY_TIP": "请输入详细地址",
205 | "DIG": "挖",
206 | "DISTRICT_INPUT_EMPTY_TIP": "省市区获取失败",
207 | "DISTRICT_INPUT_PLACEHOLDER": "省市区",
208 | "DOWNLOAD": "下载",
209 | "DRONE_NO_FLY_ZONE_BUTTON_TEXT": "点击查看无人机禁飞区相关信息",
210 | "DRONE_NO_FLY_ZONE_DIALOG_A1_1": "1. 首先点击访问链接:",
211 | "DRONE_NO_FLY_ZONE_DIALOG_A1_2": "2. 在上方选择机型为 Phantom 4 RTK;",
212 | "DRONE_NO_FLY_ZONE_DIALOG_A1_3": "3. 在地图左上角输入您项目的所在地;",
213 | "DRONE_NO_FLY_ZONE_DIALOG_A1_4": "4. 拖动地图查看项目附近是否有禁飞区、限飞区等限制无人机飞行的区域。",
214 | "DRONE_NO_FLY_ZONE_DIALOG_A1_WARING": "⚠️ 限制区域需要至少离项目范围 500 m",
215 | "DRONE_NO_FLY_ZONE_DIALOG_A2_1": "1. 下载《民用无人机飞行活动申请审批表》并填写完整;",
216 | "DRONE_NO_FLY_ZONE_DIALOG_A2_2": "2. 将《审批表》提交给表中项目所在地的相关部门审批盖章;",
217 | "DRONE_NO_FLY_ZONE_DIALOG_A2_3": "3. 将盖章后的《审批表》给到XX售后服务团队;",
218 | "DRONE_NO_FLY_ZONE_DIALOG_A2_4": "4. XX将《审批表》递交大疆官方申请解禁,通过后XX将会负责为项目当地无人机实施解禁操作。",
219 | "DRONE_NO_FLY_ZONE_DIALOG_A2_FILE_NAME": "民用无人机飞行活动申请审批表.xlsx",
220 | "DRONE_NO_FLY_ZONE_DIALOG_AFTER_SALES": "XX售后服务:(TEL-0755 2692 3283)",
221 | "DRONE_NO_FLY_ZONE_DIALOG_Q1": "如何查询您的项目是否属于无人机禁飞区?",
222 | "DRONE_NO_FLY_ZONE_DIALOG_Q2": "如何在禁飞区正常执行无人机作业任务?",
223 | "DRONE_NO_FLY_ZONE_DIALOG_Q2_DESC": "如果您的项目恰巧在禁飞区内或十分接近禁飞区,则需要向有关部门申请解禁,才能在禁飞区内使用无人机飞行。具体步骤如下:",
224 | "DRONE_NO_FLY_ZONE_DIALOG_SCAN": "请使用微信扫码",
225 | "DRONE_NO_FLY_ZONE_DIALOG_TITLE": "无人机禁飞区的相关信息",
226 | "EARTHWORK": "土方算量",
227 | "EARTHWORK_ABBR": "土方算量",
228 | "EARTHWORK_CHECKING": "土方监测中",
229 | "EARTHWORK_COST_OPTIMIZATION": "土方成本优化",
230 | "EARTHWORK_CURRENT_PROGRESS": "土方工程当前进度",
231 | "EARTHWORK_HAS_RISK": "土方工程有逾期风险",
232 | "EARTHWORK_MONITOR": "土方监测",
233 | "EARTHWORK_NO_RISK": "暂无风险",
234 | "EARTHWORK_PROGRESS_BAR": "土方工程进度条",
235 | "EARTHWORK_STAGE": "土方阶段",
236 | "EARTHWORK_SURVEY": "土方测量",
237 | "EARTHWORK_WARN_1": "土方工程有逾期风险,按照当前施工速度,将于 {expectFinishedAt} 完成土方施工,晚于期望完工时间( {expectedAt} ),超出天数 {duration} 天",
238 | "EARTHWORK_WARN_2": "土方工程有逾期风险,{startTime}至{endTime} 的净方量为 0 m³",
239 | "EARTHWORK_WARN_3": "土方工程有逾期风险,{startTime}至{endTime} 填方 {amount}",
240 | "EDIT": "编辑",
241 | "EDIT_DESIGN_SCHEME": "编辑方案",
242 | "EDIT_MODEL_3D": "编辑模型",
243 | "EDIT_PANORAMA": "编辑全景",
244 | "EDIT_PERSONAL_INFO": "编辑个人信息",
245 | "EDIT_PROJECT": "编辑项目",
246 | "EDIT_PROJECT_FOR": "正在编辑【{name}项目】",
247 | "EDIT_ROLE": "编辑角色",
248 | "ELEVATION_AVERAGE": "平均高程",
249 | "ELEVATION_MAXIMUM": "最大高程",
250 | "ELEVATION_MINIMUM": "最小高程",
251 | "ELEVATION_POINTS_NUMBER": "标高点数量",
252 | "EMAIL": "邮箱",
253 | "EMPTY": "暂无",
254 | "EMPTY_DATA": "暂无数据",
255 | "ENTERPRISE_INFO": "公司信息",
256 | "ENUM_ACCEPTING": "受理中",
257 | "ENUM_COMPLETED": "已完成",
258 | "ENUM_CUSTOM": "自定义",
259 | "ENUM_CUSTOMER_AUDIT": "审核中",
260 | "ENUM_CUSTOMER_REFUSED": "审核不通过",
261 | "ENUM_EARTHWORK_SURVEY": "土方算量",
262 | "ENUM_HEIGHT_SURVEY": "高程测量",
263 | "ENUM_LAND_EVALUATION": "投拓看地",
264 | "ENUM_MEASURE": "实景建模",
265 | "ENUM_MEASURING": "采集中",
266 | "ENUM_MODEL": "三维模型",
267 | "ENUM_PANORAMA": "全景图",
268 | "ENUM_PROCESSING": "服务中",
269 | "ENUM_REJECTED": "已退回",
270 | "ENUM_REVERTED": "已撤销",
271 | "ENUM_SECURITY_INSPECTION": "空中巡检",
272 | "ESTIMATED_COMPLETED_TIME": "预计完工时间",
273 | "ESTIMATED_PLAN": "预估方案",
274 | "ESTIMATED_PROGRESS": "预估进度",
275 | "ESTIMATED_PROGRESS_TIPS": "预估进度按最近一期施工速度推算",
276 | "EXAMPLE_3D_MODEL": "实景三维模型",
277 | "EXAMPLE_CAD_ELEVATION": "场地高程点 CAD 图",
278 | "EXAMPLE_CONTOUR_MAP": "场地等高线图",
279 | "EXAMPLE_EARTHWORK_GRID": "土方算量方格网图",
280 | "EXAMPLE_EARTHWORK_REPORT": "土方算量报告",
281 | "EXAMPLE_ELEVATION_GRID": "高程方格网图",
282 | "EXAMPLE_HD_ORTHO": "高清正射图",
283 | "EXAMPLE_SECURITY_INSPECTION": "安全文明巡检报告",
284 | "EXISTING_SPECIFICATION": "已存在相同规格的选项!",
285 | "EXPAND": "展开",
286 | "EXPECTED_COMPLETION_TIME": "期望完工时间",
287 | "FILE": "文件",
288 | "FILE_ALREADY_EXIST": "已存在文件名相同的文件",
289 | "FILE_DESC": "个文件",
290 | "FILE_NAME": "文件名称",
291 | "FILE_NUMBER_TIP": "等{count}个文件",
292 | "FILE_SIZE": "文件大小",
293 | "FILE_TIP": "根据您的选择,请上传/选择以下文件",
294 | "FILL": "填",
295 | "FILL_INFORMATION": "填写信息",
296 | "FILLED": "已填写",
297 | "FLIGHT_TYPE": "飞行类型",
298 | "FLIGHT_WAY": "飞行方式",
299 | "FORM_TIP": "注意序号前加 * 为必填项",
300 | "FOUNDATION_PIT": "基坑文件",
301 | "FOUNDATION_PIT_TIPS": "请选择至少一个基坑设计方案",
302 | "GENERAL": "通用",
303 | "GENERAL_DATA": "通用数据",
304 | "GEOLOGICAL_SURVEY": "地块勘察",
305 | "GET_ADDRESS_FAILED": "获取地址失败",
306 | "GET_DISTRICT_INFO_FAILED": "获取行政区信息失败",
307 | "GET_PROJECT_DETAIL_FAILED": "获取项目详情失败",
308 | "GET_SHARE_INFO_ERROR": "获取分享信息失败",
309 | "GO_TO_DELIVERY": "数据后台",
310 | "GRAVITY_BENCHMARK": "重力基准",
311 | "GRID_FILE": "方格网文件",
312 | "GRID_IMG": "方格网示意图",
313 | "GRID_TABLE": "方格网表格",
314 | "HEIGHT_BENCHMARK": "高程基准",
315 | "HELLO_WORLD": "你好,世界!",
316 | "HOME_COLLECTION": "上门采集",
317 | "HOME_COLLECTION_DESC": "在经过上门时间预约后,由XX提供人员完成现场数据采集、数据处理、数据交付等后续流程,完成后您可以在XX云平台查收数据成果。",
318 | "HOME_COLLECTION_DESC_VANKE": "即由无人机测量单位进行测量外业服务及测量数据处理服务。对于开发体量较小的一线公司(年度飞行需求小于7次)建议优先选择本模式。",
319 | "HOME_TIME": "上门时间",
320 | "HOME_TIME_DESC": "请填写一个初步的上门时间意向,不一定是最终上门时间",
321 | "HOME_TIME_TIPS": "请选择上门时间",
322 | "IMAGE_UPLOAD_RULE": "可以上传20张不大于10M的照片",
323 | "INFO_QUESTIONNAIRE": "地块信息调查表",
324 | "IS_SOME_SCHEMATIC_DIAGRAM": "下图为【{title}】示意图",
325 | "JOB_LEN_TIPS": "职位最长为10个字符",
326 | "LAND_EVALUATION_REPORT": "投拓看地报告",
327 | "LAND_SCOPE": "用地范围文件",
328 | "LAND_SCOPE_RED_LINE_TIPS": "请上传红线图(用地范围文件)",
329 | "LAND_SCOPE_TIPS": "请上传用地范围文件",
330 | "LATEST_DATA_DYNAMICS": "最新数据动态",
331 | "LAUNCHED_TIP": "该功能即将上线",
332 | "LICE_3D_MODEL": "实景模型",
333 | "LIMIT_EXCEEDED_TIP": "您已经上传了 {limit} 个文件,请检查是否有重复的文件。",
334 | "LINK_PASSWORD": "链接密码",
335 | "LINK_SHARE": "链接分享",
336 | "LINK_VALID_PERIOD": "链接有效期:",
337 | "LIST": "列表",
338 | "LIST_ERROR_TIP": "获取加载失败,请重试刷新",
339 | "LIST_ERROR_TIP_DATA": "数据加载失败,请重试刷新",
340 | "LIST_ERROR_TIP_RESULT": "数据成果加载失败,请重试刷新",
341 | "LIST_ERROR_TIP_SERVICE": "服务单加载失败,请重试刷新",
342 | "LIVE_3D_ORTHOPHOTO": "实景三维模型 & 正射图",
343 | "LIVE_IMAGE": "现场图片",
344 | "LOAD_MORE": "加载更多",
345 | "LOAD_MORE_ERROR": "加载失败,请重试",
346 | "LOADED_TIP": "已经到底啦",
347 | "LOADING": "加载中...",
348 | "LOG_EXCAVATION_VOLUME": "本期挖方量(m³)",
349 | "LOG_NET_VOLUME": "本期净方量(m³)",
350 | "LOG_SOIL_VOLUME": "本期堆土量(m³)",
351 | "LOGIN_BUTTON": "登录 MeshKit 账户",
352 | "LOGIN_FAIL": "登录失败",
353 | "LOGOUT": "退出",
354 | "LOGOUT_TIP": "退出登录?",
355 | "LOOSE_FACTOR": "松散系数",
356 | "MEASURE_AREA": "测区面积",
357 | "MEASURING_TIME": "测量时间",
358 | "MGMT_ORG": "管理公司",
359 | "MISSION_NAME": "任务名称",
360 | "MODEL_SOURCE": "模型来源",
361 | "MODEL_TITLE": "模型标题",
362 | "MODIFY_TICKET": "修改服务单",
363 | "MONITOR_CHART": "监测图表",
364 | "MONITOR_LATEST": "最新监测",
365 | "MONITOR_LOG": "监测日志",
366 | "MONITOR_PERIOD": "监测期数",
367 | "MONITOR_START_TIME": "开始监测",
368 | "MONITOR_TIME": "监测时间",
369 | "MONTH": "月",
370 | "MUST_PROVIDE": "必须提供",
371 | "NAME": "姓名",
372 | "NAME_LEN_TIPS": "名字最长为10个字符",
373 | "NETWORK_ERROR_TEXT": "您的网络已断开链接,请确保网络正常后再试",
374 | "NEW_ROLE": "新增角色",
375 | "NO_NEED_COLLECTION": "无需采集",
376 | "NO_PROJECT_TIPS": "请先选择项目",
377 | "NO_QUESTIONNAIRE_DATA": "您这是第一次填写地块信息调查表,没有上一次的数据",
378 | "NO_ROLE_TIP": "暂无角色可以分配,请先添加角色",
379 | "NO_SERVICE_TEXT": "很抱歉,您尚未开通企业服务,n所以无法在此页面看到数据成果,请联系我们的客服人员进行咨询",
380 | "NO_SUB_TIP": "该公司下没有子公司和项目",
381 | "NOT_FOUND_ERROR_TEXT": "请求的资源未被找到",
382 | "NOT_GRANTED": "未授予",
383 | "NOT_PERMISSION_TEXT": "很抱歉,您所在公司的管理员未给您的账号分配权限,n所以您暂时无法访问到任何数据",
384 | "NUMBER": "编号",
385 | "NUMBER_OF_CONTROL_POINTS": "已知控制点(基准点)数量",
386 | "ONE": "一种",
387 | "ONLINE": "在线查看",
388 | "OPEN": "开",
389 | "OPEN_SHARE": "开启分享",
390 | "OPEN_SHARE_ERROR": "该项目开启分享失败",
391 | "OPEN_SHARE_TIP": "该项目开启分享",
392 | "OPERATION": "操作",
393 | "ORG": "所属公司",
394 | "ORG_ADMIN": "所属公司管理员",
395 | "ORTHOPHOTO": "正射图",
396 | "OTHER_INFO": "其他信息",
397 | "OUTPUT_TIME": "输出时间",
398 | "OVERDUE_BUILDING_NAME": "主体进度滞后项目",
399 | "OVERDUE_EARTHWORK_NAME": "土方逾期风险项目",
400 | "PACKAGE_INCLUDE": "套餐内包含",
401 | "PAGE_LEAVE_TIP": "您尚未提交服务单,此时离开您修改的内容将不会被保存,确定要离开吗?",
402 | "PAGE_NOT_FOUND": "你的页面走丢了...",
403 | "PAGE_NUMBER": "第 {page}/{total} 页",
404 | "PANORAMA": "全景图",
405 | "PANORAMA_720D": "720°全景图",
406 | "PANORAMA_LOSSLESS": "无损全景图",
407 | "PASSWORD": "密码",
408 | "PASSWORD_HIDE": "●●●●●●●●",
409 | "PERMANENT": "永久",
410 | "PERMISSION_INCLUDE": "权限包括",
411 | "PERMISSION_ITEM": "权限条目",
412 | "PERMISSION_ITEM_TITLE": "权限条目(若不允许,请在权限条目尾部取消勾选)",
413 | "PERSONAL_INFO": "个人信息",
414 | "PHASE": "期数",
415 | "PHASE_NUMBER": "第{number}期",
416 | "PHONE": "手机号",
417 | "PHONE_LEN_TIPS": "电话号码最长为12个纯数字",
418 | "PHONE_RULE": "请输入正确的电话号码",
419 | "PHONE2": "电话",
420 | "PHOTOS": "照片数量",
421 | "PLAN": "方案",
422 | "PLANE_BENCHMARK": "平面基准",
423 | "PLEASE": "请",
424 | "PLEASE_CHOOSE": "请选择",
425 | "PLEASE_CHOOSE_CONTENT_RESULT": "请选择此项成果!",
426 | "PLEASE_CHOOSE_HEIGHT_SURVEY_TYPE": "选择高程测量类型(可多选)",
427 | "PLEASE_CHOOSE_SPECIFICATION": "请选择此项的规格!",
428 | "PLEASE_CONFIRM_WHETHER_TO_DELETE": "请确认是否删除项目",
429 | "PLEASE_ENTER": "请输入",
430 | "PLEASE_FILL_TIP": "请填写",
431 | "PLEASE_SELECT": "请选择",
432 | "PLEASE_SELECT_PROJECT_FIRST": "请先选择一个项目",
433 | "PLEASE_SELECT_TAGS": "请在下方点选需要查询的公司/项目",
434 | "PLOT_EARTHWORK_STAGE": "待测量地块土方阶段",
435 | "PLOT_MEASURE_AREA": "待测地块面积",
436 | "PLOT_QUESTIONNAIRE": "地块信息调查表",
437 | "POINTS_NAME": "点名称",
438 | "POSITION": "职位",
439 | "PRECISION": "精度",
440 | "PROCESSING": "服务中",
441 | "PROGRESS": "进度",
442 | "PROJECT": "项目",
443 | "PROJECT_BELONGS": "所属公司",
444 | "PROJECT_COMPLETED_AT": "工程完工于",
445 | "PROJECT_EDIT_TIP1": "项目位置编辑开关",
446 | "PROJECT_EDIT_TIP2": "开关打开时拖动将会改动项目定位,如果仅需要查看请关闭此按钮",
447 | "PROJECT_ESTIMATED_COMPLETED_AT": "预计工程完成于",
448 | "PROJECT_HAS_EARTHWORK_MONITOR": "因为项目中存在土方监测",
449 | "PROJECT_HAS_MISSIONS": "因为项目中存在已上传照片或视频的飞行任务",
450 | "PROJECT_HAS_REPORT": "因为项目中存在数据成果",
451 | "PROJECT_HAS_TICKET": "因为项目中存在不在已撤回状态的服务单",
452 | "PROJECT_IS_UNREMOVABLE": "该项目无法删除",
453 | "PROJECT_LIST": "项目列表",
454 | "PROJECT_LOCATION": "项目位置",
455 | "PROJECT_NAME": "项目名称",
456 | "PROJECT_NAME_EMPTY_TIP": "请输入项目名称",
457 | "PROJECT_NAME_LENGTH_TIP": "项目名称长度:2 - 32 字符",
458 | "PROJECT_NUM": "项目数量",
459 | "PROJECT_STAGE": "项目阶段",
460 | "PROJECT_TOTAL_NUM": "项目总数",
461 | "PROJECT_VISUALIZER": "形象进度",
462 | "PROJECT_VISUALIZER_SHARE": "分享形象进度",
463 | "PROJECT_VISUALIZER_SHARING": "形象进度分享中",
464 | "PROJECT2": "所属项目",
465 | "QUESTIONNAIRE_TITLE": "土方测量信息调查表",
466 | "RE_APPLY": "重新申请",
467 | "RECOMMENDED_SCENE": "推荐场景",
468 | "RED_LINE": "红线图",
469 | "RED_LINE_AREA": "红线面积",
470 | "RED_LINE_FILE": "红线图(用地范围文件)",
471 | "REGION_PROJECT": "所属区域/项目",
472 | "REJECTED": "审核不通过",
473 | "REQUIRED": "需要",
474 | "REQUIREMENT_AND_FILE": "需求与文件",
475 | "REQUIREMENT_TIPS": "如上述提供的数据仍无法满足您的需求,您可以在此处描述您的补充需求,我们将会在收到后回应您",
476 | "RESELECT_PROJECT": "重新选择项目",
477 | "RESET": "重置",
478 | "RESET_PASSWORD": "重置密码",
479 | "RESUBMIT": "重新提交",
480 | "RETRY": "重试",
481 | "REVERTED": "已撤销",
482 | "REVERTED_AND_MODIFY": "撤销并修改",
483 | "REVERTED_ERROR_TIP": "撤销失败!",
484 | "REVERTED_ONLY": "撤销",
485 | "REVERTED_TIP": "服务单撤销后会被标记为\"已撤销\",且需要重新提交才会受理,确定要撤销吗?",
486 | "RISK_BUILDING_OVERDUE": "主体进度滞后",
487 | "RISK_EARTHWORK_OVERDUE": "土方逾期风险",
488 | "RISK_PROJECT": "风险项目",
489 | "RISK_WARNING": "风险预警",
490 | "ROAD_AXIS": "道路中心线",
491 | "ROAD_AXIS_FILE": "道路中心线",
492 | "ROAD_AXIS_TIPS": "请上传道路中心线文件",
493 | "ROAD_SCOPE": "道路范围线",
494 | "ROAD_SCOPE_FILE": "道路范围线",
495 | "ROAD_SCOPE_TIPS": "请上传范围线文件",
496 | "ROLE": "角色",
497 | "ROLE_GRANT": "角色授予",
498 | "ROLE_GRANT_TIP": "在【角色授予】里您可以为您公司下的成员分配角色,或为您下层公司下的成员分配角色",
499 | "ROLE_GRANT_TIP1": "当前公司下有",
500 | "ROLE_GRANT_TIP2": "个成员未授予角色,他们可能无法查看任何数据",
501 | "ROLE_MANAGEMENT": "角色管理",
502 | "ROLE_MANAGEMENT_TIP": "您可以为您的公司创建角色,通过为角色制定权限然后赋予对应的成员即可实现权限管理,同时您也可以查看和修改您下层公司的角色配置。请注意,如果您不为您的公司创建角色,则意味着您公司下的成员拥有下方权限列表内的所有权限。",
503 | "ROLE_NAME": "角色名称",
504 | "ROLE_NAME_EXISTED_ERROR": "角色名称不能重复",
505 | "ROLE_NAME_LEN_TIPS": "角色名称最长为10个字符",
506 | "ROLE_NAME_TIPS": "请输入角色名称",
507 | "SAME_AS_CONTACT": "同服务单联系人",
508 | "SCHEME": "方案",
509 | "SCHEME_ADD": "新增方案",
510 | "SCHEME_DESCRIPTION": "方案详情",
511 | "SCHEME_DESCRIPTION_PLACEHOLDER": "若暂无完整的基坑设计(开挖)方案,请在这里描述土方计算的起始标高",
512 | "SCHEME_DESCRIPTION_TIPS": "请输入方案描述",
513 | "SCHEME_FILE_TIPS": "请上传文件",
514 | "SCHEME_NAME": "方案名称",
515 | "SCHEME_NAME_LEN_TIPS": "方案名称最长为32个字符",
516 | "SCHEME_NAME_TIPS": "请输入方案名称",
517 | "SCHEME_TEXT_AREA_TIPS": "无文件,请描述土方算量起始标高",
518 | "SEARCH": "查询",
519 | "SEARCH_LOCATION": "搜索位置",
520 | "SEARCH_PROJECT_EMPTY_DATA": "暂无符合条件的项目",
521 | "SEARCH_PROJECT_PLACEHOLDER": "搜索公司/项目关键字",
522 | "SEARCHING": "搜索中...",
523 | "SECURITY_INSPECTION": "安全文明巡检",
524 | "SELECT_DESIGN_SCHEME": "选择之前上传的方案",
525 | "SELECT_EXIST_DESIGN_SCHEME": "选择之前上传的基坑设计(开挖)方案",
526 | "SELECT_EXIST_DESIGN_SCHEME_TIPS": "请选择之前上传的基坑设计(开挖)方案",
527 | "SELECT_MODEL_TYPE": "选择实景模型类型(可多选)",
528 | "SELECT_PROJECT": "选择项目",
529 | "SELECT_PROJECT_OR_ENTERPRISE": "选择一个公司/项目",
530 | "SELECT_PROJECT_TIP": "请在此处选择项目",
531 | "SELECT_PROJECT_TIP2": "请先选择一个项目,选择项目后,您可以查看该项目的所有数据成果",
532 | "SELECT_PROJECT_TIP3": "查看数据成果,需要在下方选中任意一个公司或者项目,点击确定即可查看信息",
533 | "SELECTED": "已选择",
534 | "SERIAL_NUMBER": "序号",
535 | "SERVER_ERROR_TEXT": "后端服务出错,请重试",
536 | "SERVICE": "服务",
537 | "SERVICE_APPLICATION": "申请服务",
538 | "SERVICE_CONTENT": "服务内容",
539 | "SERVICE_CUSTOM": "自定义成果",
540 | "SERVICE_EARTHWORK_SURVEY": "土方算量",
541 | "SERVICE_HEIGHT_SURVEY": "高程测量",
542 | "SERVICE_LAND_EVALUATION": "投拓看地",
543 | "SERVICE_LIST": "服务单",
544 | "SERVICE_MEASURE": "实景建模",
545 | "SERVICE_PACKAGE": "服务套餐",
546 | "SERVICE_PROJECT_STAGE": "服务类型",
547 | "SERVICE_SECURITY_INSPECTION": "空中巡检",
548 | "SERVICE_TICKET_AUDIT_LIST": "待审核服务单列表",
549 | "SERVICE_TICKET_DETAIL": "服务单详情",
550 | "SERVICE_TICKET_LIST": "服务单列表",
551 | "SHARE_EFFECT_TIP": "已开启分享,链接将于 {time} 失效",
552 | "SHARE_INVALID_TIP": "该链接已失效,可停止分享后重新启用",
553 | "SHARE_OPEN_TIP": "开启后,通过链接可访问该项目下形象进度信息",
554 | "SHARE_PERMANENT_TIP": "已开启分享,链接永久有效",
555 | "SITE_ELEVATION": "场地高程数据",
556 | "SIZE_EXCEEDED_TIP": "您选择的文件大于 {size},无法上传。",
557 | "SOLUTION_DESCRIPTION": "解决方案说明",
558 | "SOURCE": "来源",
559 | "SPACE_TIME_BENCHMARK": "时空基准",
560 | "SPATIAL_DATA_SOURCE_DIALOG_TIP": "根据您的需求,您需要选择{content}",
561 | "SPATIAL_DATA_SOURCE_DIALOG_TIP_LIMITED": "根据您的需求,您只能选择{content}",
562 | "SPATIAL_DATA_SOURCE_DIALOG_TITLE": "选择已有模型",
563 | "SPATIAL_DATA_SOURCE_TYPE_AUTONOMOUS_COLLECTION_DESC": "现场采集并生成新的实景模型,可选上门采集或自主采集",
564 | "SPATIAL_DATA_SOURCE_TYPE_DATA_SOURCE_DESC": "选择本项目已生成的实景模型",
565 | "SPECIFICATION_NUMBER": "规格数值",
566 | "STAGE_CONSTRUCTION": "施工阶段",
567 | "STAGE_DESIGN": "设计阶段",
568 | "STAGE_FILTRATE": "阶段筛选:",
569 | "STAGE_INVESTMENT": "投拓阶段",
570 | "START_TIME": "开工时间",
571 | "STATUS": "状态",
572 | "STATUS_DONE": "已完成",
573 | "STATUS_PROCESSING": "处理中",
574 | "STATUS_WAIT_FOR_UPLOAD": "等待上传",
575 | "STEP_NEXT": "下一步",
576 | "STEP_PREVIOUS": "上一步",
577 | "SUBMIT": "提交",
578 | "SUBMIT_APPLICATION": "提交申请",
579 | "SUBMIT_ERROR": "提交失败,请重试",
580 | "SUBMIT_QUESTIONNAIRE": "提交调查表",
581 | "SUBMIT_QUESTIONNAIRE_ERROR_TIP": "您有未做的题,请填写完毕后再提交",
582 | "SUBMIT_QUESTIONNAIRE_FAILED": "提交调查表失败",
583 | "SUBMITTING": "提交中...",
584 | "SUPPLEMENT_DEMAND": "补充需求",
585 | "SUPPLEMENT_DEMAND_TIPS": "请上传补充需求的文件",
586 | "SUPPLEMENT_INSTRUCTION": "补充说明",
587 | "SWITCH_ENTERPRISE": "切换公司",
588 | "SWITCH_ENTERPRISE_AND_PROJECT": "切换公司/项目",
589 | "SWITCH_ORGANIZATION": "切换公司",
590 | "TABLE_CLOUM_FILE_NUM": "文件(个)",
591 | "TABLE_CLOUM_OPRATION": "操作",
592 | "TABLE_TOTAL_ITEM": "共{number}条",
593 | "TABLE_TOTAL_SELECT_ITEM": "共选择 {number} 条记录",
594 | "TABLE_VIEW_ERROR_TEXT": "数据出错",
595 | "THE_DEGREE_OF_SURFACE_CLEARING": "待测量地块清表程度",
596 | "TICKET_APPROVE_TIP": "审核通过备注如下",
597 | "TICKET_CONTACT": "服务单联系人",
598 | "TICKET_CUSTOMER_APPROVAL_REMARK": "审核通过备注",
599 | "TICKET_CUSTOMER_REFUSED_REASON": "审核不通过原因",
600 | "TICKET_CUSTOMER_REFUSED_TIP": "{cnt} 个服务单审核不通过,请及时处理。",
601 | "TICKET_FORM_DATA_ERROR": "服务单数据获取出错",
602 | "TICKET_FORM_SUBMIT_ERROR": "服务单提交失败,请重试",
603 | "TICKET_NUMBER": "服务单号",
604 | "TICKET_NUMBER2": "服务单编号",
605 | "TICKET_REJECTED_REASON": "已退回原因",
606 | "TICKET_REJECTED_TIP": "{cnt} 个服务单已退回,请及时处理。",
607 | "TICKET_REJECTED_TIP_WHIT_BUTTON": "审核不通过原因如下,请修改后「{reapply}」,若暂时不需要服务,可以「{revoke}」服务单",
608 | "TICKET_REJECTED_TIP2": "审核不通过原因如下,请修改后重新提交",
609 | "TICKET_SERVICE_CARD_TITLE": "选择服务类型",
610 | "TICKET_SERVICE_TIPS": "请选择一种服务类型",
611 | "TICKET_SERVICE_TIPS_1": "* 需要上传红线图",
612 | "TICKET_SERVICE_TIPS_2": "* 需要上传红线图、控制点文件以及设计方案",
613 | "TICKET_STATUS": "服务单状态",
614 | "TICKET_TYPE": "服务单类型",
615 | "TIME_BENCHMARK": "时间基准",
616 | "TITLE_KEYWORDS": "标题关键词",
617 | "TRANSFER_PROJECT": "转移项目",
618 | "TYPE": "类型",
619 | "UNAUTHORIZED_ERROR_TEXT": "登录信息已失效,请重新登录",
620 | "UNFILE": "无文件",
621 | "UNFILLED": "未填写",
622 | "UNIT": "单位",
623 | "UNIT_GE": "个",
624 | "UNIT_M": "米",
625 | "UNIT_ZHANG": "张",
626 | "UNREQUIRED": "不需要",
627 | "UPDATE": "更新",
628 | "UPDATE_AT": "已于{time}更新",
629 | "UPDATE_TIME": "更新时间",
630 | "UPDATED_IN": "更新于",
631 | "UPDATED_THINGS": "更新了{things}",
632 | "UPLOAD": "上传",
633 | "UPLOAD_DESIGN_CONDITION_FILE": "上传设计条件",
634 | "UPLOAD_DESIGN_CONDITION_FILE_TIPS": "(请上传CAD图纸等设计条件)",
635 | "UPLOAD_ERROR": "上传失败",
636 | "UPLOAD_FILE": "上传文件",
637 | "UPLOAD_FILE_TIPS": "控制点(基准点)、范围线(红线图)、其他文件(现场照片等)",
638 | "UPLOAD_NEW_DESIGN_SCHEME": "上传新的基坑设计(开挖)方案",
639 | "UPLOAD_NEW_DESIGN_SCHEME_TIPS": "请上传新的基坑设计(开挖)方案",
640 | "VIEW": "查看",
641 | "VIEW_DETAILS": "查看详情",
642 | "VIEW_DISTRIBUTION": "查看分布",
643 | "VIEW_FULL_PANORAMA": "查看完整全景图",
644 | "VIEW_LIVE_MODEL": "查看实景模型",
645 | "VIEW_ORG": "查看公司信息",
646 | "VIEW_ORTHOPHOTO": "查看正射图",
647 | "VIEW_PROJECT_SCREEN": "查看项目大屏",
648 | "VIEW_RESULT": "查看成果",
649 | "VISUAL_ACTIVATED_ERROR": "您选择的项目已开通了形象进度,无需再次开通",
650 | "VISUAL_DISPLAY": "实景三维模型",
651 | "VISUAL_HAS_TICKET_ERROR": "您选择的项目已提交了开通形象进度的服务单,无需再次提交",
652 | "VISUAL_STUDIO_CODE": "这是一个特殊的文件",
653 | "WEB_LINK": "网页链接",
654 | "WITHDRAW": "撤销",
655 | "WRITE_QUESTIONNAIRE": "填写调查表",
656 | "YEAR": "年"
657 | }
--------------------------------------------------------------------------------
/test_files/typescript.ts:
--------------------------------------------------------------------------------
1 | "UPPERCASE TEXT"
2 |
3 | '一个文本'
4 |
5 | "这是另一个文本"
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noImplicitAny": true,
4 | "noImplicitReturns": true,
5 | "noUnusedLocals": true,
6 | "noUnusedParameters": true,
7 | "strict": true
8 | }
9 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "target": "es2019",
6 | "lib": ["ES2019"],
7 | "outDir": "out",
8 | "rootDir": "src",
9 | "resolveJsonModule": true,
10 | "sourceMap": true
11 | },
12 | "include": [
13 | "src"
14 | ],
15 | "exclude": [
16 | "node_modules",
17 | "package-lock.json",
18 | "package.json"
19 | ],
20 | "references": [
21 | { "path": "./client" },
22 | { "path": "./server" }
23 | ]
24 | }
--------------------------------------------------------------------------------