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

mozilla / blurts-server / fc5fd966-32ac-4dce-a653-c3228ccbd2bc

pending completion
fc5fd966-32ac-4dce-a653-c3228ccbd2bc

Pull #2762

circleci

GitHub
Merge pull request #2751 from mozilla/MNTOR-1025
Pull Request #2762: Merge `main` into `localization`

282 of 1122 branches covered (25.13%)

Branch coverage included in aggregate %.

203 of 203 new or added lines in 19 files covered. (100.0%)

959 of 3014 relevant lines covered (31.82%)

5.13 hits per line

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

0.0
/src/utils/fluent.js
1
import { join, resolve } from 'node:path'
2
import { readdirSync } from 'node:fs'
3
import { readFile } from 'node:fs/promises'
4
import { FluentBundle, FluentResource } from '@fluent/bundle'
5
import { negotiateLanguages } from '@fluent/langneg'
6
import AppConstants from '../app-constants.js'
7
import { localStorage } from './local-storage.js'
8

9
const supportedLocales = AppConstants.SUPPORTED_LOCALES?.split(',')
×
10
const fluentBundles = {}
×
11

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

21
    try {
×
22
      const filenames = readdirSync(dirname).filter(item => item.endsWith('.ftl'))
×
23

24
      await Promise.all(filenames.map(async filename => {
×
25
        const str = await readFile(join(dirname, filename), 'utf8')
×
26

27
        bundle.addResource(new FluentResource(str))
×
28
      }))
29
    } catch (e) {
30
      console.error('Could not read Fluent file:', e)
×
31
      throw new Error(e)
×
32
    }
33

34
    fluentBundles[locale] = bundle
×
35
  })
36

37
  await Promise.allSettled(promises)
×
38

39
  console.log('Fluent bundles created:', Object.keys(fluentBundles))
×
40
}
41

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

54
/**
55
* Return the locale negotiated between requested and supported locales.
56
* Default 'en' if localStorage hasn't initialized (called without client request)
57
*/
58
function getLocale () {
59
  return localStorage.getStore()?.get('locale') || 'en'
×
60
}
61

62
/**
63
* Translate a message and return the raw string
64
* Defaults to en if message id not found in requested locale
65
* @param {string} id - The Fluent message id.
66
*/
67
function getRawMessage (id) {
68
  let bundle = fluentBundles[getLocale()]
×
69

70
  if (!bundle.hasMessage(id)) bundle = fluentBundles.en
×
71

72
  if (bundle.hasMessage(id)) return bundle.getMessage(id).value
×
73

74
  return id
×
75
}
76

77
/**
78
* Translate and transform a message pattern
79
* Defaults to en if message id not found in requested locale
80
* @param {string} id - The Fluent message id.
81
* @param {object} args - key/value pairs corresponding to pattern in Fluent resource.
82
* @example
83
* // Given FluentResource("hello = Hello, {$name}!")
84
* getMessage (hello, {name: "Jane"})
85
* // Returns "Hello, Jane!"
86
*/
87
function getMessage (id, args) {
88
  let bundle = fluentBundles[getLocale()]
×
89

90
  if (!bundle.hasMessage(id)) bundle = fluentBundles.en
×
91

92
  if (bundle.hasMessage(id)) return bundle.formatPattern(bundle.getMessage(id).value, args)
×
93

94
  return id
×
95
}
96

97
function fluentError (id) {
98
  return new Error(getMessage(id))
×
99
}
100

101
export { initFluentBundles, updateLocale, getLocale, getMessage, 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