UNPKG

@ices/react-locale

Version:
642 lines (629 loc) 25.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var tslib = require('tslib'); var React = require('react'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespace(React); function escapeRegExpCharacters(str) { return str.replace(/[|/\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d'); } function getLocaleFromURL(queryName) { if (queryName === void 0) { queryName = 'lang'; } var escapedName = escapeRegExpCharacters(queryName); var query = new RegExp("\\b".concat(escapedName, "=([^&#]+)")).exec(location.search); return query ? decodeURIComponent(query[1]) : ''; } function getLocaleFromCookie(key) { if (key === void 0) { key = 'lang'; } var escapedKey = escapeRegExpCharacters(key); var regx = new RegExp("^".concat(escapedKey, "=(.+)")); var cookie; for (var _i = 0, _a = document.cookie.split(/;\s*/); _i < _a.length; _i++) { var item = _a[_i]; if ((cookie = regx.exec(item))) { return decodeURIComponent(cookie[1]); } } return ''; } function getLocaleFromLocalStorage(key) { if (key === void 0) { key = 'lang'; } if (window.localStorage) { return localStorage.getItem(key); } return ''; } function getLocaleFromBrowser() { return navigator.language || navigator.userLanguage || ''; } function determineLocale(options) { var defaultKey = 'lang'; var _a = Object.assign({}, options), _b = _a.urlLocaleKey, urlLocaleKey = _b === void 0 ? defaultKey : _b, _c = _a.cookieLocaleKey, cookieLocaleKey = _c === void 0 ? defaultKey : _c, _d = _a.storageLocaleKey, storageLocaleKey = _d === void 0 ? defaultKey : _d, fallbackLocale = _a.fallbackLocale; for (var _i = 0, _e = [ function () { return getLocaleFromURL(urlLocaleKey); }, function () { return getLocaleFromCookie(cookieLocaleKey); }, function () { return getLocaleFromLocalStorage(storageLocaleKey); }, function () { return getLocaleFromBrowser(); }, ]; _i < _e.length; _i++) { var getLocale = _e[_i]; var locale = normalizeLocale(getLocale())[0]; if (locale) { return locale; } } if (fallbackLocale) { var fallback = normalizeLocale(fallbackLocale)[0]; if (fallback) { return fallback; } } return ''; } function normalizeLocale(locale) { if (typeof locale !== 'string') { locale = ''; } var langArea = locale.split('.')[0]; var _a = langArea.split(/[-_]/), lang = _a[0], _b = _a[1], area = _b === void 0 ? '' : _b; var lowerLang = lang.toLowerCase(); var upperArea = area.toUpperCase(); return ["".concat(lowerLang).concat(area ? '-' + upperArea : ''), lowerLang, upperArea]; } function isObject(obj) { return obj !== null && typeof obj === 'object'; } function hasOwnProp(obj, prop) { return Object.hasOwn ? Object.hasOwn(obj, prop) : Object.getOwnPropertyNames(obj).includes(prop); } function hasOwnProperty(obj, prop) { return !(obj === null || obj === undefined) && hasOwnProp(obj, prop); } var fallbackLocale = 'zh'; var currentLocale = determineLocale({ fallbackLocale: fallbackLocale }); var isUpdating = false; var unregisterProp = '__localeChangeUnregister'; var listenerId = 1; var localeChangeListeners = Object.create(null); var loadErrorListeners = Object.create(null); var loadFinishListeners = Object.create(null); var loadStartListeners = Object.create(null); var localeLoadStatus = Object.create(null); var debugMessageFilter = { warning: !window.__suspendReactLocaleWarning, error: !window.__suspendReactLocaleError, emptyKeyError: !window.__suspendReactLocaleEmptyKeyError, }; function setDebugMessageFilter(filter) { Object.assign(debugMessageFilter, filter); } function getFallbackLocale() { return fallbackLocale; } function getLocale() { return currentLocale; } function validateLocale(locale, isFallback, original) { if (original === void 0) { original = locale; } if (!locale || typeof locale !== 'string') { throw new Error("".concat(isFallback ? 'Fallback locale' : 'Locale', " code must be a valid string value. (currType: ").concat(typeof original, " , currValue: ").concat(JSON.stringify(original), ")")); } } function setFallbackLocale(locale) { if (locale !== fallbackLocale) { var localeCode = normalizeLocale(locale)[0]; validateLocale(localeCode, true, locale); fallbackLocale = localeCode; } } function getLocaleLoadKey(locale, fallback) { return "".concat(locale, "$$").concat(fallback); } function setLocale(locale) { if (isUpdating || locale === currentLocale) { return; } var localeCode = normalizeLocale(locale)[0]; validateLocale(localeCode, false, locale); isUpdating = true; currentLocale = localeCode; localeLoadStatus[getLocaleLoadKey(currentLocale, fallbackLocale)] = 'prepared'; for (var _i = 0, _a = Object.values(localeChangeListeners); _i < _a.length; _i++) { var handle = _a[_i]; try { handle(currentLocale); } catch (e) { console.error(e); } } isUpdating = false; } function registerListener(listeners, handle) { if (typeof handle !== 'function') { throw new Error('Handle is not a function'); } var unregister = handle[unregisterProp]; if (!unregister) { var id_1 = listenerId++; unregister = function () { if (id_1) { listeners[id_1][unregisterProp] = null; delete listeners[id_1]; id_1 = 0; } }; handle[unregisterProp] = unregister; listeners[id_1] = handle; } return unregister; } function subscribe(handler) { return registerListener(localeChangeListeners, handler); } function addLoadErrorListener(listener) { return registerListener(loadErrorListeners, listener); } function addLoadStartListener(listener) { return registerListener(loadStartListeners, listener); } function addLoadFinishListener(listener) { return registerListener(loadFinishListeners, listener); } function emitLoadEvent(listeners, status, locale, fallback, error) { var key = getLocaleLoadKey(locale, fallback); if (localeLoadStatus[key] === status) { return; } localeLoadStatus[key] = status; for (var _i = 0, _a = Object.values(listeners); _i < _a.length; _i++) { var handle = _a[_i]; try { handle(locale, fallback, error); } catch (e) { console.error(e); } } } function emitLoadError(locale, fallback, error) { if (localeLoadStatus[getLocaleLoadKey(locale, fallback)] === 'finished') { emitLoadEvent(loadErrorListeners, 'failed', locale, fallback, error); } } function emitLoadStart(locale, fallback) { if (localeLoadStatus[getLocaleLoadKey(locale, fallback)] === 'prepared') { emitLoadEvent(loadStartListeners, 'loading', locale, fallback); } } function emitLoadFinish(locale, fallback, error) { if (localeLoadStatus[getLocaleLoadKey(locale, fallback)] === 'loading') { emitLoadEvent(loadFinishListeners, 'finished', locale, fallback, error); } } function fetchLocaleData(locale, fallback, fetch) { emitLoadStart(locale, fallback); return Promise.all([fetch(locale), fetch(fallback)]).then(function (res) { emitLoadFinish(locale, fallback); return Object.assign({}, res[1], res[0]); }, function (err) { emitLoadFinish(locale, fallback, err); emitLoadError(locale, fallback, err); throw err; }); } var LocaleContext = React__namespace.createContext(currentLocale); var LocaleProvider = function LocaleProvider(_a) { var value = _a.value, props = tslib.__rest(_a, ["value"]); var _b = React.useState(value), state = _b[0], setState = _b[1]; React.useEffect(function () { return subscribe(setState); }, []); React.useEffect(function () { return setLocale(value); }, [value]); return React__namespace.createElement(LocaleContext.Provider, tslib.__assign({}, props, { value: state })); }; var placeholder = function (message, args) { if (!isObject(args[0]) || typeof message !== 'string') { return message; } var data = args[0]; return message.replace(/(.?){\s*(.*?)\s*(\\|)}/g, function (sub, g1, g2, g3) { if (g1 === '\\' || g3 === '\\') { return g1 === '\\' ? sub.substring(1) : sub; } if (!g2 || !hasOwnProperty(data, g2)) { return sub; } var str = data[g2]; return "".concat(g1).concat(str !== undefined ? str : ''); }); }; function toMessageValue(val) { return typeof val === 'number' ? val : "".concat(val); } function normalizeDefinitions(dataSet) { var definitions = {}; for (var _i = 0, _a = Object.entries(Object.assign({}, dataSet)); _i < _a.length; _i++) { var entry = _a[_i]; var _b = entry, locale = _b[0], data = _b[1]; if (data === null || typeof data !== 'object') { continue; } var localeCode = normalizeLocale(locale)[0]; if (!hasOwnProperty(definitions, localeCode)) { definitions[localeCode] = data; } } return definitions; } function filterMessage(key, dataList, preference) { for (var _i = 0, dataList_1 = dataList; _i < dataList_1.length; _i++) { var _a = dataList_1[_i], locale = _a.locale, data = _a.data; if (hasOwnProperty(data, key)) { var message = data[key]; if (locale !== preference && debugMessageFilter.warning) { console.warn("Missing message with key of \"".concat(key, "\" for locale ").concat(preference, ", using default message of locale ").concat(locale, " as fallback.")); } return { locale: locale, message: message }; } } return { locale: preference, message: '' }; } function getPluginTranslate(locale, fallback) { return function (key, definitions, options) { var _a; var plugins; var pluginArgs; var pluginFallback; if (typeof options === 'string') { pluginFallback = options; } else { (_a = Object.assign({}, options), plugins = _a.plugins, pluginArgs = _a.pluginArgs, pluginFallback = _a.fallback); } return withDefinitions(definitions, { locale: locale, plugins: plugins, fallback: pluginFallback || fallback, })(key, pluginArgs); }; } function printErrorMessage(key, locale) { if (debugMessageFilter.error) { if (key || debugMessageFilter.emptyKeyError) { console.error("Unknown localized message with key of \"".concat(key, "\" for [").concat(locale, "]")); return "<key>".concat(key, "</key>"); } } return ''; } function getLocaleMessage(key, pluginArgs, context, silent) { var locale = context.locale, fallback = context.fallback, plugins = context.plugins, dataList = context.dataList; if (!key) { return printErrorMessage(key, locale); } var localizedMessage = filterMessage(key, dataList, locale); var messageLocale = localizedMessage.locale, messageDataValue = localizedMessage.message; var translate = getPluginTranslate(messageLocale, fallback); var value = plugins.reduce(function (message, plugin) { return plugin(message, tslib.__spreadArray([], pluginArgs, true), translate, key); }, toMessageValue(messageDataValue)); if (!silent && (value === '' || value === undefined)) { return printErrorMessage(key, locale); } return "".concat(value); } function getTranslateContext(data, context) { var definitions = normalizeDefinitions(data); var plugins = context.plugins, locale = context.locale, fallback = context.fallback; var usedPlugins = Array.isArray(plugins) ? plugins : [plugins]; usedPlugins = usedPlugins.filter(function (plugin) { return typeof plugin === 'function'; }); var defaultPlugIndex = usedPlugins.indexOf(placeholder); if (defaultPlugIndex !== -1) { usedPlugins.splice(defaultPlugIndex, 1); } usedPlugins.push(placeholder); var fallbackLang = fallback || getFallbackLocale(); var _a = normalizeLocale(locale), preference = _a[0], prefLang = _a[1]; var _b = normalizeLocale(fallbackLang), backLangArea = _b[0], backLang = _b[1]; var dataList = [preference, prefLang, backLangArea, backLang].reduce(function (list, loc) { if (!list.some(function (_a) { var locale = _a.locale; return locale === loc; })) { list.push({ locale: loc, data: definitions[loc] }); } return list; }, []); return { locale: locale, fallback: fallbackLang, dataList: dataList, plugins: usedPlugins, }; } function withDefinitions(data, context, fallbackTranslate) { if (data === null) { if (fallbackTranslate) { fallbackTranslate.__toBeInvalidTranslateFunction = true; } return fallbackTranslate || (function () { return ''; }); } var translateContext = getTranslateContext(data, context); var translate = function (key) { var pluginArgs = []; for (var _i = 1; _i < arguments.length; _i++) { pluginArgs[_i - 1] = arguments[_i]; } return getLocaleMessage(key, pluginArgs, translateContext, translate.__toBeInvalidTranslateFunction); }; translate.__boundLocale = translateContext.locale; translate.__fallbackLocale = translateContext.fallback; return translate; } function getUpdatedPlugins(prevPlugins, pluginsProp) { var nextPlugins; if (!pluginsProp) { nextPlugins = []; } else if (typeof pluginsProp === 'function') { nextPlugins = [pluginsProp]; } else { nextPlugins = tslib.__spreadArray([], pluginsProp, true); } if (prevPlugins.length !== nextPlugins.length) { return nextPlugins; } for (var i = 0; i < prevPlugins.length; i++) { if (prevPlugins[i] !== nextPlugins[i]) { return nextPlugins; } } return prevPlugins; } function useTranslate(context, fallbackTranslate) { var pluginsRef = React.useRef([]); var plugins = getUpdatedPlugins(pluginsRef.current, context.plugins); pluginsRef.current = plugins; var data = context.data, locale = context.locale, fallback = context.fallback; return React.useMemo(function () { return withDefinitions(data, { locale: locale, fallback: fallback, plugins: plugins }, fallbackTranslate); }, [data, locale, fallback, plugins, fallbackTranslate]); } function useLocale(expectedLocale, definitions, plugins, fallback) { var isAsyncResources = typeof definitions === 'function'; var fallbackLocale = React.useMemo(function () { if (fallback) { setFallbackLocale(fallback); } return getFallbackLocale(); }, [fallback]); var _a = React.useState({ resourceLoader: isAsyncResources ? definitions : null, asyncData: null, asyncLocale: expectedLocale, asyncFallback: fallbackLocale, useFallbackTranslate: false, }), state = _a[0], setState = _a[1]; var asyncData = state.asyncData, resourceLoader = state.resourceLoader, asyncLocale = state.asyncLocale, asyncFallback = state.asyncFallback, useFallbackTranslate = state.useFallbackTranslate; if (isAsyncResources) { if (resourceLoader !== definitions || asyncLocale !== expectedLocale || asyncFallback !== fallbackLocale) { useFallbackTranslate = resourceLoader === definitions; asyncData = null; resourceLoader = definitions; asyncLocale = expectedLocale; asyncFallback = fallbackLocale; setState({ resourceLoader: resourceLoader, asyncData: asyncData, asyncLocale: asyncLocale, asyncFallback: asyncFallback, useFallbackTranslate: useFallbackTranslate }); } } else if (resourceLoader || asyncData || useFallbackTranslate) { useFallbackTranslate = false; setState(tslib.__assign(tslib.__assign({}, state), { resourceLoader: null, asyncData: null, useFallbackTranslate: false })); } var fallbackTranslateRef = React.useRef(null); var translate = useTranslate({ data: isAsyncResources ? asyncData : definitions || null, locale: expectedLocale, fallback: fallbackLocale, plugins: plugins, }, useFallbackTranslate ? fallbackTranslateRef.current : null); fallbackTranslateRef.current = translate; React.useEffect(function () { if (typeof definitions !== 'function') { fallbackTranslateRef.current = null; return; } fetchLocaleData(expectedLocale, fallbackLocale, definitions).then(function (data) { setState(function (prevState) { if (prevState.asyncLocale === expectedLocale && prevState.asyncFallback === fallbackLocale && prevState.resourceLoader === definitions) { fallbackTranslateRef.current = null; return tslib.__assign(tslib.__assign({}, prevState), { asyncData: data, useFallbackTranslate: false }); } return prevState; }); }, function () { setState(function (prevState) { if (prevState.asyncLocale === expectedLocale && prevState.asyncFallback === fallbackLocale && prevState.resourceLoader === definitions) { fallbackTranslateRef.current = null; return tslib.__assign(tslib.__assign({}, prevState), { asyncData: null, useFallbackTranslate: false }); } return prevState; }); }); }, [definitions, expectedLocale, fallbackLocale]); return [translate, expectedLocale, setLocale]; } function useContextLocaleTrans(contextType, plugins, fallback, definitions) { var locale = React.useContext(contextType); var localeCode; if (contextType !== LocaleContext) { localeCode = normalizeLocale(locale)[0]; validateLocale(localeCode, false, locale); } else { localeCode = locale; } return useLocale(localeCode, definitions, plugins, fallback); } function useLocaleTrans(plugins, fallback, definitions) { var _a = React.useState(function () { return getLocale(); }), locale = _a[0], setLocale = _a[1]; React.useEffect(function () { return subscribe(setLocale); }, []); return useLocale(locale, definitions, plugins, fallback); } function withDefinitionsHook(definitions) { var init = function (initLocale, initialFallback) { init = function () { }; if (typeof initLocale === 'function') { setLocale(initLocale()); } if (typeof initialFallback === 'string') { setFallbackLocale(initialFallback); } }; function useTrans() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var plugins = args[0]; var initLocale; var initialFallback; var fallback; if (args.length >= 3) { initLocale = typeof args[1] === 'function' ? args[1] : function () { return args[1]; }; initialFallback = args[2]; } else { fallback = args[1]; } init(initLocale, initialFallback); return useLocaleTrans(plugins, fallback, definitions); } return useTrans; } function withDefinitionsContextHook(definitions) { return function useContextTrans(contextType, plugins, fallback) { return useContextLocaleTrans(contextType, plugins, fallback, definitions); }; } function isTHMLText(text) { return !/^<key>.*<\/key>$/.test(text) && /<.*?\/?>/.test(text); } function RenderTextOrHTML(_a) { var tagName = _a.tagName, content = _a.content, enableHTML = _a.enableHTML, forwardedRef = _a.forwardedRef, props = tslib.__rest(_a, ["tagName", "content", "enableHTML", "forwardedRef"]); if (enableHTML && isTHMLText(content)) { return React__namespace.createElement(tagName || 'span', tslib.__assign(tslib.__assign({}, props), { ref: forwardedRef, dangerouslySetInnerHTML: { __html: content, } })); } if (tagName) { return React__namespace.createElement(tagName, tslib.__assign(tslib.__assign({}, props), { ref: forwardedRef }), content); } return content; } function LocaleTrans(props) { var id = props.id, fallback = props.fallback, plugins = props.plugins, data = props.data, definitions = props.definitions, rest = tslib.__rest(props, ["id", "fallback", "plugins", "data", "definitions"]); var translate = useLocaleTrans(plugins, fallback, definitions)[0]; return (React__namespace.createElement(RenderTextOrHTML, tslib.__assign({}, rest, { content: translate(id, data) }))); } function ContextLocaleTrans(props) { var id = props.id, fallback = props.fallback, plugins = props.plugins, data = props.data, definitions = props.definitions, contextType = props.contextType, rest = tslib.__rest(props, ["id", "fallback", "plugins", "data", "definitions", "contextType"]); var translate = useContextLocaleTrans(contextType, plugins, fallback, definitions)[0]; return (React__namespace.createElement(RenderTextOrHTML, tslib.__assign({}, rest, { content: translate(id, data) }))); } function withDefinitionsComponent(definitions) { var _a; return _a = (function (_super) { tslib.__extends(TranslateComponent, _super); function TranslateComponent() { return _super !== null && _super.apply(this, arguments) || this; } TranslateComponent.prototype.render = function () { var contextType = TranslateComponent.contextType, globalEnableHTML = TranslateComponent.enableDangerouslySetInnerHTML; var _a = this.props, _b = _a.enableHTML, enableHTML = _b === void 0 ? globalEnableHTML : _b, rest = tslib.__rest(_a, ["enableHTML"]); return contextType ? (React__namespace.createElement(ContextLocaleTrans, tslib.__assign({}, rest, { enableHTML: enableHTML, contextType: contextType, definitions: definitions }))) : (React__namespace.createElement(LocaleTrans, tslib.__assign({}, rest, { enableHTML: enableHTML, definitions: definitions }))); }; return TranslateComponent; }(React__namespace.PureComponent)), _a.contextType = LocaleContext, _a.enableDangerouslySetInnerHTML = false, _a; } function useTranslator(locales) { var _this = this; var loader = React.useCallback(function (lang) { return tslib.__awaiter(_this, void 0, void 0, function () { var data, resource; var _a, _b; return tslib.__generator(this, function (_c) { switch (_c.label) { case 0: if (typeof locales === 'function') { return [2, locales(lang)]; } if (!(locales && hasOwnProperty(locales, lang))) return [3, 4]; resource = locales[lang]; if (!(typeof resource === 'function')) return [3, 2]; return [4, resource()]; case 1: (_a = (_c.sent()).default, data = _a === void 0 ? {} : _a); return [3, 3]; case 2: data = resource || {}; _c.label = 3; case 3: return [3, 5]; case 4: data = {}; _c.label = 5; case 5: return [2, (_b = {}, _b[lang] = data, _b)]; } }); }); }, [locales]); return React.useMemo(function () { return ({ useTrans: withDefinitionsHook(loader), useContextTrans: withDefinitionsContextHook(loader), Translate: withDefinitionsComponent(loader), }); }, [loader]); } var Trans = withDefinitionsComponent(); var useTrans = withDefinitionsHook(); var useContextTrans = withDefinitionsContextHook(); var definitions = {}; exports.LocaleContext = LocaleContext; exports.LocaleProvider = LocaleProvider; exports.Trans = Trans; exports.addLoadErrorListener = addLoadErrorListener; exports.addLoadFinishListener = addLoadFinishListener; exports.addLoadStartListener = addLoadStartListener; exports.definitions = definitions; exports.determineLocale = determineLocale; exports.getFallbackLocale = getFallbackLocale; exports.getLocale = getLocale; exports.setDebugMessageFilter = setDebugMessageFilter; exports.setFallbackLocale = setFallbackLocale; exports.setLocale = setLocale; exports.subscribe = subscribe; exports.useContextTrans = useContextTrans; exports.useTrans = useTrans; exports.useTranslator = useTranslator; exports.withDefinitionsComponent = withDefinitionsComponent; exports.withDefinitionsContextHook = withDefinitionsContextHook; exports.withDefinitionsHook = withDefinitionsHook; //# sourceMappingURL=index.cjs.js.map