ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

使用 Transformers.js 在 React 中构建多语言翻译应用(Web Worker 实战指南)

使用 Transformers.js 在 React 中构建多语言翻译应用(Web Worker 实战指南) 使用 Transformers.js 在 React 中构建多语言翻译应用Web Worker 实战指南【免费下载链接】transformers.jsState-of-the-art Machine Learning for the web. Run Transformers directly in your browser, with no need for a server!项目地址: https://gitcode.com/GitHub_Trending/tr/transformers.js本教程将以huggingface/transformers为核心从零搭建一个运行在浏览器端的 React 多语言翻译应用借助 Web Worker 将模型推理与 UI 线程解耦并通过单例模式与TextStreamer实现模型的懒加载与翻译结果的流式输出。读完本文你将掌握在 Vite React 项目中引入 Transformers.js 的完整工程链路包括 worker 通信协议、模型下载进度上报与部署发布方案。说明本仓库当前核心包版本为huggingface/transformersv4.2.0见 packages/transformers/package.json。本文所有命令与代码均以该版本对应的 API 为准。前置条件开始之前请确保本地环境满足Node.js 版本 18npm 版本 9。本文使用 Vite 作为构建工具它可以用极少的配置快速搭建一个 React 应用。Step 1初始化项目在终端中执行以下命令创建一个名为react-translator的 React 项目npm create vitelatest react-translator -- --template react如果提示安装create-vite输入y并按下Enter确认。随后进入项目目录并安装依赖cd react-translator npm install启动开发服务器验证应用是否正常工作npm run dev访问终端中显示的地址如http://localhost:5173/应能看到默认的 React Vite 落地页。在终端中按下CtrlC即可停止开发服务器。Step 2安装并配置 Transformers.js接下来为本应用添加机器学习能力。首先从 npm 安装 Transformers.jsnpm install huggingface/transformers本应用将使用Xenova/nllb-200-distilled-600M模型它支持200 种语言之间的多语言互译NLLB 即 No Language Left Behind。动手编码前有两个要点必须注意ML 推理是计算密集型的最好将模型加载与运行放到独立于主UI线程的独立线程中执行避免阻塞界面渲染模型体积较大超过 1 GB不应在页面加载时立即下载而应等到用户点击 Translate 按钮时才触发下载。这两点都可以通过Web Worker与React hooks的组合来实现。创建 worker.js单例管道在src目录下创建worker.js该脚本负责所有重活包括翻译管道的加载与运行。为了保证模型只被加载一次我们创建一个MyTranslationPipeline类使用单例模式singleton pattern首次调用getInstance时懒加载创建一个管道实例后续所有调用复用同一实例import { pipeline, TextStreamer } from huggingface/transformers; class MyTranslationPipeline { static task translation; static model Xenova/nllb-200-distilled-600M; static instance null; static async getInstance(progress_callback null) { this.instance ?? pipeline(this.task, this.model, { progress_callback }); return this.instance; } }这里pipeline(task, model, options)是 Transformers.js 提供的统一工厂方法。从源码看packages/transformers/src/pipelines.js它内部会根据任务类型解析出对应的管道类并行加载 tokenizer、processor 与 ONNX 模型文件并支持progress_callback、config、cache_dir、local_files_only、revision、device、dtype、subfolder、use_external_data_format、model_file_name、session_options等选项。其中progress_callback模型文件下载与加载过程中不断收到进度事件见下文 Step 4 的进度消息详解device/dtype可显式指定运行设备与量化数据类型未指定时按环境自动选择浏览器默认走 WebGPU/WebAssembly 后端。修改 App.jsx挂载 Workersrc/App.jsx是初始化项目时自动生成的文件包含一些模板代码。我们在App组件内部使用useRef保存 worker 引用并用useEffect在组件挂载时创建 worker、注册消息监听同时在组件卸载时完成清理// Remember to import the relevant hooks import { useEffect, useRef, useState } from react import ./App.css function App() { // Create a reference to the worker object. const worker useRef(null); // We use the useEffect hook to setup the worker as soon as the App component is mounted. useEffect(() { // Create the worker if it does not yet exist. worker.current ?? new Worker(new URL(./worker.js, import.meta.url), { type: module }); // Create a callback function for messages from the worker thread. const onMessageReceived (e) { // TODO: Will fill in later }; // Attach the callback function as an event listener. worker.current.addEventListener(message, onMessageReceived); // Define a cleanup function for when the component is unmounted. return () worker.current.removeEventListener(message, onMessageReceived); }); return ( // TODO: Rest of our app goes here... ) } export default App要点解析new URL(./worker.js, import.meta.url)type: module让 Vite 将 worker 作为 ES Module 打包从而可以在 worker 中直接importTransformers.jsuseEffect未传依赖数组配合worker.current ??的空值合并逻辑保证 worker 只被创建一次清理函数负责移除消息监听避免组件卸载后继续收到消息导致状态泄漏。Step 3设计用户界面建议让开发服务器保持运行npm run dev以便实时预览界面变化。首先在src目录下创建components文件夹并创建以下组件1. LanguageSelector.jsx语言选择器让用户选择输入与输出语言。NLLB 模型使用 FLORES-200 的语言代码形如eng_Latn、fra_Latn完整语言清单可参考模型仓库的 READMEconst LANGUAGES { Acehnese (Arabic script): ace_Arab, Acehnese (Latin script): ace_Latn, Afrikaans: afr_Latn, ... Zulu: zul_Latn, } export default function LanguageSelector({ type, onChange, defaultLanguage }) { return ( div classNamelanguage-selector label{type}: /label select onChange{onChange} defaultValue{defaultLanguage} {Object.entries(LANGUAGES).map(([key, value]) { return option key{key} value{value}{key}/option })} /select /div ) }2. Progress.jsx下载进度条用于展示每个模型文件的下载进度export default function Progress({ text, percentage }) { percentage percentage ?? 0; return ( div classNameprogress-container div classNameprogress-bar style{{ width: ${percentage}% }} {text} ({${percentage.toFixed(2)}%}) /div /div ); }在 App.jsx 中使用组件在App.jsx文件顶部加入组件导入import LanguageSelector from ./components/LanguageSelector; import Progress from ./components/Progress;然后在App函数开头添加状态变量用于跟踪模型加载状态、语言选择、输入与输出文本function App() { // Model loading const [ready, setReady] useState(null); const [disabled, setDisabled] useState(false); const [progressItems, setProgressItems] useState([]); // Inputs and outputs const [input, setInput] useState(I love walking my dog.); const [sourceLanguage, setSourceLanguage] useState(eng_Latn); const [targetLanguage, setTargetLanguage] useState(fra_Latn); const [output, setOutput] useState(); // rest of the code... }接下来把自定义组件挂进App的return语句两个语言选择器、两个textarea输入与只读输出、一个触发翻译的按钮以及模型下载进度区return ( h1Transformers.js/h1 h2ML-powered multilingual translation in React!/h2 div classNamecontainer div classNamelanguage-container LanguageSelector type{Source} defaultLanguage{eng_Latn} onChange{(x) setSourceLanguage(x.target.value)} / LanguageSelector type{Target} defaultLanguage{fra_Latn} onChange{(x) setTargetLanguage(x.target.value)} / /div div classNametextbox-container textarea value{input} rows{3} onChange{(e) setInput(e.target.value)} /textarea textarea value{output} rows{3} readOnly/textarea /div /div button disabled{disabled} onClick{translate} Translate /button div classNameprogress-bars-container {ready false labelLoading models... (only run once)/label} {progressItems.map((data) ( div key{data.file} Progress text{data.file} percentage{data.progress} / /div ))} /div / );translate函数我们留到下一步再定义。添加 CSS 样式最后为应用补充样式。修改src目录下的两个样式文件index.css全局基础样式:root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color: #213547; background-color: #ffffff; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; } body { margin: 0; display: flex; place-items: center; min-width: 320px; min-height: 100vh; } h1 { font-size: 3.2em; line-height: 1; } h1, h2 { margin: 8px; } select { padding: 0.3em; cursor: pointer; } textarea { padding: 0.6em; } button { padding: 0.6em 1.2em; cursor: pointer; font-weight: 500; } button[disabled] { cursor: not-allowed; } select, textarea, button { border-radius: 8px; border: 1px solid transparent; font-size: 1em; font-family: inherit; background-color: #f9f9f9; transition: border-color 0.25s; } select:hover, textarea:hover, button:not([disabled]):hover { border-color: #646cff; } select:focus, select:focus-visible, textarea:focus, textarea:focus-visible, button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; }App.css布局与进度条样式#root { max-width: 1280px; margin: 0 auto; padding: 2rem; text-align: center; } .language-container { display: flex; gap: 20px; } .textbox-container { display: flex; justify-content: center; gap: 20px; width: 800px; } .textbox-container textarea, .language-selector { width: 50%; } .language-selector select { width: 150px; } .progress-container { position: relative; font-size: 14px; color: white; background-color: #e9ecef; border: solid 1px; border-radius: 8px; text-align: left; overflow: hidden; } .progress-bar { padding: 0 4px; z-index: 0; top: 0; width: 1%; overflow: hidden; background-color: #007bff; white-space: nowrap; } .progress-text { z-index: 2; } .selector-container { display: flex; gap: 20px; } .progress-bars-container { padding: 8px; height: 140px; } .container { margin: 25px; display: flex; flex-direction: column; gap: 10px; }Step 4连接一切界面就绪后我们来实现主线程与 worker 线程之间的通信闭环。定义 translate 函数在App函数的return语句之前添加translate函数。它负责向 worker 发送翻译请求包含输入文本、源语言与目标语言并禁用按钮防止重复点击const translate () { setDisabled(true); setOutput(); worker.current.postMessage({ text: input, src_lang: sourceLanguage, tgt_lang: targetLanguage, }); };worker.js监听消息、流式返回结果在src/worker.js中添加消息监听。worker 收到主线程消息后先获取首次调用时加载翻译管道并通过TextStreamer将逐步生成的翻译结果实时回传主线程// Listen for messages from the main thread self.addEventListener(message, async (event) { // Retrieve the translation pipeline. When called for the first time, // this will load the pipeline and save it for future use. const translator await MyTranslationPipeline.getInstance((x) { // We also add a progress callback to the pipeline so that we can // track model loading. self.postMessage(x); }); // Capture partial output as it streams from the pipeline const streamer new TextStreamer(translator.tokenizer, { skip_prompt: true, skip_special_tokens: true, callback_function: function (text) { self.postMessage({ status: update, output: text, }); }, }); // Actually perform the translation const output await translator(event.data.text, { tgt_lang: event.data.tgt_lang, src_lang: event.data.src_lang, // Allows for partial output to be captured streamer, }); // Send the output back to the main thread self.postMessage({ status: complete, output, }); });从源码看packages/transformers/src/generation/streamers.jsTextStreamer继承自BaseStreamer由生成循环在产出新 token 时调用put()在生成结束时调用end()。它支持以下关键选项skip_prompt默认false是否跳过提示词prompttokenskip_special_tokens默认true解码时是否跳过特殊 tokencallback_function每当一段完整文本就绪时被调用默认行为是打印到 stdout在 worker 场景下我们覆盖为postMessagetoken_callback_function每生成一个新 token 时被调用decode_kwargs透传给 tokenizerdecode方法的附加参数。其内部采用整词缓冲策略先把 token 累积在token_cache中整体解码再按最后一个空格或换行符切出完整可打印文本避免把半个单词渲染到界面上对 CJK 字符中文等则逐个字符即时输出见 streamers.js这对多语言翻译界面尤为关键——日语、韩语、中文等无空格分隔语言的翻译结果也能流畅滚动显示。翻译调用本身对应TranslationPipelinepackages/transformers/src/pipelines/translation.js它继承自Text2TextGenerationPipeline通过src_lang/tgt_lang两个生成参数指定源语言与目标语言返回{ translation_text: string }结构的结果数组。NLLB 模型的语言代码通过其专用的NllbTokenizer解析仓库中 packages/transformers/src/models/nllb/tokenization_nllb.js 实现了对 FLORES-200 语言标签的处理对应的行为可由 packages/transformers/tests/models/nllb/test_tokenization_nllb.js 中的测试用例印证例如英文文本会被解码为eng_Latn How are you doing?/s这样的带语言标签前缀格式。App.jsx处理 worker 消息最后填充前面useEffect中的onMessageReceived函数根据 worker 回传消息的status字段更新应用状态const onMessageReceived (e) { switch (e.data.status) { case initiate: // Model file start load: add a new progress item to the list. setReady(false); setProgressItems((prev) [...prev, e.data]); break; case progress: // Model file progress: update one of the progress items. setProgressItems((prev) prev.map((item) { if (item.file e.data.file) { return { ...item, progress: e.data.progress }; } return item; }), ); break; case done: // Model file loaded: remove the progress item from the list. setProgressItems((prev) prev.filter((item) item.file ! e.data.file), ); break; case ready: // Pipeline ready: the worker is ready to accept messages. setReady(true); break; case update: // Generation update: update the output text. setOutput((o) o e.data.output); break; case complete: // Generation complete: re-enable the Translate button setDisabled(false); break; } };这里的进度消息协议并非临时设计而是 Transformers.js 内置的回调事件体系。查看 packages/transformers/src/utils/core.js 可知progress_callback会收到initiate开始加载某文件携带file字段、progress单文件下载进度携带file、loaded、total、done文件加载完成以及ready管道就绪等状态当传入pipeline()时还会被包装为DefaultProgressCallback额外聚合出progress_total总量进度事件。worker 中直接把回调收到的对象postMessage回主线程主线程据此维护progressItems列表、控制按钮的可用状态与模型加载中提示。至此执行npm run dev即可在浏览器中直接进行多语言互译Step 5可选构建与部署构建生产版本只需运行npm run buildVite 会把应用打包为静态文件并输出到dist目录。本教程的演示应用以静态 Hugging Face Space 的形式部署你也可以部署到任意静态托管平台。静态 Space 的发布步骤为访问新建 Space 页面填写表单Space 类型务必选择Static进入 Files → Add file → Upload files将dist目录中的index.html与public/文件夹拖入上传框点击 Upload完成后点击 Commit changes to main。完成后你的应用即部署在https://huggingface.co/spaces/你的用户名/你的 Space 名称。小结本文完整走通了Vite React Transformers.js的浏览器端多语言翻译方案工程要点实现方式计算密集推理不阻塞 UI在 Web Workertype: module中加载与运行pipeline模型只加载一次单例模式MyTranslationPipeline.getInstance()配合useRef/useEffect大模型按需下载点击 Translate 时才触发pipeline()由progress_callback上报进度翻译结果流式展示TextStreamer通过callback_function逐段回传status: update增量更新输出状态同步主线程按initiate/progress/done/ready/update/complete消息驱动 UI这套模式worker 隔离推理 单例管道 流式回调同样适用于摘要、文本生成、语音识别等其他 Transformers.js 管道任务。若希望深入探究底层实现建议继续阅读仓库中的 packages/transformers/src/pipelines.js管道工厂、packages/transformers/src/generation/streamers.js流式器与 packages/transformers/src/pipelines/translation.js翻译管道。【免费下载链接】transformers.jsState-of-the-art Machine Learning for the web. Run Transformers directly in your browser, with no need for a server!项目地址: https://gitcode.com/GitHub_Trending/tr/transformers.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表