@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
114 lines • 5.63 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useContext } from 'react';
import IntlMessageFormat from 'intl-messageformat';
import { warnOnce } from '@awsui/component-toolkit/internal';
import { useTelemetry } from '../internal/hooks/use-telemetry';
import { applyDisplayName } from '../internal/utils/apply-display-name';
import { InternalI18nContext } from './context';
import { getMatchableLocales } from './get-matchable-locales';
/**
* Context to send parent messages down to child I18nProviders. This isn't
* included in the InternalI18nContext to avoid components from depending on
* MessageFormatElement types.
*/
const I18nMessagesContext = React.createContext({});
export function I18nProvider({ messages: messagesArray, locale: providedLocale, children }) {
useTelemetry('I18nProvider');
if (typeof document === 'undefined' && !providedLocale) {
warnOnce('I18nProvider', 'An explicit locale was not provided during server rendering. This can lead to a hydration mismatch on the client.');
}
// The provider accepts an array of configs. We merge parent messages and
// flatten the tree early on so that accesses by key are simpler and faster.
const parentMessages = useContext(I18nMessagesContext);
const messages = mergeMessages([parentMessages, ...messagesArray]);
let locale;
if (providedLocale) {
// If a locale is explicitly provided, use the string directly.
// Locales have a recommended case, but are matched case-insensitively,
// so we lowercase it internally.
locale = providedLocale.toLowerCase();
}
else if (typeof document !== 'undefined' && document.documentElement.lang) {
// Otherwise, use the value provided in the HTML tag.
locale = document.documentElement.lang.toLowerCase();
}
else {
// Lastly, fall back to English.
locale = 'en';
}
// Create a per-render cache of messages and IntlMessageFormat instances.
// Not memoizing it allows us to reset the cache when the component rerenders
// with potentially different locale or messages. We expect this component to
// be placed above AppLayout and therefore rerender very infrequently.
const localeFormatterCache = new Map();
const format = (namespace, component, key, provided, customHandler) => {
var _a, _b, _c;
// A general rule in this library is that undefined is basically
// treated as "not provided". So even if a user explicitly provides an
// undefined value, it will default to i18n provider values.
if (provided !== undefined) {
return provided;
}
const cacheKey = `${namespace}.${component}.${key}`;
let intlMessageFormat;
const cachedFormatter = localeFormatterCache.get(cacheKey);
if (cachedFormatter) {
// If an IntlMessageFormat instance was cached for this locale, just use that.
intlMessageFormat = cachedFormatter;
}
else {
// Widen the locale string (e.g. en-GB -> en) until we find a locale
// that contains the message we need.
let message;
const matchableLocales = getMatchableLocales(locale);
for (const matchableLocale of matchableLocales) {
message = (_c = (_b = (_a = messages === null || messages === void 0 ? void 0 : messages[namespace]) === null || _a === void 0 ? void 0 : _a[matchableLocale]) === null || _b === void 0 ? void 0 : _b[component]) === null || _c === void 0 ? void 0 : _c[key];
if (message !== undefined) {
break;
}
}
// If a message wasn't found, exit early.
if (message === undefined) {
return provided;
}
// Lazily create an IntlMessageFormat object for this key.
intlMessageFormat = new IntlMessageFormat(message, locale);
localeFormatterCache.set(cacheKey, intlMessageFormat);
}
if (customHandler) {
return customHandler(args => intlMessageFormat.format(args));
}
// Assuming `T extends string` since a customHandler wasn't provided.
return intlMessageFormat.format();
};
return (React.createElement(InternalI18nContext.Provider, { value: { locale, format } },
React.createElement(I18nMessagesContext.Provider, { value: messages }, children)));
}
applyDisplayName(I18nProvider, 'I18nProvider');
function mergeMessages(sources) {
const result = {};
for (const messages of sources) {
for (const namespace in messages) {
if (!(namespace in result)) {
result[namespace] = {};
}
for (const casedLocale in messages[namespace]) {
const locale = casedLocale.toLowerCase();
if (!(locale in result[namespace])) {
result[namespace][locale] = {};
}
for (const component in messages[namespace][casedLocale]) {
if (!(component in result[namespace][locale])) {
result[namespace][locale][component] = {};
}
for (const key in messages[namespace][casedLocale][component]) {
result[namespace][locale][component][key] = messages[namespace][casedLocale][component][key];
}
}
}
}
}
return result;
}
//# sourceMappingURL=provider.js.map