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

i18next / i18next / #51133

17 Mar 2016 09:13AM UTC coverage: 66.667% (+1.9%) from 64.722%
#51133

push

jamuhl
rebuild

514 of 771 relevant lines covered (66.67%)

20.23 hits per line

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

61.9
/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
import * as compat from './compatibility/v1';
1✔
14

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

23
    if (callback && !this.isInitialized) this.init(options, callback);
3✔
24
  }
25

26
  init(options, callback) {
1✔
27
    if (typeof options === 'function') {
1✔
28
      callback = options;
×
29
      options = {};
×
30
    }
31
    if (!options) options = {};
1✔
32

33
    if (options.compatibilityAPI === 'v1') {
1✔
34
      this.options = {...getDefaults(), ...transformOptions(compat.convertAPIOptions(options)), ...{}};
×
35
    } else if (options.compatibilityJSON === 'v1') {
1✔
36
      this.options = {...getDefaults(), ...transformOptions(compat.convertJSONOptions(options)), ...{}};
×
37
    } else {
38
      this.options = {...getDefaults(), ...this.options, ...transformOptions(options)};
1✔
39
    }
40
    if (!callback) callback = () => {};
1✔
41

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

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

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

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

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

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

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

85
      if (this.modules.languageDetector) {
1✔
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);
1✔
91
      // pipe events from translator
92
      this.translator.on('*', (event, ...args) => {
1✔
93
        this.emit(event, ...args);
×
94
      });
95
    }
96

97
    // append api
98
    const storeApi = ['getResource', 'addResource', 'addResources', 'addResourceBundle', 'removeResourceBundle', 'hasResourceBundle', 'getResourceBundle'];
1✔
99
    storeApi.forEach(fcName => {
1✔
100
      this[fcName] = function() { return this.store[fcName].apply(this.store, arguments); };
7✔
101
    });
102

103
    // TODO: COMPATIBILITY remove this
104
    if (this.options.compatibilityAPI === 'v1') compat.appendBackwardsAPI(this);
1✔
105

106
    this.changeLanguage(this.options.lng, (err, t) => {
1✔
107
      this.emit('initialized', this.options);
1✔
108
      this.logger.log('initialized', this.options);
1✔
109

110
      callback(err, t);
1✔
111
    });
112

113
    return this;
1✔
114
  }
115

116
  loadResources(callback) {
3✔
117
    if (!callback) callback = () => {};
3✔
118

119
    if (!this.options.resources) {
3✔
120
      if (this.language && this.language.toLowerCase() === 'cimode') return callback(); // avoid loading resources for cimode
3✔
121

122
      let toLoad = [];
3✔
123

124
      let append = lng => {
3✔
125
        let lngs = this.services.languageUtils.toResolveHierarchy(lng);
3✔
126
        lngs.forEach(l => {
3✔
127
          if (toLoad.indexOf(l) < 0) toLoad.push(l);
5✔
128
        });
129
      };
130

131
      append(this.language);
3✔
132

133
      if (this.options.preload) {
3✔
134
        this.options.preload.forEach(l => {
×
135
          append(l);
×
136
        });
137
      }
138

139
      this.services.cacheConnector.load(toLoad, this.options.ns, () => {
3✔
140
        this.services.backendConnector.load(toLoad, this.options.ns, callback);
3✔
141
      });
142
    } else {
143
      callback(null);
×
144
    }
145
  }
146

147
  use(module) {
148
    if (module.type === 'backend') {
×
149
      this.modules.backend = module;
×
150
    }
151

152
    if (module.type === 'cache') {
×
153
      this.modules.cache = module;
×
154
    }
155

156
    if (module.type === 'logger' || (module.log && module.warn && module.warn)) {
×
157
      this.modules.logger = module;
×
158
    }
159

160
    if (module.type === 'languageDetector') {
×
161
      this.modules.languageDetector = module;
×
162
    }
163

164
    if (module.type === 'postProcessor') {
×
165
      postProcessor.addPostProcessor(module);
×
166
    }
167

168
    return this;
×
169
  }
170

171
  changeLanguage(lng, callback) {
3✔
172
    let done = (err) => {
3✔
173
      if (lng) {
3✔
174
        this.emit('languageChanged', lng);
2✔
175
        this.logger.log('languageChanged', lng);
2✔
176
      }
177

178
      if (callback) callback(err, (...args) => { return this.t.apply(this, args); });
3✔
179
    };
180

181
    if (!lng && this.services.languageDetector) lng = this.services.languageDetector.detect();
3✔
182

183
    if (lng) {
3✔
184
      this.language = lng;
2✔
185
      this.languages = this.services.languageUtils.toResolveHierarchy(lng);
2✔
186

187
      this.translator.changeLanguage(lng);
2✔
188

189
      if (this.services.languageDetector) this.services.languageDetector.cacheUserLanguage(lng);
2✔
190
    }
191

192
    this.loadResources((err) => {
3✔
193
      done(err);
3✔
194
    });
195
  }
196

197
  getFixedT(lng, ns) {
×
198
    let fixedT = (key, options) => {
×
199
      options = options || {};
×
200
      options.lng = options.lng || fixedT.lng;
×
201
      options.ns = options.ns || fixedT.ns;
×
202
      return this.t(key, options);
×
203
    };
204
    fixedT.lng = lng;
×
205
    fixedT.ns = ns;
×
206
    return fixedT;
×
207
  }
208

209
  t() {
210
    return this.translator && this.translator.translate.apply(this.translator, arguments);
×
211
  }
212

213
  exists() {
214
    return this.translator && this.translator.exists.apply(this.translator, arguments);
×
215
  }
216

217
  setDefaultNamespace(ns) {
218
    this.options.defaultNS = ns;
×
219
  }
220

221
  loadNamespaces(ns, callback) {
×
222
    if (!this.options.ns) return callback && callback();
×
223
    if (typeof ns === 'string') ns = [ns];
×
224

225
    ns.forEach(n => {
×
226
      if (this.options.ns.indexOf(n) < 0) this.options.ns.push(n);
×
227
    });
228

229
    this.loadResources(callback);
×
230
  }
231

232
  loadLanguages(lngs, callback) {
233
    if (typeof lngs === 'string') lngs = [lngs];
×
234
    const preloaded = this.options.preload || [];
×
235

236
    const newLngs = lngs.filter(lng => {
×
237
      return preloaded.indexOf(lng) < 0;
×
238
    });
239
    // Exit early if all given languages are already preloaded
240
    if (!newLngs.length) return callback();
×
241

242
    this.options.preload = preloaded.concat(newLngs);
×
243
    this.loadResources(callback);
×
244
  }
245

246
  dir(lng) {
247
    if (!lng) lng = this.language;
×
248

249
    var ltrLngs = ['ar', 'shu', 'sqr', 'ssh', 'xaa', 'yhd', 'yud', 'aao', 'abh', 'abv', 'acm',
×
250
      'acq', 'acw', 'acx', 'acy', 'adf', 'ads', 'aeb', 'aec', 'afb', 'ajp', 'apc', 'apd', 'arb',
251
      'arq', 'ars', 'ary', 'arz', 'auz', 'avl', 'ayh', 'ayl', 'ayn', 'ayp', 'bbz', 'pga', 'he',
252
      'iw', 'ps', 'pbt', 'pbu', 'pst', 'prp', 'prd', 'ur', 'ydd', 'yds', 'yih', 'ji', 'yi', 'hbo',
253
      'men', 'xmn', 'fa', 'jpr', 'peo', 'pes', 'prs', 'dv', 'sam'
254
    ];
255

256
    return ltrLngs.indexOf(this.services.languageUtils.getLanguagePartFromCode(lng)) ? 'ltr' : 'rtl';
×
257
  }
258

259
  createInstance(options = {}, callback) {
1✔
260
    return new I18n(options, callback);
1✔
261
  }
262

263
  cloneInstance(options = {}, callback) {
1✔
264
    let clone = new I18n({...options, ...this.options, ...{isClone: true}}, callback);
1✔
265
    const membersToCopy = ['store', 'translator', 'services', 'language'];
1✔
266
    membersToCopy.forEach(m => {
1✔
267
      clone[m] = this[m];
4✔
268
    });
269

270
    return clone;
1✔
271
  }
272
}
273

274
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