英文:
Localize react-monaco-editor and set up monaco-editor-esm-webpack-plugin
问题
以下是翻译好的部分:
我有一个使用ReactJS、react-scripts(webpack)、Monaco Editor和react-monaco-editor构建的Web应用程序。默认情况下,上下文菜单是英文的:
在悬停窗口中,"View Problem"、"Quick Fix"等也是英文的。
现在,我想将这些关键词本地化为中文,有人知道如何做吗?
PS 0: 我已经搜索了类似的问题,比如 https://github.com/microsoft/monaco-editor/blob/main/samples/browser-amd-localized/index.html,但我不知道如何在使用webpack和react-monaco-editor的项目中进行配置(例如,require.config
)。
PS 1: 这里有一个使用react-monaco-editor的工作示例:https://codesandbox.io/p/sandbox/goofy-rgb-q525mq?file=%2Fsrc%2FApp.js%3A16%2C20。调用react-monaco-editor的代码如下,与我的代码中调用react-monaco-editor的方式类似。
<MonacoEditor
width="800"
height="600"
language="javascript"
value={code}
options={options}
onChange={this.onChange}
editorDidMount={this.editorDidMount}
/>
Edit 1: 感谢 @blutorange 和 @VonC,blutorange 在 codesandbox 中的演示确实有效。我尝试在我的代码中使其工作。我在导入monaco-editor
和react-monaco-editor
之前在我的文件中添加了AAA
的导入。我可以看到控制台中打印了After setLocaleData
,但编辑器的关键词仍然是英文的。此外,我在项目中使用了dva。以下是src/index.tsx
的代码:
import './AAA';
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import dva from 'dva';
import './index.css';
import router from './router';
import AuthModel from './models/auth';
import SubscribtionModel from './models/subscription';
import AppModel from './models/app';
import SpreadsheetModel from './models/spreadsheet';
import UsageModel from './models/usage';
import AiModel from './models/ai';
import SettingsModel from './models/settings';
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import zh from './i18n/locales/zh.json';
import en from './i18n/locales/en.json';
function patchOfficeExtension() {
// https://stackoverflow.com/questions/53327711/how-to-add-a-polyfill-to-support-finally-in-edge
//@ts-ignore
OfficeExtension.Promise.prototype.finally = /* Promise.prototype.finally || */ {
finally(fn: any): any {
const onFinally = (cb: any) => OfficeExtension.Promise.resolve(fn()).then(cb);
// @ts-ignore
return this.then(
(result: any) => onFinally(() => result),
(reason: any) => onFinally(() => OfficeExtension.Promise.reject(reason)),
);
},
}.finally;
// tslint:enable
}
// https://chat.openai.com/c/06d8c247-6107-463f-80a9-6b571ee23f88
i18n.use(initReactI18next).init({
lng: 'zh',
// lng: 'en',
resources: {
en: { translation: en },
zh: { translation: zh },
},
fallbackLng: 'en',
// interpolation: { escapeValue: false }
});
//@ts-ignore
//import createLoading from 'dva-loading';
initializeIcons();
// 1. 初始化
const app = dva();
//app.use(createLoading());
// 2. 插件
// app.use({});
// 3. 模型
//@ts-ignore
app.model(AuthModel);
app.model(SubscribtionModel)
app.model(AppModel);
app.model(SpreadsheetModel);
app.model(UsageModel);
app.model(AiModel);
app.model(SettingsModel);
// 4. 路由
//@ts-ignore
app.router(router);
// 5. 启动
app.start('#root');
这是更新后的webpack.config.js
:
const WEBPACK = require('webpack');
const PATH = require('path');
const MonacoWebpackPlugin = require('monaco-editor-esm-webpack-plugin');
module.exports = {
resolve: {
extensions: ['.js', '.jsx']
},
context: __dirname,
entry: {
app: ['./src/index.jsx'] // app: ['./MY_FOLDER_INPUT/MY_FILE_INDEX.jsx']
},
output: {
path: PATH.join(__dirname, '/MY_FOLDER_OUTPUT'),
filename: 'index.js'
},
module: {
rules: [
{
test: /\.js/,
enforce: 'pre',
include: /node_modules[\\\/]monaco-editor[\\\/]esm/,
use: MonacoWebpackPlugin.loader
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
},
plugins: [
new MonacoWebpackPlugin()
]
};
你能告诉我如何精确检测导入的顺序吗? 我尝试使用 source-map-explorer,它生成的映射如下,但我甚至找不到 AAA
:
英文:
I have a web application built with ReactJS, react-scripts (webpack), Monaco Editor, and react-monaco-editor. By default, the context menu is in English:
In the hover window, View Problem
, Quick Fix
, etc. are also in English.
Now, I would like to localize these keywords to Chinese, does anyone know how to do it?
PS 0: I have searched similar issues such as https://github.com/microsoft/monaco-editor/blob/main/samples/browser-amd-localized/index.html, but I don't know how to configure (e.g., require.config
) in a project with webpack and react-monaco-editor.
PS 1: Here is a working sample with react-monaco-editor: https://codesandbox.io/p/sandbox/goofy-rgb-q525mq?file=%2Fsrc%2FApp.js%3A16%2C20. The code calling react-monaco-editor is as follows, which is similar to the way of calling react-monaco-editor in my code.
<MonacoEditor
width="800"
height="600"
language="javascript"
value={code}
options={options}
onChange={this.onChange}
editorDidMount={this.editorDidMount}
/>
Edit 1: Thanks to @blutorange and @VonC, the demo of blutorange in codesandbox does work. I tried to make it work in my code. I put import AAA
in my files before the import of monaco-editor
and react-monaco-editor
. I can see that After setLocaleData
is printed in the console, but the keywords of the editor are still in English. Additionally, I use dva in the project. The follows is src/index.tsx
:
import './AAA';
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import dva from 'dva';
import './index.css';
import router from './router';
import AuthModel from './models/auth';
import SubscribtionModel from './models/subscription';
import AppModel from './models/app';
import SpreadsheetModel from './models/spreadsheet';
import UsageModel from './models/usage';
import AiModel from './models/ai';
import SettingsModel from './models/settings';
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import zh from './i18n/locales/zh.json';
import en from './i18n/locales/en.json';
function patchOfficeExtension() {
// https://stackoverflow.com/questions/53327711/how-to-add-a-polyfill-to-support-finally-in-edge
//@ts-ignore
OfficeExtension.Promise.prototype.finally = /* Promise.prototype.finally || */ {
finally(fn: any): any {
const onFinally = (cb: any) => OfficeExtension.Promise.resolve(fn()).then(cb);
// @ts-ignore
return this.then(
(result: any) => onFinally(() => result),
(reason: any) => onFinally(() => OfficeExtension.Promise.reject(reason)),
);
},
}.finally;
// tslint:enable
}
// https://chat.openai.com/c/06d8c247-6107-463f-80a9-6b571ee23f88
i18n.use(initReactI18next).init({
lng: 'zh',
// lng: 'en',
resources: {
en: { translation: en },
zh: { translation: zh },
},
fallbackLng: 'en',
// interpolation: { escapeValue: false }
});
//@ts-ignore
//import createLoading from 'dva-loading';
initializeIcons();
// 1. Initialize
const app = dva();
//app.use(createLoading());
// 2. Plugins
// app.use({});
// 3. Model
//@ts-ignore
app.model(AuthModel);
app.model(SubscribtionModel)
app.model(AppModel);
app.model(SpreadsheetModel);
app.model(UsageModel);
app.model(AiModel);
app.model(SettingsModel);
// 4. Router
//@ts-ignore
app.router(router);
// 5. Start
app.start('#root');
Here is the updated webpack.config.js
:
const WEBPACK = require('webpack');
const PATH = require('path');
const MonacoWebpackPlugin = require('monaco-editor-esm-webpack-plugin');
module.exports = {
resolve: {
extensions: ['.js', '.jsx']
},
context: __dirname,
entry: {
app: ['./src/index.jsx'] // app: ['./MY_FOLDER_INPUT/MY_FILE_INDEX.jsx']
},
output: {
path: PATH.join(__dirname, '/MY_FOLDER_OUTPUT'),
filename: 'index.js'
},
module: {
rules: [
{
test: /\.js/,
enforce: 'pre',
include: /node_modules[\\\/]monaco-editor[\\\/]esm/,
use: MonacoWebpackPlugin.loader
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
},
plugins: [
new MonacoWebpackPlugin()
]
};
Could you tell how I could detect precisely the order of imports? I tried to use source-map-explorer, it gives a map as follows, but i cannot even find AAA
:
答案1
得分: 2
请注意,我将仅翻译您提供的代码部分:
// Add your code here
// 在这里添加您的代码
// available options are documented at
// 可用选项在文档中有详细记录
// app: ['./MY_FOLDER_INPUT/MY_FILE_INDEX.jsx']
// app: ['./MY_FOLDER_INPUT/MY_FILE_INDEX.jsx']
// index.jsx or index.js
// index.jsx或index.js
// adjust this path to point to your App component
// 调整此路径以指向您的App组件
请注意,这些是您提供的代码部分的翻译。如果您需要更多帮助或有其他问题,请随时提出。
英文:
Check first if a webpack's plugin for monaco editor like monaco-editor-nls
would be enough for your use case.
You would need modify the editorDidMount
function in your React component to also set the locale data.
Following its "Using" section example, that would be for you:
import React, { useEffect } from 'react';
import MonacoEditor from 'react-monaco-editor';
import { setLocaleData } from 'monaco-editor-nls';
import zh_CN from 'monaco-editor-nls/locale/zh-hans';
const App = () => {
const code = "// Add your code here";
const options = {
selectOnLineNumbers: true
};
const editorDidMount = (editor, monaco) => {
console.log('editorDidMount', editor);
editor.focus();
// After mounting the editor, set the locale data
setLocaleData(zh_CN);
// You may then use the `monaco` instance as necessary
// Note that you already have an editor instance,
// so there's no need to call `monaco.editor.create` unless you're creating another editor instance
};
const onChange = (newValue, e) => {
console.log('onChange', newValue, e);
};
return (
<MonacoEditor
width="800"
height="600"
language="javascript"
theme="vs-dark"
value={code}
options={options}
onChange={onChange}
editorDidMount={editorDidMount}
/>
);
};
export default App;
The setLocaleData
is called in the editorDidMount
function to set the locale data after the Monaco Editor instance has been created. In your case, as you are using react-monaco-editor
, the editor instance is created automatically by the MonacoEditor
component, and you get that instance in the editorDidMount
function.
As commented, it is not enough on its own.
Try adding monaco-editor-esm-webpack-plugin (npm install monaco-editor-esm-webpack-plugin
)
And update your webpack.config.js
:
const MonacoWebpackPlugin = require('monaco-editor-esm-webpack-plugin');
const PATH = require('path');
module.exports = {
resolve: {
extensions: ['.js', '.jsx']
},
context: __dirname,
entry: {
app: ['./src/index.jsx'] // app: ['./MY_FOLDER_INPUT/MY_FILE_INDEX.jsx']
},
output: {
path: PATH.join(__dirname, '/MY_FOLDER_OUTPUT'),
filename: 'index.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
},
plugins: [
new MonacoWebpackPlugin({
// available options are documented at https://github.com/microsoft/monaco-editor-webpack-plugin#options
languages: ['json'],
features: ['coreCommands', 'find']
})
]
};
In the plugins
array, you see now new MonacoWebpackPlugin()
with some options. In the languages
array, you should list the languages you want to use in your editor. In the features
array, you can enable additional editor features.
In your main script file, you should be able to import the locale data from monaco-editor-nls
and set the locale data using the setLocaleData
function.
import { setLocaleData } from 'monaco-editor-nls';
import zh_CN from 'monaco-editor-nls/locale/zh-hans';
setLocaleData(zh_CN);
blutorange adds in the comments:
> From what I remember working on monaco
editor, it eagerly reads and caches translations, so I suspect that when you call setLocateData
when the component is mounted, it might already be too late, as monaco editor will have already loaded the English translations.
>
> There's also this note in the webpack plugin: You must import/require [monaco-editor]
after setLocaleData
.
That means, to ensure setLocaleData
is called before the Monaco Editor loads, you might want to try calling it in a higher level component or as early as possible in your application's entry point file.
Your entry point file should include:
// index.jsx or index.js
import { setLocaleData } from 'monaco-editor-nls';
import zh_CN from 'monaco-editor-nls/locale/zh-hans';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App'; // adjust this path to point to your App component
setLocaleData(zh_CN);
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Here, setLocaleData
would be called before your App
component (which presumably contains the MonacoEditor
component) is imported and used. The Monaco Editor is likely to be imported for the first time in your App
component or in a component within App
, ensuring setLocaleData
is called before any Monaco related imports.
Do update your webpack configuration to use the monaco-editor-esm-webpack-plugin
as outlined previously.
blutorange adds in the comments:
> I can see 3 potential issues, though there might be more.
>
> 1. First of all, the dependencies in that sandbox project are pretty old.
> 2. Secondly, you're importing your App (which imports monaco-editor) before you call setLocaleData
... but webpack does not care anyways.
> 3. I changed the import order in the sandbox, but if you take a look at the generated bundle, you can see the following:
All imports are moved to the top, and the actual code is executed afterwards.
And if you debug into the bundled code, it also does not seem to ever execute the replaced localize-nls
code.
That means: make sure all your packages are up-to-date:
npm install react-monaco-editor@latest
npm install monaco-editor-nls@latest
npm install monaco-editor-esm-webpack-plugin@latest
Then try and modify how you import monaco-editor
and react-monaco-editor
and where you call setLocaleData
. That is tricky because you cannot control how react-monaco-editor
imports monaco-editor
internally.
Instead of using setLocaleData
in editorDidMount
or the entry point file, try using it in a separate module that you import before you import monaco-editor
. That would ensure setLocaleData
is called before monaco-editor
is imported.
Define a separate module (let's call it set-locale.js
):
// set-locale.js
import { setLocaleData } from 'monaco-editor-nls';
import zh_CN from 'monaco-editor-nls/locale/zh-hans';
setLocaleData(zh_CN);
And use it in your component file:
import './set-locale';
import * as monaco from 'monaco-editor';
// rest of your component code
That approach attempts to ensure setLocaleData
is called before monaco-editor
is imported.
blutorange also points out in the comments:
- a working setup
monaco-editor/react
which completely sidesteps the issue by not bundling monaco editor whatsoever but just loads it from a CDN at runtime.
npm install @monaco-editor/react
That is a good point. @monaco-editor/react
is indeed another wrapper for Monaco Editor that might suit your needs better.
That means, on that last point, you would not have to deal with webpack configurations for bundling the editor yourself.
import Editor from "@monaco-editor/react";
function App() {
const handleEditorDidMount = (editor, monaco) => {
// here you can access the monaco instance
// you could apply a language change here
}
return (
<Editor
height="90vh"
defaultLanguage="javascript"
defaultValue="// some comment"
onMount={handleEditorDidMount}
/>
);
}
To localize the editor with this package, you would have to find a way to load the locale data after the editor is loaded from the CDN.
See loader-config
import { loader } from '@monaco-editor/react';
// you can change the source of the monaco files
loader.config({ paths: { vs: '...' } });
// you can configure the locales
loader.config({ 'vs/nls': { availableLanguages: { '*': 'de' } } });
// or
loader.config({
paths: {
vs: '...',
},
'vs/nls': {
availableLanguages: {
'*': 'zh-cn',
},
},
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论