The locale on client is forced to zh-Hans after the activation for the new user if the interface language is set as neither en-US nor zh-Hans #4110

Closed
opened 2026-02-21 18:04:47 -05:00 by yindo · 2 comments
Owner

Originally created by @kurokobo on GitHub (Jun 15, 2024).

Self Checks

  • This is only for bug report, if you would like to ask a question, please head to Discussions.
  • I have searched for existing issues search for existing issues, including closed ones.
  • I confirm that I am using English to submit this report (我已阅读并同意 Language Policy).
  • Please do not modify this template :) and fill in all the required fields.

Dify version

0.6.11

Cloud or Self Hosted

Self Hosted (Docker)

Steps to reproduce

  1. Invite new user to create invitation link
    image
  2. Open the invitation link and in the Interface Language field, select a language that is neither English nor 简体中文 (in this case 日本語 (Japanese).
    image
  3. 🕷️BUG: The locale in the upper right corner and the lang in the HTML are not updated, and the completion message is displayed inzh-Hans instead of ja-JP.
    image
  4. Click 现在登录 button
  5. 🕷️BUG: The user is redirected to the login page with the locale zh-Hans
    image

✔️ Expected Behavior

The browser's preferred locale is consistently respected during activation and login (in this example, we can always operate the screen in Japanese).

Actual Behavior

The locale is forcibly changed to zh-Hans during the process.

Originally created by @kurokobo on GitHub (Jun 15, 2024). ### Self Checks - [X] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general). - [X] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones. - [X] I confirm that I am using English to submit this report (我已阅读并同意 [Language Policy](https://github.com/langgenius/dify/issues/1542)). - [X] Please do not modify this template :) and fill in all the required fields. ### Dify version 0.6.11 ### Cloud or Self Hosted Self Hosted (Docker) ### Steps to reproduce 1. Invite new user to create invitation link ![image](https://github.com/langgenius/dify/assets/2920259/83799281-bf3e-4e3a-81b5-483235c3c3e2) 2. Open the invitation link and in the `Interface Language` field, **select a language that is neither `English` nor `简体中文` (in this case `日本語` (Japanese)**. ![image](https://github.com/langgenius/dify/assets/2920259/5de40abe-27ae-48b4-b841-5e7bc943e678) 4. **🕷️BUG**: The locale in the upper right corner and the `lang` in the HTML are not updated, and the completion message is displayed in`zh-Hans` instead of `ja-JP`. ![image](https://github.com/langgenius/dify/assets/2920259/8b3af73f-82e8-41fb-aecb-7e01b87be60c) 5. Click `现在登录` button 6. **🕷️BUG**: The user is redirected to the login page with the locale `zh-Hans` ![image](https://github.com/langgenius/dify/assets/2920259/fcf79081-3d23-4049-a1bd-306b3700cdb3) ### ✔️ Expected Behavior The browser's preferred locale is consistently respected during activation and login (in this example, we can always operate the screen in Japanese). ### ❌ Actual Behavior The locale is forcibly changed to `zh-Hans` during the process.
yindo added the 🐞 bug label 2026-02-21 18:04:47 -05:00
yindo closed this issue 2026-02-21 18:04:47 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jun 15, 2024):

To ensure that the browser's preferred locale is consistently respected during activation and login, you need to modify the locale handling logic in the code. Here are the steps to address the issue:

  1. Modify the initI18next function:
    Ensure that the locale is not forcibly changed to zh-Hans and instead use the locale determined by the getLocaleOnServer function.

    import { cookies, headers } from 'next/headers'
    import Negotiator from 'negotiator'
    import { match } from '@formatjs/intl-localematcher'
    
    import { createInstance } from 'i18next'
    import resourcesToBackend from 'i18next-resources-to-backend'
    import { initReactI18next } from 'react-i18next/initReactI18next'
    import { i18n } from '.'
    import type { Locale } from '.'
    
    const initI18next = async (lng: Locale, ns: string) => {
      const i18nInstance = createInstance()
      await i18nInstance
        .use(initReactI18next)
        .use(resourcesToBackend((language: string, namespace: string) => import(`./${language}/${namespace}.ts`)))
        .init({
          lng, // Use the locale directly without forcing 'zh-Hans'
          ns,
          fallbackLng: 'en-US',
        })
      return i18nInstance
    }
    
    export async function useTranslation(lng: Locale, ns = '', options: Record<string, any> = {}) {
      const i18nextInstance = await initI18next(lng, ns)
      return {
        t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
        i18n: i18nextInstance,
      }
    }
    
    export const getLocaleOnServer = (): Locale => {
      const locales: string[] = i18n.locales
    
      let languages: string[] | undefined
      const localeCookie = cookies().get('locale')
      languages = localeCookie?.value ? [localeCookie.value] : []
    
      if (!languages.length) {
        const negotiatorHeaders: Record<string, string> = {}
        headers().forEach((value, key) => (negotiatorHeaders[key] = value))
        languages = new Negotiator({ headers: negotiatorHeaders }).languages()
      }
    
      if (!Array.isArray(languages) || languages.length === 0 || !languages.every(lang => typeof lang === 'string' && /^[\w-]+$/.test(lang))) {
        console.error(`Invalid languages: ${languages}`)
        languages = [i18n.defaultLocale]
      }
    
      const matchedLocale = match(languages, locales, i18n.defaultLocale) as Locale
      return matchedLocale
    }
    
  2. Update the ActivateForm component:
    Ensure that the locale is set based on the user's selection and not forcibly changed.

    const ActivateForm = () => {
      const { t } = useTranslation()
      const { locale, setLocaleOnClient } = useContext(I18n)
      const searchParams = useSearchParams()
      const workspaceID = searchParams.get('workspace_id')
      const email = searchParams.get('email')
      const token = searchParams.get('token')
    
      const [name, setName] = useState('')
      const [password, setPassword] = useState('')
      const [timezone, setTimezone] = useState('Asia/Shanghai')
      const [language, setLanguage] = useState(locale)
      const [showSuccess, setShowSuccess] = useState(false)
    
      const handleActivate = useCallback(async () => {
        if (!valid())
          return
        try {
          await activateMember({
            url: '/activate',
            body: {
              workspace_id: workspaceID,
              email,
              token,
              name,
              password,
              interface_language: language,
              timezone,
            },
          })
          setLocaleOnClient(language, false)
          setShowSuccess(true)
        }
        catch {
          recheck()
        }
      }, [email, language, name, password, recheck, setLocaleOnClient, timezone, token, valid, workspaceID])
    
      return (
        // ... rest of the component
      )
    }
    
  3. Ensure correct client-side locale setting:
    Use the setLocaleOnClient function from the web/i18n/index.ts file to set the locale on the client side correctly.

    import Cookies from 'js-cookie'
    
    import { changeLanguage } from '@/i18n/i18next-config'
    import { LOCALE_COOKIE_NAME } from '@/config'
    import { LanguagesSupported } from '@/i18n/language'
    
    export const i18n = {
      defaultLocale: 'en-US',
      locales: LanguagesSupported,
    } as const
    
    export type Locale = typeof i18n['locales'][number]
    
    export const setLocaleOnClient = (locale: Locale, reloadPage = true) => {
      Cookies.set(LOCALE_COOKIE_NAME, locale)
      changeLanguage(locale)
      reloadPage && location.reload()
    }
    
    export const getLocaleOnClient = (): Locale => {
      return Cookies.get(LOCALE_COOKIE_NAME) as Locale || i18n.defaultLocale
    }
    

By making these changes, the browser's preferred locale will be consistently respected during activation and login [1][2][3].

To continue talking to Dosu, mention @dosu.

@dosubot[bot] commented on GitHub (Jun 15, 2024): <!-- Answer --> To ensure that the browser's preferred locale is consistently respected during activation and login, you need to modify the locale handling logic in the code. Here are the steps to address the issue: 1. **Modify the `initI18next` function**: Ensure that the locale is not forcibly changed to `zh-Hans` and instead use the locale determined by the `getLocaleOnServer` function. ```ts import { cookies, headers } from 'next/headers' import Negotiator from 'negotiator' import { match } from '@formatjs/intl-localematcher' import { createInstance } from 'i18next' import resourcesToBackend from 'i18next-resources-to-backend' import { initReactI18next } from 'react-i18next/initReactI18next' import { i18n } from '.' import type { Locale } from '.' const initI18next = async (lng: Locale, ns: string) => { const i18nInstance = createInstance() await i18nInstance .use(initReactI18next) .use(resourcesToBackend((language: string, namespace: string) => import(`./${language}/${namespace}.ts`))) .init({ lng, // Use the locale directly without forcing 'zh-Hans' ns, fallbackLng: 'en-US', }) return i18nInstance } export async function useTranslation(lng: Locale, ns = '', options: Record<string, any> = {}) { const i18nextInstance = await initI18next(lng, ns) return { t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix), i18n: i18nextInstance, } } export const getLocaleOnServer = (): Locale => { const locales: string[] = i18n.locales let languages: string[] | undefined const localeCookie = cookies().get('locale') languages = localeCookie?.value ? [localeCookie.value] : [] if (!languages.length) { const negotiatorHeaders: Record<string, string> = {} headers().forEach((value, key) => (negotiatorHeaders[key] = value)) languages = new Negotiator({ headers: negotiatorHeaders }).languages() } if (!Array.isArray(languages) || languages.length === 0 || !languages.every(lang => typeof lang === 'string' && /^[\w-]+$/.test(lang))) { console.error(`Invalid languages: ${languages}`) languages = [i18n.defaultLocale] } const matchedLocale = match(languages, locales, i18n.defaultLocale) as Locale return matchedLocale } ``` 2. **Update the `ActivateForm` component**: Ensure that the locale is set based on the user's selection and not forcibly changed. ```tsx const ActivateForm = () => { const { t } = useTranslation() const { locale, setLocaleOnClient } = useContext(I18n) const searchParams = useSearchParams() const workspaceID = searchParams.get('workspace_id') const email = searchParams.get('email') const token = searchParams.get('token') const [name, setName] = useState('') const [password, setPassword] = useState('') const [timezone, setTimezone] = useState('Asia/Shanghai') const [language, setLanguage] = useState(locale) const [showSuccess, setShowSuccess] = useState(false) const handleActivate = useCallback(async () => { if (!valid()) return try { await activateMember({ url: '/activate', body: { workspace_id: workspaceID, email, token, name, password, interface_language: language, timezone, }, }) setLocaleOnClient(language, false) setShowSuccess(true) } catch { recheck() } }, [email, language, name, password, recheck, setLocaleOnClient, timezone, token, valid, workspaceID]) return ( // ... rest of the component ) } ``` 3. **Ensure correct client-side locale setting**: Use the `setLocaleOnClient` function from the `web/i18n/index.ts` file to set the locale on the client side correctly. ```ts import Cookies from 'js-cookie' import { changeLanguage } from '@/i18n/i18next-config' import { LOCALE_COOKIE_NAME } from '@/config' import { LanguagesSupported } from '@/i18n/language' export const i18n = { defaultLocale: 'en-US', locales: LanguagesSupported, } as const export type Locale = typeof i18n['locales'][number] export const setLocaleOnClient = (locale: Locale, reloadPage = true) => { Cookies.set(LOCALE_COOKIE_NAME, locale) changeLanguage(locale) reloadPage && location.reload() } export const getLocaleOnClient = (): Locale => { return Cookies.get(LOCALE_COOKIE_NAME) as Locale || i18n.defaultLocale } ``` By making these changes, the browser's preferred locale will be consistently respected during activation and login <sup>[[1]](https://github.com/langgenius/dify/blob/main/web/i18n/server.ts)</sup><sup>[[2]](https://github.com/langgenius/dify/blob/main/web/app/activate/activateForm.tsx)</sup><sup>[[3]](https://github.com/langgenius/dify/blob/main/web/i18n/index.ts)</sup>. <!-- Dosu Comment Footer --> *To continue talking to [Dosu](https://dosu.dev), mention @dosu.*
Author
Owner

@kurokobo commented on GitHub (Jun 15, 2024):

I have updated my OP now that I have a more accurate reproduction steps.

@kurokobo commented on GitHub (Jun 15, 2024): I have updated my OP now that I have a more accurate reproduction steps.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langgenius/dify#4110