@inlang/paraglide-js 2.26.0 → 2.28.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 (225) hide show
  1. package/README.md +11 -1
  2. package/dist/bundler-plugins/per-locale-build/index.js +14 -7
  3. package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
  4. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js +237 -4
  5. package/dist/bundler-plugins/per-locale-build/vite-environments.test.js.map +1 -1
  6. package/dist/bundler-plugins/unplugin.d.ts +1 -1
  7. package/dist/bundler-plugins/unplugin.js +79 -10
  8. package/dist/bundler-plugins/unplugin.js.map +1 -1
  9. package/dist/bundler-plugins/unplugin.test.js +334 -1
  10. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  11. package/dist/bundler-plugins/vite.js +2 -1
  12. package/dist/bundler-plugins/vite.js.map +1 -1
  13. package/dist/bundler-plugins/vite.test.js +1 -1
  14. package/dist/bundler-plugins/vite.test.js.map +1 -1
  15. package/dist/cli/commands/compile/command.js +13 -0
  16. package/dist/cli/commands/compile/command.js.map +1 -1
  17. package/dist/cli/commands/compile/command.test.js +87 -3
  18. package/dist/cli/commands/compile/command.test.js.map +1 -1
  19. package/dist/cli/commands/init/command.d.ts +5 -0
  20. package/dist/cli/commands/init/command.js +216 -44
  21. package/dist/cli/commands/init/command.js.map +1 -1
  22. package/dist/cli/commands/init/command.test.d.ts +1 -0
  23. package/dist/cli/commands/init/command.test.js +123 -0
  24. package/dist/cli/commands/init/command.test.js.map +1 -0
  25. package/dist/cli/defaults.js +2 -2
  26. package/dist/cli/defaults.js.map +1 -1
  27. package/dist/cli/init-options.d.ts +11 -0
  28. package/dist/cli/init-options.js +2 -0
  29. package/dist/cli/init-options.js.map +1 -0
  30. package/dist/cli/steps/add-inlang-check.d.ts +19 -0
  31. package/dist/cli/steps/add-inlang-check.js +60 -0
  32. package/dist/cli/steps/add-inlang-check.js.map +1 -0
  33. package/dist/cli/steps/add-inlang-check.test.d.ts +1 -0
  34. package/dist/cli/steps/add-inlang-check.test.js +88 -0
  35. package/dist/cli/steps/add-inlang-check.test.js.map +1 -0
  36. package/dist/cli/steps/add-message-lint.d.ts +34 -0
  37. package/dist/cli/steps/add-message-lint.js +105 -0
  38. package/dist/cli/steps/add-message-lint.js.map +1 -0
  39. package/dist/cli/steps/add-message-lint.test.d.ts +1 -0
  40. package/dist/cli/steps/add-message-lint.test.js +74 -0
  41. package/dist/cli/steps/add-message-lint.test.js.map +1 -0
  42. package/dist/cli/steps/add-vite-plugin.d.ts +18 -0
  43. package/dist/cli/steps/add-vite-plugin.js +34 -6
  44. package/dist/cli/steps/add-vite-plugin.js.map +1 -1
  45. package/dist/cli/steps/add-vite-plugin.test.js +25 -9
  46. package/dist/cli/steps/add-vite-plugin.test.js.map +1 -1
  47. package/dist/cli/steps/agents-md-suggestion.d.ts +11 -0
  48. package/dist/cli/steps/agents-md-suggestion.js +19 -0
  49. package/dist/cli/steps/agents-md-suggestion.js.map +1 -0
  50. package/dist/cli/steps/agents-md-suggestion.test.d.ts +1 -0
  51. package/dist/cli/steps/agents-md-suggestion.test.js +36 -0
  52. package/dist/cli/steps/agents-md-suggestion.test.js.map +1 -0
  53. package/dist/cli/steps/check-for-uncomitted-changes.d.ts +3 -0
  54. package/dist/cli/steps/check-for-uncomitted-changes.js +15 -3
  55. package/dist/cli/steps/check-for-uncomitted-changes.js.map +1 -1
  56. package/dist/cli/steps/detect-bundler.d.ts +1 -0
  57. package/dist/cli/steps/detect-bundler.js +7 -1
  58. package/dist/cli/steps/detect-bundler.js.map +1 -1
  59. package/dist/cli/steps/initialize-inlang-project.d.ts +4 -3
  60. package/dist/cli/steps/initialize-inlang-project.js +60 -157
  61. package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
  62. package/dist/cli/steps/maybe-add-machine-translation.d.ts +4 -0
  63. package/dist/cli/steps/maybe-add-machine-translation.js +14 -8
  64. package/dist/cli/steps/maybe-add-machine-translation.js.map +1 -1
  65. package/dist/cli/steps/maybe-add-sherlock.d.ts +2 -0
  66. package/dist/cli/steps/maybe-add-sherlock.js +13 -7
  67. package/dist/cli/steps/maybe-add-sherlock.js.map +1 -1
  68. package/dist/cli/steps/maybe-add-sherlock.test.d.ts +1 -0
  69. package/dist/cli/steps/maybe-add-sherlock.test.js +44 -0
  70. package/dist/cli/steps/maybe-add-sherlock.test.js.map +1 -0
  71. package/dist/cli/steps/prompt-for-outdir.d.ts +2 -0
  72. package/dist/cli/steps/prompt-for-outdir.js +4 -0
  73. package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
  74. package/dist/cli/steps/update-package-json.js +8 -13
  75. package/dist/cli/steps/update-package-json.js.map +1 -1
  76. package/dist/cli/steps/update-ts-config.d.ts +6 -0
  77. package/dist/cli/steps/update-ts-config.js +27 -4
  78. package/dist/cli/steps/update-ts-config.js.map +1 -1
  79. package/dist/cli/utils.d.ts +11 -0
  80. package/dist/cli/utils.js +21 -0
  81. package/dist/cli/utils.js.map +1 -1
  82. package/dist/cli/utils.test.d.ts +1 -0
  83. package/dist/cli/utils.test.js +16 -0
  84. package/dist/cli/utils.test.js.map +1 -0
  85. package/dist/compiler/compile-annotation.d.ts +48 -1
  86. package/dist/compiler/compile-annotation.js +243 -10
  87. package/dist/compiler/compile-annotation.js.map +1 -1
  88. package/dist/compiler/compile-bundle.d.ts +7 -2
  89. package/dist/compiler/compile-bundle.js +97 -3
  90. package/dist/compiler/compile-bundle.js.map +1 -1
  91. package/dist/compiler/compile-bundle.test.js +232 -19
  92. package/dist/compiler/compile-bundle.test.js.map +1 -1
  93. package/dist/compiler/compile-lifecycle.test.d.ts +1 -0
  94. package/dist/compiler/compile-lifecycle.test.js +69 -0
  95. package/dist/compiler/compile-lifecycle.test.js.map +1 -0
  96. package/dist/compiler/compile-local-variable.d.ts +3 -0
  97. package/dist/compiler/compile-local-variable.js +5 -2
  98. package/dist/compiler/compile-local-variable.js.map +1 -1
  99. package/dist/compiler/compile-local-variable.test.js +55 -15
  100. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  101. package/dist/compiler/compile-message.d.ts +29 -1
  102. package/dist/compiler/compile-message.exact-number.test.d.ts +1 -0
  103. package/dist/compiler/compile-message.exact-number.test.js +786 -0
  104. package/dist/compiler/compile-message.exact-number.test.js.map +1 -0
  105. package/dist/compiler/compile-message.js +162 -66
  106. package/dist/compiler/compile-message.js.map +1 -1
  107. package/dist/compiler/compile-message.locals.test.d.ts +1 -0
  108. package/dist/compiler/compile-message.locals.test.js +498 -0
  109. package/dist/compiler/compile-message.locals.test.js.map +1 -0
  110. package/dist/compiler/compile-message.test.js +113 -22
  111. package/dist/compiler/compile-message.test.js.map +1 -1
  112. package/dist/compiler/compile-pattern.d.ts +5 -0
  113. package/dist/compiler/compile-pattern.js +6 -18
  114. package/dist/compiler/compile-pattern.js.map +1 -1
  115. package/dist/compiler/compile-pattern.test.js +169 -5
  116. package/dist/compiler/compile-pattern.test.js.map +1 -1
  117. package/dist/compiler/compile-project.js +5 -1
  118. package/dist/compiler/compile-project.js.map +1 -1
  119. package/dist/compiler/compile-project.test.js +1260 -185
  120. package/dist/compiler/compile-project.test.js.map +1 -1
  121. package/dist/compiler/compile.js +36 -17
  122. package/dist/compiler/compile.js.map +1 -1
  123. package/dist/compiler/compile.test.js +71 -1
  124. package/dist/compiler/compile.test.js.map +1 -1
  125. package/dist/compiler/compiler-options.d.ts +41 -0
  126. package/dist/compiler/compiler-options.js.map +1 -1
  127. package/dist/compiler/create-readme.d.ts +4 -1
  128. package/dist/compiler/create-readme.js +33 -125
  129. package/dist/compiler/create-readme.js.map +1 -1
  130. package/dist/compiler/emit-ts-declarations.interop.test.js +2 -2
  131. package/dist/compiler/emit-ts-declarations.interop.test.js.map +1 -1
  132. package/dist/compiler/emit-ts-declarations.test.js +4 -4
  133. package/dist/compiler/emit-ts-declarations.test.js.map +1 -1
  134. package/dist/compiler/index.d.ts +1 -1
  135. package/dist/compiler/index.js +1 -1
  136. package/dist/compiler/index.js.map +1 -1
  137. package/dist/compiler/input-alias.d.ts +21 -0
  138. package/dist/compiler/input-alias.js +42 -0
  139. package/dist/compiler/input-alias.js.map +1 -0
  140. package/dist/compiler/input-alias.test.d.ts +1 -0
  141. package/dist/compiler/input-alias.test.js +58 -0
  142. package/dist/compiler/input-alias.test.js.map +1 -0
  143. package/dist/compiler/message-locals.d.ts +46 -0
  144. package/dist/compiler/message-locals.js +129 -0
  145. package/dist/compiler/message-locals.js.map +1 -0
  146. package/dist/compiler/output-structure/locale-modules.js +7 -1
  147. package/dist/compiler/output-structure/locale-modules.js.map +1 -1
  148. package/dist/compiler/output-structure/locale-modules.test.js +35 -0
  149. package/dist/compiler/output-structure/locale-modules.test.js.map +1 -1
  150. package/dist/compiler/output-structure/message-modules.js +6 -4
  151. package/dist/compiler/output-structure/message-modules.js.map +1 -1
  152. package/dist/compiler/output-structure/message-modules.test.js +35 -0
  153. package/dist/compiler/output-structure/message-modules.test.js.map +1 -1
  154. package/dist/compiler/registry.d.ts +12 -0
  155. package/dist/compiler/registry.js +158 -31
  156. package/dist/compiler/registry.js.map +1 -1
  157. package/dist/compiler/registry.test.d.ts +1 -0
  158. package/dist/compiler/registry.test.js +291 -0
  159. package/dist/compiler/registry.test.js.map +1 -0
  160. package/dist/compiler/runtime/create-runtime.d.ts +2 -0
  161. package/dist/compiler/runtime/create-runtime.js +9 -0
  162. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  163. package/dist/compiler/runtime/extract-locale-from-cookie.js +8 -1
  164. package/dist/compiler/runtime/extract-locale-from-cookie.js.map +1 -1
  165. package/dist/compiler/runtime/extract-locale-from-cookie.test.js +26 -0
  166. package/dist/compiler/runtime/extract-locale-from-cookie.test.js.map +1 -1
  167. package/dist/compiler/runtime/extract-locale-from-header.js +47 -5
  168. package/dist/compiler/runtime/extract-locale-from-header.js.map +1 -1
  169. package/dist/compiler/runtime/extract-locale-from-header.test.js +58 -0
  170. package/dist/compiler/runtime/extract-locale-from-header.test.js.map +1 -1
  171. package/dist/compiler/runtime/get-locale.js +7 -1
  172. package/dist/compiler/runtime/get-locale.js.map +1 -1
  173. package/dist/compiler/runtime/get-locale.test.js +95 -0
  174. package/dist/compiler/runtime/get-locale.test.js.map +1 -1
  175. package/dist/compiler/runtime/localize-href.d.ts +8 -0
  176. package/dist/compiler/runtime/localize-href.js +60 -0
  177. package/dist/compiler/runtime/localize-href.js.map +1 -1
  178. package/dist/compiler/runtime/localize-href.test.js +227 -1
  179. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  180. package/dist/compiler/runtime/set-locale.js +24 -6
  181. package/dist/compiler/runtime/set-locale.js.map +1 -1
  182. package/dist/compiler/runtime/set-locale.test.js +182 -0
  183. package/dist/compiler/runtime/set-locale.test.js.map +1 -1
  184. package/dist/compiler/runtime/type.d.ts +2 -0
  185. package/dist/compiler/runtime/type.js.map +1 -1
  186. package/dist/compiler/runtime/type.test.js +34 -0
  187. package/dist/compiler/runtime/type.test.js.map +1 -1
  188. package/dist/compiler/runtime/variables.d.ts +2 -0
  189. package/dist/compiler/runtime/variables.js +7 -0
  190. package/dist/compiler/runtime/variables.js.map +1 -1
  191. package/dist/compiler/server/create-server-file.js +24 -0
  192. package/dist/compiler/server/create-server-file.js.map +1 -1
  193. package/dist/compiler/server/create-server-file.test.js +8 -2
  194. package/dist/compiler/server/create-server-file.test.js.map +1 -1
  195. package/dist/compiler/server/middleware.d.ts +8 -2
  196. package/dist/compiler/server/middleware.js +204 -11
  197. package/dist/compiler/server/middleware.js.map +1 -1
  198. package/dist/compiler/server/middleware.test.js +750 -3
  199. package/dist/compiler/server/middleware.test.js.map +1 -1
  200. package/dist/eslint/index.d.ts +4 -0
  201. package/dist/eslint/index.js +8 -0
  202. package/dist/eslint/index.js.map +1 -0
  203. package/dist/eslint/no-dynamic-message-access.d.ts +3 -0
  204. package/dist/eslint/no-dynamic-message-access.js +150 -0
  205. package/dist/eslint/no-dynamic-message-access.js.map +1 -0
  206. package/dist/eslint/no-dynamic-message-access.test.d.ts +1 -0
  207. package/dist/eslint/no-dynamic-message-access.test.js +126 -0
  208. package/dist/eslint/no-dynamic-message-access.test.js.map +1 -0
  209. package/dist/eslint/oxlint.test.d.ts +1 -0
  210. package/dist/eslint/oxlint.test.js +64 -0
  211. package/dist/eslint/oxlint.test.js.map +1 -0
  212. package/dist/services/config/config-schema.d.ts +2 -0
  213. package/dist/services/config/config-schema.js +2 -0
  214. package/dist/services/config/config-schema.js.map +1 -1
  215. package/dist/services/config/load-paraglide-config.test.js +12 -0
  216. package/dist/services/config/load-paraglide-config.test.js.map +1 -1
  217. package/dist/services/env-variables/index.js +1 -1
  218. package/dist/services/env-variables/index.js.map +1 -1
  219. package/dist/services/file-handling/write-output.bench.js +51 -11
  220. package/dist/services/file-handling/write-output.bench.js.map +1 -1
  221. package/dist/services/file-handling/write-output.js +60 -20
  222. package/dist/services/file-handling/write-output.js.map +1 -1
  223. package/dist/services/file-handling/write-output.test.js +175 -0
  224. package/dist/services/file-handling/write-output.test.js.map +1 -1
  225. package/package.json +15 -5
@@ -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,191 @@ 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
+ // compileOptions emits a plain data object, so the registry can safely cache it.
78
+ const cacheable = ["plural", "number", "datetime", "relativetime"].includes(functionName) &&
79
+ !options.some((option) => option.name === "__proto__")
80
+ ? ", true"
81
+ : "";
82
+ return `registry.${functionName}("${locale}", ${str}, ${compileOptions(optionsOf, options, declarations)}${cacheable})`;
83
+ }
84
+ const icuDateTimeStyles = new Set(["short", "medium", "long", "full"]);
85
+ const intlNumberStyles = new Set(["decimal", "percent", "currency", "unit"]);
86
+ /**
87
+ * Translates the ICU MessageFormat 1 `style` of an annotation to the options
88
+ * of the registry function.
89
+ *
90
+ * The ICU1 plugin imports `{n, number, integer}` as `{$n :number style=integer}`
91
+ * and `{d, date, short}` as `{$d :date style=short}`. Intl knows the number
92
+ * styles "decimal", "percent", "currency" (with a `currency`) and "unit", and
93
+ * throws for others. The date and time styles are Intl's `dateStyle` and
94
+ * `timeStyle`. A style without an Intl equivalent, like an ICU skeleton
95
+ * (`::currency/EUR`), is left out with a warning.
96
+ *
97
+ * @example
98
+ * icuStyleAnnotation({ name: "date", options: [{ name: "style", value: "short" }] })
99
+ * >> { name: "date", options: [{ name: "dateStyle", value: "short" }] }
100
+ */
101
+ export function icuStyleAnnotation(annotation) {
102
+ const style = annotation.options.find((option) => option.name === "style");
103
+ const others = annotation.options.filter((option) => option !== style);
104
+ const literal = (name, value) => ({
105
+ name,
106
+ value: { type: "literal", value },
107
+ });
108
+ if (annotation.name === "date" || annotation.name === "time") {
109
+ const optionName = annotation.name === "date" ? "dateStyle" : "timeStyle";
110
+ if (style?.value.type === "literal") {
111
+ if (icuDateTimeStyles.has(style.value.value)) {
112
+ return {
113
+ ...annotation,
114
+ options: [literal(optionName, style.value.value), ...others],
115
+ };
116
+ }
117
+ ignoreUnknownStyle(annotation.name, style.value.value);
118
+ }
119
+ else if (style) {
120
+ return {
121
+ ...annotation,
122
+ options: [{ name: optionName, value: style.value }, ...others],
123
+ };
124
+ }
125
+ // without a style, withDefaultDateTimeStyles() applies the default
126
+ return { ...annotation, options: others };
127
+ }
128
+ if (annotation.name === "number" && style?.value.type === "literal") {
129
+ const value = style.value.value;
130
+ if (value === "integer") {
131
+ const hasFractionDigits = others.some((option) => option.name === "maximumFractionDigits");
132
+ return {
133
+ ...annotation,
134
+ options: hasFractionDigits
135
+ ? others
136
+ : [literal("maximumFractionDigits", "0"), ...others],
137
+ };
138
+ }
139
+ const hasCurrency = others.some((option) => option.name === "currency");
140
+ if (!intlNumberStyles.has(value) ||
141
+ (value === "currency" && !hasCurrency)) {
142
+ ignoreUnknownStyle(annotation.name, value);
143
+ return { ...annotation, options: others };
144
+ }
145
+ }
146
+ return annotation;
147
+ }
148
+ /**
149
+ * The `Intl.DateTimeFormat` options that pick which parts of a date are shown.
150
+ * Without any of them, Intl shows only the numeric date, e.g. "10/9/2026".
151
+ * `dateStyle` and `timeStyle` cannot be combined with the others: Intl throws.
152
+ */
153
+ const dateTimeFieldOptions = new Set([
154
+ "dateStyle",
155
+ "timeStyle",
156
+ "weekday",
157
+ "era",
158
+ "year",
159
+ "month",
160
+ "day",
161
+ "dayPeriod",
162
+ "hour",
163
+ "minute",
164
+ "second",
165
+ "fractionalSecondDigits",
166
+ "timeZoneName",
167
+ ]);
168
+ /**
169
+ * The styles a date formatter uses when its options pick no fields.
170
+ *
171
+ * - `date`: MessageFormat 2 (LDML 47 `:date` `style=medium`) and ICU
172
+ * MessageFormat 1 (`{d, date}`) both default to "medium", e.g. "Oct 9, 2026".
173
+ * - `time`: ICU MessageFormat 1 `{d, time}` defaults to "medium", e.g.
174
+ * "4:30:00 PM", while MessageFormat 2 (LDML 47 `:time`) defaults to "short".
175
+ * `{$d :time}` is what the ICU1 plugin imports `{d, time}` as, so it keeps
176
+ * ICU's default. Write `{$d :time style=short}` for MessageFormat 2's.
177
+ * - `datetime`: MessageFormat 2 (LDML 47 `:datetime`) defaults to
178
+ * `dateStyle=medium timeStyle=short`, e.g. "Oct 9, 2026, 4:30 PM".
179
+ */
180
+ const defaultDateTimeStyles = new Map([
181
+ ["date", [["dateStyle", "medium"]]],
182
+ ["time", [["timeStyle", "medium"]]],
183
+ [
184
+ "datetime",
185
+ [
186
+ ["dateStyle", "medium"],
187
+ ["timeStyle", "short"],
188
+ ],
189
+ ],
190
+ ]);
191
+ /**
192
+ * Adds the default styles of `date`, `time` and `datetime` when the options
193
+ * pick no fields. Options like `timeZone` or `hour12` are kept.
194
+ *
195
+ * @example
196
+ * withDefaultDateTimeStyles({ name: "datetime", options: [] })
197
+ * >> { name: "datetime", options: [{ name: "dateStyle", value: "medium" }, { name: "timeStyle", value: "short" }] }
198
+ */
199
+ export function withDefaultDateTimeStyles(annotation) {
200
+ const defaults = defaultDateTimeStyles.get(annotation.name);
201
+ if (!defaults ||
202
+ annotation.options.some((option) => dateTimeFieldOptions.has(option.name))) {
203
+ return annotation;
204
+ }
205
+ return {
206
+ ...annotation,
207
+ options: [
208
+ ...defaults.map(([name, value]) => ({
209
+ name,
210
+ value: { type: "literal", value },
211
+ })),
212
+ ...annotation.options,
213
+ ],
214
+ };
215
+ }
216
+ const warnedUnknownStyles = new Set();
217
+ function ignoreUnknownStyle(name, style) {
218
+ const key = `${name} ${style}`;
219
+ if (warnedUnknownStyles.has(key))
220
+ return;
221
+ warnedUnknownStyles.add(key);
222
+ logger.warn(`The style "${style}" of the formatter "${name}" is not supported and will be ignored.${name === "number" && style === "currency"
223
+ ? ' The "currency" style needs a "currency" option.'
224
+ : ""}`);
225
+ }
226
+ /**
227
+ * Returns the names of the variables the compiled options of an annotation
228
+ * read.
229
+ *
230
+ * @example
231
+ * // {$date :relativetime unit=$unit}
232
+ * annotationVariableReferences(annotation) // ["unit"]
233
+ */
234
+ export function annotationVariableReferences(annotation) {
235
+ const names = [];
236
+ for (const option of annotation.options) {
237
+ if (option.value.type === "variable-reference") {
238
+ names.push(option.value.name);
239
+ }
240
+ else if (annotation.name === "relativetime" &&
241
+ option.name === "unit" &&
242
+ isDollarVariableReference(option.value.value)) {
243
+ names.push(option.value.value.slice(1));
244
+ }
245
+ }
246
+ return names;
36
247
  }
37
248
  function compileOptions(annotationName, options, declarations) {
38
249
  if (options.length === 0) {
@@ -83,6 +294,10 @@ function compileOptionLiteralOrVarRef(annotationName, optionName, value, declara
83
294
  if (shouldEmitBooleanLiteral(annotationName, optionName, value.value)) {
84
295
  return value.value;
85
296
  }
297
+ if (offsetAnnotations.has(annotationName) && optionName === "offset") {
298
+ // validated by validateOffsetOptions()
299
+ return String(Number(value.value));
300
+ }
86
301
  return `"${escapeForDoubleQuoteString(value.value)}"`;
87
302
  }
88
303
  function shouldEmitNumberLiteral(annotationName, optionName, literal) {
@@ -130,6 +345,24 @@ function validateRelativeTimeOptions(annotation) {
130
345
  throw new Error(`Invalid "relativetime" unit "${unitOption.value.value}". Expected one of: ${Array.from(relativeTimeUnits).join(", ")}.`);
131
346
  }
132
347
  }
348
+ /**
349
+ * Annotations with an ICU MessageFormat 1 plural `offset` option.
350
+ *
351
+ * `{count, plural, offset:1 …}` imports as `{$count :plural offset=1}`, and
352
+ * `#` inside it as `{$count :icu:pound offset=1}`. The registry subtracts the
353
+ * offset from the input, so it has to be a number.
354
+ */
355
+ const offsetAnnotations = new Set(["plural", "icu:pound"]);
356
+ const offsetPattern = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
357
+ function validateOffsetOptions(annotation) {
358
+ for (const option of annotation.options) {
359
+ if (option.name === "offset" &&
360
+ option.value.type === "literal" &&
361
+ !offsetPattern.test(option.value.value)) {
362
+ throw new Error(`Invalid "${annotation.name}" offset "${option.value.value}". Expected a number.`);
363
+ }
364
+ }
365
+ }
133
366
  function isDollarVariableReference(value) {
134
367
  return value.startsWith("$") && value.length > 1;
135
368
  }
@@ -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\t// compileOptions emits a plain data object, so the registry can safely cache it.\n\tconst cacheable =\n\t\t[\"plural\", \"number\", \"datetime\", \"relativetime\"].includes(functionName) &&\n\t\t!options.some((option) => option.name === \"__proto__\")\n\t\t\t? \", true\"\n\t\t\t: \"\";\n\treturn `registry.${functionName}(\"${locale}\", ${str}, ${compileOptions(optionsOf, options, declarations)}${cacheable})`;\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,12 +8,14 @@ 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;
14
15
  /** Shared typedef name for bundle input types used across emitted JSDoc */
15
16
  inputTypeAliasName?: string;
17
+ /** Compiled message helpers with no emitted reference in message-modules. */
18
+ unreferencedMessageLocales?: string[];
16
19
  };
17
20
  /**
18
21
  * Compiles all the messages in the bundle and returns an index-function + each compiled message
@@ -22,6 +25,8 @@ export declare const compileBundle: (args: {
22
25
  fallbackMap: Record<string, string | undefined>;
23
26
  messageReferenceExpression: (locale: string, bundleId: string) => string;
24
27
  settings?: ProjectSettings;
28
+ /** Internal layout control; locale modules keep text grouped by language. */
29
+ inlineMessages?: boolean;
25
30
  experimentalMiddlewareLocaleSplitting?: boolean;
26
31
  experimentalStaticLocale?: CompilerOptions["experimentalStaticLocale"];
27
32
  }) => CompiledBundleWithMessages;
@@ -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
  */
@@ -21,11 +22,22 @@ export const compileBundle = (args) => {
21
22
  // set the pattern for the language tag
22
23
  compiledMessages[message.locale] = compiledMessage;
23
24
  }
25
+ const inlinePlainTextByLocale = args.inlineMessages !== true ||
26
+ args.experimentalStaticLocale === perLocaleBuildStaticLocaleExpression
27
+ ? undefined
28
+ : findPlainTextMessages(args.bundle);
29
+ const unreferencedMessageLocales = findUnreferencedMessageLocales({
30
+ inlinePlainTextByLocale: inlinePlainTextByLocale ?? Object.create(null),
31
+ compiledMessages,
32
+ fallbackMap: args.fallbackMap,
33
+ hasMarkup,
34
+ });
24
35
  return {
25
36
  bundle: compileBundleFunction({
26
37
  bundle: args.bundle,
27
38
  availableLocales: Object.keys(args.fallbackMap),
28
39
  messageReferenceExpression: args.messageReferenceExpression,
40
+ inlinePlainTextByLocale,
29
41
  settings: args.settings,
30
42
  matchTypes,
31
43
  hasMarkup,
@@ -36,8 +48,54 @@ export const compileBundle = (args) => {
36
48
  messages: compiledMessages,
37
49
  matchTypes,
38
50
  inputTypeAliasName,
51
+ unreferencedMessageLocales,
39
52
  };
40
53
  };
54
+ /**
55
+ * Finds one-variant messages whose pattern is entirely static text. Their
56
+ * dispatcher branch can return the string directly without retaining a
57
+ * locale message function in the client bundle.
58
+ */
59
+ function findPlainTextMessages(bundle) {
60
+ const result = Object.create(null);
61
+ for (const message of bundle.messages) {
62
+ if (message.variants.length !== 1)
63
+ continue;
64
+ const variant = message.variants[0];
65
+ if (!variant || !variant.pattern.every((part) => part.type === "text")) {
66
+ continue;
67
+ }
68
+ // compilePattern emits static text inside a template literal, whose
69
+ // cooked value normalizes CR and CRLF line endings to LF.
70
+ result[message.locale] = variant.pattern
71
+ .map((part) => (part.type === "text" ? part.value : ""))
72
+ .join("")
73
+ .replace(/\r\n?/g, "\n");
74
+ }
75
+ return result;
76
+ }
77
+ function findUnreferencedMessageLocales(args) {
78
+ if (args.hasMarkup)
79
+ return [];
80
+ // Missing-locale aliases still call the first available compiled fallback
81
+ // function, even when the dispatcher's own branch uses an inline string.
82
+ const fallbackTargets = new Set();
83
+ for (const locale of Object.keys(args.fallbackMap)) {
84
+ if (Object.hasOwn(args.compiledMessages, locale))
85
+ continue;
86
+ let fallbackLocale = args.fallbackMap[locale];
87
+ const visited = new Set([locale]);
88
+ while (fallbackLocale && !visited.has(fallbackLocale)) {
89
+ visited.add(fallbackLocale);
90
+ if (Object.hasOwn(args.compiledMessages, fallbackLocale)) {
91
+ fallbackTargets.add(fallbackLocale);
92
+ break;
93
+ }
94
+ fallbackLocale = args.fallbackMap[fallbackLocale];
95
+ }
96
+ }
97
+ return Object.keys(args.inlinePlainTextByLocale).filter((locale) => !fallbackTargets.has(locale));
98
+ }
41
99
  const compileBundleFunction = (args) => {
42
100
  const inputs = args.bundle.declarations.filter((decl) => decl.type === "input-variable");
43
101
  const hasInputs = inputs.length > 0;
@@ -51,6 +109,7 @@ const compileBundleFunction = (args) => {
51
109
  baseLocale,
52
110
  ]
53
111
  : args.availableLocales;
112
+ const allStringBranchesInline = dispatchLocales.every((locale) => args.inlinePlainTextByLocale?.[locale] !== undefined);
54
113
  const inputType = args.inputTypeAliasName;
55
114
  const localesUnion = args.availableLocales.length === 0
56
115
  ? "never"
@@ -69,9 +128,16 @@ const compileBundleFunction = (args) => {
69
128
  const prefix = index > 0 ? continuationIndent : "";
70
129
  const messageRef = args.messageReferenceExpression(locale, args.bundle.id);
71
130
  if (mode === "string") {
131
+ const inlineText = args.inlinePlainTextByLocale?.[locale];
132
+ if (inlineText !== undefined) {
133
+ return `${prefix}${condition}return /** @type {LocalizedString} */ (${JSON.stringify(inlineText)})`;
134
+ }
72
135
  return `${prefix}${condition}return ${messageRef}(inputs)`;
73
136
  }
74
- return `${prefix}${condition}return typeof ${messageRef}.parts === "function" ? ${messageRef}.parts(inputs) : [{ type: "text", value: ${messageRef}(inputs) }]`;
137
+ // A locale's message has .parts() only if its own variants have
138
+ // markup. Fallbacks and messages without markup are typed without
139
+ // it, so read it through a type that declares it optional.
140
+ return `${prefix}${condition}return /** @type {{ parts?: (inputs: ${inputType}) => import('../runtime.js').MessagePart[] }} */ (${messageRef}).parts?.(inputs) ?? [{ type: "text", value: ${messageRef}(inputs) }]`;
75
141
  })
76
142
  .join("\n");
77
143
  const perLocaleBuild = args.experimentalStaticLocale === perLocaleBuildStaticLocaleExpression;
@@ -121,10 +187,13 @@ ${englishMatchTableDoc}${jsDocBundleFunctionTypes({
121
187
  }
122
188
  return `${indent}const locale = ${localeExpression}${overrideWarning}${maybeTrackMessageCall(indent)}`;
123
189
  };
190
+ const keepUnusedInputsParameter = (indent) => allStringBranchesInline && !args.experimentalMiddlewareLocaleSplitting
191
+ ? `\n${indent}void inputs;`
192
+ : "";
124
193
  let code = "";
125
194
  if (!args.hasMarkup) {
126
195
  code = `${commonJsDoc}
127
- ${isSafeBundleId ? "export " : ""}const ${safeBundleId} = /** @type {(${bundleFunctionType}) & ${messageMetadataType}} */ ((inputs${hasInputs ? "" : " = {}"}, options = {}) => {${clientMiddlewareGuard("\t")}
196
+ ${isSafeBundleId ? "export " : ""}const ${safeBundleId} = /** @type {(${bundleFunctionType}) & ${messageMetadataType}} */ ((inputs${hasInputs ? "" : " = {}"}, options = {}) => {${clientMiddlewareGuard("\t")}${keepUnusedInputsParameter("\t")}
128
197
  ${localeResolutionStatement("\t")}
129
198
  ${compileLocaleReturnStatements("string", "\t")}${!isFullyTranslated
130
199
  ? `\n return /** @type {LocalizedString} */ ("${args.bundle.id}")`
@@ -135,7 +204,7 @@ ${localeResolutionStatement("\t")}
135
204
  code = `${commonJsDoc}
136
205
  ${isSafeBundleId ? "export " : ""}const ${safeBundleId} = /** @type {(${bundleFunctionType}) & { parts: ${partsFunctionType} } & ${messageMetadataType}} */ (
137
206
  /* @__PURE__ */ Object.assign(
138
- /** @type {${bundleFunctionType}} */ ((inputs${hasInputs ? "" : " = {}"}, options = {}) => {${clientMiddlewareGuard("\t\t\t")}
207
+ /** @type {${bundleFunctionType}} */ ((inputs${hasInputs ? "" : " = {}"}, options = {}) => {${clientMiddlewareGuard("\t\t\t")}${keepUnusedInputsParameter("\t\t\t")}
139
208
  ${localeResolutionStatement("\t\t\t")}
140
209
  ${compileLocaleReturnStatements("string", "\t\t\t")}${!isFullyTranslated
141
210
  ? `\n return /** @type {LocalizedString} */ (${JSON.stringify(args.bundle.id)})`
@@ -421,6 +490,31 @@ function collectInputMatchTypes(bundle) {
421
490
  }
422
491
  }
423
492
  }
493
+ // Matches on un-annotated aliases of an input match the input value too.
494
+ // Widen an input type that was narrowed by direct matches: alias literals
495
+ // join the union and an alias catchall accepts any value. Inputs that are
496
+ // only matched through aliases keep their unnarrowed type.
497
+ const declarations = bundle.declarations ?? [];
498
+ for (const message of bundle.messages) {
499
+ for (const variant of message.variants) {
500
+ for (const match of variant.matches ?? []) {
501
+ if (inputNames.has(match.key))
502
+ continue;
503
+ const inputName = resolveInputAlias(match.key, declarations);
504
+ if (inputName === undefined)
505
+ continue;
506
+ const info = matchTypes.get(inputName);
507
+ if (!info)
508
+ continue;
509
+ if (match.type === "catchall-match") {
510
+ info.hasCatchAll = true;
511
+ }
512
+ else if (match.type === "literal-match") {
513
+ info.literals.add(match.value);
514
+ }
515
+ }
516
+ }
517
+ }
424
518
  return matchTypes;
425
519
  }
426
520
  function bundleHasMarkup(bundle) {