baptisteArno / typebot.io

💬 Typebot is a powerful chatbot builder that you can self-host.
https://typebot.io
Other
7.43k stars 2.07k forks source link

Add multi lang support for bots #392

Open os-mmasse opened 1 year ago

os-mmasse commented 1 year ago

It would be great to be able add a block "i18n" to create a translation map { default: "en", locales: [ { lang:"en", messages: { k1: "text1", k2: "text2" } }, { lang:"fr", messages: { k1: "FRtext1", k2: "FRtext2" } }, ] }

be able to declare currentLocale via script : i18n.setLocale(lang) be able to use it in all blocks like a variable {{ i18n(key) }} or maybe easier {{ i18n.key }} or {{ i18n[key] }}

baptisteArno commented 1 year ago

This kind of solution would make the bot flow unreadable, in my opinion, if you see {{i18n.key}} everywhere.

We need a deep multi-language feature for typebots where you can see the flow for different languages that you select with a dropdown.

Under the hood, yes, it would probably add a translation map to the typebot object.

satonotdead commented 1 year ago

What about using an API like DeepL?

We have it working on Ghost and Discourse and gives really good outputs. We are unsing Weglot to correct the strings.

jwalsh-vori commented 3 months ago

I have started to play with adding multi-lang support and would like to discuss approaches.

Under Settings section of a Bot config, I think adding a toggle to enable Localization and select which languages are enabled. I have this working:

Screenshot 2024-07-24 at 9 23 19 PM Screenshot 2024-07-24 at 9 31 08 PM

Then within a Block, add a language selector menu that you can choose which language to add text content for, like this:

Screenshot 2024-07-24 at 9 22 12 PM

Ideally there is a LanguageProvider that provides a LanguageContext to components so they can react to the selected language, as well as control enabling and disabling languages.

I need help figuring out the right approach to storing the actual translations themselves. I feel that they should be stored as part of the Zod schema for the Blocks so that if the Bot is exported, the translations are included. Would be nice to be able to hook into a translation manager like Tolgee but I'm not sure how to do that.

`export interface Language { code: string name: string enabled: boolean default: boolean }

export type LanguageContextType = { languages: Language[] getDefaultLanguage: () => Language select: (code: string) => void getSelected: () => Language enable: (language: Language) => void disable: (language: Language) => void }

const LanguageContext = createContext<LanguageContextType | null>(null)

const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ children, }) => { const [languages, setLanguages] = useState<Language[]>([ { code: 'en', name: 'English', enabled: true, default: true }, { code: 'es', name: 'Spanish', enabled: true, default: false }, { code: 'fr', name: 'French', enabled: true, default: false }, { code: 'de', name: 'German', enabled: true, default: false }, { code: 'it', name: 'Italian', enabled: true, default: false }, { code: 'pt', name: 'Portuguese', enabled: true, default: false }, ]) `

`import { GlobeIcon } from '@/components/icons' import { Button, Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/react' import { LanguageContext, LanguageContextType } from './LanguageProvider' import React from 'react'

export const LanguageMenu = () => { const { select, getDefaultLanguage, languages } = React.useContext( LanguageContext ) as LanguageContextType

const handleMenuItemCLick = (code: string) => { select(code) }

return (

} /> handleMenuItemCLick(getDefaultLanguage().code)} > {getDefaultLanguage().name} {languages.map((lang) => { if (lang.code === getDefaultLanguage().code) return <> return ( handleMenuItemCLick(lang.code)} > {lang.name} ) })} ) }` Thoughts?
jwalsh-vori commented 3 months ago

I included a mockup of what I think the schema changes would look like in an JSON export of a bot configuration for a text bubble and a text input. What do you think? I need some help figuring out how to make this happen though :)

{ "id": "yprw6uqo90vmr822n59c5rv7", "title": "Say Hello", "graphCoordinates": { "x": -2875.63, "y": 199.19 }, "blocks": [ { "id": "fkmug80196oq9ddfuipg9qqw", "type": "text", "content": { "richText": [ { "type": "p", "children": [ { "bold": true, "text": [{"code": "en", "text": "hello"}, {"code": "fr", "text": "bonjour"}], "italic": true, "underline": true } ] } ] } }, { "id": "ph3mi6ry0w9cgo1r0sj8g6o6", "type": "text input", "options": { "labels": { "placeholder": [{"code": "en", "text":"Enter your reply..."}, {"code": "fr", "text":"Entrez votre réponse..."}]} } } ] }

jwalsh-vori commented 3 months ago

@baptisteArno any input here?

baptisteArno commented 3 months ago

This is a tough problem to solve that needs deep understanding of how the project works under the hood.

I appreciate the input but I think I can't really get help on that issue for now. I still need to explore the right solution.

Managing translation is a tough problem. This is why there are several softwares that exist (Tolgee, Crowdin, etc...). I can't imagine a fully fledge solution embedded in Typebot. But I am thinking about integrating it with these softwares. I will most likely start with Tolgee since we are using it for the translation of Typebot, the app and it is open-source.

jwalsh-vori commented 3 months ago

Wondering if to keep it simpler for now, the flow just stores the translations, which are manually entered by the flow designer, for each block and not worry about integration to a translation management solution? Solving for text bubbles and input placeholders would handle most of this. The other issue is the need to separate the flow logic & variables from the text display or just link them to the 'default' language.

Happy to help if I can

Dimpipay commented 2 months ago

It would be great to be able add a block "i18n" to create a translation map

`{

default: "en",

locales: [

    {

        lang:"en",

        messages: {

            k1: "text1",

            k2: "text2"

        }

    },

    {

        lang:"fr",

        messages: {

            k1: "FRtext1",

            k2: "FRtext2"

        }

    },

]

}`

be able to declare currentLocale via script : i18n.setLocale(lang)

be able to use it in all blocks like a variable {{ i18n(key) }} or maybe easier {{ i18n.key }} or {{ i18n[key] }}