@better-auth/i18n 1.5.0-beta.12 → 1.5.0-beta.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { t as i18n } from "./index-DFgwTnpr.mjs";
1
+ import "./types-m1nrvPWg.mjs";
2
+ import { i18n } from "./index.mjs";
2
3
 
3
4
  //#region src/client.d.ts
4
5
  /**
package/dist/index.d.mts CHANGED
@@ -1,2 +1,55 @@
1
- import { i as TranslationDictionary, n as I18nOptions, r as LocaleDetectionStrategy, t as i18n } from "./index-DFgwTnpr.mjs";
2
- export { I18nOptions, LocaleDetectionStrategy, TranslationDictionary, i18n };
1
+ import { n as LocaleDetectionStrategy, r as TranslationDictionary, t as I18nOptions } from "./types-m1nrvPWg.mjs";
2
+ import * as better_call0 from "better-call";
3
+ import { GenericEndpointContext } from "@better-auth/core";
4
+
5
+ //#region src/index.d.ts
6
+ declare module "@better-auth/core" {
7
+ interface BetterAuthPluginRegistry<AuthOptions, Options> {
8
+ i18n: {
9
+ creator: typeof i18n;
10
+ };
11
+ }
12
+ }
13
+ /**
14
+ * i18n plugin for Better Auth
15
+ *
16
+ * Translates error messages based on detected locale.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { betterAuth } from "better-auth";
21
+ * import { i18n } from "@better-auth/i18n";
22
+ *
23
+ * export const auth = betterAuth({
24
+ * plugins: [
25
+ * i18n({
26
+ * translations: {
27
+ * en: { USER_NOT_FOUND: "User not found" },
28
+ * fr: { USER_NOT_FOUND: "Utilisateur non trouvé" },
29
+ * },
30
+ * detection: ["header", "cookie"],
31
+ * }),
32
+ * ],
33
+ * });
34
+ * ```
35
+ */
36
+ declare const i18n: <Locales extends string[]>(options: I18nOptions<Locales>) => {
37
+ id: "i18n";
38
+ hooks: {
39
+ after: {
40
+ matcher: () => true;
41
+ handler: (inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>;
42
+ }[];
43
+ };
44
+ options: {
45
+ translations: { [Locale in Locales[number]]: TranslationDictionary };
46
+ defaultLocale: Locales[number];
47
+ detection: LocaleDetectionStrategy[];
48
+ localeCookie: string;
49
+ userLocaleField: string;
50
+ getLocale?: ((ctx: GenericEndpointContext) => Locales[number] | Promise<Locales[number] | null> | null) | undefined;
51
+ };
52
+ };
53
+ //#endregion
54
+ export { type I18nOptions, type LocaleDetectionStrategy, type TranslationDictionary, i18n };
55
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -53,15 +53,15 @@ const i18n = (options) => {
53
53
  userLocaleField: "locale",
54
54
  ...options
55
55
  };
56
- async function detectLocale(request, headers, ctx) {
56
+ async function detectLocale(ctx) {
57
57
  for (const strategy of opts.detection) {
58
58
  let locale = null;
59
59
  switch (strategy) {
60
60
  case "header":
61
- locale = parseAcceptLanguage(headers?.get("Accept-Language") ?? null).find((l) => availableLocales.includes(l)) ?? null;
61
+ locale = parseAcceptLanguage(ctx.headers?.get("Accept-Language") ?? null).find((l) => availableLocales.includes(l)) ?? null;
62
62
  break;
63
63
  case "cookie": {
64
- const cookieHeader = headers?.get("Cookie");
64
+ const cookieHeader = ctx.headers?.get("Cookie");
65
65
  if (cookieHeader) {
66
66
  const cookieLocale = parseCookies(cookieHeader).get(opts.localeCookie);
67
67
  if (cookieLocale && availableLocales.includes(cookieLocale)) locale = cookieLocale;
@@ -69,14 +69,14 @@ const i18n = (options) => {
69
69
  break;
70
70
  }
71
71
  case "session":
72
- if (ctx.session?.user) {
73
- const userLocale = ctx.session.user[opts.userLocaleField];
72
+ if (ctx.context.session?.user) {
73
+ const userLocale = ctx.context.session.user[opts.userLocaleField];
74
74
  if (typeof userLocale === "string" && availableLocales.includes(userLocale)) locale = userLocale;
75
75
  }
76
76
  break;
77
77
  case "callback":
78
- if (opts.getLocale && request) {
79
- const callbackLocale = await opts.getLocale(request, ctx);
78
+ if (opts.getLocale) {
79
+ const callbackLocale = await opts.getLocale(ctx);
80
80
  if (callbackLocale && availableLocales.includes(callbackLocale)) locale = callbackLocale;
81
81
  }
82
82
  break;
@@ -94,7 +94,7 @@ const i18n = (options) => {
94
94
  if (!isAPIError(returned)) return;
95
95
  const errorCode = returned.body?.code;
96
96
  if (typeof errorCode !== "string") return;
97
- const locale = await detectLocale(ctx.request, ctx.headers, ctx.context);
97
+ const locale = await detectLocale(ctx);
98
98
  const translation = opts.translations[locale]?.[errorCode];
99
99
  if (!translation) return;
100
100
  throw new APIError(returned.status, {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { AuthContext, BetterAuthPlugin } from \"@better-auth/core\";\nimport { APIError, createAuthMiddleware, isAPIError } from \"better-auth/api\";\nimport { parseCookies } from \"better-auth/cookies\";\nimport type { I18nOptions, LocaleDetectionStrategy } from \"./types\";\n\nexport type {\n\tI18nOptions,\n\tLocaleDetectionStrategy,\n\tTranslationDictionary,\n} from \"./types\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\ti18n: {\n\t\t\tcreator: typeof i18n;\n\t\t};\n\t}\n}\n\n/**\n * Parse Accept-Language header and return locales sorted by quality\n */\nfunction parseAcceptLanguage(header: string | null): string[] {\n\tif (!header) return [];\n\treturn header\n\t\t.split(\",\")\n\t\t.map((part) => {\n\t\t\tconst [localeStr, quality = \"q=1\"] = part.trim().split(\";\");\n\t\t\tconst q = Number.parseFloat(quality.replace(\"q=\", \"\"));\n\t\t\t// Get base locale (e.g., \"en\" from \"en-US\")\n\t\t\tconst locale = localeStr?.trim().split(\"-\")[0] ?? \"\";\n\t\t\treturn { locale, q };\n\t\t})\n\t\t.filter((item) => item.locale.length > 0)\n\t\t.sort((a, b) => b.q - a.q)\n\t\t.map((item) => item.locale);\n}\n\n/**\n * i18n plugin for Better Auth\n *\n * Translates error messages based on detected locale.\n *\n * @example\n * ```ts\n * import { betterAuth } from \"better-auth\";\n * import { i18n } from \"@better-auth/i18n\";\n *\n * export const auth = betterAuth({\n * plugins: [\n * i18n({\n * translations: {\n * en: { USER_NOT_FOUND: \"User not found\" },\n * fr: { USER_NOT_FOUND: \"Utilisateur non trouvé\" },\n * },\n * detection: [\"header\", \"cookie\"],\n * }),\n * ],\n * });\n * ```\n */\nexport const i18n = <Locales extends string[]>(\n\toptions: I18nOptions<Locales>,\n) => {\n\tconst availableLocales = Object.keys(options.translations);\n\n\tlet defaultLocale: Locales[number];\n\tif (\n\t\toptions.defaultLocale &&\n\t\tavailableLocales.includes(options.defaultLocale)\n\t) {\n\t\tdefaultLocale = options.defaultLocale;\n\t} else if (availableLocales.includes(\"en\")) {\n\t\tdefaultLocale = \"en\" as Locales[number];\n\t} else if (availableLocales.length > 0) {\n\t\tdefaultLocale = availableLocales[0] as Locales[number];\n\t} else {\n\t\tthrow new Error(\n\t\t\t\"i18n plugin: translations object is empty. At least one locale must be provided.\",\n\t\t);\n\t}\n\n\tconst opts = {\n\t\tdefaultLocale,\n\t\tdetection: [\"header\"] as LocaleDetectionStrategy[],\n\t\tlocaleCookie: \"locale\",\n\t\tuserLocaleField: \"locale\",\n\t\t...options,\n\t};\n\n\tasync function detectLocale(\n\t\trequest: Request | undefined,\n\t\theaders: Headers | undefined,\n\t\tctx: AuthContext,\n\t): Promise<Locales[number]> {\n\t\tfor (const strategy of opts.detection) {\n\t\t\tlet locale: Locales[number] | null = null;\n\n\t\t\tswitch (strategy) {\n\t\t\t\tcase \"header\": {\n\t\t\t\t\tconst acceptLang = headers?.get(\"Accept-Language\") ?? null;\n\t\t\t\t\tconst preferred = parseAcceptLanguage(acceptLang);\n\t\t\t\t\tlocale = preferred.find((l) => availableLocales.includes(l)) ?? null;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"cookie\": {\n\t\t\t\t\tconst cookieHeader = headers?.get(\"Cookie\");\n\t\t\t\t\tif (cookieHeader) {\n\t\t\t\t\t\tconst cookies = parseCookies(cookieHeader);\n\t\t\t\t\t\tconst cookieLocale = cookies.get(opts.localeCookie);\n\t\t\t\t\t\tif (cookieLocale && availableLocales.includes(cookieLocale)) {\n\t\t\t\t\t\t\tlocale = cookieLocale;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"session\": {\n\t\t\t\t\tif (ctx.session?.user) {\n\t\t\t\t\t\tconst userLocale = (ctx.session.user as Record<string, unknown>)[\n\t\t\t\t\t\t\topts.userLocaleField\n\t\t\t\t\t\t];\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof userLocale === \"string\" &&\n\t\t\t\t\t\t\tavailableLocales.includes(userLocale)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tlocale = userLocale;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"callback\": {\n\t\t\t\t\tif (opts.getLocale && request) {\n\t\t\t\t\t\tconst callbackLocale = await opts.getLocale(request, ctx);\n\t\t\t\t\t\tif (callbackLocale && availableLocales.includes(callbackLocale)) {\n\t\t\t\t\t\t\tlocale = callbackLocale;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (locale) return locale;\n\t\t}\n\n\t\treturn opts.defaultLocale;\n\t}\n\n\treturn {\n\t\tid: \"i18n\",\n\n\t\thooks: {\n\t\t\tafter: [\n\t\t\t\t{\n\t\t\t\t\tmatcher: () => true,\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst returned = ctx.context.returned;\n\n\t\t\t\t\t\tif (!isAPIError(returned)) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst errorCode = (returned.body as Record<string, unknown>)?.code;\n\t\t\t\t\t\tif (typeof errorCode !== \"string\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst locale = await detectLocale(\n\t\t\t\t\t\t\tctx.request,\n\t\t\t\t\t\t\tctx.headers,\n\t\t\t\t\t\t\tctx.context,\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst translation = opts.translations[locale]?.[errorCode];\n\n\t\t\t\t\t\tif (!translation) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow new APIError(returned.status, {\n\t\t\t\t\t\t\tcode: errorCode,\n\t\t\t\t\t\t\tmessage: translation,\n\t\t\t\t\t\t\toriginalMessage: returned.message,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\n\t\toptions: opts,\n\t} satisfies BetterAuthPlugin;\n};\n"],"mappings":";;;;;;;AAsBA,SAAS,oBAAoB,QAAiC;AAC7D,KAAI,CAAC,OAAQ,QAAO,EAAE;AACtB,QAAO,OACL,MAAM,IAAI,CACV,KAAK,SAAS;EACd,MAAM,CAAC,WAAW,UAAU,SAAS,KAAK,MAAM,CAAC,MAAM,IAAI;EAC3D,MAAM,IAAI,OAAO,WAAW,QAAQ,QAAQ,MAAM,GAAG,CAAC;AAGtD,SAAO;GAAE,QADM,WAAW,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM;GACjC;GAAG;GACnB,CACD,QAAQ,SAAS,KAAK,OAAO,SAAS,EAAE,CACxC,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,EAAE,CACzB,KAAK,SAAS,KAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;AA0B7B,MAAa,QACZ,YACI;CACJ,MAAM,mBAAmB,OAAO,KAAK,QAAQ,aAAa;CAE1D,IAAI;AACJ,KACC,QAAQ,iBACR,iBAAiB,SAAS,QAAQ,cAAc,CAEhD,iBAAgB,QAAQ;UACd,iBAAiB,SAAS,KAAK,CACzC,iBAAgB;UACN,iBAAiB,SAAS,EACpC,iBAAgB,iBAAiB;KAEjC,OAAM,IAAI,MACT,mFACA;CAGF,MAAM,OAAO;EACZ;EACA,WAAW,CAAC,SAAS;EACrB,cAAc;EACd,iBAAiB;EACjB,GAAG;EACH;CAED,eAAe,aACd,SACA,SACA,KAC2B;AAC3B,OAAK,MAAM,YAAY,KAAK,WAAW;GACtC,IAAI,SAAiC;AAErC,WAAQ,UAAR;IACC,KAAK;AAGJ,cADkB,oBADC,SAAS,IAAI,kBAAkB,IAAI,KACL,CAC9B,MAAM,MAAM,iBAAiB,SAAS,EAAE,CAAC,IAAI;AAChE;IAGD,KAAK,UAAU;KACd,MAAM,eAAe,SAAS,IAAI,SAAS;AAC3C,SAAI,cAAc;MAEjB,MAAM,eADU,aAAa,aAAa,CACb,IAAI,KAAK,aAAa;AACnD,UAAI,gBAAgB,iBAAiB,SAAS,aAAa,CAC1D,UAAS;;AAGX;;IAGD,KAAK;AACJ,SAAI,IAAI,SAAS,MAAM;MACtB,MAAM,aAAc,IAAI,QAAQ,KAC/B,KAAK;AAEN,UACC,OAAO,eAAe,YACtB,iBAAiB,SAAS,WAAW,CAErC,UAAS;;AAGX;IAGD,KAAK;AACJ,SAAI,KAAK,aAAa,SAAS;MAC9B,MAAM,iBAAiB,MAAM,KAAK,UAAU,SAAS,IAAI;AACzD,UAAI,kBAAkB,iBAAiB,SAAS,eAAe,CAC9D,UAAS;;AAGX;;AAIF,OAAI,OAAQ,QAAO;;AAGpB,SAAO,KAAK;;AAGb,QAAO;EACN,IAAI;EAEJ,OAAO,EACN,OAAO,CACN;GACC,eAAe;GACf,SAAS,qBAAqB,OAAO,QAAQ;IAC5C,MAAM,WAAW,IAAI,QAAQ;AAE7B,QAAI,CAAC,WAAW,SAAS,CACxB;IAGD,MAAM,YAAa,SAAS,MAAkC;AAC9D,QAAI,OAAO,cAAc,SACxB;IAGD,MAAM,SAAS,MAAM,aACpB,IAAI,SACJ,IAAI,SACJ,IAAI,QACJ;IAED,MAAM,cAAc,KAAK,aAAa,UAAU;AAEhD,QAAI,CAAC,YACJ;AAGD,UAAM,IAAI,SAAS,SAAS,QAAQ;KACnC,MAAM;KACN,SAAS;KACT,iBAAiB,SAAS;KAC1B,CAAC;KACD;GACF,CACD,EACD;EAED,SAAS;EACT"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type {\n\tBetterAuthPlugin,\n\tGenericEndpointContext,\n} from \"@better-auth/core\";\nimport { APIError, createAuthMiddleware, isAPIError } from \"better-auth/api\";\nimport { parseCookies } from \"better-auth/cookies\";\nimport type { I18nOptions, LocaleDetectionStrategy } from \"./types\";\n\nexport type {\n\tI18nOptions,\n\tLocaleDetectionStrategy,\n\tTranslationDictionary,\n} from \"./types\";\n\ndeclare module \"@better-auth/core\" {\n\tinterface BetterAuthPluginRegistry<AuthOptions, Options> {\n\t\ti18n: {\n\t\t\tcreator: typeof i18n;\n\t\t};\n\t}\n}\n\n/**\n * Parse Accept-Language header and return locales sorted by quality\n */\nfunction parseAcceptLanguage(header: string | null): string[] {\n\tif (!header) return [];\n\treturn header\n\t\t.split(\",\")\n\t\t.map((part) => {\n\t\t\tconst [localeStr, quality = \"q=1\"] = part.trim().split(\";\");\n\t\t\tconst q = Number.parseFloat(quality.replace(\"q=\", \"\"));\n\t\t\t// Get base locale (e.g., \"en\" from \"en-US\")\n\t\t\tconst locale = localeStr?.trim().split(\"-\")[0] ?? \"\";\n\t\t\treturn { locale, q };\n\t\t})\n\t\t.filter((item) => item.locale.length > 0)\n\t\t.sort((a, b) => b.q - a.q)\n\t\t.map((item) => item.locale);\n}\n\n/**\n * i18n plugin for Better Auth\n *\n * Translates error messages based on detected locale.\n *\n * @example\n * ```ts\n * import { betterAuth } from \"better-auth\";\n * import { i18n } from \"@better-auth/i18n\";\n *\n * export const auth = betterAuth({\n * plugins: [\n * i18n({\n * translations: {\n * en: { USER_NOT_FOUND: \"User not found\" },\n * fr: { USER_NOT_FOUND: \"Utilisateur non trouvé\" },\n * },\n * detection: [\"header\", \"cookie\"],\n * }),\n * ],\n * });\n * ```\n */\nexport const i18n = <Locales extends string[]>(\n\toptions: I18nOptions<Locales>,\n) => {\n\tconst availableLocales = Object.keys(options.translations);\n\n\tlet defaultLocale: Locales[number];\n\tif (\n\t\toptions.defaultLocale &&\n\t\tavailableLocales.includes(options.defaultLocale)\n\t) {\n\t\tdefaultLocale = options.defaultLocale;\n\t} else if (availableLocales.includes(\"en\")) {\n\t\tdefaultLocale = \"en\" as Locales[number];\n\t} else if (availableLocales.length > 0) {\n\t\tdefaultLocale = availableLocales[0] as Locales[number];\n\t} else {\n\t\tthrow new Error(\n\t\t\t\"i18n plugin: translations object is empty. At least one locale must be provided.\",\n\t\t);\n\t}\n\n\tconst opts = {\n\t\tdefaultLocale,\n\t\tdetection: [\"header\"] as LocaleDetectionStrategy[],\n\t\tlocaleCookie: \"locale\",\n\t\tuserLocaleField: \"locale\",\n\t\t...options,\n\t};\n\n\tasync function detectLocale(\n\t\tctx: GenericEndpointContext,\n\t): Promise<Locales[number]> {\n\t\tfor (const strategy of opts.detection) {\n\t\t\tlet locale: Locales[number] | null = null;\n\n\t\t\tswitch (strategy) {\n\t\t\t\tcase \"header\": {\n\t\t\t\t\tconst acceptLang = ctx.headers?.get(\"Accept-Language\") ?? null;\n\t\t\t\t\tconst preferred = parseAcceptLanguage(acceptLang);\n\t\t\t\t\tlocale = preferred.find((l) => availableLocales.includes(l)) ?? null;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"cookie\": {\n\t\t\t\t\tconst cookieHeader = ctx.headers?.get(\"Cookie\");\n\t\t\t\t\tif (cookieHeader) {\n\t\t\t\t\t\tconst cookies = parseCookies(cookieHeader);\n\t\t\t\t\t\tconst cookieLocale = cookies.get(opts.localeCookie);\n\t\t\t\t\t\tif (cookieLocale && availableLocales.includes(cookieLocale)) {\n\t\t\t\t\t\t\tlocale = cookieLocale;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"session\": {\n\t\t\t\t\tif (ctx.context.session?.user) {\n\t\t\t\t\t\tconst userLocale = (\n\t\t\t\t\t\t\tctx.context.session.user as Record<string, unknown>\n\t\t\t\t\t\t)[opts.userLocaleField];\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof userLocale === \"string\" &&\n\t\t\t\t\t\t\tavailableLocales.includes(userLocale)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tlocale = userLocale;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase \"callback\": {\n\t\t\t\t\tif (opts.getLocale) {\n\t\t\t\t\t\tconst callbackLocale = await opts.getLocale(ctx);\n\t\t\t\t\t\tif (callbackLocale && availableLocales.includes(callbackLocale)) {\n\t\t\t\t\t\t\tlocale = callbackLocale;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (locale) return locale;\n\t\t}\n\n\t\treturn opts.defaultLocale;\n\t}\n\n\treturn {\n\t\tid: \"i18n\",\n\t\thooks: {\n\t\t\tafter: [\n\t\t\t\t{\n\t\t\t\t\tmatcher: () => true,\n\t\t\t\t\thandler: createAuthMiddleware(async (ctx) => {\n\t\t\t\t\t\tconst returned = ctx.context.returned;\n\n\t\t\t\t\t\tif (!isAPIError(returned)) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst errorCode = (returned.body as Record<string, unknown>)?.code;\n\t\t\t\t\t\tif (typeof errorCode !== \"string\") {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst locale = await detectLocale(ctx);\n\n\t\t\t\t\t\tconst translation = opts.translations[locale]?.[errorCode];\n\n\t\t\t\t\t\tif (!translation) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthrow new APIError(returned.status, {\n\t\t\t\t\t\t\tcode: errorCode,\n\t\t\t\t\t\t\tmessage: translation,\n\t\t\t\t\t\t\toriginalMessage: returned.message,\n\t\t\t\t\t\t});\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t},\n\n\t\toptions: opts,\n\t} satisfies BetterAuthPlugin;\n};\n\nexport type * from \"./types\";\n"],"mappings":";;;;;;;AAyBA,SAAS,oBAAoB,QAAiC;AAC7D,KAAI,CAAC,OAAQ,QAAO,EAAE;AACtB,QAAO,OACL,MAAM,IAAI,CACV,KAAK,SAAS;EACd,MAAM,CAAC,WAAW,UAAU,SAAS,KAAK,MAAM,CAAC,MAAM,IAAI;EAC3D,MAAM,IAAI,OAAO,WAAW,QAAQ,QAAQ,MAAM,GAAG,CAAC;AAGtD,SAAO;GAAE,QADM,WAAW,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM;GACjC;GAAG;GACnB,CACD,QAAQ,SAAS,KAAK,OAAO,SAAS,EAAE,CACxC,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,EAAE,CACzB,KAAK,SAAS,KAAK,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;AA0B7B,MAAa,QACZ,YACI;CACJ,MAAM,mBAAmB,OAAO,KAAK,QAAQ,aAAa;CAE1D,IAAI;AACJ,KACC,QAAQ,iBACR,iBAAiB,SAAS,QAAQ,cAAc,CAEhD,iBAAgB,QAAQ;UACd,iBAAiB,SAAS,KAAK,CACzC,iBAAgB;UACN,iBAAiB,SAAS,EACpC,iBAAgB,iBAAiB;KAEjC,OAAM,IAAI,MACT,mFACA;CAGF,MAAM,OAAO;EACZ;EACA,WAAW,CAAC,SAAS;EACrB,cAAc;EACd,iBAAiB;EACjB,GAAG;EACH;CAED,eAAe,aACd,KAC2B;AAC3B,OAAK,MAAM,YAAY,KAAK,WAAW;GACtC,IAAI,SAAiC;AAErC,WAAQ,UAAR;IACC,KAAK;AAGJ,cADkB,oBADC,IAAI,SAAS,IAAI,kBAAkB,IAAI,KACT,CAC9B,MAAM,MAAM,iBAAiB,SAAS,EAAE,CAAC,IAAI;AAChE;IAGD,KAAK,UAAU;KACd,MAAM,eAAe,IAAI,SAAS,IAAI,SAAS;AAC/C,SAAI,cAAc;MAEjB,MAAM,eADU,aAAa,aAAa,CACb,IAAI,KAAK,aAAa;AACnD,UAAI,gBAAgB,iBAAiB,SAAS,aAAa,CAC1D,UAAS;;AAGX;;IAGD,KAAK;AACJ,SAAI,IAAI,QAAQ,SAAS,MAAM;MAC9B,MAAM,aACL,IAAI,QAAQ,QAAQ,KACnB,KAAK;AACP,UACC,OAAO,eAAe,YACtB,iBAAiB,SAAS,WAAW,CAErC,UAAS;;AAGX;IAGD,KAAK;AACJ,SAAI,KAAK,WAAW;MACnB,MAAM,iBAAiB,MAAM,KAAK,UAAU,IAAI;AAChD,UAAI,kBAAkB,iBAAiB,SAAS,eAAe,CAC9D,UAAS;;AAGX;;AAIF,OAAI,OAAQ,QAAO;;AAGpB,SAAO,KAAK;;AAGb,QAAO;EACN,IAAI;EACJ,OAAO,EACN,OAAO,CACN;GACC,eAAe;GACf,SAAS,qBAAqB,OAAO,QAAQ;IAC5C,MAAM,WAAW,IAAI,QAAQ;AAE7B,QAAI,CAAC,WAAW,SAAS,CACxB;IAGD,MAAM,YAAa,SAAS,MAAkC;AAC9D,QAAI,OAAO,cAAc,SACxB;IAGD,MAAM,SAAS,MAAM,aAAa,IAAI;IAEtC,MAAM,cAAc,KAAK,aAAa,UAAU;AAEhD,QAAI,CAAC,YACJ;AAGD,UAAM,IAAI,SAAS,SAAS,QAAQ;KACnC,MAAM;KACN,SAAS;KACT,iBAAiB,SAAS;KAC1B,CAAC;KACD;GACF,CACD,EACD;EAED,SAAS;EACT"}
@@ -0,0 +1,62 @@
1
+ import { BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, GenericEndpointContext, UnionToIntersection } from "@better-auth/core";
2
+
3
+ //#region src/types.d.ts
4
+ type ALL_PLUGIN_ERROR_CODE_KEYS = keyof UnionToIntersection<{ [Key in Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]: BetterAuthPluginRegistry<unknown, unknown>[Key] extends {
5
+ creator: infer C;
6
+ } ? C extends ((...args: any[]) => infer P) ? P extends {
7
+ $ERROR_CODES: infer E;
8
+ } ? E : {} : {} : {} }[Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]>;
9
+ type InternalTranslationDictionary = Partial<{ [Key in ALL_PLUGIN_ERROR_CODE_KEYS]: string }>;
10
+ /**
11
+ * Translation dictionary mapping error codes to translated messages
12
+ */
13
+ type TranslationDictionary = InternalTranslationDictionary & Record<string, string>;
14
+ /**
15
+ * Locale detection strategy
16
+ */
17
+ type LocaleDetectionStrategy = "header" | "cookie" | "session" | "callback";
18
+ /**
19
+ * Options for the i18n plugin
20
+ */
21
+ interface I18nOptions<Locales extends string[]> {
22
+ /**
23
+ * Translation dictionaries keyed by locale code
24
+ * @example
25
+ * {
26
+ * en: { USER_NOT_FOUND: "User not found" },
27
+ * fr: { USER_NOT_FOUND: "Utilisateur non trouvé" }
28
+ * }
29
+ */
30
+ translations: { [Locale in Locales[number]]: TranslationDictionary };
31
+ /**
32
+ * Default/fallback locale when detection fails
33
+ * @default "en"
34
+ */
35
+ defaultLocale?: Locales[number] | undefined;
36
+ /**
37
+ * Locale detection strategies in priority order
38
+ * @default ["header"]
39
+ */
40
+ detection?: LocaleDetectionStrategy[] | undefined;
41
+ /**
42
+ * Cookie name for locale detection (when "cookie" strategy is used)
43
+ * @default "locale"
44
+ */
45
+ localeCookie?: string | undefined;
46
+ /**
47
+ * User field name for stored locale preference (when "session" strategy is used)
48
+ * @default "locale"
49
+ */
50
+ userLocaleField?: string | undefined;
51
+ /**
52
+ * Custom locale detection function (when "callback" strategy is used).
53
+ * @example
54
+ * getLocale: (ctx) => {
55
+ * return ctx.headers?.get("X-Custom-Locale") ?? null;
56
+ * }
57
+ */
58
+ getLocale?: undefined | ((ctx: GenericEndpointContext) => Promise<Locales[number] | null> | Locales[number] | null);
59
+ }
60
+ //#endregion
61
+ export { LocaleDetectionStrategy as n, TranslationDictionary as r, I18nOptions as t };
62
+ //# sourceMappingURL=types-m1nrvPWg.d.mts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/i18n",
3
- "version": "1.5.0-beta.12",
3
+ "version": "1.5.0-beta.15",
4
4
  "type": "module",
5
5
  "description": "i18n plugin for Better Auth - translate error messages",
6
6
  "main": "dist/index.mjs",
@@ -32,13 +32,13 @@
32
32
  }
33
33
  },
34
34
  "devDependencies": {
35
- "tsdown": "^0.20.1",
36
- "@better-auth/core": "1.5.0-beta.12",
37
- "better-auth": "1.5.0-beta.12"
35
+ "tsdown": "^0.20.3",
36
+ "@better-auth/core": "1.5.0-beta.15",
37
+ "better-auth": "1.5.0-beta.15"
38
38
  },
39
39
  "peerDependencies": {
40
- "@better-auth/core": "1.5.0-beta.12",
41
- "better-auth": "1.5.0-beta.12"
40
+ "@better-auth/core": "1.5.0-beta.15",
41
+ "better-auth": "1.5.0-beta.15"
42
42
  },
43
43
  "files": [
44
44
  "dist"
@@ -1,109 +0,0 @@
1
- import * as better_call0 from "better-call";
2
- import { AuthContext, BetterAuthPluginRegistry, BetterAuthPluginRegistryIdentifier, UnionToIntersection } from "@better-auth/core";
3
-
4
- //#region src/types.d.ts
5
- type ALL_PLUGIN_ERROR_CODE_KEYS = keyof UnionToIntersection<{ [Key in Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]: BetterAuthPluginRegistry<unknown, unknown>[Key] extends {
6
- creator: infer C;
7
- } ? C extends ((...args: any[]) => infer P) ? P extends {
8
- $ERROR_CODES: infer E;
9
- } ? E : {} : {} : {} }[Exclude<BetterAuthPluginRegistryIdentifier, "i18n">]>;
10
- type InternalTranslationDictionary = Partial<{ [Key in ALL_PLUGIN_ERROR_CODE_KEYS]: string }>;
11
- /**
12
- * Translation dictionary mapping error codes to translated messages
13
- */
14
- type TranslationDictionary = InternalTranslationDictionary & Record<string, string>;
15
- /**
16
- * Locale detection strategy
17
- */
18
- type LocaleDetectionStrategy = "header" | "cookie" | "session" | "callback";
19
- /**
20
- * Options for the i18n plugin
21
- */
22
- interface I18nOptions<Locales extends string[]> {
23
- /**
24
- * Translation dictionaries keyed by locale code
25
- * @example
26
- * {
27
- * en: { USER_NOT_FOUND: "User not found" },
28
- * fr: { USER_NOT_FOUND: "Utilisateur non trouvé" }
29
- * }
30
- */
31
- translations: { [Locale in Locales[number]]: TranslationDictionary };
32
- /**
33
- * Default/fallback locale when detection fails
34
- * @default "en"
35
- */
36
- defaultLocale?: Locales[number] | undefined;
37
- /**
38
- * Locale detection strategies in priority order
39
- * @default ["header"]
40
- */
41
- detection?: LocaleDetectionStrategy[] | undefined;
42
- /**
43
- * Cookie name for locale detection (when "cookie" strategy is used)
44
- * @default "locale"
45
- */
46
- localeCookie?: string | undefined;
47
- /**
48
- * User field name for stored locale preference (when "session" strategy is used)
49
- * @default "locale"
50
- */
51
- userLocaleField?: string | undefined;
52
- /**
53
- * Custom locale detection function (when "callback" strategy is used)
54
- * Receives request and AuthContext, return locale code or null
55
- */
56
- getLocale?: undefined | ((request: Request, ctx: AuthContext) => Promise<Locales[number] | null> | Locales[number] | null);
57
- }
58
- //#endregion
59
- //#region src/index.d.ts
60
- declare module "@better-auth/core" {
61
- interface BetterAuthPluginRegistry<AuthOptions, Options> {
62
- i18n: {
63
- creator: typeof i18n;
64
- };
65
- }
66
- }
67
- /**
68
- * i18n plugin for Better Auth
69
- *
70
- * Translates error messages based on detected locale.
71
- *
72
- * @example
73
- * ```ts
74
- * import { betterAuth } from "better-auth";
75
- * import { i18n } from "@better-auth/i18n";
76
- *
77
- * export const auth = betterAuth({
78
- * plugins: [
79
- * i18n({
80
- * translations: {
81
- * en: { USER_NOT_FOUND: "User not found" },
82
- * fr: { USER_NOT_FOUND: "Utilisateur non trouvé" },
83
- * },
84
- * detection: ["header", "cookie"],
85
- * }),
86
- * ],
87
- * });
88
- * ```
89
- */
90
- declare const i18n: <Locales extends string[]>(options: I18nOptions<Locales>) => {
91
- id: "i18n";
92
- hooks: {
93
- after: {
94
- matcher: () => true;
95
- handler: (inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>;
96
- }[];
97
- };
98
- options: {
99
- translations: { [Locale in Locales[number]]: TranslationDictionary };
100
- defaultLocale: Locales[number];
101
- detection: LocaleDetectionStrategy[];
102
- localeCookie: string;
103
- userLocaleField: string;
104
- getLocale?: ((request: Request, ctx: AuthContext) => Locales[number] | Promise<Locales[number] | null> | null) | undefined;
105
- };
106
- };
107
- //#endregion
108
- export { TranslationDictionary as i, I18nOptions as n, LocaleDetectionStrategy as r, i18n as t };
109
- //# sourceMappingURL=index-DFgwTnpr.d.mts.map