vue-i18n-bridge
Version:
A bridge for Vue I18n Legacy
1,433 lines (1,417 loc) • 93.6 kB
JavaScript
/*!
* vue-i18n-bridge v9.2.0-beta.6
* (c) 2021 kazuya kawaguchi
* Released under the MIT License.
*/
import { ref, getCurrentInstance, computed, watch, onBeforeMount, onUnmounted } from '@vue/composition-api';
/**
* Original Utilities
* written by kazuya kawaguchi
*/
const inBrowser = typeof window !== 'undefined';
let mark;
{
const perf = inBrowser && window.performance;
if (perf &&
perf.mark &&
perf.measure &&
perf.clearMarks &&
perf.clearMeasures) {
mark = (tag) => perf.mark(tag);
}
}
const RE_ARGS = /\{([0-9a-zA-Z]+)\}/g;
/* eslint-disable */
function format(message, ...args) {
if (args.length === 1 && isObject(args[0])) {
args = args[0];
}
if (!args || !args.hasOwnProperty) {
args = {};
}
return message.replace(RE_ARGS, (match, identifier) => {
return args.hasOwnProperty(identifier) ? args[identifier] : '';
});
}
const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
const makeSymbol = (name) => hasSymbol ? Symbol(name) : name;
const generateFormatCacheKey = (locale, key, source) => friendlyJSONstringify({ l: locale, k: key, s: source });
const friendlyJSONstringify = (json) => JSON.stringify(json)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029')
.replace(/\u0027/g, '\\u0027');
const isNumber = (val) => typeof val === 'number' && isFinite(val);
const isDate = (val) => toTypeString(val) === '[object Date]';
const isRegExp = (val) => toTypeString(val) === '[object RegExp]';
const isEmptyObject = (val) => isPlainObject(val) && Object.keys(val).length === 0;
function warn(msg, err) {
if (typeof console !== 'undefined') {
console.warn(`[intlify] ` + msg);
/* istanbul ignore if */
if (err) {
console.warn(err.stack);
}
}
}
const assign = Object.assign;
let _globalThis;
const getGlobalThis = () => {
// prettier-ignore
return (_globalThis ||
(_globalThis =
typeof globalThis !== 'undefined'
? globalThis
: typeof self !== 'undefined'
? self
: typeof window !== 'undefined'
? window
: typeof global !== 'undefined'
? global
: {}));
};
function escapeHtml(rawText) {
return rawText
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return hasOwnProperty.call(obj, key);
}
/* eslint-enable */
/**
* Useful Utilities By Evan you
* Modified by kazuya kawaguchi
* MIT License
* https://github.com/vuejs/vue-next/blob/master/packages/shared/src/index.ts
* https://github.com/vuejs/vue-next/blob/master/packages/shared/src/codeframe.ts
*/
const isArray = Array.isArray;
const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isBoolean = (val) => typeof val === 'boolean';
const isObject = (val) => // eslint-disable-line
val !== null && typeof val === 'object';
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
// for converting list and named values to displayed strings.
const toDisplayString = (val) => {
return val == null
? ''
: isArray(val) || (isPlainObject(val) && val.toString === objectToString)
? JSON.stringify(val, null, 2)
: String(val);
};
const CompileErrorCodes = {
// tokenizer error codes
EXPECTED_TOKEN: 1,
INVALID_TOKEN_IN_PLACEHOLDER: 2,
UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER: 3,
UNKNOWN_ESCAPE_SEQUENCE: 4,
INVALID_UNICODE_ESCAPE_SEQUENCE: 5,
UNBALANCED_CLOSING_BRACE: 6,
UNTERMINATED_CLOSING_BRACE: 7,
EMPTY_PLACEHOLDER: 8,
NOT_ALLOW_NEST_PLACEHOLDER: 9,
INVALID_LINKED_FORMAT: 10,
// parser error codes
MUST_HAVE_MESSAGES_IN_PLURAL: 11,
UNEXPECTED_EMPTY_LINKED_MODIFIER: 12,
UNEXPECTED_EMPTY_LINKED_KEY: 13,
UNEXPECTED_LEXICAL_ANALYSIS: 14,
// Special value for higher-order compilers to pick up the last code
// to avoid collision of error codes. This should always be kept as the last
// item.
__EXTEND_POINT__: 15
};
/** @internal */
const errorMessages$2 = {
// tokenizer error messages
[CompileErrorCodes.EXPECTED_TOKEN]: `Expected token: '{0}'`,
[CompileErrorCodes.INVALID_TOKEN_IN_PLACEHOLDER]: `Invalid token in placeholder: '{0}'`,
[CompileErrorCodes.UNTERMINATED_SINGLE_QUOTE_IN_PLACEHOLDER]: `Unterminated single quote in placeholder`,
[CompileErrorCodes.UNKNOWN_ESCAPE_SEQUENCE]: `Unknown escape sequence: \\{0}`,
[CompileErrorCodes.INVALID_UNICODE_ESCAPE_SEQUENCE]: `Invalid unicode escape sequence: {0}`,
[CompileErrorCodes.UNBALANCED_CLOSING_BRACE]: `Unbalanced closing brace`,
[CompileErrorCodes.UNTERMINATED_CLOSING_BRACE]: `Unterminated closing brace`,
[CompileErrorCodes.EMPTY_PLACEHOLDER]: `Empty placeholder`,
[CompileErrorCodes.NOT_ALLOW_NEST_PLACEHOLDER]: `Not allowed nest placeholder`,
[CompileErrorCodes.INVALID_LINKED_FORMAT]: `Invalid linked format`,
// parser error messages
[CompileErrorCodes.MUST_HAVE_MESSAGES_IN_PLURAL]: `Plural must have messages`,
[CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_MODIFIER]: `Unexpected empty linked modifier`,
[CompileErrorCodes.UNEXPECTED_EMPTY_LINKED_KEY]: `Unexpected empty linked key`,
[CompileErrorCodes.UNEXPECTED_LEXICAL_ANALYSIS]: `Unexpected lexical analysis in token: '{0}'`
};
function createCompileError(code, loc, options = {}) {
const { domain, messages, args } = options;
const msg = format((messages || errorMessages$2)[code] || '', ...(args || []))
;
const error = new SyntaxError(String(msg));
error.code = code;
if (loc) {
error.location = loc;
}
error.domain = domain;
return error;
}
const pathStateMachine = [];
pathStateMachine[0 /* BEFORE_PATH */] = {
["w" /* WORKSPACE */]: [0 /* BEFORE_PATH */],
["i" /* IDENT */]: [3 /* IN_IDENT */, 0 /* APPEND */],
["[" /* LEFT_BRACKET */]: [4 /* IN_SUB_PATH */],
["o" /* END_OF_FAIL */]: [7 /* AFTER_PATH */]
};
pathStateMachine[1 /* IN_PATH */] = {
["w" /* WORKSPACE */]: [1 /* IN_PATH */],
["." /* DOT */]: [2 /* BEFORE_IDENT */],
["[" /* LEFT_BRACKET */]: [4 /* IN_SUB_PATH */],
["o" /* END_OF_FAIL */]: [7 /* AFTER_PATH */]
};
pathStateMachine[2 /* BEFORE_IDENT */] = {
["w" /* WORKSPACE */]: [2 /* BEFORE_IDENT */],
["i" /* IDENT */]: [3 /* IN_IDENT */, 0 /* APPEND */],
["0" /* ZERO */]: [3 /* IN_IDENT */, 0 /* APPEND */]
};
pathStateMachine[3 /* IN_IDENT */] = {
["i" /* IDENT */]: [3 /* IN_IDENT */, 0 /* APPEND */],
["0" /* ZERO */]: [3 /* IN_IDENT */, 0 /* APPEND */],
["w" /* WORKSPACE */]: [1 /* IN_PATH */, 1 /* PUSH */],
["." /* DOT */]: [2 /* BEFORE_IDENT */, 1 /* PUSH */],
["[" /* LEFT_BRACKET */]: [4 /* IN_SUB_PATH */, 1 /* PUSH */],
["o" /* END_OF_FAIL */]: [7 /* AFTER_PATH */, 1 /* PUSH */]
};
pathStateMachine[4 /* IN_SUB_PATH */] = {
["'" /* SINGLE_QUOTE */]: [5 /* IN_SINGLE_QUOTE */, 0 /* APPEND */],
["\"" /* DOUBLE_QUOTE */]: [6 /* IN_DOUBLE_QUOTE */, 0 /* APPEND */],
["[" /* LEFT_BRACKET */]: [
4 /* IN_SUB_PATH */,
2 /* INC_SUB_PATH_DEPTH */
],
["]" /* RIGHT_BRACKET */]: [1 /* IN_PATH */, 3 /* PUSH_SUB_PATH */],
["o" /* END_OF_FAIL */]: 8 /* ERROR */,
["l" /* ELSE */]: [4 /* IN_SUB_PATH */, 0 /* APPEND */]
};
pathStateMachine[5 /* IN_SINGLE_QUOTE */] = {
["'" /* SINGLE_QUOTE */]: [4 /* IN_SUB_PATH */, 0 /* APPEND */],
["o" /* END_OF_FAIL */]: 8 /* ERROR */,
["l" /* ELSE */]: [5 /* IN_SINGLE_QUOTE */, 0 /* APPEND */]
};
pathStateMachine[6 /* IN_DOUBLE_QUOTE */] = {
["\"" /* DOUBLE_QUOTE */]: [4 /* IN_SUB_PATH */, 0 /* APPEND */],
["o" /* END_OF_FAIL */]: 8 /* ERROR */,
["l" /* ELSE */]: [6 /* IN_DOUBLE_QUOTE */, 0 /* APPEND */]
};
/**
* Check if an expression is a literal value.
*/
const literalValueRE = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;
function isLiteral(exp) {
return literalValueRE.test(exp);
}
/**
* Strip quotes from a string
*/
function stripQuotes(str) {
const a = str.charCodeAt(0);
const b = str.charCodeAt(str.length - 1);
return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str;
}
/**
* Determine the type of a character in a keypath.
*/
function getPathCharType(ch) {
if (ch === undefined || ch === null) {
return "o" /* END_OF_FAIL */;
}
const code = ch.charCodeAt(0);
switch (code) {
case 0x5b: // [
case 0x5d: // ]
case 0x2e: // .
case 0x22: // "
case 0x27: // '
return ch;
case 0x5f: // _
case 0x24: // $
case 0x2d: // -
return "i" /* IDENT */;
case 0x09: // Tab (HT)
case 0x0a: // Newline (LF)
case 0x0d: // Return (CR)
case 0xa0: // No-break space (NBSP)
case 0xfeff: // Byte Order Mark (BOM)
case 0x2028: // Line Separator (LS)
case 0x2029: // Paragraph Separator (PS)
return "w" /* WORKSPACE */;
}
return "i" /* IDENT */;
}
/**
* Format a subPath, return its plain form if it is
* a literal string or number. Otherwise prepend the
* dynamic indicator (*).
*/
function formatSubPath(path) {
const trimmed = path.trim();
// invalid leading 0
if (path.charAt(0) === '0' && isNaN(parseInt(path))) {
return false;
}
return isLiteral(trimmed)
? stripQuotes(trimmed)
: "*" /* ASTARISK */ + trimmed;
}
/**
* Parse a string path into an array of segments
*/
function parse(path) {
const keys = [];
let index = -1;
let mode = 0 /* BEFORE_PATH */;
let subPathDepth = 0;
let c;
let key; // eslint-disable-line
let newChar;
let type;
let transition;
let action;
let typeMap;
const actions = [];
actions[0 /* APPEND */] = () => {
if (key === undefined) {
key = newChar;
}
else {
key += newChar;
}
};
actions[1 /* PUSH */] = () => {
if (key !== undefined) {
keys.push(key);
key = undefined;
}
};
actions[2 /* INC_SUB_PATH_DEPTH */] = () => {
actions[0 /* APPEND */]();
subPathDepth++;
};
actions[3 /* PUSH_SUB_PATH */] = () => {
if (subPathDepth > 0) {
subPathDepth--;
mode = 4 /* IN_SUB_PATH */;
actions[0 /* APPEND */]();
}
else {
subPathDepth = 0;
if (key === undefined) {
return false;
}
key = formatSubPath(key);
if (key === false) {
return false;
}
else {
actions[1 /* PUSH */]();
}
}
};
function maybeUnescapeQuote() {
const nextChar = path[index + 1];
if ((mode === 5 /* IN_SINGLE_QUOTE */ &&
nextChar === "'" /* SINGLE_QUOTE */) ||
(mode === 6 /* IN_DOUBLE_QUOTE */ &&
nextChar === "\"" /* DOUBLE_QUOTE */)) {
index++;
newChar = '\\' + nextChar;
actions[0 /* APPEND */]();
return true;
}
}
while (mode !== null) {
index++;
c = path[index];
if (c === '\\' && maybeUnescapeQuote()) {
continue;
}
type = getPathCharType(c);
typeMap = pathStateMachine[mode];
transition = typeMap[type] || typeMap["l" /* ELSE */] || 8 /* ERROR */;
// check parse error
if (transition === 8 /* ERROR */) {
return;
}
mode = transition[0];
if (transition[1] !== undefined) {
action = actions[transition[1]];
if (action) {
newChar = c;
if (action() === false) {
return;
}
}
}
// check parse finish
if (mode === 7 /* AFTER_PATH */) {
return keys;
}
}
}
// path token cache
const cache = new Map();
/**
* key-value message resolver
*
* @remarks
* Resolves messages with the key-value structure. Note that messages with a hierarchical structure such as objects cannot be resolved
*
* @param obj - A target object to be resolved with path
* @param path - A {@link Path | path} to resolve the value of message
*
* @returns A resolved {@link PathValue | path value}
*
* @VueI18nGeneral
*/
function resolveWithKeyValue(obj, path) {
return isObject(obj) ? obj[path] : null;
}
/**
* message resolver
*
* @remarks
* Resolves messages. messages with a hierarchical structure such as objects can be resolved. This resolver is used in VueI18n as default.
*
* @param obj - A target object to be resolved with path
* @param path - A {@link Path | path} to resolve the value of message
*
* @returns A resolved {@link PathValue | path value}
*
* @VueI18nGeneral
*/
function resolveValue(obj, path) {
// check object
if (!isObject(obj)) {
return null;
}
// parse path
let hit = cache.get(path);
if (!hit) {
hit = parse(path);
if (hit) {
cache.set(path, hit);
}
}
// check hit
if (!hit) {
return null;
}
// resolve path value
const len = hit.length;
let last = obj;
let i = 0;
while (i < len) {
const val = last[hit[i]];
if (val === undefined) {
return null;
}
last = val;
i++;
}
return last;
}
const DEFAULT_MODIFIER = (str) => str;
const DEFAULT_MESSAGE = (ctx) => ''; // eslint-disable-line
const DEFAULT_MESSAGE_DATA_TYPE = 'text';
const DEFAULT_NORMALIZE = (values) => values.length === 0 ? '' : values.join('');
const DEFAULT_INTERPOLATE = toDisplayString;
function pluralDefault(choice, choicesLength) {
choice = Math.abs(choice);
if (choicesLength === 2) {
// prettier-ignore
return choice
? choice > 1
? 1
: 0
: 1;
}
return choice ? Math.min(choice, 2) : 0;
}
function getPluralIndex(options) {
// prettier-ignore
const index = isNumber(options.pluralIndex)
? options.pluralIndex
: -1;
// prettier-ignore
return options.named && (isNumber(options.named.count) || isNumber(options.named.n))
? isNumber(options.named.count)
? options.named.count
: isNumber(options.named.n)
? options.named.n
: index
: index;
}
function normalizeNamed(pluralIndex, props) {
if (!props.count) {
props.count = pluralIndex;
}
if (!props.n) {
props.n = pluralIndex;
}
}
function createMessageContext(options = {}) {
const locale = options.locale;
const pluralIndex = getPluralIndex(options);
const pluralRule = isObject(options.pluralRules) &&
isString(locale) &&
isFunction(options.pluralRules[locale])
? options.pluralRules[locale]
: pluralDefault;
const orgPluralRule = isObject(options.pluralRules) &&
isString(locale) &&
isFunction(options.pluralRules[locale])
? pluralDefault
: undefined;
const plural = (messages) => messages[pluralRule(pluralIndex, messages.length, orgPluralRule)];
const _list = options.list || [];
const list = (index) => _list[index];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const _named = options.named || {};
isNumber(options.pluralIndex) && normalizeNamed(pluralIndex, _named);
const named = (key) => _named[key];
// TODO: need to design resolve message function?
function message(key) {
// prettier-ignore
const msg = isFunction(options.messages)
? options.messages(key)
: isObject(options.messages)
? options.messages[key]
: false;
return !msg
? options.parent
? options.parent.message(key) // resolve from parent messages
: DEFAULT_MESSAGE
: msg;
}
const _modifier = (name) => options.modifiers
? options.modifiers[name]
: DEFAULT_MODIFIER;
const normalize = isPlainObject(options.processor) && isFunction(options.processor.normalize)
? options.processor.normalize
: DEFAULT_NORMALIZE;
const interpolate = isPlainObject(options.processor) &&
isFunction(options.processor.interpolate)
? options.processor.interpolate
: DEFAULT_INTERPOLATE;
const type = isPlainObject(options.processor) && isString(options.processor.type)
? options.processor.type
: DEFAULT_MESSAGE_DATA_TYPE;
const ctx = {
["list" /* LIST */]: list,
["named" /* NAMED */]: named,
["plural" /* PLURAL */]: plural,
["linked" /* LINKED */]: (key, modifier) => {
// TODO: should check `key`
const msg = message(key)(ctx);
return isString(modifier) ? _modifier(modifier)(msg) : msg;
},
["message" /* MESSAGE */]: message,
["type" /* TYPE */]: type,
["interpolate" /* INTERPOLATE */]: interpolate,
["normalize" /* NORMALIZE */]: normalize
};
return ctx;
}
const IntlifyDevToolsHooks = {
I18nInit: 'i18n:init',
FunctionTranslate: 'function:translate'
};
let devtools = null;
function setDevToolsHook(hook) {
devtools = hook;
}
function initI18nDevTools(i18n, version, meta) {
// TODO: queue if devtools is undefined
devtools &&
devtools.emit(IntlifyDevToolsHooks.I18nInit, {
timestamp: Date.now(),
i18n,
version,
meta
});
}
const translateDevTools = /* #__PURE__*/ createDevToolsHook(IntlifyDevToolsHooks.FunctionTranslate);
function createDevToolsHook(hook) {
return (payloads) => devtools && devtools.emit(hook, payloads);
}
const CoreWarnCodes = {
NOT_FOUND_KEY: 1,
FALLBACK_TO_TRANSLATE: 2,
CANNOT_FORMAT_NUMBER: 3,
FALLBACK_TO_NUMBER_FORMAT: 4,
CANNOT_FORMAT_DATE: 5,
FALLBACK_TO_DATE_FORMAT: 6,
__EXTEND_POINT__: 7
};
/** @internal */
const warnMessages$1 = {
[CoreWarnCodes.NOT_FOUND_KEY]: `Not found '{key}' key in '{locale}' locale messages.`,
[CoreWarnCodes.FALLBACK_TO_TRANSLATE]: `Fall back to translate '{key}' key with '{target}' locale.`,
[CoreWarnCodes.CANNOT_FORMAT_NUMBER]: `Cannot format a number value due to not supported Intl.NumberFormat.`,
[CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT]: `Fall back to number format '{key}' key with '{target}' locale.`,
[CoreWarnCodes.CANNOT_FORMAT_DATE]: `Cannot format a date value due to not supported Intl.DateTimeFormat.`,
[CoreWarnCodes.FALLBACK_TO_DATE_FORMAT]: `Fall back to datetime format '{key}' key with '{target}' locale.`
};
function getWarnMessage$1(code, ...args) {
return format(warnMessages$1[code], ...args);
}
/**
* Fallback with simple implemenation
*
* @remarks
* A fallback locale function implemented with a simple fallback algorithm.
*
* Basically, it returns the value as specified in the `fallbackLocale` props, and is processed with the fallback inside intlify.
*
* @param ctx - A {@link CoreContext | context}
* @param fallback - A {@link FallbackLocale | fallback locale}
* @param start - A starting {@link Locale | locale}
*
* @returns Fallback locales
*
* @VueI18nGeneral
*/
function fallbackWithSimple(ctx, fallback, start // eslint-disable-line @typescript-eslint/no-unused-vars
) {
// prettier-ignore
return [...new Set([
start,
...(isArray(fallback)
? fallback
: isObject(fallback)
? Object.keys(fallback)
: isString(fallback)
? [fallback]
: [start])
])];
}
/**
* Fallback with locale chain
*
* @remarks
* A fallback locale function implemented with a fallback chain algorithm. It's used in VueI18n as default.
*
* @param ctx - A {@link CoreContext | context}
* @param fallback - A {@link FallbackLocale | fallback locale}
* @param start - A starting {@link Locale | locale}
*
* @returns Fallback locales
*
* @VueI18nSee [Fallbacking](../guide/essentials/fallback)
*
* @VueI18nGeneral
*/
function fallbackWithLocaleChain(ctx, fallback, start) {
const startLocale = isString(start) ? start : DEFAULT_LOCALE;
const context = ctx;
if (!context.__localeChainCache) {
context.__localeChainCache = new Map();
}
let chain = context.__localeChainCache.get(startLocale);
if (!chain) {
chain = [];
// first block defined by start
let block = [start];
// while any intervening block found
while (isArray(block)) {
block = appendBlockToChain(chain, block, fallback);
}
// prettier-ignore
// last block defined by default
const defaults = isArray(fallback) || !isPlainObject(fallback)
? fallback
: fallback['default']
? fallback['default']
: null;
// convert defaults to array
block = isString(defaults) ? [defaults] : defaults;
if (isArray(block)) {
appendBlockToChain(chain, block, false);
}
context.__localeChainCache.set(startLocale, chain);
}
return chain;
}
function appendBlockToChain(chain, block, blocks) {
let follow = true;
for (let i = 0; i < block.length && isBoolean(follow); i++) {
const locale = block[i];
if (isString(locale)) {
follow = appendLocaleToChain(chain, block[i], blocks);
}
}
return follow;
}
function appendLocaleToChain(chain, locale, blocks) {
let follow;
const tokens = locale.split('-');
do {
const target = tokens.join('-');
follow = appendItemToChain(chain, target, blocks);
tokens.splice(-1, 1);
} while (tokens.length && follow === true);
return follow;
}
function appendItemToChain(chain, target, blocks) {
let follow = false;
if (!chain.includes(target)) {
follow = true;
if (target) {
follow = target[target.length - 1] !== '!';
const locale = target.replace(/!/g, '');
chain.push(locale);
if ((isArray(blocks) || isPlainObject(blocks)) &&
blocks[locale] // eslint-disable-line @typescript-eslint/no-explicit-any
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
follow = blocks[locale];
}
}
}
return follow;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Intlify core-base version
* @internal
*/
const VERSION$1 = '9.2.0-beta.6';
const NOT_REOSLVED = -1;
const DEFAULT_LOCALE = 'en-US';
const MISSING_RESOLVE_VALUE = '';
function getDefaultLinkedModifiers() {
return {
upper: (val) => (isString(val) ? val.toUpperCase() : val),
lower: (val) => (isString(val) ? val.toLowerCase() : val),
// prettier-ignore
capitalize: (val) => (isString(val)
? `${val.charAt(0).toLocaleUpperCase()}${val.substr(1)}`
: val)
};
}
let _compiler;
let _resolver;
/**
* Register the message resolver
*
* @param resolver - A {@link MessageResolver} function
*
* @VueI18nGeneral
*/
function registerMessageResolver(resolver) {
_resolver = resolver;
}
let _fallbacker;
/**
* Register the locale fallbacker
*
* @param fallbacker - A {@link LocaleFallbacker} function
*
* @VueI18nGeneral
*/
function registerLocaleFallbacker(fallbacker) {
_fallbacker = fallbacker;
}
// Additional Meta for Intlify DevTools
let _additionalMeta = null;
const setAdditionalMeta = (meta) => {
_additionalMeta = meta;
};
const getAdditionalMeta = () => _additionalMeta;
// ID for CoreContext
let _cid = 0;
function createCoreContext(options = {}) {
// setup options
const version = isString(options.version) ? options.version : VERSION$1;
const locale = isString(options.locale) ? options.locale : DEFAULT_LOCALE;
const fallbackLocale = isArray(options.fallbackLocale) ||
isPlainObject(options.fallbackLocale) ||
isString(options.fallbackLocale) ||
options.fallbackLocale === false
? options.fallbackLocale
: locale;
const messages = isPlainObject(options.messages)
? options.messages
: { [locale]: {} };
const datetimeFormats = isPlainObject(options.datetimeFormats)
? options.datetimeFormats
: { [locale]: {} }
;
const numberFormats = isPlainObject(options.numberFormats)
? options.numberFormats
: { [locale]: {} }
;
const modifiers = assign({}, options.modifiers || {}, getDefaultLinkedModifiers());
const pluralRules = options.pluralRules || {};
const missing = isFunction(options.missing) ? options.missing : null;
const missingWarn = isBoolean(options.missingWarn) || isRegExp(options.missingWarn)
? options.missingWarn
: true;
const fallbackWarn = isBoolean(options.fallbackWarn) || isRegExp(options.fallbackWarn)
? options.fallbackWarn
: true;
const fallbackFormat = !!options.fallbackFormat;
const unresolving = !!options.unresolving;
const postTranslation = isFunction(options.postTranslation)
? options.postTranslation
: null;
const processor = isPlainObject(options.processor) ? options.processor : null;
const warnHtmlMessage = isBoolean(options.warnHtmlMessage)
? options.warnHtmlMessage
: true;
const escapeParameter = !!options.escapeParameter;
const messageCompiler = isFunction(options.messageCompiler)
? options.messageCompiler
: _compiler;
const messageResolver = isFunction(options.messageResolver)
? options.messageResolver
: _resolver || resolveWithKeyValue;
const localeFallbacker = isFunction(options.localeFallbacker)
? options.localeFallbacker
: _fallbacker || fallbackWithSimple;
const onWarn = isFunction(options.onWarn) ? options.onWarn : warn;
// setup internal options
const internalOptions = options;
const __datetimeFormatters = isObject(internalOptions.__datetimeFormatters)
? internalOptions.__datetimeFormatters
: new Map()
;
const __numberFormatters = isObject(internalOptions.__numberFormatters)
? internalOptions.__numberFormatters
: new Map()
;
const __meta = isObject(internalOptions.__meta) ? internalOptions.__meta : {};
_cid++;
const context = {
version,
cid: _cid,
locale,
fallbackLocale,
messages,
modifiers,
pluralRules,
missing,
missingWarn,
fallbackWarn,
fallbackFormat,
unresolving,
postTranslation,
processor,
warnHtmlMessage,
escapeParameter,
messageCompiler,
messageResolver,
localeFallbacker,
onWarn,
__meta
};
{
context.datetimeFormats = datetimeFormats;
context.numberFormats = numberFormats;
context.__datetimeFormatters = __datetimeFormatters;
context.__numberFormatters = __numberFormatters;
}
// NOTE: experimental !!
{
initI18nDevTools(context, version, __meta);
}
return context;
}
/** @internal */
function isTranslateFallbackWarn(fallback, key) {
return fallback instanceof RegExp ? fallback.test(key) : fallback;
}
/** @internal */
function isTranslateMissingWarn(missing, key) {
return missing instanceof RegExp ? missing.test(key) : missing;
}
/** @internal */
function handleMissing(context, key, locale, missingWarn, type) {
const { missing, onWarn } = context;
if (missing !== null) {
const ret = missing(context, locale, key, type);
return isString(ret) ? ret : key;
}
else {
if (isTranslateMissingWarn(missingWarn, key)) {
onWarn(getWarnMessage$1(CoreWarnCodes.NOT_FOUND_KEY, { key, locale }));
}
return key;
}
}
/** @internal */
function updateFallbackLocale(ctx, locale, fallback) {
const context = ctx;
context.__localeChainCache = new Map();
ctx.localeFallbacker(ctx, fallback, locale);
}
/* eslint-enable @typescript-eslint/no-explicit-any */
let code$2 = CompileErrorCodes.__EXTEND_POINT__;
const inc$2 = () => code$2++;
const CoreErrorCodes = {
INVALID_ARGUMENT: code$2,
INVALID_DATE_ARGUMENT: inc$2(),
INVALID_ISO_DATE_ARGUMENT: inc$2(),
__EXTEND_POINT__: inc$2() // 18
};
function createCoreError(code) {
return createCompileError(code, null, { messages: errorMessages$1 } );
}
/** @internal */
const errorMessages$1 = {
[CoreErrorCodes.INVALID_ARGUMENT]: 'Invalid arguments',
[CoreErrorCodes.INVALID_DATE_ARGUMENT]: 'The date provided is an invalid Date object.' +
'Make sure your Date represents a valid date.',
[CoreErrorCodes.INVALID_ISO_DATE_ARGUMENT]: 'The argument provided is not a valid ISO date string'
};
const NOOP_MESSAGE_FUNCTION = () => '';
const isMessageFunction = (val) => isFunction(val);
// implementation of `translate` function
function translate(context, ...args) {
const { fallbackFormat, postTranslation, unresolving, fallbackLocale, messages } = context;
const [key, options] = parseTranslateArgs(...args);
const missingWarn = isBoolean(options.missingWarn)
? options.missingWarn
: context.missingWarn;
const fallbackWarn = isBoolean(options.fallbackWarn)
? options.fallbackWarn
: context.fallbackWarn;
const escapeParameter = isBoolean(options.escapeParameter)
? options.escapeParameter
: context.escapeParameter;
const resolvedMessage = !!options.resolvedMessage;
// prettier-ignore
const defaultMsgOrKey = isString(options.default) || isBoolean(options.default) // default by function option
? !isBoolean(options.default)
? options.default
: key
: fallbackFormat // default by `fallbackFormat` option
? key
: '';
const enableDefaultMsg = fallbackFormat || defaultMsgOrKey !== '';
const locale = isString(options.locale) ? options.locale : context.locale;
// escape params
escapeParameter && escapeParams(options);
// resolve message format
// eslint-disable-next-line prefer-const
let [format, targetLocale, message] = !resolvedMessage
? resolveMessageFormat(context, key, locale, fallbackLocale, fallbackWarn, missingWarn)
: [
key,
locale,
messages[locale] || {}
];
// if you use default message, set it as message format!
let cacheBaseKey = key;
if (!resolvedMessage &&
!(isString(format) || isMessageFunction(format))) {
if (enableDefaultMsg) {
format = defaultMsgOrKey;
cacheBaseKey = format;
}
}
// checking message format and target locale
if (!resolvedMessage &&
(!(isString(format) || isMessageFunction(format)) ||
!isString(targetLocale))) {
return unresolving ? NOT_REOSLVED : key;
}
if (isString(format) && context.messageCompiler == null) {
warn(`The message format compilation is not supported in this build. ` +
`Because message compiler isn't included. ` +
`You need to pre-compilation all message format. ` +
`So translate function return '${key}'.`);
return key;
}
// setup compile error detecting
let occurred = false;
const errorDetector = () => {
occurred = true;
};
// compile message format
const msg = !isMessageFunction(format)
? compileMessageFormat(context, key, targetLocale, format, cacheBaseKey, errorDetector)
: format;
// if occurred compile error, return the message format
if (occurred) {
return format;
}
// evaluate message with context
const ctxOptions = getMessageContextOptions(context, targetLocale, message, options);
const msgContext = createMessageContext(ctxOptions);
const messaged = evaluateMessage(context, msg, msgContext);
// if use post translation option, proceed it with handler
const ret = postTranslation ? postTranslation(messaged) : messaged;
// NOTE: experimental !!
{
// prettier-ignore
const payloads = {
timestamp: Date.now(),
key: isString(key)
? key
: isMessageFunction(format)
? format.key
: '',
locale: targetLocale || (isMessageFunction(format)
? format.locale
: ''),
format: isString(format)
? format
: isMessageFunction(format)
? format.source
: '',
message: ret
};
payloads.meta = assign({}, context.__meta, getAdditionalMeta() || {});
translateDevTools(payloads);
}
return ret;
}
function escapeParams(options) {
if (isArray(options.list)) {
options.list = options.list.map(item => isString(item) ? escapeHtml(item) : item);
}
else if (isObject(options.named)) {
Object.keys(options.named).forEach(key => {
if (isString(options.named[key])) {
options.named[key] = escapeHtml(options.named[key]);
}
});
}
}
function resolveMessageFormat(context, key, locale, fallbackLocale, fallbackWarn, missingWarn) {
const { messages, onWarn, messageResolver: resolveValue, localeFallbacker } = context;
const locales = localeFallbacker(context, fallbackLocale, locale); // eslint-disable-line @typescript-eslint/no-explicit-any
let message = {};
let targetLocale;
let format = null;
const type = 'translate';
for (let i = 0; i < locales.length; i++) {
targetLocale = locales[i];
if (locale !== targetLocale &&
isTranslateFallbackWarn(fallbackWarn, key)) {
onWarn(getWarnMessage$1(CoreWarnCodes.FALLBACK_TO_TRANSLATE, {
key,
target: targetLocale
}));
}
message =
messages[targetLocale] || {};
let startTag;
if (inBrowser) {
window.performance.now();
startTag = 'intlify-message-resolve-start';
mark && mark(startTag);
}
if ((format = resolveValue(message, key)) === null) {
// if null, resolve with object key path
format = message[key]; // eslint-disable-line @typescript-eslint/no-explicit-any
}
if (isString(format) || isFunction(format))
break;
const missingRet = handleMissing(context, // eslint-disable-line @typescript-eslint/no-explicit-any
key, targetLocale, missingWarn, type);
if (missingRet !== key) {
format = missingRet;
}
}
return [format, targetLocale, message];
}
function compileMessageFormat(context, key, targetLocale, format, cacheBaseKey, errorDetector) {
const { messageCompiler, warnHtmlMessage } = context;
if (isMessageFunction(format)) {
const msg = format;
msg.locale = msg.locale || targetLocale;
msg.key = msg.key || key;
return msg;
}
let startTag;
if (inBrowser) {
window.performance.now();
startTag = 'intlify-message-compilation-start';
mark && mark(startTag);
}
const msg = messageCompiler(format, getCompileOptions(context, targetLocale, cacheBaseKey, format, warnHtmlMessage, errorDetector));
msg.locale = targetLocale;
msg.key = key;
msg.source = format;
return msg;
}
function evaluateMessage(context, msg, msgCtx) {
let startTag;
if (inBrowser) {
window.performance.now();
startTag = 'intlify-message-evaluation-start';
mark && mark(startTag);
}
const messaged = msg(msgCtx);
return messaged;
}
/** @internal */
function parseTranslateArgs(...args) {
const [arg1, arg2, arg3] = args;
const options = {};
if (!isString(arg1) && !isNumber(arg1) && !isMessageFunction(arg1)) {
throw createCoreError(CoreErrorCodes.INVALID_ARGUMENT);
}
// prettier-ignore
const key = isNumber(arg1)
? String(arg1)
: isMessageFunction(arg1)
? arg1
: arg1;
if (isNumber(arg2)) {
options.plural = arg2;
}
else if (isString(arg2)) {
options.default = arg2;
}
else if (isPlainObject(arg2) && !isEmptyObject(arg2)) {
options.named = arg2;
}
else if (isArray(arg2)) {
options.list = arg2;
}
if (isNumber(arg3)) {
options.plural = arg3;
}
else if (isString(arg3)) {
options.default = arg3;
}
else if (isPlainObject(arg3)) {
assign(options, arg3);
}
return [key, options];
}
function getCompileOptions(context, locale, key, source, warnHtmlMessage, errorDetector) {
return {
warnHtmlMessage,
onError: (err) => {
errorDetector && errorDetector(err);
{
throw err;
}
},
onCacheKey: (source) => generateFormatCacheKey(locale, key, source)
};
}
function getMessageContextOptions(context, locale, message, options) {
const { modifiers, pluralRules, messageResolver: resolveValue } = context;
const resolveMessage = (key) => {
const val = resolveValue(message, key);
if (isString(val)) {
let occurred = false;
const errorDetector = () => {
occurred = true;
};
const msg = compileMessageFormat(context, key, locale, val, key, errorDetector);
return !occurred
? msg
: NOOP_MESSAGE_FUNCTION;
}
else if (isMessageFunction(val)) {
return val;
}
else {
// TODO: should be implemented warning message
return NOOP_MESSAGE_FUNCTION;
}
};
const ctxOptions = {
locale,
modifiers,
pluralRules,
messages: resolveMessage
};
if (context.processor) {
ctxOptions.processor = context.processor;
}
if (options.list) {
ctxOptions.list = options.list;
}
if (options.named) {
ctxOptions.named = options.named;
}
if (isNumber(options.plural)) {
ctxOptions.pluralIndex = options.plural;
}
return ctxOptions;
}
const intlDefined = typeof Intl !== 'undefined';
const Availabilities = {
dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
};
// implementation of `datetime` function
function datetime(context, ...args) {
const { datetimeFormats, unresolving, fallbackLocale, onWarn, localeFallbacker } = context;
const { __datetimeFormatters } = context;
if (!Availabilities.dateTimeFormat) {
onWarn(getWarnMessage$1(CoreWarnCodes.CANNOT_FORMAT_DATE));
return MISSING_RESOLVE_VALUE;
}
const [key, value, options, overrides] = parseDateTimeArgs(...args);
const missingWarn = isBoolean(options.missingWarn)
? options.missingWarn
: context.missingWarn;
const fallbackWarn = isBoolean(options.fallbackWarn)
? options.fallbackWarn
: context.fallbackWarn;
const part = !!options.part;
const locale = isString(options.locale) ? options.locale : context.locale;
const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
fallbackLocale, locale);
if (!isString(key) || key === '') {
return new Intl.DateTimeFormat(locale).format(value);
}
// resolve format
let datetimeFormat = {};
let targetLocale;
let format = null;
const type = 'datetime format';
for (let i = 0; i < locales.length; i++) {
targetLocale = locales[i];
if (locale !== targetLocale &&
isTranslateFallbackWarn(fallbackWarn, key)) {
onWarn(getWarnMessage$1(CoreWarnCodes.FALLBACK_TO_DATE_FORMAT, {
key,
target: targetLocale
}));
}
datetimeFormat =
datetimeFormats[targetLocale] || {};
format = datetimeFormat[key];
if (isPlainObject(format))
break;
handleMissing(context, key, targetLocale, missingWarn, type); // eslint-disable-line @typescript-eslint/no-explicit-any
}
// checking format and target locale
if (!isPlainObject(format) || !isString(targetLocale)) {
return unresolving ? NOT_REOSLVED : key;
}
let id = `${targetLocale}__${key}`;
if (!isEmptyObject(overrides)) {
id = `${id}__${JSON.stringify(overrides)}`;
}
let formatter = __datetimeFormatters.get(id);
if (!formatter) {
formatter = new Intl.DateTimeFormat(targetLocale, assign({}, format, overrides));
__datetimeFormatters.set(id, formatter);
}
return !part ? formatter.format(value) : formatter.formatToParts(value);
}
/** @internal */
function parseDateTimeArgs(...args) {
const [arg1, arg2, arg3, arg4] = args;
let options = {};
let overrides = {};
let value;
if (isString(arg1)) {
// Only allow ISO strings - other date formats are often supported,
// but may cause different results in different browsers.
const matches = arg1.match(/(\d{4}-\d{2}-\d{2})(T|\s)?(.*)/);
if (!matches) {
throw createCoreError(CoreErrorCodes.INVALID_ISO_DATE_ARGUMENT);
}
// Some browsers can not parse the iso datetime separated by space,
// this is a compromise solution by replace the 'T'/' ' with 'T'
const dateTime = matches[3]
? matches[3].trim().startsWith('T')
? `${matches[1].trim()}${matches[3].trim()}`
: `${matches[1].trim()}T${matches[3].trim()}`
: matches[1].trim();
value = new Date(dateTime);
try {
// This will fail if the date is not valid
value.toISOString();
}
catch (e) {
throw createCoreError(CoreErrorCodes.INVALID_ISO_DATE_ARGUMENT);
}
}
else if (isDate(arg1)) {
if (isNaN(arg1.getTime())) {
throw createCoreError(CoreErrorCodes.INVALID_DATE_ARGUMENT);
}
value = arg1;
}
else if (isNumber(arg1)) {
value = arg1;
}
else {
throw createCoreError(CoreErrorCodes.INVALID_ARGUMENT);
}
if (isString(arg2)) {
options.key = arg2;
}
else if (isPlainObject(arg2)) {
options = arg2;
}
if (isString(arg3)) {
options.locale = arg3;
}
else if (isPlainObject(arg3)) {
overrides = arg3;
}
if (isPlainObject(arg4)) {
overrides = arg4;
}
return [options.key || '', value, options, overrides];
}
/** @internal */
function clearDateTimeFormat(ctx, locale, format) {
const context = ctx;
for (const key in format) {
const id = `${locale}__${key}`;
if (!context.__datetimeFormatters.has(id)) {
continue;
}
context.__datetimeFormatters.delete(id);
}
}
// implementation of `number` function
function number(context, ...args) {
const { numberFormats, unresolving, fallbackLocale, onWarn, localeFallbacker } = context;
const { __numberFormatters } = context;
if (!Availabilities.numberFormat) {
onWarn(getWarnMessage$1(CoreWarnCodes.CANNOT_FORMAT_NUMBER));
return MISSING_RESOLVE_VALUE;
}
const [key, value, options, overrides] = parseNumberArgs(...args);
const missingWarn = isBoolean(options.missingWarn)
? options.missingWarn
: context.missingWarn;
const fallbackWarn = isBoolean(options.fallbackWarn)
? options.fallbackWarn
: context.fallbackWarn;
const part = !!options.part;
const locale = isString(options.locale) ? options.locale : context.locale;
const locales = localeFallbacker(context, // eslint-disable-line @typescript-eslint/no-explicit-any
fallbackLocale, locale);
if (!isString(key) || key === '') {
return new Intl.NumberFormat(locale).format(value);
}
// resolve format
let numberFormat = {};
let targetLocale;
let format = null;
const type = 'number format';
for (let i = 0; i < locales.length; i++) {
targetLocale = locales[i];
if (locale !== targetLocale &&
isTranslateFallbackWarn(fallbackWarn, key)) {
onWarn(getWarnMessage$1(CoreWarnCodes.FALLBACK_TO_NUMBER_FORMAT, {
key,
target: targetLocale
}));
}
numberFormat =
numberFormats[targetLocale] || {};
format = numberFormat[key];
if (isPlainObject(format))
break;
handleMissing(context, key, targetLocale, missingWarn, type); // eslint-disable-line @typescript-eslint/no-explicit-any
}
// checking format and target locale
if (!isPlainObject(format) || !isString(targetLocale)) {
return unresolving ? NOT_REOSLVED : key;
}
let id = `${targetLocale}__${key}`;
if (!isEmptyObject(overrides)) {
id = `${id}__${JSON.stringify(overrides)}`;
}
let formatter = __numberFormatters.get(id);
if (!formatter) {
formatter = new Intl.NumberFormat(targetLocale, assign({}, format, overrides));
__numberFormatters.set(id, formatter);
}
return !part ? formatter.format(value) : formatter.formatToParts(value);
}
/** @internal */
function parseNumberArgs(...args) {
const [arg1, arg2, arg3, arg4] = args;
let options = {};
let overrides = {};
if (!isNumber(arg1)) {
throw createCoreError(CoreErrorCodes.INVALID_ARGUMENT);
}
const value = arg1;
if (isString(arg2)) {
options.key = arg2;
}
else if (isPlainObject(arg2)) {
options = arg2;
}
if (isString(arg3)) {
options.locale = arg3;
}
else if (isPlainObject(arg3)) {
overrides = arg3;
}
if (isPlainObject(arg4)) {
overrides = arg4;
}
return [options.key || '', value, options, overrides];
}
/** @internal */
function clearNumberFormat(ctx, locale, format) {
const context = ctx;
for (const key in format) {
const id = `${locale}__${key}`;
if (!context.__numberFormatters.has(id)) {
continue;
}
context.__numberFormatters.delete(id);
}
}
/**
* Vue I18n Version
*
* @remarks
* Semver format. Same format as the package.json `version` field.
*
* @VueI18nGeneral
*/
const VERSION = '9.2.0-beta.6';
/**
* This is only called development env
* istanbul-ignore-next
*/
function initDev() {
{
{
console.info(`You are running a development build of vue-i18n.\n` +
`Make sure to use the production build (*.prod.js) when deploying for production.`);
}
}
}
let code$1 = CoreWarnCodes.__EXTEND_POINT__;
const inc$1 = () => code$1++;
const I18nWarnCodes = {
FALLBACK_TO_ROOT: code$1,
NOT_SUPPORTED_PRESERVE: inc$1(),
NOT_SUPPORTED_FORMATTER: inc$1(),
NOT_SUPPORTED_PRESERVE_DIRECTIVE: inc$1(),
NOT_SUPPORTED_GET_CHOICE_INDEX: inc$1(),
COMPONENT_NAME_LEGACY_COMPATIBLE: inc$1(),
NOT_FOUND_PARENT_SCOPE: inc$1(),
NOT_SUPPORT_MULTI_I18N_INSTANCE: inc$1() // 14
};
const warnMessages = {
[I18nWarnCodes.FALLBACK_TO_ROOT]: `Fall back to {type} '{key}' with root locale.`,
[I18nWarnCodes.NOT_SUPPORTED_PRESERVE]: `Not supported 'preserve'.`,
[I18nWarnCodes.NOT_SUPPORTED_FORMATTER]: `Not supported 'formatter'.`,
[I18nWarnCodes.NOT_SUPPORTED_PRESERVE_DIRECTIVE]: `Not supported 'preserveDirectiveContent'.`,
[I18n