@ices/react-locale
Version:
React components for locale
642 lines (629 loc) • 25.9 kB
JavaScript
;
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