Skip to content

Commit 765386b

Browse files
authored
Release/0.3.0 (#7)
* Optimised SSR flow * Nested Formatting for SSR
1 parent 1a7220c commit 765386b

File tree

7 files changed

+309
-142
lines changed

7 files changed

+309
-142
lines changed

README.md

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ npm install react-autolocalise
2626
yarn add react-autolocalise
2727
```
2828

29-
## Usage
29+
## React Client Side Component Usage
3030

3131
### Initialize the SDK
3232

@@ -117,7 +117,7 @@ const MyComponent = () => {
117117

118118
This SDK provides comprehensive SSR support through middleware-based locale detection and server components. Here's how to implement end-to-end server-side translation:
119119

120-
### Middleware Setup
120+
### Middleware Setup for language detection
121121

122122
Create a middleware file to detect user's locale from request headers or URL parameters:
123123

@@ -146,45 +146,96 @@ export const config = {
146146
};
147147
```
148148

149+
### Initialize Translation Service (Singleton Pattern)
150+
151+
The SDK uses a singleton pattern for the TranslationService to ensure efficient caching and batch processing. Create a utility file to manage the translator instance:
152+
153+
```typescript
154+
// utils/translator.ts
155+
import ServerTranslation from "react-autolocalise/server";
156+
157+
const config = {
158+
apiKey: "your-api-key",
159+
sourceLocale: "fr",
160+
targetLocale: "en",
161+
};
162+
163+
// Simple function to get a translator instance
164+
export function getTranslator() {
165+
return new ServerTranslation(config);
166+
}
167+
```
168+
149169
### Server Component Implementation
150170

151171
Create server components that utilize the detected locale:
152172

153173
> **Note**: For server-side rendering, all translations must be completed before sending the response to the client. This requires a two-step process: first mark texts for translation using t() , then execute all translations in a single batch with execute() . This ensures all translations are ready before rendering occurs.
154174
155-
```tsx
156-
import { ServerTranslation } from "react-autolocalise/server";
157-
import { headers } from "next/headers";
158-
159-
export default async function ServerComponent() {
160-
const headersList = headers();
161-
const targetLocale = headersList.get("x-locale") || "en";
175+
**Basic Usage:**
162176

163-
const config = {
164-
apiKey: "your-api-key",
165-
sourceLocale: "en",
166-
targetLocale,
167-
};
177+
```tsx
178+
import { getTranslator } from "@/utils/translator";
168179

169-
// Create a server-side translation instance
170-
const translator = new ServerTranslation(config);
180+
async function ServerComponent() {
181+
const translator = getTranslator();
171182

172183
// Mark texts for translation
173184
const title = translator.t("Hello from Server Component");
174185
const description = translator.t(
175186
"This component is rendered on the server side"
176187
);
177188

178-
// Execute all translations at once
189+
// Execute all translations in a single batch
179190
await translator.execute();
180191

192+
// Get translated texts
181193
return (
182194
<div>
183195
<h1>{translator.get(title)}</h1>
184196
<p>{translator.get(description)}</p>
185197
</div>
186198
);
187199
}
200+
201+
export default ServerComponent;
202+
```
203+
204+
**Use with nested text formatting:**
205+
206+
For components with styled text, use `tFormatted()` and `getFormatted()` to preserve formatting:
207+
208+
```tsx
209+
import { getTranslator } from "@/utils/translator";
210+
211+
async function FormattedServerComponent() {
212+
const translator = getTranslator();
213+
214+
// Mark formatted text with nested styling for translation
215+
const formattedContent = (
216+
<>
217+
Hello, we <span style={{ color: "red" }}>want</span> you to be{" "}
218+
<strong style={{ fontWeight: "bold" }}>happy</strong>!
219+
</>
220+
);
221+
// Mark the formatted texts for translation
222+
translator.tFormatted(formattedContent);
223+
224+
// Also mark some regular text
225+
const subtitle = translator.t("Server-side nested formatting example");
226+
227+
// Execute all translations in a single batch
228+
await translator.execute();
229+
230+
return (
231+
<div>
232+
<h3>{translator.get(subtitle)}</h3>
233+
<p>{translator.getFormatted(formattedContent)}</p>
234+
</div>
235+
);
236+
}
237+
238+
export default FormattedServerComponent;
188239
```
189240

190241
### SEO Considerations

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-autolocalise",
4-
"version": "0.2.0",
4+
"version": "0.3.0",
55
"description": "Auto-translation SDK for React and Next.js applications with SSR support",
66
"main": "dist/index.cjs",
77
"module": "dist/index.mjs",

src/components/FormattedText.tsx

Lines changed: 4 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import React from "react";
22
import { useAutoTranslate } from "../context/TranslationContext";
3+
import {
4+
extractTextAndStyles,
5+
restoreStyledText,
6+
} from "../utils/textFormatting";
37

48
/**
59
* FormattedText is a component that handles nested text formatting during translation.
@@ -30,70 +34,6 @@ export const FormattedText: React.FC<FormattedTextProps> = ({
3034
}) => {
3135
const { t } = useAutoTranslate();
3236

33-
/**
34-
* Extracts text content and styled nodes from the children prop.
35-
* Converts nested span elements into a template format (e.g., <0>styled text</0>)
36-
* while preserving the original styled nodes for later restoration.
37-
*
38-
* @param nodes - The React nodes to process (typically the children prop)
39-
* @returns An object containing the template text and an array of styled nodes
40-
*/
41-
const extractTextAndStyles = (
42-
nodes: React.ReactNode
43-
): {
44-
text: string;
45-
styles: Array<{ node: React.ReactElement; text: string }>;
46-
} => {
47-
const styles: Array<{ node: React.ReactElement; text: string }> = [];
48-
let text = "";
49-
50-
const processNode = (node: React.ReactNode) => {
51-
if (typeof node === "string") {
52-
text += node;
53-
return;
54-
}
55-
56-
if (React.isValidElement(node)) {
57-
const children = node.props.children;
58-
if (typeof children === "string") {
59-
text += `<${styles.length}>${children}</${styles.length}>`;
60-
styles.push({ node, text: children });
61-
} else if (Array.isArray(children)) {
62-
children.forEach(processNode);
63-
} else if (children) {
64-
processNode(children);
65-
}
66-
}
67-
};
68-
69-
processNode(nodes);
70-
return { text, styles };
71-
};
72-
73-
/**
74-
* Restores the styled nodes in the translated text by replacing template markers
75-
* with the original styled components, but with translated content.
76-
*
77-
* @param translatedText - The translated text containing template markers
78-
* @param styles - Array of original styled nodes and their text content
79-
* @returns An array of React nodes with restored styling and translated content
80-
*/
81-
const restoreStyledText = (
82-
translatedText: string,
83-
styles: Array<{ node: React.ReactElement; text: string }>
84-
): React.ReactNode[] => {
85-
const parts = translatedText.split(/(<\d+>.*?<\/\d+>)/g);
86-
return parts.map((part, index) => {
87-
const match = part.match(/<(\d+)>(.*?)<\/\1>/);
88-
if (match) {
89-
const [, styleIndex, content] = match;
90-
const { node } = styles[parseInt(styleIndex)];
91-
return React.cloneElement(node, { key: `styled-${index}` }, content);
92-
}
93-
return part;
94-
});
95-
};
96-
9737
const { text, styles } = extractTextAndStyles(children);
9838
const translatedText = t(text, persist);
9939

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,9 @@ const autoTranslate = {
1919
};
2020

2121
export { FormattedText } from "./components/FormattedText";
22+
export {
23+
extractTextAndStyles,
24+
restoreStyledText,
25+
} from "./utils/textFormatting";
2226

2327
export default autoTranslate;

0 commit comments

Comments
 (0)