inlang-plugin-rich-icu 0.1.0 → 0.1.3

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 (3) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +36 -36
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -27,14 +27,14 @@ as a dependency. Add it to your `project.inlang/settings.json`:
27
27
  Catalogs are JSON maps of `key → ICU string`, one file per locale:
28
28
 
29
29
  ```json
30
- { "resumeCount": "{count, plural, one {Vous avez <strong># CV</strong>.} other {Vous avez <strong># CV</strong>.}}" }
30
+ { "messageCount": "{count, plural, one {Vous avez <strong># nouveau message</strong>.} other {Vous avez <strong># nouveaux messages</strong>.}}" }
31
31
  ```
32
32
 
33
33
  ## Render (Paraglide + React)
34
34
 
35
35
  ```tsx
36
36
  <ParaglideMessage
37
- message={m.resumeCount}
37
+ message={m.messageCount}
38
38
  inputs={{ count }}
39
39
  markup={{ strong: ({ children }) => <strong>{children}</strong> }}
40
40
  />
@@ -71,7 +71,7 @@ Other notes:
71
71
 
72
72
  - `@inlang/sdk` 2.10.2
73
73
  - `@formatjs/icu-messageformat-parser` ^2
74
- - Parité d'AST vérifiée contre `@inlang/plugin-icu1` 1.1.0 (corpus sans markup)
74
+ - AST parity verified against `@inlang/plugin-icu1` 1.1.0 (markup-free corpus)
75
75
 
76
76
  ## Development
77
77
 
package/dist/index.js CHANGED
@@ -4366,8 +4366,16 @@ function markupPartToTag(part) {
4366
4366
  }
4367
4367
  }
4368
4368
 
4369
+ // src/order-cases.ts
4370
+ function orderCaseKeys(keys, isExact2) {
4371
+ const exacts = keys.filter(isExact2);
4372
+ const cldr = keys.filter((k) => !isExact2(k) && k !== "other");
4373
+ const other = keys.includes("other") ? ["other"] : [];
4374
+ return [...exacts, ...cldr, ...other];
4375
+ }
4376
+
4369
4377
  // src/parse.ts
4370
- var SELF_CLOSE_MARK = "\uE000";
4378
+ var SELF_CLOSE_MARK = String.fromCodePoint(57344);
4371
4379
  var SELF_CLOSE_RE = /<([\w][\w-]*)\s*\/>/y;
4372
4380
  function normalizeSelfClosingTags(source) {
4373
4381
  let out = "";
@@ -4466,7 +4474,7 @@ function extendVariant(v, el, ctx, poundArg) {
4466
4474
  if (el.type === TYPE.plural || el.type === TYPE.select) {
4467
4475
  const sel = registerSelector(el, ctx);
4468
4476
  const out = [];
4469
- for (const caseKey of orderCases(el)) {
4477
+ for (const caseKey of orderCaseKeys(Object.keys(el.options), isExact)) {
4470
4478
  const childPound = el.type === TYPE.plural ? el.value : poundArg;
4471
4479
  const caseMatches = sel.matchesFor(caseKey);
4472
4480
  for (const cv of expand(el.options[caseKey].value, ctx, childPound)) {
@@ -4499,13 +4507,6 @@ function isSelfClosing(children) {
4499
4507
  return children.length === 1 && children[0].type === TYPE.literal && // biome-ignore lint/suspicious/noExplicitAny: FormatJS literal carries .value
4500
4508
  children[0].value === SELF_CLOSE_MARK;
4501
4509
  }
4502
- function orderCases(el) {
4503
- const keys = Object.keys(el.options);
4504
- const exacts = keys.filter(isExact);
4505
- const cldr = keys.filter((k) => !isExact(k) && k !== "other");
4506
- const other = keys.includes("other") ? ["other"] : [];
4507
- return [...exacts, ...cldr, ...other];
4508
- }
4509
4510
  function registerSelector(el, ctx) {
4510
4511
  const arg = el.value;
4511
4512
  if (el.type === TYPE.select) {
@@ -4586,7 +4587,7 @@ function mapSimple(el, ctx, poundArg) {
4586
4587
  ];
4587
4588
  }
4588
4589
  default:
4589
- throw new Error(`Type ICU non support\xE9 : ${el.type}`);
4590
+ throw new Error(`Unsupported ICU element type: ${el.type}`);
4590
4591
  }
4591
4592
  }
4592
4593
 
@@ -4596,7 +4597,7 @@ function bundleToMessageString(args) {
4596
4597
  if (variants.length === 0) return "";
4597
4598
  if (message.selectors.length === 0) return patternToString(variants[0].pattern, false);
4598
4599
  const icuSelectors = resolveIcuSelectors(message, bundle);
4599
- return reconstruct(icuSelectors, variants);
4600
+ return reconstruct(icuSelectors, variants, false);
4600
4601
  }
4601
4602
  function declMap(bundle) {
4602
4603
  return new Map(bundle.declarations.map((d) => [d.name, d]));
@@ -4609,11 +4610,11 @@ function resolveIcuSelectors(message, bundle) {
4609
4610
  function resolvePlural(pluralDecl, pluralKey, exactKey) {
4610
4611
  const arg = pluralDecl.value.arg.name;
4611
4612
  const ordinal = pluralDecl.value.annotation.options?.some(
4612
- // biome-ignore lint/suspicious/noExplicitAny: AST inlang dynamique
4613
+ // biome-ignore lint/suspicious/noExplicitAny: dynamic inlang AST
4613
4614
  (o) => o.name === "type" && o.value.value === "ordinal"
4614
4615
  );
4615
4616
  const offset = Number(
4616
- // biome-ignore lint/suspicious/noExplicitAny: AST inlang dynamique
4617
+ // biome-ignore lint/suspicious/noExplicitAny: dynamic inlang AST
4617
4618
  pluralDecl.value.annotation.options?.find((o) => o.name === "offset")?.value.value ?? 0
4618
4619
  );
4619
4620
  out.push({ arg, kind: ordinal ? "selectordinal" : "plural", offset, pluralKey, exactKey });
@@ -4661,16 +4662,9 @@ function caseFor(sel, matches) {
4661
4662
  const plural = matches.find((x) => x.key === sel.pluralKey);
4662
4663
  return plural?.type === "literal-match" ? plural.value : "other";
4663
4664
  }
4664
- function orderIcuCases(keys) {
4665
- const isEx = (k) => k.startsWith("=");
4666
- const exacts = keys.filter(isEx);
4667
- const cldr = keys.filter((k) => !isEx(k) && k !== "other");
4668
- const other = keys.includes("other") ? ["other"] : [];
4669
- return [...exacts, ...cldr, ...other];
4670
- }
4671
- function reconstruct(selectors, variants) {
4665
+ function reconstruct(selectors, variants, ancestorInPlural) {
4672
4666
  const [sel, ...rest] = selectors;
4673
- const inPlural = sel.kind !== "select";
4667
+ const inPlural = ancestorInPlural || sel.kind !== "select";
4674
4668
  const groups = /* @__PURE__ */ new Map();
4675
4669
  for (const v of variants) {
4676
4670
  const c = caseFor(sel, v.matches);
@@ -4683,9 +4677,9 @@ function reconstruct(selectors, variants) {
4683
4677
  }
4684
4678
  const offsetStr = sel.offset ? `offset:${sel.offset} ` : "";
4685
4679
  const head = `${sel.arg}, ${sel.kind}, ${offsetStr}`;
4686
- const body = orderIcuCases([...groups.keys()]).map((c) => {
4680
+ const body = orderCaseKeys([...groups.keys()], (k) => k.startsWith("=")).map((c) => {
4687
4681
  const grp = groups.get(c) ?? [];
4688
- const inner = rest.length === 0 ? patternToString(grp[0].pattern, inPlural) : reconstruct(rest, grp);
4682
+ const inner = rest.length === 0 ? patternToString(grp[0].pattern, inPlural) : reconstruct(rest, grp, inPlural);
4689
4683
  return `${c} {${inner}}`;
4690
4684
  }).join(" ");
4691
4685
  return `{${head}${body}}`;
@@ -4703,7 +4697,7 @@ function patternToString(pattern, inPlural) {
4703
4697
  case "markup-standalone":
4704
4698
  return markupPartToTag(p);
4705
4699
  default:
4706
- throw new Error(`part inconnue: ${p.type}`);
4700
+ throw new Error(`Unknown pattern part: ${p.type}`);
4707
4701
  }
4708
4702
  }).join("");
4709
4703
  }
@@ -4758,7 +4752,7 @@ var richIcuPlugin = {
4758
4752
  json = JSON.parse(decoder.decode(file.content));
4759
4753
  } catch (err) {
4760
4754
  console.warn(
4761
- `${LOG_PREFIX} JSON invalide pour la locale "${file.locale}", fichier ignor\xE9 : ${err.message}`
4755
+ `${LOG_PREFIX} invalid JSON for locale "${file.locale}", file skipped: ${err.message}`
4762
4756
  );
4763
4757
  continue;
4764
4758
  }
@@ -4766,7 +4760,7 @@ var richIcuPlugin = {
4766
4760
  if (key === "$schema") continue;
4767
4761
  if (typeof value !== "string") {
4768
4762
  console.warn(
4769
- `${LOG_PREFIX} valeur non-string ignor\xE9e pour "${key}" (locale "${file.locale}")`
4763
+ `${LOG_PREFIX} non-string value skipped for "${key}" (locale "${file.locale}")`
4770
4764
  );
4771
4765
  continue;
4772
4766
  }
@@ -4775,7 +4769,7 @@ var richIcuPlugin = {
4775
4769
  imported = messageToImport({ source: value, bundleId: key, locale: file.locale });
4776
4770
  } catch (err) {
4777
4771
  console.warn(
4778
- `${LOG_PREFIX} ICU invalide pour "${key}" (locale "${file.locale}"), message ignor\xE9 : ${err.message}`
4772
+ `${LOG_PREFIX} invalid ICU for "${key}" (locale "${file.locale}"), message skipped: ${err.message}`
4779
4773
  );
4780
4774
  continue;
4781
4775
  }
@@ -4806,7 +4800,7 @@ var richIcuPlugin = {
4806
4800
  const s = settings;
4807
4801
  const bundleById = new Map(bundles.map((b) => [b.id, b]));
4808
4802
  const variantsByMsg = /* @__PURE__ */ new Map();
4809
- const keyOf = (v) => v.messageId != null ? `id:${v.messageId}` : `bl:${v.messageBundleId}\0${v.messageLocale}`;
4803
+ const keyOf = (v) => v.messageId != null ? `id:${v.messageId}` : `bl:${v.messageBundleId} ${v.messageLocale}`;
4810
4804
  for (const v of variants) {
4811
4805
  const k = keyOf(v);
4812
4806
  const arr = variantsByMsg.get(k);
@@ -4817,20 +4811,26 @@ var richIcuPlugin = {
4817
4811
  for (const message of messages) {
4818
4812
  const bundle = bundleById.get(message.bundleId);
4819
4813
  if (!bundle) continue;
4820
- const vs = (message.id != null ? variantsByMsg.get(`id:${message.id}`) : void 0) ?? variantsByMsg.get(`bl:${message.bundleId}\0${message.locale}`) ?? [];
4814
+ const vs = (message.id != null ? variantsByMsg.get(`id:${message.id}`) : void 0) ?? variantsByMsg.get(`bl:${message.bundleId} ${message.locale}`) ?? [];
4821
4815
  if (vs.length === 0) continue;
4816
+ let serialized;
4817
+ try {
4818
+ serialized = bundleToMessageString({ bundle, message, variants: vs });
4819
+ } catch (err) {
4820
+ console.warn(
4821
+ `${LOG_PREFIX} failed to export "${message.bundleId}" (locale "${message.locale}"), message skipped: ${err.message}`
4822
+ );
4823
+ continue;
4824
+ }
4822
4825
  if (!byLocale[message.locale]) byLocale[message.locale] = {};
4823
- byLocale[message.locale][message.bundleId] = bundleToMessageString({
4824
- bundle,
4825
- message,
4826
- variants: vs
4827
- });
4826
+ byLocale[message.locale][message.bundleId] = serialized;
4828
4827
  }
4829
4828
  const pattern = pathPattern(s);
4829
+ const encoder = new TextEncoder();
4830
4830
  return Object.entries(byLocale).map(([locale, map]) => ({
4831
4831
  locale,
4832
4832
  name: pattern.replace("{locale}", locale).split("/").at(-1) ?? locale,
4833
- content: new TextEncoder().encode(`${JSON.stringify(map, null, " ")}
4833
+ content: encoder.encode(`${JSON.stringify(map, null, " ")}
4834
4834
  `)
4835
4835
  }));
4836
4836
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inlang-plugin-rich-icu",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "inlang storage plugin: inline ICU MessageFormat with rich-text markup tags preserved as first-class inlang markup parts.",
5
5
  "type": "module",
6
6
  "exports": {