|
1 | | -import React, { ReactNode } from 'react'; |
| 1 | +import React, { isValidElement, ReactElement, ReactNode } from 'react'; |
2 | 2 | import { useTranslate } from './useTranslate'; |
3 | 3 |
|
| 4 | +const SPLIT_MARKER = '#@RA_CORE_INTERNAL_SPLIT@#'; |
| 5 | + |
4 | 6 | export const Translate = ({ i18nKey, options, children }: TranslateProps) => { |
5 | 7 | const translate = useTranslate(); |
6 | | - const translatedMessage = translate( |
7 | | - i18nKey, |
8 | | - typeof children === 'string' ? { _: children, ...options } : options |
9 | | - ); |
10 | 8 |
|
11 | | - if (translatedMessage) { |
| 9 | + // Separate React element values from plain values |
| 10 | + const elementMap: Record<string, ReactElement> = {}; |
| 11 | + const sanitizedOptions: Record<string, any> = {}; |
| 12 | + let placeholderIndex = 0; |
| 13 | + |
| 14 | + if (options) { |
| 15 | + for (const [key, value] of Object.entries(options)) { |
| 16 | + if (isValidElement(value)) { |
| 17 | + const placeholder = `TRANSLATION_PLACEHOLDER_${placeholderIndex++}`; |
| 18 | + elementMap[placeholder] = value; |
| 19 | + sanitizedOptions[key] = |
| 20 | + `${SPLIT_MARKER}${placeholder}${SPLIT_MARKER}`; |
| 21 | + } else { |
| 22 | + sanitizedOptions[key] = value; |
| 23 | + } |
| 24 | + } |
| 25 | + } |
| 26 | + |
| 27 | + const translateOptions = |
| 28 | + typeof children === 'string' |
| 29 | + ? { _: children, ...sanitizedOptions } |
| 30 | + : sanitizedOptions; |
| 31 | + |
| 32 | + const translatedMessage = translate(i18nKey, translateOptions); |
| 33 | + |
| 34 | + if (!translatedMessage) { |
| 35 | + return children; |
| 36 | + } |
| 37 | + |
| 38 | + // If no elements were extracted, return plain string |
| 39 | + if (placeholderIndex === 0) { |
12 | 40 | return <>{translatedMessage}</>; |
13 | 41 | } |
14 | | - return children; |
| 42 | + |
| 43 | + // Split the translated string and replace placeholders with React elements |
| 44 | + const parts = translatedMessage.split(SPLIT_MARKER); |
| 45 | + return ( |
| 46 | + <> |
| 47 | + {/* After splitting by SPLIT_MARKER, even indices are text and odd indices are placeholders */} |
| 48 | + {parts.map((part, index) => |
| 49 | + index % 2 === 1 ? ( |
| 50 | + <React.Fragment key={index}> |
| 51 | + {elementMap[part]} |
| 52 | + </React.Fragment> |
| 53 | + ) : ( |
| 54 | + <React.Fragment key={index}>{part}</React.Fragment> |
| 55 | + ) |
| 56 | + )} |
| 57 | + </> |
| 58 | + ); |
15 | 59 | }; |
16 | 60 |
|
17 | 61 | export interface TranslateProps { |
18 | 62 | i18nKey: string; |
19 | 63 | children?: ReactNode; |
20 | | - options?: Object; |
| 64 | + options?: Record<string, any>; |
21 | 65 | } |
0 commit comments