Skip to content

Commit

Permalink
Language switching (#170)
Browse files Browse the repository at this point in the history
* Language switching

* Fix linter

* Update packages/chat-core/src/index.ts

Co-authored-by: Jakub Hampl <[email protected]>

---------

Co-authored-by: Jakub Hampl <[email protected]>
  • Loading branch information
peterszerzo and jakub-nlx authored Jan 7, 2025
1 parent db78440 commit ddebbc8
Showing 1 changed file with 98 additions and 60 deletions.
158 changes: 98 additions & 60 deletions packages/chat-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ export interface Config {
* The language code to use for the bot. In the browser this can be fetched with `navigator.language`.
* If you don't have translations, hard-code this to the language code you support.
*/
languageCode: string;
languageCode: LanguageCode;
/**
* @hidden
* this should only be used for NLX internal testing.
Expand Down Expand Up @@ -522,6 +522,11 @@ export interface ChoiceRequestMetadata {
intentId?: string;
}

/**
* Language code named for clarity, may restrict it to a finite list
*/
export type LanguageCode = string;

/**
* Instead of sending a request to the bot, handle it in a custom fashion
* @param botRequest - The {@link BotRequest} that is being overridden
Expand Down Expand Up @@ -597,6 +602,14 @@ export interface ConversationHandler {
* Get the current conversation ID if it's set, or undefined if there is no conversation.
*/
currentConversationId: () => string | undefined;
/**
* Get the current language code
*/
currentLanguageCode: () => LanguageCode;
/**
* Set the language code
*/
setLanguageCode: (languageCode: LanguageCode) => void;
/**
* Forces a new conversation. If `clearResponses` is set to true, will also clear historical responses passed to subscribers.
* Retains all existing subscribers.
Expand All @@ -619,6 +632,7 @@ export interface ConversationHandler {

interface InternalState {
responses: Response[];
languageCode: string;
conversationId: string;
userId?: string;
}
Expand Down Expand Up @@ -675,6 +689,7 @@ export function createConversation(config: Config): ConversationHandler {

let state: InternalState = {
responses: config.responses ?? [],
languageCode: config.languageCode,
userId: config.userId,
conversationId: initialConversationId,
};
Expand Down Expand Up @@ -778,7 +793,7 @@ export function createConversation(config: Config): ConversationHandler {
userId: state.userId,
conversationId: state.conversationId,
...body,
languageCode: config.languageCode,
languageCode: state.languageCode,
channelType: config.experimental?.channelType,
environment: config.environment,
};
Expand All @@ -793,7 +808,7 @@ export function createConversation(config: Config): ConversationHandler {
`${config.botUrl}${
config.experimental?.completeBotUrl === true
? ""
: `-${config.languageCode}`
: `-${state.languageCode}`
}`,
{
method: "POST",
Expand Down Expand Up @@ -833,10 +848,10 @@ export function createConversation(config: Config): ConversationHandler {
const setupWebsocket = (): void => {
const url = new URL(config.botUrl);
if (config.experimental?.completeBotUrl !== true) {
url.searchParams.set("languageCode", config.languageCode);
url.searchParams.set("languageCode", state.languageCode);
url.searchParams.set(
"channelKey",
`${url.searchParams.get("channelKey") ?? ""}-${config.languageCode}`,
`${url.searchParams.get("channelKey") ?? ""}-${state.languageCode}`,
);
}
url.searchParams.set("conversationId", state.conversationId);
Expand Down Expand Up @@ -925,6 +940,67 @@ export function createConversation(config: Config): ConversationHandler {
});
};

const sendChoice = (
choiceId: string,
context?: Context,
metadata?: ChoiceRequestMetadata,
): void => {
let newResponses: Response[] = [...state.responses];

const choiceResponse: Response = {
type: "user",
receivedAt: new Date().getTime(),
payload: {
type: "choice",
choiceId,
},
};

const responseIndex = metadata?.responseIndex ?? -1;
const messageIndex = metadata?.messageIndex ?? -1;

if (responseIndex > -1 && messageIndex > -1) {
newResponses = adjust(
responseIndex,
(response) =>
response.type === "bot"
? {
...response,
payload: {
...response.payload,
messages: adjust(
messageIndex,
(message) => ({ ...message, selectedChoiceId: choiceId }),
response.payload.messages,
),
},
}
: response,
newResponses,
);
}

newResponses = [...newResponses, choiceResponse];

setState(
{
responses: newResponses,
},
choiceResponse,
);

void sendToBot({
context,
request: {
structured: {
nodeId: metadata?.nodeId,
intentId: metadata?.intentId,
choiceId,
},
},
});
};

const unsubscribe = (subscriber: Subscriber): void => {
subscribers = subscribers.filter((fn) => fn !== subscriber);
};
Expand Down Expand Up @@ -963,64 +1039,26 @@ export function createConversation(config: Config): ConversationHandler {
sendWelcomeIntent: (context) => {
sendIntent(welcomeIntent, context);
},
sendChoice: (choiceId, context, metadata) => {
let newResponses: Response[] = [...state.responses];

const choiceResponse: Response = {
type: "user",
receivedAt: new Date().getTime(),
payload: {
type: "choice",
choiceId,
},
};

const responseIndex = metadata?.responseIndex ?? -1;
const messageIndex = metadata?.messageIndex ?? -1;

if (responseIndex > -1 && messageIndex > -1) {
newResponses = adjust(
responseIndex,
(response) =>
response.type === "bot"
? {
...response,
payload: {
...response.payload,
messages: adjust(
messageIndex,
(message) => ({ ...message, selectedChoiceId: choiceId }),
response.payload.messages,
),
},
}
: response,
newResponses,
sendChoice,
currentConversationId: () => {
return state.conversationId;
},
setLanguageCode: (languageCode: string) => {
if (languageCode === state.languageCode) {
// eslint-disable-next-line no-console
console.warn(
"Attempted to set language code to the one already active.",
);
return;
}

newResponses = [...newResponses, choiceResponse];

setState(
{
responses: newResponses,
},
choiceResponse,
);

void sendToBot({
context,
request: {
structured: {
nodeId: metadata?.nodeId,
intentId: metadata?.intentId,
choiceId,
},
},
});
if (isUsingWebSockets()) {
teardownWebsocket();
setupWebsocket();
}
setState({ languageCode });
},
currentConversationId: () => {
return state.conversationId;
currentLanguageCode: () => {
return state.languageCode;
},
subscribe,
unsubscribe,
Expand Down

0 comments on commit ddebbc8

Please sign in to comment.