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

i18next / i18next / #51629

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

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

82.98
/src/BackendConnector.js
1
import * as utils from './utils';
1✔
2
import baseLogger from './logger';
1✔
3
import EventEmitter from './EventEmitter';
1✔
4

5
function remove(arr, what) {
1✔
6
  var found = arr.indexOf(what);
1✔
7

8
  while (found !== -1) {
1✔
9
    arr.splice(found, 1);
1✔
10
    found = arr.indexOf(what);
1✔
11
  }
12
}
13

14
class Connector  extends EventEmitter {
15
  constructor(backend, store, services, options = {}) {
2✔
16
    super();
17
    this.backend = backend;
2✔
18
    this.store = store;
2✔
19
    this.services = services;
2✔
20
    this.options = options;
2✔
21
    this.logger = baseLogger.create('backendConnector');
2✔
22

23
    this.state = {};
2✔
24
    this.queue = [];
2✔
25

26
    this.backend && this.backend.init && this.backend.init(services, options.backend, options);
2✔
27
  }
28

29
  queueLoad(languages, namespaces, callback) {
1✔
30
    // find what needs to be loaded
31
    let toLoad = [], pending = [], toLoadLanguages = [], toLoadNamespaces = [];
1✔
32

33
    languages.forEach(lng => {
1✔
34
      let hasAllNamespaces = true;
1✔
35

36
      namespaces.forEach(ns => {
1✔
37
        const name = `${lng}|${ns}`;
1✔
38

39
        if (this.store.hasResourceBundle(lng, ns)) {
1✔
40
          this.state[name] = 2; // loaded
×
41
        } else if (this.state[name] < 0) {
1✔
42
          // nothing to do for err
43
        } else if (this.state[name] === 1) {
1✔
44
          if (pending.indexOf(name) < 0) pending.push(name);
×
45
        } else {
46
          this.state[name] = 1; // pending
1✔
47

48
          hasAllNamespaces = false;
1✔
49

50
          if (pending.indexOf(name) < 0) pending.push(name);
1✔
51
          if (toLoad.indexOf(name) < 0) toLoad.push(name);
1✔
52
          if (toLoadNamespaces.indexOf(ns) < 0) toLoadNamespaces.push(ns);
1✔
53
        }
54
      });
55

56
      if (!hasAllNamespaces) toLoadLanguages.push(lng);
1✔
57
    });
58

59
    if (toLoad.length || pending.length) {
1✔
60
      this.queue.push({
1✔
61
        pending: pending,
62
        loaded: {},
63
        errors: [],
64
        callback: callback
65
      });
66
    }
67

68
    return {
1✔
69
      toLoad: toLoad,
70
      pending: pending,
71
      toLoadLanguages: toLoadLanguages,
72
      toLoadNamespaces: toLoadNamespaces
73
    };
74
  }
75

76
  loaded(name, err, data) {
1✔
77
    const [lng, ns] = name.split('|');
78

79
    if (err) this.emit('failedLoading', lng, ns, err);
1✔
80

81
    if (data) {
1✔
82
      this.store.addResourceBundle(lng, ns, data);
1✔
83
    }
84

85
    // set loaded
86
    this.state[name] = err ? -1 : 2;
1✔
87
    // callback if ready
88
    this.queue.forEach(q => {
1✔
89
      utils.pushPath(q.loaded, [lng], ns);
1✔
90
      remove(q.pending, name);
1✔
91

92
      if (err) q.errors.push(err);
1✔
93

94
      if (q.pending.length === 0 && !q.done) {
1✔
95
        q.errors.length ? q.callback(q.errors) : q.callback();
1✔
96
        this.emit('loaded', q.loaded);
1✔
97
        q.done = true;
1✔
98
      }
99
    });
100

101
    // remove done load requests
102
    this.queue = this.queue.filter(q => {
1✔
103
      return !q.done;
1✔
104
    });
105
  }
106

107
  read(lng, ns, fcName, tried, wait, callback) {
3✔
108
    if (!tried) tried = 0;
3✔
109
    if (!wait) wait = 250;
3✔
110

111
    if (!lng.length) return callback(null, {}); // noting to load
3✔
112

113
    this.backend[fcName](lng, ns, (err, data) => {
3✔
114
      if (err && data /* = retryFlag */ && tried < 5) {
3✔
115
        setTimeout(() => {
2✔
116
          this.read.call(this, lng, ns, fcName, ++tried, wait*2, callback);
2✔
117
        }, wait);
118
        return;
2✔
119
      }
120
      callback(err, data);
1✔
121
    });
122
  }
123

124
  load(languages, namespaces, callback) {
4✔
125
    if (!this.backend) {
4✔
126
      this.logger.warn('No backend was added via i18next.use. Will not load resources.')
3✔
127
      return callback && callback();
3✔
128
    }
129
    let options = {...this.backend.options, ...this.options.backend};
1✔
130

131
    if (typeof languages === 'string') languages = this.services.languageUtils.toResolveHierarchy(languages);
1✔
132
    if (typeof namespaces === 'string') namespaces = [namespaces];
1✔
133

134
    let toLoad = this.queueLoad(languages, namespaces, callback);
1✔
135
    if (!toLoad.toLoad.length) {
1✔
136
      if (!toLoad.pending.length) callback(); // nothing to load and no pendings...callback now
×
137
      return; // pendings will trigger callback
×
138
    }
139

140
    // load with multi-load
141
    if (options.allowMultiLoading && this.backend.readMulti) {
1✔
142
      this.read(toLoad.toLoadLanguages, toLoad.toLoadNamespaces, 'readMulti', null, null, (err, data) => {
×
143
        if (err) this.logger.warn(`loading namespaces ${toLoad.toLoadNamespaces.join(', ')} for languages ${toLoad.toLoadLanguages.join(', ')} via multiloading failed`, err);
×
144
        if (!err && data) this.logger.log(`loaded namespaces ${toLoad.toLoadNamespaces.join(', ')} for languages ${toLoad.toLoadLanguages.join(', ')} via multiloading`, data);
×
145

146
        toLoad.toLoad.forEach(name => {
×
147
          const [l, n] = name.split('|');
148

149
          let bundle = utils.getPath(data, [l, n]);
×
150
          if (bundle) {
×
151
            this.loaded(name, err, bundle);
×
152
          } else {
153
            let err = `loading namespace ${n} for language ${l} via multiloading failed`;
×
154
            this.loaded(name, err);
×
155
            this.logger.error(err);
×
156
          }
157
        });
158
      });
159
    }
160

161
    // load one by one
162
    else {
1✔
163
      function read(name) {
1✔
164
        const [lng, ns] = name.split('|');
165

166
        this.read(lng, ns, 'read', null, null, (err, data) => {
1✔
167
          if (err) this.logger.warn(`loading namespace ${ns} for language ${lng} failed`, err);
1✔
168
          if (!err && data) this.logger.log(`loaded namespace ${ns} for language ${lng}`, data);
1✔
169

170
          this.loaded(name, err, data);
1✔
171
        });
172
      };
173

174
      toLoad.toLoad.forEach(name => {
1✔
175
        read.call(this, name);
1✔
176
      });
177
    }
178
  }
179

180
  saveMissing(languages, namespace, key, fallbackValue) {
181
    if (this.backend && this.backend.create) this.backend.create(languages, namespace, key, fallbackValue);
×
182

183
    // write to store to avoid resending
184
    this.store.addResource(languages[0], namespace, key, fallbackValue);
×
185
  }
186
}
187

188
export default Connector;
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