fhirsmith 0.3.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 (277) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/FHIRsmith.png +0 -0
  3. package/README.md +277 -0
  4. package/config-template.json +144 -0
  5. package/library/folder-setup.js +58 -0
  6. package/library/html-server.js +166 -0
  7. package/library/html.js +835 -0
  8. package/library/i18nsupport.js +259 -0
  9. package/library/languages.js +779 -0
  10. package/library/logger-telnet.js +205 -0
  11. package/library/logger.js +279 -0
  12. package/library/package-manager.js +876 -0
  13. package/library/utilities.js +196 -0
  14. package/library/version-utilities.js +1056 -0
  15. package/npmprojector/config-example.json +13 -0
  16. package/npmprojector/indexer.js +394 -0
  17. package/npmprojector/npmprojector.js +395 -0
  18. package/npmprojector/readme.md +174 -0
  19. package/npmprojector/watcher.js +335 -0
  20. package/package.json +119 -0
  21. package/packages/package-crawler.js +846 -0
  22. package/packages/packages-template.html +126 -0
  23. package/packages/packages.js +2838 -0
  24. package/passwords.ini +2 -0
  25. package/publisher/publisher-template.html +208 -0
  26. package/publisher/publisher.js +2167 -0
  27. package/publisher/task-draft.js +458 -0
  28. package/registry/api.js +735 -0
  29. package/registry/crawler.js +637 -0
  30. package/registry/model.js +513 -0
  31. package/registry/readme.md +243 -0
  32. package/registry/registry-data.json +121015 -0
  33. package/registry/registry-template.html +126 -0
  34. package/registry/registry.js +1395 -0
  35. package/registry/test-runner.js +237 -0
  36. package/root-template.html +124 -0
  37. package/server.js +524 -0
  38. package/shl/private-key.pem +5 -0
  39. package/shl/public-key.pem +18 -0
  40. package/shl/shl.js +1125 -0
  41. package/shl/vhl.js +69 -0
  42. package/static/FHIRsmith128.png +0 -0
  43. package/static/FHIRsmith16.png +0 -0
  44. package/static/FHIRsmith32.png +0 -0
  45. package/static/FHIRsmith64.png +0 -0
  46. package/static/assets/css/bootstrap-fhir.css +5302 -0
  47. package/static/assets/css/bootstrap-glyphicons.css +2 -0
  48. package/static/assets/css/bootstrap.css +4097 -0
  49. package/static/assets/css/jquery-ui.css +523 -0
  50. package/static/assets/css/jquery-ui.structure.css +863 -0
  51. package/static/assets/css/jquery-ui.structure.min.css +5 -0
  52. package/static/assets/css/jquery-ui.theme.css +439 -0
  53. package/static/assets/css/jquery-ui.theme.min.css +5 -0
  54. package/static/assets/css/jquery.ui.all.css +7 -0
  55. package/static/assets/css/modules.css +18 -0
  56. package/static/assets/css/project.css +367 -0
  57. package/static/assets/css/pygments-manni.css +66 -0
  58. package/static/assets/css/tags.css +74 -0
  59. package/static/assets/css/xml.css +2 -0
  60. package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
  61. package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
  62. package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
  63. package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
  64. package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
  65. package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
  66. package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
  67. package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
  68. package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
  69. package/static/assets/ico/favicon.ico +0 -0
  70. package/static/assets/ico/favicon.png +0 -0
  71. package/static/assets/images/fhir-logo-www.png +0 -0
  72. package/static/assets/images/fhir-logo.png +0 -0
  73. package/static/assets/images/hl7-logo.png +0 -0
  74. package/static/assets/images/logo_ansinew.jpg +0 -0
  75. package/static/assets/images/search.png +0 -0
  76. package/static/assets/images/stripe.png +0 -0
  77. package/static/assets/images/target.png +0 -0
  78. package/static/assets/images/tx-registry-root.gif +0 -0
  79. package/static/assets/images/tx-registry.png +0 -0
  80. package/static/assets/images/tx-server.png +0 -0
  81. package/static/assets/images/tx-version.png +0 -0
  82. package/static/assets/js/bootstrap.min.js +6 -0
  83. package/static/assets/js/fhir-gw.js +259 -0
  84. package/static/assets/js/fhir.js +2 -0
  85. package/static/assets/js/html5shiv.js +8 -0
  86. package/static/assets/js/jcookie.js +96 -0
  87. package/static/assets/js/jquery-ui.min.js +6 -0
  88. package/static/assets/js/jquery.js +10716 -0
  89. package/static/assets/js/jquery.min.js +2 -0
  90. package/static/assets/js/jquery.ui.core.js +314 -0
  91. package/static/assets/js/jquery.ui.draggable.js +825 -0
  92. package/static/assets/js/jquery.ui.mouse.js +162 -0
  93. package/static/assets/js/jquery.ui.resizable.js +842 -0
  94. package/static/assets/js/jquery.ui.widget.js +268 -0
  95. package/static/assets/js/json2.js +487 -0
  96. package/static/assets/js/jtip.js +97 -0
  97. package/static/assets/js/respond.min.js +6 -0
  98. package/static/assets/js/statuspage.js +70 -0
  99. package/static/assets/js/xml.js +2 -0
  100. package/static/dist/js/bootstrap.js +1964 -0
  101. package/static/favicon.png +0 -0
  102. package/static/fhir.css +626 -0
  103. package/static/icon-fhir-16.png +0 -0
  104. package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  105. package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  106. package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
  107. package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  108. package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  109. package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  110. package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  111. package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  112. package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  113. package/static/images/ui-icons_222222_256x240.png +0 -0
  114. package/static/images/ui-icons_228ef1_256x240.png +0 -0
  115. package/static/images/ui-icons_ef8c08_256x240.png +0 -0
  116. package/static/images/ui-icons_ffd27a_256x240.png +0 -0
  117. package/static/images/ui-icons_ffffff_256x240.png +0 -0
  118. package/static/js/jquery.effects.blind.js +49 -0
  119. package/static/js/jquery.effects.bounce.js +78 -0
  120. package/static/js/jquery.effects.clip.js +54 -0
  121. package/static/js/jquery.effects.core.js +763 -0
  122. package/static/js/jquery.effects.drop.js +50 -0
  123. package/static/js/jquery.effects.explode.js +79 -0
  124. package/static/js/jquery.effects.fade.js +32 -0
  125. package/static/js/jquery.effects.fold.js +56 -0
  126. package/static/js/jquery.effects.highlight.js +50 -0
  127. package/static/js/jquery.effects.pulsate.js +51 -0
  128. package/static/js/jquery.effects.scale.js +178 -0
  129. package/static/js/jquery.effects.shake.js +57 -0
  130. package/static/js/jquery.effects.slide.js +50 -0
  131. package/static/js/jquery.effects.transfer.js +45 -0
  132. package/static/js/jquery.ui.accordion.js +611 -0
  133. package/static/js/jquery.ui.autocomplete.js +612 -0
  134. package/static/js/jquery.ui.button.js +416 -0
  135. package/static/js/jquery.ui.datepicker.js +1823 -0
  136. package/static/js/jquery.ui.dialog.js +878 -0
  137. package/static/js/jquery.ui.droppable.js +296 -0
  138. package/static/js/jquery.ui.position.js +252 -0
  139. package/static/js/jquery.ui.progressbar.js +109 -0
  140. package/static/js/jquery.ui.selectable.js +266 -0
  141. package/static/js/jquery.ui.slider.js +666 -0
  142. package/static/js/jquery.ui.sortable.js +1077 -0
  143. package/static/js/jquery.ui.tabs.js +758 -0
  144. package/stats.js +80 -0
  145. package/test-cache/vsac/vsac-valuesets.db +0 -0
  146. package/token/nginx_passport_setup.md +383 -0
  147. package/token/security_guide.md +294 -0
  148. package/token/token-template.html +330 -0
  149. package/token/token.js +1300 -0
  150. package/translations/Messages.properties +1510 -0
  151. package/translations/Messages_ar.properties +1399 -0
  152. package/translations/Messages_de.properties +836 -0
  153. package/translations/Messages_es.properties +737 -0
  154. package/translations/Messages_fr.properties +1 -0
  155. package/translations/Messages_ja.properties +893 -0
  156. package/translations/Messages_nl.properties +1357 -0
  157. package/translations/Messages_pt.properties +1302 -0
  158. package/translations/Messages_ru.properties +1 -0
  159. package/translations/Messages_uz.properties +1 -0
  160. package/translations/Messages_zh.properties +1 -0
  161. package/translations/rendering-phrases.properties +1128 -0
  162. package/translations/rendering-phrases_ar.properties +1091 -0
  163. package/translations/rendering-phrases_de.properties +6 -0
  164. package/translations/rendering-phrases_es.properties +6 -0
  165. package/translations/rendering-phrases_fr.properties +624 -0
  166. package/translations/rendering-phrases_ja.properties +21 -0
  167. package/translations/rendering-phrases_nl.properties +970 -0
  168. package/translations/rendering-phrases_pt.properties +1020 -0
  169. package/translations/rendering-phrases_ru.properties +1094 -0
  170. package/translations/rendering-phrases_uz.properties +1 -0
  171. package/translations/rendering-phrases_zh.properties +1 -0
  172. package/tx/README.md +418 -0
  173. package/tx/cm/cm-api.js +110 -0
  174. package/tx/cm/cm-database.js +735 -0
  175. package/tx/cm/cm-package.js +325 -0
  176. package/tx/cs/cs-api.js +789 -0
  177. package/tx/cs/cs-areacode.js +615 -0
  178. package/tx/cs/cs-country.js +1110 -0
  179. package/tx/cs/cs-cpt.js +785 -0
  180. package/tx/cs/cs-cs.js +1579 -0
  181. package/tx/cs/cs-currency.js +539 -0
  182. package/tx/cs/cs-db.js +1321 -0
  183. package/tx/cs/cs-hgvs.js +329 -0
  184. package/tx/cs/cs-lang.js +465 -0
  185. package/tx/cs/cs-loinc.js +1485 -0
  186. package/tx/cs/cs-mimetypes.js +238 -0
  187. package/tx/cs/cs-ndc.js +704 -0
  188. package/tx/cs/cs-omop.js +1025 -0
  189. package/tx/cs/cs-provider-api.js +43 -0
  190. package/tx/cs/cs-provider-list.js +37 -0
  191. package/tx/cs/cs-rxnorm.js +808 -0
  192. package/tx/cs/cs-snomed.js +1102 -0
  193. package/tx/cs/cs-ucum.js +514 -0
  194. package/tx/cs/cs-unii.js +271 -0
  195. package/tx/cs/cs-uri.js +218 -0
  196. package/tx/cs/cs-usstates.js +305 -0
  197. package/tx/dev.fhir.org.yml +14 -0
  198. package/tx/fixtures/test-cases-setup.json +18 -0
  199. package/tx/fixtures/test-cases.yml +16 -0
  200. package/tx/html/codesystem-operations.liquid +25 -0
  201. package/tx/html/home-metrics.liquid +247 -0
  202. package/tx/html/operations-form.liquid +148 -0
  203. package/tx/html/search-form.liquid +62 -0
  204. package/tx/html/tx-template.html +133 -0
  205. package/tx/html/valueset-operations.liquid +54 -0
  206. package/tx/importers/atc-to-fhir.js +316 -0
  207. package/tx/importers/import-loinc.module.js +1536 -0
  208. package/tx/importers/import-ndc.module.js +1088 -0
  209. package/tx/importers/import-rxnorm.module.js +898 -0
  210. package/tx/importers/import-sct.module.js +2457 -0
  211. package/tx/importers/import-unii.module.js +601 -0
  212. package/tx/importers/readme.md +453 -0
  213. package/tx/importers/subset-loinc.module.js +1081 -0
  214. package/tx/importers/subset-rxnorm.module.js +938 -0
  215. package/tx/importers/tx-import-base.js +351 -0
  216. package/tx/importers/tx-import-settings.js +310 -0
  217. package/tx/importers/tx-import.js +357 -0
  218. package/tx/library/canonical-resource.js +88 -0
  219. package/tx/library/capabilitystatement.js +292 -0
  220. package/tx/library/codesystem.js +774 -0
  221. package/tx/library/conceptmap.js +568 -0
  222. package/tx/library/designations.js +932 -0
  223. package/tx/library/errors.js +77 -0
  224. package/tx/library/extensions.js +117 -0
  225. package/tx/library/namingsystem.js +322 -0
  226. package/tx/library/operation-outcome.js +127 -0
  227. package/tx/library/parameters.js +105 -0
  228. package/tx/library/renderer.js +1559 -0
  229. package/tx/library/terminologycapabilities.js +418 -0
  230. package/tx/library/ucum-parsers.js +1029 -0
  231. package/tx/library/ucum-service.js +370 -0
  232. package/tx/library/ucum-types.js +1099 -0
  233. package/tx/library/valueset.js +543 -0
  234. package/tx/library.js +676 -0
  235. package/tx/ocl/cm-ocl.js +106 -0
  236. package/tx/ocl/cs-ocl.js +39 -0
  237. package/tx/ocl/vs-ocl.js +105 -0
  238. package/tx/operation-context.js +568 -0
  239. package/tx/params.js +613 -0
  240. package/tx/provider.js +403 -0
  241. package/tx/sct/ecl.js +1560 -0
  242. package/tx/sct/expressions.js +2077 -0
  243. package/tx/sct/structures.js +1396 -0
  244. package/tx/tx-html.js +1063 -0
  245. package/tx/tx.fhir.org.yml +39 -0
  246. package/tx/tx.js +927 -0
  247. package/tx/vs/vs-api.js +112 -0
  248. package/tx/vs/vs-database.js +786 -0
  249. package/tx/vs/vs-package.js +358 -0
  250. package/tx/vs/vs-vsac.js +366 -0
  251. package/tx/workers/batch-validate.js +129 -0
  252. package/tx/workers/batch.js +361 -0
  253. package/tx/workers/closure.js +32 -0
  254. package/tx/workers/expand.js +1845 -0
  255. package/tx/workers/lookup.js +407 -0
  256. package/tx/workers/metadata.js +467 -0
  257. package/tx/workers/operations.js +34 -0
  258. package/tx/workers/read.js +164 -0
  259. package/tx/workers/search.js +384 -0
  260. package/tx/workers/subsumes.js +334 -0
  261. package/tx/workers/translate.js +492 -0
  262. package/tx/workers/validate.js +2504 -0
  263. package/tx/workers/worker.js +904 -0
  264. package/tx/xml/capabilitystatement-xml.js +63 -0
  265. package/tx/xml/codesystem-xml.js +62 -0
  266. package/tx/xml/conceptmap-xml.js +65 -0
  267. package/tx/xml/namingsystem-xml.js +65 -0
  268. package/tx/xml/operationoutcome-xml.js +127 -0
  269. package/tx/xml/parameters-xml.js +312 -0
  270. package/tx/xml/terminologycapabilities-xml.js +64 -0
  271. package/tx/xml/valueset-xml.js +64 -0
  272. package/tx/xml/xml-base.js +603 -0
  273. package/vcl/vcl-parser.js +1098 -0
  274. package/vcl/vcl.js +253 -0
  275. package/windows-install.js +19 -0
  276. package/xig/xig-template.html +124 -0
  277. package/xig/xig.js +3049 -0
@@ -0,0 +1,259 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+ const { getProperties } = require("properties-file");
4
+ const {validateParameter} = require("./utilities");
5
+ const {LanguageDefinitions} = require("./languages");
6
+
7
+ /**
8
+ * Internationalization support for loading Java properties files
9
+ */
10
+ class I18nSupport {
11
+ constructor(translationsPath, languageDefinitions) {
12
+ validateParameter(translationsPath, "translationsPath", String);
13
+ validateParameter(languageDefinitions, "languageDefinitions", LanguageDefinitions);
14
+ this.translationsPath = translationsPath;
15
+ this.languageDefinitions = languageDefinitions;
16
+ this.bundles = new Map(); // Cache for loaded message bundles by language code
17
+ this.phrases = new Map(); // Cache for loaded message bundles by language code
18
+ }
19
+
20
+ /**
21
+ * Load all available message bundles from the translations directory
22
+ */
23
+ async load() {
24
+ await this._loadResourceBundle(this.bundles, 'Messages');
25
+ await this._loadResourceBundle(this.phrases, 'rendering-phrases');
26
+ }
27
+
28
+ async _loadResourceBundle(bundles, name) {
29
+ // Load default Messages.properties first
30
+ await this._loadBundle(bundles, 'en', name+'.properties');
31
+
32
+ // Scan for Messages_*.properties files
33
+ try {
34
+ const files = fs.readdirSync(this.translationsPath);
35
+ const messageFiles = files.filter(file =>
36
+ file.startsWith(name+'_') && file.endsWith('.properties')
37
+ );
38
+
39
+ for (const file of messageFiles) {
40
+ // Extract language code from filename: Messages_fr_FR.properties -> fr-FR
41
+ const langCode = file
42
+ .substring(name+'_'.length, file.length - '.properties'.length)
43
+ .replace(/_/g, '-');
44
+
45
+ await this._loadBundle(bundles, langCode, file);
46
+ }
47
+ } catch (error) {
48
+ throw new Error(`Failed to scan translations directory: ${error.message}`);
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Load a specific message bundle file
54
+ */
55
+ async _loadBundle(bundles, langCode, filename) {
56
+ try {
57
+ const filePath = path.join(this.translationsPath, filename);
58
+ const content = fs.readFileSync(filePath, 'utf8');
59
+ const properties = getProperties(content);
60
+
61
+ bundles.set(langCode, properties);
62
+ } catch (error) {
63
+ // Don't throw for missing files - just skip them
64
+ console.warn(`Warning: Could not load ${filename}: ${error.message}`);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Format a message with parameter substitution
70
+ * @param {Languages} languages - Languages object with preference order
71
+ * @param {string} messageId - Message key from properties file
72
+ * @param {Array} parameters - Parameters for {0}, {1}, etc. substitution
73
+ * @returns {string} Formatted message
74
+ */
75
+ formatMessage(languages, messageId, parameters = []) {
76
+ return this._formatMessageFromBundle(this.bundles, languages, messageId, parameters);
77
+ }
78
+
79
+ translate(messageId, languages, parameters = []) {
80
+ return this.formatMessage(languages, messageId, parameters);
81
+ }
82
+
83
+ translatePlural(count, messageId, languages, parameters = []) {
84
+ return this.formatMessagePlural(languages, messageId, count, parameters);
85
+ }
86
+
87
+ /**
88
+ * Format a message with pluralization support
89
+ * @param {Languages} languages - Languages object with preference order
90
+ * @param {string} messageId - Base message key from properties file
91
+ * @param {number} count - Count for pluralization (becomes {0} in final message)
92
+ * @param {Array} parameters - Additional parameters for {1}, {2}, etc. substitution
93
+ * @returns {string} Formatted message
94
+ */
95
+ formatMessagePlural(languages, messageId, count, parameters = []) {
96
+ return this._formatMessagePluralFromBundle(this.bundles, languages, messageId, count, parameters);
97
+ }
98
+
99
+ /**
100
+ * Format a message with parameter substitution
101
+ * @param {Languages} languages - Languages object with preference order
102
+ * @param {string} messageId - Message key from properties file
103
+ * @param {Array} parameters - Parameters for {0}, {1}, etc. substitution
104
+ * @returns {string} Formatted message
105
+ */
106
+ formatPhrase(messageId, languages, parameters = []) {
107
+ return this._formatMessageFromBundle(this.phrases, languages, messageId, parameters);
108
+ }
109
+
110
+ translatePhrase(messageId, languages, parameters = []) {
111
+ return this.formatPhrase(languages, messageId, parameters);
112
+ }
113
+
114
+ translatePhrasePlural(count, messageId, languages, parameters = []) {
115
+ return this.formatPhrasePlural(languages, messageId, count, parameters);
116
+ }
117
+
118
+ /**
119
+ * Format a message with pluralization support
120
+ * @param {Languages} languages - Languages object with preference order
121
+ * @param {string} messageId - Base message key from properties file
122
+ * @param {number} count - Count for pluralization (becomes {0} in final message)
123
+ * @param {Array} parameters - Additional parameters for {1}, {2}, etc. substitution
124
+ * @returns {string} Formatted message
125
+ */
126
+ formatPhrasePlural(messageId, languages, count, parameters = []) {
127
+ return this._formatMessagePluralFromBundle(this.phrases, languages, messageId, count, parameters);
128
+ }
129
+
130
+ _formatMessageFromBundle(bundles, languages, messageId, parameters = []) {
131
+ // Find the best language bundle that has this message
132
+ const message = this._findMessage(bundles, languages, messageId);
133
+
134
+ if (!message) {
135
+ return messageId; // Fallback to message ID if not found
136
+ }
137
+
138
+ // Substitute parameters {0}, {1}, etc.
139
+ return this._substituteParameters(message.trim(), parameters).replaceAll("''", "'");
140
+ }
141
+
142
+ _formatMessagePluralFromBundle(bundles, languages, messageId, count, parameters = []) {
143
+ // Determine plural form suffix
144
+ const pluralSuffix = count === 1 ? '_one' : '_other';
145
+ const pluralMessageId = messageId + pluralSuffix;
146
+
147
+ // Try to find the plural-specific message first
148
+ let message = this._findMessage(bundles, languages, pluralMessageId);
149
+
150
+ // If not found, fall back to the base message
151
+ if (!message) {
152
+ message = this._findMessage(bundles, languages, messageId);
153
+ }
154
+
155
+ if (!message) {
156
+ return messageId; // Fallback to message ID if not found
157
+ }
158
+
159
+ // Prepend count as parameter 0, shift other parameters
160
+ const allParameters = [count.toString(), ...parameters];
161
+
162
+ // Substitute parameters {0}, {1}, etc.
163
+ return this._substituteParameters(message, allParameters).replaceAll("''", "'");
164
+ }
165
+
166
+ /**
167
+ * Find message in language bundles with fallback logic
168
+ */
169
+ _findMessage(bundles, languages, messageId) {
170
+ // Try each language in preference order
171
+ if (languages) {
172
+ for (const language of languages) {
173
+ const message = this._getMessageForLanguage(bundles, language.code, messageId);
174
+ if (message) {
175
+ return message;
176
+ }
177
+
178
+ // Try language without region (e.g., 'fr' for 'fr-FR')
179
+ if (language.language && language.language !== language.code) {
180
+ const message = this._getMessageForLanguage(bundles, language.language, messageId);
181
+ if (message) {
182
+ return message;
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ // Final fallback to English
189
+ return this._getMessageForLanguage(bundles,'en', messageId);
190
+ }
191
+
192
+ /**
193
+ * Get message for specific language code
194
+ */
195
+ _getMessageForLanguage(bundles, langCode, messageId) {
196
+ const bundle = bundles.get(langCode);
197
+ return bundle ? bundle[messageId] : null;
198
+ }
199
+
200
+ /**
201
+ * Substitute parameters in message string
202
+ * Replaces {0}, {1}, etc. with provided parameters
203
+ */
204
+ _substituteParameters(message, parameters) {
205
+ if (!parameters || parameters.length === 0) {
206
+ return message;
207
+ }
208
+
209
+ return message.replace(/\{(\d+)\}/g, (match, index) => {
210
+ const paramIndex = parseInt(index);
211
+ return paramIndex < parameters.length ? parameters[paramIndex] : match;
212
+ });
213
+ }
214
+
215
+ /**
216
+ * Get all available language codes
217
+ */
218
+ getAvailableLanguages() {
219
+ return Array.from(this.bundles.keys());
220
+ }
221
+
222
+ /**
223
+ * Check if a message exists for any language
224
+ */
225
+ hasMessage(messageId) {
226
+ for (const bundle of this.bundles.values()) {
227
+ if (bundle[messageId]) {
228
+ return true;
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+
234
+ /**
235
+ * Check if a message exists for any language
236
+ */
237
+ hasPhrase(messageId) {
238
+ for (const bundle of this.phrases.values()) {
239
+ if (bundle[messageId]) {
240
+ return true;
241
+ }
242
+ }
243
+ return false;
244
+ }
245
+
246
+ /**
247
+ * Create a linked copy of this I18nSupport instance
248
+ */
249
+ link() {
250
+ const copy = new I18nSupport(this.translationsPath, this.languageDefinitions);
251
+ copy.bundles = new Map(this.bundles); // Shallow copy of bundles map
252
+ copy.phrases = new Map(this.phrases); // Shallow copy of bundles map
253
+ return copy;
254
+ }
255
+ }
256
+
257
+ module.exports = {
258
+ I18nSupport
259
+ };