@main12/auth-login 0.3.8 → 0.4.0
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/components/AuthLayout.d.ts +9 -0
- package/dist/components/AuthPages.d.ts +15 -1
- package/dist/components/AuthPages.js +7 -1
- package/dist/components/AuthPagesServer.d.ts +6 -1
- package/dist/components/AuthPagesServer.js +11 -2
- package/dist/components/pages/ForgotPasswordPageHero.d.ts +1 -1
- package/dist/components/pages/ForgotPasswordPageHero.js +8 -6
- package/dist/components/pages/ForgotPasswordPageTailwind.d.ts +1 -1
- package/dist/components/pages/ForgotPasswordPageTailwind.js +12 -7
- package/dist/components/pages/LoginPageHero.js +18 -16
- package/dist/components/pages/LoginPageTailwind.js +18 -16
- package/dist/components/pages/SetPasswordPageHero.d.ts +1 -1
- package/dist/components/pages/SetPasswordPageHero.js +8 -6
- package/dist/components/pages/SetPasswordPageTailwind.d.ts +1 -1
- package/dist/components/pages/SetPasswordPageTailwind.js +9 -7
- package/dist/components/pages/SignupPageHero.d.ts +1 -1
- package/dist/components/pages/SignupPageHero.js +20 -14
- package/dist/components/pages/SignupPageTailwind.d.ts +1 -1
- package/dist/components/pages/SignupPageTailwind.js +17 -14
- package/dist/components/pages/VerifyOtpPageHero.js +9 -7
- package/dist/components/pages/VerifyOtpPageTailwind.js +13 -8
- package/dist/components/ui/locale.d.ts +29 -0
- package/dist/components/ui/locale.js +53 -0
- package/dist/components/ui/translations.d.ts +91 -0
- package/dist/components/ui/translations.js +179 -0
- package/package.json +1 -1
|
@@ -5,11 +5,13 @@ import { Button, Input, Label, TextField, Separator, Spinner } from '@heroui/rea
|
|
|
5
5
|
import { Icon } from '@iconify/react';
|
|
6
6
|
import { motion } from 'framer-motion';
|
|
7
7
|
import { AuthLayout } from '../AuthLayout.js';
|
|
8
|
-
|
|
8
|
+
import { getUiTranslations } from '../ui/translations.js';
|
|
9
|
+
export default function SignupPageHero({ onSignup, showGoogleOAuth = true, loginUrl = '/login', logo, poweredBy, cardClassName, backgroundClass, locale, messages }) {
|
|
9
10
|
const [name, setName] = React.useState('');
|
|
10
11
|
const [email, setEmail] = React.useState('');
|
|
11
12
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
12
13
|
const [error, setError] = React.useState(null);
|
|
14
|
+
const t = getUiTranslations(locale, messages).signup;
|
|
13
15
|
const handleSubmit = async (e)=>{
|
|
14
16
|
e.preventDefault();
|
|
15
17
|
setIsLoading(true);
|
|
@@ -28,19 +30,20 @@ export default function SignupPageHero({ onSignup, showGoogleOAuth = true, login
|
|
|
28
30
|
};
|
|
29
31
|
return /*#__PURE__*/ _jsxs(AuthLayout, {
|
|
30
32
|
logo: logo,
|
|
31
|
-
title:
|
|
32
|
-
subtitle:
|
|
33
|
+
title: t.title,
|
|
34
|
+
subtitle: t.subtitle,
|
|
33
35
|
poweredBy: poweredBy,
|
|
34
36
|
cardClassName: cardClassName,
|
|
35
37
|
backgroundClass: backgroundClass,
|
|
36
38
|
footer: /*#__PURE__*/ _jsxs("p", {
|
|
37
39
|
className: "text-center text-gray-600 text-sm",
|
|
38
40
|
children: [
|
|
39
|
-
|
|
41
|
+
t.haveAccount,
|
|
42
|
+
" ",
|
|
40
43
|
/*#__PURE__*/ _jsx("a", {
|
|
41
44
|
href: loginUrl,
|
|
42
45
|
className: "text-gray-900 font-medium hover:underline",
|
|
43
|
-
children:
|
|
46
|
+
children: t.loginLink
|
|
44
47
|
})
|
|
45
48
|
]
|
|
46
49
|
}),
|
|
@@ -57,7 +60,7 @@ export default function SignupPageHero({ onSignup, showGoogleOAuth = true, login
|
|
|
57
60
|
icon: "flat-color-icons:google",
|
|
58
61
|
width: 20
|
|
59
62
|
}),
|
|
60
|
-
|
|
63
|
+
t.continueWithGoogle
|
|
61
64
|
]
|
|
62
65
|
}),
|
|
63
66
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -68,7 +71,7 @@ export default function SignupPageHero({ onSignup, showGoogleOAuth = true, login
|
|
|
68
71
|
}),
|
|
69
72
|
/*#__PURE__*/ _jsx("span", {
|
|
70
73
|
className: "text-gray-500 text-sm",
|
|
71
|
-
children:
|
|
74
|
+
children: t.or
|
|
72
75
|
}),
|
|
73
76
|
/*#__PURE__*/ _jsx(Separator, {
|
|
74
77
|
className: "flex-1"
|
|
@@ -100,7 +103,7 @@ export default function SignupPageHero({ onSignup, showGoogleOAuth = true, login
|
|
|
100
103
|
children: [
|
|
101
104
|
/*#__PURE__*/ _jsx(Label, {
|
|
102
105
|
className: "text-gray-600",
|
|
103
|
-
children:
|
|
106
|
+
children: t.fullNameLabel
|
|
104
107
|
}),
|
|
105
108
|
/*#__PURE__*/ _jsx(Input, {
|
|
106
109
|
variant: "secondary"
|
|
@@ -116,7 +119,7 @@ export default function SignupPageHero({ onSignup, showGoogleOAuth = true, login
|
|
|
116
119
|
children: [
|
|
117
120
|
/*#__PURE__*/ _jsx(Label, {
|
|
118
121
|
className: "text-gray-600",
|
|
119
|
-
children:
|
|
122
|
+
children: t.emailLabel
|
|
120
123
|
}),
|
|
121
124
|
/*#__PURE__*/ _jsx(Input, {
|
|
122
125
|
variant: "secondary"
|
|
@@ -126,17 +129,20 @@ export default function SignupPageHero({ onSignup, showGoogleOAuth = true, login
|
|
|
126
129
|
/*#__PURE__*/ _jsxs("p", {
|
|
127
130
|
className: "text-xs text-gray-600 text-center",
|
|
128
131
|
children: [
|
|
129
|
-
|
|
132
|
+
t.termsNotice,
|
|
133
|
+
" ",
|
|
130
134
|
/*#__PURE__*/ _jsx("a", {
|
|
131
135
|
href: "/terms",
|
|
132
136
|
className: "text-gray-900 hover:underline",
|
|
133
|
-
children:
|
|
137
|
+
children: t.termsLink
|
|
134
138
|
}),
|
|
135
|
-
"
|
|
139
|
+
" ",
|
|
140
|
+
t.andSeparator,
|
|
141
|
+
" ",
|
|
136
142
|
/*#__PURE__*/ _jsx("a", {
|
|
137
143
|
href: "/privacy",
|
|
138
144
|
className: "text-gray-900 hover:underline",
|
|
139
|
-
children:
|
|
145
|
+
children: t.privacyLink
|
|
140
146
|
})
|
|
141
147
|
]
|
|
142
148
|
}),
|
|
@@ -152,7 +158,7 @@ export default function SignupPageHero({ onSignup, showGoogleOAuth = true, login
|
|
|
152
158
|
color: "current",
|
|
153
159
|
size: "sm"
|
|
154
160
|
}),
|
|
155
|
-
|
|
161
|
+
t.createAccount
|
|
156
162
|
]
|
|
157
163
|
})
|
|
158
164
|
})
|
|
@@ -7,4 +7,4 @@ export interface SignupPageProps extends AuthLayoutConfig {
|
|
|
7
7
|
showGoogleOAuth?: boolean;
|
|
8
8
|
loginUrl?: string;
|
|
9
9
|
}
|
|
10
|
-
export default function SignupPage({ onSignup, showGoogleOAuth, loginUrl, logo, poweredBy, cardClassName, backgroundClass, }: SignupPageProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export default function SignupPage({ onSignup, showGoogleOAuth, loginUrl, logo, poweredBy, cardClassName, backgroundClass, locale, messages, }: SignupPageProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -3,11 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Button, Input, Divider } from '../ui/index.js';
|
|
5
5
|
import { AuthLayout } from '../AuthLayout.js';
|
|
6
|
-
|
|
6
|
+
import { getUiTranslations } from '../ui/translations.js';
|
|
7
|
+
export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl = '/login', logo, poweredBy, cardClassName, backgroundClass, locale, messages }) {
|
|
7
8
|
const [name, setName] = React.useState('');
|
|
8
9
|
const [email, setEmail] = React.useState('');
|
|
9
10
|
const [isLoading, setIsLoading] = React.useState(false);
|
|
10
11
|
const [error, setError] = React.useState(null);
|
|
12
|
+
const t = getUiTranslations(locale, messages).signup;
|
|
11
13
|
const handleSubmit = async (e)=>{
|
|
12
14
|
e.preventDefault();
|
|
13
15
|
setIsLoading(true);
|
|
@@ -26,20 +28,20 @@ export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl
|
|
|
26
28
|
};
|
|
27
29
|
return /*#__PURE__*/ _jsxs(AuthLayout, {
|
|
28
30
|
logo: logo,
|
|
29
|
-
title:
|
|
30
|
-
subtitle:
|
|
31
|
+
title: t.title,
|
|
32
|
+
subtitle: t.subtitle,
|
|
31
33
|
poweredBy: poweredBy,
|
|
32
34
|
cardClassName: cardClassName,
|
|
33
35
|
backgroundClass: backgroundClass,
|
|
34
36
|
footer: /*#__PURE__*/ _jsxs("p", {
|
|
35
37
|
className: "text-center text-gray-600 text-sm",
|
|
36
38
|
children: [
|
|
37
|
-
|
|
39
|
+
t.haveAccount,
|
|
38
40
|
' ',
|
|
39
41
|
/*#__PURE__*/ _jsx("a", {
|
|
40
42
|
href: loginUrl,
|
|
41
43
|
className: "text-gray-900 font-medium hover:underline",
|
|
42
|
-
children:
|
|
44
|
+
children: t.loginLink
|
|
43
45
|
})
|
|
44
46
|
]
|
|
45
47
|
}),
|
|
@@ -75,7 +77,7 @@ export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl
|
|
|
75
77
|
})
|
|
76
78
|
]
|
|
77
79
|
}),
|
|
78
|
-
|
|
80
|
+
t.continueWithGoogle
|
|
79
81
|
]
|
|
80
82
|
}),
|
|
81
83
|
/*#__PURE__*/ _jsxs("div", {
|
|
@@ -86,7 +88,7 @@ export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl
|
|
|
86
88
|
}),
|
|
87
89
|
/*#__PURE__*/ _jsx("span", {
|
|
88
90
|
className: "text-gray-500 text-sm",
|
|
89
|
-
children:
|
|
91
|
+
children: t.or
|
|
90
92
|
}),
|
|
91
93
|
/*#__PURE__*/ _jsx(Divider, {
|
|
92
94
|
className: "flex-1"
|
|
@@ -104,14 +106,14 @@ export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl
|
|
|
104
106
|
children: error
|
|
105
107
|
}),
|
|
106
108
|
/*#__PURE__*/ _jsx(Input, {
|
|
107
|
-
label:
|
|
109
|
+
label: t.fullNameLabel,
|
|
108
110
|
value: name,
|
|
109
111
|
onValueChange: setName,
|
|
110
112
|
isRequired: true
|
|
111
113
|
}),
|
|
112
114
|
/*#__PURE__*/ _jsx(Input, {
|
|
113
115
|
type: "email",
|
|
114
|
-
label:
|
|
116
|
+
label: t.emailLabel,
|
|
115
117
|
value: email,
|
|
116
118
|
onValueChange: setEmail,
|
|
117
119
|
isRequired: true
|
|
@@ -119,19 +121,20 @@ export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl
|
|
|
119
121
|
/*#__PURE__*/ _jsxs("p", {
|
|
120
122
|
className: "text-xs text-gray-600 text-center",
|
|
121
123
|
children: [
|
|
122
|
-
|
|
124
|
+
t.termsNotice,
|
|
123
125
|
' ',
|
|
124
126
|
/*#__PURE__*/ _jsx("a", {
|
|
125
127
|
href: "/terms",
|
|
126
128
|
className: "text-gray-900 hover:underline",
|
|
127
|
-
children:
|
|
129
|
+
children: t.termsLink
|
|
128
130
|
}),
|
|
129
|
-
"
|
|
131
|
+
" ",
|
|
132
|
+
t.andSeparator,
|
|
130
133
|
' ',
|
|
131
134
|
/*#__PURE__*/ _jsx("a", {
|
|
132
135
|
href: "/privacy",
|
|
133
136
|
className: "text-gray-900 hover:underline",
|
|
134
|
-
children:
|
|
137
|
+
children: t.privacyLink
|
|
135
138
|
})
|
|
136
139
|
]
|
|
137
140
|
}),
|
|
@@ -139,7 +142,7 @@ export default function SignupPage({ onSignup, showGoogleOAuth = true, loginUrl
|
|
|
139
142
|
type: "submit",
|
|
140
143
|
variant: "primary",
|
|
141
144
|
isLoading: isLoading,
|
|
142
|
-
children:
|
|
145
|
+
children: t.createAccount
|
|
143
146
|
})
|
|
144
147
|
]
|
|
145
148
|
})
|
|
@@ -7,7 +7,8 @@ import { Icon } from '@iconify/react';
|
|
|
7
7
|
import { motion } from 'framer-motion';
|
|
8
8
|
import { useVerifyOtpFlow } from '../../auth/application/hooks/useVerifyOtpFlow.js';
|
|
9
9
|
import { AuthLayout } from '../AuthLayout.js';
|
|
10
|
-
|
|
10
|
+
import { getUiTranslations } from '../ui/translations.js';
|
|
11
|
+
function VerifyOtpHeroContent({ loginUrl = '/login', logo, poweredBy, cardClassName, backgroundClass, locale, messages }) {
|
|
11
12
|
const searchParams = useSearchParams();
|
|
12
13
|
const email = searchParams.get('email') || '';
|
|
13
14
|
const purpose = searchParams.get('purpose') || 'login';
|
|
@@ -17,11 +18,12 @@ function VerifyOtpHeroContent({ loginUrl = '/login', logo, poweredBy, cardClassN
|
|
|
17
18
|
purpose,
|
|
18
19
|
redirectTo
|
|
19
20
|
});
|
|
21
|
+
const t = getUiTranslations(locale, messages).verifyOtp;
|
|
20
22
|
if (!email) return null;
|
|
21
23
|
return /*#__PURE__*/ _jsx(AuthLayout, {
|
|
22
24
|
logo: logo,
|
|
23
|
-
title: purpose === 'password-reset' ?
|
|
24
|
-
subtitle: purpose === 'password-reset' ?
|
|
25
|
+
title: purpose === 'password-reset' ? t.passwordResetTitle : t.title,
|
|
26
|
+
subtitle: purpose === 'password-reset' ? t.passwordResetSubtitle : t.subtitle,
|
|
25
27
|
poweredBy: poweredBy,
|
|
26
28
|
cardClassName: cardClassName,
|
|
27
29
|
backgroundClass: backgroundClass,
|
|
@@ -33,7 +35,7 @@ function VerifyOtpHeroContent({ loginUrl = '/login', logo, poweredBy, cardClassN
|
|
|
33
35
|
icon: "lucide:arrow-left",
|
|
34
36
|
width: 16
|
|
35
37
|
}),
|
|
36
|
-
|
|
38
|
+
t.backToLogin
|
|
37
39
|
]
|
|
38
40
|
}),
|
|
39
41
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
@@ -103,7 +105,7 @@ function VerifyOtpHeroContent({ loginUrl = '/login', logo, poweredBy, cardClassN
|
|
|
103
105
|
color: "current",
|
|
104
106
|
size: "sm"
|
|
105
107
|
}),
|
|
106
|
-
|
|
108
|
+
t.verify
|
|
107
109
|
]
|
|
108
110
|
})
|
|
109
111
|
}),
|
|
@@ -112,13 +114,13 @@ function VerifyOtpHeroContent({ loginUrl = '/login', logo, poweredBy, cardClassN
|
|
|
112
114
|
children: [
|
|
113
115
|
/*#__PURE__*/ _jsx("p", {
|
|
114
116
|
className: "text-gray-600 text-sm mb-2",
|
|
115
|
-
children:
|
|
117
|
+
children: t.noCodeReceived
|
|
116
118
|
}),
|
|
117
119
|
/*#__PURE__*/ _jsx("button", {
|
|
118
120
|
onClick: handleResendCode,
|
|
119
121
|
disabled: isResending || resendCooldown > 0,
|
|
120
122
|
className: "text-primary font-medium text-sm hover:underline disabled:opacity-50",
|
|
121
|
-
children: isResending ?
|
|
123
|
+
children: isResending ? t.resending : resendCooldown > 0 ? t.resendIn.replace('{seconds}', String(resendCooldown)) : t.resendCode
|
|
122
124
|
})
|
|
123
125
|
]
|
|
124
126
|
})
|
|
@@ -5,7 +5,8 @@ import { useSearchParams } from 'next/navigation';
|
|
|
5
5
|
import { Button, OtpInput, Spinner } from '../ui/index.js';
|
|
6
6
|
import { useVerifyOtpFlow } from '../../auth/application/hooks/useVerifyOtpFlow.js';
|
|
7
7
|
import { AuthLayout } from '../AuthLayout.js';
|
|
8
|
-
|
|
8
|
+
import { getUiTranslations } from '../ui/translations.js';
|
|
9
|
+
function VerifyOtpContent({ loginUrl = '/login', logo, poweredBy, cardClassName, backgroundClass, locale, messages }) {
|
|
9
10
|
const searchParams = useSearchParams();
|
|
10
11
|
const email = searchParams.get('email') || '';
|
|
11
12
|
const purpose = searchParams.get('purpose') || 'login';
|
|
@@ -15,19 +16,23 @@ function VerifyOtpContent({ loginUrl = '/login', logo, poweredBy, cardClassName,
|
|
|
15
16
|
purpose,
|
|
16
17
|
redirectTo
|
|
17
18
|
});
|
|
19
|
+
const t = getUiTranslations(locale, messages).verifyOtp;
|
|
18
20
|
if (!email) return null;
|
|
19
21
|
const isPasswordReset = purpose === 'password-reset';
|
|
20
22
|
return /*#__PURE__*/ _jsx(AuthLayout, {
|
|
21
23
|
logo: logo,
|
|
22
|
-
title: isPasswordReset ?
|
|
23
|
-
subtitle: isPasswordReset ?
|
|
24
|
+
title: isPasswordReset ? t.passwordResetTitle : t.title,
|
|
25
|
+
subtitle: isPasswordReset ? t.passwordResetSubtitle : t.subtitle,
|
|
24
26
|
poweredBy: poweredBy,
|
|
25
27
|
cardClassName: cardClassName,
|
|
26
28
|
backgroundClass: backgroundClass,
|
|
27
|
-
footer: /*#__PURE__*/
|
|
29
|
+
footer: /*#__PURE__*/ _jsxs("a", {
|
|
28
30
|
href: loginUrl,
|
|
29
31
|
className: "text-sm text-gray-600 hover:text-gray-900 flex items-center gap-1",
|
|
30
|
-
children:
|
|
32
|
+
children: [
|
|
33
|
+
"← ",
|
|
34
|
+
t.backToLogin
|
|
35
|
+
]
|
|
31
36
|
}),
|
|
32
37
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
33
38
|
className: "flex flex-col items-center gap-4",
|
|
@@ -51,20 +56,20 @@ function VerifyOtpContent({ loginUrl = '/login', logo, poweredBy, cardClassName,
|
|
|
51
56
|
isLoading: isLoading,
|
|
52
57
|
isDisabled: otp.length !== 6,
|
|
53
58
|
onPress: handleSubmit,
|
|
54
|
-
children:
|
|
59
|
+
children: t.verify
|
|
55
60
|
}),
|
|
56
61
|
/*#__PURE__*/ _jsxs("div", {
|
|
57
62
|
className: "text-center",
|
|
58
63
|
children: [
|
|
59
64
|
/*#__PURE__*/ _jsx("p", {
|
|
60
65
|
className: "text-gray-600 text-sm mb-2",
|
|
61
|
-
children:
|
|
66
|
+
children: t.noCodeReceived
|
|
62
67
|
}),
|
|
63
68
|
/*#__PURE__*/ _jsx("button", {
|
|
64
69
|
onClick: handleResendCode,
|
|
65
70
|
disabled: isResending || resendCooldown > 0,
|
|
66
71
|
className: "text-[#0071e3] font-medium text-sm hover:underline disabled:opacity-50 disabled:cursor-not-allowed",
|
|
67
|
-
children: isResending ?
|
|
72
|
+
children: isResending ? t.resending : resendCooldown > 0 ? t.resendIn.replace('{seconds}', String(resendCooldown)) : t.resendCode
|
|
68
73
|
})
|
|
69
74
|
]
|
|
70
75
|
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency locale detection.
|
|
3
|
+
*
|
|
4
|
+
* The plugin never imports next-intl, next-i18next, or any i18n library —
|
|
5
|
+
* instead it reads the same conventional signals those libraries already
|
|
6
|
+
* write, so `locale` "just works" automatically for consumers using them,
|
|
7
|
+
* while still defaulting sanely (`en`) for consumers with no i18n setup.
|
|
8
|
+
*
|
|
9
|
+
* Priority order:
|
|
10
|
+
* 1. Explicit `locale` prop passed to `<AuthPages />` (always wins, handled by caller)
|
|
11
|
+
* 2. `NEXT_LOCALE` cookie — written by next-intl, next-i18next, and most
|
|
12
|
+
* i18n routing middlewares by convention
|
|
13
|
+
* 3. `Accept-Language` request header (server-side only)
|
|
14
|
+
* 4. `<html lang="...">` attribute (client-side only)
|
|
15
|
+
* 5. Fallback: 'en'
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Server-side locale detection — reads `Cookie` and `Accept-Language` headers.
|
|
19
|
+
* Use inside an RSC (e.g. `AuthPagesServer`) where `next/headers` is available.
|
|
20
|
+
*/
|
|
21
|
+
export declare function detectServerLocale(headers: {
|
|
22
|
+
get(name: string): string | null;
|
|
23
|
+
}): string;
|
|
24
|
+
/**
|
|
25
|
+
* Client-side locale detection — reads the `NEXT_LOCALE` cookie or falls back
|
|
26
|
+
* to `<html lang>`. Use inside client components when no explicit locale was
|
|
27
|
+
* passed down from the server.
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectClientLocale(): string;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency locale detection.
|
|
3
|
+
*
|
|
4
|
+
* The plugin never imports next-intl, next-i18next, or any i18n library —
|
|
5
|
+
* instead it reads the same conventional signals those libraries already
|
|
6
|
+
* write, so `locale` "just works" automatically for consumers using them,
|
|
7
|
+
* while still defaulting sanely (`en`) for consumers with no i18n setup.
|
|
8
|
+
*
|
|
9
|
+
* Priority order:
|
|
10
|
+
* 1. Explicit `locale` prop passed to `<AuthPages />` (always wins, handled by caller)
|
|
11
|
+
* 2. `NEXT_LOCALE` cookie — written by next-intl, next-i18next, and most
|
|
12
|
+
* i18n routing middlewares by convention
|
|
13
|
+
* 3. `Accept-Language` request header (server-side only)
|
|
14
|
+
* 4. `<html lang="...">` attribute (client-side only)
|
|
15
|
+
* 5. Fallback: 'en'
|
|
16
|
+
*/ const SUPPORTED_FALLBACK = 'en';
|
|
17
|
+
/** Extract the primary language code from a locale string, e.g. 'es-MX' -> 'es'. */ function normalize(locale) {
|
|
18
|
+
if (!locale) return undefined;
|
|
19
|
+
const primary = locale.split(/[-_]/)[0]?.toLowerCase();
|
|
20
|
+
return primary || undefined;
|
|
21
|
+
}
|
|
22
|
+
/** Parse the `NEXT_LOCALE` cookie value from a raw `Cookie` header string. */ function parseCookieLocale(cookieHeader) {
|
|
23
|
+
if (!cookieHeader) return undefined;
|
|
24
|
+
const match = cookieHeader.match(/(?:^|;\s*)NEXT_LOCALE=([^;]+)/);
|
|
25
|
+
return match ? normalize(decodeURIComponent(match[1])) : undefined;
|
|
26
|
+
}
|
|
27
|
+
/** Parse the first preferred language from an `Accept-Language` header. */ function parseAcceptLanguage(header) {
|
|
28
|
+
if (!header) return undefined;
|
|
29
|
+
const first = header.split(',')[0]?.trim();
|
|
30
|
+
return normalize(first);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Server-side locale detection — reads `Cookie` and `Accept-Language` headers.
|
|
34
|
+
* Use inside an RSC (e.g. `AuthPagesServer`) where `next/headers` is available.
|
|
35
|
+
*/ export function detectServerLocale(headers) {
|
|
36
|
+
const fromCookie = parseCookieLocale(headers.get('cookie'));
|
|
37
|
+
if (fromCookie) return fromCookie;
|
|
38
|
+
const fromAcceptLanguage = parseAcceptLanguage(headers.get('accept-language'));
|
|
39
|
+
if (fromAcceptLanguage) return fromAcceptLanguage;
|
|
40
|
+
return SUPPORTED_FALLBACK;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Client-side locale detection — reads the `NEXT_LOCALE` cookie or falls back
|
|
44
|
+
* to `<html lang>`. Use inside client components when no explicit locale was
|
|
45
|
+
* passed down from the server.
|
|
46
|
+
*/ export function detectClientLocale() {
|
|
47
|
+
if (typeof document === 'undefined') return SUPPORTED_FALLBACK;
|
|
48
|
+
const fromCookie = parseCookieLocale(document.cookie);
|
|
49
|
+
if (fromCookie) return fromCookie;
|
|
50
|
+
const fromHtmlLang = normalize(document.documentElement?.lang);
|
|
51
|
+
if (fromHtmlLang) return fromHtmlLang;
|
|
52
|
+
return SUPPORTED_FALLBACK;
|
|
53
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI copy for the auth pages (login, signup, forgot-password, verify-otp, set-password).
|
|
3
|
+
*
|
|
4
|
+
* Built-in support for English (`en`) and Spanish (`es`) out of the box — no
|
|
5
|
+
* configuration required. Consumers can override any subset of keys, and/or add
|
|
6
|
+
* entirely new locales, via the `messages` prop on `<AuthPages />` / individual
|
|
7
|
+
* page components. Missing keys always fall back to the built-in English copy.
|
|
8
|
+
*/
|
|
9
|
+
export interface UiTranslations {
|
|
10
|
+
login: {
|
|
11
|
+
title: string;
|
|
12
|
+
subtitle: string;
|
|
13
|
+
passwordStepTitle: string;
|
|
14
|
+
passwordStepSubtitle: string;
|
|
15
|
+
otpPromptTitle: string;
|
|
16
|
+
otpPromptSubtitle: string;
|
|
17
|
+
continueWithGoogle: string;
|
|
18
|
+
or: string;
|
|
19
|
+
emailLabel: string;
|
|
20
|
+
passwordLabel: string;
|
|
21
|
+
continue: string;
|
|
22
|
+
forgotPassword: string;
|
|
23
|
+
edit: string;
|
|
24
|
+
verificationNotice: string;
|
|
25
|
+
sendCode: string;
|
|
26
|
+
sendingCode: string;
|
|
27
|
+
noAccount: string;
|
|
28
|
+
signUpLink: string;
|
|
29
|
+
};
|
|
30
|
+
signup: {
|
|
31
|
+
title: string;
|
|
32
|
+
subtitle: string;
|
|
33
|
+
fullNameLabel: string;
|
|
34
|
+
emailLabel: string;
|
|
35
|
+
continueWithGoogle: string;
|
|
36
|
+
or: string;
|
|
37
|
+
termsNotice: string;
|
|
38
|
+
termsLink: string;
|
|
39
|
+
andSeparator: string;
|
|
40
|
+
privacyLink: string;
|
|
41
|
+
createAccount: string;
|
|
42
|
+
haveAccount: string;
|
|
43
|
+
loginLink: string;
|
|
44
|
+
};
|
|
45
|
+
forgotPassword: {
|
|
46
|
+
title: string;
|
|
47
|
+
subtitle: string;
|
|
48
|
+
emailLabel: string;
|
|
49
|
+
sendResetCode: string;
|
|
50
|
+
backToLogin: string;
|
|
51
|
+
};
|
|
52
|
+
verifyOtp: {
|
|
53
|
+
title: string;
|
|
54
|
+
passwordResetTitle: string;
|
|
55
|
+
subtitle: string;
|
|
56
|
+
passwordResetSubtitle: string;
|
|
57
|
+
verify: string;
|
|
58
|
+
noCodeReceived: string;
|
|
59
|
+
resendCode: string;
|
|
60
|
+
resending: string;
|
|
61
|
+
resendIn: string;
|
|
62
|
+
backToLogin: string;
|
|
63
|
+
};
|
|
64
|
+
setPassword: {
|
|
65
|
+
title: string;
|
|
66
|
+
subtitle: string;
|
|
67
|
+
newPasswordLabel: string;
|
|
68
|
+
confirmPasswordLabel: string;
|
|
69
|
+
passwordRequirements: string;
|
|
70
|
+
setPassword: string;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Deep partial — every field at every level is optional, for override objects. */
|
|
74
|
+
export type DeepPartial<T> = {
|
|
75
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
76
|
+
};
|
|
77
|
+
/** Built-in dictionaries. Consumers can add more locales via the `messages` prop. */
|
|
78
|
+
export declare const uiTranslations: Record<string, UiTranslations>;
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the final UI translations for a given locale, applying any consumer
|
|
81
|
+
* overrides on top of the built-in dictionaries.
|
|
82
|
+
*
|
|
83
|
+
* Resolution order per key: `messages[locale]` → `messages.en` → built-in
|
|
84
|
+
* `[locale]` → built-in `en`. Consumers may pass a partial override object —
|
|
85
|
+
* any key they don't specify falls back automatically.
|
|
86
|
+
*
|
|
87
|
+
* @param locale - Target locale, e.g. 'en', 'es', or any custom locale defined in `messages`.
|
|
88
|
+
* @param messages - Optional map of locale -> partial translation overrides. Can also
|
|
89
|
+
* introduce brand-new locales not built into the plugin.
|
|
90
|
+
*/
|
|
91
|
+
export declare function getUiTranslations(locale?: string, messages?: Record<string, DeepPartial<UiTranslations>>): UiTranslations;
|