// @ts-nocheck import { warn, isObject } from './util' type Token = { type : 'text' | 'named' | 'list' | 'unknown', value : string } const RE_TOKEN_LIST_VALUE = /^(?:\d)+/ const RE_TOKEN_NAMED_VALUE = /^(?:\w)+/ /** * 解析格式化字符串并生成一个包含标记(Token)的数组。 * 这些标记可以是文本、列表或命名值。 * * @param {string} format - 需要解析的格式化字符串。 * @returns {Array} 返回一个包含解析后的标记的数组。 */ export function parse(format : string) : Array { const tokens : Array = [] let position : number = 0 let text : string = '' while (position < format.length) { let char : string = format.charAt(position++) if (char == '{') { if (text.length > 0) { const token : Token = { type: 'text', value: text } tokens.push(token) } text = '' let sub : string = '' char = format.charAt(position++) while (char != '}') { sub += char char = format.charAt(position++) } const isClosed = char == '}' const type = RE_TOKEN_LIST_VALUE.test(sub) ? 'list' : isClosed && RE_TOKEN_NAMED_VALUE.test(sub) ? 'named' : 'unknown' const token : Token = { type, value: sub } tokens.push(token) } else if (char == '%') { // when found rails i18n syntax, skip text capture if (format.charAt(position) != '{') { text += char } } else { text += char } } if (text.length > 0) { const token : Token = { type: 'text', value: text } tokens.push(token) } return tokens } /** * 根据给定的标记数组和值对象或数组,编译出相应的值数组。 * * @param {Array} tokens - 标记数组,包含文本、列表和命名值。 * @param {Object | Array} values - 值对象或数组,用于替换标记中的占位符。 * @returns {Array} 返回编译后的值数组。 */ function compile(tokens : Array, values : UTSJSONObject) : Array function compile(tokens : Array, values : Array) : Array function compile(tokens : Array, values : any) : Array { const compiled : Array = [] let index : number = 0; const mode : string = Array.isArray(values) ? 'list' : isObject(values) ? 'named' : 'unknown' if (mode == 'unknown') { return compiled } while (index < tokens.length) { const token : Token = tokens[index] switch (token.type) { case 'text': compiled.push(token.value) break case 'list': const index = parseInt(token.value, 10) if(mode == 'list') { const value = (values as any[])[index] compiled.push(value) } else { if (process.env.NODE_ENV !== 'production') { warn('list did not receive a valid values array') } } break case 'named': if (mode == 'named') { const value = (values as UTSJSONObject)[token.value] ?? '' compiled.push(`${value}`) } else { if (process.env.NODE_ENV !== 'production') { warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`) } } break case 'unknown': if(token.value.startsWith("'") && token.value.endsWith("'")) { compiled.push(token.value.slice(1, -1)) } else if (process.env.NODE_ENV !== 'production') { warn(`Detect 'unknown' type of token!`) } break } index++ } return compiled } export {compile} export default class BaseFormatter { private _caches : Map constructor() { this._caches = new Map() } interpolate(message : string, values : any | null) : any[] { if (values == null) { return [message] } let tokens : Array | null = this._caches.get(message) if (tokens == null) { tokens = parse(message) this._caches.set(message, tokens) } return compile(tokens, values) } }