UNPKG

@voerkai18n/runtime

Version:
180 lines (174 loc) 6.72 kB
import { isMessageId } from "@/utils/isMessageId"; import { isFunction } from "flex-tools/typecheck/isFunction"; import { isNumber } from "flex-tools/typecheck/isNumber"; import { isPlainObject } from "flex-tools/typecheck/isPlainObject"; import type { VoerkaI18nScope } from ".."; import type { VoerkaI18nTranslateVars, VoerkaI18nTranslateOptions, VoerkaI18nLanguageMessages, } from "@/types"; export class TranslateMixin { /** * 根据值的单数和复数形式,从messages中取得相应的消息 * * @param {*} messages 复数形式的文本内容 = [<=0时的内容>,<=1时的内容>,<=2时的内容>,...,<>=N的内容>] * @param {*} value */ private _getPluraMessage( this: VoerkaI18nScope, messages: string | string[], value: number ) { try { if (Array.isArray(messages)) { return messages.length > value ? messages[value] : messages[messages.length - 1]; } else { return messages; } } catch { return Array.isArray(messages) ? messages[0] : messages; } } private _getPluraValue(args: any): [number | null, any[]] { let pluraValue: number | null = null; // 复数值 let vars: any[] = []; // 插值变量列表 // 1. 预处理变量: 复数变量保存至pluralVars中 , 变量如果是Function则调用 if (isPlainObject(args)) { // 字典插值 const dictVars: Record<string, any> = args; for (const [name, value] of Object.entries(dictVars)) { if (isFunction(value)) { try { dictVars[name] = value(); } catch { dictVars[name] = value; } } const isNum: boolean = typeof dictVars[name] === "number"; // 以$开头的视为复数变量,记录下来 if ((pluraValue == null && isNum) || (name.startsWith("$") && isNum)) { pluraValue = dictVars[name]; } } vars = [dictVars]; } else if (Array.isArray(args)) { // 位置插值 vars = args.map((arg) => { try { arg = isFunction(arg) ? arg() : arg; if (isNumber(arg) && !pluraValue) pluraValue = parseInt(arg); // 约定:位置参数中以第一个数值变量作为指示复数变量 } catch { return String(arg); } return arg; }); } else if (args !== undefined) { // 单个插值 pluraValue = isNumber(args) ? parseInt(args) : 0; vars = [args]; } return [pluraValue, vars]; } /** * 翻译组件 * */ protected _getTranslateComponent(this: VoerkaI18nScope): any { if (!this._translateComponent) { const builder = this.options.component || this.appScope.options.component; if (typeof builder === "function") { this._translateComponent = builder.call(this, this); } else { this._translateComponent = () => {}; this.logger.warn("No translate component builder configured"); } } return this._translateComponent; } protected _getTranslateTransformer(this: VoerkaI18nScope): any { if (!this._translateTransformer) { const builder = this.options.transform || this.appScope.options.transform; if (typeof builder === "function") { this._translateTransformer = builder.call(this, this); } } return this._translateTransformer; } private _getActiveMessages( this: VoerkaI18nScope, language: string ): VoerkaI18nLanguageMessages { const messages = this.messages[language]; if (typeof messages === "function") { this.logger.warn( `When the t function specifies the language <${language}> , only synchronized language packs can be used` ); return this.activeMessages; } return (this.messages as any)[language] as VoerkaI18nLanguageMessages; } translate<R = string>( this: VoerkaI18nScope, message: string, vars?: VoerkaI18nTranslateVars, options?: VoerkaI18nTranslateOptions ): R { if (typeof message !== "string") { this.logger.debug( `failed to translate message:${message},it is not a string` ); return "" as R; } const activeLanguage = options?.language || this.activeLanguage; const activeMessages = this._getActiveMessages(activeLanguage); // 为什么样要转义换行符?因为在translates/*.json中key不允许换行符存在,需要转义为\\n,这里需要转回来 message = message.replace(/\n/g, "\\n"); // 如果内容是复数,则其值是一个数组,数组中的每个元素是从1-N数量形式的文本内容 let result: any = message; if (!(typeof message === "string")) return message; const finalArgs = vars === undefined ? [] : isFunction(vars) ? vars() : vars; try { if (isMessageId(message)) { // 如果是数字id, result = (activeMessages as any)[message] || message; } else { const msgId = this.idMap[message]; // 语言包可能是使用idMap映射过的,则需要转换 result = (activeMessages[msgId] || activeMessages[message] || message) as string | string[]; } const [pluraValue, vars] = this._getPluraValue(finalArgs); // 2. 处理复数 // 经过上面的处理,content可能是字符串或者数组 // content = "原始文本内容" || 复数形式["原始文本内容","原始文本内容"....] // 如果是数组说明要启用复数机制,需要根据插值变量中的某个变量来判断复数形式 if (Array.isArray(result) && result.length > 0) { // 如果存在复数命名变量,只取第一个复数变量 if (pluraValue !== null) { // 启用的是位置插值 result = this._getPluraMessage(result, pluraValue!); } else { // 如果找不到复数变量,则使用第一个内容 result = result[0]; } } // 如果没有传入插值变量,则直接返回 if (finalArgs.length === 0) result as string; // 进行插值处理 result = this.interpolator.replace(result as string, ...vars); if (isFunction(this.options.onTranslated)) { result = this.options.onTranslated(result); } if (this._translateTransformer && options?.transform) { result = this._translateTransformer(result, vars, options); } } catch (e: any) { this.logger.error(`翻译失败:${e.stack}`); } return result as R; } }