• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / blurts-server / 888b0fd0-c8ae-440d-8d0a-f67e71a46a26

pending completion
888b0fd0-c8ae-440d-8d0a-f67e71a46a26

push

circleci

GitHub
Merge pull request #2905 from mozilla/MNTOR-1316-lost-locale

282 of 1435 branches covered (19.65%)

Branch coverage included in aggregate %.

5 of 5 new or added lines in 2 files covered. (100.0%)

959 of 3916 relevant lines covered (24.49%)

4.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/utils/fluent.js
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4

5
import { join, resolve } from 'node:path'
6
import { readdirSync } from 'node:fs'
7
import { readFile } from 'node:fs/promises'
8
import { FluentBundle, FluentResource } from '@fluent/bundle'
9
import { negotiateLanguages } from '@fluent/langneg'
10
import AppConstants from '../app-constants.js'
11
import { localStorage } from './local-storage.js'
12

13
const supportedLocales = AppConstants.SUPPORTED_LOCALES?.split(',')
×
14
const fluentBundles = {}
×
15

16
/**
17
 * Create Fluent bundles for all supported locales.
18
 * Reads .ftl files in parallel for better server start performance.
19
 */
20
async function initFluentBundles () {
21
  const promises = supportedLocales.map(async locale => {
×
22
    const bundle = new FluentBundle(locale, { useIsolating: false })
×
23
    const dirname = resolve('../locales', locale)
×
24

25
    try {
×
26
      const filenames = readdirSync(dirname).filter(item => item.endsWith('.ftl'))
×
27

28
      await Promise.all(filenames.map(async filename => {
×
29
        const str = await readFile(join(dirname, filename), 'utf8')
×
30

31
        bundle.addResource(new FluentResource(str))
×
32
      }))
33
    } catch (e) {
34
      console.error('Could not read Fluent file:', e)
×
35
      throw new Error(e)
×
36
    }
37

38
    fluentBundles[locale] = bundle
×
39
  })
40

41
  await Promise.allSettled(promises)
×
42

43
  console.log('Fluent bundles created:', Object.keys(fluentBundles))
×
44
}
45

46
/**
47
 * Set the locale used for translations negotiated between requested and available
48
 *
49
 * @param {Array} requestedLocales - Locales requested by client.
50
 */
51
function updateLocale (requestedLocales) {
52
  return negotiateLanguages(
×
53
    requestedLocales,
54
    supportedLocales,
55
    { strategy: 'lookup', defaultLocale: 'en' }
56
  )
57
}
58

59
/**
60
 * Return the locale negotiated between requested and supported locales.
61
 * Default 'en' if localStorage hasn't initialized (called without client request)
62
 */
63
function getLocale () {
64
  return localStorage.getStore()?.get('locale') || ['en']
×
65
}
66

67
/**
68
 * Translate a message and return the raw string
69
 * Defaults to en if message id not found in requested locale
70
 *
71
 * @param {string} id - The Fluent message id.
72
 */
73
function getRawMessage (id) {
74
  let bundle = fluentBundles[getLocale()]
×
75

76
  if (!bundle.hasMessage(id)) bundle = fluentBundles.en
×
77

78
  if (bundle.hasMessage(id)) return bundle.getMessage(id).value
×
79

80
  return id
×
81
}
82

83
/**
84
 * Translate and transform a message pattern with current locale
85
 * Defaults to en if message id not found in requested locale
86
 *
87
 * @param {string} id - The Fluent message id.
88
 * @param {object} args - key/value pairs corresponding to pattern in Fluent resource.
89
 * @example
90
 * // Given FluentResource("hello = Hello, {$name}!")
91
 * getMessage (hello, {name: "Jane"})
92
 * // Returns "Hello, Jane!"
93
 */
94
function getMessage (id, args) {
95
  return getMessageWithLocale(id, getLocale(), args)
×
96
}
97

98
/**
99
 * Translate and transform a message pattern
100
 * Can pass in any locale
101
 * Defaults to en if message id not found in requested locale
102
 *
103
 * @param {string} id - The Fluent message id.
104
 * @param {string{}} localePreferences
105
 * @param {object} args - key/value pairs corresponding to pattern in Fluent resource.
106
 * @example
107
 * // Given FluentResource("hello = Hello, {$name}!")
108
 * getMessage (hello, {name: "Jane"})
109
 * // Returns "Hello, Jane!"
110
 */
111
function getMessageWithLocale (id, localePreferences, args) {
112
  let bundle = fluentBundles[localePreferences[0]]
×
113

114
  if (!bundle.hasMessage(id)) bundle = fluentBundles.en
×
115

116
  if (bundle.hasMessage(id)) return bundle.formatPattern(bundle.getMessage(id).value, args)
×
117

118
  return id
×
119
}
120

121
function fluentError (id) {
122
  return new Error(getMessage(id))
×
123
}
124

125
export { initFluentBundles, updateLocale, getLocale, getMessage, getMessageWithLocale, getRawMessage, fluentError }
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc