update
This commit is contained in:
parent
64f9eaa93a
commit
37ad528dd0
1
packages/runtime/src/errors.ts
Normal file
1
packages/runtime/src/errors.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export class InvalidLanguageError extends Error{}
|
@ -24,6 +24,7 @@ const EmptyFormatters:any = {
|
|||||||
$config:{},
|
$config:{},
|
||||||
$types:{}
|
$types:{}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VoerkaI18nFormatterRegistry{
|
export class VoerkaI18nFormatterRegistry{
|
||||||
// 由于语言的格式化器集合允许是一个异步加载块,所以需要一个ready标志
|
// 由于语言的格式化器集合允许是一个异步加载块,所以需要一个ready标志
|
||||||
// 当语言格式化器集合加载完成后,ready标志才会变为true
|
// 当语言格式化器集合加载完成后,ready标志才会变为true
|
||||||
@ -101,6 +102,36 @@ export class VoerkaI18nFormatterRegistry{
|
|||||||
getConfig(language?:string){
|
getConfig(language?:string){
|
||||||
return language ? getByPath(this.#formatters,`${language}.$config`,{defaultValue:{}}) : {}
|
return language ? getByPath(this.#formatters,`${language}.$config`,{defaultValue:{}}) : {}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
获取指定语言中为每个数据类型指定的格式化器
|
||||||
|
*/
|
||||||
|
getTypes(language?:string){
|
||||||
|
return language ? getByPath(this.#formatters,`${language}.$types`,{defaultValue:{}}) : {}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
获取指定语言中为每个数据类型指定的格式化器
|
||||||
|
*/
|
||||||
|
getFormatters(language?:string){
|
||||||
|
return language ? getByPath(this.#formatters,language,{defaultValue:{}}) : {}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 更新指定语言的格式化器配置参数
|
||||||
|
* @param language
|
||||||
|
* @param config
|
||||||
|
*/
|
||||||
|
updateConfig(language:string,config:VoerkaI18nFormatterConfigs){
|
||||||
|
if(language in this.#formatters){
|
||||||
|
const formatters = this.#formatters[language]
|
||||||
|
if(typeof(formatters)=='function'){
|
||||||
|
throw new FormattersNotLoadedError(language)
|
||||||
|
}else{
|
||||||
|
if(!formatters.$config){
|
||||||
|
formatters.$config = {}
|
||||||
|
}
|
||||||
|
Object.assign(formatters.$config,config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 返回指定语言是否具有格式化器集合
|
* 返回指定语言是否具有格式化器集合
|
||||||
* @param language
|
* @param language
|
||||||
@ -151,4 +182,13 @@ export class VoerkaI18nFormatterRegistry{
|
|||||||
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
||||||
return (this.#formatters[this.#language] as VoerkaI18nFormatters).$config as VoerkaI18nFormatterConfigs
|
return (this.#formatters[this.#language] as VoerkaI18nFormatters).$config as VoerkaI18nFormatterConfigs
|
||||||
}
|
}
|
||||||
|
get types(){
|
||||||
|
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
||||||
|
return (this.#formatters[this.#language] as VoerkaI18nFormatters).$types as VoerkaI18nFormatterConfigs
|
||||||
|
}
|
||||||
|
get items(){
|
||||||
|
if(!this.#ready) throw new FormattersNotLoadedError(this.#language)
|
||||||
|
return this.#formatters[this.#language] as VoerkaI18nFormatterConfigs
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -173,23 +173,24 @@ function getDataTypeDefaultFormatter(scope:VoerkaI18nScope, activeLanguage:strin
|
|||||||
if (scope.cache.activeLanguage === activeLanguage) {
|
if (scope.cache.activeLanguage === activeLanguage) {
|
||||||
if (scope.cache.typedFormatters && dataType in scope.cache.typedFormatters)
|
if (scope.cache.typedFormatters && dataType in scope.cache.typedFormatters)
|
||||||
return scope.cache.typedFormatters[dataType];
|
return scope.cache.typedFormatters[dataType];
|
||||||
} else {
|
} else {
|
||||||
// 当语言切换时清空缓存
|
resetScopeCache(scope, activeLanguage); // 当语言切换时清空缓存
|
||||||
resetScopeCache(scope, activeLanguage);
|
|
||||||
}
|
}
|
||||||
const fallbackLanguage = scope.getLanguage(activeLanguage)?.fallback;
|
const fallbackLanguage = scope.getLanguage(activeLanguage)?.fallback;
|
||||||
// 先在当前作用域中查找,再在全局查找
|
// 先在当前作用域中查找,再在全局查找
|
||||||
const targets = [
|
const targets = [
|
||||||
scope.activeFormatters,
|
scope.formatters.types,
|
||||||
fallbackLanguage ? scope.formatters[fallbackLanguage] : null, // 如果指定了回退语言时,也在该回退语言中查找
|
scope.formatters.getTypes(fallbackLanguage),
|
||||||
scope.global.formatters[activeLanguage],
|
scope.formatters.getTypes("*"),
|
||||||
scope.global.formatters["*"],
|
scope.global.formatters.types,
|
||||||
|
scope.global.formatters.getTypes(fallbackLanguage),
|
||||||
|
scope.global.formatters.getTypes("*"),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
if (!target) continue;
|
if (!target) continue;
|
||||||
if(target){
|
if(target){
|
||||||
if (isPlainObject(target.$types) && isFunction(target.$types?.[dataType])) {
|
if (isPlainObject(target.$types) && isFunction(target.$types?.[dataType])) {
|
||||||
|
|
||||||
return (scope.cache.typedFormatters[dataType] = target.$types[dataType]);
|
return (scope.cache.typedFormatters[dataType] = target.$types[dataType]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,6 +202,9 @@ function getDataTypeDefaultFormatter(scope:VoerkaI18nScope, activeLanguage:strin
|
|||||||
*
|
*
|
||||||
* 查找逻辑
|
* 查找逻辑
|
||||||
* - 在当前作用域中查找
|
* - 在当前作用域中查找
|
||||||
|
* - 当前语言
|
||||||
|
* - 回退语言
|
||||||
|
* - 全局语言
|
||||||
* - 在全局作用域中查找
|
* - 在全局作用域中查找
|
||||||
*
|
*
|
||||||
* @param {*} scope
|
* @param {*} scope
|
||||||
@ -210,30 +214,32 @@ function getDataTypeDefaultFormatter(scope:VoerkaI18nScope, activeLanguage:strin
|
|||||||
*/
|
*/
|
||||||
function getFormatter(scope:VoerkaI18nScope, activeLanguage:string, name:string) {
|
function getFormatter(scope:VoerkaI18nScope, activeLanguage:string, name:string) {
|
||||||
// 1. 从缓存中直接读取: 缓存格式化器引用,避免重复检索
|
// 1. 从缓存中直接读取: 缓存格式化器引用,避免重复检索
|
||||||
if (!scope.$cache) resetScopeCache(scope);
|
if (!scope.cache) resetScopeCache(scope,activeLanguage);
|
||||||
if (scope.$cache.activeLanguage === activeLanguage) {
|
if (scope.cache.activeLanguage === activeLanguage) {
|
||||||
if (name in scope.$cache.formatters)
|
if (name in scope.cache.formatters)
|
||||||
return scope.$cache.formatters[name];
|
return scope.cache.formatters[name];
|
||||||
} else { // 当语言切换时清空缓存
|
} else { // 当语言切换时清空缓存
|
||||||
resetScopeCache(scope, activeLanguage);
|
resetScopeCache(scope, activeLanguage);
|
||||||
}
|
}
|
||||||
const fallbackLanguage = scope.getLanguage(activeLanguage).fallback;
|
const fallbackLanguage = scope.getLanguage(activeLanguage)?.fallback;
|
||||||
// 2. 先在当前作用域中查找,再在全局查找 formatters={$types,$config,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}
|
// 2. 先在当前作用域中查找,再在全局查找 formatters={$types,$config,[格式化器名称]:()=>{},[格式化器名称]:()=>{}}
|
||||||
const range = [
|
const range = [
|
||||||
scope.activeFormatters,
|
scope.formatters.formatters,
|
||||||
scope.formatters[fallbackLanguage], // 如果指定了回退语言时,也在该回退语言中查找
|
scope.formatters.getFormatters(fallbackLanguage),
|
||||||
scope.formatters["*"],
|
scope.formatters.getFormatters('*'),
|
||||||
scope.global.formatters[activeLanguage], // 适用于activeLanguage全局格式化器
|
|
||||||
scope.global.formatters[fallbackLanguage],
|
scope.global.formatters.formatters,
|
||||||
scope.global.formatters["*"], // 适用于所有语言的格式化器
|
scope.global.formatters.getFormatters(fallbackLanguage),
|
||||||
|
scope.global.formatters.getFormatters('*'),
|
||||||
];
|
];
|
||||||
for (const formatters of range) {
|
for (const formatters of range) {
|
||||||
if (!formatters) continue;
|
if (!formatters) continue;
|
||||||
if (isFunction(formatters[name])) {
|
if (isFunction(formatters[name])) {
|
||||||
return (scope.$cache.formatters[name] = formatters[name]);
|
return (scope.cache.formatters[name] = formatters[name]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checker是一种特殊的格式化器,会在特定的时间执行
|
* Checker是一种特殊的格式化器,会在特定的时间执行
|
||||||
*
|
*
|
||||||
@ -441,7 +447,7 @@ export function replaceInterpolatedVars(this:VoerkaI18nScope,template:string, ..
|
|||||||
// 读取模板字符串中的插值变量列表
|
// 读取模板字符串中的插值变量列表
|
||||||
// [[var1,[formatter,formatter,...],match],[var2,[formatter,formatter,...],match],...}
|
// [[var1,[formatter,formatter,...],match],[var2,[formatter,formatter,...],match],...}
|
||||||
let varValues = args[0];
|
let varValues = args[0];
|
||||||
return forEachInterpolatedVars(template,(varname, formatters, match) => {
|
return forEachInterpolatedVars(template,(varname:string, formatters, match) => {
|
||||||
let value = varname in varValues ? varValues[varname] : "";
|
let value = varname in varValues ? varValues[varname] : "";
|
||||||
return getFormattedValue(scope,activeLanguage,formatters,value,template);
|
return getFormattedValue(scope,activeLanguage,formatters,value,template);
|
||||||
}
|
}
|
||||||
@ -452,7 +458,7 @@ export function replaceInterpolatedVars(this:VoerkaI18nScope,template:string, ..
|
|||||||
const params =args.length === 1 && Array.isArray(args[0]) ? [...args[0]] : args;
|
const params =args.length === 1 && Array.isArray(args[0]) ? [...args[0]] : args;
|
||||||
if (params.length === 0) return template; // 没有变量则不需要进行插值处理,返回原字符串
|
if (params.length === 0) return template; // 没有变量则不需要进行插值处理,返回原字符串
|
||||||
let i = 0;
|
let i = 0;
|
||||||
return forEachInterpolatedVars(template,(varname, formatters, match) => {
|
return forEachInterpolatedVars(template,(varname:string, formatters, match) => {
|
||||||
if (params.length > i) {
|
if (params.length > i) {
|
||||||
return getFormattedValue(scope,activeLanguage,formatters,params[i++],template);
|
return getFormattedValue(scope,activeLanguage,formatters,params[i++],template);
|
||||||
} else {
|
} else {
|
||||||
|
@ -19,12 +19,12 @@ import type {
|
|||||||
VoerkaI18nScopeCache,
|
VoerkaI18nScopeCache,
|
||||||
VoerkaI18nTranslate,
|
VoerkaI18nTranslate,
|
||||||
VoerkaI18nLoaders,
|
VoerkaI18nLoaders,
|
||||||
VoerkaI18nTypesFormatters,
|
VoerkaI18nTypesFormatters,
|
||||||
VoerkaI18nFormatters,
|
VoerkaI18nFormatters,
|
||||||
VoerkaI18nDynamicLanguageMessages,
|
VoerkaI18nDynamicLanguageMessages,
|
||||||
VoerkaI18nTypesFormatterConfigs
|
|
||||||
} from "./types"
|
} from "./types"
|
||||||
import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
|
import { VoerkaI18nFormatterRegistry } from './formatterRegistry';
|
||||||
|
import { InvalidLanguageError } from "./errors"
|
||||||
|
|
||||||
export interface VoerkaI18nScopeOptions {
|
export interface VoerkaI18nScopeOptions {
|
||||||
id?: string
|
id?: string
|
||||||
@ -292,7 +292,7 @@ export class VoerkaI18nScope {
|
|||||||
* @param {*} language
|
* @param {*} language
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
private getLanguage(language:string):VoerkaI18nLanguageDefine | undefined{
|
getLanguage(language:string):VoerkaI18nLanguageDefine | undefined{
|
||||||
let index = this.languages.findIndex((lng) => lng.name == language);
|
let index = this.languages.findIndex((lng) => lng.name == language);
|
||||||
if (index !== -1) return this.languages[index];
|
if (index !== -1) return this.languages[index];
|
||||||
}
|
}
|
||||||
@ -311,7 +311,42 @@ export class VoerkaI18nScope {
|
|||||||
this.#options.messages = this.default;
|
this.#options.messages = this.default;
|
||||||
this.#options.activeLanguage = this.defaultLanguage;
|
this.#options.activeLanguage = this.defaultLanguage;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 语言信息包可以是:
|
||||||
|
* - 简单的对象{}
|
||||||
|
* - 或者是一个返回Promise<VoerkaI18nLanguageMessages>的异步函数
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param language 语言名称
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
private async loadLanguageMessages(language:string):Promise<VoerkaI18nLanguageMessages>{
|
||||||
|
if(!this.hasLanguage(language)) throw new InvalidLanguageError(`Not found language <${language}>`)
|
||||||
|
// 非默认语言可以是:语言包对象,也可以是一个异步加载语言包文件,加载器是一个异步函数
|
||||||
|
// 如果没有加载器,则无法加载语言包,因此回退到默认语言
|
||||||
|
let loader = this.loaders[language];
|
||||||
|
let messages:VoerkaI18nLanguageMessages = {}
|
||||||
|
if (isPlainObject(loader)) { // 静态语言包
|
||||||
|
messages = loader as unknown as VoerkaI18nLanguageMessages;
|
||||||
|
} else if (isFunction(loader)) { // 语言包异步chunk
|
||||||
|
messages = (await loader()).default;
|
||||||
|
} else if (isFunction(this.global.defaultMessageLoader)) {
|
||||||
|
// 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器
|
||||||
|
const loadedMessages = (await this.global.loadMessagesFromDefaultLoader(language,this)) as unknown as VoerkaI18nDynamicLanguageMessages;
|
||||||
|
if(isPlainObject(loadedMessages)){
|
||||||
|
// 需要保存动态语言包中的$config,合并到对应语言的格式化器配置
|
||||||
|
if(isPlainObject(loadedMessages.$config)){
|
||||||
|
this.formatters.updateConfig(language,loadedMessages.$config!)
|
||||||
|
delete loadedMessages.$config
|
||||||
|
}
|
||||||
|
messages = Object.assign({
|
||||||
|
$remote : true // 添加一个标识,表示该语言包是从远程加载的
|
||||||
|
},this.default,loadedMessages); // 合并默认语言包和动态语言包,这样就可以局部覆盖默认语言包
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return messages
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 刷新当前语言包
|
* 刷新当前语言包
|
||||||
* @param {*} newLanguage
|
* @param {*} newLanguage
|
||||||
@ -324,50 +359,31 @@ export class VoerkaI18nScope {
|
|||||||
this.#options.messages = this.default;
|
this.#options.messages = this.default;
|
||||||
await this._patch(this.#options.messages, newLanguage); // 异步补丁
|
await this._patch(this.#options.messages, newLanguage); // 异步补丁
|
||||||
await this._changeFormatters(newLanguage);
|
await this._changeFormatters(newLanguage);
|
||||||
|
this.#refreshing = false
|
||||||
return;
|
return;
|
||||||
}
|
}else{ // 非默认语言可以是静态语言包也可以是异步加载语言包
|
||||||
// 非默认语言需要异步加载语言包文件,加载器是一个异步函数
|
try{
|
||||||
// 如果没有加载器,则无法加载语言包,因此回退到默认语言
|
let messages = await this.loadLanguageMessages(newLanguage)
|
||||||
let loader = this.loaders[newLanguage];
|
if(messages){
|
||||||
try {
|
this.#options.messages = messages
|
||||||
let newMessages, useRemote =false;
|
this.#options.activeLanguage = newLanguage;
|
||||||
if (isPlainObject(loader)) { // 静态语言包
|
// 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了
|
||||||
newMessages = loader;
|
// 因为远程加载的语言包已经是补丁过的了
|
||||||
} else if (isFunction(loader)) { // 语言包异步chunk
|
if(!messages.$remote) {
|
||||||
newMessages = (await loader()).default;
|
await this._patch(this.#options.messages, newLanguage);
|
||||||
} else if (isFunction(this.global.defaultMessageLoader)) { // 从远程加载语言包:如果该语言没有指定加载器,则使用全局配置的默认加载器
|
|
||||||
const loadedMessages = (await this.global.loadMessagesFromDefaultLoader(newLanguage,this)) as unknown as VoerkaI18nDynamicLanguageMessages;
|
|
||||||
if(isPlainObject(loadedMessages)){
|
|
||||||
useRemote = true
|
|
||||||
// 需要保存动态语言包中的$config,合并到对应语言的格式化器配置
|
|
||||||
if(isPlainObject(loadedMessages.$config)){
|
|
||||||
this.formatters[newLanguage] = {
|
|
||||||
$config : loadedMessages.$config as any
|
|
||||||
}
|
|
||||||
delete loadedMessages.$config
|
|
||||||
}
|
}
|
||||||
newMessages = Object.assign({},this.default,loadedMessages);
|
// 切换到对应语言的格式化器
|
||||||
|
await this._changeFormatters(newLanguage);
|
||||||
|
}else{
|
||||||
|
this._fallback();
|
||||||
}
|
}
|
||||||
}
|
}catch(e:any){
|
||||||
if(newMessages){
|
if (this.debug) console.warn(`Error while loading language <${newLanguage}> on i18nScope(${this.id}): ${e.message}`);
|
||||||
this.#options.messages = newMessages
|
|
||||||
this.#options.activeLanguage = newLanguage;
|
|
||||||
// 打语言包补丁, 如果是从远程加载语言包则不需要再打补丁了
|
|
||||||
if(!useRemote) {
|
|
||||||
await this._patch(this.#options.messages, newLanguage);
|
|
||||||
}
|
|
||||||
// 切换到对应语言的格式化器
|
|
||||||
await this._changeFormatters(newLanguage);
|
|
||||||
}else{
|
|
||||||
this._fallback();
|
this._fallback();
|
||||||
}
|
} finally {
|
||||||
|
this.#refreshing = false;
|
||||||
} catch (e) {
|
}
|
||||||
if (this.debug) console.warn(`Error while loading language <${newLanguage}> on i18nScope(${this.id}): ${e.message}`);
|
}
|
||||||
this._fallback();
|
|
||||||
} finally {
|
|
||||||
this.#refreshing = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 当指定了默认语言包加载器后,会从服务加载语言补丁包来更新本地的语言包
|
* 当指定了默认语言包加载器后,会从服务加载语言补丁包来更新本地的语言包
|
||||||
@ -386,8 +402,8 @@ export class VoerkaI18nScope {
|
|||||||
Object.assign(messages, pachedMessages);
|
Object.assign(messages, pachedMessages);
|
||||||
this._savePatchedMessages(pachedMessages, newLanguage);
|
this._savePatchedMessages(pachedMessages, newLanguage);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e:any) {
|
||||||
if (this.debug) console.error(`Error while loading <${newLanguage}> patch messages from remote:`,e);
|
if (this.debug) console.error(`Error while loading <${newLanguage}> patch messages from remote:`,e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -66,8 +66,8 @@ function getPluraMessage(messages:any,value:number){
|
|||||||
* translate("要翻译的文本内容") 如果默认语言是中文,则不会进行翻译直接返回
|
* translate("要翻译的文本内容") 如果默认语言是中文,则不会进行翻译直接返回
|
||||||
* translate("I am {} {}","man") == I am man 位置插值
|
* translate("I am {} {}","man") == I am man 位置插值
|
||||||
* translate("I am {p}",{p:"man"}) 字典插值
|
* translate("I am {p}",{p:"man"}) 字典插值
|
||||||
* translate("total {$count} items", {$count:1}) //复数形式
|
* translate("total {$count} items", {$count:1}) 复数形式
|
||||||
* translate("total {} {} {} items",a,b,c) // 位置变量插值
|
* translate("total {} {} {} items",a,b,c) 位置变量插值
|
||||||
*
|
*
|
||||||
* this===scope 当前绑定的scope
|
* this===scope 当前绑定的scope
|
||||||
*
|
*
|
||||||
@ -75,37 +75,41 @@ function getPluraMessage(messages:any,value:number){
|
|||||||
export function translate(this:VoerkaI18nScope,message:string,...args:any[]) {
|
export function translate(this:VoerkaI18nScope,message:string,...args:any[]) {
|
||||||
const scope = this
|
const scope = this
|
||||||
const activeLanguage = scope.global.activeLanguage
|
const activeLanguage = scope.global.activeLanguage
|
||||||
let content:string = message
|
// 如果内容是复数,则其值是一个数组,数组中的每个元素是从1-N数量形式的文本内容
|
||||||
let vars=[] // 插值变量列表
|
let result:string | string[] = message
|
||||||
let pluralVars= [] // 复数变量
|
let vars=[] // 插值变量列表
|
||||||
let pluraValue = null // 复数值
|
let pluraValue = null // 复数值
|
||||||
if(!(typeof(message)==="string")) return message
|
if(!(typeof(message)==="string")) return message
|
||||||
try{
|
try{
|
||||||
// 1. 预处理变量: 复数变量保存至pluralVars中 , 变量如果是Function则调用
|
// 1. 预处理变量: 复数变量保存至pluralVars中 , 变量如果是Function则调用
|
||||||
if(arguments.length === 2 && isPlainObject(arguments[1])){
|
if(arguments.length === 2 && isPlainObject(arguments[1])){// 字典插值
|
||||||
const dictVars:Record<string,any>={}
|
const dictVars:Record<string,any>=arguments[1]
|
||||||
Object.entries(arguments[1]).forEach(([name,value])=>{
|
for(const [name,value] of Object.entries(dictVars)){
|
||||||
if(typeof(value)=="function"){
|
if(isFunction(value)){
|
||||||
try{
|
try{
|
||||||
dictVars[name] = value()
|
dictVars[name] = value()
|
||||||
}catch(e){
|
}catch(e){
|
||||||
dictVars[name] = value
|
dictVars[name] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 以$开头的视为复数变量
|
// 以$开头的视为复数变量,记录下来
|
||||||
if(name.startsWith("$") && typeof(dictVars[name])==="number") pluralVars.push(name)
|
const isNum:boolean = typeof(dictVars[name])==="number"
|
||||||
})
|
if((pluraValue==null && isNum) || name.startsWith("$") && isNum){
|
||||||
vars = [arguments[1]]
|
pluraValue = dictVars[name]
|
||||||
}else if(arguments.length >= 2){
|
}
|
||||||
|
}
|
||||||
|
vars = [dictVars]
|
||||||
|
}else if(arguments.length >= 2){ // 位置插值
|
||||||
vars = [...arguments].splice(1).map((arg,index)=>{
|
vars = [...arguments].splice(1).map((arg,index)=>{
|
||||||
try{
|
try{
|
||||||
arg = isFunction(arg) ? arg() : arg
|
arg = isFunction(arg) ? arg() : arg
|
||||||
// 位置参数中以第一个数值变量为复数变量
|
// 约定:位置参数中以第一个数值变量作为指示复数变量
|
||||||
if(isNumber(arg)) pluraValue = parseInt(arg)
|
if(isNumber(arg)) pluraValue = parseInt(arg)
|
||||||
}catch(e){ }
|
}catch(e){
|
||||||
|
return String(arg)
|
||||||
|
}
|
||||||
return arg
|
return arg
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 取得翻译文本模板字符串
|
// 3. 取得翻译文本模板字符串
|
||||||
@ -114,37 +118,35 @@ export function translate(this:VoerkaI18nScope,message:string,...args:any[]) {
|
|||||||
// 如果当前语言就是默认语言,不需要查询加载,只需要做插值变换即可
|
// 如果当前语言就是默认语言,不需要查询加载,只需要做插值变换即可
|
||||||
// 当源文件运用了babel插件后会将原始文本内容转换为msgId
|
// 当源文件运用了babel插件后会将原始文本内容转换为msgId
|
||||||
// 如果是msgId则从scope.default中读取,scope.default=默认语言包={<id>:<message>}
|
// 如果是msgId则从scope.default中读取,scope.default=默认语言包={<id>:<message>}
|
||||||
if(isMessageId(content)){
|
if(isMessageId(result)){
|
||||||
content = scope.default[content] || message
|
result = scope.default[result] || message
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
// 2.2 从当前语言包中取得翻译文本模板字符串
|
// 2.2 从当前语言包中取得翻译文本模板字符串
|
||||||
// 如果没有启用babel插件将源文本转换为msgId,需要先将文本内容转换为msgId
|
// 如果没有启用babel插件将源文本转换为msgId,需要先将文本内容转换为msgId
|
||||||
let msgId = isMessageId(content) ? content : scope.idMap[content]
|
let msgId = isMessageId(result) ? result : scope.idMap[result]
|
||||||
content = scope.messages[msgId] || content
|
result = scope.messages[msgId] || result
|
||||||
}
|
}
|
||||||
// 2. 处理复数
|
// 2. 处理复数
|
||||||
// 经过上面的处理,content可能是字符串或者数组
|
// 经过上面的处理,content可能是字符串或者数组
|
||||||
// content = "原始文本内容" || 复数形式["原始文本内容","原始文本内容"....]
|
// content = "原始文本内容" || 复数形式["原始文本内容","原始文本内容"....]
|
||||||
// 如果是数组说明要启用复数机制,需要根据插值变量中的某个变量来判断复数形式
|
// 如果是数组说明要启用复数机制,需要根据插值变量中的某个变量来判断复数形式
|
||||||
if(Array.isArray(content) && content.length>0){
|
if(Array.isArray(result) && result.length>0){
|
||||||
// 如果存在复数命名变量,只取第一个复数变量
|
// 如果存在复数命名变量,只取第一个复数变量
|
||||||
if(pluraValue!==null){ // 启用的是位置插值,pluraIndex=第一个数字变量的位置
|
if(pluraValue!==null){ // 启用的是位置插值
|
||||||
content = getPluraMessage(content,pluraValue)
|
result = getPluraMessage(result,pluraValue)
|
||||||
}else if(pluralVar.length>0){
|
|
||||||
content = getPluraMessage(content,parseInt(vars(pluralVar[0])))
|
|
||||||
}else{ // 如果找不到复数变量,则使用第一个内容
|
}else{ // 如果找不到复数变量,则使用第一个内容
|
||||||
content = content[0]
|
result = result[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 进行插值处理
|
// 进行插值处理
|
||||||
if(vars.length==0){
|
if(vars.length==0){
|
||||||
return content
|
return result
|
||||||
}else{
|
}else{
|
||||||
return replaceInterpolatedVars.call(scope,content,...vars)
|
return replaceInterpolatedVars.call(scope,result as string,...vars)
|
||||||
}
|
}
|
||||||
}catch(e){
|
}catch(e){
|
||||||
return content // 出错则返回原始文本
|
return result // 出错则返回原始文本
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user