Skip to content

Commit 44fdf82

Browse files
committed
feat(react,js): add locale option
1 parent cfb7a7c commit 44fdf82

File tree

10 files changed

+791
-9
lines changed

10 files changed

+791
-9
lines changed

workspaces/js/src/init.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@ let locationChangeInterval: number | null = null;
1414
export const init = (options: FlowsOptions): void => {
1515
const apiUrl = options.apiUrl ?? "https://api.flows-cloud.com";
1616
config.value = { ...options, apiUrl };
17-
const { environment, organizationId, userId, userProperties } = options;
17+
const { environment, organizationId, userId, userProperties, locale } = options;
1818

19-
connectToWebsocketAndFetchBlocks({ apiUrl, environment, organizationId, userId, userProperties });
19+
connectToWebsocketAndFetchBlocks({
20+
apiUrl,
21+
environment,
22+
organizationId,
23+
userId,
24+
userProperties,
25+
locale,
26+
});
2027

2128
if (locationChangeInterval !== null) clearInterval(locationChangeInterval);
2229
locationChangeInterval = window.setInterval(() => {

workspaces/js/src/lib/blocks.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { type BlockUpdatesPayload, getApi, log, type UserProperties } from "@flows/shared";
1+
import {
2+
type BlockUpdatesPayload,
3+
getApi,
4+
getUserLocale,
5+
type LocaleOption,
6+
log,
7+
type UserProperties,
8+
} from "@flows/shared";
29
import { blocks } from "../store";
310
import { type Disconnect, websocket } from "./websocket";
411
import { packageAndVersion } from "./constants";
@@ -9,6 +16,7 @@ interface Props {
916
organizationId: string;
1017
userId: string;
1118
userProperties?: UserProperties;
19+
locale?: LocaleOption;
1220
}
1321

1422
let disconnect: Disconnect | null = null;
@@ -23,7 +31,11 @@ export const connectToWebsocketAndFetchBlocks = (props: Props): void => {
2331

2432
const fetchBlocks = (): void => {
2533
void getApi(apiUrl, packageAndVersion)
26-
.getBlocks({ ...params, userProperties: props.userProperties })
34+
.getBlocks({
35+
...params,
36+
locale: getUserLocale(props.locale),
37+
userProperties: props.userProperties,
38+
})
2739
.then((res) => {
2840
blocks.value = res.blocks;
2941
// Disconnect if the user is usage limited

workspaces/js/src/types/configuration.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type UserProperties } from "@flows/shared";
1+
import { type LocaleOption, type UserProperties } from "@flows/shared";
22

33
export interface FlowsOptions {
44
/**
@@ -21,4 +21,12 @@ export interface FlowsOptions {
2121
* Custom API URL useful when using proxy to send Flows requests through your own domain.
2222
*/
2323
apiUrl?: string;
24+
/**
25+
* Locale used to select the correct translation for the block data.
26+
* - `disabled` - the default language will be served. (default)
27+
* - `automatic` - the locale will be detected from the browser settings.
28+
* - specific locale (e.g. `en`, `en-US`) - The whole list of supported locales can be found: TODO:
29+
* @defaultValue `disabled`
30+
*/
31+
locale?: LocaleOption;
2432
}

workspaces/react/src/flows-provider.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type FC, type ReactNode } from "react";
2-
import { type UserProperties } from "@flows/shared";
2+
import { type LocaleOption, type UserProperties } from "@flows/shared";
33
import { type TourComponents, type Components } from "./types";
44
import { FlowsContext } from "./flows-context";
55
import { useRunningTours } from "./hooks/use-running-tours";
@@ -40,6 +40,14 @@ export interface FlowsProviderProps {
4040
* Components used for tour blocks.
4141
*/
4242
tourComponents: TourComponents;
43+
/**
44+
* Locale used to select the correct translation for the block data.
45+
* - `disabled` - the default language will be served. (default)
46+
* - `automatic` - the locale will be detected from the browser settings.
47+
* - specific locale (e.g. `en`, `en-US`) - The whole list of supported locales can be found: TODO:
48+
* @defaultValue `disabled`
49+
*/
50+
locale?: LocaleOption;
4351

4452
children: ReactNode;
4553
}
@@ -64,6 +72,7 @@ const FlowsProviderInner: FC<Props> = ({
6472
components,
6573
tourComponents,
6674
userProperties,
75+
locale,
6776
}) => {
6877
globalConfig.apiUrl = apiUrl;
6978
globalConfig.environment = environment;
@@ -76,6 +85,7 @@ const FlowsProviderInner: FC<Props> = ({
7685
organizationId,
7786
userId,
7887
userProperties,
88+
locale,
7989
});
8090

8191
const runningTours = useRunningTours({ blocks, removeBlock });

workspaces/react/src/hooks/use-blocks.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
type Block,
77
type TourStep,
88
type BlockUpdatesPayload,
9+
type LocaleOption,
10+
getUserLocale,
911
} from "@flows/shared";
1012
import { packageAndVersion } from "../lib/constants";
1113
import { type RemoveBlock, type UpdateBlock } from "../flows-context";
@@ -17,6 +19,7 @@ interface Props {
1719
organizationId: string;
1820
userId: string;
1921
userProperties?: UserProperties;
22+
locale?: LocaleOption;
2023
}
2124

2225
interface Return {
@@ -31,6 +34,7 @@ export const useBlocks = ({
3134
organizationId,
3235
userId,
3336
userProperties,
37+
locale,
3438
}: Props): Return => {
3539
const [blocks, setBlocks] = useState<Block[]>([]);
3640
const [usageLimited, setUsageLimited] = useState(false);
@@ -44,18 +48,21 @@ export const useBlocks = ({
4448
userPropertiesRef.current = userProperties;
4549
}, [userProperties]);
4650

47-
// TODO: call fetchBlocks on reconnect
4851
const fetchBlocks = useCallback(() => {
4952
void getApi(apiUrl, packageAndVersion)
50-
.getBlocks({ ...params, userProperties: userPropertiesRef.current })
53+
.getBlocks({
54+
...params,
55+
locale: getUserLocale(locale),
56+
userProperties: userPropertiesRef.current,
57+
})
5158
.then((res) => {
5259
setBlocks(res.blocks);
5360
if (res.meta?.usage_limited) setUsageLimited(true);
5461
})
5562
.catch((err: unknown) => {
5663
log.error("Failed to load blocks", err);
5764
});
58-
}, [apiUrl, params]);
65+
}, [apiUrl, locale, params]);
5966

6067
const websocketUrl = useMemo(() => {
6168
if (usageLimited) return;

workspaces/shared/src/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface GetBlocksRequest {
2626
environment: string;
2727
organizationId: string;
2828
userProperties?: Record<string, unknown>;
29+
locale?: string;
2930
}
3031

3132
interface BlockResponseMeta {

workspaces/shared/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from "./component-props";
33
export * from "./log";
44
export * from "./matchers";
55
export * from "./pathname";
6+
export * from "./locale";
67
export * from "./types";

workspaces/shared/src/locale.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { type LocaleOption } from "./types";
2+
3+
export const getUserLocale = (locale: LocaleOption = "disabled"): string | undefined => {
4+
if (locale === "disabled") return undefined;
5+
if (locale === "automatic") {
6+
const browserLanguage = navigator.languages.at(0) ?? navigator.language;
7+
return browserLanguage;
8+
}
9+
return locale;
10+
};

workspaces/shared/src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from "./active-block";
22
export * from "./block";
33
export * from "./components";
4+
export * from "./locale";
45
export * from "./tooltip";
56
export * from "./user-properties";

0 commit comments

Comments
 (0)