@djangocfg/ext-newsletter 1.0.21 → 1.0.23
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/config.cjs +10 -1
- package/dist/config.js +10 -1
- package/dist/hooks.cjs +87 -9
- package/dist/hooks.js +88 -10
- package/dist/i18n.cjs +98 -0
- package/dist/i18n.d.cts +48 -0
- package/dist/i18n.d.ts +48 -0
- package/dist/i18n.js +70 -0
- package/dist/index.cjs +10 -1
- package/dist/index.js +10 -1
- package/package.json +16 -7
- package/src/components/Hero/index.tsx +21 -9
- package/src/i18n/index.ts +26 -0
- package/src/i18n/locales/en.ts +12 -0
- package/src/i18n/locales/ko.ts +12 -0
- package/src/i18n/locales/ru.ts +12 -0
- package/src/i18n/types.ts +33 -0
- package/src/i18n/useNewsletterT.ts +60 -0
package/dist/config.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var import_ext_base = require("@djangocfg/ext-base");
|
|
|
27
27
|
// package.json
|
|
28
28
|
var package_default = {
|
|
29
29
|
name: "@djangocfg/ext-newsletter",
|
|
30
|
-
version: "1.0.
|
|
30
|
+
version: "1.0.23",
|
|
31
31
|
description: "Newsletter and subscription management extension for DjangoCFG",
|
|
32
32
|
keywords: [
|
|
33
33
|
"django",
|
|
@@ -73,6 +73,11 @@ var package_default = {
|
|
|
73
73
|
types: "./dist/config.d.ts",
|
|
74
74
|
import: "./dist/config.js",
|
|
75
75
|
require: "./dist/config.cjs"
|
|
76
|
+
},
|
|
77
|
+
"./i18n": {
|
|
78
|
+
types: "./dist/i18n.d.ts",
|
|
79
|
+
import: "./dist/i18n.js",
|
|
80
|
+
require: "./dist/i18n.cjs"
|
|
76
81
|
}
|
|
77
82
|
},
|
|
78
83
|
files: [
|
|
@@ -88,10 +93,12 @@ var package_default = {
|
|
|
88
93
|
peerDependencies: {
|
|
89
94
|
"@djangocfg/api": "workspace:*",
|
|
90
95
|
"@djangocfg/ext-base": "workspace:*",
|
|
96
|
+
"@djangocfg/i18n": "workspace:*",
|
|
91
97
|
"@djangocfg/ui-core": "workspace:*",
|
|
92
98
|
consola: "^3.4.2",
|
|
93
99
|
"lucide-react": "^0.545.0",
|
|
94
100
|
next: "^16",
|
|
101
|
+
"next-intl": "^4",
|
|
95
102
|
"p-retry": "^7.0.0",
|
|
96
103
|
react: "^19",
|
|
97
104
|
"react-dom": "^19",
|
|
@@ -101,10 +108,12 @@ var package_default = {
|
|
|
101
108
|
devDependencies: {
|
|
102
109
|
"@djangocfg/api": "workspace:*",
|
|
103
110
|
"@djangocfg/ext-base": "workspace:*",
|
|
111
|
+
"@djangocfg/i18n": "workspace:*",
|
|
104
112
|
"@djangocfg/typescript-config": "workspace:*",
|
|
105
113
|
"@types/node": "^24.7.2",
|
|
106
114
|
"@types/react": "^19.0.0",
|
|
107
115
|
consola: "^3.4.2",
|
|
116
|
+
"next-intl": "^4.1.0",
|
|
108
117
|
"p-retry": "^7.0.0",
|
|
109
118
|
swr: "^2.3.7",
|
|
110
119
|
tsup: "^8.5.0",
|
package/dist/config.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createExtensionConfig } from "@djangocfg/ext-base";
|
|
|
4
4
|
// package.json
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "@djangocfg/ext-newsletter",
|
|
7
|
-
version: "1.0.
|
|
7
|
+
version: "1.0.23",
|
|
8
8
|
description: "Newsletter and subscription management extension for DjangoCFG",
|
|
9
9
|
keywords: [
|
|
10
10
|
"django",
|
|
@@ -50,6 +50,11 @@ var package_default = {
|
|
|
50
50
|
types: "./dist/config.d.ts",
|
|
51
51
|
import: "./dist/config.js",
|
|
52
52
|
require: "./dist/config.cjs"
|
|
53
|
+
},
|
|
54
|
+
"./i18n": {
|
|
55
|
+
types: "./dist/i18n.d.ts",
|
|
56
|
+
import: "./dist/i18n.js",
|
|
57
|
+
require: "./dist/i18n.cjs"
|
|
53
58
|
}
|
|
54
59
|
},
|
|
55
60
|
files: [
|
|
@@ -65,10 +70,12 @@ var package_default = {
|
|
|
65
70
|
peerDependencies: {
|
|
66
71
|
"@djangocfg/api": "workspace:*",
|
|
67
72
|
"@djangocfg/ext-base": "workspace:*",
|
|
73
|
+
"@djangocfg/i18n": "workspace:*",
|
|
68
74
|
"@djangocfg/ui-core": "workspace:*",
|
|
69
75
|
consola: "^3.4.2",
|
|
70
76
|
"lucide-react": "^0.545.0",
|
|
71
77
|
next: "^16",
|
|
78
|
+
"next-intl": "^4",
|
|
72
79
|
"p-retry": "^7.0.0",
|
|
73
80
|
react: "^19",
|
|
74
81
|
"react-dom": "^19",
|
|
@@ -78,10 +85,12 @@ var package_default = {
|
|
|
78
85
|
devDependencies: {
|
|
79
86
|
"@djangocfg/api": "workspace:*",
|
|
80
87
|
"@djangocfg/ext-base": "workspace:*",
|
|
88
|
+
"@djangocfg/i18n": "workspace:*",
|
|
81
89
|
"@djangocfg/typescript-config": "workspace:*",
|
|
82
90
|
"@types/node": "^24.7.2",
|
|
83
91
|
"@types/react": "^19.0.0",
|
|
84
92
|
consola: "^3.4.2",
|
|
93
|
+
"next-intl": "^4.1.0",
|
|
85
94
|
"p-retry": "^7.0.0",
|
|
86
95
|
swr: "^2.3.7",
|
|
87
96
|
tsup: "^8.5.0",
|
package/dist/hooks.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var react = require('react');
|
|
|
10
10
|
var hooks = require('@djangocfg/ext-base/hooks');
|
|
11
11
|
var jsxRuntime = require('react/jsx-runtime');
|
|
12
12
|
var lucideReact = require('lucide-react');
|
|
13
|
+
var nextIntl = require('next-intl');
|
|
13
14
|
var uiCore = require('@djangocfg/ui-core');
|
|
14
15
|
|
|
15
16
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -2124,7 +2125,7 @@ var apiNewsletter = api.createExtensionAPI(API);
|
|
|
2124
2125
|
// package.json
|
|
2125
2126
|
var package_default = {
|
|
2126
2127
|
name: "@djangocfg/ext-newsletter",
|
|
2127
|
-
version: "1.0.
|
|
2128
|
+
version: "1.0.23",
|
|
2128
2129
|
description: "Newsletter and subscription management extension for DjangoCFG",
|
|
2129
2130
|
keywords: [
|
|
2130
2131
|
"django",
|
|
@@ -2170,6 +2171,11 @@ var package_default = {
|
|
|
2170
2171
|
types: "./dist/config.d.ts",
|
|
2171
2172
|
import: "./dist/config.js",
|
|
2172
2173
|
require: "./dist/config.cjs"
|
|
2174
|
+
},
|
|
2175
|
+
"./i18n": {
|
|
2176
|
+
types: "./dist/i18n.d.ts",
|
|
2177
|
+
import: "./dist/i18n.js",
|
|
2178
|
+
require: "./dist/i18n.cjs"
|
|
2173
2179
|
}
|
|
2174
2180
|
},
|
|
2175
2181
|
files: [
|
|
@@ -2185,10 +2191,12 @@ var package_default = {
|
|
|
2185
2191
|
peerDependencies: {
|
|
2186
2192
|
"@djangocfg/api": "workspace:*",
|
|
2187
2193
|
"@djangocfg/ext-base": "workspace:*",
|
|
2194
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2188
2195
|
"@djangocfg/ui-core": "workspace:*",
|
|
2189
2196
|
consola: "^3.4.2",
|
|
2190
2197
|
"lucide-react": "^0.545.0",
|
|
2191
2198
|
next: "^16",
|
|
2199
|
+
"next-intl": "^4",
|
|
2192
2200
|
"p-retry": "^7.0.0",
|
|
2193
2201
|
react: "^19",
|
|
2194
2202
|
"react-dom": "^19",
|
|
@@ -2198,10 +2206,12 @@ var package_default = {
|
|
|
2198
2206
|
devDependencies: {
|
|
2199
2207
|
"@djangocfg/api": "workspace:*",
|
|
2200
2208
|
"@djangocfg/ext-base": "workspace:*",
|
|
2209
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2201
2210
|
"@djangocfg/typescript-config": "workspace:*",
|
|
2202
2211
|
"@types/node": "^24.7.2",
|
|
2203
2212
|
"@types/react": "^19.0.0",
|
|
2204
2213
|
consola: "^3.4.2",
|
|
2214
|
+
"next-intl": "^4.1.0",
|
|
2205
2215
|
"p-retry": "^7.0.0",
|
|
2206
2216
|
swr: "^2.3.7",
|
|
2207
2217
|
tsup: "^8.5.0",
|
|
@@ -2476,6 +2486,65 @@ function useNewsletterContext() {
|
|
|
2476
2486
|
}
|
|
2477
2487
|
return context;
|
|
2478
2488
|
}
|
|
2489
|
+
|
|
2490
|
+
// src/i18n/locales/en.ts
|
|
2491
|
+
var en = {
|
|
2492
|
+
hero: {
|
|
2493
|
+
placeholder: "Enter your email",
|
|
2494
|
+
subscribe: "Subscribe",
|
|
2495
|
+
subscribing: "Subscribing...",
|
|
2496
|
+
successMessage: "Successfully subscribed!",
|
|
2497
|
+
errorMessage: "Subscription failed. Please try again.",
|
|
2498
|
+
privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
|
|
2499
|
+
}
|
|
2500
|
+
};
|
|
2501
|
+
|
|
2502
|
+
// src/i18n/locales/ru.ts
|
|
2503
|
+
var ru = {
|
|
2504
|
+
hero: {
|
|
2505
|
+
placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
|
|
2506
|
+
subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
|
|
2507
|
+
subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
|
|
2508
|
+
successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
|
|
2509
|
+
errorMessage: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437.",
|
|
2510
|
+
privacyNotice: "\u041F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u044F\u0441\u044C, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441 \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438 \u0438 \u0434\u0430\u0451\u0442\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043D\u0430 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439."
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2513
|
+
|
|
2514
|
+
// src/i18n/locales/ko.ts
|
|
2515
|
+
var ko = {
|
|
2516
|
+
hero: {
|
|
2517
|
+
placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
|
|
2518
|
+
subscribe: "\uAD6C\uB3C5\uD558\uAE30",
|
|
2519
|
+
subscribing: "\uAD6C\uB3C5 \uC911...",
|
|
2520
|
+
successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
|
|
2521
|
+
errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
|
|
2522
|
+
privacyNotice: "\uAD6C\uB3C5 \uC2DC \uAC1C\uC778\uC815\uBCF4 \uCC98\uB9AC\uBC29\uCE68\uC5D0 \uB3D9\uC758\uD558\uACE0 \uC5C5\uB370\uC774\uD2B8 \uC218\uC2E0\uC5D0 \uB3D9\uC758\uD558\uB294 \uAC83\uC73C\uB85C \uAC04\uC8FC\uB429\uB2C8\uB2E4."
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
|
|
2526
|
+
// src/i18n/useNewsletterT.ts
|
|
2527
|
+
var translations = { en, ru, ko };
|
|
2528
|
+
function getNestedValue(obj, path) {
|
|
2529
|
+
const keys = path.split(".");
|
|
2530
|
+
let result = obj;
|
|
2531
|
+
for (const key of keys) {
|
|
2532
|
+
if (result && typeof result === "object" && key in result) {
|
|
2533
|
+
result = result[key];
|
|
2534
|
+
} else {
|
|
2535
|
+
return path;
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
return typeof result === "string" ? result : path;
|
|
2539
|
+
}
|
|
2540
|
+
function useNewsletterT() {
|
|
2541
|
+
const locale = nextIntl.useLocale();
|
|
2542
|
+
const t = react.useMemo(() => translations[locale] || translations.en, [locale]);
|
|
2543
|
+
return react.useCallback(
|
|
2544
|
+
(key) => getNestedValue(t, key),
|
|
2545
|
+
[t]
|
|
2546
|
+
);
|
|
2547
|
+
}
|
|
2479
2548
|
var isDevelopment = process.env.NODE_ENV === "development";
|
|
2480
2549
|
var logger = consola.createConsola({
|
|
2481
2550
|
level: isDevelopment ? 4 : 1
|
|
@@ -2487,15 +2556,24 @@ function Hero({
|
|
|
2487
2556
|
primaryAction,
|
|
2488
2557
|
secondaryAction,
|
|
2489
2558
|
showNewsletter = true,
|
|
2490
|
-
newsletterPlaceholder
|
|
2491
|
-
newsletterButtonText
|
|
2559
|
+
newsletterPlaceholder,
|
|
2560
|
+
newsletterButtonText,
|
|
2492
2561
|
onNewsletterSubmit,
|
|
2493
2562
|
className = ""
|
|
2494
2563
|
}) {
|
|
2564
|
+
const nt = useNewsletterT();
|
|
2495
2565
|
const [email, setEmail] = react.useState("");
|
|
2496
2566
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
2497
2567
|
const [status, setStatus] = react.useState("idle");
|
|
2498
2568
|
const [message, setMessage] = react.useState("");
|
|
2569
|
+
const labels = react.useMemo(() => ({
|
|
2570
|
+
placeholder: newsletterPlaceholder ?? nt("hero.placeholder"),
|
|
2571
|
+
subscribe: newsletterButtonText ?? nt("hero.subscribe"),
|
|
2572
|
+
subscribing: nt("hero.subscribing"),
|
|
2573
|
+
successMessage: nt("hero.successMessage"),
|
|
2574
|
+
errorMessage: nt("hero.errorMessage"),
|
|
2575
|
+
privacyNotice: nt("hero.privacyNotice")
|
|
2576
|
+
}), [nt, newsletterPlaceholder, newsletterButtonText]);
|
|
2499
2577
|
const handleSubmit = async (e) => {
|
|
2500
2578
|
e.preventDefault();
|
|
2501
2579
|
if (!email || !onNewsletterSubmit) return;
|
|
@@ -2505,12 +2583,12 @@ function Hero({
|
|
|
2505
2583
|
try {
|
|
2506
2584
|
const result = await onNewsletterSubmit(email);
|
|
2507
2585
|
setStatus("success");
|
|
2508
|
-
setMessage((result && "message" in result ? result.message : void 0) ||
|
|
2586
|
+
setMessage((result && "message" in result ? result.message : void 0) || labels.successMessage);
|
|
2509
2587
|
setEmail("");
|
|
2510
2588
|
newsletterLogger.success("Newsletter subscription successful:", email);
|
|
2511
2589
|
} catch (error) {
|
|
2512
2590
|
setStatus("error");
|
|
2513
|
-
setMessage(error instanceof Error ? error.message :
|
|
2591
|
+
setMessage(error instanceof Error ? error.message : labels.errorMessage);
|
|
2514
2592
|
newsletterLogger.error("Newsletter subscription failed:", error);
|
|
2515
2593
|
} finally {
|
|
2516
2594
|
setIsLoading(false);
|
|
@@ -2553,7 +2631,7 @@ function Hero({
|
|
|
2553
2631
|
uiCore.Input,
|
|
2554
2632
|
{
|
|
2555
2633
|
type: "email",
|
|
2556
|
-
placeholder:
|
|
2634
|
+
placeholder: labels.placeholder,
|
|
2557
2635
|
value: email,
|
|
2558
2636
|
onChange: (e) => setEmail(e.target.value),
|
|
2559
2637
|
disabled: isLoading,
|
|
@@ -2570,8 +2648,8 @@ function Hero({
|
|
|
2570
2648
|
className: "w-full sm:w-auto",
|
|
2571
2649
|
children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2572
2650
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
2573
|
-
|
|
2574
|
-
] }) :
|
|
2651
|
+
labels.subscribing
|
|
2652
|
+
] }) : labels.subscribe
|
|
2575
2653
|
}
|
|
2576
2654
|
)
|
|
2577
2655
|
] }),
|
|
@@ -2584,7 +2662,7 @@ function Hero({
|
|
|
2584
2662
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: message })
|
|
2585
2663
|
] })
|
|
2586
2664
|
] }),
|
|
2587
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-3", children:
|
|
2665
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-3", children: labels.privacyNotice })
|
|
2588
2666
|
] })
|
|
2589
2667
|
] }) }) });
|
|
2590
2668
|
}
|
package/dist/hooks.js
CHANGED
|
@@ -4,10 +4,11 @@ import { z } from 'zod';
|
|
|
4
4
|
import { initializeExtensionAPI, createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
5
5
|
import { createExtensionConfig } from '@djangocfg/ext-base';
|
|
6
6
|
import useSWR, { useSWRConfig } from 'swr';
|
|
7
|
-
import { createContext, useContext, useState } from 'react';
|
|
7
|
+
import { createContext, useContext, useState, useMemo, useCallback } from 'react';
|
|
8
8
|
import { ExtensionProvider } from '@djangocfg/ext-base/hooks';
|
|
9
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
10
|
import { Mail, Loader2, CheckCircle2, AlertCircle } from 'lucide-react';
|
|
11
|
+
import { useLocale } from 'next-intl';
|
|
11
12
|
import { Button, Input } from '@djangocfg/ui-core';
|
|
12
13
|
|
|
13
14
|
var __defProp = Object.defineProperty;
|
|
@@ -2117,7 +2118,7 @@ var apiNewsletter = createExtensionAPI(API);
|
|
|
2117
2118
|
// package.json
|
|
2118
2119
|
var package_default = {
|
|
2119
2120
|
name: "@djangocfg/ext-newsletter",
|
|
2120
|
-
version: "1.0.
|
|
2121
|
+
version: "1.0.23",
|
|
2121
2122
|
description: "Newsletter and subscription management extension for DjangoCFG",
|
|
2122
2123
|
keywords: [
|
|
2123
2124
|
"django",
|
|
@@ -2163,6 +2164,11 @@ var package_default = {
|
|
|
2163
2164
|
types: "./dist/config.d.ts",
|
|
2164
2165
|
import: "./dist/config.js",
|
|
2165
2166
|
require: "./dist/config.cjs"
|
|
2167
|
+
},
|
|
2168
|
+
"./i18n": {
|
|
2169
|
+
types: "./dist/i18n.d.ts",
|
|
2170
|
+
import: "./dist/i18n.js",
|
|
2171
|
+
require: "./dist/i18n.cjs"
|
|
2166
2172
|
}
|
|
2167
2173
|
},
|
|
2168
2174
|
files: [
|
|
@@ -2178,10 +2184,12 @@ var package_default = {
|
|
|
2178
2184
|
peerDependencies: {
|
|
2179
2185
|
"@djangocfg/api": "workspace:*",
|
|
2180
2186
|
"@djangocfg/ext-base": "workspace:*",
|
|
2187
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2181
2188
|
"@djangocfg/ui-core": "workspace:*",
|
|
2182
2189
|
consola: "^3.4.2",
|
|
2183
2190
|
"lucide-react": "^0.545.0",
|
|
2184
2191
|
next: "^16",
|
|
2192
|
+
"next-intl": "^4",
|
|
2185
2193
|
"p-retry": "^7.0.0",
|
|
2186
2194
|
react: "^19",
|
|
2187
2195
|
"react-dom": "^19",
|
|
@@ -2191,10 +2199,12 @@ var package_default = {
|
|
|
2191
2199
|
devDependencies: {
|
|
2192
2200
|
"@djangocfg/api": "workspace:*",
|
|
2193
2201
|
"@djangocfg/ext-base": "workspace:*",
|
|
2202
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2194
2203
|
"@djangocfg/typescript-config": "workspace:*",
|
|
2195
2204
|
"@types/node": "^24.7.2",
|
|
2196
2205
|
"@types/react": "^19.0.0",
|
|
2197
2206
|
consola: "^3.4.2",
|
|
2207
|
+
"next-intl": "^4.1.0",
|
|
2198
2208
|
"p-retry": "^7.0.0",
|
|
2199
2209
|
swr: "^2.3.7",
|
|
2200
2210
|
tsup: "^8.5.0",
|
|
@@ -2469,6 +2479,65 @@ function useNewsletterContext() {
|
|
|
2469
2479
|
}
|
|
2470
2480
|
return context;
|
|
2471
2481
|
}
|
|
2482
|
+
|
|
2483
|
+
// src/i18n/locales/en.ts
|
|
2484
|
+
var en = {
|
|
2485
|
+
hero: {
|
|
2486
|
+
placeholder: "Enter your email",
|
|
2487
|
+
subscribe: "Subscribe",
|
|
2488
|
+
subscribing: "Subscribing...",
|
|
2489
|
+
successMessage: "Successfully subscribed!",
|
|
2490
|
+
errorMessage: "Subscription failed. Please try again.",
|
|
2491
|
+
privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
|
|
2492
|
+
}
|
|
2493
|
+
};
|
|
2494
|
+
|
|
2495
|
+
// src/i18n/locales/ru.ts
|
|
2496
|
+
var ru = {
|
|
2497
|
+
hero: {
|
|
2498
|
+
placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
|
|
2499
|
+
subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
|
|
2500
|
+
subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
|
|
2501
|
+
successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
|
|
2502
|
+
errorMessage: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437.",
|
|
2503
|
+
privacyNotice: "\u041F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u044F\u0441\u044C, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441 \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438 \u0438 \u0434\u0430\u0451\u0442\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043D\u0430 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439."
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
|
|
2507
|
+
// src/i18n/locales/ko.ts
|
|
2508
|
+
var ko = {
|
|
2509
|
+
hero: {
|
|
2510
|
+
placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
|
|
2511
|
+
subscribe: "\uAD6C\uB3C5\uD558\uAE30",
|
|
2512
|
+
subscribing: "\uAD6C\uB3C5 \uC911...",
|
|
2513
|
+
successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
|
|
2514
|
+
errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
|
|
2515
|
+
privacyNotice: "\uAD6C\uB3C5 \uC2DC \uAC1C\uC778\uC815\uBCF4 \uCC98\uB9AC\uBC29\uCE68\uC5D0 \uB3D9\uC758\uD558\uACE0 \uC5C5\uB370\uC774\uD2B8 \uC218\uC2E0\uC5D0 \uB3D9\uC758\uD558\uB294 \uAC83\uC73C\uB85C \uAC04\uC8FC\uB429\uB2C8\uB2E4."
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
|
|
2519
|
+
// src/i18n/useNewsletterT.ts
|
|
2520
|
+
var translations = { en, ru, ko };
|
|
2521
|
+
function getNestedValue(obj, path) {
|
|
2522
|
+
const keys = path.split(".");
|
|
2523
|
+
let result = obj;
|
|
2524
|
+
for (const key of keys) {
|
|
2525
|
+
if (result && typeof result === "object" && key in result) {
|
|
2526
|
+
result = result[key];
|
|
2527
|
+
} else {
|
|
2528
|
+
return path;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
return typeof result === "string" ? result : path;
|
|
2532
|
+
}
|
|
2533
|
+
function useNewsletterT() {
|
|
2534
|
+
const locale = useLocale();
|
|
2535
|
+
const t = useMemo(() => translations[locale] || translations.en, [locale]);
|
|
2536
|
+
return useCallback(
|
|
2537
|
+
(key) => getNestedValue(t, key),
|
|
2538
|
+
[t]
|
|
2539
|
+
);
|
|
2540
|
+
}
|
|
2472
2541
|
var isDevelopment = process.env.NODE_ENV === "development";
|
|
2473
2542
|
var logger = createConsola({
|
|
2474
2543
|
level: isDevelopment ? 4 : 1
|
|
@@ -2480,15 +2549,24 @@ function Hero({
|
|
|
2480
2549
|
primaryAction,
|
|
2481
2550
|
secondaryAction,
|
|
2482
2551
|
showNewsletter = true,
|
|
2483
|
-
newsletterPlaceholder
|
|
2484
|
-
newsletterButtonText
|
|
2552
|
+
newsletterPlaceholder,
|
|
2553
|
+
newsletterButtonText,
|
|
2485
2554
|
onNewsletterSubmit,
|
|
2486
2555
|
className = ""
|
|
2487
2556
|
}) {
|
|
2557
|
+
const nt = useNewsletterT();
|
|
2488
2558
|
const [email, setEmail] = useState("");
|
|
2489
2559
|
const [isLoading, setIsLoading] = useState(false);
|
|
2490
2560
|
const [status, setStatus] = useState("idle");
|
|
2491
2561
|
const [message, setMessage] = useState("");
|
|
2562
|
+
const labels = useMemo(() => ({
|
|
2563
|
+
placeholder: newsletterPlaceholder ?? nt("hero.placeholder"),
|
|
2564
|
+
subscribe: newsletterButtonText ?? nt("hero.subscribe"),
|
|
2565
|
+
subscribing: nt("hero.subscribing"),
|
|
2566
|
+
successMessage: nt("hero.successMessage"),
|
|
2567
|
+
errorMessage: nt("hero.errorMessage"),
|
|
2568
|
+
privacyNotice: nt("hero.privacyNotice")
|
|
2569
|
+
}), [nt, newsletterPlaceholder, newsletterButtonText]);
|
|
2492
2570
|
const handleSubmit = async (e) => {
|
|
2493
2571
|
e.preventDefault();
|
|
2494
2572
|
if (!email || !onNewsletterSubmit) return;
|
|
@@ -2498,12 +2576,12 @@ function Hero({
|
|
|
2498
2576
|
try {
|
|
2499
2577
|
const result = await onNewsletterSubmit(email);
|
|
2500
2578
|
setStatus("success");
|
|
2501
|
-
setMessage((result && "message" in result ? result.message : void 0) ||
|
|
2579
|
+
setMessage((result && "message" in result ? result.message : void 0) || labels.successMessage);
|
|
2502
2580
|
setEmail("");
|
|
2503
2581
|
newsletterLogger.success("Newsletter subscription successful:", email);
|
|
2504
2582
|
} catch (error) {
|
|
2505
2583
|
setStatus("error");
|
|
2506
|
-
setMessage(error instanceof Error ? error.message :
|
|
2584
|
+
setMessage(error instanceof Error ? error.message : labels.errorMessage);
|
|
2507
2585
|
newsletterLogger.error("Newsletter subscription failed:", error);
|
|
2508
2586
|
} finally {
|
|
2509
2587
|
setIsLoading(false);
|
|
@@ -2546,7 +2624,7 @@ function Hero({
|
|
|
2546
2624
|
Input,
|
|
2547
2625
|
{
|
|
2548
2626
|
type: "email",
|
|
2549
|
-
placeholder:
|
|
2627
|
+
placeholder: labels.placeholder,
|
|
2550
2628
|
value: email,
|
|
2551
2629
|
onChange: (e) => setEmail(e.target.value),
|
|
2552
2630
|
disabled: isLoading,
|
|
@@ -2563,8 +2641,8 @@ function Hero({
|
|
|
2563
2641
|
className: "w-full sm:w-auto",
|
|
2564
2642
|
children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2565
2643
|
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
2566
|
-
|
|
2567
|
-
] }) :
|
|
2644
|
+
labels.subscribing
|
|
2645
|
+
] }) : labels.subscribe
|
|
2568
2646
|
}
|
|
2569
2647
|
)
|
|
2570
2648
|
] }),
|
|
@@ -2577,7 +2655,7 @@ function Hero({
|
|
|
2577
2655
|
/* @__PURE__ */ jsx("span", { children: message })
|
|
2578
2656
|
] })
|
|
2579
2657
|
] }),
|
|
2580
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-3", children:
|
|
2658
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-3", children: labels.privacyNotice })
|
|
2581
2659
|
] })
|
|
2582
2660
|
] }) }) });
|
|
2583
2661
|
}
|
package/dist/i18n.cjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/i18n/index.ts
|
|
21
|
+
var i18n_exports = {};
|
|
22
|
+
__export(i18n_exports, {
|
|
23
|
+
en: () => en,
|
|
24
|
+
ko: () => ko,
|
|
25
|
+
ru: () => ru,
|
|
26
|
+
useNewsletterT: () => useNewsletterT
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(i18n_exports);
|
|
29
|
+
|
|
30
|
+
// src/i18n/useNewsletterT.ts
|
|
31
|
+
var import_next_intl = require("next-intl");
|
|
32
|
+
var import_react = require("react");
|
|
33
|
+
|
|
34
|
+
// src/i18n/locales/en.ts
|
|
35
|
+
var en = {
|
|
36
|
+
hero: {
|
|
37
|
+
placeholder: "Enter your email",
|
|
38
|
+
subscribe: "Subscribe",
|
|
39
|
+
subscribing: "Subscribing...",
|
|
40
|
+
successMessage: "Successfully subscribed!",
|
|
41
|
+
errorMessage: "Subscription failed. Please try again.",
|
|
42
|
+
privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/i18n/locales/ru.ts
|
|
47
|
+
var ru = {
|
|
48
|
+
hero: {
|
|
49
|
+
placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
|
|
50
|
+
subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
|
|
51
|
+
subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
|
|
52
|
+
successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
|
|
53
|
+
errorMessage: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437.",
|
|
54
|
+
privacyNotice: "\u041F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u044F\u0441\u044C, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441 \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438 \u0438 \u0434\u0430\u0451\u0442\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043D\u0430 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439."
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/i18n/locales/ko.ts
|
|
59
|
+
var ko = {
|
|
60
|
+
hero: {
|
|
61
|
+
placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
|
|
62
|
+
subscribe: "\uAD6C\uB3C5\uD558\uAE30",
|
|
63
|
+
subscribing: "\uAD6C\uB3C5 \uC911...",
|
|
64
|
+
successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
|
|
65
|
+
errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
|
|
66
|
+
privacyNotice: "\uAD6C\uB3C5 \uC2DC \uAC1C\uC778\uC815\uBCF4 \uCC98\uB9AC\uBC29\uCE68\uC5D0 \uB3D9\uC758\uD558\uACE0 \uC5C5\uB370\uC774\uD2B8 \uC218\uC2E0\uC5D0 \uB3D9\uC758\uD558\uB294 \uAC83\uC73C\uB85C \uAC04\uC8FC\uB429\uB2C8\uB2E4."
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/i18n/useNewsletterT.ts
|
|
71
|
+
var translations = { en, ru, ko };
|
|
72
|
+
function getNestedValue(obj, path) {
|
|
73
|
+
const keys = path.split(".");
|
|
74
|
+
let result = obj;
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
if (result && typeof result === "object" && key in result) {
|
|
77
|
+
result = result[key];
|
|
78
|
+
} else {
|
|
79
|
+
return path;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return typeof result === "string" ? result : path;
|
|
83
|
+
}
|
|
84
|
+
function useNewsletterT() {
|
|
85
|
+
const locale = (0, import_next_intl.useLocale)();
|
|
86
|
+
const t = (0, import_react.useMemo)(() => translations[locale] || translations.en, [locale]);
|
|
87
|
+
return (0, import_react.useCallback)(
|
|
88
|
+
(key) => getNestedValue(t, key),
|
|
89
|
+
[t]
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
93
|
+
0 && (module.exports = {
|
|
94
|
+
en,
|
|
95
|
+
ko,
|
|
96
|
+
ru,
|
|
97
|
+
useNewsletterT
|
|
98
|
+
});
|
package/dist/i18n.d.cts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter Extension I18n Types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Helper type to get dot-notation paths from nested object
|
|
6
|
+
*/
|
|
7
|
+
type PathKeys<T, Prefix extends string = ''> = T extends object ? {
|
|
8
|
+
[K in keyof T]: K extends string ? T[K] extends object ? PathKeys<T[K], `${Prefix}${K}.`> : `${Prefix}${K}` : never;
|
|
9
|
+
}[keyof T] : never;
|
|
10
|
+
/**
|
|
11
|
+
* Keys for newsletter translations
|
|
12
|
+
*/
|
|
13
|
+
type NewsletterLocalKeys = PathKeys<NewsletterTranslations>;
|
|
14
|
+
interface NewsletterTranslations {
|
|
15
|
+
/** Hero */
|
|
16
|
+
hero: {
|
|
17
|
+
placeholder: string;
|
|
18
|
+
subscribe: string;
|
|
19
|
+
subscribing: string;
|
|
20
|
+
successMessage: string;
|
|
21
|
+
errorMessage: string;
|
|
22
|
+
privacyNotice: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Self-contained translation hook for newsletter extension
|
|
28
|
+
*
|
|
29
|
+
* Uses built-in translations based on current locale from next-intl.
|
|
30
|
+
* No need to add translations to app's i18n config.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* function SubscribeForm() {
|
|
35
|
+
* const t = useNewsletterT();
|
|
36
|
+
* return <button>{t('hero.subscribe')}</button>;
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function useNewsletterT(): (key: NewsletterLocalKeys) => string;
|
|
41
|
+
|
|
42
|
+
declare const en: NewsletterTranslations;
|
|
43
|
+
|
|
44
|
+
declare const ru: NewsletterTranslations;
|
|
45
|
+
|
|
46
|
+
declare const ko: NewsletterTranslations;
|
|
47
|
+
|
|
48
|
+
export { type NewsletterLocalKeys, type NewsletterTranslations, en, ko, ru, useNewsletterT };
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter Extension I18n Types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Helper type to get dot-notation paths from nested object
|
|
6
|
+
*/
|
|
7
|
+
type PathKeys<T, Prefix extends string = ''> = T extends object ? {
|
|
8
|
+
[K in keyof T]: K extends string ? T[K] extends object ? PathKeys<T[K], `${Prefix}${K}.`> : `${Prefix}${K}` : never;
|
|
9
|
+
}[keyof T] : never;
|
|
10
|
+
/**
|
|
11
|
+
* Keys for newsletter translations
|
|
12
|
+
*/
|
|
13
|
+
type NewsletterLocalKeys = PathKeys<NewsletterTranslations>;
|
|
14
|
+
interface NewsletterTranslations {
|
|
15
|
+
/** Hero */
|
|
16
|
+
hero: {
|
|
17
|
+
placeholder: string;
|
|
18
|
+
subscribe: string;
|
|
19
|
+
subscribing: string;
|
|
20
|
+
successMessage: string;
|
|
21
|
+
errorMessage: string;
|
|
22
|
+
privacyNotice: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Self-contained translation hook for newsletter extension
|
|
28
|
+
*
|
|
29
|
+
* Uses built-in translations based on current locale from next-intl.
|
|
30
|
+
* No need to add translations to app's i18n config.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* function SubscribeForm() {
|
|
35
|
+
* const t = useNewsletterT();
|
|
36
|
+
* return <button>{t('hero.subscribe')}</button>;
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function useNewsletterT(): (key: NewsletterLocalKeys) => string;
|
|
41
|
+
|
|
42
|
+
declare const en: NewsletterTranslations;
|
|
43
|
+
|
|
44
|
+
declare const ru: NewsletterTranslations;
|
|
45
|
+
|
|
46
|
+
declare const ko: NewsletterTranslations;
|
|
47
|
+
|
|
48
|
+
export { type NewsletterLocalKeys, type NewsletterTranslations, en, ko, ru, useNewsletterT };
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/i18n/useNewsletterT.ts
|
|
4
|
+
import { useLocale } from "next-intl";
|
|
5
|
+
import { useMemo, useCallback } from "react";
|
|
6
|
+
|
|
7
|
+
// src/i18n/locales/en.ts
|
|
8
|
+
var en = {
|
|
9
|
+
hero: {
|
|
10
|
+
placeholder: "Enter your email",
|
|
11
|
+
subscribe: "Subscribe",
|
|
12
|
+
subscribing: "Subscribing...",
|
|
13
|
+
successMessage: "Successfully subscribed!",
|
|
14
|
+
errorMessage: "Subscription failed. Please try again.",
|
|
15
|
+
privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// src/i18n/locales/ru.ts
|
|
20
|
+
var ru = {
|
|
21
|
+
hero: {
|
|
22
|
+
placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
|
|
23
|
+
subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
|
|
24
|
+
subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
|
|
25
|
+
successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
|
|
26
|
+
errorMessage: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437.",
|
|
27
|
+
privacyNotice: "\u041F\u043E\u0434\u043F\u0438\u0441\u044B\u0432\u0430\u044F\u0441\u044C, \u0432\u044B \u0441\u043E\u0433\u043B\u0430\u0448\u0430\u0435\u0442\u0435\u0441\u044C \u0441 \u041F\u043E\u043B\u0438\u0442\u0438\u043A\u043E\u0439 \u043A\u043E\u043D\u0444\u0438\u0434\u0435\u043D\u0446\u0438\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u0438 \u0438 \u0434\u0430\u0451\u0442\u0435 \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u043D\u0430 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u0438\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439."
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/i18n/locales/ko.ts
|
|
32
|
+
var ko = {
|
|
33
|
+
hero: {
|
|
34
|
+
placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
|
|
35
|
+
subscribe: "\uAD6C\uB3C5\uD558\uAE30",
|
|
36
|
+
subscribing: "\uAD6C\uB3C5 \uC911...",
|
|
37
|
+
successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
|
|
38
|
+
errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
|
|
39
|
+
privacyNotice: "\uAD6C\uB3C5 \uC2DC \uAC1C\uC778\uC815\uBCF4 \uCC98\uB9AC\uBC29\uCE68\uC5D0 \uB3D9\uC758\uD558\uACE0 \uC5C5\uB370\uC774\uD2B8 \uC218\uC2E0\uC5D0 \uB3D9\uC758\uD558\uB294 \uAC83\uC73C\uB85C \uAC04\uC8FC\uB429\uB2C8\uB2E4."
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/i18n/useNewsletterT.ts
|
|
44
|
+
var translations = { en, ru, ko };
|
|
45
|
+
function getNestedValue(obj, path) {
|
|
46
|
+
const keys = path.split(".");
|
|
47
|
+
let result = obj;
|
|
48
|
+
for (const key of keys) {
|
|
49
|
+
if (result && typeof result === "object" && key in result) {
|
|
50
|
+
result = result[key];
|
|
51
|
+
} else {
|
|
52
|
+
return path;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return typeof result === "string" ? result : path;
|
|
56
|
+
}
|
|
57
|
+
function useNewsletterT() {
|
|
58
|
+
const locale = useLocale();
|
|
59
|
+
const t = useMemo(() => translations[locale] || translations.en, [locale]);
|
|
60
|
+
return useCallback(
|
|
61
|
+
(key) => getNestedValue(t, key),
|
|
62
|
+
[t]
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
en,
|
|
67
|
+
ko,
|
|
68
|
+
ru,
|
|
69
|
+
useNewsletterT
|
|
70
|
+
};
|
package/dist/index.cjs
CHANGED
|
@@ -2117,7 +2117,7 @@ var apiNewsletter = api.createExtensionAPI(API);
|
|
|
2117
2117
|
// package.json
|
|
2118
2118
|
var package_default = {
|
|
2119
2119
|
name: "@djangocfg/ext-newsletter",
|
|
2120
|
-
version: "1.0.
|
|
2120
|
+
version: "1.0.23",
|
|
2121
2121
|
description: "Newsletter and subscription management extension for DjangoCFG",
|
|
2122
2122
|
keywords: [
|
|
2123
2123
|
"django",
|
|
@@ -2163,6 +2163,11 @@ var package_default = {
|
|
|
2163
2163
|
types: "./dist/config.d.ts",
|
|
2164
2164
|
import: "./dist/config.js",
|
|
2165
2165
|
require: "./dist/config.cjs"
|
|
2166
|
+
},
|
|
2167
|
+
"./i18n": {
|
|
2168
|
+
types: "./dist/i18n.d.ts",
|
|
2169
|
+
import: "./dist/i18n.js",
|
|
2170
|
+
require: "./dist/i18n.cjs"
|
|
2166
2171
|
}
|
|
2167
2172
|
},
|
|
2168
2173
|
files: [
|
|
@@ -2178,10 +2183,12 @@ var package_default = {
|
|
|
2178
2183
|
peerDependencies: {
|
|
2179
2184
|
"@djangocfg/api": "workspace:*",
|
|
2180
2185
|
"@djangocfg/ext-base": "workspace:*",
|
|
2186
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2181
2187
|
"@djangocfg/ui-core": "workspace:*",
|
|
2182
2188
|
consola: "^3.4.2",
|
|
2183
2189
|
"lucide-react": "^0.545.0",
|
|
2184
2190
|
next: "^16",
|
|
2191
|
+
"next-intl": "^4",
|
|
2185
2192
|
"p-retry": "^7.0.0",
|
|
2186
2193
|
react: "^19",
|
|
2187
2194
|
"react-dom": "^19",
|
|
@@ -2191,10 +2198,12 @@ var package_default = {
|
|
|
2191
2198
|
devDependencies: {
|
|
2192
2199
|
"@djangocfg/api": "workspace:*",
|
|
2193
2200
|
"@djangocfg/ext-base": "workspace:*",
|
|
2201
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2194
2202
|
"@djangocfg/typescript-config": "workspace:*",
|
|
2195
2203
|
"@types/node": "^24.7.2",
|
|
2196
2204
|
"@types/react": "^19.0.0",
|
|
2197
2205
|
consola: "^3.4.2",
|
|
2206
|
+
"next-intl": "^4.1.0",
|
|
2198
2207
|
"p-retry": "^7.0.0",
|
|
2199
2208
|
swr: "^2.3.7",
|
|
2200
2209
|
tsup: "^8.5.0",
|
package/dist/index.js
CHANGED
|
@@ -2111,7 +2111,7 @@ var apiNewsletter = createExtensionAPI(API);
|
|
|
2111
2111
|
// package.json
|
|
2112
2112
|
var package_default = {
|
|
2113
2113
|
name: "@djangocfg/ext-newsletter",
|
|
2114
|
-
version: "1.0.
|
|
2114
|
+
version: "1.0.23",
|
|
2115
2115
|
description: "Newsletter and subscription management extension for DjangoCFG",
|
|
2116
2116
|
keywords: [
|
|
2117
2117
|
"django",
|
|
@@ -2157,6 +2157,11 @@ var package_default = {
|
|
|
2157
2157
|
types: "./dist/config.d.ts",
|
|
2158
2158
|
import: "./dist/config.js",
|
|
2159
2159
|
require: "./dist/config.cjs"
|
|
2160
|
+
},
|
|
2161
|
+
"./i18n": {
|
|
2162
|
+
types: "./dist/i18n.d.ts",
|
|
2163
|
+
import: "./dist/i18n.js",
|
|
2164
|
+
require: "./dist/i18n.cjs"
|
|
2160
2165
|
}
|
|
2161
2166
|
},
|
|
2162
2167
|
files: [
|
|
@@ -2172,10 +2177,12 @@ var package_default = {
|
|
|
2172
2177
|
peerDependencies: {
|
|
2173
2178
|
"@djangocfg/api": "workspace:*",
|
|
2174
2179
|
"@djangocfg/ext-base": "workspace:*",
|
|
2180
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2175
2181
|
"@djangocfg/ui-core": "workspace:*",
|
|
2176
2182
|
consola: "^3.4.2",
|
|
2177
2183
|
"lucide-react": "^0.545.0",
|
|
2178
2184
|
next: "^16",
|
|
2185
|
+
"next-intl": "^4",
|
|
2179
2186
|
"p-retry": "^7.0.0",
|
|
2180
2187
|
react: "^19",
|
|
2181
2188
|
"react-dom": "^19",
|
|
@@ -2185,10 +2192,12 @@ var package_default = {
|
|
|
2185
2192
|
devDependencies: {
|
|
2186
2193
|
"@djangocfg/api": "workspace:*",
|
|
2187
2194
|
"@djangocfg/ext-base": "workspace:*",
|
|
2195
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2188
2196
|
"@djangocfg/typescript-config": "workspace:*",
|
|
2189
2197
|
"@types/node": "^24.7.2",
|
|
2190
2198
|
"@types/react": "^19.0.0",
|
|
2191
2199
|
consola: "^3.4.2",
|
|
2200
|
+
"next-intl": "^4.1.0",
|
|
2192
2201
|
"p-retry": "^7.0.0",
|
|
2193
2202
|
swr: "^2.3.7",
|
|
2194
2203
|
tsup: "^8.5.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ext-newsletter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "Newsletter and subscription management extension for DjangoCFG",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"django",
|
|
@@ -46,6 +46,11 @@
|
|
|
46
46
|
"types": "./dist/config.d.ts",
|
|
47
47
|
"import": "./dist/config.js",
|
|
48
48
|
"require": "./dist/config.cjs"
|
|
49
|
+
},
|
|
50
|
+
"./i18n": {
|
|
51
|
+
"types": "./dist/i18n.d.ts",
|
|
52
|
+
"import": "./dist/i18n.js",
|
|
53
|
+
"require": "./dist/i18n.cjs"
|
|
49
54
|
}
|
|
50
55
|
},
|
|
51
56
|
"files": [
|
|
@@ -59,12 +64,14 @@
|
|
|
59
64
|
"check": "tsc --noEmit"
|
|
60
65
|
},
|
|
61
66
|
"peerDependencies": {
|
|
62
|
-
"@djangocfg/api": "^2.1.
|
|
63
|
-
"@djangocfg/ext-base": "^1.0.
|
|
64
|
-
"@djangocfg/
|
|
67
|
+
"@djangocfg/api": "^2.1.124",
|
|
68
|
+
"@djangocfg/ext-base": "^1.0.18",
|
|
69
|
+
"@djangocfg/i18n": "^2.1.124",
|
|
70
|
+
"@djangocfg/ui-core": "^2.1.124",
|
|
65
71
|
"consola": "^3.4.2",
|
|
66
72
|
"lucide-react": "^0.545.0",
|
|
67
73
|
"next": "^16",
|
|
74
|
+
"next-intl": "^4",
|
|
68
75
|
"p-retry": "^7.0.0",
|
|
69
76
|
"react": "^19",
|
|
70
77
|
"react-dom": "^19",
|
|
@@ -72,12 +79,14 @@
|
|
|
72
79
|
"zod": "^4.3.4"
|
|
73
80
|
},
|
|
74
81
|
"devDependencies": {
|
|
75
|
-
"@djangocfg/api": "^2.1.
|
|
76
|
-
"@djangocfg/ext-base": "^1.0.
|
|
77
|
-
"@djangocfg/
|
|
82
|
+
"@djangocfg/api": "^2.1.124",
|
|
83
|
+
"@djangocfg/ext-base": "^1.0.18",
|
|
84
|
+
"@djangocfg/i18n": "^2.1.124",
|
|
85
|
+
"@djangocfg/typescript-config": "^2.1.124",
|
|
78
86
|
"@types/node": "^24.7.2",
|
|
79
87
|
"@types/react": "^19.0.0",
|
|
80
88
|
"consola": "^3.4.2",
|
|
89
|
+
"next-intl": "^4.1.0",
|
|
81
90
|
"p-retry": "^7.0.0",
|
|
82
91
|
"swr": "^2.3.7",
|
|
83
92
|
"tsup": "^8.5.0",
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
8
|
import { AlertCircle, CheckCircle2, Loader2, Mail } from 'lucide-react';
|
|
9
|
-
import React, { useState } from 'react';
|
|
9
|
+
import React, { useMemo, useState } from 'react';
|
|
10
10
|
|
|
11
|
+
import { useNewsletterT } from '../../i18n';
|
|
11
12
|
import { Button, Input } from '@djangocfg/ui-core';
|
|
12
13
|
|
|
13
14
|
import { newsletterLogger } from '../../utils/logger';
|
|
@@ -20,16 +21,27 @@ export function Hero({
|
|
|
20
21
|
primaryAction,
|
|
21
22
|
secondaryAction,
|
|
22
23
|
showNewsletter = true,
|
|
23
|
-
newsletterPlaceholder
|
|
24
|
-
newsletterButtonText
|
|
24
|
+
newsletterPlaceholder,
|
|
25
|
+
newsletterButtonText,
|
|
25
26
|
onNewsletterSubmit,
|
|
26
27
|
className = '',
|
|
27
28
|
}: HeroProps) {
|
|
29
|
+
const nt = useNewsletterT();
|
|
28
30
|
const [email, setEmail] = useState('');
|
|
29
31
|
const [isLoading, setIsLoading] = useState(false);
|
|
30
32
|
const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle');
|
|
31
33
|
const [message, setMessage] = useState('');
|
|
32
34
|
|
|
35
|
+
// Prepare labels before JSX render
|
|
36
|
+
const labels = useMemo(() => ({
|
|
37
|
+
placeholder: newsletterPlaceholder ?? nt('hero.placeholder'),
|
|
38
|
+
subscribe: newsletterButtonText ?? nt('hero.subscribe'),
|
|
39
|
+
subscribing: nt('hero.subscribing'),
|
|
40
|
+
successMessage: nt('hero.successMessage'),
|
|
41
|
+
errorMessage: nt('hero.errorMessage'),
|
|
42
|
+
privacyNotice: nt('hero.privacyNotice'),
|
|
43
|
+
}), [nt, newsletterPlaceholder, newsletterButtonText]);
|
|
44
|
+
|
|
33
45
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
34
46
|
e.preventDefault();
|
|
35
47
|
|
|
@@ -42,12 +54,12 @@ export function Hero({
|
|
|
42
54
|
try {
|
|
43
55
|
const result = await onNewsletterSubmit(email);
|
|
44
56
|
setStatus('success');
|
|
45
|
-
setMessage((result && 'message' in result ? result.message : undefined) ||
|
|
57
|
+
setMessage((result && 'message' in result ? result.message : undefined) || labels.successMessage);
|
|
46
58
|
setEmail('');
|
|
47
59
|
newsletterLogger.success('Newsletter subscription successful:', email);
|
|
48
60
|
} catch (error) {
|
|
49
61
|
setStatus('error');
|
|
50
|
-
setMessage(error instanceof Error ? error.message :
|
|
62
|
+
setMessage(error instanceof Error ? error.message : labels.errorMessage);
|
|
51
63
|
newsletterLogger.error('Newsletter subscription failed:', error);
|
|
52
64
|
} finally {
|
|
53
65
|
setIsLoading(false);
|
|
@@ -109,7 +121,7 @@ export function Hero({
|
|
|
109
121
|
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground" />
|
|
110
122
|
<Input
|
|
111
123
|
type="email"
|
|
112
|
-
placeholder={
|
|
124
|
+
placeholder={labels.placeholder}
|
|
113
125
|
value={email}
|
|
114
126
|
onChange={(e) => setEmail(e.target.value)}
|
|
115
127
|
disabled={isLoading}
|
|
@@ -125,10 +137,10 @@ export function Hero({
|
|
|
125
137
|
{isLoading ? (
|
|
126
138
|
<>
|
|
127
139
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
128
|
-
|
|
140
|
+
{labels.subscribing}
|
|
129
141
|
</>
|
|
130
142
|
) : (
|
|
131
|
-
|
|
143
|
+
labels.subscribe
|
|
132
144
|
)}
|
|
133
145
|
</Button>
|
|
134
146
|
</div>
|
|
@@ -150,7 +162,7 @@ export function Hero({
|
|
|
150
162
|
</form>
|
|
151
163
|
|
|
152
164
|
<p className="text-xs text-muted-foreground mt-3">
|
|
153
|
-
|
|
165
|
+
{labels.privacyNotice}
|
|
154
166
|
</p>
|
|
155
167
|
</div>
|
|
156
168
|
)}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter Extension I18n
|
|
3
|
+
*
|
|
4
|
+
* Self-contained translations - no app configuration needed.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { useNewsletterT } from '@djangocfg/ext-newsletter/i18n';
|
|
9
|
+
*
|
|
10
|
+
* function MyComponent() {
|
|
11
|
+
* const t = useNewsletterT();
|
|
12
|
+
* return <button>{t('hero.subscribe')}</button>;
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Self-contained hook (recommended)
|
|
18
|
+
export { useNewsletterT } from './useNewsletterT';
|
|
19
|
+
|
|
20
|
+
// Types
|
|
21
|
+
export type { NewsletterTranslations, NewsletterLocalKeys } from './types';
|
|
22
|
+
|
|
23
|
+
// Locales (for direct access if needed)
|
|
24
|
+
export { en } from './locales/en';
|
|
25
|
+
export { ru } from './locales/ru';
|
|
26
|
+
export { ko } from './locales/ko';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NewsletterTranslations } from '../types';
|
|
2
|
+
|
|
3
|
+
export const en: NewsletterTranslations = {
|
|
4
|
+
hero: {
|
|
5
|
+
placeholder: 'Enter your email',
|
|
6
|
+
subscribe: 'Subscribe',
|
|
7
|
+
subscribing: 'Subscribing...',
|
|
8
|
+
successMessage: 'Successfully subscribed!',
|
|
9
|
+
errorMessage: 'Subscription failed. Please try again.',
|
|
10
|
+
privacyNotice: 'By subscribing, you agree to our Privacy Policy and consent to receive updates.',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NewsletterTranslations } from '../types';
|
|
2
|
+
|
|
3
|
+
export const ko: NewsletterTranslations = {
|
|
4
|
+
hero: {
|
|
5
|
+
placeholder: '이메일을 입력하세요',
|
|
6
|
+
subscribe: '구독하기',
|
|
7
|
+
subscribing: '구독 중...',
|
|
8
|
+
successMessage: '성공적으로 구독되었습니다!',
|
|
9
|
+
errorMessage: '구독에 실패했습니다. 다시 시도해 주세요.',
|
|
10
|
+
privacyNotice: '구독 시 개인정보 처리방침에 동의하고 업데이트 수신에 동의하는 것으로 간주됩니다.',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NewsletterTranslations } from '../types';
|
|
2
|
+
|
|
3
|
+
export const ru: NewsletterTranslations = {
|
|
4
|
+
hero: {
|
|
5
|
+
placeholder: 'Введите email',
|
|
6
|
+
subscribe: 'Подписаться',
|
|
7
|
+
subscribing: 'Подписка...',
|
|
8
|
+
successMessage: 'Вы успешно подписались!',
|
|
9
|
+
errorMessage: 'Не удалось подписаться. Попробуйте ещё раз.',
|
|
10
|
+
privacyNotice: 'Подписываясь, вы соглашаетесь с Политикой конфиденциальности и даёте согласие на получение обновлений.',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter Extension I18n Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Helper type to get dot-notation paths from nested object
|
|
7
|
+
*/
|
|
8
|
+
type PathKeys<T, Prefix extends string = ''> = T extends object
|
|
9
|
+
? {
|
|
10
|
+
[K in keyof T]: K extends string
|
|
11
|
+
? T[K] extends object
|
|
12
|
+
? PathKeys<T[K], `${Prefix}${K}.`>
|
|
13
|
+
: `${Prefix}${K}`
|
|
14
|
+
: never;
|
|
15
|
+
}[keyof T]
|
|
16
|
+
: never;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Keys for newsletter translations
|
|
20
|
+
*/
|
|
21
|
+
export type NewsletterLocalKeys = PathKeys<NewsletterTranslations>;
|
|
22
|
+
|
|
23
|
+
export interface NewsletterTranslations {
|
|
24
|
+
/** Hero */
|
|
25
|
+
hero: {
|
|
26
|
+
placeholder: string;
|
|
27
|
+
subscribe: string;
|
|
28
|
+
subscribing: string;
|
|
29
|
+
successMessage: string;
|
|
30
|
+
errorMessage: string;
|
|
31
|
+
privacyNotice: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Self-contained translation hook for ext-newsletter
|
|
5
|
+
*
|
|
6
|
+
* Uses built-in translations, no app configuration needed.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useLocale } from 'next-intl';
|
|
10
|
+
import { useMemo, useCallback } from 'react';
|
|
11
|
+
|
|
12
|
+
import type { NewsletterTranslations, NewsletterLocalKeys } from './types';
|
|
13
|
+
import { en } from './locales/en';
|
|
14
|
+
import { ru } from './locales/ru';
|
|
15
|
+
import { ko } from './locales/ko';
|
|
16
|
+
|
|
17
|
+
const translations: Record<string, NewsletterTranslations> = { en, ru, ko };
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get nested value from object by dot-notation path
|
|
21
|
+
*/
|
|
22
|
+
function getNestedValue(obj: Record<string, unknown>, path: string): string {
|
|
23
|
+
const keys = path.split('.');
|
|
24
|
+
let result: unknown = obj;
|
|
25
|
+
|
|
26
|
+
for (const key of keys) {
|
|
27
|
+
if (result && typeof result === 'object' && key in result) {
|
|
28
|
+
result = (result as Record<string, unknown>)[key];
|
|
29
|
+
} else {
|
|
30
|
+
return path; // Return key if not found
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return typeof result === 'string' ? result : path;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Self-contained translation hook for newsletter extension
|
|
39
|
+
*
|
|
40
|
+
* Uses built-in translations based on current locale from next-intl.
|
|
41
|
+
* No need to add translations to app's i18n config.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* function SubscribeForm() {
|
|
46
|
+
* const t = useNewsletterT();
|
|
47
|
+
* return <button>{t('hero.subscribe')}</button>;
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function useNewsletterT(): (key: NewsletterLocalKeys) => string {
|
|
52
|
+
const locale = useLocale();
|
|
53
|
+
|
|
54
|
+
const t = useMemo(() => translations[locale] || translations.en, [locale]);
|
|
55
|
+
|
|
56
|
+
return useCallback(
|
|
57
|
+
(key: NewsletterLocalKeys): string => getNestedValue(t as unknown as Record<string, unknown>, key),
|
|
58
|
+
[t]
|
|
59
|
+
);
|
|
60
|
+
}
|