@inlang/paraglide-js 2.25.4 → 2.27.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.
Files changed (124) hide show
  1. package/README.md +2 -1
  2. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js +2 -2
  3. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js.map +1 -1
  4. package/dist/cli/commands/init/command.js +19 -1
  5. package/dist/cli/commands/init/command.js.map +1 -1
  6. package/dist/cli/defaults.js +2 -2
  7. package/dist/cli/defaults.js.map +1 -1
  8. package/dist/cli/steps/add-inlang-check.d.ts +19 -0
  9. package/dist/cli/steps/add-inlang-check.js +60 -0
  10. package/dist/cli/steps/add-inlang-check.js.map +1 -0
  11. package/dist/cli/steps/add-inlang-check.test.d.ts +1 -0
  12. package/dist/cli/steps/add-inlang-check.test.js +88 -0
  13. package/dist/cli/steps/add-inlang-check.test.js.map +1 -0
  14. package/dist/cli/steps/agents-md-suggestion.d.ts +11 -0
  15. package/dist/cli/steps/agents-md-suggestion.js +19 -0
  16. package/dist/cli/steps/agents-md-suggestion.js.map +1 -0
  17. package/dist/cli/steps/agents-md-suggestion.test.d.ts +1 -0
  18. package/dist/cli/steps/agents-md-suggestion.test.js +36 -0
  19. package/dist/cli/steps/agents-md-suggestion.test.js.map +1 -0
  20. package/dist/cli/steps/maybe-add-machine-translation.js +3 -2
  21. package/dist/cli/steps/maybe-add-machine-translation.js.map +1 -1
  22. package/dist/cli/steps/maybe-add-sherlock.js +1 -1
  23. package/dist/cli/steps/maybe-add-sherlock.js.map +1 -1
  24. package/dist/cli/utils.d.ts +9 -0
  25. package/dist/cli/utils.js +15 -0
  26. package/dist/cli/utils.js.map +1 -1
  27. package/dist/compiler/compile-annotation.d.ts +48 -1
  28. package/dist/compiler/compile-annotation.js +238 -10
  29. package/dist/compiler/compile-annotation.js.map +1 -1
  30. package/dist/compiler/compile-bundle.d.ts +3 -2
  31. package/dist/compiler/compile-bundle.js +30 -1
  32. package/dist/compiler/compile-bundle.js.map +1 -1
  33. package/dist/compiler/compile-bundle.test.js +18 -18
  34. package/dist/compiler/compile-bundle.test.js.map +1 -1
  35. package/dist/compiler/compile-local-variable.d.ts +3 -0
  36. package/dist/compiler/compile-local-variable.js +5 -2
  37. package/dist/compiler/compile-local-variable.js.map +1 -1
  38. package/dist/compiler/compile-local-variable.test.js +44 -4
  39. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  40. package/dist/compiler/compile-message.d.ts +29 -1
  41. package/dist/compiler/compile-message.exact-number.test.d.ts +1 -0
  42. package/dist/compiler/compile-message.exact-number.test.js +786 -0
  43. package/dist/compiler/compile-message.exact-number.test.js.map +1 -0
  44. package/dist/compiler/compile-message.js +162 -66
  45. package/dist/compiler/compile-message.js.map +1 -1
  46. package/dist/compiler/compile-message.locals.test.d.ts +1 -0
  47. package/dist/compiler/compile-message.locals.test.js +498 -0
  48. package/dist/compiler/compile-message.locals.test.js.map +1 -0
  49. package/dist/compiler/compile-message.test.js +111 -20
  50. package/dist/compiler/compile-message.test.js.map +1 -1
  51. package/dist/compiler/compile-pattern.d.ts +5 -0
  52. package/dist/compiler/compile-pattern.js +6 -18
  53. package/dist/compiler/compile-pattern.js.map +1 -1
  54. package/dist/compiler/compile-pattern.test.js +165 -1
  55. package/dist/compiler/compile-pattern.test.js.map +1 -1
  56. package/dist/compiler/compile-project.js +4 -1
  57. package/dist/compiler/compile-project.js.map +1 -1
  58. package/dist/compiler/compile-project.test.js +1013 -31
  59. package/dist/compiler/compile-project.test.js.map +1 -1
  60. package/dist/compiler/compile.test.js +1 -1
  61. package/dist/compiler/compile.test.js.map +1 -1
  62. package/dist/compiler/create-readme.d.ts +4 -1
  63. package/dist/compiler/create-readme.js +33 -125
  64. package/dist/compiler/create-readme.js.map +1 -1
  65. package/dist/compiler/emit-ts-declarations.interop.test.js +2 -2
  66. package/dist/compiler/emit-ts-declarations.interop.test.js.map +1 -1
  67. package/dist/compiler/emit-ts-declarations.test.js +4 -4
  68. package/dist/compiler/emit-ts-declarations.test.js.map +1 -1
  69. package/dist/compiler/index.d.ts +1 -1
  70. package/dist/compiler/index.js +1 -1
  71. package/dist/compiler/index.js.map +1 -1
  72. package/dist/compiler/input-alias.d.ts +21 -0
  73. package/dist/compiler/input-alias.js +42 -0
  74. package/dist/compiler/input-alias.js.map +1 -0
  75. package/dist/compiler/input-alias.test.d.ts +1 -0
  76. package/dist/compiler/input-alias.test.js +58 -0
  77. package/dist/compiler/input-alias.test.js.map +1 -0
  78. package/dist/compiler/message-locals.d.ts +46 -0
  79. package/dist/compiler/message-locals.js +129 -0
  80. package/dist/compiler/message-locals.js.map +1 -0
  81. package/dist/compiler/output-structure/locale-modules.js +7 -1
  82. package/dist/compiler/output-structure/locale-modules.js.map +1 -1
  83. package/dist/compiler/output-structure/locale-modules.test.js +35 -0
  84. package/dist/compiler/output-structure/locale-modules.test.js.map +1 -1
  85. package/dist/compiler/output-structure/message-modules.js +5 -3
  86. package/dist/compiler/output-structure/message-modules.js.map +1 -1
  87. package/dist/compiler/output-structure/message-modules.test.js +35 -0
  88. package/dist/compiler/output-structure/message-modules.test.js.map +1 -1
  89. package/dist/compiler/registry.d.ts +12 -0
  90. package/dist/compiler/registry.js +97 -28
  91. package/dist/compiler/registry.js.map +1 -1
  92. package/dist/compiler/runtime/create-runtime.js +24 -1
  93. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  94. package/dist/compiler/runtime/extract-locale-from-url.js +11 -4
  95. package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
  96. package/dist/compiler/runtime/generate-static-localized-urls.js +5 -3
  97. package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
  98. package/dist/compiler/runtime/localize-url.js +25 -54
  99. package/dist/compiler/runtime/localize-url.js.map +1 -1
  100. package/dist/compiler/runtime/route-strategy.js +9 -3
  101. package/dist/compiler/runtime/route-strategy.js.map +1 -1
  102. package/dist/compiler/runtime/type.test.js +9 -0
  103. package/dist/compiler/runtime/type.test.js.map +1 -1
  104. package/dist/compiler/runtime/url-pattern-cache.d.ts +11 -0
  105. package/dist/compiler/runtime/url-pattern-cache.js +42 -0
  106. package/dist/compiler/runtime/url-pattern-cache.js.map +1 -0
  107. package/dist/compiler/runtime/url-pattern-index.d.ts +24 -0
  108. package/dist/compiler/runtime/url-pattern-index.differential.test.d.ts +1 -0
  109. package/dist/compiler/runtime/url-pattern-index.differential.test.js +152 -0
  110. package/dist/compiler/runtime/url-pattern-index.differential.test.js.map +1 -0
  111. package/dist/compiler/runtime/url-pattern-index.js +168 -0
  112. package/dist/compiler/runtime/url-pattern-index.js.map +1 -0
  113. package/dist/compiler/runtime/url-pattern-index.test.d.ts +1 -0
  114. package/dist/compiler/runtime/url-pattern-index.test.js +262 -0
  115. package/dist/compiler/runtime/url-pattern-index.test.js.map +1 -0
  116. package/dist/compiler/server/create-server-file.js +25 -1
  117. package/dist/compiler/server/create-server-file.js.map +1 -1
  118. package/dist/compiler/server/create-server-file.test.js +20 -2
  119. package/dist/compiler/server/create-server-file.test.js.map +1 -1
  120. package/dist/compiler/server/middleware.js +29 -4
  121. package/dist/compiler/server/middleware.js.map +1 -1
  122. package/dist/services/env-variables/index.js +1 -1
  123. package/dist/services/env-variables/index.js.map +1 -1
  124. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"maybe-add-machine-translation.js","sourceRoot":"","sources":["../../../src/cli/steps/maybe-add-machine-translation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,0BAA0B,GAenC,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,iCAAiC;IACjC,MAAM,qBAAqB,GAAG,MAAM,MAAM,CACzC,6CAA6C,EAC7C;QACC,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;KACb,CACD,CAAC;IAEF,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACJ,mDAAmD;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;QAE9D,2FAA2F;QAC3F,MAAM,iBAAiB,CAAC;YACvB,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAClC,iBAAiB;gBACjB,IAAI,OAAO,CAAC,aAAa,CAAC;oBAAE,OAAO,OAAO,CAAC;gBAC3C,OAAO;oBACN,GAAG,OAAO;oBACV,aAAa,EAAE,QAAQ;iBACvB,CAAC;YACH,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAC1B,iBAAiB;gBACjB,IAAI,OAAO,CAAC,mBAAmB,CAAC;oBAAE,OAAO,OAAO,CAAC;gBACjD,OAAO;oBACN,GAAG,OAAO;oBACV,mBAAmB,EAAE,sCAAsC,YAAY,EAAE;iBACzE,CAAC;YACH,CAAC;SACD,CAAC,CAAC,GAAG,CAAC,CAAC;QAER,GAAG,CAAC,MAAM,CAAC,OAAO,CACjB,gEAAgE,CAChE,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,MAAM,CAAC,KAAK,CACf;YACC,+DAA+D;YAC/D,uCAAuC;YACvC,mDAAmD;YACnD,KAAK;SACL,CAAC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC","sourcesContent":["import type { Logger } from \"../../services/logger/index.js\";\nimport type { CliStep } from \"../utils.js\";\nimport { prompt } from \"../utils.js\";\nimport { updatePackageJson } from \"./update-package-json.js\";\nimport path from \"node:path\";\n\nexport const maybeAddMachineTranslation: CliStep<\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tprojectPath: string;\n\t\troot: string;\n\t\tpackageJsonPath: string;\n\t},\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tprojectPath: string;\n\t\troot: string;\n\t\tpackageJsonPath: string;\n\t}\n> = async (ctx) => {\n\t// Prompt for machine translation\n\tconst useMachineTranslation = await prompt(\n\t\t`Do you want to set up machine translations?`,\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tinitial: true,\n\t\t}\n\t);\n\n\tif (!useMachineTranslation) {\n\t\treturn ctx;\n\t}\n\n\ttry {\n\t\t// Determine the relative path from root to project\n\t\tconst relativePath = path.relative(ctx.root, ctx.projectPath);\n\n\t\t// Use updatePackageJson to add @inlang/cli to devDependencies and machine-translate script\n\t\tawait updatePackageJson({\n\t\t\tdevDependencies: async (devDeps) => {\n\t\t\t\t// exists already\n\t\t\t\tif (devDeps[\"@inlang/cli\"]) return devDeps;\n\t\t\t\treturn {\n\t\t\t\t\t...devDeps,\n\t\t\t\t\t\"@inlang/cli\": \"^3.0.0\",\n\t\t\t\t};\n\t\t\t},\n\t\t\tscripts: async (scripts) => {\n\t\t\t\t// exists already\n\t\t\t\tif (scripts[\"machine-translate\"]) return scripts;\n\t\t\t\treturn {\n\t\t\t\t\t...scripts,\n\t\t\t\t\t\"machine-translate\": `inlang machine translate --project ${relativePath}`,\n\t\t\t\t};\n\t\t\t},\n\t\t})(ctx);\n\n\t\tctx.logger.success(\n\t\t\t\"Added @inlang/cli and machine-translate script to package.json\"\n\t\t);\n\t} catch (error) {\n\t\tctx.logger.error(\n\t\t\t[\n\t\t\t\t\"Failed to set up machine translation. Please add it manually:\",\n\t\t\t\t\"1. Add @inlang/cli to devDependencies\",\n\t\t\t\t\"2. Add a machine-translate script to package.json\",\n\t\t\t\terror,\n\t\t\t].join(\"\\n\")\n\t\t);\n\t}\n\n\treturn ctx;\n};\n"]}
1
+ {"version":3,"file":"maybe-add-machine-translation.js","sourceRoot":"","sources":["../../../src/cli/steps/maybe-add-machine-translation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,MAAM,CAAC,MAAM,0BAA0B,GAenC,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,iCAAiC;IACjC,MAAM,qBAAqB,GAAG,MAAM,MAAM,CACzC,6CAA6C,EAC7C;QACC,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;KACb,CACD,CAAC;IAEF,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACJ,mDAAmD;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;QAE9D,2FAA2F;QAC3F,MAAM,iBAAiB,CAAC;YACvB,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAClC,iBAAiB;gBACjB,IAAI,OAAO,CAAC,aAAa,CAAC;oBAAE,OAAO,OAAO,CAAC;gBAC3C,OAAO;oBACN,GAAG,OAAO;oBACV,aAAa,EAAE,wBAAwB;iBACvC,CAAC;YACH,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;gBAC1B,iBAAiB;gBACjB,IAAI,OAAO,CAAC,mBAAmB,CAAC;oBAAE,OAAO,OAAO,CAAC;gBACjD,OAAO;oBACN,GAAG,OAAO;oBACV,mBAAmB,EAAE,sCAAsC,YAAY,EAAE;iBACzE,CAAC;YACH,CAAC;SACD,CAAC,CAAC,GAAG,CAAC,CAAC;QAER,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,MAAM,CAAC,KAAK,CACf;YACC,+DAA+D;YAC/D,uCAAuC;YACvC,mDAAmD;YACnD,KAAK;SACL,CAAC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC","sourcesContent":["import type { Logger } from \"../../services/logger/index.js\";\nimport type { CliStep } from \"../utils.js\";\nimport { prompt } from \"../utils.js\";\nimport { updatePackageJson } from \"./update-package-json.js\";\nimport path from \"node:path\";\nimport { INLANG_CLI_VERSION_RANGE } from \"./add-inlang-check.js\";\n\nexport const maybeAddMachineTranslation: CliStep<\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tprojectPath: string;\n\t\troot: string;\n\t\tpackageJsonPath: string;\n\t},\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tprojectPath: string;\n\t\troot: string;\n\t\tpackageJsonPath: string;\n\t}\n> = async (ctx) => {\n\t// Prompt for machine translation\n\tconst useMachineTranslation = await prompt(\n\t\t`Do you want to set up machine translations?`,\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tinitial: true,\n\t\t}\n\t);\n\n\tif (!useMachineTranslation) {\n\t\treturn ctx;\n\t}\n\n\ttry {\n\t\t// Determine the relative path from root to project\n\t\tconst relativePath = path.relative(ctx.root, ctx.projectPath);\n\n\t\t// Use updatePackageJson to add @inlang/cli to devDependencies and machine-translate script\n\t\tawait updatePackageJson({\n\t\t\tdevDependencies: async (devDeps) => {\n\t\t\t\t// exists already\n\t\t\t\tif (devDeps[\"@inlang/cli\"]) return devDeps;\n\t\t\t\treturn {\n\t\t\t\t\t...devDeps,\n\t\t\t\t\t\"@inlang/cli\": INLANG_CLI_VERSION_RANGE,\n\t\t\t\t};\n\t\t\t},\n\t\t\tscripts: async (scripts) => {\n\t\t\t\t// exists already\n\t\t\t\tif (scripts[\"machine-translate\"]) return scripts;\n\t\t\t\treturn {\n\t\t\t\t\t...scripts,\n\t\t\t\t\t\"machine-translate\": `inlang machine translate --project ${relativePath}`,\n\t\t\t\t};\n\t\t\t},\n\t\t})(ctx);\n\n\t\tctx.logger.success(\"Added a machine-translate script to package.json.\");\n\t} catch (error) {\n\t\tctx.logger.error(\n\t\t\t[\n\t\t\t\t\"Failed to set up machine translation. Please add it manually:\",\n\t\t\t\t\"1. Add @inlang/cli to devDependencies\",\n\t\t\t\t\"2. Add a machine-translate script to package.json\",\n\t\t\t\terror,\n\t\t\t].join(\"\\n\")\n\t\t);\n\t}\n\n\treturn ctx;\n};\n"]}
@@ -22,7 +22,7 @@ export const maybeAddSherlock = async (ctx) => {
22
22
  if (settings.modules?.some((m) => m.includes("plugin-m-function-matcher")) ===
23
23
  false) {
24
24
  // add the m function matcher plugin
25
- settings.modules.push("https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.2.6/dist/index.js");
25
+ settings.modules.push("https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.3.0/dist/index.js");
26
26
  await ctx.project.settings.set(settings);
27
27
  }
28
28
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"maybe-add-sherlock.js","sourceRoot":"","sources":["../../../src/cli/steps/maybe-add-sherlock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,KAAK,QAAQ,MAAM,4BAA4B,CAAC;AAEvD,MAAM,CAAC,MAAM,gBAAgB,GAOzB,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,iBAAiB,GAAG,OAAO,EAAE,GAAG,EAAE,YAAY,KAAK,QAAQ,CAAC;IAClE,IAAI,aAAa,GAAG,iBAAiB,CAAC;IAEtC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACxB,aAAa,GAAG,MAAM,MAAM,CAC3B,sEAAsE,EACtE;YACC,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;SACb,CACD,CAAC;IACH,CAAC;IACD,IAAI,aAAa,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC;IAExC,MAAM,iBAAiB,GAAG,MAAM,MAAM,CACrC,+EAA+E,EAC/E;QACC,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;KACb,CACD,CAAC;IACF,IAAI,iBAAiB,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC;IAE5C,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAElD,sCAAsC;IACtC,IACC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC;QACtE,KAAK,EACJ,CAAC;QACF,oCAAoC;QACpC,QAAQ,CAAC,OAAO,CAAC,IAAI,CACpB,oFAAoF,CACpF,CAAC;QACF,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC;QACJ,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,GAAG,CAAC,MAAM,CAAC,OAAO,CACjB,uEAAuE,CACvE,CAAC;QACH,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,MAAM,CAAC,KAAK,CACf;YACC,mEAAmE;YACnE,KAAK;SACL,CAAC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC","sourcesContent":["import type { Logger } from \"../../services/logger/index.js\";\nimport { prompt } from \"../utils.js\";\nimport type { CliStep } from \"../utils.js\";\nimport type { InlangProject } from \"@inlang/sdk\";\nimport * as Sherlock from \"@inlang/recommend-sherlock\";\n\nexport const maybeAddSherlock: CliStep<\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tproject: InlangProject;\n\t},\n\tunknown\n> = async (ctx) => {\n\tconst isCertainlyVsCode = process?.env?.TERM_PROGRAM === \"vscode\";\n\tlet isUsingVsCode = isCertainlyVsCode;\n\n\tif (!isCertainlyVsCode) {\n\t\tisUsingVsCode = await prompt(\n\t\t\t`Are you using Visual Studio Code or a compatible fork (like Cursor)?`,\n\t\t\t{\n\t\t\t\ttype: \"confirm\",\n\t\t\t\tinitial: true,\n\t\t\t}\n\t\t);\n\t}\n\tif (isUsingVsCode === false) return ctx;\n\n\tconst shouldAddSherlock = await prompt(\n\t\t`Do you want to use the Sherlock VSCode (or Cursor) extension for a better DX?`,\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tinitial: true,\n\t\t}\n\t);\n\tif (shouldAddSherlock === false) return ctx;\n\n\tconst settings = await ctx.project.settings.get();\n\n\t// m function matcher is not installed\n\tif (\n\t\tsettings.modules?.some((m) => m.includes(\"plugin-m-function-matcher\")) ===\n\t\tfalse\n\t) {\n\t\t// add the m function matcher plugin\n\t\tsettings.modules.push(\n\t\t\t\"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.2.6/dist/index.js\"\n\t\t);\n\t\tawait ctx.project.settings.set(settings);\n\t}\n\n\ttry {\n\t\tif (!(await Sherlock.isAdopted({ fs: ctx.fs }))) {\n\t\t\tawait Sherlock.add({ fs: ctx.fs });\n\t\t\tctx.logger.success(\n\t\t\t\t\"Added the Sherlock VSCode extension to the workspace recommendations.\"\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\tctx.logger.error(\n\t\t\t[\n\t\t\t\t\"Failed to add the Sherlock VSCode extension. Please open an issue\",\n\t\t\t\terror,\n\t\t\t].join(\"\\n\")\n\t\t);\n\t}\n\n\treturn ctx;\n};\n"]}
1
+ {"version":3,"file":"maybe-add-sherlock.js","sourceRoot":"","sources":["../../../src/cli/steps/maybe-add-sherlock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,KAAK,QAAQ,MAAM,4BAA4B,CAAC;AAEvD,MAAM,CAAC,MAAM,gBAAgB,GAOzB,KAAK,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,iBAAiB,GAAG,OAAO,EAAE,GAAG,EAAE,YAAY,KAAK,QAAQ,CAAC;IAClE,IAAI,aAAa,GAAG,iBAAiB,CAAC;IAEtC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACxB,aAAa,GAAG,MAAM,MAAM,CAC3B,sEAAsE,EACtE;YACC,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;SACb,CACD,CAAC;IACH,CAAC;IACD,IAAI,aAAa,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC;IAExC,MAAM,iBAAiB,GAAG,MAAM,MAAM,CACrC,+EAA+E,EAC/E;QACC,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;KACb,CACD,CAAC;IACF,IAAI,iBAAiB,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC;IAE5C,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IAElD,sCAAsC;IACtC,IACC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC;QACtE,KAAK,EACJ,CAAC;QACF,oCAAoC;QACpC,QAAQ,CAAC,OAAO,CAAC,IAAI,CACpB,oFAAoF,CACpF,CAAC;QACF,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC;QACJ,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,GAAG,CAAC,MAAM,CAAC,OAAO,CACjB,uEAAuE,CACvE,CAAC;QACH,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,MAAM,CAAC,KAAK,CACf;YACC,mEAAmE;YACnE,KAAK;SACL,CAAC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACZ,CAAC,CAAC","sourcesContent":["import type { Logger } from \"../../services/logger/index.js\";\nimport { prompt } from \"../utils.js\";\nimport type { CliStep } from \"../utils.js\";\nimport type { InlangProject } from \"@inlang/sdk\";\nimport * as Sherlock from \"@inlang/recommend-sherlock\";\n\nexport const maybeAddSherlock: CliStep<\n\t{\n\t\tfs: typeof import(\"node:fs/promises\");\n\t\tlogger: Logger;\n\t\tproject: InlangProject;\n\t},\n\tunknown\n> = async (ctx) => {\n\tconst isCertainlyVsCode = process?.env?.TERM_PROGRAM === \"vscode\";\n\tlet isUsingVsCode = isCertainlyVsCode;\n\n\tif (!isCertainlyVsCode) {\n\t\tisUsingVsCode = await prompt(\n\t\t\t`Are you using Visual Studio Code or a compatible fork (like Cursor)?`,\n\t\t\t{\n\t\t\t\ttype: \"confirm\",\n\t\t\t\tinitial: true,\n\t\t\t}\n\t\t);\n\t}\n\tif (isUsingVsCode === false) return ctx;\n\n\tconst shouldAddSherlock = await prompt(\n\t\t`Do you want to use the Sherlock VSCode (or Cursor) extension for a better DX?`,\n\t\t{\n\t\t\ttype: \"confirm\",\n\t\t\tinitial: true,\n\t\t}\n\t);\n\tif (shouldAddSherlock === false) return ctx;\n\n\tconst settings = await ctx.project.settings.get();\n\n\t// m function matcher is not installed\n\tif (\n\t\tsettings.modules?.some((m) => m.includes(\"plugin-m-function-matcher\")) ===\n\t\tfalse\n\t) {\n\t\t// add the m function matcher plugin\n\t\tsettings.modules.push(\n\t\t\t\"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2.3.0/dist/index.js\"\n\t\t);\n\t\tawait ctx.project.settings.set(settings);\n\t}\n\n\ttry {\n\t\tif (!(await Sherlock.isAdopted({ fs: ctx.fs }))) {\n\t\t\tawait Sherlock.add({ fs: ctx.fs });\n\t\t\tctx.logger.success(\n\t\t\t\t\"Added the Sherlock VSCode extension to the workspace recommendations.\"\n\t\t\t);\n\t\t}\n\t} catch (error) {\n\t\tctx.logger.error(\n\t\t\t[\n\t\t\t\t\"Failed to add the Sherlock VSCode extension. Please open an issue\",\n\t\t\t\terror,\n\t\t\t].join(\"\\n\")\n\t\t);\n\t}\n\n\treturn ctx;\n};\n"]}
@@ -34,3 +34,12 @@ export declare const promptSelection: <T extends string>(message: string, option
34
34
  value: T;
35
35
  }[];
36
36
  }) => Promise<T>;
37
+ /**
38
+ * A path relative to `root` with forward slashes and without a leading `./`,
39
+ * for messages and package.json scripts.
40
+ *
41
+ * @example
42
+ * toRelativeDisplayPath("/app", "./project.inlang") // "project.inlang"
43
+ * toRelativeDisplayPath("/app", "/app/src/paraglide") // "src/paraglide"
44
+ */
45
+ export declare function toRelativeDisplayPath(root: string, path: string): string;
package/dist/cli/utils.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import consola from "consola";
2
+ import nodePath from "node:path";
2
3
  /**
3
4
  * Wrapper to exit the process if the user presses CTRL+C.
4
5
  */
@@ -17,4 +18,18 @@ export const promptSelection = async (message, options = {
17
18
  ...options,
18
19
  });
19
20
  };
21
+ /**
22
+ * A path relative to `root` with forward slashes and without a leading `./`,
23
+ * for messages and package.json scripts.
24
+ *
25
+ * @example
26
+ * toRelativeDisplayPath("/app", "./project.inlang") // "project.inlang"
27
+ * toRelativeDisplayPath("/app", "/app/src/paraglide") // "src/paraglide"
28
+ */
29
+ export function toRelativeDisplayPath(root, path) {
30
+ return nodePath
31
+ .relative(root, nodePath.resolve(root, path))
32
+ .split(nodePath.sep)
33
+ .join("/");
34
+ }
20
35
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/cli/utils.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AA6B9B;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAA0B,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;IACvE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACxD,IAAI,QAAQ,EAAE,QAAQ,EAAE,KAAK,sBAAsB,EAAE,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EACnC,OAAe,EACf,OAAO,GAA4D;IAClE,OAAO,EAAE,EAAE;CACX,EACY,EAAE;IACf,OAAO,MAAM,CAAC,OAAO,EAAE;QACtB,IAAI,EAAE,QAAQ;QACd,GAAG,OAAO;KACV,CAA0B,CAAC;AAC7B,CAAC,CAAC","sourcesContent":["import consola from \"consola\";\n\n/**\n * One step in a CLI chain.\n * Defines which types the context needs to extend and how it extends the context\n *\n * Cli Steps can be chained to slowly build up the context over time\n *\n * @example\n * ```ts\n * const step1: CliStep<unknown, { foo: string }> = async (ctx) => {\n * return {...ctx, foo: \"hello\" }\n * }\n *\n * const step2: CliStep<{ foo: string }, { bar: number }> = async (ctx) => {\n * return { ...ctx, bar: 42 }\n * }\n *\n * const initial = { baz: \"baz\" } as const;\n * const ctx1 = await step1(initial);\n * const ctx2 = await step2(ctx1);\n *\n * ctx2 // Has type { foo: string, bar: number, baz: \"baz\" }\n * ```\n */\nexport type CliStep<In extends object, Out> = <Ctx extends In>(\n\tctx: Ctx\n) => Promise<Ctx & Out>;\n\n/**\n * Wrapper to exit the process if the user presses CTRL+C.\n */\nexport const prompt: typeof consola.prompt = async (message, options) => {\n\tconst response = await consola.prompt(message, options);\n\tif (response?.toString() === \"Symbol(clack:cancel)\") {\n\t\tprocess.exit(0);\n\t}\n\treturn response;\n};\n\nexport const promptSelection = async <T extends string>(\n\tmessage: string,\n\toptions: { initial?: T; options: { label: string; value: T }[] } = {\n\t\toptions: [],\n\t}\n): Promise<T> => {\n\treturn prompt(message, {\n\t\ttype: \"select\",\n\t\t...options,\n\t}) as unknown as Promise<T>;\n};\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/cli/utils.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,QAAQ,MAAM,WAAW,CAAC;AA6BjC;;GAEG;AACH,MAAM,CAAC,MAAM,MAAM,GAA0B,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;IACvE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACxD,IAAI,QAAQ,EAAE,QAAQ,EAAE,KAAK,sBAAsB,EAAE,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EACnC,OAAe,EACf,OAAO,GAA4D;IAClE,OAAO,EAAE,EAAE;CACX,EACY,EAAE;IACf,OAAO,MAAM,CAAC,OAAO,EAAE;QACtB,IAAI,EAAE,QAAQ;QACd,GAAG,OAAO;KACV,CAA0B,CAAC;AAC7B,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,IAAY;IAC/D,OAAO,QAAQ;SACb,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;SAC5C,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;SACnB,IAAI,CAAC,GAAG,CAAC,CAAC;AACb,CAAC","sourcesContent":["import consola from \"consola\";\nimport nodePath from \"node:path\";\n\n/**\n * One step in a CLI chain.\n * Defines which types the context needs to extend and how it extends the context\n *\n * Cli Steps can be chained to slowly build up the context over time\n *\n * @example\n * ```ts\n * const step1: CliStep<unknown, { foo: string }> = async (ctx) => {\n * return {...ctx, foo: \"hello\" }\n * }\n *\n * const step2: CliStep<{ foo: string }, { bar: number }> = async (ctx) => {\n * return { ...ctx, bar: 42 }\n * }\n *\n * const initial = { baz: \"baz\" } as const;\n * const ctx1 = await step1(initial);\n * const ctx2 = await step2(ctx1);\n *\n * ctx2 // Has type { foo: string, bar: number, baz: \"baz\" }\n * ```\n */\nexport type CliStep<In extends object, Out> = <Ctx extends In>(\n\tctx: Ctx\n) => Promise<Ctx & Out>;\n\n/**\n * Wrapper to exit the process if the user presses CTRL+C.\n */\nexport const prompt: typeof consola.prompt = async (message, options) => {\n\tconst response = await consola.prompt(message, options);\n\tif (response?.toString() === \"Symbol(clack:cancel)\") {\n\t\tprocess.exit(0);\n\t}\n\treturn response;\n};\n\nexport const promptSelection = async <T extends string>(\n\tmessage: string,\n\toptions: { initial?: T; options: { label: string; value: T }[] } = {\n\t\toptions: [],\n\t}\n): Promise<T> => {\n\treturn prompt(message, {\n\t\ttype: \"select\",\n\t\t...options,\n\t}) as unknown as Promise<T>;\n};\n\n/**\n * A path relative to `root` with forward slashes and without a leading `./`,\n * for messages and package.json scripts.\n *\n * @example\n * toRelativeDisplayPath(\"/app\", \"./project.inlang\") // \"project.inlang\"\n * toRelativeDisplayPath(\"/app\", \"/app/src/paraglide\") // \"src/paraglide\"\n */\nexport function toRelativeDisplayPath(root: string, path: string): string {\n\treturn nodePath\n\t\t.relative(root, nodePath.resolve(root, path))\n\t\t.split(nodePath.sep)\n\t\t.join(\"/\");\n}\n"]}
@@ -1,6 +1,19 @@
1
1
  import type { Declaration, FunctionReference } from "@inlang/sdk";
2
2
  export declare function isRegistryFunction(name: string): boolean;
3
3
  export declare function registryFunctionNamesForDisplay(): string;
4
+ /**
5
+ * Returns the value unformatted, and warns once per formatter name.
6
+ *
7
+ * Unknown annotations, on pattern expressions and on local variables, fall
8
+ * back to plain interpolation to avoid breaking compilation of messages
9
+ * imported from other i18n libraries (e.g. i18next's `{{value, customFormat}}`).
10
+ */
11
+ export declare function ignoreUnknownFormatter(name: string, value: string): string;
12
+ /**
13
+ * The names of the registry functions a message calls, collected while it is
14
+ * compiled. The output decides from it whether to import the registry.
15
+ */
16
+ export type RegistryUsage = Set<string>;
4
17
  /**
5
18
  * Wraps a compiled expression value in a `registry.*` call if an
6
19
  * annotation is present.
@@ -9,4 +22,38 @@ export declare function registryFunctionNamesForDisplay(): string;
9
22
  * compileAnnotation("i?.count", "en", { type: "function-reference", name: "number", options: [] })
10
23
  * >> 'registry.number("en", i?.count, {})'
11
24
  */
12
- export declare function compileAnnotation(str: string, locale: string, annotation?: FunctionReference, declarations?: Declaration[]): string;
25
+ export declare function compileAnnotation(str: string, locale: string, annotation?: FunctionReference, declarations?: Declaration[], registryUsage?: RegistryUsage): string;
26
+ /**
27
+ * Translates the ICU MessageFormat 1 `style` of an annotation to the options
28
+ * of the registry function.
29
+ *
30
+ * The ICU1 plugin imports `{n, number, integer}` as `{$n :number style=integer}`
31
+ * and `{d, date, short}` as `{$d :date style=short}`. Intl knows the number
32
+ * styles "decimal", "percent", "currency" (with a `currency`) and "unit", and
33
+ * throws for others. The date and time styles are Intl's `dateStyle` and
34
+ * `timeStyle`. A style without an Intl equivalent, like an ICU skeleton
35
+ * (`::currency/EUR`), is left out with a warning.
36
+ *
37
+ * @example
38
+ * icuStyleAnnotation({ name: "date", options: [{ name: "style", value: "short" }] })
39
+ * >> { name: "date", options: [{ name: "dateStyle", value: "short" }] }
40
+ */
41
+ export declare function icuStyleAnnotation(annotation: FunctionReference): FunctionReference;
42
+ /**
43
+ * Adds the default styles of `date`, `time` and `datetime` when the options
44
+ * pick no fields. Options like `timeZone` or `hour12` are kept.
45
+ *
46
+ * @example
47
+ * withDefaultDateTimeStyles({ name: "datetime", options: [] })
48
+ * >> { name: "datetime", options: [{ name: "dateStyle", value: "medium" }, { name: "timeStyle", value: "short" }] }
49
+ */
50
+ export declare function withDefaultDateTimeStyles(annotation: FunctionReference): FunctionReference;
51
+ /**
52
+ * Returns the names of the variables the compiled options of an annotation
53
+ * read.
54
+ *
55
+ * @example
56
+ * // {$date :relativetime unit=$unit}
57
+ * annotationVariableReferences(annotation) // ["unit"]
58
+ */
59
+ export declare function annotationVariableReferences(annotation: FunctionReference): string[];
@@ -1,21 +1,55 @@
1
1
  import { compileVariableAccess } from "./variable-access.js";
2
2
  import { escapeForDoubleQuoteString } from "../services/codegen/escape.js";
3
+ import { Logger } from "../services/logger/index.js";
3
4
  /**
4
- * The functions shipped in the generated registry.js file.
5
+ * The functions shipped in the generated registry.js file, by the annotation
6
+ * name that calls them.
5
7
  *
6
8
  * @see createRegistry()
7
9
  */
8
- const registryFunctionNames = new Set([
9
- "plural",
10
- "number",
11
- "datetime",
12
- "relativetime",
10
+ const registryFunctionByAnnotation = new Map([
11
+ ["plural", "plural"],
12
+ ["number", "number"],
13
+ ["datetime", "datetime"],
14
+ ["relativetime", "relativetime"],
15
+ // `#` in ICU MessageFormat 1 plurals, as imported by the ICU1 plugin
16
+ ["icu:pound", "icuPound"],
17
+ // `{d, date, short}` and `{d, time, short}` in ICU MessageFormat 1, as
18
+ // imported by the ICU1 plugin, see icuStyleAnnotation()
19
+ ["date", "datetime"],
20
+ ["time", "datetime"],
13
21
  ]);
22
+ /**
23
+ * The formatter names listed in warnings: the annotation names that call a
24
+ * registry function.
25
+ */
26
+ const displayedRegistryFunctions = Array.from(registryFunctionByAnnotation.keys());
14
27
  export function isRegistryFunction(name) {
15
- return registryFunctionNames.has(name);
28
+ return registryFunctionByAnnotation.has(name);
16
29
  }
17
30
  export function registryFunctionNamesForDisplay() {
18
- return Array.from(registryFunctionNames).join(", ");
31
+ return displayedRegistryFunctions.join(", ");
32
+ }
33
+ const logger = new Logger();
34
+ /**
35
+ * Tracks annotation names that have already been warned about to avoid
36
+ * spamming the console when the same unsupported formatter is used in
37
+ * many messages (or across watch-mode recompiles).
38
+ */
39
+ const warnedUnsupportedAnnotations = new Set();
40
+ /**
41
+ * Returns the value unformatted, and warns once per formatter name.
42
+ *
43
+ * Unknown annotations, on pattern expressions and on local variables, fall
44
+ * back to plain interpolation to avoid breaking compilation of messages
45
+ * imported from other i18n libraries (e.g. i18next's `{{value, customFormat}}`).
46
+ */
47
+ export function ignoreUnknownFormatter(name, value) {
48
+ if (!warnedUnsupportedAnnotations.has(name)) {
49
+ warnedUnsupportedAnnotations.add(name);
50
+ logger.warn(`The formatter "${name}" is unknown and will be ignored. The value is interpolated without formatting. Supported formatters: ${registryFunctionNamesForDisplay()}.`);
51
+ }
52
+ return value;
19
53
  }
20
54
  /**
21
55
  * Wraps a compiled expression value in a `registry.*` call if an
@@ -25,14 +59,186 @@ export function registryFunctionNamesForDisplay() {
25
59
  * compileAnnotation("i?.count", "en", { type: "function-reference", name: "number", options: [] })
26
60
  * >> 'registry.number("en", i?.count, {})'
27
61
  */
28
- export function compileAnnotation(str, locale, annotation, declarations) {
62
+ export function compileAnnotation(str, locale, annotation, declarations, registryUsage) {
29
63
  if (!annotation) {
30
64
  return str;
31
65
  }
32
66
  if (annotation.name === "relativetime") {
33
67
  validateRelativeTimeOptions(annotation);
34
68
  }
35
- return `registry.${annotation.name}("${locale}", ${str}, ${compileOptions(annotation.name, annotation.options, declarations)})`;
69
+ if (offsetAnnotations.has(annotation.name)) {
70
+ validateOffsetOptions(annotation);
71
+ }
72
+ const functionName = registryFunctionByAnnotation.get(annotation.name) ?? annotation.name;
73
+ registryUsage?.add(functionName);
74
+ const options = withDefaultDateTimeStyles(icuStyleAnnotation(annotation)).options;
75
+ // the options of `date` and `time` are those of `datetime`
76
+ const optionsOf = functionName === "datetime" ? "datetime" : annotation.name;
77
+ return `registry.${functionName}("${locale}", ${str}, ${compileOptions(optionsOf, options, declarations)})`;
78
+ }
79
+ const icuDateTimeStyles = new Set(["short", "medium", "long", "full"]);
80
+ const intlNumberStyles = new Set(["decimal", "percent", "currency", "unit"]);
81
+ /**
82
+ * Translates the ICU MessageFormat 1 `style` of an annotation to the options
83
+ * of the registry function.
84
+ *
85
+ * The ICU1 plugin imports `{n, number, integer}` as `{$n :number style=integer}`
86
+ * and `{d, date, short}` as `{$d :date style=short}`. Intl knows the number
87
+ * styles "decimal", "percent", "currency" (with a `currency`) and "unit", and
88
+ * throws for others. The date and time styles are Intl's `dateStyle` and
89
+ * `timeStyle`. A style without an Intl equivalent, like an ICU skeleton
90
+ * (`::currency/EUR`), is left out with a warning.
91
+ *
92
+ * @example
93
+ * icuStyleAnnotation({ name: "date", options: [{ name: "style", value: "short" }] })
94
+ * >> { name: "date", options: [{ name: "dateStyle", value: "short" }] }
95
+ */
96
+ export function icuStyleAnnotation(annotation) {
97
+ const style = annotation.options.find((option) => option.name === "style");
98
+ const others = annotation.options.filter((option) => option !== style);
99
+ const literal = (name, value) => ({
100
+ name,
101
+ value: { type: "literal", value },
102
+ });
103
+ if (annotation.name === "date" || annotation.name === "time") {
104
+ const optionName = annotation.name === "date" ? "dateStyle" : "timeStyle";
105
+ if (style?.value.type === "literal") {
106
+ if (icuDateTimeStyles.has(style.value.value)) {
107
+ return {
108
+ ...annotation,
109
+ options: [literal(optionName, style.value.value), ...others],
110
+ };
111
+ }
112
+ ignoreUnknownStyle(annotation.name, style.value.value);
113
+ }
114
+ else if (style) {
115
+ return {
116
+ ...annotation,
117
+ options: [{ name: optionName, value: style.value }, ...others],
118
+ };
119
+ }
120
+ // without a style, withDefaultDateTimeStyles() applies the default
121
+ return { ...annotation, options: others };
122
+ }
123
+ if (annotation.name === "number" && style?.value.type === "literal") {
124
+ const value = style.value.value;
125
+ if (value === "integer") {
126
+ const hasFractionDigits = others.some((option) => option.name === "maximumFractionDigits");
127
+ return {
128
+ ...annotation,
129
+ options: hasFractionDigits
130
+ ? others
131
+ : [literal("maximumFractionDigits", "0"), ...others],
132
+ };
133
+ }
134
+ const hasCurrency = others.some((option) => option.name === "currency");
135
+ if (!intlNumberStyles.has(value) ||
136
+ (value === "currency" && !hasCurrency)) {
137
+ ignoreUnknownStyle(annotation.name, value);
138
+ return { ...annotation, options: others };
139
+ }
140
+ }
141
+ return annotation;
142
+ }
143
+ /**
144
+ * The `Intl.DateTimeFormat` options that pick which parts of a date are shown.
145
+ * Without any of them, Intl shows only the numeric date, e.g. "10/9/2026".
146
+ * `dateStyle` and `timeStyle` cannot be combined with the others: Intl throws.
147
+ */
148
+ const dateTimeFieldOptions = new Set([
149
+ "dateStyle",
150
+ "timeStyle",
151
+ "weekday",
152
+ "era",
153
+ "year",
154
+ "month",
155
+ "day",
156
+ "dayPeriod",
157
+ "hour",
158
+ "minute",
159
+ "second",
160
+ "fractionalSecondDigits",
161
+ "timeZoneName",
162
+ ]);
163
+ /**
164
+ * The styles a date formatter uses when its options pick no fields.
165
+ *
166
+ * - `date`: MessageFormat 2 (LDML 47 `:date` `style=medium`) and ICU
167
+ * MessageFormat 1 (`{d, date}`) both default to "medium", e.g. "Oct 9, 2026".
168
+ * - `time`: ICU MessageFormat 1 `{d, time}` defaults to "medium", e.g.
169
+ * "4:30:00 PM", while MessageFormat 2 (LDML 47 `:time`) defaults to "short".
170
+ * `{$d :time}` is what the ICU1 plugin imports `{d, time}` as, so it keeps
171
+ * ICU's default. Write `{$d :time style=short}` for MessageFormat 2's.
172
+ * - `datetime`: MessageFormat 2 (LDML 47 `:datetime`) defaults to
173
+ * `dateStyle=medium timeStyle=short`, e.g. "Oct 9, 2026, 4:30 PM".
174
+ */
175
+ const defaultDateTimeStyles = new Map([
176
+ ["date", [["dateStyle", "medium"]]],
177
+ ["time", [["timeStyle", "medium"]]],
178
+ [
179
+ "datetime",
180
+ [
181
+ ["dateStyle", "medium"],
182
+ ["timeStyle", "short"],
183
+ ],
184
+ ],
185
+ ]);
186
+ /**
187
+ * Adds the default styles of `date`, `time` and `datetime` when the options
188
+ * pick no fields. Options like `timeZone` or `hour12` are kept.
189
+ *
190
+ * @example
191
+ * withDefaultDateTimeStyles({ name: "datetime", options: [] })
192
+ * >> { name: "datetime", options: [{ name: "dateStyle", value: "medium" }, { name: "timeStyle", value: "short" }] }
193
+ */
194
+ export function withDefaultDateTimeStyles(annotation) {
195
+ const defaults = defaultDateTimeStyles.get(annotation.name);
196
+ if (!defaults ||
197
+ annotation.options.some((option) => dateTimeFieldOptions.has(option.name))) {
198
+ return annotation;
199
+ }
200
+ return {
201
+ ...annotation,
202
+ options: [
203
+ ...defaults.map(([name, value]) => ({
204
+ name,
205
+ value: { type: "literal", value },
206
+ })),
207
+ ...annotation.options,
208
+ ],
209
+ };
210
+ }
211
+ const warnedUnknownStyles = new Set();
212
+ function ignoreUnknownStyle(name, style) {
213
+ const key = `${name} ${style}`;
214
+ if (warnedUnknownStyles.has(key))
215
+ return;
216
+ warnedUnknownStyles.add(key);
217
+ logger.warn(`The style "${style}" of the formatter "${name}" is not supported and will be ignored.${name === "number" && style === "currency"
218
+ ? ' The "currency" style needs a "currency" option.'
219
+ : ""}`);
220
+ }
221
+ /**
222
+ * Returns the names of the variables the compiled options of an annotation
223
+ * read.
224
+ *
225
+ * @example
226
+ * // {$date :relativetime unit=$unit}
227
+ * annotationVariableReferences(annotation) // ["unit"]
228
+ */
229
+ export function annotationVariableReferences(annotation) {
230
+ const names = [];
231
+ for (const option of annotation.options) {
232
+ if (option.value.type === "variable-reference") {
233
+ names.push(option.value.name);
234
+ }
235
+ else if (annotation.name === "relativetime" &&
236
+ option.name === "unit" &&
237
+ isDollarVariableReference(option.value.value)) {
238
+ names.push(option.value.value.slice(1));
239
+ }
240
+ }
241
+ return names;
36
242
  }
37
243
  function compileOptions(annotationName, options, declarations) {
38
244
  if (options.length === 0) {
@@ -83,6 +289,10 @@ function compileOptionLiteralOrVarRef(annotationName, optionName, value, declara
83
289
  if (shouldEmitBooleanLiteral(annotationName, optionName, value.value)) {
84
290
  return value.value;
85
291
  }
292
+ if (offsetAnnotations.has(annotationName) && optionName === "offset") {
293
+ // validated by validateOffsetOptions()
294
+ return String(Number(value.value));
295
+ }
86
296
  return `"${escapeForDoubleQuoteString(value.value)}"`;
87
297
  }
88
298
  function shouldEmitNumberLiteral(annotationName, optionName, literal) {
@@ -130,6 +340,24 @@ function validateRelativeTimeOptions(annotation) {
130
340
  throw new Error(`Invalid "relativetime" unit "${unitOption.value.value}". Expected one of: ${Array.from(relativeTimeUnits).join(", ")}.`);
131
341
  }
132
342
  }
343
+ /**
344
+ * Annotations with an ICU MessageFormat 1 plural `offset` option.
345
+ *
346
+ * `{count, plural, offset:1 …}` imports as `{$count :plural offset=1}`, and
347
+ * `#` inside it as `{$count :icu:pound offset=1}`. The registry subtracts the
348
+ * offset from the input, so it has to be a number.
349
+ */
350
+ const offsetAnnotations = new Set(["plural", "icu:pound"]);
351
+ const offsetPattern = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
352
+ function validateOffsetOptions(annotation) {
353
+ for (const option of annotation.options) {
354
+ if (option.name === "offset" &&
355
+ option.value.type === "literal" &&
356
+ !offsetPattern.test(option.value.value)) {
357
+ throw new Error(`Invalid "${annotation.name}" offset "${option.value.value}". Expected a number.`);
358
+ }
359
+ }
360
+ }
133
361
  function isDollarVariableReference(value) {
134
362
  return value.startsWith("$") && value.length > 1;
135
363
  }
@@ -1 +1 @@
1
- {"version":3,"file":"compile-annotation.js","sourceRoot":"","sources":["../../src/compiler/compile-annotation.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tDeclaration,\n\tFunctionReference,\n\tLiteral,\n\tVariableReference,\n} from \"@inlang/sdk\";\nimport { compileVariableAccess } from \"./variable-access.js\";\nimport { escapeForDoubleQuoteString } from \"../services/codegen/escape.js\";\n\n/**\n * The functions shipped in the generated registry.js file.\n *\n * @see createRegistry()\n */\nconst registryFunctionNames = new Set([\n\t\"plural\",\n\t\"number\",\n\t\"datetime\",\n\t\"relativetime\",\n]);\n\nexport function isRegistryFunction(name: string): boolean {\n\treturn registryFunctionNames.has(name);\n}\n\nexport function registryFunctionNamesForDisplay(): string {\n\treturn Array.from(registryFunctionNames).join(\", \");\n}\n\n/**\n * Wraps a compiled expression value in a `registry.*` call if an\n * annotation is present.\n *\n * @example\n * compileAnnotation(\"i?.count\", \"en\", { type: \"function-reference\", name: \"number\", options: [] })\n * >> 'registry.number(\"en\", i?.count, {})'\n */\nexport function compileAnnotation(\n\tstr: string,\n\tlocale: string,\n\tannotation?: FunctionReference,\n\tdeclarations?: Declaration[]\n): string {\n\tif (!annotation) {\n\t\treturn str;\n\t}\n\tif (annotation.name === \"relativetime\") {\n\t\tvalidateRelativeTimeOptions(annotation);\n\t}\n\treturn `registry.${annotation.name}(\"${locale}\", ${str}, ${compileOptions(annotation.name, annotation.options, declarations)})`;\n}\n\nfunction compileOptions(\n\tannotationName: string,\n\toptions: FunctionReference[\"options\"],\n\tdeclarations?: Declaration[]\n): string {\n\tif (options.length === 0) {\n\t\treturn \"{}\";\n\t}\n\tconst entries: string[] = options.map(\n\t\t(option) =>\n\t\t\t`${option.name}: ${compileOptionLiteralOrVarRef(\n\t\t\t\tannotationName,\n\t\t\t\toption.name,\n\t\t\t\toption.value,\n\t\t\t\tdeclarations\n\t\t\t)}`\n\t);\n\tconst code = \"{ \" + entries.join(\", \") + \" }\";\n\n\treturn code;\n}\n\nconst numericOptionNamesByAnnotation: Record<string, ReadonlySet<string>> = {\n\tnumber: new Set([\n\t\t\"minimumIntegerDigits\",\n\t\t\"minimumFractionDigits\",\n\t\t\"maximumFractionDigits\",\n\t\t\"minimumSignificantDigits\",\n\t\t\"maximumSignificantDigits\",\n\t\t\"roundingIncrement\",\n\t]),\n\tplural: new Set([\n\t\t\"minimumIntegerDigits\",\n\t\t\"minimumFractionDigits\",\n\t\t\"maximumFractionDigits\",\n\t\t\"minimumSignificantDigits\",\n\t\t\"maximumSignificantDigits\",\n\t]),\n\tdatetime: new Set([\"fractionalSecondDigits\"]),\n};\n\nconst booleanOptionNamesByAnnotation: Record<string, ReadonlySet<string>> = {\n\tnumber: new Set([\"useGrouping\"]),\n\tdatetime: new Set([\"hour12\"]),\n};\n\nconst jsNonNegativeIntegerPattern = /^(?:0|[1-9]\\d*)$/;\n\nfunction compileOptionLiteralOrVarRef(\n\tannotationName: string,\n\toptionName: string,\n\tvalue: Literal | VariableReference,\n\tdeclarations?: Declaration[]\n): string {\n\tif (value.type === \"variable-reference\") {\n\t\tif (annotationName === \"relativetime\" && optionName === \"unit\") {\n\t\t\treturn `/** @type {import(\"../registry.js\").RelativeTimeFormatUnit} */ (${compileVariableAccess(value.name, declarations)})`;\n\t\t}\n\t\treturn compileVariableAccess(value.name, declarations);\n\t}\n\n\tif (\n\t\tannotationName === \"relativetime\" &&\n\t\toptionName === \"unit\" &&\n\t\tisDollarVariableReference(value.value)\n\t) {\n\t\treturn `/** @type {import(\"../registry.js\").RelativeTimeFormatUnit} */ (${compileVariableAccess(value.value.slice(1), declarations)})`;\n\t}\n\n\tif (shouldEmitNumberLiteral(annotationName, optionName, value.value)) {\n\t\treturn value.value;\n\t}\n\n\tif (shouldEmitBooleanLiteral(annotationName, optionName, value.value)) {\n\t\treturn value.value;\n\t}\n\n\treturn `\"${escapeForDoubleQuoteString(value.value)}\"`;\n}\n\nfunction shouldEmitNumberLiteral(\n\tannotationName: string,\n\toptionName: string,\n\tliteral: string\n): boolean {\n\treturn (\n\t\tnumericOptionNamesByAnnotation[annotationName]?.has(optionName) === true &&\n\t\tjsNonNegativeIntegerPattern.test(literal)\n\t);\n}\n\nfunction shouldEmitBooleanLiteral(\n\tannotationName: string,\n\toptionName: string,\n\tliteral: string\n): boolean {\n\treturn (\n\t\tbooleanOptionNamesByAnnotation[annotationName]?.has(optionName) === true &&\n\t\t(literal === \"true\" || literal === \"false\")\n\t);\n}\n\nconst relativeTimeUnits = new Set([\n\t\"year\",\n\t\"years\",\n\t\"quarter\",\n\t\"quarters\",\n\t\"month\",\n\t\"months\",\n\t\"week\",\n\t\"weeks\",\n\t\"day\",\n\t\"days\",\n\t\"hour\",\n\t\"hours\",\n\t\"minute\",\n\t\"minutes\",\n\t\"second\",\n\t\"seconds\",\n]);\n\nfunction validateRelativeTimeOptions(annotation: FunctionReference): void {\n\tconst unitOptions = annotation.options.filter(\n\t\t(option) => option.name === \"unit\"\n\t);\n\n\tif (unitOptions.length === 0) {\n\t\tthrow new Error('The \"relativetime\" formatter requires a \"unit\" option.');\n\t}\n\n\tif (unitOptions.length > 1) {\n\t\tthrow new Error(\n\t\t\t'The \"relativetime\" formatter requires exactly one \"unit\" option.'\n\t\t);\n\t}\n\n\tconst [unitOption] = unitOptions;\n\tif (!unitOption || unitOption.value.type === \"variable-reference\") {\n\t\treturn;\n\t}\n\n\tif (isDollarVariableReference(unitOption.value.value)) {\n\t\treturn;\n\t}\n\n\tif (!relativeTimeUnits.has(unitOption.value.value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid \"relativetime\" unit \"${unitOption.value.value}\". Expected one of: ${Array.from(\n\t\t\t\trelativeTimeUnits\n\t\t\t).join(\", \")}.`\n\t\t);\n\t}\n}\n\nfunction isDollarVariableReference(value: string): boolean {\n\treturn value.startsWith(\"$\") && value.length > 1;\n}\n"]}
1
+ {"version":3,"file":"compile-annotation.js","sourceRoot":"","sources":["../../src/compiler/compile-annotation.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tDeclaration,\n\tFunctionReference,\n\tLiteral,\n\tVariableReference,\n} from \"@inlang/sdk\";\nimport { compileVariableAccess } from \"./variable-access.js\";\nimport { escapeForDoubleQuoteString } from \"../services/codegen/escape.js\";\nimport { Logger } from \"../services/logger/index.js\";\n\n/**\n * The functions shipped in the generated registry.js file, by the annotation\n * name that calls them.\n *\n * @see createRegistry()\n */\nconst registryFunctionByAnnotation = new Map([\n\t[\"plural\", \"plural\"],\n\t[\"number\", \"number\"],\n\t[\"datetime\", \"datetime\"],\n\t[\"relativetime\", \"relativetime\"],\n\t// `#` in ICU MessageFormat 1 plurals, as imported by the ICU1 plugin\n\t[\"icu:pound\", \"icuPound\"],\n\t// `{d, date, short}` and `{d, time, short}` in ICU MessageFormat 1, as\n\t// imported by the ICU1 plugin, see icuStyleAnnotation()\n\t[\"date\", \"datetime\"],\n\t[\"time\", \"datetime\"],\n]);\n\n/**\n * The formatter names listed in warnings: the annotation names that call a\n * registry function.\n */\nconst displayedRegistryFunctions = Array.from(\n\tregistryFunctionByAnnotation.keys()\n);\n\nexport function isRegistryFunction(name: string): boolean {\n\treturn registryFunctionByAnnotation.has(name);\n}\n\nexport function registryFunctionNamesForDisplay(): string {\n\treturn displayedRegistryFunctions.join(\", \");\n}\n\nconst logger = new Logger();\n\n/**\n * Tracks annotation names that have already been warned about to avoid\n * spamming the console when the same unsupported formatter is used in\n * many messages (or across watch-mode recompiles).\n */\nconst warnedUnsupportedAnnotations = new Set<string>();\n\n/**\n * Returns the value unformatted, and warns once per formatter name.\n *\n * Unknown annotations, on pattern expressions and on local variables, fall\n * back to plain interpolation to avoid breaking compilation of messages\n * imported from other i18n libraries (e.g. i18next's `{{value, customFormat}}`).\n */\nexport function ignoreUnknownFormatter(name: string, value: string): string {\n\tif (!warnedUnsupportedAnnotations.has(name)) {\n\t\twarnedUnsupportedAnnotations.add(name);\n\t\tlogger.warn(\n\t\t\t`The formatter \"${name}\" is unknown and will be ignored. The value is interpolated without formatting. Supported formatters: ${registryFunctionNamesForDisplay()}.`\n\t\t);\n\t}\n\treturn value;\n}\n\n/**\n * The names of the registry functions a message calls, collected while it is\n * compiled. The output decides from it whether to import the registry.\n */\nexport type RegistryUsage = Set<string>;\n\n/**\n * Wraps a compiled expression value in a `registry.*` call if an\n * annotation is present.\n *\n * @example\n * compileAnnotation(\"i?.count\", \"en\", { type: \"function-reference\", name: \"number\", options: [] })\n * >> 'registry.number(\"en\", i?.count, {})'\n */\nexport function compileAnnotation(\n\tstr: string,\n\tlocale: string,\n\tannotation?: FunctionReference,\n\tdeclarations?: Declaration[],\n\tregistryUsage?: RegistryUsage\n): string {\n\tif (!annotation) {\n\t\treturn str;\n\t}\n\tif (annotation.name === \"relativetime\") {\n\t\tvalidateRelativeTimeOptions(annotation);\n\t}\n\tif (offsetAnnotations.has(annotation.name)) {\n\t\tvalidateOffsetOptions(annotation);\n\t}\n\tconst functionName =\n\t\tregistryFunctionByAnnotation.get(annotation.name) ?? annotation.name;\n\tregistryUsage?.add(functionName);\n\tconst options = withDefaultDateTimeStyles(\n\t\ticuStyleAnnotation(annotation)\n\t).options;\n\t// the options of `date` and `time` are those of `datetime`\n\tconst optionsOf = functionName === \"datetime\" ? \"datetime\" : annotation.name;\n\treturn `registry.${functionName}(\"${locale}\", ${str}, ${compileOptions(optionsOf, options, declarations)})`;\n}\n\nconst icuDateTimeStyles = new Set([\"short\", \"medium\", \"long\", \"full\"]);\n\nconst intlNumberStyles = new Set([\"decimal\", \"percent\", \"currency\", \"unit\"]);\n\n/**\n * Translates the ICU MessageFormat 1 `style` of an annotation to the options\n * of the registry function.\n *\n * The ICU1 plugin imports `{n, number, integer}` as `{$n :number style=integer}`\n * and `{d, date, short}` as `{$d :date style=short}`. Intl knows the number\n * styles \"decimal\", \"percent\", \"currency\" (with a `currency`) and \"unit\", and\n * throws for others. The date and time styles are Intl's `dateStyle` and\n * `timeStyle`. A style without an Intl equivalent, like an ICU skeleton\n * (`::currency/EUR`), is left out with a warning.\n *\n * @example\n * icuStyleAnnotation({ name: \"date\", options: [{ name: \"style\", value: \"short\" }] })\n * >> { name: \"date\", options: [{ name: \"dateStyle\", value: \"short\" }] }\n */\nexport function icuStyleAnnotation(\n\tannotation: FunctionReference\n): FunctionReference {\n\tconst style = annotation.options.find((option) => option.name === \"style\");\n\tconst others = annotation.options.filter((option) => option !== style);\n\tconst literal = (name: string, value: string) => ({\n\t\tname,\n\t\tvalue: { type: \"literal\" as const, value },\n\t});\n\n\tif (annotation.name === \"date\" || annotation.name === \"time\") {\n\t\tconst optionName = annotation.name === \"date\" ? \"dateStyle\" : \"timeStyle\";\n\t\tif (style?.value.type === \"literal\") {\n\t\t\tif (icuDateTimeStyles.has(style.value.value)) {\n\t\t\t\treturn {\n\t\t\t\t\t...annotation,\n\t\t\t\t\toptions: [literal(optionName, style.value.value), ...others],\n\t\t\t\t};\n\t\t\t}\n\t\t\tignoreUnknownStyle(annotation.name, style.value.value);\n\t\t} else if (style) {\n\t\t\treturn {\n\t\t\t\t...annotation,\n\t\t\t\toptions: [{ name: optionName, value: style.value }, ...others],\n\t\t\t};\n\t\t}\n\t\t// without a style, withDefaultDateTimeStyles() applies the default\n\t\treturn { ...annotation, options: others };\n\t}\n\n\tif (annotation.name === \"number\" && style?.value.type === \"literal\") {\n\t\tconst value = style.value.value;\n\t\tif (value === \"integer\") {\n\t\t\tconst hasFractionDigits = others.some(\n\t\t\t\t(option) => option.name === \"maximumFractionDigits\"\n\t\t\t);\n\t\t\treturn {\n\t\t\t\t...annotation,\n\t\t\t\toptions: hasFractionDigits\n\t\t\t\t\t? others\n\t\t\t\t\t: [literal(\"maximumFractionDigits\", \"0\"), ...others],\n\t\t\t};\n\t\t}\n\t\tconst hasCurrency = others.some((option) => option.name === \"currency\");\n\t\tif (\n\t\t\t!intlNumberStyles.has(value) ||\n\t\t\t(value === \"currency\" && !hasCurrency)\n\t\t) {\n\t\t\tignoreUnknownStyle(annotation.name, value);\n\t\t\treturn { ...annotation, options: others };\n\t\t}\n\t}\n\n\treturn annotation;\n}\n\n/**\n * The `Intl.DateTimeFormat` options that pick which parts of a date are shown.\n * Without any of them, Intl shows only the numeric date, e.g. \"10/9/2026\".\n * `dateStyle` and `timeStyle` cannot be combined with the others: Intl throws.\n */\nconst dateTimeFieldOptions = new Set([\n\t\"dateStyle\",\n\t\"timeStyle\",\n\t\"weekday\",\n\t\"era\",\n\t\"year\",\n\t\"month\",\n\t\"day\",\n\t\"dayPeriod\",\n\t\"hour\",\n\t\"minute\",\n\t\"second\",\n\t\"fractionalSecondDigits\",\n\t\"timeZoneName\",\n]);\n\n/**\n * The styles a date formatter uses when its options pick no fields.\n *\n * - `date`: MessageFormat 2 (LDML 47 `:date` `style=medium`) and ICU\n * MessageFormat 1 (`{d, date}`) both default to \"medium\", e.g. \"Oct 9, 2026\".\n * - `time`: ICU MessageFormat 1 `{d, time}` defaults to \"medium\", e.g.\n * \"4:30:00 PM\", while MessageFormat 2 (LDML 47 `:time`) defaults to \"short\".\n * `{$d :time}` is what the ICU1 plugin imports `{d, time}` as, so it keeps\n * ICU's default. Write `{$d :time style=short}` for MessageFormat 2's.\n * - `datetime`: MessageFormat 2 (LDML 47 `:datetime`) defaults to\n * `dateStyle=medium timeStyle=short`, e.g. \"Oct 9, 2026, 4:30 PM\".\n */\nconst defaultDateTimeStyles = new Map<string, Array<[string, string]>>([\n\t[\"date\", [[\"dateStyle\", \"medium\"]]],\n\t[\"time\", [[\"timeStyle\", \"medium\"]]],\n\t[\n\t\t\"datetime\",\n\t\t[\n\t\t\t[\"dateStyle\", \"medium\"],\n\t\t\t[\"timeStyle\", \"short\"],\n\t\t],\n\t],\n]);\n\n/**\n * Adds the default styles of `date`, `time` and `datetime` when the options\n * pick no fields. Options like `timeZone` or `hour12` are kept.\n *\n * @example\n * withDefaultDateTimeStyles({ name: \"datetime\", options: [] })\n * >> { name: \"datetime\", options: [{ name: \"dateStyle\", value: \"medium\" }, { name: \"timeStyle\", value: \"short\" }] }\n */\nexport function withDefaultDateTimeStyles(\n\tannotation: FunctionReference\n): FunctionReference {\n\tconst defaults = defaultDateTimeStyles.get(annotation.name);\n\tif (\n\t\t!defaults ||\n\t\tannotation.options.some((option) => dateTimeFieldOptions.has(option.name))\n\t) {\n\t\treturn annotation;\n\t}\n\treturn {\n\t\t...annotation,\n\t\toptions: [\n\t\t\t...defaults.map(([name, value]) => ({\n\t\t\t\tname,\n\t\t\t\tvalue: { type: \"literal\" as const, value },\n\t\t\t})),\n\t\t\t...annotation.options,\n\t\t],\n\t};\n}\n\nconst warnedUnknownStyles = new Set<string>();\n\nfunction ignoreUnknownStyle(name: string, style: string): void {\n\tconst key = `${name} ${style}`;\n\tif (warnedUnknownStyles.has(key)) return;\n\twarnedUnknownStyles.add(key);\n\tlogger.warn(\n\t\t`The style \"${style}\" of the formatter \"${name}\" is not supported and will be ignored.${\n\t\t\tname === \"number\" && style === \"currency\"\n\t\t\t\t? ' The \"currency\" style needs a \"currency\" option.'\n\t\t\t\t: \"\"\n\t\t}`\n\t);\n}\n\n/**\n * Returns the names of the variables the compiled options of an annotation\n * read.\n *\n * @example\n * // {$date :relativetime unit=$unit}\n * annotationVariableReferences(annotation) // [\"unit\"]\n */\nexport function annotationVariableReferences(\n\tannotation: FunctionReference\n): string[] {\n\tconst names: string[] = [];\n\tfor (const option of annotation.options) {\n\t\tif (option.value.type === \"variable-reference\") {\n\t\t\tnames.push(option.value.name);\n\t\t} else if (\n\t\t\tannotation.name === \"relativetime\" &&\n\t\t\toption.name === \"unit\" &&\n\t\t\tisDollarVariableReference(option.value.value)\n\t\t) {\n\t\t\tnames.push(option.value.value.slice(1));\n\t\t}\n\t}\n\treturn names;\n}\n\nfunction compileOptions(\n\tannotationName: string,\n\toptions: FunctionReference[\"options\"],\n\tdeclarations?: Declaration[]\n): string {\n\tif (options.length === 0) {\n\t\treturn \"{}\";\n\t}\n\tconst entries: string[] = options.map(\n\t\t(option) =>\n\t\t\t`${option.name}: ${compileOptionLiteralOrVarRef(\n\t\t\t\tannotationName,\n\t\t\t\toption.name,\n\t\t\t\toption.value,\n\t\t\t\tdeclarations\n\t\t\t)}`\n\t);\n\tconst code = \"{ \" + entries.join(\", \") + \" }\";\n\n\treturn code;\n}\n\nconst numericOptionNamesByAnnotation: Record<string, ReadonlySet<string>> = {\n\tnumber: new Set([\n\t\t\"minimumIntegerDigits\",\n\t\t\"minimumFractionDigits\",\n\t\t\"maximumFractionDigits\",\n\t\t\"minimumSignificantDigits\",\n\t\t\"maximumSignificantDigits\",\n\t\t\"roundingIncrement\",\n\t]),\n\tplural: new Set([\n\t\t\"minimumIntegerDigits\",\n\t\t\"minimumFractionDigits\",\n\t\t\"maximumFractionDigits\",\n\t\t\"minimumSignificantDigits\",\n\t\t\"maximumSignificantDigits\",\n\t]),\n\tdatetime: new Set([\"fractionalSecondDigits\"]),\n};\n\nconst booleanOptionNamesByAnnotation: Record<string, ReadonlySet<string>> = {\n\tnumber: new Set([\"useGrouping\"]),\n\tdatetime: new Set([\"hour12\"]),\n};\n\nconst jsNonNegativeIntegerPattern = /^(?:0|[1-9]\\d*)$/;\n\nfunction compileOptionLiteralOrVarRef(\n\tannotationName: string,\n\toptionName: string,\n\tvalue: Literal | VariableReference,\n\tdeclarations?: Declaration[]\n): string {\n\tif (value.type === \"variable-reference\") {\n\t\tif (annotationName === \"relativetime\" && optionName === \"unit\") {\n\t\t\treturn `/** @type {import(\"../registry.js\").RelativeTimeFormatUnit} */ (${compileVariableAccess(value.name, declarations)})`;\n\t\t}\n\t\treturn compileVariableAccess(value.name, declarations);\n\t}\n\n\tif (\n\t\tannotationName === \"relativetime\" &&\n\t\toptionName === \"unit\" &&\n\t\tisDollarVariableReference(value.value)\n\t) {\n\t\treturn `/** @type {import(\"../registry.js\").RelativeTimeFormatUnit} */ (${compileVariableAccess(value.value.slice(1), declarations)})`;\n\t}\n\n\tif (shouldEmitNumberLiteral(annotationName, optionName, value.value)) {\n\t\treturn value.value;\n\t}\n\n\tif (shouldEmitBooleanLiteral(annotationName, optionName, value.value)) {\n\t\treturn value.value;\n\t}\n\n\tif (offsetAnnotations.has(annotationName) && optionName === \"offset\") {\n\t\t// validated by validateOffsetOptions()\n\t\treturn String(Number(value.value));\n\t}\n\n\treturn `\"${escapeForDoubleQuoteString(value.value)}\"`;\n}\n\nfunction shouldEmitNumberLiteral(\n\tannotationName: string,\n\toptionName: string,\n\tliteral: string\n): boolean {\n\treturn (\n\t\tnumericOptionNamesByAnnotation[annotationName]?.has(optionName) === true &&\n\t\tjsNonNegativeIntegerPattern.test(literal)\n\t);\n}\n\nfunction shouldEmitBooleanLiteral(\n\tannotationName: string,\n\toptionName: string,\n\tliteral: string\n): boolean {\n\treturn (\n\t\tbooleanOptionNamesByAnnotation[annotationName]?.has(optionName) === true &&\n\t\t(literal === \"true\" || literal === \"false\")\n\t);\n}\n\nconst relativeTimeUnits = new Set([\n\t\"year\",\n\t\"years\",\n\t\"quarter\",\n\t\"quarters\",\n\t\"month\",\n\t\"months\",\n\t\"week\",\n\t\"weeks\",\n\t\"day\",\n\t\"days\",\n\t\"hour\",\n\t\"hours\",\n\t\"minute\",\n\t\"minutes\",\n\t\"second\",\n\t\"seconds\",\n]);\n\nfunction validateRelativeTimeOptions(annotation: FunctionReference): void {\n\tconst unitOptions = annotation.options.filter(\n\t\t(option) => option.name === \"unit\"\n\t);\n\n\tif (unitOptions.length === 0) {\n\t\tthrow new Error('The \"relativetime\" formatter requires a \"unit\" option.');\n\t}\n\n\tif (unitOptions.length > 1) {\n\t\tthrow new Error(\n\t\t\t'The \"relativetime\" formatter requires exactly one \"unit\" option.'\n\t\t);\n\t}\n\n\tconst [unitOption] = unitOptions;\n\tif (!unitOption || unitOption.value.type === \"variable-reference\") {\n\t\treturn;\n\t}\n\n\tif (isDollarVariableReference(unitOption.value.value)) {\n\t\treturn;\n\t}\n\n\tif (!relativeTimeUnits.has(unitOption.value.value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid \"relativetime\" unit \"${unitOption.value.value}\". Expected one of: ${Array.from(\n\t\t\t\trelativeTimeUnits\n\t\t\t).join(\", \")}.`\n\t\t);\n\t}\n}\n\n/**\n * Annotations with an ICU MessageFormat 1 plural `offset` option.\n *\n * `{count, plural, offset:1 …}` imports as `{$count :plural offset=1}`, and\n * `#` inside it as `{$count :icu:pound offset=1}`. The registry subtracts the\n * offset from the input, so it has to be a number.\n */\nconst offsetAnnotations = new Set([\"plural\", \"icu:pound\"]);\n\nconst offsetPattern = /^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$/;\n\nfunction validateOffsetOptions(annotation: FunctionReference): void {\n\tfor (const option of annotation.options) {\n\t\tif (\n\t\t\toption.name === \"offset\" &&\n\t\t\toption.value.type === \"literal\" &&\n\t\t\t!offsetPattern.test(option.value.value)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid \"${annotation.name}\" offset \"${option.value.value}\". Expected a number.`\n\t\t\t);\n\t\t}\n\t}\n}\n\nfunction isDollarVariableReference(value: string): boolean {\n\treturn value.startsWith(\"$\") && value.length > 1;\n}\n"]}
@@ -1,4 +1,5 @@
1
- import type { Bundle, BundleNested, Message, ProjectSettings } from "@inlang/sdk";
1
+ import type { Bundle, BundleNested, ProjectSettings } from "@inlang/sdk";
2
+ import { type CompiledMessage } from "./compile-message.js";
2
3
  import type { Compiled } from "./types.js";
3
4
  import { type InputMatchTypes } from "./jsdoc-types.js";
4
5
  import type { CompilerOptions } from "./compiler-options.js";
@@ -7,7 +8,7 @@ export type CompiledBundleWithMessages = {
7
8
  bundle: Compiled<Bundle>;
8
9
  /** The compilation results for the languages */
9
10
  messages: {
10
- [locale: string]: Compiled<Message>;
11
+ [locale: string]: CompiledMessage;
11
12
  };
12
13
  /** Match literal types inferred from bundle variants */
13
14
  matchTypes: InputMatchTypes;
@@ -4,6 +4,7 @@ import { isValidIdentifier, quotePropertyKey } from "./variable-access.js";
4
4
  import { toSafeModuleId } from "./safe-module-id.js";
5
5
  import { escapeForDoubleQuoteString } from "../services/codegen/escape.js";
6
6
  import { perLocaleBuildStaticLocaleExpression } from "./per-locale-build.js";
7
+ import { resolveInputAlias } from "./input-alias.js";
7
8
  /**
8
9
  * Compiles all the messages in the bundle and returns an index-function + each compiled message
9
10
  */
@@ -71,7 +72,10 @@ const compileBundleFunction = (args) => {
71
72
  if (mode === "string") {
72
73
  return `${prefix}${condition}return ${messageRef}(inputs)`;
73
74
  }
74
- return `${prefix}${condition}return typeof ${messageRef}.parts === "function" ? ${messageRef}.parts(inputs) : [{ type: "text", value: ${messageRef}(inputs) }]`;
75
+ // A locale's message has .parts() only if its own variants have
76
+ // markup. Fallbacks and messages without markup are typed without
77
+ // it, so read it through a type that declares it optional.
78
+ return `${prefix}${condition}return /** @type {{ parts?: (inputs: ${inputType}) => import('../runtime.js').MessagePart[] }} */ (${messageRef}).parts?.(inputs) ?? [{ type: "text", value: ${messageRef}(inputs) }]`;
75
79
  })
76
80
  .join("\n");
77
81
  const perLocaleBuild = args.experimentalStaticLocale === perLocaleBuildStaticLocaleExpression;
@@ -421,6 +425,31 @@ function collectInputMatchTypes(bundle) {
421
425
  }
422
426
  }
423
427
  }
428
+ // Matches on un-annotated aliases of an input match the input value too.
429
+ // Widen an input type that was narrowed by direct matches: alias literals
430
+ // join the union and an alias catchall accepts any value. Inputs that are
431
+ // only matched through aliases keep their unnarrowed type.
432
+ const declarations = bundle.declarations ?? [];
433
+ for (const message of bundle.messages) {
434
+ for (const variant of message.variants) {
435
+ for (const match of variant.matches ?? []) {
436
+ if (inputNames.has(match.key))
437
+ continue;
438
+ const inputName = resolveInputAlias(match.key, declarations);
439
+ if (inputName === undefined)
440
+ continue;
441
+ const info = matchTypes.get(inputName);
442
+ if (!info)
443
+ continue;
444
+ if (match.type === "catchall-match") {
445
+ info.hasCatchAll = true;
446
+ }
447
+ else if (match.type === "literal-match") {
448
+ info.literals.add(match.value);
449
+ }
450
+ }
451
+ }
452
+ }
424
453
  return matchTypes;
425
454
  }
426
455
  function bundleHasMarkup(bundle) {