feat: monaco 编辑器菜单汉化
This commit is contained in:
parent
cdb58a28ab
commit
6db714d819
@ -49,6 +49,7 @@
|
|||||||
"jsplumb": "^2.15.6",
|
"jsplumb": "^2.15.6",
|
||||||
"jwchat": "^2.0.3",
|
"jwchat": "^2.0.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"magic-string": "^0.30.17",
|
||||||
"md-editor-v3": "^5.6.1",
|
"md-editor-v3": "^5.6.1",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
|
|||||||
257
Web/public/monaco/vite-plugin-i18n-nls.ts
Normal file
257
Web/public/monaco/vite-plugin-i18n-nls.ts
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
import type { Plugin } from 'vite'
|
||||||
|
import MagicString from 'magic-string'
|
||||||
|
|
||||||
|
export enum Languages {
|
||||||
|
bg = 'bg',
|
||||||
|
cs = 'cs',
|
||||||
|
de = 'de',
|
||||||
|
en_gb = 'en-gb',
|
||||||
|
es = 'es',
|
||||||
|
fr = 'fr',
|
||||||
|
hu = 'hu',
|
||||||
|
id = 'id',
|
||||||
|
it = 'it',
|
||||||
|
ja = 'ja',
|
||||||
|
ko = 'ko',
|
||||||
|
nl = 'nl',
|
||||||
|
pl = 'pl',
|
||||||
|
ps = 'ps',
|
||||||
|
pt_br = 'pt-br',
|
||||||
|
ru = 'ru',
|
||||||
|
tr = 'tr',
|
||||||
|
uk = 'uk',
|
||||||
|
zh_hans = 'zh-hans',
|
||||||
|
zh_hant = 'zh-hant',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
locale: Languages;
|
||||||
|
localeData?: Record<string, any>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在vite中dev模式下会使用esbuild对node_modules进行预编译,导致找不到映射表中的filepath,
|
||||||
|
* 需要在预编译之前进行替换
|
||||||
|
* @param options 替换语言包
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function esbuildPluginMonacoEditorNls(options: Options) {
|
||||||
|
options = Object.assign({ locale: Languages.en_gb }, options)
|
||||||
|
const CURRENT_LOCALE_DATA = getLocalizeMapping(options.locale, options.localeData)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'esbuild-plugin-monaco-editor-nls',
|
||||||
|
setup(build) {
|
||||||
|
build.onLoad({ filter: /esm[/\\]vs[/\\]nls\.js/ }, async() => {
|
||||||
|
return {
|
||||||
|
contents: getLocalizeCode(CURRENT_LOCALE_DATA),
|
||||||
|
loader: 'js',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
build.onLoad(
|
||||||
|
{ filter: /monaco-editor[/\\]esm[/\\]vs.+\.js/ },
|
||||||
|
async args => {
|
||||||
|
return {
|
||||||
|
contents: transformLocalizeFuncCode(
|
||||||
|
args.path,
|
||||||
|
CURRENT_LOCALE_DATA,
|
||||||
|
),
|
||||||
|
loader: 'js',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用了monaco-editor-nls的语言映射包,把原始localize(data, message)的方法,替换成了localize(path, data, defaultMessage)
|
||||||
|
* vite build 模式下,使用rollup处理
|
||||||
|
* @param options 替换语言包
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export default function(options: Options): Plugin {
|
||||||
|
options = Object.assign({ locale: Languages.en_gb }, options)
|
||||||
|
const CURRENT_LOCALE_DATA = getLocalizeMapping(options.locale, options.localeData)
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'rollup-plugin-monaco-editor-nls',
|
||||||
|
|
||||||
|
enforce: 'pre',
|
||||||
|
|
||||||
|
load(filepath) {
|
||||||
|
if (/esm[/\\]vs[/\\]nls\.js/.test(filepath)) {
|
||||||
|
|
||||||
|
return getLocalizeCode(CURRENT_LOCALE_DATA)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transform(code, filepath) {
|
||||||
|
if (
|
||||||
|
/monaco-editor[/\\]esm[/\\]vs.+\.js/.test(filepath)
|
||||||
|
&& !/esm[/\\]vs[/\\].*nls\.js/.test(filepath)
|
||||||
|
) {
|
||||||
|
const re = /monaco-editor[/\\]esm[/\\](.+)(?=\.js)/
|
||||||
|
if (re.exec(filepath) && code.includes('localize(')) {
|
||||||
|
let path = RegExp.$1
|
||||||
|
path = path.replace(/\\/g, '/')
|
||||||
|
code = code.replace(/localize\(/g, `localize('${path}', `)
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: code,
|
||||||
|
|
||||||
|
/** 使用magic-string 生成 source map */
|
||||||
|
map: new MagicString(code).generateMap({
|
||||||
|
includeContent: true,
|
||||||
|
hires: true,
|
||||||
|
source: filepath,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换调用方法接口参数,替换成相应语言包语言
|
||||||
|
* @param filepath 路径
|
||||||
|
* @param CURRENT_LOCALE_DATA 替换规则
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function transformLocalizeFuncCode(
|
||||||
|
filepath: string,
|
||||||
|
_CURRENT_LOCALE_DATA: string,
|
||||||
|
) {
|
||||||
|
let code = fs.readFileSync(filepath, 'utf8')
|
||||||
|
const re = /monaco-editor[/\\]esm[/\\](.+)(?=\.js)/
|
||||||
|
if (re.exec(filepath)) {
|
||||||
|
let path = RegExp.$1
|
||||||
|
path = path.replace(/\\/g, '/')
|
||||||
|
|
||||||
|
// if (filepath.includes('contextmenu')) {
|
||||||
|
// console.log(filepath);
|
||||||
|
// console.log(JSON.parse(CURRENT_LOCALE_DATA)[path]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// console.log(path, JSON.parse(CURRENT_LOCALE_DATA)[path])
|
||||||
|
code = code.replace(/localize\(/g, `localize('${path}', `)
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取语言包
|
||||||
|
* @param locale 语言
|
||||||
|
* @param localeData
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function getLocalizeMapping(locale: Languages, localeData: Record<string, any> | undefined = undefined) {
|
||||||
|
if(localeData)return JSON.stringify(localeData)
|
||||||
|
const locale_data_path = path.join(__dirname, `./locale/${locale}.json`)
|
||||||
|
|
||||||
|
return fs.readFileSync(locale_data_path) as unknown as string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换代码
|
||||||
|
* @param CURRENT_LOCALE_DATA 语言包
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function getLocalizeCode(CURRENT_LOCALE_DATA: string) {
|
||||||
|
return `
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
// eslint-disable-next-line local/code-import-patterns
|
||||||
|
import { getNLSLanguage, getNLSMessages } from './nls.messages.js';
|
||||||
|
// eslint-disable-next-line local/code-import-patterns
|
||||||
|
export { getNLSLanguage, getNLSMessages } from './nls.messages.js';
|
||||||
|
const isPseudo = getNLSLanguage() === 'pseudo' || (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0);
|
||||||
|
function _format(message, args) {
|
||||||
|
let result;
|
||||||
|
if (args.length === 0) {
|
||||||
|
result = message;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = message.replace(/\\{(\\d+)\\}/g, (match, rest) => {
|
||||||
|
const index = rest[0];
|
||||||
|
const arg = args[index];
|
||||||
|
let result = match;
|
||||||
|
if (typeof arg === 'string') {
|
||||||
|
result = arg;
|
||||||
|
}
|
||||||
|
else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) {
|
||||||
|
result = String(arg);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isPseudo) {
|
||||||
|
// FF3B and FF3D is the Unicode zenkaku representation for [ and ]
|
||||||
|
result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @skipMangle
|
||||||
|
*/
|
||||||
|
// export function localize(data /* | number when built */, message /* | null when built */, ...args) {
|
||||||
|
// if (typeof data === 'number') {
|
||||||
|
// return _format(lookupMessage(data, message), args);
|
||||||
|
// }
|
||||||
|
// return _format(message, args);
|
||||||
|
// }
|
||||||
|
// ------------------------invoke----------------------------------------
|
||||||
|
export function localize(path, data, defaultMessage, ...args) {
|
||||||
|
if (typeof data === 'number') {
|
||||||
|
return _format(lookupMessage(data, message), args);
|
||||||
|
}
|
||||||
|
var key = typeof data === 'object' ? data.key : data;
|
||||||
|
var data = ${CURRENT_LOCALE_DATA} || {};
|
||||||
|
var message = (data[path] || data?.contents?.[path] || {})[key];
|
||||||
|
if (!message) {
|
||||||
|
message = defaultMessage;
|
||||||
|
}
|
||||||
|
return _format(message, args);
|
||||||
|
}
|
||||||
|
// ------------------------invoke----------------------------------------
|
||||||
|
/**
|
||||||
|
* Only used when built: Looks up the message in the global NLS table.
|
||||||
|
* This table is being made available as a global through bootstrapping
|
||||||
|
* depending on the target context.
|
||||||
|
*/
|
||||||
|
function lookupMessage(index, fallback) {
|
||||||
|
const message = getNLSMessages()?.[index];
|
||||||
|
if (typeof message !== 'string') {
|
||||||
|
if (typeof fallback === 'string') {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
throw new Error(\`!!! NLS MISSING: \${index} !!!\`);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @skipMangle
|
||||||
|
*/
|
||||||
|
export function localize2(data /* | number when built */, originalMessage, ...args) {
|
||||||
|
let message;
|
||||||
|
if (typeof data === 'number') {
|
||||||
|
message = lookupMessage(data, originalMessage);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message = originalMessage;
|
||||||
|
}
|
||||||
|
const value = _format(message, args);
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
original: originalMessage === message ? value : _format(originalMessage, args)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
}
|
||||||
14458
Web/public/monaco/zh-hans.json
Normal file
14458
Web/public/monaco/zh-hans.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,9 @@ import { CodeInspectorPlugin } from 'code-inspector-plugin';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { visualizer } from 'rollup-plugin-visualizer';
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
import { webUpdateNotice } from '@plugin-web-update-notification/vite';
|
import { webUpdateNotice } from '@plugin-web-update-notification/vite';
|
||||||
|
// monaco 菜单汉化 https://wf0.github.io/example/plugins/I18n.html
|
||||||
|
import monacoZhHans from './public/monaco/zh-hans.json';
|
||||||
|
import nlsPlugin, { Languages, esbuildPluginMonacoEditorNls } from './public/monaco/vite-plugin-i18n-nls';
|
||||||
|
|
||||||
const pathResolve = (dir: string) => {
|
const pathResolve = (dir: string) => {
|
||||||
return resolve(__dirname, '.', dir);
|
return resolve(__dirname, '.', dir);
|
||||||
@ -52,11 +55,27 @@ const viteConfig = defineConfig((mode: ConfigEnv) => {
|
|||||||
// exclude: [/\.(br)$/, /\.(gz)$/], // 排除指定文件
|
// exclude: [/\.(br)$/, /\.(gz)$/], // 排除指定文件
|
||||||
}),
|
}),
|
||||||
JSON.parse(env.VITE_OPEN_CDN) ? buildConfig.cdn() : null,
|
JSON.parse(env.VITE_OPEN_CDN) ? buildConfig.cdn() : null,
|
||||||
|
// 生产环境 monaco 汉化
|
||||||
|
nlsPlugin({
|
||||||
|
locale: Languages.zh_hans,
|
||||||
|
localeData: monacoZhHans,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
root: process.cwd(),
|
root: process.cwd(),
|
||||||
resolve: { alias },
|
resolve: { alias },
|
||||||
base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
|
base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
|
||||||
optimizeDeps: { exclude: ['vue-demi'] },
|
optimizeDeps: {
|
||||||
|
exclude: ['vue-demi'],
|
||||||
|
// 开发环境 monaco 汉化 使用 esbuild 配置实现
|
||||||
|
esbuildOptions: {
|
||||||
|
plugins: [
|
||||||
|
esbuildPluginMonacoEditorNls({
|
||||||
|
locale: Languages.zh_hans,
|
||||||
|
localeData: monacoZhHans,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: env.VITE_PORT as unknown as number,
|
port: env.VITE_PORT as unknown as number,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user