本地化 react-monaco-editor 并设置 monaco-editor-esm-webpack-plugin。

huangapple go评论77阅读模式
英文:

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-editorreact-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

本地化 react-monaco-editor 并设置 monaco-editor-esm-webpack-plugin。

英文:

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:

本地化 react-monaco-editor 并设置 monaco-editor-esm-webpack-plugin。

In the hover window, View Problem, Quick Fix, etc. are also in English.

本地化 react-monaco-editor 并设置 monaco-editor-esm-webpack-plugin。

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.

    &lt;MonacoEditor
width=&quot;800&quot;
height=&quot;600&quot;
language=&quot;javascript&quot;
value={code}
options={options}
onChange={this.onChange}
editorDidMount={this.editorDidMount}
/&gt;

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 &#39;./AAA&#39;;
import &#39;react-app-polyfill/ie11&#39;;
import &#39;react-app-polyfill/stable&#39;;
import dva from &#39;dva&#39;;
import &#39;./index.css&#39;;
import router from &#39;./router&#39;;
import AuthModel from &#39;./models/auth&#39;;
import SubscribtionModel from &#39;./models/subscription&#39;;
import AppModel from &#39;./models/app&#39;;
import SpreadsheetModel from &#39;./models/spreadsheet&#39;;
import UsageModel from &#39;./models/usage&#39;;
import AiModel from &#39;./models/ai&#39;;
import SettingsModel from &#39;./models/settings&#39;;
import { initializeIcons } from &#39;office-ui-fabric-react/lib/Icons&#39;;
import i18n from &#39;i18next&#39;;
import { initReactI18next } from &#39;react-i18next&#39;;
import zh from &#39;./i18n/locales/zh.json&#39;;
import en from &#39;./i18n/locales/en.json&#39;;
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) =&gt; OfficeExtension.Promise.resolve(fn()).then(cb);
// @ts-ignore
return this.then(
(result: any) =&gt; onFinally(() =&gt; result),
(reason: any) =&gt; onFinally(() =&gt; OfficeExtension.Promise.reject(reason)),
);
},
}.finally;
// tslint:enable
}
// https://chat.openai.com/c/06d8c247-6107-463f-80a9-6b571ee23f88
i18n.use(initReactI18next).init({
lng: &#39;zh&#39;,
// lng: &#39;en&#39;,
resources: {
en: { translation: en },
zh: { translation: zh },
},
fallbackLng: &#39;en&#39;,
// interpolation: { escapeValue: false }
});
//@ts-ignore
//import createLoading from &#39;dva-loading&#39;;
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(&#39;#root&#39;);

Here is the updated webpack.config.js:

const WEBPACK = require(&#39;webpack&#39;);
const PATH = require(&#39;path&#39;);
const MonacoWebpackPlugin = require(&#39;monaco-editor-esm-webpack-plugin&#39;);
module.exports = {
resolve: {
extensions: [&#39;.js&#39;, &#39;.jsx&#39;]
},
context: __dirname,
entry: {
app: [&#39;./src/index.jsx&#39;] // app: [&#39;./MY_FOLDER_INPUT/MY_FILE_INDEX.jsx&#39;]
},
output: {
path: PATH.join(__dirname, &#39;/MY_FOLDER_OUTPUT&#39;),
filename: &#39;index.js&#39;
},
module: {
rules: [
{
test: /\.js/,
enforce: &#39;pre&#39;,
include: /node_modules[\\\/]monaco-editor[\\\/]esm/,
use: MonacoWebpackPlugin.loader
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: &quot;babel-loader&quot;
}
}
]
},
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:

本地化 react-monaco-editor 并设置 monaco-editor-esm-webpack-plugin。

答案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 &#39;react&#39;;
import MonacoEditor from &#39;react-monaco-editor&#39;;
import { setLocaleData } from &#39;monaco-editor-nls&#39;;
import zh_CN from &#39;monaco-editor-nls/locale/zh-hans&#39;;

const App = () =&gt; {
  const code = &quot;// Add your code here&quot;;
  const options = {
    selectOnLineNumbers: true
  };

  const editorDidMount = (editor, monaco) =&gt; {
    console.log(&#39;editorDidMount&#39;, 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&#39;s no need to call `monaco.editor.create` unless you&#39;re creating another editor instance
  };

  const onChange = (newValue, e) =&gt; {
    console.log(&#39;onChange&#39;, newValue, e);
  };

  return (
    &lt;MonacoEditor
      width=&quot;800&quot;
      height=&quot;600&quot;
      language=&quot;javascript&quot;
      theme=&quot;vs-dark&quot;
      value={code}
      options={options}
      onChange={onChange}
      editorDidMount={editorDidMount}
    /&gt;
  );
};

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(&#39;monaco-editor-esm-webpack-plugin&#39;);
const PATH = require(&#39;path&#39;);

module.exports = {
  resolve: {
    extensions: [&#39;.js&#39;, &#39;.jsx&#39;]
  },
  context: __dirname,
  entry: {
    app: [&#39;./src/index.jsx&#39;] // app: [&#39;./MY_FOLDER_INPUT/MY_FILE_INDEX.jsx&#39;]
  },
  output: {
    path: PATH.join(__dirname, &#39;/MY_FOLDER_OUTPUT&#39;),
    filename: &#39;index.js&#39;
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: &quot;babel-loader&quot;
        }
      }
    ]
  },
  plugins: [
    new MonacoWebpackPlugin({
      // available options are documented at https://github.com/microsoft/monaco-editor-webpack-plugin#options
      languages: [&#39;json&#39;], 
      features: [&#39;coreCommands&#39;, &#39;find&#39;]
    })
  ]
};

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 &#39;monaco-editor-nls&#39;;
import zh_CN from &#39;monaco-editor-nls/locale/zh-hans&#39;;

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 &#39;monaco-editor-nls&#39;;
import zh_CN from &#39;monaco-editor-nls/locale/zh-hans&#39;;
import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;
import App from &#39;./App&#39;;  // adjust this path to point to your App component

setLocaleData(zh_CN);

ReactDOM.render(
  &lt;React.StrictMode&gt;
    &lt;App /&gt;
  &lt;/React.StrictMode&gt;,
  document.getElementById(&#39;root&#39;)
);

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:
本地化 react-monaco-editor 并设置 monaco-editor-esm-webpack-plugin。
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 &#39;monaco-editor-nls&#39;;
import zh_CN from &#39;monaco-editor-nls/locale/zh-hans&#39;;

setLocaleData(zh_CN);

And use it in your component file:

import &#39;./set-locale&#39;;
import * as monaco from &#39;monaco-editor&#39;;

// 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 &quot;@monaco-editor/react&quot;;

function App() {
  const handleEditorDidMount = (editor, monaco) =&gt; {
    // here you can access the monaco instance
    // you could apply a language change here
  }

  return (
    &lt;Editor
      height=&quot;90vh&quot;
      defaultLanguage=&quot;javascript&quot;
      defaultValue=&quot;// some comment&quot;
      onMount={handleEditorDidMount}
    /&gt;
  );
}

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 &#39;@monaco-editor/react&#39;;

// you can change the source of the monaco files
loader.config({ paths: { vs: &#39;...&#39; } });

// you can configure the locales
loader.config({ &#39;vs/nls&#39;: { availableLanguages: { &#39;*&#39;: &#39;de&#39; } } });

// or
loader.config({
  paths: {
    vs: &#39;...&#39;,
  },
  &#39;vs/nls&#39;: {
    availableLanguages: {
      &#39;*&#39;: &#39;zh-cn&#39;,
    },
  },
});

huangapple
  • 本文由 发表于 2023年7月24日 19:56:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76754247.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定