@museumwnf/viewer-core 2.13.0 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@museumwnf/viewer-core",
3
- "version": "2.13.0",
3
+ "version": "2.15.0",
4
4
  "description": "Application engine for MWNF websites: turns a dataset.config.js into a mounted Vue application.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -56,15 +56,18 @@ function byStartDate(a, b) {
56
56
  }
57
57
 
58
58
  /**
59
- * `fields`: `{ [field]: (record, text) => string | string[] }`, what each
60
- * field of the form searches in a record and one of its translations.
59
+ * `fields`: `{ [field]: (record, text, language) => string | string[] }`,
60
+ * what each field of the form searches in a record and one of its
61
+ * translations, given that translation's language (so a field can read a
62
+ * related record's text in the same language, as legacy's joins did).
61
63
  * `countryFields`: the fields in which a word naming a country (in any of
62
64
  * its names) also finds the records of that country.
63
65
  * `glossaryFields`: the fields in which a glossary term also searches its
64
66
  * spellings in the site's language.
65
- * `score`: `{ [field]: (like, record, text, inCountry) => number }`, the
66
- * relevance a matching row adds, `like(value)` telling whether a value holds
67
- * the row's own word. Summed over the rows, the best translation counting.
67
+ * `score`: `{ [field]: (like, record, text, inCountry, language) => number }`,
68
+ * the relevance a matching row adds, `like(value)` telling whether a value
69
+ * holds the row's own word. Summed over the rows, the best translation
70
+ * counting.
68
71
  *
69
72
  * Returns `narrow(list, filters, helpers)` for a results spec (pass it
70
73
  * `sort: false`): the records of `list` that match, in legacy's order.
@@ -88,13 +91,13 @@ export function useLegacySearch({
88
91
  // translation object, so a file loaded again starts afresh.
89
92
  const folded = new WeakMap()
90
93
 
91
- function hayOf(field, record, text) {
94
+ function hayOf(field, record, text, language) {
92
95
  let byField = folded.get(text)
93
96
  if (!byField) folded.set(text, (byField = new Map()))
94
97
  let hay = byField.get(field)
95
98
  if (!hay) {
96
99
  const getter = fields[field] ?? fields.keyword
97
- byField.set(field, (hay = strings(getter(record, text)).map(collate)))
100
+ byField.set(field, (hay = strings(getter(record, text, language)).map(collate)))
98
101
  }
99
102
  return hay
100
103
  }
@@ -161,13 +164,13 @@ export function useLegacySearch({
161
164
  let relevance = 0
162
165
  for (const row of active) {
163
166
  const inCountry = row.countries.has(record.country_id)
164
- const hay = hayOf(row.field, record, text)
167
+ const hay = hayOf(row.field, record, text, language)
165
168
  const hit = inCountry || hay.some((h) => row.needles.some((n) => h.includes(n)))
166
169
  if (result === null) result = hit
167
170
  else if (String(row.cond).toUpperCase() === 'OR') result = result || hit
168
171
  else result = result && hit
169
172
  const rank = score[row.field]
170
- if (rank) relevance += rank((value) => strings(value).some((v) => collate(v).includes(row.needle)), record, text, inCountry)
173
+ if (rank) relevance += rank((value) => strings(value).some((v) => collate(v).includes(row.needle)), record, text, inCountry, language)
171
174
  }
172
175
  if (result === null || result) best = Math.max(best ?? 0, relevance)
173
176
  }
package/src/i18n/index.js CHANGED
@@ -47,18 +47,23 @@ export function mergeMessages(...catalogues) {
47
47
  export function createI18n({ messages = {}, locale = BASE_LANGUAGE } = {}) {
48
48
  const current = ref(locale)
49
49
 
50
- const t = (key) => {
51
- const active = messages[current.value]
50
+ // A text in a language of the caller's choosing (a record's own, say),
51
+ // English where that language has none.
52
+ const tIn = (language, key) => {
53
+ const active = messages[language]
52
54
  if (active && typeof active[key] === 'string') return active[key]
53
55
  const base = messages[BASE_LANGUAGE]
54
56
  if (base && typeof base[key] === 'string') return base[key]
55
57
  return key
56
58
  }
57
59
 
60
+ const t = (key) => tIn(current.value, key)
61
+
58
62
  const i18n = {
59
63
  locale: current,
60
64
  availableLocales: Object.keys(messages),
61
65
  t,
66
+ tIn,
62
67
  install(app) {
63
68
  app.provide(VIEWER_I18N, i18n)
64
69
  // `$t` in a template, `useI18n()` in a script — the two shapes Vue
@@ -54,6 +54,9 @@ export function defineViewerConfig({
54
54
  test: {
55
55
  environment: 'jsdom',
56
56
  testTimeout: 60000,
57
+ // Node 26 has a `localStorage` of its own, which hides jsdom's from the
58
+ // tests: without a storage file it is undefined.
59
+ execArgv: ['--no-experimental-webstorage'],
57
60
  server: {
58
61
  deps: {
59
62
  // viewer-core ships .vue source; Node cannot load it unless Vitest