Skip to content

Commit 053d06c

Browse files
authored
Add SSR support (#309)
1 parent e360456 commit 053d06c

File tree

11 files changed

+437
-250
lines changed

11 files changed

+437
-250
lines changed

README.md

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -106,62 +106,64 @@ No setup headaches. Just clean, context-aware translations, out of the box.
106106

107107
## API
108108

109-
### getLocales()
109+
### getCalendar()
110110

111-
Returns the user preferred locales, in order.
111+
Returns the user preferred calendar format.
112112

113113
#### Method type
114114

115115
```ts
116-
type getLocales = () => Array<{
117-
languageCode: string;
118-
scriptCode?: string;
119-
countryCode: string;
120-
languageTag: string;
121-
isRTL: boolean;
122-
}>;
116+
type getCalendar = () =>
117+
| "gregorian"
118+
| "buddhist"
119+
| "coptic"
120+
| "ethiopic"
121+
| "ethiopic-amete-alem"
122+
| "hebrew"
123+
| "indian"
124+
| "islamic"
125+
| "islamic-umm-al-qura"
126+
| "islamic-civil"
127+
| "islamic-tabular"
128+
| "iso8601"
129+
| "japanese"
130+
| "persian";
123131
```
124132

125133
#### Usage example
126134

127135
```ts
128-
import { getLocales } from "react-native-localize";
136+
import { getCalendar } from "react-native-localize";
129137

130-
console.log(getLocales());
131-
/* -> [
132-
{ countryCode: "GB", languageTag: "en-GB", languageCode: "en", isRTL: false },
133-
{ countryCode: "US", languageTag: "en-US", languageCode: "en", isRTL: false },
134-
{ countryCode: "FR", languageTag: "fr-FR", languageCode: "fr", isRTL: false },
135-
] */
138+
console.log(getCalendar());
139+
// -> "gregorian"
136140
```
137141

138142
---
139143

140-
### getNumberFormatSettings()
144+
### getCountry()
141145

142-
Returns number formatting settings.
146+
Returns the user current country code (based on its device locale, **not** on its position).
143147

144148
#### Method type
145149

146150
```ts
147-
type getNumberFormatSettings = () => {
148-
decimalSeparator: string;
149-
groupingSeparator: string;
150-
};
151+
type getCountry = () => string;
151152
```
152153

153154
#### Usage example
154155

155156
```ts
156-
import { getNumberFormatSettings } from "react-native-localize";
157+
import { getCountry } from "react-native-localize";
157158

158-
console.log(getNumberFormatSettings());
159-
/* -> {
160-
decimalSeparator: ".",
161-
groupingSeparator: ",",
162-
} */
159+
console.log(getCountry());
160+
// -> "FR"
163161
```
164162

163+
#### Note
164+
165+
Devices using Latin American regional settings will return "UN" instead of "419", as the latter is not a standard country code.
166+
165167
---
166168

167169
### getCurrencies()
@@ -185,62 +187,60 @@ console.log(getCurrencies());
185187

186188
---
187189

188-
### getCountry()
190+
### getLocales()
189191

190-
Returns the user current country code (based on its device locale, **not** on its position).
192+
Returns the user preferred locales, in order.
191193

192194
#### Method type
193195

194196
```ts
195-
type getCountry = () => string;
197+
type getLocales = () => Array<{
198+
languageCode: string;
199+
scriptCode?: string;
200+
countryCode: string;
201+
languageTag: string;
202+
isRTL: boolean;
203+
}>;
196204
```
197205

198206
#### Usage example
199207

200208
```ts
201-
import { getCountry } from "react-native-localize";
209+
import { getLocales } from "react-native-localize";
202210

203-
console.log(getCountry());
204-
// -> "FR"
211+
console.log(getLocales());
212+
/* -> [
213+
{ countryCode: "GB", languageTag: "en-GB", languageCode: "en", isRTL: false },
214+
{ countryCode: "US", languageTag: "en-US", languageCode: "en", isRTL: false },
215+
{ countryCode: "FR", languageTag: "fr-FR", languageCode: "fr", isRTL: false },
216+
] */
205217
```
206218

207-
#### Note
208-
209-
Devices using Latin American regional settings will return "UN" instead of "419", as the latter is not a standard country code.
210-
211219
---
212220

213-
### getCalendar()
221+
### getNumberFormatSettings()
214222

215-
Returns the user preferred calendar format.
223+
Returns number formatting settings.
216224

217225
#### Method type
218226

219227
```ts
220-
type getCalendar = () =>
221-
| "gregorian"
222-
| "buddhist"
223-
| "coptic"
224-
| "ethiopic"
225-
| "ethiopic-amete-alem"
226-
| "hebrew"
227-
| "indian"
228-
| "islamic"
229-
| "islamic-umm-al-qura"
230-
| "islamic-civil"
231-
| "islamic-tabular"
232-
| "iso8601"
233-
| "japanese"
234-
| "persian";
228+
type getNumberFormatSettings = () => {
229+
decimalSeparator: string;
230+
groupingSeparator: string;
231+
};
235232
```
236233

237234
#### Usage example
238235

239236
```ts
240-
import { getCalendar } from "react-native-localize";
237+
import { getNumberFormatSettings } from "react-native-localize";
241238

242-
console.log(getCalendar());
243-
// -> "gregorian"
239+
console.log(getNumberFormatSettings());
240+
/* -> {
241+
decimalSeparator: ".",
242+
groupingSeparator: ",",
243+
} */
244244
```
245245

246246
---
@@ -382,7 +382,7 @@ Returns the best language tag possible and its reading direction. Useful to pick
382382
```ts
383383
type findBestLanguageTag = (
384384
languageTags: string[],
385-
) => { languageTag: string; isRTL: boolean } | void;
385+
) => { languageTag: string; isRTL: boolean } | undefined;
386386
```
387387

388388
#### Usage example
@@ -420,6 +420,41 @@ openAppLanguageSettings("application").catch((error) => {
420420
});
421421
```
422422

423+
## Server-side rendering
424+
425+
On the client, `react-native-localize` uses `navigator.languages`. During SSR, it gets language preferences from the server via the parsed `Accept-Language` header.
426+
427+
#### 1. Wrap your app with `ServerLanguagesProvider`
428+
429+
On the server, wrap your app with `ServerLanguagesProvider` and pass the user's languages:
430+
431+
```tsx
432+
import accepts from "accepts";
433+
import { ServerLanguagesProvider } from "react-native-localize";
434+
435+
// parse the Accept-Language header; any approach returning string[] is fine
436+
const languages = accepts(request).languages();
437+
438+
const html = renderToString(
439+
<ServerLanguagesProvider value={languages}>
440+
<App />
441+
</ServerLanguagesProvider>,
442+
);
443+
```
444+
445+
#### 2. Use the `useLocalize` hook in your components
446+
447+
In your components, use the `useLocalize` hook instead of calling the API methods directly:
448+
449+
```tsx
450+
import { useLocalize } from "react-native-localize";
451+
452+
const App = () => {
453+
const { getCountry } = useLocalize();
454+
return <Text>Country: {getCountry()}</Text>;
455+
};
456+
```
457+
423458
## Examples with [@formatjs/intl](https://formatjs.io/docs/intl)
424459

425460
Browse the files in the [/example](https://github.com/zoontek/react-native-localize/tree/master/example) directory.

docs/screenshot.png

-188 KB
Loading

example/src/App.tsx

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createIntl, createIntlCache } from "@formatjs/intl";
2-
import * as React from "react";
32
import {
43
Button,
54
I18nManager,
@@ -9,7 +8,21 @@ import {
98
Text,
109
View,
1110
} from "react-native";
12-
import * as Localize from "react-native-localize";
11+
import {
12+
findBestLanguageTag,
13+
getCalendar,
14+
getCountry,
15+
getCurrencies,
16+
getLocales,
17+
getNumberFormatSettings,
18+
getTemperatureUnit,
19+
getTimeZone,
20+
openAppLanguageSettings,
21+
uses24HourClock,
22+
usesAutoDateAndTime,
23+
usesAutoTimeZone,
24+
usesMetricSystem,
25+
} from "react-native-localize";
1326
import {
1427
SafeAreaProvider,
1528
useSafeAreaInsets,
@@ -27,7 +40,7 @@ type Translation = keyof typeof translations;
2740
const fallback = { languageTag: "en", isRTL: false };
2841

2942
const { languageTag, isRTL } =
30-
Localize.findBestLanguageTag(Object.keys(translations)) ?? fallback;
43+
findBestLanguageTag(Object.keys(translations)) ?? fallback;
3144

3245
// update layout direction
3346
I18nManager.forceRTL(isRTL);
@@ -93,53 +106,31 @@ const AppContent = () => {
93106
},
94107
]}
95108
>
96-
<Line name="Localize.getLocales()" value={Localize.getLocales()} />
97-
98-
<Line name="Localize.getCurrencies()" value={Localize.getCurrencies()} />
99-
100-
<Line name="Localize.getCountry()" value={Localize.getCountry()} />
101-
102-
<Line name="Localize.getCalendar()" value={Localize.getCalendar()} />
103-
104-
<Line
105-
name="Localize.getNumberFormatSettings()"
106-
value={Localize.getNumberFormatSettings()}
107-
/>
109+
<Line name="getCalendar()" value={getCalendar()} />
110+
<Line name="getCountry()" value={getCountry()} />
111+
<Line name="getCurrencies()" value={getCurrencies()} />
112+
<Line name="getLocales()" value={getLocales()} />
108113

109114
<Line
110-
name="Localize.getTemperatureUnit()"
111-
value={Localize.getTemperatureUnit()}
115+
name="getNumberFormatSettings()"
116+
value={getNumberFormatSettings()}
112117
/>
113118

114-
<Line name="Localize.getTimeZone()" value={Localize.getTimeZone()} />
115-
116-
<Line
117-
name="Localize.uses24HourClock()"
118-
value={Localize.uses24HourClock()}
119-
/>
120-
121-
<Line
122-
name="Localize.usesMetricSystem()"
123-
value={Localize.usesMetricSystem()}
124-
/>
119+
<Line name="getTemperatureUnit()" value={getTemperatureUnit()} />
120+
<Line name="getTimeZone()" value={getTimeZone()} />
121+
<Line name="uses24HourClock()" value={uses24HourClock()} />
122+
<Line name="usesMetricSystem()" value={usesMetricSystem()} />
125123

126124
{Platform.OS === "android" && (
127125
<>
128-
<Line
129-
name="Localize.usesAutoDateAndTime()"
130-
value={Localize.usesAutoDateAndTime()}
131-
/>
132-
133-
<Line
134-
name="Localize.usesAutoTimeZone()"
135-
value={Localize.usesAutoTimeZone()}
136-
/>
126+
<Line name="usesAutoDateAndTime()" value={usesAutoDateAndTime()} />
127+
<Line name="usesAutoTimeZone()" value={usesAutoTimeZone()} />
137128
</>
138129
)}
139130

140131
<Line
141-
name="Localize.findBestLanguageTag(['en-US', 'en', 'fr'])"
142-
value={Localize.findBestLanguageTag(["en-US", "en", "fr"])}
132+
name="findBestLanguageTag(['en-US', 'en', 'fr'])"
133+
value={findBestLanguageTag(["en-US", "en", "fr"])}
143134
/>
144135

145136
<Line name="Translation example" value={translate("hello")} />
@@ -148,7 +139,7 @@ const AppContent = () => {
148139
<Button
149140
title="Open app language settings"
150141
onPress={() => {
151-
Localize.openAppLanguageSettings().catch((error) => {
142+
openAppLanguageSettings().catch((error) => {
152143
console.error(error);
153144
});
154145
}}

example/webpack.config.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// http://necolas.github.io/react-native-web/docs/?path=/docs/guides-multi-platform--page#web-packaging-for-existing-react-native-apps
22

33
const path = require("path");
4-
const fromRoot = (_) => path.resolve(__dirname, _);
54

6-
const isProd = process.env.NODE_ENV === "production";
5+
const fromRoot = (_) => path.resolve(__dirname, _);
6+
const nodeModules = fromRoot("node_modules");
77

88
module.exports = {
9-
mode: isProd ? "production" : "development",
9+
mode: process.env.NODE_ENV === "production" ? "production" : "development",
1010
entry: fromRoot("index.js"),
1111
output: {
1212
path: fromRoot("dist"),
@@ -26,7 +26,11 @@ module.exports = {
2626
],
2727
},
2828
resolve: {
29-
alias: { "react-native$": "react-native-web" },
29+
alias: {
30+
react: path.join(nodeModules, "react"),
31+
"react-dom": path.join(nodeModules, "react-dom"),
32+
"react-native$": "react-native-web",
33+
},
3034
extensions: [".ts", ".tsx", ".js", ".jsx", ".json"]
3135
.map((extension) => [".web" + extension, extension])
3236
.flat(),

0 commit comments

Comments
 (0)