@lingui/react
Version:
React components for translations
130 lines (126 loc) • 4.05 kB
JavaScript
import React from 'react';
const tagRe = /<([a-zA-Z0-9]+)>([\s\S]*?)<\/\1>|<([a-zA-Z0-9]+)\/>/;
const voidElementTags = {
area: true,
base: true,
br: true,
col: true,
embed: true,
hr: true,
img: true,
input: true,
keygen: true,
link: true,
meta: true,
param: true,
source: true,
track: true,
wbr: true,
menuitem: true
};
function formatElements(value, elements = {}) {
const uniqueId = makeCounter(0, "$lingui$");
const parts = value.split(tagRe);
if (parts.length === 1)
return value;
const tree = [];
const before = parts.shift();
if (before)
tree.push(before);
for (const [index, children, after] of getElements(parts)) {
let element = typeof index !== "undefined" ? elements[index] : void 0;
if (!element || voidElementTags[element.type] && children) {
if (!element) {
console.error(
`Can't use element at index '${index}' as it is not declared in the original translation`
);
} else {
console.error(
`${element.type} is a void element tag therefore it must have no children`
);
}
element = React.createElement(React.Fragment);
}
if (Array.isArray(element)) {
element = React.createElement(React.Fragment, {}, element);
}
tree.push(
React.cloneElement(
element,
{ key: uniqueId() },
// format children for pair tags
// unpaired tags might have children if it's a component passed as a variable
children ? formatElements(children, elements) : element.props.children
)
);
if (after)
tree.push(after);
}
return tree.length === 1 ? tree[0] : tree;
}
function getElements(parts) {
if (!parts.length)
return [];
const [paired, children, unpaired, after] = parts.slice(0, 4);
const triple = [paired || unpaired, children || "", after];
return [triple].concat(getElements(parts.slice(4, parts.length)));
}
const makeCounter = (count = 0, prefix = "") => () => `${prefix}_${count++}`;
function TransNoContext(props) {
const {
render,
component,
id,
message,
formats,
lingui: { i18n, defaultComponent }
} = props;
const values = { ...props.values };
const components = { ...props.components };
if (values) {
Object.keys(values).forEach((key) => {
const index = Object.keys(components).length;
if (typeof values[key] === "string" || typeof values[key] === "number") {
return;
}
components[index] = /* @__PURE__ */ React.createElement(React.Fragment, null, values[key]);
values[key] = `<${index}/>`;
});
}
const _translation = i18n && typeof i18n._ === "function" ? i18n._(id, values, { message, formats }) : id;
const translation = _translation ? formatElements(_translation, components) : null;
if (render === null || component === null) {
return translation;
}
const FallbackComponent = defaultComponent || RenderFragment;
const i18nProps = {
id,
message,
translation,
children: translation
// for type-compatibility with `component` prop
};
if (render && component) {
console.error(
"You can't use both `component` and `render` prop at the same time. `component` is ignored."
);
} else if (render && typeof render !== "function") {
console.error(
`Invalid value supplied to prop \`render\`. It must be a function, provided ${render}`
);
} else if (component && typeof component !== "function") {
console.error(
`Invalid value supplied to prop \`component\`. It must be a React component, provided ${component}`
);
return React.createElement(FallbackComponent, i18nProps, translation);
}
if (typeof render === "function") {
return render(i18nProps);
}
const Component = component || FallbackComponent;
return React.createElement(Component, i18nProps, translation);
}
const RenderFragment = ({ children }) => {
return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
};
export { TransNoContext as T };