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)
|
||
};
|
||
}
|
||
|
||
`
|
||
}
|