@pactor-app/i18n 1.57.0 → 1.58.0
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/README.md +1 -1
- package/dist/{chunk-3YCHXWF6.js → chunk-QU4YMNND.js} +50 -54
- package/dist/index.cjs +50 -54
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -1
- package/dist/node.cjs +50 -54
- package/dist/node.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ const schema = localizePageDsl({
|
|
|
98
98
|
});
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
统一处理 `$i18n.messages`、`$i18n.key`
|
|
101
|
+
统一处理 `$i18n.messages`、`$i18n.key` 与字段内 `$i18n{key:default}` 三种声明;查找顺序为 当前 locale → 显式语言级回退(fallbacks 有序)→ defaultLocale → 描述 fallback(简写中的 default)→ key;空字符串视为有效翻译。返回新对象,不修改输入;静态结果输出字符串;含 `${...}` 运行时参数时编译为 `{ $localizedText: { template, params } }`;词条/fallback 含字面量 `${` 抛 `UNSAFE_MESSAGE`。
|
|
102
102
|
|
|
103
103
|
## localizeYamlResource(Node 入口,YAML 资源编排)
|
|
104
104
|
|
|
@@ -529,12 +529,13 @@ var TranslatableFieldRegistry = class {
|
|
|
529
529
|
}
|
|
530
530
|
};
|
|
531
531
|
function createDefaultTranslatableFieldRegistry() {
|
|
532
|
-
return new TranslatableFieldRegistry().register("Button", ["props.text", "props.label", "props.description", "props.ariaLabel"]).register("Text", ["props.text", "props.description", "props.ariaLabel"]).register("Input", [
|
|
532
|
+
return new TranslatableFieldRegistry().register("Page", "props.title").register("Card", "props.title").register("Statistic", "props.title").register("Alert", ["props.message", "props.description"]).register("Typography", "props.text").register("Login", ["props.brand.title", "props.title", "props.submitText"]).register("Header", ["props.user.items[*].label"]).register("DropdownMenu", ["props.items[*].label"]).register("Button", ["props.text", "props.label", "props.description", "props.ariaLabel"]).register("Text", ["props.text", "props.description", "props.ariaLabel"]).register("Input", [
|
|
533
533
|
"props.text",
|
|
534
534
|
"props.label",
|
|
535
535
|
"props.placeholder",
|
|
536
536
|
"props.description",
|
|
537
|
-
"props.ariaLabel"
|
|
537
|
+
"props.ariaLabel",
|
|
538
|
+
"props.rules[*].message"
|
|
538
539
|
]).register("Table", [
|
|
539
540
|
"props.label",
|
|
540
541
|
"props.description",
|
|
@@ -546,7 +547,7 @@ function createDefaultTranslatableFieldRegistry() {
|
|
|
546
547
|
"props.description",
|
|
547
548
|
"props.ariaLabel",
|
|
548
549
|
"props.options[*].label"
|
|
549
|
-
]).registerAction("message", ["params.text"]).registerAction("confirm", ["params.title", "params.content"]);
|
|
550
|
+
]).register("DatePicker", ["props.placeholder", "props.presets[*].label"]).register("ToggleGroup", ["props.options[*].label"]).registerAction("message", ["params.text"]).registerAction("confirm", ["params.title", "params.content"]);
|
|
550
551
|
}
|
|
551
552
|
var defaultRegistry;
|
|
552
553
|
function getDefaultTranslatableFieldRegistry() {
|
|
@@ -637,7 +638,6 @@ function resolveJsonPointer(root, segments, pointer) {
|
|
|
637
638
|
// src/dsl-localizer.ts
|
|
638
639
|
import { isDslSourceRef } from "@pactor-app/core";
|
|
639
640
|
var MISSING_TRANSLATION_TEXT = "[missing translation]";
|
|
640
|
-
var FORBIDDEN_BINDING_ROOTS = /* @__PURE__ */ new Set(["state", "dataSources", "requires", "meta", "i18n"]);
|
|
641
641
|
function isPlainObject3(value) {
|
|
642
642
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
643
643
|
}
|
|
@@ -791,7 +791,32 @@ function convertDescriptor(ctx, desc, implicitFallback, path) {
|
|
|
791
791
|
}
|
|
792
792
|
return template;
|
|
793
793
|
}
|
|
794
|
+
function parseShorthand(value, path) {
|
|
795
|
+
const invalid = () => new I18nError("I18N_DESCRIPTOR_INVALID", `\u65E0\u6548\u7684\u56FD\u9645\u5316\u7B80\u5199: ${path}`, { path });
|
|
796
|
+
const colon = value.indexOf(":", 6);
|
|
797
|
+
if (!value.startsWith("$i18n{") || colon < 7) throw invalid();
|
|
798
|
+
const key = value.slice(6, colon);
|
|
799
|
+
if (!isValidMessageKey(key) || /[{}\\]/.test(key)) throw invalid();
|
|
800
|
+
let fallback = "";
|
|
801
|
+
for (let i = colon + 1; i < value.length; i++) {
|
|
802
|
+
const char = value[i];
|
|
803
|
+
if (char === "}") {
|
|
804
|
+
if (i !== value.length - 1 || fallback.includes("${")) throw invalid();
|
|
805
|
+
return { key, fallback };
|
|
806
|
+
}
|
|
807
|
+
if (char === "\\") {
|
|
808
|
+
const next = value[++i];
|
|
809
|
+
if (next !== "}" && next !== "\\") throw invalid();
|
|
810
|
+
fallback += next;
|
|
811
|
+
} else fallback += char;
|
|
812
|
+
}
|
|
813
|
+
throw invalid();
|
|
814
|
+
}
|
|
794
815
|
function walkValue(ctx, value, canonical, display, isTranslatable) {
|
|
816
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
817
|
+
if (!isTranslatable(canonical)) throw new I18nError("UNREGISTERED_I18N_FIELD", `\u672A\u6CE8\u518C\u7684\u53EF\u7FFB\u8BD1\u5B57\u6BB5: ${display}`, { path: display });
|
|
818
|
+
return convertDescriptor(ctx, parseShorthand(value, display), void 0, display);
|
|
819
|
+
}
|
|
795
820
|
if (Array.isArray(value)) {
|
|
796
821
|
for (let i = 0; i < value.length; i += 1) {
|
|
797
822
|
value[i] = walkValue(ctx, value[i], `${canonical}[*]`, `${display}/${i}`, isTranslatable);
|
|
@@ -799,6 +824,14 @@ function walkValue(ctx, value, canonical, display, isTranslatable) {
|
|
|
799
824
|
return value;
|
|
800
825
|
}
|
|
801
826
|
if (isPlainObject3(value)) {
|
|
827
|
+
if (typeof value["type"] === "string" && isPlainObject3(value["props"])) {
|
|
828
|
+
walkNode(ctx, value, display);
|
|
829
|
+
return value;
|
|
830
|
+
}
|
|
831
|
+
if (typeof value["action"] === "string") {
|
|
832
|
+
walkAction(ctx, value, display);
|
|
833
|
+
return value;
|
|
834
|
+
}
|
|
802
835
|
if ("$i18n" in value) {
|
|
803
836
|
if (!isTranslatable(canonical)) {
|
|
804
837
|
throw new I18nError(
|
|
@@ -887,55 +920,13 @@ function walkNode(ctx, node, display) {
|
|
|
887
920
|
walkNode(ctx, node["children"], `${display}/children`);
|
|
888
921
|
}
|
|
889
922
|
}
|
|
890
|
-
function assertBindingAllowed(segments, pointer) {
|
|
891
|
-
const root = segments[0];
|
|
892
|
-
if (root !== void 0 && FORBIDDEN_BINDING_ROOTS.has(root)) {
|
|
893
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u8BE5\u5B57\u6BB5: ${pointer}`, { pointer });
|
|
894
|
-
}
|
|
895
|
-
if (root === "page" && segments[1] === "id") {
|
|
896
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u9875\u9762\u6807\u8BC6: ${pointer}`, { pointer });
|
|
897
|
-
}
|
|
898
|
-
if (segments[segments.length - 1] === "permission") {
|
|
899
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u6743\u9650\u5B57\u6BB5: ${pointer}`, { pointer });
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
function applyBindings(ctx, root, bindings) {
|
|
903
|
-
const seen = /* @__PURE__ */ new Map();
|
|
904
|
-
for (const [pointer, rawDesc] of Object.entries(bindings)) {
|
|
905
|
-
const segments = parseJsonPointer(pointer);
|
|
906
|
-
const canonical = segments.map(escapeJsonPointerSegment).join("/");
|
|
907
|
-
const previous = seen.get(canonical);
|
|
908
|
-
if (previous !== void 0) {
|
|
909
|
-
throw new I18nError("BINDING_INVALID", `bindings \u8DEF\u5F84\u91CD\u590D: ${previous} \u4E0E ${pointer}`, {
|
|
910
|
-
pointer,
|
|
911
|
-
previous
|
|
912
|
-
});
|
|
913
|
-
}
|
|
914
|
-
seen.set(canonical, pointer);
|
|
915
|
-
assertBindingAllowed(segments, pointer);
|
|
916
|
-
const desc = assertDescriptor(rawDesc, pointer);
|
|
917
|
-
const resolved = resolveJsonPointer(root, segments, pointer);
|
|
918
|
-
if (isPlainObject3(resolved.value) && "$i18n" in resolved.value) {
|
|
919
|
-
throw new I18nError(
|
|
920
|
-
"BINDING_CONFLICT",
|
|
921
|
-
`\u540C\u4E00\u5B57\u6BB5\u88AB $i18n \u4E0E bindings \u91CD\u590D\u58F0\u660E: ${pointer}`,
|
|
922
|
-
{ pointer }
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
if (typeof resolved.value !== "string") {
|
|
926
|
-
throw new I18nError("BINDING_INVALID", `bindings \u76EE\u6807\u5FC5\u987B\u662F\u5B57\u7B26\u4E32: ${pointer}`, {
|
|
927
|
-
pointer,
|
|
928
|
-
valueType: Array.isArray(resolved.value) ? "array" : typeof resolved.value
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
const converted = convertDescriptor(ctx, desc, resolved.value, pointer);
|
|
932
|
-
resolved.parent[resolved.key] = converted;
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
923
|
function dedupeLocales(locales) {
|
|
936
924
|
return [...new Set(locales)];
|
|
937
925
|
}
|
|
938
926
|
function assertNoResidualI18n(value, display) {
|
|
927
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
928
|
+
throw new I18nError("UNREGISTERED_I18N_FIELD", `\u56FD\u9645\u5316\u7B80\u5199\u51FA\u73B0\u5728\u4E0D\u53EF\u7FFB\u8BD1\u7684\u4F4D\u7F6E: ${display}`, { path: display });
|
|
929
|
+
}
|
|
939
930
|
if (Array.isArray(value)) {
|
|
940
931
|
value.forEach((item, index) => assertNoResidualI18n(item, `${display}/${index}`));
|
|
941
932
|
return;
|
|
@@ -974,12 +965,17 @@ function localizePageDsl(input) {
|
|
|
974
965
|
namedComponents: isPlainObject3(page.components) ? page.components : void 0,
|
|
975
966
|
onDiagnostic: input.onDiagnostic
|
|
976
967
|
};
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
968
|
+
if (isPlainObject3(page.i18n) && "bindings" in page.i18n) {
|
|
969
|
+
throw new I18nError("I18N_DESCRIPTOR_INVALID", "i18n.bindings \u5DF2\u79FB\u9664\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BB5\u5185\u7B80\u5199");
|
|
970
|
+
}
|
|
971
|
+
delete page.i18n;
|
|
972
|
+
if (isPlainObject3(page.page)) {
|
|
973
|
+
for (const field of ["title", "description"]) {
|
|
974
|
+
const value = page.page[field];
|
|
975
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
976
|
+
page.page[field] = convertDescriptor(ctx, parseShorthand(value, `/page/${field}`), void 0, `/page/${field}`);
|
|
977
|
+
}
|
|
981
978
|
}
|
|
982
|
-
delete page.i18n;
|
|
983
979
|
}
|
|
984
980
|
if (page.body !== void 0) {
|
|
985
981
|
walkNode(ctx, page.body, "/body");
|
package/dist/index.cjs
CHANGED
|
@@ -572,12 +572,13 @@ var TranslatableFieldRegistry = class {
|
|
|
572
572
|
}
|
|
573
573
|
};
|
|
574
574
|
function createDefaultTranslatableFieldRegistry() {
|
|
575
|
-
return new TranslatableFieldRegistry().register("Button", ["props.text", "props.label", "props.description", "props.ariaLabel"]).register("Text", ["props.text", "props.description", "props.ariaLabel"]).register("Input", [
|
|
575
|
+
return new TranslatableFieldRegistry().register("Page", "props.title").register("Card", "props.title").register("Statistic", "props.title").register("Alert", ["props.message", "props.description"]).register("Typography", "props.text").register("Login", ["props.brand.title", "props.title", "props.submitText"]).register("Header", ["props.user.items[*].label"]).register("DropdownMenu", ["props.items[*].label"]).register("Button", ["props.text", "props.label", "props.description", "props.ariaLabel"]).register("Text", ["props.text", "props.description", "props.ariaLabel"]).register("Input", [
|
|
576
576
|
"props.text",
|
|
577
577
|
"props.label",
|
|
578
578
|
"props.placeholder",
|
|
579
579
|
"props.description",
|
|
580
|
-
"props.ariaLabel"
|
|
580
|
+
"props.ariaLabel",
|
|
581
|
+
"props.rules[*].message"
|
|
581
582
|
]).register("Table", [
|
|
582
583
|
"props.label",
|
|
583
584
|
"props.description",
|
|
@@ -589,7 +590,7 @@ function createDefaultTranslatableFieldRegistry() {
|
|
|
589
590
|
"props.description",
|
|
590
591
|
"props.ariaLabel",
|
|
591
592
|
"props.options[*].label"
|
|
592
|
-
]).registerAction("message", ["params.text"]).registerAction("confirm", ["params.title", "params.content"]);
|
|
593
|
+
]).register("DatePicker", ["props.placeholder", "props.presets[*].label"]).register("ToggleGroup", ["props.options[*].label"]).registerAction("message", ["params.text"]).registerAction("confirm", ["params.title", "params.content"]);
|
|
593
594
|
}
|
|
594
595
|
var defaultRegistry;
|
|
595
596
|
function getDefaultTranslatableFieldRegistry() {
|
|
@@ -680,7 +681,6 @@ function resolveJsonPointer(root, segments, pointer) {
|
|
|
680
681
|
// src/dsl-localizer.ts
|
|
681
682
|
var import_core = require("@pactor-app/core");
|
|
682
683
|
var MISSING_TRANSLATION_TEXT = "[missing translation]";
|
|
683
|
-
var FORBIDDEN_BINDING_ROOTS = /* @__PURE__ */ new Set(["state", "dataSources", "requires", "meta", "i18n"]);
|
|
684
684
|
function isPlainObject3(value) {
|
|
685
685
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
686
686
|
}
|
|
@@ -834,7 +834,32 @@ function convertDescriptor(ctx, desc, implicitFallback, path) {
|
|
|
834
834
|
}
|
|
835
835
|
return template;
|
|
836
836
|
}
|
|
837
|
+
function parseShorthand(value, path) {
|
|
838
|
+
const invalid = () => new I18nError("I18N_DESCRIPTOR_INVALID", `\u65E0\u6548\u7684\u56FD\u9645\u5316\u7B80\u5199: ${path}`, { path });
|
|
839
|
+
const colon = value.indexOf(":", 6);
|
|
840
|
+
if (!value.startsWith("$i18n{") || colon < 7) throw invalid();
|
|
841
|
+
const key = value.slice(6, colon);
|
|
842
|
+
if (!isValidMessageKey(key) || /[{}\\]/.test(key)) throw invalid();
|
|
843
|
+
let fallback = "";
|
|
844
|
+
for (let i = colon + 1; i < value.length; i++) {
|
|
845
|
+
const char = value[i];
|
|
846
|
+
if (char === "}") {
|
|
847
|
+
if (i !== value.length - 1 || fallback.includes("${")) throw invalid();
|
|
848
|
+
return { key, fallback };
|
|
849
|
+
}
|
|
850
|
+
if (char === "\\") {
|
|
851
|
+
const next = value[++i];
|
|
852
|
+
if (next !== "}" && next !== "\\") throw invalid();
|
|
853
|
+
fallback += next;
|
|
854
|
+
} else fallback += char;
|
|
855
|
+
}
|
|
856
|
+
throw invalid();
|
|
857
|
+
}
|
|
837
858
|
function walkValue(ctx, value, canonical, display, isTranslatable) {
|
|
859
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
860
|
+
if (!isTranslatable(canonical)) throw new I18nError("UNREGISTERED_I18N_FIELD", `\u672A\u6CE8\u518C\u7684\u53EF\u7FFB\u8BD1\u5B57\u6BB5: ${display}`, { path: display });
|
|
861
|
+
return convertDescriptor(ctx, parseShorthand(value, display), void 0, display);
|
|
862
|
+
}
|
|
838
863
|
if (Array.isArray(value)) {
|
|
839
864
|
for (let i = 0; i < value.length; i += 1) {
|
|
840
865
|
value[i] = walkValue(ctx, value[i], `${canonical}[*]`, `${display}/${i}`, isTranslatable);
|
|
@@ -842,6 +867,14 @@ function walkValue(ctx, value, canonical, display, isTranslatable) {
|
|
|
842
867
|
return value;
|
|
843
868
|
}
|
|
844
869
|
if (isPlainObject3(value)) {
|
|
870
|
+
if (typeof value["type"] === "string" && isPlainObject3(value["props"])) {
|
|
871
|
+
walkNode(ctx, value, display);
|
|
872
|
+
return value;
|
|
873
|
+
}
|
|
874
|
+
if (typeof value["action"] === "string") {
|
|
875
|
+
walkAction(ctx, value, display);
|
|
876
|
+
return value;
|
|
877
|
+
}
|
|
845
878
|
if ("$i18n" in value) {
|
|
846
879
|
if (!isTranslatable(canonical)) {
|
|
847
880
|
throw new I18nError(
|
|
@@ -930,55 +963,13 @@ function walkNode(ctx, node, display) {
|
|
|
930
963
|
walkNode(ctx, node["children"], `${display}/children`);
|
|
931
964
|
}
|
|
932
965
|
}
|
|
933
|
-
function assertBindingAllowed(segments, pointer) {
|
|
934
|
-
const root = segments[0];
|
|
935
|
-
if (root !== void 0 && FORBIDDEN_BINDING_ROOTS.has(root)) {
|
|
936
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u8BE5\u5B57\u6BB5: ${pointer}`, { pointer });
|
|
937
|
-
}
|
|
938
|
-
if (root === "page" && segments[1] === "id") {
|
|
939
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u9875\u9762\u6807\u8BC6: ${pointer}`, { pointer });
|
|
940
|
-
}
|
|
941
|
-
if (segments[segments.length - 1] === "permission") {
|
|
942
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u6743\u9650\u5B57\u6BB5: ${pointer}`, { pointer });
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
function applyBindings(ctx, root, bindings) {
|
|
946
|
-
const seen = /* @__PURE__ */ new Map();
|
|
947
|
-
for (const [pointer, rawDesc] of Object.entries(bindings)) {
|
|
948
|
-
const segments = parseJsonPointer(pointer);
|
|
949
|
-
const canonical = segments.map(escapeJsonPointerSegment).join("/");
|
|
950
|
-
const previous = seen.get(canonical);
|
|
951
|
-
if (previous !== void 0) {
|
|
952
|
-
throw new I18nError("BINDING_INVALID", `bindings \u8DEF\u5F84\u91CD\u590D: ${previous} \u4E0E ${pointer}`, {
|
|
953
|
-
pointer,
|
|
954
|
-
previous
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
seen.set(canonical, pointer);
|
|
958
|
-
assertBindingAllowed(segments, pointer);
|
|
959
|
-
const desc = assertDescriptor(rawDesc, pointer);
|
|
960
|
-
const resolved = resolveJsonPointer(root, segments, pointer);
|
|
961
|
-
if (isPlainObject3(resolved.value) && "$i18n" in resolved.value) {
|
|
962
|
-
throw new I18nError(
|
|
963
|
-
"BINDING_CONFLICT",
|
|
964
|
-
`\u540C\u4E00\u5B57\u6BB5\u88AB $i18n \u4E0E bindings \u91CD\u590D\u58F0\u660E: ${pointer}`,
|
|
965
|
-
{ pointer }
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
if (typeof resolved.value !== "string") {
|
|
969
|
-
throw new I18nError("BINDING_INVALID", `bindings \u76EE\u6807\u5FC5\u987B\u662F\u5B57\u7B26\u4E32: ${pointer}`, {
|
|
970
|
-
pointer,
|
|
971
|
-
valueType: Array.isArray(resolved.value) ? "array" : typeof resolved.value
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
const converted = convertDescriptor(ctx, desc, resolved.value, pointer);
|
|
975
|
-
resolved.parent[resolved.key] = converted;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
966
|
function dedupeLocales(locales) {
|
|
979
967
|
return [...new Set(locales)];
|
|
980
968
|
}
|
|
981
969
|
function assertNoResidualI18n(value, display) {
|
|
970
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
971
|
+
throw new I18nError("UNREGISTERED_I18N_FIELD", `\u56FD\u9645\u5316\u7B80\u5199\u51FA\u73B0\u5728\u4E0D\u53EF\u7FFB\u8BD1\u7684\u4F4D\u7F6E: ${display}`, { path: display });
|
|
972
|
+
}
|
|
982
973
|
if (Array.isArray(value)) {
|
|
983
974
|
value.forEach((item, index) => assertNoResidualI18n(item, `${display}/${index}`));
|
|
984
975
|
return;
|
|
@@ -1017,12 +1008,17 @@ function localizePageDsl(input) {
|
|
|
1017
1008
|
namedComponents: isPlainObject3(page.components) ? page.components : void 0,
|
|
1018
1009
|
onDiagnostic: input.onDiagnostic
|
|
1019
1010
|
};
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1011
|
+
if (isPlainObject3(page.i18n) && "bindings" in page.i18n) {
|
|
1012
|
+
throw new I18nError("I18N_DESCRIPTOR_INVALID", "i18n.bindings \u5DF2\u79FB\u9664\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BB5\u5185\u7B80\u5199");
|
|
1013
|
+
}
|
|
1014
|
+
delete page.i18n;
|
|
1015
|
+
if (isPlainObject3(page.page)) {
|
|
1016
|
+
for (const field of ["title", "description"]) {
|
|
1017
|
+
const value = page.page[field];
|
|
1018
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
1019
|
+
page.page[field] = convertDescriptor(ctx, parseShorthand(value, `/page/${field}`), void 0, `/page/${field}`);
|
|
1020
|
+
}
|
|
1024
1021
|
}
|
|
1025
|
-
delete page.i18n;
|
|
1026
1022
|
}
|
|
1027
1023
|
if (page.body !== void 0) {
|
|
1028
1024
|
walkNode(ctx, page.body, "/body");
|
package/dist/index.d.cts
CHANGED
|
@@ -209,15 +209,14 @@ declare function resolveJsonPointer(root: unknown, segments: readonly string[],
|
|
|
209
209
|
* DslLocalizer(设计第 4、6、7、8 节,缺口 G03/G04/G17)。
|
|
210
210
|
*
|
|
211
211
|
* 纯转换入口:语言包作为参数显式传入,不在遍历组件时自行请求。
|
|
212
|
-
* 统一处理三种声明方式(`$i18n.messages`、`$i18n.key
|
|
212
|
+
* 统一处理三种声明方式(`$i18n.messages`、`$i18n.key`、字段内简写),
|
|
213
213
|
* 返回已本地化的新对象(不修改输入);编译后移除作者态 `i18n` 字段。
|
|
214
214
|
*/
|
|
215
|
-
/**
|
|
215
|
+
/** 翻译描述(`$i18n` 内层形态) */
|
|
216
216
|
type TranslationDescriptor = I18nKeyRef | I18nInlineMessages;
|
|
217
217
|
/** 页面作者态 i18n 字段(设计 4.2) */
|
|
218
218
|
interface AuthorPageI18n {
|
|
219
219
|
namespaces?: string[];
|
|
220
|
-
bindings?: Record<string, TranslationDescriptor>;
|
|
221
220
|
}
|
|
222
221
|
/** 作者态 Page DSL(PageDsl + 顶层 i18n 字段) */
|
|
223
222
|
type AuthorPageDsl = PageDsl & {
|
package/dist/index.d.ts
CHANGED
|
@@ -209,15 +209,14 @@ declare function resolveJsonPointer(root: unknown, segments: readonly string[],
|
|
|
209
209
|
* DslLocalizer(设计第 4、6、7、8 节,缺口 G03/G04/G17)。
|
|
210
210
|
*
|
|
211
211
|
* 纯转换入口:语言包作为参数显式传入,不在遍历组件时自行请求。
|
|
212
|
-
* 统一处理三种声明方式(`$i18n.messages`、`$i18n.key
|
|
212
|
+
* 统一处理三种声明方式(`$i18n.messages`、`$i18n.key`、字段内简写),
|
|
213
213
|
* 返回已本地化的新对象(不修改输入);编译后移除作者态 `i18n` 字段。
|
|
214
214
|
*/
|
|
215
|
-
/**
|
|
215
|
+
/** 翻译描述(`$i18n` 内层形态) */
|
|
216
216
|
type TranslationDescriptor = I18nKeyRef | I18nInlineMessages;
|
|
217
217
|
/** 页面作者态 i18n 字段(设计 4.2) */
|
|
218
218
|
interface AuthorPageI18n {
|
|
219
219
|
namespaces?: string[];
|
|
220
|
-
bindings?: Record<string, TranslationDescriptor>;
|
|
221
220
|
}
|
|
222
221
|
/** 作者态 Page DSL(PageDsl + 顶层 i18n 字段) */
|
|
223
222
|
type AuthorPageDsl = PageDsl & {
|
package/dist/index.js
CHANGED
package/dist/node.cjs
CHANGED
|
@@ -576,12 +576,13 @@ var TranslatableFieldRegistry = class {
|
|
|
576
576
|
}
|
|
577
577
|
};
|
|
578
578
|
function createDefaultTranslatableFieldRegistry() {
|
|
579
|
-
return new TranslatableFieldRegistry().register("Button", ["props.text", "props.label", "props.description", "props.ariaLabel"]).register("Text", ["props.text", "props.description", "props.ariaLabel"]).register("Input", [
|
|
579
|
+
return new TranslatableFieldRegistry().register("Page", "props.title").register("Card", "props.title").register("Statistic", "props.title").register("Alert", ["props.message", "props.description"]).register("Typography", "props.text").register("Login", ["props.brand.title", "props.title", "props.submitText"]).register("Header", ["props.user.items[*].label"]).register("DropdownMenu", ["props.items[*].label"]).register("Button", ["props.text", "props.label", "props.description", "props.ariaLabel"]).register("Text", ["props.text", "props.description", "props.ariaLabel"]).register("Input", [
|
|
580
580
|
"props.text",
|
|
581
581
|
"props.label",
|
|
582
582
|
"props.placeholder",
|
|
583
583
|
"props.description",
|
|
584
|
-
"props.ariaLabel"
|
|
584
|
+
"props.ariaLabel",
|
|
585
|
+
"props.rules[*].message"
|
|
585
586
|
]).register("Table", [
|
|
586
587
|
"props.label",
|
|
587
588
|
"props.description",
|
|
@@ -593,7 +594,7 @@ function createDefaultTranslatableFieldRegistry() {
|
|
|
593
594
|
"props.description",
|
|
594
595
|
"props.ariaLabel",
|
|
595
596
|
"props.options[*].label"
|
|
596
|
-
]).registerAction("message", ["params.text"]).registerAction("confirm", ["params.title", "params.content"]);
|
|
597
|
+
]).register("DatePicker", ["props.placeholder", "props.presets[*].label"]).register("ToggleGroup", ["props.options[*].label"]).registerAction("message", ["params.text"]).registerAction("confirm", ["params.title", "params.content"]);
|
|
597
598
|
}
|
|
598
599
|
var defaultRegistry;
|
|
599
600
|
function getDefaultTranslatableFieldRegistry() {
|
|
@@ -684,7 +685,6 @@ function resolveJsonPointer(root, segments, pointer) {
|
|
|
684
685
|
// src/dsl-localizer.ts
|
|
685
686
|
var import_core = require("@pactor-app/core");
|
|
686
687
|
var MISSING_TRANSLATION_TEXT = "[missing translation]";
|
|
687
|
-
var FORBIDDEN_BINDING_ROOTS = /* @__PURE__ */ new Set(["state", "dataSources", "requires", "meta", "i18n"]);
|
|
688
688
|
function isPlainObject3(value) {
|
|
689
689
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
690
690
|
}
|
|
@@ -838,7 +838,32 @@ function convertDescriptor(ctx, desc, implicitFallback, path) {
|
|
|
838
838
|
}
|
|
839
839
|
return template;
|
|
840
840
|
}
|
|
841
|
+
function parseShorthand(value, path) {
|
|
842
|
+
const invalid = () => new I18nError("I18N_DESCRIPTOR_INVALID", `\u65E0\u6548\u7684\u56FD\u9645\u5316\u7B80\u5199: ${path}`, { path });
|
|
843
|
+
const colon = value.indexOf(":", 6);
|
|
844
|
+
if (!value.startsWith("$i18n{") || colon < 7) throw invalid();
|
|
845
|
+
const key = value.slice(6, colon);
|
|
846
|
+
if (!isValidMessageKey(key) || /[{}\\]/.test(key)) throw invalid();
|
|
847
|
+
let fallback = "";
|
|
848
|
+
for (let i = colon + 1; i < value.length; i++) {
|
|
849
|
+
const char = value[i];
|
|
850
|
+
if (char === "}") {
|
|
851
|
+
if (i !== value.length - 1 || fallback.includes("${")) throw invalid();
|
|
852
|
+
return { key, fallback };
|
|
853
|
+
}
|
|
854
|
+
if (char === "\\") {
|
|
855
|
+
const next = value[++i];
|
|
856
|
+
if (next !== "}" && next !== "\\") throw invalid();
|
|
857
|
+
fallback += next;
|
|
858
|
+
} else fallback += char;
|
|
859
|
+
}
|
|
860
|
+
throw invalid();
|
|
861
|
+
}
|
|
841
862
|
function walkValue(ctx, value, canonical, display, isTranslatable) {
|
|
863
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
864
|
+
if (!isTranslatable(canonical)) throw new I18nError("UNREGISTERED_I18N_FIELD", `\u672A\u6CE8\u518C\u7684\u53EF\u7FFB\u8BD1\u5B57\u6BB5: ${display}`, { path: display });
|
|
865
|
+
return convertDescriptor(ctx, parseShorthand(value, display), void 0, display);
|
|
866
|
+
}
|
|
842
867
|
if (Array.isArray(value)) {
|
|
843
868
|
for (let i = 0; i < value.length; i += 1) {
|
|
844
869
|
value[i] = walkValue(ctx, value[i], `${canonical}[*]`, `${display}/${i}`, isTranslatable);
|
|
@@ -846,6 +871,14 @@ function walkValue(ctx, value, canonical, display, isTranslatable) {
|
|
|
846
871
|
return value;
|
|
847
872
|
}
|
|
848
873
|
if (isPlainObject3(value)) {
|
|
874
|
+
if (typeof value["type"] === "string" && isPlainObject3(value["props"])) {
|
|
875
|
+
walkNode(ctx, value, display);
|
|
876
|
+
return value;
|
|
877
|
+
}
|
|
878
|
+
if (typeof value["action"] === "string") {
|
|
879
|
+
walkAction(ctx, value, display);
|
|
880
|
+
return value;
|
|
881
|
+
}
|
|
849
882
|
if ("$i18n" in value) {
|
|
850
883
|
if (!isTranslatable(canonical)) {
|
|
851
884
|
throw new I18nError(
|
|
@@ -934,55 +967,13 @@ function walkNode(ctx, node, display) {
|
|
|
934
967
|
walkNode(ctx, node["children"], `${display}/children`);
|
|
935
968
|
}
|
|
936
969
|
}
|
|
937
|
-
function assertBindingAllowed(segments, pointer) {
|
|
938
|
-
const root = segments[0];
|
|
939
|
-
if (root !== void 0 && FORBIDDEN_BINDING_ROOTS.has(root)) {
|
|
940
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u8BE5\u5B57\u6BB5: ${pointer}`, { pointer });
|
|
941
|
-
}
|
|
942
|
-
if (root === "page" && segments[1] === "id") {
|
|
943
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u9875\u9762\u6807\u8BC6: ${pointer}`, { pointer });
|
|
944
|
-
}
|
|
945
|
-
if (segments[segments.length - 1] === "permission") {
|
|
946
|
-
throw new I18nError("BINDING_INVALID", `bindings \u7981\u6B62\u7ED1\u5B9A\u6743\u9650\u5B57\u6BB5: ${pointer}`, { pointer });
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
function applyBindings(ctx, root, bindings) {
|
|
950
|
-
const seen = /* @__PURE__ */ new Map();
|
|
951
|
-
for (const [pointer, rawDesc] of Object.entries(bindings)) {
|
|
952
|
-
const segments = parseJsonPointer(pointer);
|
|
953
|
-
const canonical = segments.map(escapeJsonPointerSegment).join("/");
|
|
954
|
-
const previous = seen.get(canonical);
|
|
955
|
-
if (previous !== void 0) {
|
|
956
|
-
throw new I18nError("BINDING_INVALID", `bindings \u8DEF\u5F84\u91CD\u590D: ${previous} \u4E0E ${pointer}`, {
|
|
957
|
-
pointer,
|
|
958
|
-
previous
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
seen.set(canonical, pointer);
|
|
962
|
-
assertBindingAllowed(segments, pointer);
|
|
963
|
-
const desc = assertDescriptor(rawDesc, pointer);
|
|
964
|
-
const resolved = resolveJsonPointer(root, segments, pointer);
|
|
965
|
-
if (isPlainObject3(resolved.value) && "$i18n" in resolved.value) {
|
|
966
|
-
throw new I18nError(
|
|
967
|
-
"BINDING_CONFLICT",
|
|
968
|
-
`\u540C\u4E00\u5B57\u6BB5\u88AB $i18n \u4E0E bindings \u91CD\u590D\u58F0\u660E: ${pointer}`,
|
|
969
|
-
{ pointer }
|
|
970
|
-
);
|
|
971
|
-
}
|
|
972
|
-
if (typeof resolved.value !== "string") {
|
|
973
|
-
throw new I18nError("BINDING_INVALID", `bindings \u76EE\u6807\u5FC5\u987B\u662F\u5B57\u7B26\u4E32: ${pointer}`, {
|
|
974
|
-
pointer,
|
|
975
|
-
valueType: Array.isArray(resolved.value) ? "array" : typeof resolved.value
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
const converted = convertDescriptor(ctx, desc, resolved.value, pointer);
|
|
979
|
-
resolved.parent[resolved.key] = converted;
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
970
|
function dedupeLocales(locales) {
|
|
983
971
|
return [...new Set(locales)];
|
|
984
972
|
}
|
|
985
973
|
function assertNoResidualI18n(value, display) {
|
|
974
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
975
|
+
throw new I18nError("UNREGISTERED_I18N_FIELD", `\u56FD\u9645\u5316\u7B80\u5199\u51FA\u73B0\u5728\u4E0D\u53EF\u7FFB\u8BD1\u7684\u4F4D\u7F6E: ${display}`, { path: display });
|
|
976
|
+
}
|
|
986
977
|
if (Array.isArray(value)) {
|
|
987
978
|
value.forEach((item, index) => assertNoResidualI18n(item, `${display}/${index}`));
|
|
988
979
|
return;
|
|
@@ -1021,12 +1012,17 @@ function localizePageDsl(input) {
|
|
|
1021
1012
|
namedComponents: isPlainObject3(page.components) ? page.components : void 0,
|
|
1022
1013
|
onDiagnostic: input.onDiagnostic
|
|
1023
1014
|
};
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1015
|
+
if (isPlainObject3(page.i18n) && "bindings" in page.i18n) {
|
|
1016
|
+
throw new I18nError("I18N_DESCRIPTOR_INVALID", "i18n.bindings \u5DF2\u79FB\u9664\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BB5\u5185\u7B80\u5199");
|
|
1017
|
+
}
|
|
1018
|
+
delete page.i18n;
|
|
1019
|
+
if (isPlainObject3(page.page)) {
|
|
1020
|
+
for (const field of ["title", "description"]) {
|
|
1021
|
+
const value = page.page[field];
|
|
1022
|
+
if (typeof value === "string" && value.startsWith("$i18n{")) {
|
|
1023
|
+
page.page[field] = convertDescriptor(ctx, parseShorthand(value, `/page/${field}`), void 0, `/page/${field}`);
|
|
1024
|
+
}
|
|
1028
1025
|
}
|
|
1029
|
-
delete page.i18n;
|
|
1030
1026
|
}
|
|
1031
1027
|
if (page.body !== void 0) {
|
|
1032
1028
|
walkNode(ctx, page.body, "/body");
|
package/dist/node.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
resolveJsonPointer,
|
|
18
18
|
resolveRequestLocale,
|
|
19
19
|
validateBundleSetEnvelope
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-QU4YMNND.js";
|
|
21
21
|
|
|
22
22
|
// src/yaml-message-provider.ts
|
|
23
23
|
import { isMap, isPair, isScalar, isSeq, parseDocument } from "yaml";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pactor-app/i18n",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.58.0",
|
|
4
4
|
"description": "Pactor i18n: request locale resolution and message bundle providers (YAML / API)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pactor",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@pactor-app/
|
|
60
|
-
"@pactor-app/
|
|
59
|
+
"@pactor-app/schema": "1.58.0",
|
|
60
|
+
"@pactor-app/core": "1.58.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"yaml": "^2.9.0"
|