talizen 0.2.0 → 0.2.2

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 (4) hide show
  1. package/cms.js +24 -2
  2. package/i18n.d.ts +22 -0
  3. package/i18n.js +35 -0
  4. package/package.json +1 -1
package/cms.js CHANGED
@@ -1,4 +1,18 @@
1
1
  import { requestJson } from "./core.js";
2
+ import { useLocale } from "./i18n.js";
3
+ // 字段级本地化解码:把 body._i18n[当前语言] 覆盖到同级字段并删除 _i18n。
4
+ // 线上渲染引擎已在服务端解码(body 无 _i18n)→ 此处 no-op;编辑器预览未解码 → 此处按当前语言解码。
5
+ function decodeBodyLocalization(body) {
6
+ if (!body || typeof body !== "object" || !("_i18n" in body))
7
+ return body;
8
+ const { _i18n, ...base } = body;
9
+ const locale = useLocale().locale;
10
+ const over = locale && _i18n ? _i18n[locale] : undefined;
11
+ return over && typeof over === "object" ? { ...base, ...over } : base;
12
+ }
13
+ function decodeItem(item) {
14
+ return item && item.body ? { ...item, body: decodeBodyLocalization(item.body) } : item;
15
+ }
2
16
  export async function getContentCollection(key, options) {
3
17
  return requestJson(`/cms/${key}`, undefined, options);
4
18
  }
@@ -14,6 +28,8 @@ export async function listContents(key, params = {}, options) {
14
28
  filter: params.filter,
15
29
  }),
16
30
  }, options);
31
+ if (response.list)
32
+ response.list = response.list.map(decodeItem);
17
33
  return response;
18
34
  }
19
35
  export async function getContent(key, slug, params, options) {
@@ -22,10 +38,11 @@ export async function getContent(key, slug, params, options) {
22
38
  if (params?.builtinRef != null) {
23
39
  url.searchParams.set("builtin_ref", String(params.builtinRef));
24
40
  }
25
- return requestJson(url.pathname + url.search, undefined, options);
41
+ const item = await requestJson(url.pathname + url.search, undefined, options);
42
+ return decodeItem(item);
26
43
  }
27
44
  export async function getContentWithPrevNext(key, slug, params = {}, options) {
28
- return requestJson(`/cms/${key}/content_with_prev_next`, {
45
+ const res = await requestJson(`/cms/${key}/content_with_prev_next`, {
29
46
  method: "POST",
30
47
  body: JSON.stringify({
31
48
  slug,
@@ -37,4 +54,9 @@ export async function getContentWithPrevNext(key, slug, params = {}, options) {
37
54
  filter: params.filter,
38
55
  }),
39
56
  }, options);
57
+ return {
58
+ current: res.current ? decodeItem(res.current) : res.current,
59
+ next: res.next ? decodeItem(res.next) : res.next,
60
+ prev: res.prev ? decodeItem(res.prev) : res.prev,
61
+ };
40
62
  }
package/i18n.d.ts CHANGED
@@ -53,3 +53,25 @@ export type LinkProps = React.AnchorHTMLAttributes<HTMLAnchorElement> & {
53
53
  * ```
54
54
  */
55
55
  export declare function Link(props: LinkProps): React.ReactElement;
56
+ /**
57
+ * 当前语言的 UI 文案(界面 chrome,非 CMS 内容)。由渲染引擎按当前 locale 从站点
58
+ * `/messages/{locale}.json` 读取、与默认语言逐 key 合并后注入 `globalThis.__TALIZEN_MESSAGES__`
59
+ * (SSR 与浏览器端一致)。缺失的 key 已在引擎侧回退默认语言,这里再兜底返回 key 本身。
60
+ */
61
+ declare global {
62
+ var __TALIZEN_MESSAGES__: Record<string, unknown> | undefined;
63
+ }
64
+ /** 翻译函数:按 key 取文案(支持点路径),`{var}` 插值;缺失时返回 key 本身。 */
65
+ export type Translator = (key: string, vars?: Record<string, unknown>) => string;
66
+ /**
67
+ * 读取 UI 文案翻译器。`namespace` 可将后续 key 限定到 messages 的某个子树(对齐 next-intl)。
68
+ *
69
+ * ```tsx
70
+ * const t = useTranslations("home")
71
+ * t("title") // messages.home.title
72
+ * t("greeting", { name }) // "你好 {name}" -> "你好 小明"
73
+ * ```
74
+ *
75
+ * UI chrome 用 useTranslations;文章等内容用 CMS 字段级 _i18n(见 listContents/getContent)。
76
+ */
77
+ export declare function useTranslations(namespace?: string): Translator;
package/i18n.js CHANGED
@@ -60,3 +60,38 @@ export function Link(props) {
60
60
  };
61
61
  return React.createElement("a", { ...rest, href: finalHref, onClick: handleClick });
62
62
  }
63
+ function readMessages() {
64
+ return (typeof globalThis !== "undefined" ? globalThis.__TALIZEN_MESSAGES__ : undefined) ?? {};
65
+ }
66
+ function lookupMessage(obj, path) {
67
+ let cur = obj;
68
+ for (const seg of path.split(".")) {
69
+ if (cur == null || typeof cur !== "object")
70
+ return undefined;
71
+ cur = cur[seg];
72
+ }
73
+ return cur;
74
+ }
75
+ /**
76
+ * 读取 UI 文案翻译器。`namespace` 可将后续 key 限定到 messages 的某个子树(对齐 next-intl)。
77
+ *
78
+ * ```tsx
79
+ * const t = useTranslations("home")
80
+ * t("title") // messages.home.title
81
+ * t("greeting", { name }) // "你好 {name}" -> "你好 小明"
82
+ * ```
83
+ *
84
+ * UI chrome 用 useTranslations;文章等内容用 CMS 字段级 _i18n(见 listContents/getContent)。
85
+ */
86
+ export function useTranslations(namespace) {
87
+ const messages = readMessages();
88
+ const scoped = namespace ? lookupMessage(messages, namespace) : messages;
89
+ const base = (scoped && typeof scoped === "object" ? scoped : {});
90
+ return (key, vars) => {
91
+ const raw = lookupMessage(base, key);
92
+ const text = typeof raw === "string" ? raw : key;
93
+ if (!vars)
94
+ return text;
95
+ return text.replace(/\{(\w+)\}/g, (_, k) => (k in vars ? String(vars[k]) : `{${k}}`));
96
+ };
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "talizen",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Talizen frontend SDK types for cms, form and core.",
5
5
  "type": "module",
6
6
  "license": "MIT",