Skip to content

Commit a85bc0a

Browse files
wadeywjjoy-zz
andauthored
v0.2.0 Nested formatting
* v0.1.0 * update test branch triggering * add manual trigger test flow * Nested formatting * handle persist flag * update readme --------- Signed-off-by: Wade <wadezyw@gmail.com> Co-authored-by: wade <xxx@gmail.com>
1 parent cd84979 commit a85bc0a

File tree

4 files changed

+139
-11
lines changed

4 files changed

+139
-11
lines changed

README.md

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ You don't need to prepare any translation files, just provide your API key and t
1313
- 🎯 Dynamic parameter interpolation
1414
- 🔍 Persist translation tracking
1515
- 🔌 Offline mode support
16+
- 🎨 Nested text formatting support
1617
- ⚙️ Configurable cache TTL
1718
- ⚡️ Lightweight and efficient
1819

@@ -49,40 +50,66 @@ const App = () => {
4950

5051
### 2. Use the Translation Hook
5152

52-
Basic usage:
53+
**Basic usage:**
5354

5455
```typescript
56+
import { View, Text } from "react-native";
5557
import { useAutoTranslate } from "react-native-autolocalise";
5658

5759
const MyComponent = () => {
5860
const { t, loading, error } = useAutoTranslate();
5961

6062
return (
61-
<div>
62-
<h1>{t("Welcome to our app!", false)}</h1>
63-
<p>{t("This text will be automatically translated")}</p>
64-
</div>
63+
<View>
64+
<Text>{t("Welcome to our app!", false)}</Text>
65+
<Text>{t("This text will be automatically translated")}</Text>
66+
</View>
67+
);
68+
};
69+
```
70+
71+
**Use with nested text formatting:**
72+
73+
```typescript
74+
import { Text, View } from "react-native";
75+
import { FormattedText } from "react-native-autolocalise";
76+
77+
const MyComponent = () => {
78+
return (
79+
<View>
80+
<FormattedText>
81+
<Text>
82+
Hello, we <Text style={{ color: "red" }}>want</Text> you to be{" "}
83+
<Text style={{ fontWeight: "bold" }}>happy</Text>!
84+
</Text>
85+
</FormattedText>
86+
<FormattedText persist={false}>
87+
Hello,
88+
<Text style={{ color: "red" }}>World</Text>
89+
</FormattedText>
90+
</View>
6591
);
6692
};
6793
```
6894

69-
Use with params:
95+
**Use with params:**
7096

7197
```typescript
98+
import { View, Text } from "react-native";
7299
import { useAutoTranslate } from "react-native-autolocalise";
73100

74101
const MyComponent = () => {
75102
const { t } = useAutoTranslate();
76103
const name = "John";
77104

78105
return (
79-
<div>
80-
<p>
106+
<View>
107+
<Text>
81108
{t("Welcome, {{1}}!, Nice to meet you. {{2}}.")
82109
.replace("{{1}}", name)
83110
.replace("{{2}}", t("Have a great day!"))}
84-
</p>
85-
</div>
111+
</Text>
112+
</View>
86113
);
87114
};
88115
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"type": "module",
33
"name": "react-native-autolocalise",
4-
"version": "0.1.0",
4+
"version": "0.2.0",
55
"description": "Auto-translation SDK for React Native and Expo applications",
66
"main": "dist/index.js",
77
"module": "dist/index.esm.js",

src/components/FormattedText.tsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React from "react";
2+
import { Text, TextStyle } from "react-native";
3+
import { useAutoTranslate } from "../context/TranslationContext";
4+
5+
/**
6+
* FormattedText is a component that handles nested text formatting during translation.
7+
* It preserves styling and structure of nested Text components while allowing the content
8+
* to be translated.
9+
*
10+
* @example
11+
* ```tsx
12+
* <FormattedText>
13+
* Hello, <Text style={{ color: 'red' }}>world</Text>!
14+
* </FormattedText>
15+
* ```
16+
*/
17+
interface FormattedTextProps {
18+
children: React.ReactNode;
19+
style?: TextStyle;
20+
/**
21+
* Whether to persist the text for review in the dashboard.
22+
* @default true
23+
*/
24+
persist?: boolean;
25+
}
26+
27+
export const FormattedText: React.FC<FormattedTextProps> = ({
28+
children,
29+
style,
30+
persist = true,
31+
}) => {
32+
const { t } = useAutoTranslate();
33+
34+
/**
35+
* Extracts text content and styled nodes from the children prop.
36+
* Converts nested Text components into a template format (e.g., <0>styled text</0>)
37+
* while preserving the original styled nodes for later restoration.
38+
*
39+
* @param nodes - The React nodes to process (typically the children prop)
40+
* @returns An object containing the template text and an array of styled nodes
41+
*/
42+
const extractTextAndStyles = (
43+
nodes: React.ReactNode
44+
): {
45+
text: string;
46+
styles: Array<{ node: React.ReactElement; text: string }>;
47+
} => {
48+
const styles: Array<{ node: React.ReactElement; text: string }> = [];
49+
let text = "";
50+
51+
const processNode = (node: React.ReactNode) => {
52+
if (typeof node === "string") {
53+
text += node;
54+
return;
55+
}
56+
57+
if (React.isValidElement(node)) {
58+
const children = node.props.children;
59+
if (typeof children === "string") {
60+
text += `<${styles.length}>${children}</${styles.length}>`;
61+
styles.push({ node, text: children });
62+
} else if (Array.isArray(children)) {
63+
children.forEach(processNode);
64+
}
65+
}
66+
};
67+
68+
processNode(nodes);
69+
return { text, styles };
70+
};
71+
72+
/**
73+
* Restores the styled nodes in the translated text by replacing template markers
74+
* with the original styled components, but with translated content.
75+
*
76+
* @param translatedText - The translated text containing template markers
77+
* @param styles - Array of original styled nodes and their text content
78+
* @returns An array of React nodes with restored styling and translated content
79+
*/
80+
const restoreStyledText = (
81+
translatedText: string,
82+
styles: Array<{ node: React.ReactElement; text: string }>
83+
): React.ReactNode[] => {
84+
const parts = translatedText.split(/(<\d+>.*?<\/\d+>)/g);
85+
return parts.map((part, index) => {
86+
const match = part.match(/<(\d+)>(.*?)<\/\1>/);
87+
if (match) {
88+
const [, styleIndex, content] = match;
89+
const { node } = styles[parseInt(styleIndex)];
90+
return React.cloneElement(node, { key: `styled-${index}` }, content);
91+
}
92+
return part;
93+
});
94+
};
95+
96+
const { text, styles } = extractTextAndStyles(children);
97+
const translatedText = t(text, persist);
98+
99+
return <Text style={style}>{restoreStyledText(translatedText, styles)}</Text>;
100+
};

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ const autoTranslate = {
1313
return service.init();
1414
},
1515
};
16+
export { FormattedText } from "./components/FormattedText";
1617

1718
export default autoTranslate;

0 commit comments

Comments
 (0)