[GH-ISSUE #1246] Does better-Auth support i18n? #17287

Closed
opened 2026-04-15 15:22:21 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @bigOK666 on GitHub (Jan 19, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1246

I just read through the documentation and am very excited about what betterauth provides. And I want to try it with my nextjs project.

But I didn't find i18n support. Is there any document/examples showing Betterauth with i18n?

Originally created by @bigOK666 on GitHub (Jan 19, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1246 I just read through the documentation and am very excited about what betterauth provides. And I want to try it with my nextjs project. But I didn't find i18n support. Is there any document/examples showing Betterauth with i18n?
GiteaMirror added the locked label 2026-04-15 15:22:21 -05:00
Author
Owner

@ping-maxwell commented on GitHub (Feb 10, 2025):

There isn't built-in support for i18n.
However, most cases where you have to expose data from Better Auth and display it to the end users are likely error messages.
In Better Auth, there is a way to see all of the possible error messages, and from there you could implement translations for i18n for each message.

Each (internal) plugin that has error messages, should expose an $ERROR_CODES, which you can then utilize and translate accordingly.

Image

Keep in mind that this requires you to initialize the plugin first, before getting the codes.
So I recommend initializing the plugin outside of the plugins array, then getting each code, then also use the variable for the plugin you initialized in the plugin array.

<!-- gh-comment-id:2646868290 --> @ping-maxwell commented on GitHub (Feb 10, 2025): There isn't built-in support for i18n. However, most cases where you have to expose data from Better Auth and display it to the end users are likely error messages. In Better Auth, there is a way to see all of the possible error messages, and from there you could implement translations for i18n for each message. Each (internal) plugin that has error messages, should expose an `$ERROR_CODES`, which you can then utilize and translate accordingly. <img width="659" alt="Image" src="https://github.com/user-attachments/assets/6b0082d2-1eff-4cd7-a112-2890f6f8b2e2" /> Keep in mind that this requires you to initialize the plugin first, before getting the codes. So I recommend initializing the plugin outside of the plugins array, then getting each code, then also use the variable for the plugin you initialized in the plugin array.
Author
Owner

@hunter-nl commented on GitHub (Feb 20, 2025):

I've tried to let it work with next-intl (even with v4). For SEO it is better to have the pathnames translated also. Like /sign-up for en-US and /aanmelden for nl-NL language. But this is as far I can see not possible with better-auth. It use hardcoded pathnames and it is not working nicely with the middleware to handle this. If someone has a solution for it, please let me know. better-auth is great as authentication layer.

<!-- gh-comment-id:2672141492 --> @hunter-nl commented on GitHub (Feb 20, 2025): I've tried to let it work with next-intl (even with v4). For SEO it is better to have the pathnames translated also. Like /sign-up for en-US and /aanmelden for nl-NL language. But this is as far I can see not possible with better-auth. It use hardcoded pathnames and it is not working nicely with the middleware to handle this. If someone has a solution for it, please let me know. better-auth is great as authentication layer.
Author
Owner

@ping-maxwell commented on GitHub (Feb 20, 2025):

BTW, for anyone reading this, the information I provided previously was misleading, as I didn't know at the time.

But you can access the error codes straight from the auth instance like so:

auth.$ERROR_CODES
<!-- gh-comment-id:2672150335 --> @ping-maxwell commented on GitHub (Feb 20, 2025): BTW, for anyone reading this, the information I provided previously was misleading, as I didn't know at the time. But you can access the error codes straight from the `auth` instance like so: ```ts auth.$ERROR_CODES ```
Author
Owner

@ping-maxwell commented on GitHub (Feb 20, 2025):

I've tried to let it work with next-intl (even with v4). For SEO it is better to have the pathnames translated also. Like /sign-up for en-US and /aanmelden for nl-NL language. But this is as far I can see not possible with better-auth. It use hardcoded pathnames and it is not working nicely with the middleware to handle this. If someone has a solution for it, please let me know. better-auth is great as authentication layer.

I'm pretty certain most endpoints by better-auth wouldn't be accessed by the end-user.
Something like the sign-up page would be a page made by yourselves, which can be translated.
However the auth calls behind the scenes wouldn't be seen, or at least, wouldn't matter to the end consumers.

<!-- gh-comment-id:2672155416 --> @ping-maxwell commented on GitHub (Feb 20, 2025): > I've tried to let it work with next-intl (even with v4). For SEO it is better to have the pathnames translated also. Like /sign-up for en-US and /aanmelden for nl-NL language. But this is as far I can see not possible with better-auth. It use hardcoded pathnames and it is not working nicely with the middleware to handle this. If someone has a solution for it, please let me know. better-auth is great as authentication layer. I'm pretty certain most endpoints by better-auth wouldn't be accessed by the end-user. Something like the sign-up page would be a page made by yourselves, which can be translated. However the auth calls behind the scenes wouldn't be seen, or at least, wouldn't matter to the end consumers.
Author
Owner

@sd44 commented on GitHub (Mar 10, 2025):

I'm new to web development. Could someone provide a code example for translating $ERROR_CODES into another language? Thank you.

<!-- gh-comment-id:2709307919 --> @sd44 commented on GitHub (Mar 10, 2025): I'm new to web development. Could someone provide a code example for translating $ERROR_CODES into another language? Thank you.
Author
Owner

@ping-maxwell commented on GitHub (Mar 10, 2025):

I'm new to web development. Could someone provide a code example for translating $ERROR_CODES into another language? Thank you.

Most cases we use libraries to help us make translations / i18n development easier.
Take a look at this for Nextjs translations lib: https://next-intl.dev/docs/getting-started

But you still have to do the translating by hand at the end of the day.
So using something like Google translate is probably needed.

<!-- gh-comment-id:2709386560 --> @ping-maxwell commented on GitHub (Mar 10, 2025): > I'm new to web development. Could someone provide a code example for translating $ERROR_CODES into another language? Thank you. Most cases we use libraries to help us make translations / i18n development easier. Take a look at this for Nextjs translations lib: https://next-intl.dev/docs/getting-started But you still have to do the translating by hand at the end of the day. So using something like Google translate is probably needed.
Author
Owner

@sd44 commented on GitHub (Mar 10, 2025):

Thanks very much. I found the solution: https://better-auth.vercel.app/docs/concepts/client#error-codes

ex: For Simplified Chinese user,can do this in auth-client.ts

type ErrorTypes = Partial<
  Record<
    keyof typeof client.$ERROR_CODES,
    {
      en: string;
      'zh-hans': string;
    }
  >
>;

const errorCodes = {
  INVALID_PASSWORD: { en: 'invalid password', 'zh-hans': '密码错误' },
  INVALID_EMAIL: { en: 'invalid email', 'zh-hans': 'email错误' },
  INVALID_TOKEN: { en: 'invalid token', 'zh-hans': 'token错误' },
  USER_NOT_FOUND: { en: 'User not found', 'zh-hans': '用户不存在' },
  FAILED_TO_CREATE_USER: { en: 'Failed to create user', 'zh-hans': '创建用户失败' },
  FAILED_TO_CREATE_SESSION: { en: 'Failed to create session', 'zh-hans': '创建会话失败' },
  FAILED_TO_UPDATE_USER: { en: 'Failed to update user', 'zh-hans': '更新用户失败' },
  FAILED_TO_GET_SESSION: { en: 'Failed to get session', 'zh-hans': '获取会话失败' },
  INVALID_EMAIL_OR_PASSWORD: { en: 'Invalid email or password', 'zh-hans': '邮箱或密码无效' },
  SOCIAL_ACCOUNT_ALREADY_LINKED: {
    en: 'Social account already linked',
    'zh-hans': '社交账号已关联',
  },
  PROVIDER_NOT_FOUND: { en: 'Provider not found', 'zh-hans': '未找到提供商' },
  ID_TOKEN_NOT_SUPPORTED: { en: 'id_token not supported', 'zh-hans': '不支持id_token' },
  FAILED_TO_GET_USER_INFO: { en: 'Failed to get user info', 'zh-hans': '获取用户信息失败' },
  USER_EMAIL_NOT_FOUND: { en: 'User email not found', 'zh-hans': '未找到用户邮箱' },
  EMAIL_NOT_VERIFIED: { en: 'Email not verified', 'zh-hans': '邮箱未验证' },
  PASSWORD_TOO_SHORT: { en: 'Password too short', 'zh-hans': '密码太短' },
  PASSWORD_TOO_LONG: { en: 'Password too long', 'zh-hans': '密码太长' },
  USER_ALREADY_EXISTS: { en: 'User already exists', 'zh-hans': '该账户已存在' },
  EMAIL_CAN_NOT_BE_UPDATED: { en: 'Email can not be updated', 'zh-hans': '邮箱无法更新' },
  CREDENTIAL_ACCOUNT_NOT_FOUND: { en: 'Credential account not found', 'zh-hans': '凭据账号未找到' },
  SESSION_EXPIRED: {
    en: 'Session expired. Re-authenticate to perform this action.',
    'zh-hans': '会话过期。请重新进行身份验证以执行此操作。',
  },
  FAILED_TO_UNLINK_LAST_ACCOUNT: {
    en: "You can't unlink your last account",
    'zh-hans': '您无法取消关联您的最后一个帐户',
  },
  ACCOUNT_NOT_FOUND: { en: 'Account not found', 'zh-hans': '帐户不存在' },
} satisfies ErrorTypes;

export const getErrorMessage = (code: string, lang: 'en' | 'zh-hans' = 'zh-hans') => {
  if (code in errorCodes) {
    return errorCodes[code as keyof typeof errorCodes][lang];
  }
  return code;
};

then as suggested in the Better Auth document

const { error } = await authClient.signUp.email({
	email: "user@email.com",
	password: "password",
	name: "User",
});
if(error?.code){
    alert(getErrorMessage(error.code), "en");
}

or in signin or up:

                fetchOptions: {
                  onResponse: () => {
                    setLoading(false);
                  },
                  onRequest: () => {
                    setLoading(true);
                  },
                  onError: (ctx) => {
                    {
                      /* console.log(ctx.error.message); */
                    }
                    const err = ctx.error.code;
                    toast.error(getErrorMessage(err, 'zh-hans'));
                  },
                  onSuccess: async () => {
                    router.push('/dashboard');
                  },
                },

<!-- gh-comment-id:2709487445 --> @sd44 commented on GitHub (Mar 10, 2025): Thanks very much. I found the solution: https://better-auth.vercel.app/docs/concepts/client#error-codes ex: For Simplified Chinese user,can do this in `auth-client.ts` ``` type ErrorTypes = Partial< Record< keyof typeof client.$ERROR_CODES, { en: string; 'zh-hans': string; } > >; const errorCodes = { INVALID_PASSWORD: { en: 'invalid password', 'zh-hans': '密码错误' }, INVALID_EMAIL: { en: 'invalid email', 'zh-hans': 'email错误' }, INVALID_TOKEN: { en: 'invalid token', 'zh-hans': 'token错误' }, USER_NOT_FOUND: { en: 'User not found', 'zh-hans': '用户不存在' }, FAILED_TO_CREATE_USER: { en: 'Failed to create user', 'zh-hans': '创建用户失败' }, FAILED_TO_CREATE_SESSION: { en: 'Failed to create session', 'zh-hans': '创建会话失败' }, FAILED_TO_UPDATE_USER: { en: 'Failed to update user', 'zh-hans': '更新用户失败' }, FAILED_TO_GET_SESSION: { en: 'Failed to get session', 'zh-hans': '获取会话失败' }, INVALID_EMAIL_OR_PASSWORD: { en: 'Invalid email or password', 'zh-hans': '邮箱或密码无效' }, SOCIAL_ACCOUNT_ALREADY_LINKED: { en: 'Social account already linked', 'zh-hans': '社交账号已关联', }, PROVIDER_NOT_FOUND: { en: 'Provider not found', 'zh-hans': '未找到提供商' }, ID_TOKEN_NOT_SUPPORTED: { en: 'id_token not supported', 'zh-hans': '不支持id_token' }, FAILED_TO_GET_USER_INFO: { en: 'Failed to get user info', 'zh-hans': '获取用户信息失败' }, USER_EMAIL_NOT_FOUND: { en: 'User email not found', 'zh-hans': '未找到用户邮箱' }, EMAIL_NOT_VERIFIED: { en: 'Email not verified', 'zh-hans': '邮箱未验证' }, PASSWORD_TOO_SHORT: { en: 'Password too short', 'zh-hans': '密码太短' }, PASSWORD_TOO_LONG: { en: 'Password too long', 'zh-hans': '密码太长' }, USER_ALREADY_EXISTS: { en: 'User already exists', 'zh-hans': '该账户已存在' }, EMAIL_CAN_NOT_BE_UPDATED: { en: 'Email can not be updated', 'zh-hans': '邮箱无法更新' }, CREDENTIAL_ACCOUNT_NOT_FOUND: { en: 'Credential account not found', 'zh-hans': '凭据账号未找到' }, SESSION_EXPIRED: { en: 'Session expired. Re-authenticate to perform this action.', 'zh-hans': '会话过期。请重新进行身份验证以执行此操作。', }, FAILED_TO_UNLINK_LAST_ACCOUNT: { en: "You can't unlink your last account", 'zh-hans': '您无法取消关联您的最后一个帐户', }, ACCOUNT_NOT_FOUND: { en: 'Account not found', 'zh-hans': '帐户不存在' }, } satisfies ErrorTypes; export const getErrorMessage = (code: string, lang: 'en' | 'zh-hans' = 'zh-hans') => { if (code in errorCodes) { return errorCodes[code as keyof typeof errorCodes][lang]; } return code; }; ``` then as suggested in the Better Auth document ``` const { error } = await authClient.signUp.email({ email: "user@email.com", password: "password", name: "User", }); if(error?.code){ alert(getErrorMessage(error.code), "en"); } ``` or in signin or up: ``` fetchOptions: { onResponse: () => { setLoading(false); }, onRequest: () => { setLoading(true); }, onError: (ctx) => { { /* console.log(ctx.error.message); */ } const err = ctx.error.code; toast.error(getErrorMessage(err, 'zh-hans')); }, onSuccess: async () => { router.push('/dashboard'); }, }, ```
Author
Owner

@tanosugi commented on GitHub (Jun 8, 2025):

@ping-maxwell
I made pull request as #2917 . but two failing on request as follows. Do I have to modify additionally to be reviewed? I understand your team alocate time for review based on priority.

Image

<!-- gh-comment-id:2954033563 --> @tanosugi commented on GitHub (Jun 8, 2025): @ping-maxwell I made pull request as #2917 . but two failing on request as follows. Do I have to modify additionally to be reviewed? I understand your team alocate time for review based on priority. ![Image](https://github.com/user-attachments/assets/083b8d04-1f28-46d2-a0e3-670a974edd77)
Author
Owner

@ping-maxwell commented on GitHub (Aug 11, 2025):

Hey guys, there is a community package for localization:
https://github.com/marcellosso/better-auth-localization

I'm closing this as that plugin looks like a solid solution.

<!-- gh-comment-id:3176503945 --> @ping-maxwell commented on GitHub (Aug 11, 2025): Hey guys, there is a community package for localization: https://github.com/marcellosso/better-auth-localization I'm closing this as that plugin looks like a solid solution.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#17287