You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
7.5 KiB
282 lines
7.5 KiB
const isArray = Array.isArray;
|
|
const isObject = (val) => val !== null && typeof val === "object";
|
|
const defaultDelimiters = ["{", "}"];
|
|
class BaseFormatter {
|
|
constructor() {
|
|
this._caches = /* @__PURE__ */ Object.create(null);
|
|
}
|
|
interpolate(message, values, delimiters = defaultDelimiters) {
|
|
if (!values) {
|
|
return [message];
|
|
}
|
|
let tokens = this._caches[message];
|
|
if (!tokens) {
|
|
tokens = parse(message, delimiters);
|
|
this._caches[message] = tokens;
|
|
}
|
|
return compile(tokens, values);
|
|
}
|
|
}
|
|
const RE_TOKEN_LIST_VALUE = /^(?:\d)+/;
|
|
const RE_TOKEN_NAMED_VALUE = /^(?:\w)+/;
|
|
function parse(format, [startDelimiter, endDelimiter]) {
|
|
const tokens = [];
|
|
let position = 0;
|
|
let text = "";
|
|
while (position < format.length) {
|
|
let char = format[position++];
|
|
if (char === startDelimiter) {
|
|
if (text) {
|
|
tokens.push({ type: "text", value: text });
|
|
}
|
|
text = "";
|
|
let sub = "";
|
|
char = format[position++];
|
|
while (char !== void 0 && char !== endDelimiter) {
|
|
sub += char;
|
|
char = format[position++];
|
|
}
|
|
const isClosed = char === endDelimiter;
|
|
const type = RE_TOKEN_LIST_VALUE.test(sub) ? "list" : isClosed && RE_TOKEN_NAMED_VALUE.test(sub) ? "named" : "unknown";
|
|
tokens.push({ value: sub, type });
|
|
} else {
|
|
text += char;
|
|
}
|
|
}
|
|
text && tokens.push({ type: "text", value: text });
|
|
return tokens;
|
|
}
|
|
function compile(tokens, values) {
|
|
const compiled = [];
|
|
let index = 0;
|
|
const mode = isArray(values) ? "list" : isObject(values) ? "named" : "unknown";
|
|
if (mode === "unknown") {
|
|
return compiled;
|
|
}
|
|
while (index < tokens.length) {
|
|
const token = tokens[index];
|
|
switch (token.type) {
|
|
case "text":
|
|
compiled.push(token.value);
|
|
break;
|
|
case "list":
|
|
compiled.push(values[parseInt(token.value, 10)]);
|
|
break;
|
|
case "named":
|
|
if (mode === "named") {
|
|
compiled.push(values[token.value]);
|
|
} else {
|
|
{
|
|
console.warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`);
|
|
}
|
|
}
|
|
break;
|
|
case "unknown":
|
|
{
|
|
console.warn(`Detect 'unknown' type of token!`);
|
|
}
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
return compiled;
|
|
}
|
|
const LOCALE_ZH_HANS = "zh-Hans";
|
|
const LOCALE_ZH_HANT = "zh-Hant";
|
|
const LOCALE_EN = "en";
|
|
const LOCALE_FR = "fr";
|
|
const LOCALE_ES = "es";
|
|
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
|
|
const defaultFormatter = new BaseFormatter();
|
|
function include(str, parts) {
|
|
return !!parts.find((part) => str.indexOf(part) !== -1);
|
|
}
|
|
function startsWith(str, parts) {
|
|
return parts.find((part) => str.indexOf(part) === 0);
|
|
}
|
|
function normalizeLocale(locale, messages) {
|
|
if (!locale) {
|
|
return;
|
|
}
|
|
locale = locale.trim().replace(/_/g, "-");
|
|
if (messages && messages[locale]) {
|
|
return locale;
|
|
}
|
|
locale = locale.toLowerCase();
|
|
if (locale === "chinese") {
|
|
return LOCALE_ZH_HANS;
|
|
}
|
|
if (locale.indexOf("zh") === 0) {
|
|
if (locale.indexOf("-hans") > -1) {
|
|
return LOCALE_ZH_HANS;
|
|
}
|
|
if (locale.indexOf("-hant") > -1) {
|
|
return LOCALE_ZH_HANT;
|
|
}
|
|
if (include(locale, ["-tw", "-hk", "-mo", "-cht"])) {
|
|
return LOCALE_ZH_HANT;
|
|
}
|
|
return LOCALE_ZH_HANS;
|
|
}
|
|
const lang = startsWith(locale, [LOCALE_EN, LOCALE_FR, LOCALE_ES]);
|
|
if (lang) {
|
|
return lang;
|
|
}
|
|
}
|
|
class I18n {
|
|
constructor({ locale, fallbackLocale, messages, watcher, formater }) {
|
|
this.locale = LOCALE_EN;
|
|
this.fallbackLocale = LOCALE_EN;
|
|
this.message = {};
|
|
this.messages = {};
|
|
this.watchers = [];
|
|
if (fallbackLocale) {
|
|
this.fallbackLocale = fallbackLocale;
|
|
}
|
|
this.formater = formater || defaultFormatter;
|
|
this.messages = messages || {};
|
|
this.setLocale(locale || LOCALE_EN);
|
|
if (watcher) {
|
|
this.watchLocale(watcher);
|
|
}
|
|
}
|
|
setLocale(locale) {
|
|
const oldLocale = this.locale;
|
|
this.locale = normalizeLocale(locale, this.messages) || this.fallbackLocale;
|
|
if (!this.messages[this.locale]) {
|
|
this.messages[this.locale] = {};
|
|
}
|
|
this.message = this.messages[this.locale];
|
|
if (oldLocale !== this.locale) {
|
|
this.watchers.forEach((watcher) => {
|
|
watcher(this.locale, oldLocale);
|
|
});
|
|
}
|
|
}
|
|
getLocale() {
|
|
return this.locale;
|
|
}
|
|
watchLocale(fn) {
|
|
const index = this.watchers.push(fn) - 1;
|
|
return () => {
|
|
this.watchers.splice(index, 1);
|
|
};
|
|
}
|
|
add(locale, message, override = true) {
|
|
const curMessages = this.messages[locale];
|
|
if (curMessages) {
|
|
if (override) {
|
|
Object.assign(curMessages, message);
|
|
} else {
|
|
Object.keys(message).forEach((key) => {
|
|
if (!hasOwn(curMessages, key)) {
|
|
curMessages[key] = message[key];
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
this.messages[locale] = message;
|
|
}
|
|
}
|
|
f(message, values, delimiters) {
|
|
return this.formater.interpolate(message, values, delimiters).join("");
|
|
}
|
|
t(key, locale, values) {
|
|
let message = this.message;
|
|
if (typeof locale === "string") {
|
|
locale = normalizeLocale(locale, this.messages);
|
|
locale && (message = this.messages[locale]);
|
|
} else {
|
|
values = locale;
|
|
}
|
|
if (!hasOwn(message, key)) {
|
|
console.warn(`Cannot translate the value of keypath ${key}. Use the value of keypath as default.`);
|
|
return key;
|
|
}
|
|
return this.formater.interpolate(message[key], values).join("");
|
|
}
|
|
}
|
|
function watchAppLocale(appVm, i18n) {
|
|
if (appVm.$watchLocale) {
|
|
appVm.$watchLocale((newLocale) => {
|
|
i18n.setLocale(newLocale);
|
|
});
|
|
} else {
|
|
appVm.$watch(() => appVm.$locale, (newLocale) => {
|
|
i18n.setLocale(newLocale);
|
|
});
|
|
}
|
|
}
|
|
function getDefaultLocale() {
|
|
if (typeof uni !== "undefined" && uni.getLocale) {
|
|
return uni.getLocale();
|
|
}
|
|
if (typeof global !== "undefined" && global.getLocale) {
|
|
return global.getLocale();
|
|
}
|
|
return LOCALE_EN;
|
|
}
|
|
function initVueI18n(locale, messages = {}, fallbackLocale, watcher) {
|
|
if (typeof locale !== "string") {
|
|
[locale, messages] = [
|
|
messages,
|
|
locale
|
|
];
|
|
}
|
|
if (typeof locale !== "string") {
|
|
locale = getDefaultLocale();
|
|
}
|
|
if (typeof fallbackLocale !== "string") {
|
|
fallbackLocale = typeof __uniConfig !== "undefined" && __uniConfig.fallbackLocale || LOCALE_EN;
|
|
}
|
|
const i18n = new I18n({
|
|
locale,
|
|
fallbackLocale,
|
|
messages,
|
|
watcher
|
|
});
|
|
let t = (key, values) => {
|
|
if (typeof getApp !== "function") {
|
|
t = function(key2, values2) {
|
|
return i18n.t(key2, values2);
|
|
};
|
|
} else {
|
|
let isWatchedAppLocale = false;
|
|
t = function(key2, values2) {
|
|
const appVm = getApp().$vm;
|
|
if (appVm) {
|
|
appVm.$locale;
|
|
if (!isWatchedAppLocale) {
|
|
isWatchedAppLocale = true;
|
|
watchAppLocale(appVm, i18n);
|
|
}
|
|
}
|
|
return i18n.t(key2, values2);
|
|
};
|
|
}
|
|
return t(key, values);
|
|
};
|
|
return {
|
|
i18n,
|
|
f(message, values, delimiters) {
|
|
return i18n.f(message, values, delimiters);
|
|
},
|
|
t(key, values) {
|
|
return t(key, values);
|
|
},
|
|
add(locale2, message, override = true) {
|
|
return i18n.add(locale2, message, override);
|
|
},
|
|
watch(fn) {
|
|
return i18n.watchLocale(fn);
|
|
},
|
|
getLocale() {
|
|
return i18n.getLocale();
|
|
},
|
|
setLocale(newLocale) {
|
|
return i18n.setLocale(newLocale);
|
|
}
|
|
};
|
|
}
|
|
export { initVueI18n as i };
|
|
|