@djangocfg/ext-newsletter 1.0.21 → 1.0.22

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 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.21",
30
+ version: "1.0.22",
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,6 +93,7 @@ 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",
@@ -101,6 +107,7 @@ var package_default = {
101
107
  devDependencies: {
102
108
  "@djangocfg/api": "workspace:*",
103
109
  "@djangocfg/ext-base": "workspace:*",
110
+ "@djangocfg/i18n": "workspace:*",
104
111
  "@djangocfg/typescript-config": "workspace:*",
105
112
  "@types/node": "^24.7.2",
106
113
  "@types/react": "^19.0.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.21",
7
+ version: "1.0.22",
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,6 +70,7 @@ 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",
@@ -78,6 +84,7 @@ var package_default = {
78
84
  devDependencies: {
79
85
  "@djangocfg/api": "workspace:*",
80
86
  "@djangocfg/ext-base": "workspace:*",
87
+ "@djangocfg/i18n": "workspace:*",
81
88
  "@djangocfg/typescript-config": "workspace:*",
82
89
  "@types/node": "^24.7.2",
83
90
  "@types/react": "^19.0.0",
package/dist/hooks.cjs CHANGED
@@ -10,6 +10,8 @@ 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 i18n = require('@djangocfg/ext-base/i18n');
14
+ var i18n$1 = require('@djangocfg/i18n');
13
15
  var uiCore = require('@djangocfg/ui-core');
14
16
 
15
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -2124,7 +2126,7 @@ var apiNewsletter = api.createExtensionAPI(API);
2124
2126
  // package.json
2125
2127
  var package_default = {
2126
2128
  name: "@djangocfg/ext-newsletter",
2127
- version: "1.0.21",
2129
+ version: "1.0.22",
2128
2130
  description: "Newsletter and subscription management extension for DjangoCFG",
2129
2131
  keywords: [
2130
2132
  "django",
@@ -2170,6 +2172,11 @@ var package_default = {
2170
2172
  types: "./dist/config.d.ts",
2171
2173
  import: "./dist/config.js",
2172
2174
  require: "./dist/config.cjs"
2175
+ },
2176
+ "./i18n": {
2177
+ types: "./dist/i18n.d.ts",
2178
+ import: "./dist/i18n.js",
2179
+ require: "./dist/i18n.cjs"
2173
2180
  }
2174
2181
  },
2175
2182
  files: [
@@ -2185,6 +2192,7 @@ var package_default = {
2185
2192
  peerDependencies: {
2186
2193
  "@djangocfg/api": "workspace:*",
2187
2194
  "@djangocfg/ext-base": "workspace:*",
2195
+ "@djangocfg/i18n": "workspace:*",
2188
2196
  "@djangocfg/ui-core": "workspace:*",
2189
2197
  consola: "^3.4.2",
2190
2198
  "lucide-react": "^0.545.0",
@@ -2198,6 +2206,7 @@ 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",
@@ -2476,6 +2485,51 @@ function useNewsletterContext() {
2476
2485
  }
2477
2486
  return context;
2478
2487
  }
2488
+
2489
+ // src/i18n/locales/en.ts
2490
+ var en = {
2491
+ hero: {
2492
+ placeholder: "Enter your email",
2493
+ subscribe: "Subscribe",
2494
+ subscribing: "Subscribing...",
2495
+ successMessage: "Successfully subscribed!",
2496
+ errorMessage: "Subscription failed. Please try again.",
2497
+ privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
2498
+ }
2499
+ };
2500
+
2501
+ // src/i18n/locales/ru.ts
2502
+ var ru = {
2503
+ hero: {
2504
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
2505
+ subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
2506
+ subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
2507
+ successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
2508
+ 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.",
2509
+ 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."
2510
+ }
2511
+ };
2512
+
2513
+ // src/i18n/locales/ko.ts
2514
+ var ko = {
2515
+ hero: {
2516
+ placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
2517
+ subscribe: "\uAD6C\uB3C5\uD558\uAE30",
2518
+ subscribing: "\uAD6C\uB3C5 \uC911...",
2519
+ successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
2520
+ errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
2521
+ 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."
2522
+ }
2523
+ };
2524
+
2525
+ // src/i18n/index.ts
2526
+ var NEWSLETTER_NAMESPACE = "newsletter";
2527
+ var newsletterI18n = i18n.createExtensionI18n({
2528
+ namespace: NEWSLETTER_NAMESPACE,
2529
+ defaultLocale: "en",
2530
+ locales: { en, ru, ko }
2531
+ });
2532
+ newsletterI18n.getAllTranslations();
2479
2533
  var isDevelopment = process.env.NODE_ENV === "development";
2480
2534
  var logger = consola.createConsola({
2481
2535
  level: isDevelopment ? 4 : 1
@@ -2487,15 +2541,25 @@ function Hero({
2487
2541
  primaryAction,
2488
2542
  secondaryAction,
2489
2543
  showNewsletter = true,
2490
- newsletterPlaceholder = "Enter your email",
2491
- newsletterButtonText = "Subscribe",
2544
+ newsletterPlaceholder,
2545
+ newsletterButtonText,
2492
2546
  onNewsletterSubmit,
2493
2547
  className = ""
2494
2548
  }) {
2549
+ const baseT = i18n$1.useT();
2495
2550
  const [email, setEmail] = react.useState("");
2496
2551
  const [isLoading, setIsLoading] = react.useState(false);
2497
2552
  const [status, setStatus] = react.useState("idle");
2498
2553
  const [message, setMessage] = react.useState("");
2554
+ const nt = i18n.createTypedExtensionT(baseT, NEWSLETTER_NAMESPACE);
2555
+ const labels = react.useMemo(() => ({
2556
+ placeholder: newsletterPlaceholder ?? nt("hero.placeholder"),
2557
+ subscribe: newsletterButtonText ?? nt("hero.subscribe"),
2558
+ subscribing: nt("hero.subscribing"),
2559
+ successMessage: nt("hero.successMessage"),
2560
+ errorMessage: nt("hero.errorMessage"),
2561
+ privacyNotice: nt("hero.privacyNotice")
2562
+ }), [nt, newsletterPlaceholder, newsletterButtonText]);
2499
2563
  const handleSubmit = async (e) => {
2500
2564
  e.preventDefault();
2501
2565
  if (!email || !onNewsletterSubmit) return;
@@ -2505,12 +2569,12 @@ function Hero({
2505
2569
  try {
2506
2570
  const result = await onNewsletterSubmit(email);
2507
2571
  setStatus("success");
2508
- setMessage((result && "message" in result ? result.message : void 0) || "Successfully subscribed!");
2572
+ setMessage((result && "message" in result ? result.message : void 0) || labels.successMessage);
2509
2573
  setEmail("");
2510
2574
  newsletterLogger.success("Newsletter subscription successful:", email);
2511
2575
  } catch (error) {
2512
2576
  setStatus("error");
2513
- setMessage(error instanceof Error ? error.message : "Subscription failed. Please try again.");
2577
+ setMessage(error instanceof Error ? error.message : labels.errorMessage);
2514
2578
  newsletterLogger.error("Newsletter subscription failed:", error);
2515
2579
  } finally {
2516
2580
  setIsLoading(false);
@@ -2553,7 +2617,7 @@ function Hero({
2553
2617
  uiCore.Input,
2554
2618
  {
2555
2619
  type: "email",
2556
- placeholder: newsletterPlaceholder,
2620
+ placeholder: labels.placeholder,
2557
2621
  value: email,
2558
2622
  onChange: (e) => setEmail(e.target.value),
2559
2623
  disabled: isLoading,
@@ -2570,8 +2634,8 @@ function Hero({
2570
2634
  className: "w-full sm:w-auto",
2571
2635
  children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2572
2636
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
2573
- "Subscribing..."
2574
- ] }) : newsletterButtonText
2637
+ labels.subscribing
2638
+ ] }) : labels.subscribe
2575
2639
  }
2576
2640
  )
2577
2641
  ] }),
@@ -2584,7 +2648,7 @@ function Hero({
2584
2648
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: message })
2585
2649
  ] })
2586
2650
  ] }),
2587
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-3", children: "By subscribing, you agree to our Privacy Policy and consent to receive updates." })
2651
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-3", children: labels.privacyNotice })
2588
2652
  ] })
2589
2653
  ] }) }) });
2590
2654
  }
package/dist/hooks.js CHANGED
@@ -4,10 +4,12 @@ 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 } 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 { createExtensionI18n, createTypedExtensionT } from '@djangocfg/ext-base/i18n';
12
+ import { useT } from '@djangocfg/i18n';
11
13
  import { Button, Input } from '@djangocfg/ui-core';
12
14
 
13
15
  var __defProp = Object.defineProperty;
@@ -2117,7 +2119,7 @@ var apiNewsletter = createExtensionAPI(API);
2117
2119
  // package.json
2118
2120
  var package_default = {
2119
2121
  name: "@djangocfg/ext-newsletter",
2120
- version: "1.0.21",
2122
+ version: "1.0.22",
2121
2123
  description: "Newsletter and subscription management extension for DjangoCFG",
2122
2124
  keywords: [
2123
2125
  "django",
@@ -2163,6 +2165,11 @@ var package_default = {
2163
2165
  types: "./dist/config.d.ts",
2164
2166
  import: "./dist/config.js",
2165
2167
  require: "./dist/config.cjs"
2168
+ },
2169
+ "./i18n": {
2170
+ types: "./dist/i18n.d.ts",
2171
+ import: "./dist/i18n.js",
2172
+ require: "./dist/i18n.cjs"
2166
2173
  }
2167
2174
  },
2168
2175
  files: [
@@ -2178,6 +2185,7 @@ var package_default = {
2178
2185
  peerDependencies: {
2179
2186
  "@djangocfg/api": "workspace:*",
2180
2187
  "@djangocfg/ext-base": "workspace:*",
2188
+ "@djangocfg/i18n": "workspace:*",
2181
2189
  "@djangocfg/ui-core": "workspace:*",
2182
2190
  consola: "^3.4.2",
2183
2191
  "lucide-react": "^0.545.0",
@@ -2191,6 +2199,7 @@ 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",
@@ -2469,6 +2478,51 @@ function useNewsletterContext() {
2469
2478
  }
2470
2479
  return context;
2471
2480
  }
2481
+
2482
+ // src/i18n/locales/en.ts
2483
+ var en = {
2484
+ hero: {
2485
+ placeholder: "Enter your email",
2486
+ subscribe: "Subscribe",
2487
+ subscribing: "Subscribing...",
2488
+ successMessage: "Successfully subscribed!",
2489
+ errorMessage: "Subscription failed. Please try again.",
2490
+ privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
2491
+ }
2492
+ };
2493
+
2494
+ // src/i18n/locales/ru.ts
2495
+ var ru = {
2496
+ hero: {
2497
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
2498
+ subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
2499
+ subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
2500
+ successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
2501
+ 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.",
2502
+ 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."
2503
+ }
2504
+ };
2505
+
2506
+ // src/i18n/locales/ko.ts
2507
+ var ko = {
2508
+ hero: {
2509
+ placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
2510
+ subscribe: "\uAD6C\uB3C5\uD558\uAE30",
2511
+ subscribing: "\uAD6C\uB3C5 \uC911...",
2512
+ successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
2513
+ errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
2514
+ 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."
2515
+ }
2516
+ };
2517
+
2518
+ // src/i18n/index.ts
2519
+ var NEWSLETTER_NAMESPACE = "newsletter";
2520
+ var newsletterI18n = createExtensionI18n({
2521
+ namespace: NEWSLETTER_NAMESPACE,
2522
+ defaultLocale: "en",
2523
+ locales: { en, ru, ko }
2524
+ });
2525
+ newsletterI18n.getAllTranslations();
2472
2526
  var isDevelopment = process.env.NODE_ENV === "development";
2473
2527
  var logger = createConsola({
2474
2528
  level: isDevelopment ? 4 : 1
@@ -2480,15 +2534,25 @@ function Hero({
2480
2534
  primaryAction,
2481
2535
  secondaryAction,
2482
2536
  showNewsletter = true,
2483
- newsletterPlaceholder = "Enter your email",
2484
- newsletterButtonText = "Subscribe",
2537
+ newsletterPlaceholder,
2538
+ newsletterButtonText,
2485
2539
  onNewsletterSubmit,
2486
2540
  className = ""
2487
2541
  }) {
2542
+ const baseT = useT();
2488
2543
  const [email, setEmail] = useState("");
2489
2544
  const [isLoading, setIsLoading] = useState(false);
2490
2545
  const [status, setStatus] = useState("idle");
2491
2546
  const [message, setMessage] = useState("");
2547
+ const nt = createTypedExtensionT(baseT, NEWSLETTER_NAMESPACE);
2548
+ const labels = useMemo(() => ({
2549
+ placeholder: newsletterPlaceholder ?? nt("hero.placeholder"),
2550
+ subscribe: newsletterButtonText ?? nt("hero.subscribe"),
2551
+ subscribing: nt("hero.subscribing"),
2552
+ successMessage: nt("hero.successMessage"),
2553
+ errorMessage: nt("hero.errorMessage"),
2554
+ privacyNotice: nt("hero.privacyNotice")
2555
+ }), [nt, newsletterPlaceholder, newsletterButtonText]);
2492
2556
  const handleSubmit = async (e) => {
2493
2557
  e.preventDefault();
2494
2558
  if (!email || !onNewsletterSubmit) return;
@@ -2498,12 +2562,12 @@ function Hero({
2498
2562
  try {
2499
2563
  const result = await onNewsletterSubmit(email);
2500
2564
  setStatus("success");
2501
- setMessage((result && "message" in result ? result.message : void 0) || "Successfully subscribed!");
2565
+ setMessage((result && "message" in result ? result.message : void 0) || labels.successMessage);
2502
2566
  setEmail("");
2503
2567
  newsletterLogger.success("Newsletter subscription successful:", email);
2504
2568
  } catch (error) {
2505
2569
  setStatus("error");
2506
- setMessage(error instanceof Error ? error.message : "Subscription failed. Please try again.");
2570
+ setMessage(error instanceof Error ? error.message : labels.errorMessage);
2507
2571
  newsletterLogger.error("Newsletter subscription failed:", error);
2508
2572
  } finally {
2509
2573
  setIsLoading(false);
@@ -2546,7 +2610,7 @@ function Hero({
2546
2610
  Input,
2547
2611
  {
2548
2612
  type: "email",
2549
- placeholder: newsletterPlaceholder,
2613
+ placeholder: labels.placeholder,
2550
2614
  value: email,
2551
2615
  onChange: (e) => setEmail(e.target.value),
2552
2616
  disabled: isLoading,
@@ -2563,8 +2627,8 @@ function Hero({
2563
2627
  className: "w-full sm:w-auto",
2564
2628
  children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
2565
2629
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
2566
- "Subscribing..."
2567
- ] }) : newsletterButtonText
2630
+ labels.subscribing
2631
+ ] }) : labels.subscribe
2568
2632
  }
2569
2633
  )
2570
2634
  ] }),
@@ -2577,7 +2641,7 @@ function Hero({
2577
2641
  /* @__PURE__ */ jsx("span", { children: message })
2578
2642
  ] })
2579
2643
  ] }),
2580
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-3", children: "By subscribing, you agree to our Privacy Policy and consent to receive updates." })
2644
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-3", children: labels.privacyNotice })
2581
2645
  ] })
2582
2646
  ] }) }) });
2583
2647
  }
package/dist/i18n.cjs ADDED
@@ -0,0 +1,78 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/i18n/index.ts
20
+ var i18n_exports = {};
21
+ __export(i18n_exports, {
22
+ NEWSLETTER_NAMESPACE: () => NEWSLETTER_NAMESPACE,
23
+ newsletterI18n: () => newsletterI18n,
24
+ newsletterTranslations: () => newsletterTranslations
25
+ });
26
+ module.exports = __toCommonJS(i18n_exports);
27
+ var import_i18n = require("@djangocfg/ext-base/i18n");
28
+
29
+ // src/i18n/locales/en.ts
30
+ var en = {
31
+ hero: {
32
+ placeholder: "Enter your email",
33
+ subscribe: "Subscribe",
34
+ subscribing: "Subscribing...",
35
+ successMessage: "Successfully subscribed!",
36
+ errorMessage: "Subscription failed. Please try again.",
37
+ privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
38
+ }
39
+ };
40
+
41
+ // src/i18n/locales/ru.ts
42
+ var ru = {
43
+ hero: {
44
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
45
+ subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
46
+ subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
47
+ successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
48
+ 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.",
49
+ 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."
50
+ }
51
+ };
52
+
53
+ // src/i18n/locales/ko.ts
54
+ var ko = {
55
+ hero: {
56
+ placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
57
+ subscribe: "\uAD6C\uB3C5\uD558\uAE30",
58
+ subscribing: "\uAD6C\uB3C5 \uC911...",
59
+ successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
60
+ errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
61
+ 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."
62
+ }
63
+ };
64
+
65
+ // src/i18n/index.ts
66
+ var NEWSLETTER_NAMESPACE = "newsletter";
67
+ var newsletterI18n = (0, import_i18n.createExtensionI18n)({
68
+ namespace: NEWSLETTER_NAMESPACE,
69
+ defaultLocale: "en",
70
+ locales: { en, ru, ko }
71
+ });
72
+ var newsletterTranslations = newsletterI18n.getAllTranslations();
73
+ // Annotate the CommonJS export names for ESM import in node:
74
+ 0 && (module.exports = {
75
+ NEWSLETTER_NAMESPACE,
76
+ newsletterI18n,
77
+ newsletterTranslations
78
+ });
@@ -0,0 +1,35 @@
1
+ import * as _djangocfg_ext_base_i18n from '@djangocfg/ext-base/i18n';
2
+ import { ExtensionKeys, PathKeys } from '@djangocfg/ext-base/i18n';
3
+
4
+ /**
5
+ * Newsletter Extension I18n Types
6
+ */
7
+
8
+ /**
9
+ * All valid keys for newsletter translations (with namespace)
10
+ */
11
+ type NewsletterKeys = ExtensionKeys<'newsletter', NewsletterTranslations>;
12
+ /**
13
+ * Keys without namespace prefix (for createTypedExtensionT)
14
+ */
15
+ type NewsletterLocalKeys = PathKeys<NewsletterTranslations>;
16
+ interface NewsletterTranslations {
17
+ /** Hero */
18
+ hero: {
19
+ placeholder: string;
20
+ subscribe: string;
21
+ subscribing: string;
22
+ successMessage: string;
23
+ errorMessage: string;
24
+ privacyNotice: string;
25
+ };
26
+ }
27
+
28
+ /** Newsletter extension namespace */
29
+ declare const NEWSLETTER_NAMESPACE: "newsletter";
30
+ declare const newsletterI18n: _djangocfg_ext_base_i18n.ExtensionI18n<NewsletterTranslations>;
31
+ declare const newsletterTranslations: Record<string, {
32
+ [namespace: string]: NewsletterTranslations;
33
+ }>;
34
+
35
+ export { NEWSLETTER_NAMESPACE, type NewsletterKeys, type NewsletterLocalKeys, type NewsletterTranslations, newsletterI18n, newsletterTranslations };
package/dist/i18n.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ import * as _djangocfg_ext_base_i18n from '@djangocfg/ext-base/i18n';
2
+ import { ExtensionKeys, PathKeys } from '@djangocfg/ext-base/i18n';
3
+
4
+ /**
5
+ * Newsletter Extension I18n Types
6
+ */
7
+
8
+ /**
9
+ * All valid keys for newsletter translations (with namespace)
10
+ */
11
+ type NewsletterKeys = ExtensionKeys<'newsletter', NewsletterTranslations>;
12
+ /**
13
+ * Keys without namespace prefix (for createTypedExtensionT)
14
+ */
15
+ type NewsletterLocalKeys = PathKeys<NewsletterTranslations>;
16
+ interface NewsletterTranslations {
17
+ /** Hero */
18
+ hero: {
19
+ placeholder: string;
20
+ subscribe: string;
21
+ subscribing: string;
22
+ successMessage: string;
23
+ errorMessage: string;
24
+ privacyNotice: string;
25
+ };
26
+ }
27
+
28
+ /** Newsletter extension namespace */
29
+ declare const NEWSLETTER_NAMESPACE: "newsletter";
30
+ declare const newsletterI18n: _djangocfg_ext_base_i18n.ExtensionI18n<NewsletterTranslations>;
31
+ declare const newsletterTranslations: Record<string, {
32
+ [namespace: string]: NewsletterTranslations;
33
+ }>;
34
+
35
+ export { NEWSLETTER_NAMESPACE, type NewsletterKeys, type NewsletterLocalKeys, type NewsletterTranslations, newsletterI18n, newsletterTranslations };
package/dist/i18n.js ADDED
@@ -0,0 +1,52 @@
1
+ // src/i18n/index.ts
2
+ import { createExtensionI18n } from "@djangocfg/ext-base/i18n";
3
+
4
+ // src/i18n/locales/en.ts
5
+ var en = {
6
+ hero: {
7
+ placeholder: "Enter your email",
8
+ subscribe: "Subscribe",
9
+ subscribing: "Subscribing...",
10
+ successMessage: "Successfully subscribed!",
11
+ errorMessage: "Subscription failed. Please try again.",
12
+ privacyNotice: "By subscribing, you agree to our Privacy Policy and consent to receive updates."
13
+ }
14
+ };
15
+
16
+ // src/i18n/locales/ru.ts
17
+ var ru = {
18
+ hero: {
19
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 email",
20
+ subscribe: "\u041F\u043E\u0434\u043F\u0438\u0441\u0430\u0442\u044C\u0441\u044F",
21
+ subscribing: "\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430...",
22
+ successMessage: "\u0412\u044B \u0443\u0441\u043F\u0435\u0448\u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043B\u0438\u0441\u044C!",
23
+ 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.",
24
+ 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."
25
+ }
26
+ };
27
+
28
+ // src/i18n/locales/ko.ts
29
+ var ko = {
30
+ hero: {
31
+ placeholder: "\uC774\uBA54\uC77C\uC744 \uC785\uB825\uD558\uC138\uC694",
32
+ subscribe: "\uAD6C\uB3C5\uD558\uAE30",
33
+ subscribing: "\uAD6C\uB3C5 \uC911...",
34
+ successMessage: "\uC131\uACF5\uC801\uC73C\uB85C \uAD6C\uB3C5\uB418\uC5C8\uC2B5\uB2C8\uB2E4!",
35
+ errorMessage: "\uAD6C\uB3C5\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.",
36
+ 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."
37
+ }
38
+ };
39
+
40
+ // src/i18n/index.ts
41
+ var NEWSLETTER_NAMESPACE = "newsletter";
42
+ var newsletterI18n = createExtensionI18n({
43
+ namespace: NEWSLETTER_NAMESPACE,
44
+ defaultLocale: "en",
45
+ locales: { en, ru, ko }
46
+ });
47
+ var newsletterTranslations = newsletterI18n.getAllTranslations();
48
+ export {
49
+ NEWSLETTER_NAMESPACE,
50
+ newsletterI18n,
51
+ newsletterTranslations
52
+ };
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.21",
2120
+ version: "1.0.22",
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,6 +2183,7 @@ 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",
@@ -2191,6 +2197,7 @@ var package_default = {
2191
2197
  devDependencies: {
2192
2198
  "@djangocfg/api": "workspace:*",
2193
2199
  "@djangocfg/ext-base": "workspace:*",
2200
+ "@djangocfg/i18n": "workspace:*",
2194
2201
  "@djangocfg/typescript-config": "workspace:*",
2195
2202
  "@types/node": "^24.7.2",
2196
2203
  "@types/react": "^19.0.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.21",
2114
+ version: "1.0.22",
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,6 +2177,7 @@ 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",
@@ -2185,6 +2191,7 @@ var package_default = {
2185
2191
  devDependencies: {
2186
2192
  "@djangocfg/api": "workspace:*",
2187
2193
  "@djangocfg/ext-base": "workspace:*",
2194
+ "@djangocfg/i18n": "workspace:*",
2188
2195
  "@djangocfg/typescript-config": "workspace:*",
2189
2196
  "@types/node": "^24.7.2",
2190
2197
  "@types/react": "^19.0.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ext-newsletter",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
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,9 +64,10 @@
59
64
  "check": "tsc --noEmit"
60
65
  },
61
66
  "peerDependencies": {
62
- "@djangocfg/api": "^2.1.109",
63
- "@djangocfg/ext-base": "^1.0.16",
64
- "@djangocfg/ui-core": "^2.1.109",
67
+ "@djangocfg/api": "^2.1.111",
68
+ "@djangocfg/ext-base": "^1.0.17",
69
+ "@djangocfg/i18n": "^2.1.111",
70
+ "@djangocfg/ui-core": "^2.1.111",
65
71
  "consola": "^3.4.2",
66
72
  "lucide-react": "^0.545.0",
67
73
  "next": "^16",
@@ -72,9 +78,10 @@
72
78
  "zod": "^4.3.4"
73
79
  },
74
80
  "devDependencies": {
75
- "@djangocfg/api": "^2.1.109",
76
- "@djangocfg/ext-base": "^1.0.16",
77
- "@djangocfg/typescript-config": "^2.1.109",
81
+ "@djangocfg/api": "^2.1.111",
82
+ "@djangocfg/ext-base": "^1.0.17",
83
+ "@djangocfg/i18n": "^2.1.111",
84
+ "@djangocfg/typescript-config": "^2.1.111",
78
85
  "@types/node": "^24.7.2",
79
86
  "@types/react": "^19.0.0",
80
87
  "consola": "^3.4.2",
@@ -6,8 +6,11 @@
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 { createTypedExtensionT } from '@djangocfg/ext-base/i18n';
12
+ import { useT } from '@djangocfg/i18n';
13
+ import { NEWSLETTER_NAMESPACE, type NewsletterTranslations } from '../../i18n';
11
14
  import { Button, Input } from '@djangocfg/ui-core';
12
15
 
13
16
  import { newsletterLogger } from '../../utils/logger';
@@ -20,16 +23,31 @@ export function Hero({
20
23
  primaryAction,
21
24
  secondaryAction,
22
25
  showNewsletter = true,
23
- newsletterPlaceholder = 'Enter your email',
24
- newsletterButtonText = 'Subscribe',
26
+ newsletterPlaceholder,
27
+ newsletterButtonText,
25
28
  onNewsletterSubmit,
26
29
  className = '',
27
30
  }: HeroProps) {
31
+ const baseT = useT();
28
32
  const [email, setEmail] = useState('');
29
33
  const [isLoading, setIsLoading] = useState(false);
30
34
  const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle');
31
35
  const [message, setMessage] = useState('');
32
36
 
37
+ // Type-safe translation function for newsletter extension
38
+ // Keys are validated at compile time: nt('hero.placeholder') OK, nt('hero.typo') Error
39
+ const nt = createTypedExtensionT<typeof NEWSLETTER_NAMESPACE, NewsletterTranslations>(baseT, NEWSLETTER_NAMESPACE);
40
+
41
+ // Prepare labels before JSX render
42
+ const labels = useMemo(() => ({
43
+ placeholder: newsletterPlaceholder ?? nt('hero.placeholder'),
44
+ subscribe: newsletterButtonText ?? nt('hero.subscribe'),
45
+ subscribing: nt('hero.subscribing'),
46
+ successMessage: nt('hero.successMessage'),
47
+ errorMessage: nt('hero.errorMessage'),
48
+ privacyNotice: nt('hero.privacyNotice'),
49
+ }), [nt, newsletterPlaceholder, newsletterButtonText]);
50
+
33
51
  const handleSubmit = async (e: React.FormEvent) => {
34
52
  e.preventDefault();
35
53
 
@@ -42,12 +60,12 @@ export function Hero({
42
60
  try {
43
61
  const result = await onNewsletterSubmit(email);
44
62
  setStatus('success');
45
- setMessage((result && 'message' in result ? result.message : undefined) || 'Successfully subscribed!');
63
+ setMessage((result && 'message' in result ? result.message : undefined) || labels.successMessage);
46
64
  setEmail('');
47
65
  newsletterLogger.success('Newsletter subscription successful:', email);
48
66
  } catch (error) {
49
67
  setStatus('error');
50
- setMessage(error instanceof Error ? error.message : 'Subscription failed. Please try again.');
68
+ setMessage(error instanceof Error ? error.message : labels.errorMessage);
51
69
  newsletterLogger.error('Newsletter subscription failed:', error);
52
70
  } finally {
53
71
  setIsLoading(false);
@@ -109,7 +127,7 @@ export function Hero({
109
127
  <Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground" />
110
128
  <Input
111
129
  type="email"
112
- placeholder={newsletterPlaceholder}
130
+ placeholder={labels.placeholder}
113
131
  value={email}
114
132
  onChange={(e) => setEmail(e.target.value)}
115
133
  disabled={isLoading}
@@ -125,10 +143,10 @@ export function Hero({
125
143
  {isLoading ? (
126
144
  <>
127
145
  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
128
- Subscribing...
146
+ {labels.subscribing}
129
147
  </>
130
148
  ) : (
131
- newsletterButtonText
149
+ labels.subscribe
132
150
  )}
133
151
  </Button>
134
152
  </div>
@@ -150,7 +168,7 @@ export function Hero({
150
168
  </form>
151
169
 
152
170
  <p className="text-xs text-muted-foreground mt-3">
153
- By subscribing, you agree to our Privacy Policy and consent to receive updates.
171
+ {labels.privacyNotice}
154
172
  </p>
155
173
  </div>
156
174
  )}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Newsletter Extension I18n
3
+ */
4
+
5
+ import { createExtensionI18n } from '@djangocfg/ext-base/i18n';
6
+ import type { NewsletterTranslations } from './types';
7
+ import { en } from './locales/en';
8
+ import { ru } from './locales/ru';
9
+ import { ko } from './locales/ko';
10
+
11
+ /** Newsletter extension namespace */
12
+ export const NEWSLETTER_NAMESPACE = 'newsletter' as const;
13
+
14
+ export const newsletterI18n = createExtensionI18n<NewsletterTranslations>({
15
+ namespace: NEWSLETTER_NAMESPACE,
16
+ defaultLocale: 'en',
17
+ locales: { en, ru, ko },
18
+ });
19
+
20
+ export const newsletterTranslations = newsletterI18n.getAllTranslations();
21
+
22
+ // Types
23
+ export type { NewsletterTranslations, NewsletterKeys, NewsletterLocalKeys } from './types';
@@ -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,27 @@
1
+ /**
2
+ * Newsletter Extension I18n Types
3
+ */
4
+
5
+ import type { ExtensionKeys, PathKeys } from '@djangocfg/ext-base/i18n';
6
+
7
+ /**
8
+ * All valid keys for newsletter translations (with namespace)
9
+ */
10
+ export type NewsletterKeys = ExtensionKeys<'newsletter', NewsletterTranslations>;
11
+
12
+ /**
13
+ * Keys without namespace prefix (for createTypedExtensionT)
14
+ */
15
+ export type NewsletterLocalKeys = PathKeys<NewsletterTranslations>;
16
+
17
+ export interface NewsletterTranslations {
18
+ /** Hero */
19
+ hero: {
20
+ placeholder: string;
21
+ subscribe: string;
22
+ subscribing: string;
23
+ successMessage: string;
24
+ errorMessage: string;
25
+ privacyNotice: string;
26
+ };
27
+ }