cloudflare-next-intl 0.9.11 → 0.9.12
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/src/config/cookie_key.d.ts +2 -0
- package/dist/src/config/cookie_key.js +2 -0
- package/dist/src/config/middleware.js +37 -9
- package/dist/src/cookie_consent/client/cookie_consent_provider.d.ts +1 -1
- package/dist/src/cookie_consent/client/cookie_consent_provider.js +17 -4
- package/dist/src/server/components/server_provider.js +1 -4
- package/dist/src/server/components/server_provider_static.js +1 -4
- package/dist/src/server/functions/geo.js +67 -0
- package/dist/src/types/types.d.ts +8 -0
- package/package.json +1 -1
|
@@ -3,3 +3,5 @@ export declare const isBotCookieKey = "__is_bot_key__";
|
|
|
3
3
|
export declare const isDarkCookieKey = "__is_dark_key__";
|
|
4
4
|
export declare const cookieConsentCookieKey = "__cookie_consent_key__";
|
|
5
5
|
export declare const privacyPolicyDateCookieKey = "__privacy_policy_date_key__";
|
|
6
|
+
export declare const countryCookieKey = "__cf_country__";
|
|
7
|
+
export declare const timezoneCookieKey = "__cf_timezone__";
|
|
@@ -3,3 +3,5 @@ export const isBotCookieKey = '__is_bot_key__';
|
|
|
3
3
|
export const isDarkCookieKey = '__is_dark_key__';
|
|
4
4
|
export const cookieConsentCookieKey = '__cookie_consent_key__';
|
|
5
5
|
export const privacyPolicyDateCookieKey = '__privacy_policy_date_key__';
|
|
6
|
+
export const countryCookieKey = '__cf_country__';
|
|
7
|
+
export const timezoneCookieKey = '__cf_timezone__';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server.js';
|
|
2
2
|
import { languageDetecotr } from '../server/functions/get_user_locale.js';
|
|
3
3
|
import config from './intl_config.js';
|
|
4
|
-
import { isBotCookieKey, localeCookieName } from './cookie_key.js';
|
|
4
|
+
import { countryCookieKey, isBotCookieKey, localeCookieName, timezoneCookieKey } from './cookie_key.js';
|
|
5
5
|
import { cache } from 'react';
|
|
6
6
|
import reportError from '../error_handling/report_error.js';
|
|
7
|
+
import { defaultCountryHeaderNames, defaultTimezoneHeaderNames } from '../server/functions/geo.js';
|
|
7
8
|
const sameSite = false;
|
|
8
9
|
const defaultCookieOption = {
|
|
9
10
|
path: '/',
|
|
@@ -24,6 +25,14 @@ async function getIsBotValue(userAgent) {
|
|
|
24
25
|
const getIsBotValueCache = cache(getIsBotValue);
|
|
25
26
|
export const localesSet = new Set(config.locales);
|
|
26
27
|
let updateSessionModule;
|
|
28
|
+
function getFirstHeaderValue(headers, names) {
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
const value = headers.get(name);
|
|
31
|
+
if (value)
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
27
36
|
export default async function intlMiddleware(request, options) {
|
|
28
37
|
try {
|
|
29
38
|
let initialChosenLocale;
|
|
@@ -59,14 +68,19 @@ export default async function intlMiddleware(request, options) {
|
|
|
59
68
|
pathWithoutLocale = pathname;
|
|
60
69
|
}
|
|
61
70
|
const effectiveLocaleForRequest = urlLocale ?? initialChosenLocale;
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
const cf = request.cf;
|
|
72
|
+
const countryHeaderNames = config.cookieConsent?.countryHeaderNames
|
|
73
|
+
?? config.generate?.countryHeaderNames
|
|
74
|
+
?? defaultCountryHeaderNames;
|
|
75
|
+
const timezoneHeaderNames = config.generate?.timezoneHeaderNames
|
|
76
|
+
?? defaultTimezoneHeaderNames;
|
|
77
|
+
const countryCookieName = config.cookieConsent?.countryCookieName
|
|
78
|
+
?? config.generate?.countryCookieName
|
|
79
|
+
?? countryCookieKey;
|
|
80
|
+
const timezoneCookieName = config.generate?.timezoneCookieName
|
|
81
|
+
?? timezoneCookieKey;
|
|
82
|
+
const country = cf?.country || getFirstHeaderValue(request.headers, countryHeaderNames);
|
|
83
|
+
const timezone = cf?.timezone || getFirstHeaderValue(request.headers, timezoneHeaderNames);
|
|
70
84
|
const requestHeaders = new Headers(request.headers);
|
|
71
85
|
requestHeaders.set('x-pathname', pathWithoutLocale);
|
|
72
86
|
requestHeaders.set('x-search', search);
|
|
@@ -123,9 +137,23 @@ export default async function intlMiddleware(request, options) {
|
|
|
123
137
|
response.headers.set('x-search', search);
|
|
124
138
|
if (country) {
|
|
125
139
|
response.headers.set('x-cf-country', country);
|
|
140
|
+
response.cookies.set(countryCookieName, country, {
|
|
141
|
+
path: '/',
|
|
142
|
+
maxAge: 86400,
|
|
143
|
+
httpOnly: false,
|
|
144
|
+
secure: process.env.NODE_ENV === 'production',
|
|
145
|
+
sameSite: 'lax',
|
|
146
|
+
});
|
|
126
147
|
}
|
|
127
148
|
if (timezone) {
|
|
128
149
|
response.headers.set('x-cf-timezone', timezone);
|
|
150
|
+
response.cookies.set(timezoneCookieName, timezone, {
|
|
151
|
+
path: '/',
|
|
152
|
+
maxAge: 86400,
|
|
153
|
+
httpOnly: false,
|
|
154
|
+
secure: process.env.NODE_ENV === 'production',
|
|
155
|
+
sameSite: 'lax',
|
|
156
|
+
});
|
|
129
157
|
}
|
|
130
158
|
if (!isRedirect && config.firebaseAuth && config.firebaseAuth.middlewareEnabled !== false) {
|
|
131
159
|
if (!updateSessionModule) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CookieConsentContextType } from '../types.js';
|
|
2
2
|
export declare const CookieConsentContext: import("react").Context<CookieConsentContextType | null>;
|
|
3
|
-
export default function CookieConsentProvider({ requiresConsent, children }: {
|
|
3
|
+
export default function CookieConsentProvider({ requiresConsent: requiresConsentProp, children }: {
|
|
4
4
|
requiresConsent?: boolean;
|
|
5
5
|
children: React.ReactNode;
|
|
6
6
|
}): React.ReactElement;
|
|
@@ -6,7 +6,8 @@ import config from '../../config/intl_config.js';
|
|
|
6
6
|
import requireCookieConsentConfig from '../require_config.js';
|
|
7
7
|
import getCookie from '../../client/functions/get_cookie.js';
|
|
8
8
|
import setCookie from '../../client/functions/set_cookie.js';
|
|
9
|
-
import { cookieConsentCookieKey, privacyPolicyDateCookieKey } from '../../config/cookie_key.js';
|
|
9
|
+
import { cookieConsentCookieKey, countryCookieKey, privacyPolicyDateCookieKey } from '../../config/cookie_key.js';
|
|
10
|
+
import { defaultGdprCountries } from '../gdpr_countries.js';
|
|
10
11
|
export const CookieConsentContext = createContext(null);
|
|
11
12
|
function parseConsent(raw) {
|
|
12
13
|
if (raw === 'true')
|
|
@@ -15,8 +16,9 @@ function parseConsent(raw) {
|
|
|
15
16
|
return false;
|
|
16
17
|
return null;
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
const defaultGdprCountriesSet = new Set(defaultGdprCountries);
|
|
20
|
+
export default function CookieConsentProvider({ requiresConsent: requiresConsentProp = true, children }) {
|
|
21
|
+
const { consentCookieName, dateCookieName, maxAge, policyDate, privacyPolicyPath, showPrivacyPolicy, gdprCountries } = useMemo(() => {
|
|
20
22
|
const cc = requireCookieConsentConfig(config.cookieConsent);
|
|
21
23
|
return {
|
|
22
24
|
consentCookieName: cc.consentCookieName ?? cookieConsentCookieKey,
|
|
@@ -25,17 +27,28 @@ export default function CookieConsentProvider({ requiresConsent = true, children
|
|
|
25
27
|
policyDate: cc.privacyPolicyDate ? new Date(cc.privacyPolicyDate) : null,
|
|
26
28
|
privacyPolicyPath: cc.privacyPolicyPath ?? '/privacy-policy',
|
|
27
29
|
showPrivacyPolicy: cc.showPrivacyPolicy ?? true,
|
|
30
|
+
gdprCountries: cc.gdprCountries,
|
|
28
31
|
};
|
|
29
32
|
}, []);
|
|
30
33
|
const [consent, setConsentState] = useState(null);
|
|
31
34
|
const [privacyPolicyUpdated, setPrivacyPolicyUpdated] = useState(false);
|
|
32
35
|
const [isMounted, setIsMounted] = useState(false);
|
|
36
|
+
const [requiresConsent, setRequiresConsent] = useState(requiresConsentProp);
|
|
33
37
|
const pathname = usePathname();
|
|
34
38
|
useEffect(() => {
|
|
35
39
|
const rawConsent = getCookie(consentCookieName);
|
|
36
40
|
const storedConsent = parseConsent(rawConsent);
|
|
37
41
|
const isFirstVisit = rawConsent === null;
|
|
38
42
|
setConsentState(storedConsent);
|
|
43
|
+
if (requiresConsentProp) {
|
|
44
|
+
const countryCookie = getCookie(countryCookieKey);
|
|
45
|
+
if (countryCookie) {
|
|
46
|
+
const gdprSet = gdprCountries
|
|
47
|
+
? new Set(gdprCountries)
|
|
48
|
+
: defaultGdprCountriesSet;
|
|
49
|
+
setRequiresConsent(gdprSet.has(countryCookie));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
39
52
|
setIsMounted(true);
|
|
40
53
|
if (isFirstVisit || !policyDate)
|
|
41
54
|
return;
|
|
@@ -46,7 +59,7 @@ export default function CookieConsentProvider({ requiresConsent = true, children
|
|
|
46
59
|
}
|
|
47
60
|
const storedDate = new Date(storedDateRaw);
|
|
48
61
|
setPrivacyPolicyUpdated(!Number.isNaN(storedDate.getTime()) && storedDate < policyDate);
|
|
49
|
-
}, [consentCookieName, dateCookieName, maxAge, policyDate]);
|
|
62
|
+
}, [consentCookieName, dateCookieName, maxAge, policyDate, requiresConsentProp, gdprCountries]);
|
|
50
63
|
const setConsent = useCallback((value) => {
|
|
51
64
|
if (value === null) {
|
|
52
65
|
setCookie({ name: consentCookieName, value: 'null', maxAge });
|
|
@@ -4,7 +4,6 @@ import { getMessage } from "../functions/server.js";
|
|
|
4
4
|
import dynamic from "next/dynamic.js";
|
|
5
5
|
import { localesSet } from "../../config/middleware.js";
|
|
6
6
|
import config from "../../config/intl_config.js";
|
|
7
|
-
import resolveRequiresConsent from "../../cookie_consent/gdpr_countries.js";
|
|
8
7
|
import installConsoleErrorOverride from "../../error_handling/install_console_error_override.js";
|
|
9
8
|
import reportError from "../../error_handling/report_error.js";
|
|
10
9
|
const LocationzationClientProvider = dynamic(() => import("../../client/components/client_provider.js"));
|
|
@@ -43,9 +42,7 @@ export default async function LocationzationProvider({ language, messages, stati
|
|
|
43
42
|
let requiresConsent = true;
|
|
44
43
|
if (config.cookieConsent) {
|
|
45
44
|
const isDevEnvironment = process.env.NODE_ENV === 'development';
|
|
46
|
-
requiresConsent = !isDevEnvironment
|
|
47
|
-
? await resolveRequiresConsent(config.cookieConsent.getCountryCode, config.generate?.getCloudflareContext, config.cookieConsent.gdprCountries, config.errorHandling, config.cookieConsent.countryHeaderNames, config.generate)
|
|
48
|
-
: false;
|
|
45
|
+
requiresConsent = !isDevEnvironment;
|
|
49
46
|
const analyticsAllowedInEnv = config.cookieConsent.enableAnalyticsInDevMode === true || !isDevEnvironment;
|
|
50
47
|
if (config.cookieConsent.autoWireAnalytics !== false && analyticsAllowedInEnv) {
|
|
51
48
|
if (config.cookieConsent.getAnalytics) {
|
|
@@ -4,7 +4,6 @@ import { getMessage } from "../functions/server.js";
|
|
|
4
4
|
import dynamic from "next/dynamic.js";
|
|
5
5
|
import { localesSet } from "../../config/middleware.js";
|
|
6
6
|
import config from "../../config/intl_config.js";
|
|
7
|
-
import resolveRequiresConsent from "../../cookie_consent/gdpr_countries.js";
|
|
8
7
|
import installConsoleErrorOverride from "../../error_handling/install_console_error_override.js";
|
|
9
8
|
import reportError from "../../error_handling/report_error.js";
|
|
10
9
|
const LocationzationClientProvider = dynamic(() => import("../../client/components/client_provider_static.js"));
|
|
@@ -32,9 +31,7 @@ export default async function LocationzationProvider({ language, messages, child
|
|
|
32
31
|
let requiresConsent = true;
|
|
33
32
|
if (config.cookieConsent) {
|
|
34
33
|
const isDevEnvironment = process.env.NODE_ENV === 'development';
|
|
35
|
-
requiresConsent = !isDevEnvironment
|
|
36
|
-
? await resolveRequiresConsent(config.cookieConsent.getCountryCode, config.generate?.getCloudflareContext, config.cookieConsent.gdprCountries, config.errorHandling, config.cookieConsent.countryHeaderNames, config.generate)
|
|
37
|
-
: false;
|
|
34
|
+
requiresConsent = !isDevEnvironment;
|
|
38
35
|
const analyticsAllowedInEnv = config.cookieConsent.enableAnalyticsInDevMode === true || !isDevEnvironment;
|
|
39
36
|
if (config.cookieConsent.autoWireAnalytics !== false && analyticsAllowedInEnv) {
|
|
40
37
|
if (config.cookieConsent.getAnalytics) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { countryCookieKey, timezoneCookieKey } from '../../config/cookie_key.js';
|
|
1
2
|
export const defaultCountryHeaderNames = ['x-cf-country', 'cf-ipcountry'];
|
|
2
3
|
export const defaultTimezoneHeaderNames = ['x-cf-timezone', 'cf-timezone'];
|
|
3
4
|
function extractHeader(h, name) {
|
|
@@ -26,6 +27,28 @@ function extractFromHeaderNames(h, headerNames) {
|
|
|
26
27
|
}
|
|
27
28
|
return undefined;
|
|
28
29
|
}
|
|
30
|
+
function extractCookieHeader(h, name) {
|
|
31
|
+
const cookieHeader = extractHeader(h, 'cookie');
|
|
32
|
+
if (!cookieHeader)
|
|
33
|
+
return undefined;
|
|
34
|
+
const parts = cookieHeader.split(';');
|
|
35
|
+
for (const part of parts) {
|
|
36
|
+
const index = part.indexOf('=');
|
|
37
|
+
if (index === -1)
|
|
38
|
+
continue;
|
|
39
|
+
const key = part.slice(0, index).trim();
|
|
40
|
+
if (key !== name)
|
|
41
|
+
continue;
|
|
42
|
+
const value = part.slice(index + 1).trim();
|
|
43
|
+
return value ? decodeURIComponent(value) : undefined;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
function extractCookie(cookieStore, name) {
|
|
48
|
+
const cookie = cookieStore?.get?.(name);
|
|
49
|
+
const value = typeof cookie === 'string' ? cookie : cookie?.value;
|
|
50
|
+
return value || undefined;
|
|
51
|
+
}
|
|
29
52
|
export async function getCountry(input, generate, headerNames) {
|
|
30
53
|
const gen = generate ?? await configuredGenerate();
|
|
31
54
|
const names = headerNames
|
|
@@ -36,11 +59,21 @@ export async function getCountry(input, generate, headerNames) {
|
|
|
36
59
|
const country = extractFromHeaderNames(input.headers, names);
|
|
37
60
|
if (country)
|
|
38
61
|
return country;
|
|
62
|
+
const cookieCountry = extractCookieHeader(input.headers, countryCookieKey);
|
|
63
|
+
if (cookieCountry)
|
|
64
|
+
return cookieCountry;
|
|
39
65
|
}
|
|
40
66
|
else if (typeof input.get === 'function') {
|
|
41
67
|
const country = extractFromHeaderNames(input, names);
|
|
42
68
|
if (country)
|
|
43
69
|
return country;
|
|
70
|
+
const cookieCountry = extractCookieHeader(input, countryCookieKey);
|
|
71
|
+
if (cookieCountry)
|
|
72
|
+
return cookieCountry;
|
|
73
|
+
}
|
|
74
|
+
const cookieCountry = extractCookie(input.cookies, countryCookieKey);
|
|
75
|
+
if (cookieCountry) {
|
|
76
|
+
return cookieCountry;
|
|
44
77
|
}
|
|
45
78
|
const cf = input.cf;
|
|
46
79
|
if (cf?.country && typeof cf.country === 'string' && cf.country.length > 0) {
|
|
@@ -53,6 +86,18 @@ export async function getCountry(input, generate, headerNames) {
|
|
|
53
86
|
const country = extractFromHeaderNames(h, names);
|
|
54
87
|
if (country)
|
|
55
88
|
return country;
|
|
89
|
+
const cookieCountry = extractCookieHeader(h, countryCookieKey);
|
|
90
|
+
if (cookieCountry)
|
|
91
|
+
return cookieCountry;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const { cookies } = await import('next/headers.js');
|
|
97
|
+
const c = await cookies();
|
|
98
|
+
const country = extractCookie(c, countryCookieKey);
|
|
99
|
+
if (country)
|
|
100
|
+
return country;
|
|
56
101
|
}
|
|
57
102
|
catch {
|
|
58
103
|
}
|
|
@@ -89,11 +134,21 @@ export async function getTimezone(input, fallback, generate, headerNames) {
|
|
|
89
134
|
const tz = extractFromHeaderNames(input.headers, names);
|
|
90
135
|
if (tz)
|
|
91
136
|
return tz;
|
|
137
|
+
const cookieTimezone = extractCookieHeader(input.headers, timezoneCookieKey);
|
|
138
|
+
if (cookieTimezone)
|
|
139
|
+
return cookieTimezone;
|
|
92
140
|
}
|
|
93
141
|
else if (typeof input.get === 'function') {
|
|
94
142
|
const tz = extractFromHeaderNames(input, names);
|
|
95
143
|
if (tz)
|
|
96
144
|
return tz;
|
|
145
|
+
const cookieTimezone = extractCookieHeader(input, timezoneCookieKey);
|
|
146
|
+
if (cookieTimezone)
|
|
147
|
+
return cookieTimezone;
|
|
148
|
+
}
|
|
149
|
+
const cookieTimezone = extractCookie(input.cookies, timezoneCookieKey);
|
|
150
|
+
if (cookieTimezone) {
|
|
151
|
+
return cookieTimezone;
|
|
97
152
|
}
|
|
98
153
|
const cf = input.cf;
|
|
99
154
|
if (cf?.timezone && typeof cf.timezone === 'string' && cf.timezone.length > 0) {
|
|
@@ -106,6 +161,18 @@ export async function getTimezone(input, fallback, generate, headerNames) {
|
|
|
106
161
|
const tz = extractFromHeaderNames(h, names);
|
|
107
162
|
if (tz)
|
|
108
163
|
return tz;
|
|
164
|
+
const cookieTimezone = extractCookieHeader(h, timezoneCookieKey);
|
|
165
|
+
if (cookieTimezone)
|
|
166
|
+
return cookieTimezone;
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const { cookies } = await import('next/headers.js');
|
|
172
|
+
const c = await cookies();
|
|
173
|
+
const tz = extractCookie(c, timezoneCookieKey);
|
|
174
|
+
if (tz)
|
|
175
|
+
return tz;
|
|
109
176
|
}
|
|
110
177
|
catch {
|
|
111
178
|
}
|
|
@@ -29,10 +29,17 @@ export interface GenerateRoutingConfig {
|
|
|
29
29
|
} | undefined);
|
|
30
30
|
getCloudflareContext?: CookieConsentGetCloudflareContext;
|
|
31
31
|
countryHeaderNames?: readonly string[];
|
|
32
|
+
countryCookieName?: string;
|
|
32
33
|
timezoneHeaderNames?: readonly string[];
|
|
34
|
+
timezoneCookieName?: string;
|
|
33
35
|
}
|
|
34
36
|
export type RequestOrHeaders = Request | Headers | {
|
|
35
37
|
headers?: Headers | Record<string, string | null | undefined>;
|
|
38
|
+
cookies?: {
|
|
39
|
+
get?: (name: string) => {
|
|
40
|
+
value?: string;
|
|
41
|
+
} | string | undefined;
|
|
42
|
+
};
|
|
36
43
|
cf?: {
|
|
37
44
|
country?: string;
|
|
38
45
|
timezone?: string;
|
|
@@ -75,6 +82,7 @@ export interface CookieConsentRoutingConfig {
|
|
|
75
82
|
autoAnalyticsEvents?: AutoAnalyticsEventsConfig;
|
|
76
83
|
getCountryCode?: () => string | undefined | Promise<string | undefined>;
|
|
77
84
|
countryHeaderNames?: readonly string[];
|
|
85
|
+
countryCookieName?: string;
|
|
78
86
|
gdprCountries?: readonly string[];
|
|
79
87
|
enableAnalyticsInDevMode?: boolean;
|
|
80
88
|
autoWireDialogs?: boolean;
|