258 lines
8.0 KiB
TypeScript
258 lines
8.0 KiB
TypeScript
|
|
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)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
`
|
|||
|
|
}
|