Last active
June 19, 2023 10:20
-
-
Save raphaelbadia/82f1c202e57b557bf88ea04cbbc0be29 to your computer and use it in GitHub Desktop.
Trans component from lingui
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable @typescript-eslint/no-explicit-any */ | |
import { | |
ComponentType, | |
createElement, | |
ElementType, | |
isValidElement, | |
ReactElement, | |
ReactNode, | |
} from 'react'; | |
import { i18n } from '@lingui/core'; | |
// getMsgs: (language: string) => Promise<Record<string, string>>; | |
// const language = getLanguage(); | |
// const messages = await getMsgs(language); | |
// i18n.loadAndActivate({ locale: language, messages }); | |
import { formatElements } from './format'; | |
export type TransRenderProps = { | |
id: string; | |
translation: ReactNode; | |
children: ReactNode; | |
message?: string | null; | |
isTranslated: boolean; | |
}; | |
export type TransRenderCallbackOrComponent = | |
| { | |
component?: undefined; | |
render?: (props: TransRenderProps) => ReactElement<any, any> | null; | |
} | |
| { | |
component?: ComponentType<TransRenderProps> | null; | |
render?: undefined; | |
}; | |
const DefaultI18n = ({ isTranslated, children }) => ( | |
<span style={{ color: isTranslated ? undefined : 'red' }}>{children}</span> | |
); | |
export type TransProps = { | |
id: string; | |
message?: string; | |
values?: Record<string, unknown>; | |
components?: Record<string, ElementType | any>; | |
formats?: Record<string, unknown>; | |
children?: ReactNode; | |
} & TransRenderCallbackOrComponent; | |
export async function TransServer({ | |
render, | |
component, | |
id, | |
message, | |
formats = {}, | |
values, | |
components = {}, | |
}: TransProps): Promise<ReactElement<any, any> | null> { | |
const defaultComponent = DefaultI18n; | |
if (values) { | |
Object.keys(values).forEach((key) => { | |
const value = values[key]; | |
const valueIsReactEl = | |
isValidElement(value) || (Array.isArray(value) && value.every((el) => isValidElement(el))); | |
if (!valueIsReactEl) { | |
return; | |
} | |
const index = Object.keys(components).length; | |
components[index] = value; | |
values[key] = `<${index}/>`; | |
}); | |
} | |
console.log(i18n.messages); | |
const _translation: string = | |
i18n && typeof i18n._ === 'function' ? i18n._(id, values, { message, formats }) : id; // i18n provider isn't loaded at all | |
const translation = _translation ? formatElements(_translation, components) : null; | |
console.log({ translation }); | |
if (render === null || component === null) { | |
// Although `string` is a valid react element, types only allow `Element` | |
// Upstream issue: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/20544 | |
return translation as unknown as ReactElement<any, any>; | |
} | |
const FallbackComponent: ComponentType<TransRenderProps> = defaultComponent || RenderFragment; | |
const i18nProps: TransRenderProps = { | |
id, | |
message, | |
translation, | |
isTranslated: id !== translation && message !== translation, | |
children: translation, // for type-compatibility with `component` prop | |
}; | |
// Validation of `render` and `component` props | |
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') { | |
// Apparently, both function components and class components are functions | |
// See https://stackoverflow.com/a/41658173/1535540 | |
console.error( | |
`Invalid value supplied to prop \`component\`. It must be a React component, provided ${component}`, | |
); | |
return createElement(FallbackComponent, i18nProps, translation); | |
} | |
// Rendering using a render prop | |
if (typeof render === 'function') { | |
// Component: render={(props) => <a title={props.translation}>x</a>} | |
return render(i18nProps); | |
} | |
// `component` prop has a higher precedence over `defaultComponent` | |
const Component: ComponentType<TransRenderProps> = component || FallbackComponent; | |
return createElement(Component, i18nProps, translation); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment