@spfn/cms 0.1.0-alpha.64 → 0.1.0-alpha.65

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.
Files changed (103) hide show
  1. package/README.md +67 -30
  2. package/dist/actions-CwvmPG0e.d.ts +296 -0
  3. package/dist/actions.d.ts +1 -143
  4. package/dist/actions.js +5 -5
  5. package/dist/actions.js.map +1 -1
  6. package/dist/api.d.ts +376 -0
  7. package/dist/api.js +364 -0
  8. package/dist/api.js.map +1 -0
  9. package/dist/client.d.ts +115 -141
  10. package/dist/client.js +860 -63
  11. package/dist/client.js.map +1 -1
  12. package/dist/{types.d.ts → index-Dh5FjWzR.d.ts} +32 -1
  13. package/dist/index.d.ts +21 -43
  14. package/dist/index.js +556 -1061
  15. package/dist/index.js.map +1 -1
  16. package/dist/label-sync-generator-B0EmvtWM.d.ts +32 -0
  17. package/dist/{contracts → lib/contracts}/labels.d.ts +13 -12
  18. package/dist/{contracts → lib/contracts}/labels.js +7 -7
  19. package/dist/lib/contracts/labels.js.map +1 -0
  20. package/dist/{contracts → lib/contracts}/published-cache.d.ts +3 -2
  21. package/dist/{contracts → lib/contracts}/published-cache.js +2 -2
  22. package/dist/lib/contracts/published-cache.js.map +1 -0
  23. package/dist/{contracts → lib/contracts}/values.d.ts +6 -4
  24. package/dist/{contracts → lib/contracts}/values.js +3 -3
  25. package/dist/lib/contracts/values.js.map +1 -0
  26. package/dist/{entities → server/entities}/cms-audit-logs.js +3 -3
  27. package/dist/server/entities/cms-audit-logs.js.map +1 -0
  28. package/dist/{entities → server/entities}/cms-draft-cache.js +1 -1
  29. package/dist/server/entities/cms-draft-cache.js.map +1 -0
  30. package/dist/{entities → server/entities}/cms-label-values.js +3 -3
  31. package/dist/server/entities/cms-label-values.js.map +1 -0
  32. package/dist/{entities → server/entities}/cms-label-versions.js +3 -3
  33. package/dist/server/entities/cms-label-versions.js.map +1 -0
  34. package/dist/{entities → server/entities}/cms-labels.js +1 -1
  35. package/dist/server/entities/cms-labels.js.map +1 -0
  36. package/dist/{entities → server/entities}/cms-published-cache.js +1 -1
  37. package/dist/server/entities/cms-published-cache.js.map +1 -0
  38. package/dist/{entities → server/entities}/index.js +6 -6
  39. package/dist/server/entities/index.js.map +1 -0
  40. package/dist/{generators → server/generators}/index.d.ts +1 -2
  41. package/dist/{generators → server/generators}/index.js +169 -75
  42. package/dist/server/generators/index.js.map +1 -0
  43. package/dist/server/labels/index.d.ts +1 -0
  44. package/dist/{labels → server/labels}/index.js +1 -1
  45. package/dist/server/labels/index.js.map +1 -0
  46. package/dist/{repositories → server/repositories}/index.js +11 -11
  47. package/dist/server/repositories/index.js.map +1 -0
  48. package/dist/{routes → server/routes}/labels/[id]/index.js +20 -20
  49. package/dist/server/routes/labels/[id]/index.js.map +1 -0
  50. package/dist/{routes → server/routes}/labels/by-key/[key]/index.js +20 -20
  51. package/dist/server/routes/labels/by-key/[key]/index.js.map +1 -0
  52. package/dist/{routes → server/routes}/labels/index.d.ts +2 -2
  53. package/dist/{routes → server/routes}/labels/index.js +21 -22
  54. package/dist/server/routes/labels/index.js.map +1 -0
  55. package/dist/{routes → server/routes}/published-cache/index.js +14 -14
  56. package/dist/server/routes/published-cache/index.js.map +1 -0
  57. package/dist/{routes → server/routes}/values/[labelId]/[version]/index.js +16 -16
  58. package/dist/server/routes/values/[labelId]/[version]/index.js.map +1 -0
  59. package/dist/{routes → server/routes}/values/[labelId]/index.js +16 -16
  60. package/dist/server/routes/values/[labelId]/index.js.map +1 -0
  61. package/dist/server.d.ts +76 -7
  62. package/dist/server.js +1601 -42
  63. package/dist/server.js.map +1 -1
  64. package/package.json +37 -35
  65. package/dist/contracts/labels.js.map +0 -1
  66. package/dist/contracts/published-cache.js.map +0 -1
  67. package/dist/contracts/values.js.map +0 -1
  68. package/dist/entities/cms-audit-logs.js.map +0 -1
  69. package/dist/entities/cms-draft-cache.js.map +0 -1
  70. package/dist/entities/cms-label-values.js.map +0 -1
  71. package/dist/entities/cms-label-versions.js.map +0 -1
  72. package/dist/entities/cms-labels.js.map +0 -1
  73. package/dist/entities/cms-published-cache.js.map +0 -1
  74. package/dist/entities/index.js.map +0 -1
  75. package/dist/generators/index.js.map +0 -1
  76. package/dist/label-sync-generator-lQrcVfja.d.ts +0 -36
  77. package/dist/labels/index.d.ts +0 -34
  78. package/dist/labels/index.js.map +0 -1
  79. package/dist/repositories/index.js.map +0 -1
  80. package/dist/routes/labels/[id]/index.js.map +0 -1
  81. package/dist/routes/labels/by-key/[key]/index.js.map +0 -1
  82. package/dist/routes/labels/index.js.map +0 -1
  83. package/dist/routes/published-cache/index.js.map +0 -1
  84. package/dist/routes/values/[labelId]/[version]/index.js.map +0 -1
  85. package/dist/routes/values/[labelId]/index.js.map +0 -1
  86. package/dist/store.d.ts +0 -81
  87. package/dist/store.js +0 -403
  88. package/dist/store.js.map +0 -1
  89. package/dist/types.js +0 -1
  90. package/dist/types.js.map +0 -1
  91. package/dist/{entities → server/entities}/cms-audit-logs.d.ts +0 -0
  92. package/dist/{entities → server/entities}/cms-draft-cache.d.ts +0 -0
  93. package/dist/{entities → server/entities}/cms-label-values.d.ts +0 -0
  94. package/dist/{entities → server/entities}/cms-label-versions.d.ts +0 -0
  95. package/dist/{entities → server/entities}/cms-labels.d.ts +0 -0
  96. package/dist/{entities → server/entities}/cms-published-cache.d.ts +0 -0
  97. package/dist/{entities → server/entities}/index.d.ts +0 -0
  98. package/dist/{repositories → server/repositories}/index.d.ts +14 -14
  99. /package/dist/{routes → server/routes}/labels/_id_/index.d.ts +0 -0
  100. /package/dist/{routes → server/routes}/labels/by-key/_key_/index.d.ts +0 -0
  101. /package/dist/{routes → server/routes}/published-cache/index.d.ts +0 -0
  102. /package/dist/{routes → server/routes}/values/_labelId_/_version_/index.d.ts +0 -0
  103. /package/dist/{routes → server/routes}/values/_labelId_/index.d.ts +0 -0
package/dist/client.js CHANGED
@@ -1,11 +1,20 @@
1
- // src/client.ts
1
+ // src/client/hooks/useSection.ts
2
+ import React from "react";
3
+
4
+ // src/client/store/cms.store.ts
5
+ import { create } from "zustand";
6
+
7
+ // src/api/index.ts
8
+ import { client as client5 } from "@spfn/core/client";
9
+
10
+ // src/api/CmsLabels.ts
2
11
  import { client } from "@spfn/core/client";
3
12
 
4
- // src/contracts/labels.ts
13
+ // src/lib/contracts/labels.ts
5
14
  import { Type } from "@sinclair/typebox";
6
15
  var getLabelsContract = {
7
16
  method: "GET",
8
- path: "/cms/labels",
17
+ path: "/labels",
9
18
  query: Type.Object({
10
19
  section: Type.Optional(Type.String({ description: "\uC139\uC158\uC73C\uB85C \uD544\uD130\uB9C1 (\uC608: home, why-futureplay)" })),
11
20
  limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100, default: 20, description: "\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218" })),
@@ -29,7 +38,7 @@ var getLabelsContract = {
29
38
  };
30
39
  var createLabelContract = {
31
40
  method: "POST",
32
- path: "/cms/labels",
41
+ path: "/labels",
33
42
  body: Type.Object({
34
43
  key: Type.String({
35
44
  description: "\uACE0\uC720 \uD0A4 (\uC608: home.hero.title)",
@@ -67,7 +76,7 @@ var createLabelContract = {
67
76
  };
68
77
  var getLabelContract = {
69
78
  method: "GET",
70
- path: "/cms/labels/:id",
79
+ path: "/labels/:id",
71
80
  params: Type.Object({
72
81
  id: Type.String({ description: "\uB77C\uBCA8 ID" })
73
82
  }),
@@ -89,7 +98,7 @@ var getLabelContract = {
89
98
  };
90
99
  var updateLabelContract = {
91
100
  method: "PATCH",
92
- path: "/cms/labels/:id",
101
+ path: "/labels/:id",
93
102
  params: Type.Object({
94
103
  id: Type.String({ description: "\uB77C\uBCA8 ID" })
95
104
  }),
@@ -121,7 +130,7 @@ var updateLabelContract = {
121
130
  };
122
131
  var deleteLabelContract = {
123
132
  method: "DELETE",
124
- path: "/cms/labels/:id",
133
+ path: "/labels/:id",
125
134
  params: Type.Object({
126
135
  id: Type.String({ description: "\uB77C\uBCA8 ID" })
127
136
  }),
@@ -137,7 +146,7 @@ var deleteLabelContract = {
137
146
  };
138
147
  var getLabelByKeyContract = {
139
148
  method: "GET",
140
- path: "/cms/labels/by-key/:key",
149
+ path: "/labels/by-key/:key",
141
150
  params: Type.Object({
142
151
  key: Type.String({ description: "\uB77C\uBCA8 Key (\uC608: home.hero.title)" })
143
152
  }),
@@ -159,7 +168,43 @@ var getLabelByKeyContract = {
159
168
  ])
160
169
  };
161
170
 
162
- // src/contracts/published-cache.ts
171
+ // src/api/CmsLabels.ts
172
+ var CmsLabels = {
173
+ /**
174
+ * GET /_cms/labels
175
+ */
176
+ get: (options) => client.call(getLabelsContract, options),
177
+ /**
178
+ * POST /_cms/labels
179
+ */
180
+ post: (options) => client.call(createLabelContract, options),
181
+ /**
182
+ * GET /_cms/labels/:id
183
+ */
184
+ getById: (options) => client.call(getLabelContract, options),
185
+ /**
186
+ * PATCH /_cms/labels/:id
187
+ */
188
+ update: (options) => client.call(updateLabelContract, options),
189
+ /**
190
+ * DELETE /_cms/labels/:id
191
+ */
192
+ delete: (options) => client.call(deleteLabelContract, options)
193
+ };
194
+
195
+ // src/api/CmsLabelsByKey.ts
196
+ import { client as client2 } from "@spfn/core/client";
197
+ var CmsLabelsByKey = {
198
+ /**
199
+ * GET /_cms/labels/by-key/:key
200
+ */
201
+ getById: (options) => client2.call(getLabelByKeyContract, options)
202
+ };
203
+
204
+ // src/api/CmsPublishedCache.ts
205
+ import { client as client3 } from "@spfn/core/client";
206
+
207
+ // src/lib/contracts/published-cache.ts
163
208
  import { Type as Type2 } from "@sinclair/typebox";
164
209
  var SectionData = Type2.Object({
165
210
  section: Type2.String(),
@@ -170,7 +215,7 @@ var SectionData = Type2.Object({
170
215
  });
171
216
  var getPublishedCacheContract = {
172
217
  method: "GET",
173
- path: "/cms/published-cache",
218
+ path: "/published-cache",
174
219
  query: Type2.Object({
175
220
  sections: Type2.Union([
176
221
  Type2.String({ description: "\uB2E8\uC77C \uC139\uC158 \uC774\uB984 (\uC608: home)" }),
@@ -188,15 +233,134 @@ var getPublishedCacheContract = {
188
233
  ])
189
234
  };
190
235
 
191
- // src/store.ts
192
- import React from "react";
193
- import { create } from "zustand";
194
- function replaceVariables(text, replace) {
195
- return text.replace(/\{(\w+)}/g, (match, key) => {
196
- const value = replace[key];
197
- return value !== void 0 ? String(value) : match;
198
- });
199
- }
236
+ // src/api/CmsPublishedCache.ts
237
+ var CmsPublishedCache = {
238
+ /**
239
+ * GET /_cms/published-cache
240
+ */
241
+ get: (options) => client3.call(getPublishedCacheContract, options)
242
+ };
243
+
244
+ // src/api/CmsValues.ts
245
+ import { client as client4 } from "@spfn/core/client";
246
+
247
+ // src/lib/contracts/values.ts
248
+ import { Type as Type3 } from "@sinclair/typebox";
249
+ var LabelValueSchema = Type3.Object({
250
+ type: Type3.Union([
251
+ Type3.Literal("text"),
252
+ Type3.Literal("image"),
253
+ Type3.Literal("video"),
254
+ Type3.Literal("file"),
255
+ Type3.Literal("object")
256
+ ]),
257
+ content: Type3.Optional(Type3.String()),
258
+ // text type
259
+ url: Type3.Optional(Type3.String()),
260
+ // image, video, file types
261
+ alt: Type3.Optional(Type3.String()),
262
+ // image type
263
+ width: Type3.Optional(Type3.Number()),
264
+ // image type
265
+ height: Type3.Optional(Type3.Number()),
266
+ // image type
267
+ thumbnail: Type3.Optional(Type3.String()),
268
+ // video type
269
+ duration: Type3.Optional(Type3.Number()),
270
+ // video type
271
+ filename: Type3.Optional(Type3.String()),
272
+ // file type
273
+ size: Type3.Optional(Type3.Number()),
274
+ // file type
275
+ fields: Type3.Optional(Type3.Any())
276
+ // object type - recursive structure
277
+ });
278
+ var saveValuesContract = {
279
+ method: "POST",
280
+ path: "/values/:labelId",
281
+ params: Type3.Object({
282
+ labelId: Type3.String({ description: "\uB77C\uBCA8 ID" })
283
+ }),
284
+ body: Type3.Object({
285
+ version: Type3.Number({ description: "\uBC84\uC804 \uBC88\uD638", minimum: 1 }),
286
+ values: Type3.Array(
287
+ Type3.Object({
288
+ locale: Type3.String({ description: "\uC5B8\uC5B4 \uCF54\uB4DC (ko, en, ja)", default: "ko" }),
289
+ breakpoint: Type3.Optional(Type3.Union([
290
+ Type3.Literal("sm"),
291
+ Type3.Literal("md"),
292
+ Type3.Literal("lg"),
293
+ Type3.Literal("xl"),
294
+ Type3.Literal("2xl"),
295
+ Type3.Null()
296
+ ], { description: "\uBC18\uC751\uD615 \uBE0C\uB808\uC774\uD06C\uD3EC\uC778\uD2B8" })),
297
+ value: LabelValueSchema
298
+ })
299
+ )
300
+ }),
301
+ response: Type3.Union([
302
+ Type3.Object({
303
+ success: Type3.Boolean(),
304
+ saved: Type3.Number(),
305
+ version: Type3.Number()
306
+ }),
307
+ Type3.Object({
308
+ error: Type3.String()
309
+ })
310
+ ])
311
+ };
312
+ var getValuesContract = {
313
+ method: "GET",
314
+ path: "/values/:labelId/:version",
315
+ params: Type3.Object({
316
+ labelId: Type3.String({ description: "\uB77C\uBCA8 ID" }),
317
+ version: Type3.String({ description: "\uBC84\uC804 \uBC88\uD638" })
318
+ }),
319
+ query: Type3.Object({
320
+ locale: Type3.Optional(Type3.String({ description: "\uC5B8\uC5B4 \uCF54\uB4DC (ko, en, ja)" })),
321
+ breakpoint: Type3.Optional(Type3.String({ description: "\uBC18\uC751\uD615 \uBE0C\uB808\uC774\uD06C\uD3EC\uC778\uD2B8" }))
322
+ }),
323
+ response: Type3.Union([
324
+ Type3.Object({
325
+ labelId: Type3.Number(),
326
+ version: Type3.Number(),
327
+ values: Type3.Array(
328
+ Type3.Object({
329
+ id: Type3.Number(),
330
+ locale: Type3.String(),
331
+ breakpoint: Type3.Union([Type3.String(), Type3.Null()]),
332
+ value: Type3.Any(),
333
+ createdAt: Type3.String()
334
+ })
335
+ )
336
+ }),
337
+ Type3.Object({
338
+ error: Type3.String()
339
+ })
340
+ ])
341
+ };
342
+
343
+ // src/api/CmsValues.ts
344
+ var CmsValues = {
345
+ /**
346
+ * POST /_cms/values/:labelId
347
+ */
348
+ post: (options) => client4.call(saveValuesContract, options),
349
+ /**
350
+ * GET /_cms/values/:labelId/:version
351
+ */
352
+ getById: (options) => client4.call(getValuesContract, options)
353
+ };
354
+
355
+ // src/api/index.ts
356
+ var cmsApi = {
357
+ cmsLabels: CmsLabels,
358
+ cmsLabelsByKey: CmsLabelsByKey,
359
+ cmsPublishedCache: CmsPublishedCache,
360
+ cmsValues: CmsValues
361
+ };
362
+
363
+ // src/client/store/cms.store.ts
200
364
  var useCmsStore = create((set, get) => ({
201
365
  sections: {},
202
366
  loading: {},
@@ -228,7 +392,7 @@ var useCmsStore = create((set, get) => ({
228
392
  loading: { ...state2.loading, [section]: true }
229
393
  }));
230
394
  try {
231
- const response = await cmsApi.publishedCache.get({
395
+ const response = await cmsApi.cmsPublishedCache.get({
232
396
  query: { sections: section, locale }
233
397
  });
234
398
  if ("error" in response) {
@@ -282,6 +446,14 @@ var useCmsStore = create((set, get) => ({
282
446
  });
283
447
  }
284
448
  }));
449
+
450
+ // src/client/hooks/useSection.ts
451
+ function replaceVariables(text, replace) {
452
+ return text.replace(/\{(\w+)}/g, (match, key) => {
453
+ const value = replace[key];
454
+ return value !== void 0 ? String(value) : match;
455
+ });
456
+ }
285
457
  function useSection(section, options = {}) {
286
458
  const { autoLoad = false, locale = "ko" } = options;
287
459
  const sectionData = useCmsStore((state) => state.sections[section]);
@@ -315,6 +487,14 @@ function useSection(section, options = {}) {
315
487
  loading
316
488
  };
317
489
  }
490
+
491
+ // src/client/hooks/useSections.ts
492
+ function replaceVariables2(text, replace) {
493
+ return text.replace(/\{(\w+)}/g, (match, key) => {
494
+ const value = replace[key];
495
+ return value !== void 0 ? String(value) : match;
496
+ });
497
+ }
318
498
  function useSections(sectionNames) {
319
499
  const allSections = useCmsStore((state) => state.sections);
320
500
  const allLoading = useCmsStore((state) => state.loading);
@@ -332,7 +512,7 @@ function useSections(sectionNames) {
332
512
  value = defaultValue;
333
513
  }
334
514
  if (typeof value === "string" && replace) {
335
- value = replaceVariables(value, replace);
515
+ value = replaceVariables2(value, replace);
336
516
  }
337
517
  return value;
338
518
  };
@@ -345,7 +525,7 @@ function useSections(sectionNames) {
345
525
  return result;
346
526
  }
347
527
 
348
- // src/init.tsx
528
+ // src/client/components/InitCms.tsx
349
529
  import { useEffect } from "react";
350
530
  function InitCms({ sections }) {
351
531
  const setSections = useCmsStore((state) => state.setSections);
@@ -359,52 +539,669 @@ function InitCms({ sections }) {
359
539
  return null;
360
540
  }
361
541
 
362
- // src/client.ts
363
- var cmsApi = {
364
- /**
365
- * Labels API
366
- */
367
- labels: {
368
- /**
369
- * GET /cms/labels
370
- * 라벨 목록 조회 (섹션 필터, 페이지네이션)
371
- */
372
- list: (options) => client.call(getLabelsContract, options),
373
- /**
374
- * GET /cms/labels/:id
375
- * 특정 라벨 조회
376
- */
377
- getById: (options) => client.call(getLabelContract, options),
378
- /**
379
- * POST /cms/labels
380
- * 새 라벨 생성
381
- */
382
- create: (options) => client.call(createLabelContract, options),
383
- /**
384
- * PATCH /cms/labels/:id
385
- * 라벨 업데이트
386
- */
387
- update: (options) => client.call(updateLabelContract, options),
388
- /**
389
- * DELETE /cms/labels/:id
390
- * 라벨 삭제
391
- */
392
- delete: (options) => client.call(deleteLabelContract, options)
542
+ // src/server/helpers/locale.actions.ts
543
+ import { cookies, headers } from "next/headers";
544
+
545
+ // src/server/config/cms.config.ts
546
+ function getEnvVar(key, defaultValue) {
547
+ return process.env[key] || defaultValue;
548
+ }
549
+ function getEnvBoolean(key, defaultValue) {
550
+ const value = process.env[key];
551
+ if (value === void 0) return defaultValue;
552
+ return value === "true" || value === "1";
553
+ }
554
+ function loadConfigFromEnv() {
555
+ const defaultLocale = getEnvVar("SPFN_CMS_DEFAULT_LOCALE", "en");
556
+ const supportedLocalesStr = getEnvVar("SPFN_CMS_SUPPORTED_LOCALES", "en,ko");
557
+ const detectBrowserLanguage2 = getEnvBoolean("SPFN_CMS_DETECT_BROWSER_LANGUAGE", true);
558
+ const supportedLocales = supportedLocalesStr.split(",").map((locale) => locale.trim()).filter((locale) => locale.length > 0);
559
+ if (!supportedLocales.includes(defaultLocale)) {
560
+ supportedLocales.unshift(defaultLocale);
561
+ }
562
+ return {
563
+ defaultLocale,
564
+ supportedLocales,
565
+ detectBrowserLanguage: detectBrowserLanguage2
566
+ };
567
+ }
568
+ var currentConfig = loadConfigFromEnv();
569
+ function getCmsConfig() {
570
+ return currentConfig;
571
+ }
572
+
573
+ // src/lib/constants/locale.constants.ts
574
+ var LOCALE_COOKIE_KEY = "spfn-locale";
575
+ var LOCALE_INFO_MAP = {
576
+ // 한국어
577
+ ko: {
578
+ locale: "ko",
579
+ countryCode: "KR",
580
+ flag: "🇰🇷",
581
+ dialCode: "+82",
582
+ nativeName: "\uD55C\uAD6D\uC5B4",
583
+ englishName: "Korean",
584
+ currencyCode: "KRW",
585
+ dateFormat: "YYYY.MM.DD"
393
586
  },
394
- /**
395
- * Published Cache API
396
- */
397
- publishedCache: {
398
- /**
399
- * GET /cms/published-cache
400
- * 발행된 콘텐츠 캐시 조회
401
- */
402
- get: (options) => client.call(getPublishedCacheContract, options)
587
+ // 영어 (미국)
588
+ en: {
589
+ locale: "en",
590
+ countryCode: "US",
591
+ flag: "🇺🇸",
592
+ dialCode: "+1",
593
+ nativeName: "English",
594
+ englishName: "English",
595
+ currencyCode: "USD",
596
+ dateFormat: "MM/DD/YYYY"
597
+ },
598
+ // 일본어
599
+ ja: {
600
+ locale: "ja",
601
+ countryCode: "JP",
602
+ flag: "🇯🇵",
603
+ dialCode: "+81",
604
+ nativeName: "\u65E5\u672C\u8A9E",
605
+ englishName: "Japanese",
606
+ currencyCode: "JPY",
607
+ dateFormat: "YYYY/MM/DD"
608
+ },
609
+ // 중국어 (간체)
610
+ zh: {
611
+ locale: "zh",
612
+ countryCode: "CN",
613
+ flag: "🇨🇳",
614
+ dialCode: "+86",
615
+ nativeName: "\u7B80\u4F53\u4E2D\u6587",
616
+ englishName: "Chinese (Simplified)",
617
+ currencyCode: "CNY",
618
+ dateFormat: "YYYY-MM-DD"
619
+ },
620
+ // 중국어 (번체, 대만)
621
+ "zh-TW": {
622
+ locale: "zh-TW",
623
+ countryCode: "TW",
624
+ flag: "🇹🇼",
625
+ dialCode: "+886",
626
+ nativeName: "\u7E41\u9AD4\u4E2D\u6587",
627
+ englishName: "Chinese (Traditional)",
628
+ currencyCode: "TWD",
629
+ dateFormat: "YYYY/MM/DD"
630
+ },
631
+ // 스페인어
632
+ es: {
633
+ locale: "es",
634
+ countryCode: "ES",
635
+ flag: "🇪🇸",
636
+ dialCode: "+34",
637
+ nativeName: "Espa\xF1ol",
638
+ englishName: "Spanish",
639
+ currencyCode: "EUR",
640
+ dateFormat: "DD/MM/YYYY"
641
+ },
642
+ // 프랑스어
643
+ fr: {
644
+ locale: "fr",
645
+ countryCode: "FR",
646
+ flag: "🇫🇷",
647
+ dialCode: "+33",
648
+ nativeName: "Fran\xE7ais",
649
+ englishName: "French",
650
+ currencyCode: "EUR",
651
+ dateFormat: "DD/MM/YYYY"
652
+ },
653
+ // 독일어
654
+ de: {
655
+ locale: "de",
656
+ countryCode: "DE",
657
+ flag: "🇩🇪",
658
+ dialCode: "+49",
659
+ nativeName: "Deutsch",
660
+ englishName: "German",
661
+ currencyCode: "EUR",
662
+ dateFormat: "DD.MM.YYYY"
663
+ },
664
+ // 이탈리아어
665
+ it: {
666
+ locale: "it",
667
+ countryCode: "IT",
668
+ flag: "🇮🇹",
669
+ dialCode: "+39",
670
+ nativeName: "Italiano",
671
+ englishName: "Italian",
672
+ currencyCode: "EUR",
673
+ dateFormat: "DD/MM/YYYY"
674
+ },
675
+ // 포르투갈어 (브라질)
676
+ pt: {
677
+ locale: "pt",
678
+ countryCode: "BR",
679
+ flag: "🇧🇷",
680
+ dialCode: "+55",
681
+ nativeName: "Portugu\xEAs",
682
+ englishName: "Portuguese",
683
+ currencyCode: "BRL",
684
+ dateFormat: "DD/MM/YYYY"
685
+ },
686
+ // 러시아어
687
+ ru: {
688
+ locale: "ru",
689
+ countryCode: "RU",
690
+ flag: "🇷🇺",
691
+ dialCode: "+7",
692
+ nativeName: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
693
+ englishName: "Russian",
694
+ currencyCode: "RUB",
695
+ dateFormat: "DD.MM.YYYY"
696
+ },
697
+ // 아랍어
698
+ ar: {
699
+ locale: "ar",
700
+ countryCode: "SA",
701
+ flag: "🇸🇦",
702
+ dialCode: "+966",
703
+ nativeName: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629",
704
+ englishName: "Arabic",
705
+ rtl: true,
706
+ currencyCode: "SAR",
707
+ dateFormat: "DD/MM/YYYY"
708
+ },
709
+ // 힌디어
710
+ hi: {
711
+ locale: "hi",
712
+ countryCode: "IN",
713
+ flag: "🇮🇳",
714
+ dialCode: "+91",
715
+ nativeName: "\u0939\u093F\u0928\u094D\u0926\u0940",
716
+ englishName: "Hindi",
717
+ currencyCode: "INR",
718
+ dateFormat: "DD/MM/YYYY"
719
+ },
720
+ // 태국어
721
+ th: {
722
+ locale: "th",
723
+ countryCode: "TH",
724
+ flag: "🇹🇭",
725
+ dialCode: "+66",
726
+ nativeName: "\u0E44\u0E17\u0E22",
727
+ englishName: "Thai",
728
+ currencyCode: "THB",
729
+ dateFormat: "DD/MM/YYYY"
730
+ },
731
+ // 베트남어
732
+ vi: {
733
+ locale: "vi",
734
+ countryCode: "VN",
735
+ flag: "🇻🇳",
736
+ dialCode: "+84",
737
+ nativeName: "Ti\u1EBFng Vi\u1EC7t",
738
+ englishName: "Vietnamese",
739
+ currencyCode: "VND",
740
+ dateFormat: "DD/MM/YYYY"
741
+ },
742
+ // 인도네시아어
743
+ id: {
744
+ locale: "id",
745
+ countryCode: "ID",
746
+ flag: "🇮🇩",
747
+ dialCode: "+62",
748
+ nativeName: "Bahasa Indonesia",
749
+ englishName: "Indonesian",
750
+ currencyCode: "IDR",
751
+ dateFormat: "DD/MM/YYYY"
752
+ },
753
+ // 터키어
754
+ tr: {
755
+ locale: "tr",
756
+ countryCode: "TR",
757
+ flag: "🇹🇷",
758
+ dialCode: "+90",
759
+ nativeName: "T\xFCrk\xE7e",
760
+ englishName: "Turkish",
761
+ currencyCode: "TRY",
762
+ dateFormat: "DD.MM.YYYY"
763
+ },
764
+ // 폴란드어
765
+ pl: {
766
+ locale: "pl",
767
+ countryCode: "PL",
768
+ flag: "🇵🇱",
769
+ dialCode: "+48",
770
+ nativeName: "Polski",
771
+ englishName: "Polish",
772
+ currencyCode: "PLN",
773
+ dateFormat: "DD.MM.YYYY"
774
+ },
775
+ // 네덜란드어
776
+ nl: {
777
+ locale: "nl",
778
+ countryCode: "NL",
779
+ flag: "🇳🇱",
780
+ dialCode: "+31",
781
+ nativeName: "Nederlands",
782
+ englishName: "Dutch",
783
+ currencyCode: "EUR",
784
+ dateFormat: "DD-MM-YYYY"
785
+ },
786
+ // 중국어 (홍콩)
787
+ "zh-HK": {
788
+ locale: "zh-HK",
789
+ countryCode: "HK",
790
+ flag: "🇭🇰",
791
+ dialCode: "+852",
792
+ nativeName: "\u7E41\u9AD4\u4E2D\u6587 (\u9999\u6E2F)",
793
+ englishName: "Chinese (Hong Kong)",
794
+ currencyCode: "HKD",
795
+ dateFormat: "YYYY/MM/DD"
796
+ },
797
+ // 말레이어
798
+ ms: {
799
+ locale: "ms",
800
+ countryCode: "MY",
801
+ flag: "🇲🇾",
802
+ dialCode: "+60",
803
+ nativeName: "Bahasa Melayu",
804
+ englishName: "Malay",
805
+ currencyCode: "MYR",
806
+ dateFormat: "DD/MM/YYYY"
807
+ },
808
+ // 영어 (영국)
809
+ "en-GB": {
810
+ locale: "en-GB",
811
+ countryCode: "GB",
812
+ flag: "🇬🇧",
813
+ dialCode: "+44",
814
+ nativeName: "English (UK)",
815
+ englishName: "English (United Kingdom)",
816
+ currencyCode: "GBP",
817
+ dateFormat: "DD/MM/YYYY"
818
+ },
819
+ // 영어 (캐나다)
820
+ "en-CA": {
821
+ locale: "en-CA",
822
+ countryCode: "CA",
823
+ flag: "🇨🇦",
824
+ dialCode: "+1",
825
+ nativeName: "English (Canada)",
826
+ englishName: "English (Canada)",
827
+ currencyCode: "CAD",
828
+ dateFormat: "YYYY-MM-DD"
829
+ },
830
+ // 영어 (호주)
831
+ "en-AU": {
832
+ locale: "en-AU",
833
+ countryCode: "AU",
834
+ flag: "🇦🇺",
835
+ dialCode: "+61",
836
+ nativeName: "English (Australia)",
837
+ englishName: "English (Australia)",
838
+ currencyCode: "AUD",
839
+ dateFormat: "DD/MM/YYYY"
840
+ },
841
+ // 영어 (뉴질랜드)
842
+ "en-NZ": {
843
+ locale: "en-NZ",
844
+ countryCode: "NZ",
845
+ flag: "🇳🇿",
846
+ dialCode: "+64",
847
+ nativeName: "English (New Zealand)",
848
+ englishName: "English (New Zealand)",
849
+ currencyCode: "NZD",
850
+ dateFormat: "DD/MM/YYYY"
851
+ },
852
+ // 스페인어 (멕시코)
853
+ "es-MX": {
854
+ locale: "es-MX",
855
+ countryCode: "MX",
856
+ flag: "🇲🇽",
857
+ dialCode: "+52",
858
+ nativeName: "Espa\xF1ol (M\xE9xico)",
859
+ englishName: "Spanish (Mexico)",
860
+ currencyCode: "MXN",
861
+ dateFormat: "DD/MM/YYYY"
862
+ },
863
+ // 스페인어 (아르헨티나)
864
+ "es-AR": {
865
+ locale: "es-AR",
866
+ countryCode: "AR",
867
+ flag: "🇦🇷",
868
+ dialCode: "+54",
869
+ nativeName: "Espa\xF1ol (Argentina)",
870
+ englishName: "Spanish (Argentina)",
871
+ currencyCode: "ARS",
872
+ dateFormat: "DD/MM/YYYY"
873
+ },
874
+ // 스페인어 (콜롬비아)
875
+ "es-CO": {
876
+ locale: "es-CO",
877
+ countryCode: "CO",
878
+ flag: "🇨🇴",
879
+ dialCode: "+57",
880
+ nativeName: "Espa\xF1ol (Colombia)",
881
+ englishName: "Spanish (Colombia)",
882
+ currencyCode: "COP",
883
+ dateFormat: "DD/MM/YYYY"
884
+ },
885
+ // 스웨덴어
886
+ sv: {
887
+ locale: "sv",
888
+ countryCode: "SE",
889
+ flag: "🇸🇪",
890
+ dialCode: "+46",
891
+ nativeName: "Svenska",
892
+ englishName: "Swedish",
893
+ currencyCode: "SEK",
894
+ dateFormat: "YYYY-MM-DD"
895
+ },
896
+ // 노르웨이어
897
+ no: {
898
+ locale: "no",
899
+ countryCode: "NO",
900
+ flag: "🇳🇴",
901
+ dialCode: "+47",
902
+ nativeName: "Norsk",
903
+ englishName: "Norwegian",
904
+ currencyCode: "NOK",
905
+ dateFormat: "DD.MM.YYYY"
906
+ },
907
+ // 덴마크어
908
+ da: {
909
+ locale: "da",
910
+ countryCode: "DK",
911
+ flag: "🇩🇰",
912
+ dialCode: "+45",
913
+ nativeName: "Dansk",
914
+ englishName: "Danish",
915
+ currencyCode: "DKK",
916
+ dateFormat: "DD-MM-YYYY"
917
+ },
918
+ // 핀란드어
919
+ fi: {
920
+ locale: "fi",
921
+ countryCode: "FI",
922
+ flag: "🇫🇮",
923
+ dialCode: "+358",
924
+ nativeName: "Suomi",
925
+ englishName: "Finnish",
926
+ currencyCode: "EUR",
927
+ dateFormat: "DD.MM.YYYY"
928
+ },
929
+ // 우크라이나어
930
+ uk: {
931
+ locale: "uk",
932
+ countryCode: "UA",
933
+ flag: "🇺🇦",
934
+ dialCode: "+380",
935
+ nativeName: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430",
936
+ englishName: "Ukrainian",
937
+ currencyCode: "UAH",
938
+ dateFormat: "DD.MM.YYYY"
939
+ },
940
+ // 체코어
941
+ cs: {
942
+ locale: "cs",
943
+ countryCode: "CZ",
944
+ flag: "🇨🇿",
945
+ dialCode: "+420",
946
+ nativeName: "\u010Ce\u0161tina",
947
+ englishName: "Czech",
948
+ currencyCode: "CZK",
949
+ dateFormat: "DD.MM.YYYY"
950
+ },
951
+ // 헝가리어
952
+ hu: {
953
+ locale: "hu",
954
+ countryCode: "HU",
955
+ flag: "🇭🇺",
956
+ dialCode: "+36",
957
+ nativeName: "Magyar",
958
+ englishName: "Hungarian",
959
+ currencyCode: "HUF",
960
+ dateFormat: "YYYY.MM.DD."
961
+ },
962
+ // 루마니아어
963
+ ro: {
964
+ locale: "ro",
965
+ countryCode: "RO",
966
+ flag: "🇷🇴",
967
+ dialCode: "+40",
968
+ nativeName: "Rom\xE2n\u0103",
969
+ englishName: "Romanian",
970
+ currencyCode: "RON",
971
+ dateFormat: "DD.MM.YYYY"
972
+ },
973
+ // 불가리아어
974
+ bg: {
975
+ locale: "bg",
976
+ countryCode: "BG",
977
+ flag: "🇧🇬",
978
+ dialCode: "+359",
979
+ nativeName: "\u0411\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438",
980
+ englishName: "Bulgarian",
981
+ currencyCode: "BGN",
982
+ dateFormat: "DD.MM.YYYY"
983
+ },
984
+ // 크로아티아어
985
+ hr: {
986
+ locale: "hr",
987
+ countryCode: "HR",
988
+ flag: "🇭🇷",
989
+ dialCode: "+385",
990
+ nativeName: "Hrvatski",
991
+ englishName: "Croatian",
992
+ currencyCode: "HRK",
993
+ dateFormat: "DD.MM.YYYY."
994
+ },
995
+ // 세르비아어
996
+ sr: {
997
+ locale: "sr",
998
+ countryCode: "RS",
999
+ flag: "🇷🇸",
1000
+ dialCode: "+381",
1001
+ nativeName: "\u0421\u0440\u043F\u0441\u043A\u0438",
1002
+ englishName: "Serbian",
1003
+ currencyCode: "RSD",
1004
+ dateFormat: "DD.MM.YYYY."
1005
+ },
1006
+ // 슬로바키아어
1007
+ sk: {
1008
+ locale: "sk",
1009
+ countryCode: "SK",
1010
+ flag: "🇸🇰",
1011
+ dialCode: "+421",
1012
+ nativeName: "Sloven\u010Dina",
1013
+ englishName: "Slovak",
1014
+ currencyCode: "EUR",
1015
+ dateFormat: "DD.MM.YYYY"
1016
+ },
1017
+ // 슬로베니아어
1018
+ sl: {
1019
+ locale: "sl",
1020
+ countryCode: "SI",
1021
+ flag: "🇸🇮",
1022
+ dialCode: "+386",
1023
+ nativeName: "Sloven\u0161\u010Dina",
1024
+ englishName: "Slovenian",
1025
+ currencyCode: "EUR",
1026
+ dateFormat: "DD.MM.YYYY"
1027
+ },
1028
+ // 리투아니아어
1029
+ lt: {
1030
+ locale: "lt",
1031
+ countryCode: "LT",
1032
+ flag: "🇱🇹",
1033
+ dialCode: "+370",
1034
+ nativeName: "Lietuvi\u0173",
1035
+ englishName: "Lithuanian",
1036
+ currencyCode: "EUR",
1037
+ dateFormat: "YYYY-MM-DD"
1038
+ },
1039
+ // 라트비아어
1040
+ lv: {
1041
+ locale: "lv",
1042
+ countryCode: "LV",
1043
+ flag: "🇱🇻",
1044
+ dialCode: "+371",
1045
+ nativeName: "Latvie\u0161u",
1046
+ englishName: "Latvian",
1047
+ currencyCode: "EUR",
1048
+ dateFormat: "DD.MM.YYYY."
1049
+ },
1050
+ // 에스토니아어
1051
+ et: {
1052
+ locale: "et",
1053
+ countryCode: "EE",
1054
+ flag: "🇪🇪",
1055
+ dialCode: "+372",
1056
+ nativeName: "Eesti",
1057
+ englishName: "Estonian",
1058
+ currencyCode: "EUR",
1059
+ dateFormat: "DD.MM.YYYY"
1060
+ },
1061
+ // 그리스어
1062
+ el: {
1063
+ locale: "el",
1064
+ countryCode: "GR",
1065
+ flag: "🇬🇷",
1066
+ dialCode: "+30",
1067
+ nativeName: "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC",
1068
+ englishName: "Greek",
1069
+ currencyCode: "EUR",
1070
+ dateFormat: "DD/MM/YYYY"
1071
+ },
1072
+ // 페르시아어
1073
+ fa: {
1074
+ locale: "fa",
1075
+ countryCode: "IR",
1076
+ flag: "🇮🇷",
1077
+ dialCode: "+98",
1078
+ nativeName: "\u0641\u0627\u0631\u0633\u06CC",
1079
+ englishName: "Persian",
1080
+ rtl: true,
1081
+ currencyCode: "IRR",
1082
+ dateFormat: "YYYY/MM/DD"
1083
+ },
1084
+ // 히브리어
1085
+ he: {
1086
+ locale: "he",
1087
+ countryCode: "IL",
1088
+ flag: "🇮🇱",
1089
+ dialCode: "+972",
1090
+ nativeName: "\u05E2\u05D1\u05E8\u05D9\u05EA",
1091
+ englishName: "Hebrew",
1092
+ rtl: true,
1093
+ currencyCode: "ILS",
1094
+ dateFormat: "DD/MM/YYYY"
1095
+ },
1096
+ // 스와힐리어
1097
+ sw: {
1098
+ locale: "sw",
1099
+ countryCode: "KE",
1100
+ flag: "🇰🇪",
1101
+ dialCode: "+254",
1102
+ nativeName: "Kiswahili",
1103
+ englishName: "Swahili",
1104
+ currencyCode: "KES",
1105
+ dateFormat: "DD/MM/YYYY"
403
1106
  }
404
1107
  };
1108
+ function getLocaleInfo(locale) {
1109
+ return LOCALE_INFO_MAP[locale];
1110
+ }
1111
+ function getSupportedLocales() {
1112
+ return Object.keys(LOCALE_INFO_MAP);
1113
+ }
1114
+ function getFlag(locale) {
1115
+ return LOCALE_INFO_MAP[locale]?.flag ?? "";
1116
+ }
1117
+ function getDialCode(locale) {
1118
+ return LOCALE_INFO_MAP[locale]?.dialCode ?? "";
1119
+ }
1120
+ function isRTL(locale) {
1121
+ return LOCALE_INFO_MAP[locale]?.rtl ?? false;
1122
+ }
1123
+
1124
+ // src/server/helpers/locale.actions.ts
1125
+ async function detectBrowserLanguage() {
1126
+ try {
1127
+ const headersList = await headers();
1128
+ const acceptLanguage = headersList.get("accept-language");
1129
+ if (!acceptLanguage) {
1130
+ return null;
1131
+ }
1132
+ const languages = acceptLanguage.split(",").map((lang) => {
1133
+ const [code] = lang.split(";");
1134
+ return code.split("-")[0].trim();
1135
+ });
1136
+ const config = getCmsConfig();
1137
+ for (const lang of languages) {
1138
+ if (config.supportedLocales.includes(lang)) {
1139
+ return lang;
1140
+ }
1141
+ }
1142
+ return null;
1143
+ } catch (error) {
1144
+ return null;
1145
+ }
1146
+ }
1147
+ async function getLocale() {
1148
+ const config = getCmsConfig();
1149
+ const cookieStore = await cookies();
1150
+ const cookieLocale = cookieStore.get(LOCALE_COOKIE_KEY)?.value;
1151
+ if (cookieLocale && config.supportedLocales.includes(cookieLocale)) {
1152
+ return cookieLocale;
1153
+ }
1154
+ if (config.detectBrowserLanguage) {
1155
+ const browserLang = await detectBrowserLanguage();
1156
+ if (browserLang) {
1157
+ return browserLang;
1158
+ }
1159
+ }
1160
+ return config.defaultLocale;
1161
+ }
1162
+ async function setLocale(locale) {
1163
+ const config = getCmsConfig();
1164
+ if (!config.supportedLocales.includes(locale)) {
1165
+ throw new Error(
1166
+ `Unsupported locale: ${locale}. Supported locales: ${config.supportedLocales.join(", ")}`
1167
+ );
1168
+ }
1169
+ const cookieStore = await cookies();
1170
+ cookieStore.set(LOCALE_COOKIE_KEY, locale, {
1171
+ path: "/",
1172
+ maxAge: 60 * 60 * 24 * 365,
1173
+ // 1년
1174
+ sameSite: "lax"
1175
+ });
1176
+ }
1177
+ async function getLocales() {
1178
+ const config = getCmsConfig();
1179
+ return config.supportedLocales;
1180
+ }
1181
+ async function getLocaleWithInfo() {
1182
+ const locale = await getLocale();
1183
+ const info = getLocaleInfo(locale);
1184
+ return { locale, info };
1185
+ }
1186
+ async function getLocalesWithInfo() {
1187
+ const config = getCmsConfig();
1188
+ const supportedLocales = config.supportedLocales;
1189
+ return supportedLocales.map((locale) => getLocaleInfo(locale)).filter((info) => info !== void 0);
1190
+ }
405
1191
  export {
406
1192
  InitCms,
407
- cmsApi,
1193
+ LOCALE_COOKIE_KEY,
1194
+ LOCALE_INFO_MAP,
1195
+ getDialCode,
1196
+ getFlag,
1197
+ getLocale,
1198
+ getLocaleInfo,
1199
+ getLocaleWithInfo,
1200
+ getLocales,
1201
+ getLocalesWithInfo,
1202
+ getSupportedLocales,
1203
+ isRTL,
1204
+ setLocale,
408
1205
  useCmsStore,
409
1206
  useSection,
410
1207
  useSections