@lobehub/i18n-cli
Version:
Lobe i18n is a CLI tool that automate translate your i18n localization with AI
10 lines (8 loc) • 23 kB
JavaScript
var xt=Object.defineProperty;var i=(e,t)=>xt(e,"name",{value:t,configurable:!0});var bt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);import{jsx as d,jsxs as F}from"react/jsx-runtime";import{alert as m,ConfigPanel as Tt,useTheme as vt,SplitView as Ct,render as j}from"@lobehub/cli-ui";import{Command as Ot,Option as I}from"commander";import Mt from"update-notifier";import Nt from"conf";import{cosmiconfigSync as Lt}from"cosmiconfig";import{TextInput as V,Spinner as Ft,ProgressBar as jt,StatusMessage as It}from"@inkjs/ui";import{memo as Q,useState as At,useMemo as Et}from"react";import l from"chalk";import $t from"dotenv";import{merge as M,sumBy as Jt,isPlainObject as z,cloneDeep as Pt,unset as Bt,set as Rt,reduce as Ut,isString as Dt}from"lodash-es";import{consola as p}from"consola";import{resolve as y,dirname as _t,join as Y,relative as A}from"node:path";import{Text as b,Box as qt}from"ink";import W from"p-map";import{RecursiveCharacterTextSplitter as Kt}from"langchain/text_splitter";import{encode as X}from"gpt-tokenizer";import H from"remark-frontmatter";import Z from"remark-gfm";import Gt from"remark-parse";import Vt from"remark-stringify";import{unified as tt}from"unified";import{visit as et}from"unist-util-visit";import{diff as Qt}from"just-diff";import{ChatOpenAI as zt}from"@langchain/openai";import Yt from"dirty-json";import{ChatPromptTemplate as nt}from"@langchain/core/prompts";import{writeFileSync as ot,readFileSync as rt,existsSync as T,mkdirSync as st}from"node:fs";import Wt from"json-stable-stringify";import{globSync as it}from"glob";import*as Xt from"node:process";import at from"gray-matter";var Xe=bt((K,G)=>{var Ht="@lobehub/i18n-cli",Zt="1.20.2",te="Lobe i18n is a CLI tool that automate translate your i18n localization with AI",ee=["ai","i18n","openai","gpt","langchain"],ne="https://github.comlobehub/lobe-cli-toolbox/tree/master/packages/lobe-i18n",oe={url:"https://github.com/lobehub/lobe-cli-toolbox/issues/new"},re={type:"git",url:"https://github.com/lobehub/lobe-cli-toolbox.git"},se="MIT",ie="LobeHub <i@lobehub.com>",ae=!1,ce="module",le={"@":"./src"},K={require:{types:"./dist/index.d.cts",default:"./dist/index.cjs"},import:{types:"./dist/index.d.mts",default:"./dist/index.mjs"}},ue="./dist/index.cjs",G="./dist/index.mjs",pe="./dist/index.d.cts",fe={"lobe-i18n":"dist/cli.js"},de=["dist"],me={build:"npm run type-check && pkgroll --minify -p tsconfig.prod.json --env.NODE_ENV=production && npm run shebang",dev:"pkgroll -p tsconfig.prod.json --env.NODE_ENV=development --watch",link:"npm run build && npm link -f",shebang:"lobe-shebang -t ./dist/cli.js",start:"node ./dist/cli.js",test:"vitest --passWithNoTests","test:coverage":"vitest run --coverage --passWithNoTests","type-check":"tsc --noEmit"},ge={"@inkjs/ui":"^1.0.0","@langchain/core":"^0.2.20","@langchain/openai":"^0.2.5","@lobehub/cli-ui":"1.10.0",chalk:"^5.3.0",commander:"^12.1.0",conf:"^12.0.0",consola:"^3.2.3",cosmiconfig:"^9.0.0","dirty-json":"^0.9.2",dotenv:"^16.4.5","fast-deep-equal":"^3.1.3",glob:"^10.4.5","gpt-tokenizer":"^2.2.1","gray-matter":"^4.0.3",ink:"^4.4.1","json-stable-stringify":"^1.1.1","just-diff":"^6.0.2",langchain:"^0.2.12","lodash-es":"^4.17.21","p-map":"^7.0.2",pangu:"^4.0.7",react:"^18.3.1","remark-frontmatter":"^4.0.1","remark-gfm":"^3.0.1","remark-parse":"^10.0.2","remark-stringify":"^10.0.3",swr:"^2.2.5",unified:"^11.0.5","unist-util-visit":"^5.0.0","update-notifier":"^7.2.0",zustand:"^4.5.4"},he={"@types/json-stable-stringify":"^1.0.36","@types/lodash-es":"^4.17.12","@types/node":"^20.16.13","@types/unist":"^3.0.3"},ye={node:">=18"},ke={access:"public",registry:"https://registry.npmjs.org/"},E={name:Ht,version:Zt,description:te,keywords:ee,homepage:ne,bugs:oe,repository:re,license:se,author:ie,sideEffects:ae,type:ce,imports:le,exports:K,main:ue,module:G,types:pe,bin:fe,files:de,scripts:me,dependencies:ge,devDependencies:he,engines:ye,publishConfig:ke},v=(e=>(e.MDAST="mdast",e.STRING="string",e))(v||{});const $=i(e=>`.${e}.md`,"getDefaultExtension"),we={"gpt-3.5-turbo":16385,"gpt-3.5-turbo-0125":16385,"gpt-3.5-turbo-1106":16385,"gpt-3.5-turbo-16k":16385,"gpt-4":8192,"gpt-4-0613":8192,"gpt-4-32k":32768,"gpt-4-0125-preview":128e3,"gpt-4-vision-preview":128e3,"gpt-4-turbo":128e3,"gpt-4-turbo-vision":128e3,"gpt-4-turbo-preview":128e3,"gpt-4-1106-vision-preview":128e3,"gpt-4-1106-preview":128e3,"gpt-4o-2024-05-13":128e3,"gpt-4o-mini":16385},ct="gpt-4o-mini",Se={concurrency:5,markdown:{entry:[],mode:v.STRING,outputExtensions:$},modelName:ct,temperature:0},k=i((e,t)=>{e[t]||m.error(`Can't find ${l.bold.yellow("outputLocales")} in config`)},"checkOptionKeys"),lt={apiBaseUrl:{default:"",type:"string"},openaiToken:{default:"",type:"string"}},J=new Nt({projectName:"lobe-i18n",schema:lt});class xe{static{i(this,"ExplorerConfig")}explorer;customConfig;constructor(){this.explorer=Lt("i18n")}loadCustomConfig(t){this.customConfig=t}getConfigFile(){return this.customConfig?this.explorer.load(this.customConfig)?.config:this.explorer.search()?.config}}const P=new xe;$t.config();const B=i(e=>J.get(e),"getConfig"),be=i(e=>lt[e].default,"getDefulatConfig"),Te=i((e,t)=>J.set(e,t),"setConfig"),ve=i(()=>process.env.OPENAI_API_KEY||B("openaiToken"),"getOpenAIApiKey"),Ce=i(()=>process.env.OPENAI_PROXY_URL||B("apiBaseUrl"),"getOpenAIProxyUrl"),R=i(()=>{const e=P.getConfigFile();return e?M(Se,e):m.error(`Can't find ${l.bold.yellow("config")}`,!0)},"getConfigFile"),Oe=i(()=>{const e=R();return k(e,"entry"),k(e,"entryLocale"),k(e,"output"),k(e,"outputLocales"),e},"getLocaleConfig"),Me=i(()=>{const e=R();if(!e.markdown)return m.error(`Can't find ${l.bold.yellow("config.markdown")}`,!0);const t=M(e?.markdown||{},{entryLocale:e?.markdown?.entryLocale||e.entryLocale,outputLocales:e?.markdown?.outputLocales||e.outputLocales});return k(t,"entry"),k(t,"entryLocale"),k(t,"outputLocales"),t},"getMarkdownConfigFile");var g={getConfig:B,getConfigFile:R,getDefulatConfig:be,getLocaleConfig:Oe,getMarkdownConfigFile:Me,getOpenAIApiKey:ve,getOpenAIProxyUrl:Ce,setConfig:Te};const Ne=i(()=>{const e=J.store;return{get:g.getConfig,getDefault:g.getDefulatConfig,set:g.setConfig,store:e}},"useConfStore"),Le=Q(()=>{const[e,t]=At(),{store:n,set:o,getDefault:r}=Ne(),a=i((c,u)=>{o(c,u),t("")},"setConfig"),s=Et(()=>[{children:d(V,{defaultValue:n.openaiToken,onSubmit:i(c=>a("openaiToken",c),"onSubmit"),placeholder:"Input OpenAI token..."}),defaultValue:r("openaiToken"),key:"openaiToken",label:"OpenAI token",showValue:!1,value:n.openaiToken},{children:d(V,{defaultValue:n.apiBaseUrl,onSubmit:i(c=>a("apiBaseUrl",c),"onSubmit"),placeholder:"Set openAI API proxy, default value: https://api.openai.com/v1/..."}),defaultValue:r("apiBaseUrl"),desc:"OpenAI API proxy, default value: https://api.openai.com/v1/",key:"apiBaseUrl",label:"OpenAI API proxy",showValue:!1,value:n.apiBaseUrl}],[n]);return d(Tt,{active:e,items:s,logo:"\u{1F92F}",setActive:t,title:"Lobe I18N Config"})}),N=Q(({hide:e,filename:t,to:n,from:o,progress:r,maxStep:a,step:s,isLoading:c,needToken:u})=>{const f=vt();return e?null:F(Ct,{flexDirection:"column",children:[d(b,{backgroundColor:f.colorBgLayout,color:f.colorText,children:` \u{1F4DD} ${t} `}),F(b,{color:f.colorTextDescription,children:["- from ",d(b,{bold:!0,color:f.colorInfo,children:o})," to ",d(b,{bold:!0,color:f.colorInfo,children:n}),d(b,{color:f.colorTextDescription,children:` [Tokens: ${u}]`})]}),c?F(qt,{children:[d(Ft,{label:` ${r}% [${s}/${a} chunks] `}),d(jt,{value:r})]}):d(It,{variant:"success",children:"Success"})]})}),Fe=3,L=2,ut=2,h=i(e=>X(e).length,"calcToken"),C=i(e=>X(String(e)).length,"calcEncodedKeyToken"),pt=i(e=>C(e)+Fe,"calcPrimitiveValueToken"),ft=i((e,t=0)=>Jt(Object.entries(e),([n,o])=>C(n)+L+(z(o)?ft(o,t+1):pt(o)))+ut+t,"calcJsonToken"),je=new Set([`
`,`\r
`,"[!NOTE]","[!IMPORTANT]","[!WARNING]","[!CAUTION]","\\[!NOTE]","\\[!IMPORTANT]","\\[!WARNING]","\\[!CAUTION]","\\[!NOTE]\\","\\[!IMPORTANT]\\","\\[!WARNING]\\","\\[!CAUTION]\\"]),Ie=i(e=>je.has(e)?!0:/^[\s\p{P}\u{1F300}-\u{1F5FF}\u{1F600}-\u{1F64F}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}]*$/u.test(e.replaceAll(" ","")),"checkMdString"),dt=i(async e=>tt().use(Gt).use(Z).use(H).parse(e.trim()),"convertMarkdownToMdast"),Ae=i((e,t)=>{const n={};let o=0;return et(e,t||"text",r=>{n[o]=r.value,o++}),n},"convertMdastToMdastObj"),Ee=i(e=>{const t={};for(const[n,o]of Object.entries(e))Ie(o)||(t[Number(n)]=o);return t},"pickMdastObj"),$e=i(({mdast:e,entry:t,target:n},o)=>{const r={...t,...n};let a=0;return et(e,o||"text",s=>{s.value=r[a],a++}),e},"mergeMdastObj"),U=i(async e=>tt().use(Vt,{bullet:"-",emphasis:"*",fences:!0,listItemIndent:1,rule:"-",strong:"*",tightDefinitions:!0}).use(H).use(Z).stringify(e).toString(),"convertMdastToMarkdown"),D=i((e,t)=>{const n=Qt(t,e),o=n.filter(c=>c.op==="add"),r=n.filter(c=>c.op==="remove"),a=Pt(t),s={};for(const c of r)Bt(a,c.path);for(const c of o)Rt(s,c.path,c.value);return{add:o,entry:s,remove:r,target:a}},"diff"),Je=i((e,t)=>Ut(Object.entries(e),(n,[o,r])=>{let[a,s]=n.pop()||[{},ut];const c=z(r)?ft(r,1):pt(r);return s+C(o)+L+c<=t?(a[o]=r,s+=C(o)+L+c,n.push([a,s])):n.push([a,s],[{[o]:r},C(o)+L+c]),n},[]).map(([n])=>n),"splitJSONtoSmallChunks"),mt=i((e,t)=>{let n=(we[e.modelName||ct]-h(t))/3;return e.splitToken&&e.splitToken<n&&(n=e.splitToken),n=Math.floor(n),n},"getSplitToken"),Pe=i((e,t,n,o)=>{const r=D(t,n).entry,a=mt(e,o);return Je(r,a)},"splitJsonToChunks");let Be=class{static{i(this,"TranslateMarkdown")}mdast;entry={};config;check;definition;constructor(t){this.config=t,this.check=["text","yaml",t?.markdown?.translateCode&&"code"].filter(Boolean)}async genTarget(t){return this.mdast=await dt(t),this.entry=Ae(this.mdast,this.check),Ee(this.entry)}async genMarkdownByMdast(t){if(!t)return;const n=$e({entry:this.entry,mdast:this.mdast,target:t},this.check);return U(n)}async clearMarkdownString(t){const n=[],o=await dt(t);return o.children=o.children.map(r=>r.type==="definition"?(n.push(r),!1):r).filter(Boolean),{content:await U(o),definition:await U({children:n,type:"root"})}}async genSplitMarkdown(t,n){this.definition="";const{content:o,definition:r}=await this.clearMarkdownString(t);return this.definition=r,await Kt.fromLanguage("markdown",{chunkOverlap:0,chunkSize:mt(this.config,n),lengthFunction:i(s=>h(s),"lengthFunction")}).splitText(o)}async genMarkdownByString(t){return[...t,this.definition].join(`
`)}};const Re=i(e=>{let t={};for(const n of e)t=M(t,n);return t},"mergeJsonFromChunks"),gt="You can adjust the tone and style, taking into account the cultural connotations and regional differences of certain words. As a translator, you need to translate the original text into a translation that meets the standards of accuracy and elegance.",Ue=i((e=gt)=>nt.fromMessages([["system",["Translate the i18n JSON file from {from} to {to} according to the BCP 47 standard",`Here are some reference to help with better translation. ---${e}---`,"Keep the keys the same as the original file and make sure the output remains a valid i18n JSON file.","Do not include any additional text or explanations outside the JSON object.Start directly with a left brace and end with a right brace."].filter(Boolean).join(`
`)],["human","{json}"]]),"promptJsonTranslate"),De=i((e=gt)=>nt.fromMessages([["system",["Translate the markdown file from {from} to {to} according to the BCP 47 standard",`Here are some reference to help with better translation. ---${e}---`,"Make sure the output remains a valid markdown file."].filter(Boolean).join(`
`)],["human","{text}"]]),"promptStringTranslate");let _e=class{static{i(this,"TranslateLocale")}model;config;isJsonMode;promptJson;promptString;constructor(t,n,o){this.config=t,this.model=new zt({configuration:{baseURL:o},maxConcurrency:t.concurrency,maxRetries:4,modelName:t.modelName,openAIApiKey:n,temperature:t.temperature,topP:t.topP}),this.promptJson=Ue(t.reference),this.promptString=De(t.reference),this.isJsonMode=!!this.config?.experimental?.jsonMode}async runByString({from:t,to:n,text:o}){try{const r=await this.promptString.formatMessages({from:t||this.config.entryLocale,text:o,to:n}),s=(await this.model.call(r)).text;return s||this.handleError(),s}catch(r){this.handleError(r)}}async runByJson({from:t,to:n,json:o}){try{const r=await this.promptJson.formatMessages({from:t||this.config.entryLocale,json:JSON.stringify(o),to:n}),a=await this.model.invoke(r,this.isJsonMode?{response_format:{type:"json_object"}}:void 0),s=this.isJsonMode?a.content:a.text;s||this.handleError();try{return JSON.parse(s)}catch{m.warn("parse fail, try to use dirty json");try{return Yt.parse(s)}catch{m.error("i18n dirty json fail"),m.error(s,!0)}}}catch(r){this.handleError(r)}}handleError(t){m.error(`Translate failed, ${t||"please check your network or try again..."}`,!0)}};class ht{static{i(this,"I18n")}config;step=0;maxStep=1;translateLocaleService;translateMarkdownService;constructor({openAIApiKey:t,openAIProxyUrl:n,config:o}){this.config=o,this.translateLocaleService=new _e(o,t,n),this.translateMarkdownService=new Be(o)}async translateMarkdown(t){return t.mode===v.STRING?this.translateMarkdownByString(t):this.translateMarkdownByMdast(t)}async translateMarkdownByString({md:t,to:n,onProgress:o,from:r}){const a=await this.translateLocaleService.promptString.formatMessages({from:r,text:"",to:n}),s=await this.translateMarkdownService.genSplitMarkdown(t,JSON.stringify(a));if(this.maxStep=s.length,this.step=0,s.length===0)return;const c=s.length*h(JSON.stringify(a))+h(JSON.stringify(s));o?.({isLoading:!0,maxStep:this.maxStep,needToken:c,progress:0,step:0});const u=await W(s,async O=>{o?.({isLoading:this.step<this.maxStep,maxStep:this.maxStep,needToken:c,progress:this.step<this.maxStep?Math.floor(this.step/this.maxStep*100):100,step:this.step});const x=await this.translateLocaleService.runByString({from:r,text:O,to:n});return this.step<this.maxStep&&this.step++,x},{concurrency:this.config?.concurrency});return o?.({isLoading:!1,maxStep:this.maxStep,needToken:c,progress:100,step:this.maxStep}),{result:await this.translateMarkdownService.genMarkdownByString(u),tokenUsage:c+h(JSON.stringify(u))}}async translateMarkdownByMdast({md:t,...n}){const o=await this.translateMarkdownService.genTarget(t),r=await this.translate({...n,entry:o,target:{}});if(!r?.result)return;const a=await this.translateMarkdownService.genMarkdownByMdast(r);if(a)return{result:a,tokenUsage:r.tokenUsage}}async translate({entry:t,target:n,to:o,onProgress:r,from:a}){const s=await this.translateLocaleService.promptJson.formatMessages({from:a,json:{},to:o}),c=Pe(this.config,t,n,JSON.stringify(s));if(this.maxStep=c.length,this.step=0,c.length===0)return;const u=c.length*h(JSON.stringify(s))+h(JSON.stringify(c));r?.({isLoading:!0,maxStep:this.maxStep,needToken:u,progress:0,step:0});const f=await W(c,async x=>{r?.({isLoading:this.step<this.maxStep,maxStep:this.maxStep,needToken:u,progress:this.step<this.maxStep?Math.floor(this.step/this.maxStep*100):100,step:this.step});const St=await this.translateLocaleService.runByJson({from:a,json:x,to:o});return this.step<this.maxStep&&this.step++,St},{concurrency:this.config?.concurrency});return r?.({isLoading:!1,maxStep:this.maxStep,needToken:u,progress:100,step:this.maxStep}),{result:await M(n,Re(f)),tokenUsage:u+h(JSON.stringify(f))}}}const _=i(e=>{const t=rt(e,"utf8");return JSON.parse(t)},"readJSON"),w=i((e,t)=>{const n=Wt(t,{space:" "});ot(e,n+`\r
`,"utf8")},"writeJSON"),qe=i(e=>rt(e,"utf8"),"readMarkdown"),Ke=i((e,t)=>{ot(e,t,"utf8")},"writeMarkdown"),Ge=i(e=>{for(const t of e.outputLocales){const n=y(e.output,`${t}.json`);T(n)||w(n,{})}},"checkLocales"),Ve=i((e,t)=>{for(const n of e.outputLocales){const o=y(e.output,n);T(o)||st(o)}for(const n of e.outputLocales)for(const o of t){const r=y(e.output,n,o);try{const a=_t(r);st(a,{recursive:!0})}catch{}T(r)||w(r,{})}},"checkLocaleFolders"),Qe=i(e=>{try{const t=y("./",e.entry);return T(t)||m.error(`Can't find ${l.bold.yellow(e.entry)} in dir`,!0),_(t)}catch{Xt.exit(1)}},"getEntryFile"),ze=i(e=>{const t=e.entry.replaceAll("*","").replaceAll("*.json",""),n=it(Y(t,"**/*.json").replaceAll("\\","/"),{nodir:!0}),o={};for(const r of n)o[A(t,r)]=_(r);if(Object.keys(o).length===0){m.error(`Can't find .json files in ${l.bold.yellow(t)}`,!0);return}return o},"getEntryFolderFiles"),yt=i(e=>{const t=_(e);return t||(w(e,{}),{})},"getLocaleObj"),q=i((e,t)=>{try{if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e=="object"&&typeof t=="object"){const n=Object.keys(e),o=Object.keys(t);if(n.length!==o.length)return!1;for(const r of n)if(!o.includes(r)||!q(e[r],t[r]))return!1}return typeof e==typeof t}catch{return!1}},"isEqualJsonKeys");class Ye{static{i(this,"TranslateLocale")}config;query=[];i18n;constructor(){this.config=g.getLocaleConfig(),this.i18n=new ht({config:this.config,openAIApiKey:g.getOpenAIApiKey(),openAIProxyUrl:g.getOpenAIProxyUrl()})}async start(){p.start("Lobe I18N is analyzing your project... \u{1F92F}\u{1F30F}\u{1F50D}"),!this.config.entry.includes(".json")||this.config.entry.includes("*")?this.genFolderQuery():this.genFlatQuery(),this.query.length>0?await this.runQuery():p.success("No content requiring translation was found."),p.success("All i18n tasks have been completed\uFF01")}async runQuery(){p.info(`Current model setting: ${l.cyan(this.config.modelName)} (temperature: ${l.cyan(this.config.temperature)}) ${this.config.experimental?.jsonMode?l.red(" [JSON Mode]"):""}}`);let t=0;for(const n of this.query){const o={filename:n.filename,from:n.from||this.config.entryLocale,to:n.to},{rerender:r,clear:a}=j(d(N,{hide:!0,isLoading:!0,maxStep:1,progress:0,step:0,...o})),s=await this.i18n.translate({...n,onProgress:i(u=>{u.maxStep>0?r(d(N,{...u,...o})):a()},"onProgress")});a();const c=A(".",n.filename);s?.result&&Object.keys(s.result).length>0?(w(n.filename,s.result),t+=s.tokenUsage,p.success(l.yellow(c),l.gray(`[Token usage: ${s.tokenUsage}]`))):p.warn("No translation result was found:",l.yellow(c))}t>0&&p.info("Total token usage:",l.cyan(t))}genFolderQuery(){const t=this.config,n=ze(t),o=Object.keys(n);p.info(`Running in ${l.bold.cyan("\u{1F4C2} Folder Mode")} and has found ${l.bold.cyan(o.length)} files.`),Ve(t,o);for(const r of t.outputLocales)for(const[a,s]of o.entries()){process.stdout.isTTY&&(process.stdout.clearLine(0),process.stdout.cursorTo(0)),process.stdout.write(`${l.cyan(r)}${l.gray(`[${a+1}/${o.length}] - `)}${l.yellow(s)}`);const c=y(t.output,r,s),u=n[s],f=D(u,yt(c)).target;w(c,f),!q(u,f)&&this.query.push({entry:u,filename:c,from:t.entryLocale,target:f,to:r})}process.stdout.isTTY&&(process.stdout.clearLine(0),process.stdout.cursorTo(0))}genFlatQuery(){const t=this.config,n=Qe(t);p.start(`Running in ${l.bold.cyan("\u{1F4C4} Flat Mode")}, and translating ${l.bold.cyan(t.outputLocales.join("/"))} locales..`),Ge(t);for(const o of t.outputLocales){const r=y(t.output,o)+".json",a=n,s=D(a,yt(r)).target;w(r,s),!q(a,s)&&this.query.push({entry:a,filename:r,from:t.entryLocale,target:s,to:o})}}}const kt=i((e,t)=>e.map(n=>n.includes("*")||n.includes(t)?n:Y(n,`**/*${t}`).replaceAll("\\","/")),"matchInputPattern");class wt{static{i(this,"TranslateMarkdown")}config;markdownConfig;query=[];i18n;constructor(){this.markdownConfig=g.getMarkdownConfigFile();const t=g.getConfigFile();this.config={...t,entryLocale:t.entryLocale||this.markdownConfig.entryLocale,markdown:this.markdownConfig,outputLocales:t.outputLocales||this.markdownConfig.outputLocales},this.i18n=new ht({config:this.config,openAIApiKey:g.getOpenAIApiKey(),openAIProxyUrl:g.getOpenAIProxyUrl()})}async start(){p.start("Lobe I18N is analyzing your markdown... \u{1F92F}\u{1F30F}\u{1F50D}");const t=this.markdownConfig.entry;(!t||t.length===0)&&m.error("No markdown entry was found.",!0);let n=it(kt(t,".md"),{ignore:this.markdownConfig.exclude?kt(this.markdownConfig.exclude||[],".md"):void 0,nodir:!0});this.markdownConfig.entryExtension&&(n=n.filter(o=>o.includes(this.markdownConfig.entryExtension||".md"))),(!n||n.length===0)&&m.error("No markdown entry was found.",!0),this.genFilesQuery(n),this.query.length>0?await this.runQuery():p.success("No content requiring translation was found."),p.success("All i18n tasks have been completed\uFF01")}async runQuery(){p.info(`Current model setting: ${l.cyan(this.config.modelName)} (temperature: ${l.cyan(this.config.temperature)}) ${this.config.experimental?.jsonMode?l.red(" [JSON Mode]"):""}}`);let t=0;for(const n of this.query){const o={filename:n.filename,from:n.from||this.markdownConfig.entryLocale||this.config.entryLocale,to:n.to},{rerender:r,clear:a}=j(d(N,{hide:!0,isLoading:!0,maxStep:1,progress:0,step:0,...o})),s=await this.i18n.translateMarkdown({...n,onProgress:i(u=>{u.maxStep>0?r(d(N,{...u,...o})):a()},"onProgress")});a();const c=A(".",n.filename);if(s?.result&&Object.keys(s.result).length>0){let u=s.result;this.markdownConfig.includeMatter||(u=at.stringify(s.result,n.matter)),Ke(n.filename,u),t+=s.tokenUsage,p.success(l.yellow(c),l.gray(`[Token usage: ${s.tokenUsage}]`))}else p.warn("No translation result was found:",l.yellow(c))}t>0&&p.info("Total token usage:",l.cyan(t))}genFilesQuery(t,n){const o=this.markdownConfig;n||p.start(`Running in ${l.bold.cyan(`\u{1F4C4} ${t.length} Markdown`)}, and translating to ${l.bold.cyan(o?.outputLocales?.join("/"))} locales..`);for(const r of t)try{const a=qe(r);for(const s of o.outputLocales||[]){const c=this.getTargetExtension(s,r,a),u=this.getTargetFilename(r,c);if(T(u))continue;const f=this.getMode(r,a),{data:O,content:x}=at(a);p.info(`\u{1F4C4} To ${s}: ${l.yellow(u)}`),this.query.push({filename:u,from:o.entryLocale,matter:O,md:this.markdownConfig.includeMatter?a:x,mode:f,to:s})}}catch{m.error(`${r} not found`,!0)}}getTargetExtension(t,n,o){return this.markdownConfig.outputExtensions?.(t,{fileContent:o,filePath:n,getDefaultExtension:$})||$(t)}getTargetFilename(t,n){if(this.markdownConfig.entryExtension)return y(".",t.replace(this.markdownConfig.entryExtension||".md",n));if(this.markdownConfig.entryLocale&&t.includes(`.${this.markdownConfig.entryLocale}.`))return[t.split(`.${this.markdownConfig.entryLocale}.`)[0],n].join("");{const o=t.split(".");return o.pop(),[o.join("."),n].join("")}}getMode(t,n){const o=this.markdownConfig.mode;return o?Dt(o)?o:o({fileContent:n,filePath:t})||v.STRING:v.STRING}}const We=Mt({pkg:E,shouldNotifyInNpmScript:!0});We.notify({isGlobal:!0});const S=new Ot;S.name("lobe-i18n").description(E.description).version(E.version).addOption(new I("-o, --option","Setup lobe-i18n preferences")).addOption(new I("-c, --config <string>","Specify the configuration file")).addOption(new I("-m, --with-md","Run i18n translation and markdown translation simultaneously")),S.command("locale",{isDefault:!0}).action(async()=>{const e=S.opts();e.option?j(d(Le,{})):(e.config&&P.loadCustomConfig(e.config),await new Ye().start(),e.withMd&&await new wt().start())}),S.command("md").action(async()=>{const e=S.opts();e.config&&P.loadCustomConfig(e.config),await new wt().start()}),S.parse()});export default Xe();