1 |
|
2 | 'use strict'
|
3 |
|
4 | var assign = require('object-assign')
|
5 | var parse = require('format-message-parse')
|
6 | var interpret = require('format-message-interpret')
|
7 | var plurals = require('format-message-interpret/plurals')
|
8 | var lookupClosestLocale = require('lookup-closest-locale')
|
9 | var formats = require('format-message-formats')
|
10 |
|
11 | function namespace () {
|
12 | var cache = {}
|
13 |
|
14 | var nsFormats = assign({}, formats)
|
15 | var currentLocale = 'en'
|
16 | var translations = null
|
17 | var generateId = function (pattern) { return pattern }
|
18 | var missingReplacement = null
|
19 | var missingTranslation = 'warning'
|
20 |
|
21 | function formatMessage (msg, args, locale) {
|
22 | locale = locale || currentLocale
|
23 | var pattern = typeof msg === 'string' ? msg : msg.default
|
24 | var id = (typeof msg === 'object' && msg.id) || generateId(pattern)
|
25 | var key = locale + ':' + id
|
26 | var format = cache[key] ||
|
27 | (cache[key] = generateFormat(pattern, id, locale))
|
28 | if (typeof format === 'string') return format
|
29 | return format(args)
|
30 | }
|
31 |
|
32 | function generateFormat (pattern, id, locale) {
|
33 | pattern = translate(pattern, id, locale)
|
34 | return interpret(locale, parse(pattern))
|
35 | }
|
36 |
|
37 | function translate (pattern, id, locale) {
|
38 | if (!translations) return pattern
|
39 |
|
40 | locale = lookupClosestLocale(locale, translations)
|
41 | var translated = translations[locale] && translations[locale][id]
|
42 | if (translated && translated.message) translated = translated.message
|
43 | if (translated != null) return translated
|
44 |
|
45 | var replacement = missingReplacement || pattern
|
46 | if (typeof replacement === 'function') {
|
47 | replacement = replacement(pattern, id, locale) || pattern
|
48 | }
|
49 | var message = 'Translation for "' + id + '" in "' + locale + '" is missing'
|
50 |
|
51 | if (missingTranslation === 'ignore') {
|
52 |
|
53 | } else if (missingTranslation === 'warning') {
|
54 | if (typeof console !== 'undefined') console.warn(message)
|
55 | } else {
|
56 | throw new Error(message)
|
57 | }
|
58 |
|
59 | return replacement
|
60 | }
|
61 |
|
62 | formatMessage.setup = function setup (opt) {
|
63 | opt = opt || {}
|
64 | if (opt.locale) currentLocale = opt.locale
|
65 | if ('translations' in opt) translations = opt.translations
|
66 | if (opt.generateId) generateId = opt.generateId
|
67 | if ('missingReplacement' in opt) missingReplacement = opt.missingReplacement
|
68 | if (opt.missingTranslation) missingTranslation = opt.missingTranslation
|
69 | if (opt.formats) {
|
70 | if (opt.formats.number) assign(nsFormats.number, opt.formats.number)
|
71 | if (opt.formats.date) assign(nsFormats.date, opt.formats.date)
|
72 | if (opt.formats.time) assign(nsFormats.time, opt.formats.time)
|
73 | }
|
74 | return {
|
75 | locale: currentLocale,
|
76 | translations: translations,
|
77 | generateId: generateId,
|
78 | missingReplacement: missingReplacement,
|
79 | missingTranslation: missingTranslation,
|
80 | formats: nsFormats
|
81 | }
|
82 | }
|
83 |
|
84 | function helper (type, value, style, locale) {
|
85 | locale = locale || currentLocale
|
86 | var options = nsFormats[type][style] || nsFormats[type].default
|
87 | var cache = options.cache || (options.cache = {})
|
88 | var format = cache[locale] || (cache[locale] = type === 'number'
|
89 | ? Intl.NumberFormat(locale, options).format
|
90 | : Intl.DateTimeFormat(locale, options).format
|
91 | )
|
92 | return format(value)
|
93 | }
|
94 |
|
95 | formatMessage.number = helper.bind(null, 'number')
|
96 | formatMessage.date = helper.bind(null, 'date')
|
97 | formatMessage.time = helper.bind(null, 'time')
|
98 |
|
99 | formatMessage.select = function (value, options) {
|
100 | return options[value] || options.other
|
101 | }
|
102 |
|
103 | function selectPlural (pluralType, value, offset, options, locale) {
|
104 | if (typeof offset === 'object') {
|
105 | locale = options
|
106 | options = offset
|
107 | offset = 0
|
108 | }
|
109 |
|
110 | var closest = lookupClosestLocale(locale || currentLocale, plurals)
|
111 | var plural = plurals[closest][pluralType]
|
112 | if (!plural) return options.other
|
113 |
|
114 | return (
|
115 | options['=' + +value] ||
|
116 | options[plural(value - offset)] ||
|
117 | options.other
|
118 | )
|
119 | }
|
120 |
|
121 | formatMessage.plural = selectPlural.bind(null, 'cardinal')
|
122 | formatMessage.selectordinal = selectPlural.bind(null, 'ordinal')
|
123 |
|
124 | return formatMessage
|
125 | }
|
126 |
|
127 | module.exports = exports = namespace()
|
128 | exports.namespace = namespace
|