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

i18next / i18next / #11775

18 Aug 2017 09:06AM UTC coverage: 84.185% (+0.2%) from 83.97%
#11775

push

jamuhl
adds the defaultValue option for getFixedT

0 of 1 new or added line in 1 file covered. (0.0%)

3 existing lines in 1 file now uncovered.

692 of 822 relevant lines covered (84.18%)

35.59 hits per line

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

58.62
/src/i18next.js
1
import baseLogger from './logger';
1✔
2
import EventEmitter from './EventEmitter';
1✔
3
import ResourceStore from './ResourceStore';
1✔
4
import Translator from './Translator';
1✔
5
import LanguageUtils from './LanguageUtils';
1✔
6
import PluralResolver from './PluralResolver';
1✔
7
import Interpolator from './Interpolator';
1✔
8
import BackendConnector from './BackendConnector';
1✔
9
import CacheConnector from './CacheConnector';
1✔
10
import { get as getDefaults, transformOptions } from './defaults';
1✔
11
import postProcessor from './postProcessor';
1✔
12

13
function noop() {}
1✔
14

15
class I18n extends EventEmitter {
16
  constructor(options = {}, callback) {
5✔
17
    super();
18
    this.options = transformOptions(options);
5✔
19
    this.services = {};
5✔
20
    this.logger = baseLogger;
5✔
21
    this.modules = { external: [] };
5✔
22

23
    if (callback && !this.isInitialized && !options.isClone) {
5✔
24
      // https://github.com/i18next/i18next/issues/879
25
      if (!this.options.initImmediate) return this.init(options, callback);
1✔
26
      setTimeout(() => {
×
27
        this.init(options, callback);
×
28
      }, 0);
29
    }
30
  }
31

32
  init(options = {}, callback) {
4✔
33
    if (typeof options === 'function') {
4✔
34
      callback = options;
×
35
      options = {};
×
36
    }
37
    this.options = { ...getDefaults(), ...this.options, ...transformOptions(options) };
4✔
38

39
    this.format = this.options.interpolation.format;
4✔
40
    if (!callback) callback = noop;
4✔
41

42
    function createClassOnDemand(ClassOrObject) {
1✔
43
      if (!ClassOrObject) return null;
4✔
44
      if (typeof ClassOrObject === 'function') return new ClassOrObject();
×
45
      return ClassOrObject;
×
46
    }
47

48
    // init services
49
    if (!this.options.isClone) {
4✔
50
      if (this.modules.logger) {
2✔
51
        baseLogger.init(createClassOnDemand(this.modules.logger), this.options);
×
52
      } else {
53
        baseLogger.init(null, this.options);
2✔
54
      }
55

56
      const lu = new LanguageUtils(this.options);
2✔
57
      this.store = new ResourceStore(this.options.resources, this.options);
2✔
58

59
      const s = this.services;
2✔
60
      s.logger = baseLogger;
2✔
61
      s.resourceStore = this.store;
2✔
62
      s.resourceStore.on('added removed', (lng, ns) => {
2✔
63
        s.cacheConnector.save();
×
64
      });
65
      s.languageUtils = lu;
2✔
66
      s.pluralResolver = new PluralResolver(lu, { prepend: this.options.pluralSeparator, compatibilityJSON: this.options.compatibilityJSON, simplifyPluralSuffix: this.options.simplifyPluralSuffix });
2✔
67
      s.interpolator = new Interpolator(this.options);
2✔
68

69
      s.backendConnector = new BackendConnector(createClassOnDemand(this.modules.backend), s.resourceStore, s, this.options);
2✔
70
      // pipe events from backendConnector
71
      s.backendConnector.on('*', (event, ...args) => {
2✔
72
        this.emit(event, ...args);
×
73
      });
74

75
      s.backendConnector.on('loaded', (loaded) => {
2✔
76
        s.cacheConnector.save();
×
77
      });
78

79
      s.cacheConnector = new CacheConnector(createClassOnDemand(this.modules.cache), s.resourceStore, s, this.options);
2✔
80
      // pipe events from backendConnector
81
      s.cacheConnector.on('*', (event, ...args) => {
2✔
82
        this.emit(event, ...args);
×
83
      });
84

85
      if (this.modules.languageDetector) {
2✔
86
        s.languageDetector = createClassOnDemand(this.modules.languageDetector);
×
87
        s.languageDetector.init(s, this.options.detection, this.options);
×
88
      }
89

90
      this.translator = new Translator(this.services, this.options);
2✔
91
      // pipe events from translator
92
      this.translator.on('*', (event, ...args) => {
2✔
93
        this.emit(event, ...args);
×
94
      });
95

96
      this.modules.external.forEach((m) => {
2✔
97
        if (m.init) m.init(this);
×
98
      });
99
    }
100

101
    // append api
102
    const storeApi = ['getResource', 'addResource', 'addResources', 'addResourceBundle', 'removeResourceBundle', 'hasResourceBundle', 'getResourceBundle'];
4✔
103
    storeApi.forEach((fcName) => {
4✔
104
      this[fcName] = (...args) => this.store[fcName](...args);
28✔
105
    });
106

107
    const load = () => {
4✔
108
      this.changeLanguage(this.options.lng, (err, t) => {
4✔
109
        this.isInitialized = true;
4✔
110
        this.logger.log('initialized', this.options);
4✔
111
        this.emit('initialized', this.options);
4✔
112

113
        callback(err, t);
4✔
114
      });
115
    };
116

117
    if (this.options.resources || !this.options.initImmediate) {
4✔
118
      load();
×
119
    } else {
120
      setTimeout(load, 0);
4✔
121
    }
122

123
    return this;
4✔
124
  }
125

126
  /* eslint consistent-return: 0 */
127
  loadResources(callback = noop) {
6✔
128
    if (!this.options.resources) {
6✔
129
      if (this.language && this.language.toLowerCase() === 'cimode') return callback(); // avoid loading resources for cimode
6✔
130

131
      const toLoad = [];
6✔
132

133
      const append = (lng) => {
6✔
134
        if (!lng) return;
6✔
135
        const lngs = this.services.languageUtils.toResolveHierarchy(lng);
6✔
136
        lngs.forEach((l) => {
6✔
137
          if (toLoad.indexOf(l) < 0) toLoad.push(l);
12✔
138
        });
139
      };
140

141
      if (!this.language) {
6✔
142
        // at least load fallbacks in this case
143
        const fallbacks = this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);
×
144
        fallbacks.forEach(l => append(l));
×
145
      } else {
146
        append(this.language);
6✔
147
      }
148

149
      if (this.options.preload) {
6✔
150
        this.options.preload.forEach(l => append(l));
×
151
      }
152

153
      this.services.cacheConnector.load(toLoad, this.options.ns, () => {
6✔
154
        this.services.backendConnector.load(toLoad, this.options.ns, callback);
6✔
155
      });
156
    } else {
157
      callback(null);
×
158
    }
159
  }
160

161
  reloadResources(lngs, ns) {
162
    if (!lngs) lngs = this.languages;
×
163
    if (!ns) ns = this.options.ns;
×
164
    this.services.backendConnector.reload(lngs, ns);
×
165
  }
166

167
  use(module) {
168
    if (module.type === 'backend') {
×
169
      this.modules.backend = module;
×
170
    }
171

172
    if (module.type === 'cache') {
×
173
      this.modules.cache = module;
×
174
    }
175

176
    if (module.type === 'logger' || (module.log && module.warn && module.error)) {
×
177
      this.modules.logger = module;
×
178
    }
179

180
    if (module.type === 'languageDetector') {
×
181
      this.modules.languageDetector = module;
×
182
    }
183

184
    if (module.type === 'postProcessor') {
×
185
      postProcessor.addPostProcessor(module);
×
186
    }
187

188
    if (module.type === '3rdParty') {
×
189
      this.modules.external.push(module);
×
190
    }
191

192
    return this;
×
193
  }
194

195
  changeLanguage(lng, callback) {
6✔
196
    const done = (err, l) => {
6✔
197
      if (l) {
6✔
198
        this.emit('languageChanged', l);
4✔
199
        this.logger.log('languageChanged', l);
4✔
200
      }
201

202
      if (callback) callback(err, (...args) => this.t(...args));
6✔
203
    };
204

205
    const setLng = (l) => {
6✔
206
      if (l) {
6✔
207
        this.language = l;
4✔
208
        this.languages = this.services.languageUtils.toResolveHierarchy(l);
4✔
209

210
        this.translator.changeLanguage(l);
4✔
211

212
        if (this.services.languageDetector) this.services.languageDetector.cacheUserLanguage(l);
4✔
213
      }
214

215
      this.loadResources((err) => {
6✔
216
        done(err, l);
6✔
217
      });
218
    };
219

220
    if (!lng && this.services.languageDetector && !this.services.languageDetector.async) {
6✔
221
      setLng(this.services.languageDetector.detect());
×
222
    } else if (!lng && this.services.languageDetector && this.services.languageDetector.async) {
6✔
223
      this.services.languageDetector.detect(setLng);
×
224
    } else {
225
      setLng(lng);
6✔
226
    }
227
  }
228

229
  getFixedT(lng, ns) {
×
230
    const fixedT = (key, opts = {}) => {
×
NEW
231
      const options = (typeof opts === 'string') ? { defaultValue: opts } : { ...opts };
×
232
      options.lng = options.lng || fixedT.lng;
×
233
      options.lngs = options.lngs || fixedT.lngs;
×
234
      options.ns = options.ns || fixedT.ns;
×
235
      return this.t(key, options);
×
236
    };
237
    if (typeof lng === 'string') {
×
238
      fixedT.lng = lng;
×
239
    } else {
240
      fixedT.lngs = lng;
×
241
    }
242
    fixedT.ns = ns;
×
243
    return fixedT;
×
244
  }
245

246
  t(...args) {
×
247
    return this.translator && this.translator.translate(...args);
×
248
  }
249

250
  exists(...args) {
×
251
    return this.translator && this.translator.exists(...args);
×
252
  }
253

254
  setDefaultNamespace(ns) {
255
    this.options.defaultNS = ns;
×
256
  }
257

258
  loadNamespaces(ns, callback) {
×
259
    if (!this.options.ns) return callback && callback();
×
260
    if (typeof ns === 'string') ns = [ns];
×
261

262
    ns.forEach((n) => {
×
263
      if (this.options.ns.indexOf(n) < 0) this.options.ns.push(n);
×
264
    });
265

266
    this.loadResources(callback);
×
267
  }
268

269
  loadLanguages(lngs, callback) {
270
    if (typeof lngs === 'string') lngs = [lngs];
×
271
    const preloaded = this.options.preload || [];
×
272

273
    const newLngs = lngs.filter(lng => preloaded.indexOf(lng) < 0);
×
274
    // Exit early if all given languages are already preloaded
275
    if (!newLngs.length) return callback();
×
276

277
    this.options.preload = preloaded.concat(newLngs);
×
278
    this.loadResources(callback);
×
279
  }
280

281
  dir(lng) {
282
    if (!lng) lng = this.languages && this.languages.length > 0 ? this.languages[0] : this.language;
×
283
    if (!lng) return 'rtl';
×
284

285
    const rtlLngs = ['ar', 'shu', 'sqr', 'ssh', 'xaa', 'yhd', 'yud', 'aao', 'abh', 'abv', 'acm',
×
286
      'acq', 'acw', 'acx', 'acy', 'adf', 'ads', 'aeb', 'aec', 'afb', 'ajp', 'apc', 'apd', 'arb',
287
      'arq', 'ars', 'ary', 'arz', 'auz', 'avl', 'ayh', 'ayl', 'ayn', 'ayp', 'bbz', 'pga', 'he',
288
      'iw', 'ps', 'pbt', 'pbu', 'pst', 'prp', 'prd', 'ur', 'ydd', 'yds', 'yih', 'ji', 'yi', 'hbo',
289
      'men', 'xmn', 'fa', 'jpr', 'peo', 'pes', 'prs', 'dv', 'sam'
290
    ];
291

292
    return rtlLngs.indexOf(this.services.languageUtils.getLanguagePartFromCode(lng)) >= 0 ? 'rtl' : 'ltr';
×
293
  }
294

295
  /* eslint class-methods-use-this: 0 */
296
  createInstance(options = {}, callback) {
2✔
297
    return new I18n(options, callback);
2✔
298
  }
299

300
  cloneInstance(options = {}, callback = noop) {
2✔
301
    const mergedOptions = { ...this.options, ...options, ...{ isClone: true } };
2✔
302
    const clone = new I18n(mergedOptions, callback);
2✔
303
    const membersToCopy = ['store', 'services', 'language'];
2✔
304
    membersToCopy.forEach((m) => {
2✔
305
      clone[m] = this[m];
6✔
306
    });
307
    clone.translator = new Translator(clone.services, clone.options);
2✔
308
    clone.translator.on('*', (event, ...args) => {
2✔
309
      clone.emit(event, ...args);
×
310
    });
311
    clone.init(mergedOptions, callback);
2✔
312

313
    return clone;
2✔
314
  }
315
}
316

317
export default new I18n();
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

© 2026 Coveralls, Inc