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

i18next / i18next / #12544

19 Apr 2018 07:07AM UTC coverage: 87.43% (+2.0%) from 85.448%
#12544

push

jamuhl
rebuild

932 of 1066 relevant lines covered (87.43%)

39.04 hits per line

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

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

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

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

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

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

1✔
26
    if (this.backend && this.backend.init) {
27
      this.backend.init(services, options.backend, options);
5✔
28
    }
29
  }
4✔
30

31
  queueLoad(languages, namespaces, callback) {
4✔
32
    // find what needs to be loaded
33
    const toLoad = [];
1✔
34
    const pending = [];
35
    const toLoadLanguages = [];
1✔
36
    const toLoadNamespaces = [];
1✔
37

38
    languages.forEach((lng) => {
1✔
39
      let hasAllNamespaces = true;
1✔
40

1✔
41
      namespaces.forEach((ns) => {
42
        const name = `${lng}|${ns}`;
43

44
        if (this.store.hasResourceBundle(lng, ns)) {
1✔
45
          this.state[name] = 2; // loaded
1✔
46
        } else if (this.state[name] < 0) {
47
          // nothing to do for err
1✔
48
        } else if (this.state[name] === 1) {
4✔
49
          if (pending.indexOf(name) < 0) pending.push(name);
50
        } else {
4✔
51
          this.state[name] = 1; // pending
52

4✔
53
          hasAllNamespaces = false;
54

4✔
55
          if (pending.indexOf(name) < 0) pending.push(name);
4✔
56
          if (toLoad.indexOf(name) < 0) toLoad.push(name);
4✔
57
          if (toLoadNamespaces.indexOf(ns) < 0) toLoadNamespaces.push(ns);
4✔
58
        }
4✔
59
      });
60

4✔
61
      if (!hasAllNamespaces) toLoadLanguages.push(lng);
4✔
62
    });
63

4✔
64
    if (toLoad.length || pending.length) {
2✔
65
      this.queue.push({
66
        pending,
4✔
67
        loaded: {},
68
        errors: [],
69
        callback
1✔
70
      });
1✔
71
    }
72

73
    return {
1✔
74
      toLoad,
1✔
75
      pending,
1✔
76
      toLoadLanguages,
1✔
77
      toLoadNamespaces
78
    };
1✔
79
  }
1✔
80

81
  loaded(name, err, data) {
1✔
82
    const [lng, ns] = name.split('|');
1✔
83

84
    if (err) this.emit('failedLoading', lng, ns, err);
1✔
85

×
86
    if (data) {
1✔
87
      this.store.addResourceBundle(lng, ns, data);
88
    }
1✔
89

×
90
    // set loaded
91
    this.state[name] = err ? -1 : 2;
1✔
92

93
    // callback if ready
1✔
94
    this.queue.forEach((q) => {
95
      utils.pushPath(q.loaded, [lng], ns);
1✔
96
      remove(q.pending, name);
1✔
97

1✔
98
      if (err) q.errors.push(err);
99

100
      if (q.pending.length === 0 && !q.done) {
101
        this.emit('loaded', q.loaded);
1✔
102
        /* eslint no-param-reassign: 0 */
103
        q.done = true;
104
        if (q.errors.length) {
1✔
105
          q.callback(q.errors);
1✔
106
        } else {
107
          q.callback();
108
        }
109
      }
110
    });
111

112
    // remove done load requests
113
    this.queue = this.queue.filter(q => !q.done);
1✔
114
  }
115

116
  read(lng, ns, fcName, tried = 0, wait = 250, callback) {
117
    if (!lng.length) return callback(null, {}); // noting to load
118

119
    return this.backend[fcName](lng, ns, (err, data) => {
120
      if (err && data /* = retryFlag */ && tried < 5) {
121
        setTimeout(() => {
1✔
122
          this.read.call(this, lng, ns, fcName, tried + 1, wait * 2, callback);
2✔
123
        }, wait);
124
        return;
2✔
125
      }
126
      callback(err, data);
127
    });
128
  }
129

2✔
130
  /* eslint consistent-return: 0 */
131
  load(languages, namespaces, callback) {
2✔
132
    if (!this.backend) {
2✔
133
      this.logger.warn('No backend was added via i18next.use. Will not load resources.');
134
      return callback && callback();
135
    }
136

2✔
137
    if (typeof languages === 'string') languages = this.languageUtils.toResolveHierarchy(languages);
138
    if (typeof namespaces === 'string') namespaces = [namespaces];
139

2✔
140
    const toLoad = this.queueLoad(languages, namespaces, callback);
1✔
141
    if (!toLoad.toLoad.length) {
1✔
142
      if (!toLoad.pending.length) callback(); // nothing to load and no pendings...callback now
143
      return null; // pendings will trigger callback
1✔
144
    }
145

1✔
146
    toLoad.toLoad.forEach((name) => {
1✔
147
      this.loadOne(name);
148
    });
1✔
149
  }
1✔
150

×
151
  reload(languages, namespaces) {
152
    if (!this.backend) {
1✔
153
      this.logger.warn('No backend was added via i18next.use. Will not load resources.');
154
    }
155

156
    if (typeof languages === 'string') languages = this.languageUtils.toResolveHierarchy(languages);
157
    if (typeof namespaces === 'string') namespaces = [namespaces];
158

2✔
159
    languages.forEach((l) => {
1✔
160
      namespaces.forEach((n) => {
161
        this.loadOne(`${l}|${n}`, 're');
162
      });
163
    });
1✔
164
  }
4✔
165

166
  loadOne(name, prefix = '') {
4✔
167
    const [lng, ns] = name.split('|');
168

4✔
169
    this.read(lng, ns, 'read', null, null, (err, data) => {
4✔
170
      if (err) this.logger.warn(`${prefix}loading namespace ${ns} for language ${lng} failed`, err);
171
      if (!err && data) this.logger.log(`${prefix}loaded namespace ${ns} for language ${lng}`, data);
4✔
172

173
      this.loaded(name, err, data);
4✔
174
    });
4✔
175
  }
2✔
176

2✔
177
  saveMissing(languages, namespace, key, fallbackValue, isUpdate, options = {}) {
178
    if (this.backend && this.backend.create) {
2✔
179
      this.backend.create(languages, namespace, key, fallbackValue, null /* unused callback */, { ...options, isUpdate });
180
    }
2✔
181

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

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