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

hybridsjs / hybrids / 9387127968

05 Jun 2024 03:38PM UTC coverage: 46.684% (-53.3%) from 99.956%
9387127968

Pull #258

github

web-flow
Merge e936aa704 into 36d6e398d
Pull Request #258: feat: remove `content` property & add shadow mode detection to render property

611 of 1778 branches covered (34.36%)

69 of 97 new or added lines in 9 files covered. (71.13%)

1169 existing lines in 25 files now uncovered.

1049 of 2247 relevant lines covered (46.68%)

32.63 hits per line

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

19.77
/src/localize.js
1
import { getPlaceholder } from "./template/utils.js";
2

3
import { compile } from "./template/index.js";
4

5
const dictionary = new Map();
2✔
6
const cache = new Map();
2✔
7
let translate = null;
2✔
8

9
const languages = (() => {
2✔
10
  let list;
11

12
  // istanbul ignore next
13
  try {
14
    list = globalThis.navigator.languages || [globalThis.navigator.language];
15
  } catch (e) {
16
    list = [];
17
  }
18

19
  return list.reduce((set, code) => {
2✔
20
    const codeWithoutRegion = code.split("-")[0];
2✔
21
    set.add(code);
2✔
22
    if (code !== codeWithoutRegion) set.add(codeWithoutRegion);
2!
23
    return set;
2✔
24
  }, new Set());
25
})();
26

27
export function isLocalizeEnabled() {
28
  return translate !== null || dictionary.size;
87✔
29
}
30

31
export function clear() {
UNCOV
32
  languages.delete("default");
×
UNCOV
33
  dictionary.clear();
×
UNCOV
34
  cache.clear();
×
35

UNCOV
36
  translate = null;
×
37
}
38

39
const pluralRules = new Map();
2✔
40
export function get(key, context, args = []) {
×
UNCOV
41
  key = key.trim().replace(/\s+/g, " ");
×
UNCOV
42
  context = context.trim();
×
43

UNCOV
44
  const cacheKey = `${key} | ${context}`;
×
UNCOV
45
  let msg = cache.get(cacheKey);
×
46

UNCOV
47
  if (!msg) {
×
UNCOV
48
    if (dictionary.size) {
×
UNCOV
49
      for (const lang of languages) {
×
UNCOV
50
        const msgs = dictionary.get(lang);
×
UNCOV
51
        if (msgs) {
×
UNCOV
52
          msg = msgs[cacheKey] || msgs[key];
×
53

UNCOV
54
          if (msg) {
×
UNCOV
55
            msg = msg.message;
×
56

UNCOV
57
            if (typeof msg === "object") {
×
UNCOV
58
              let rules = pluralRules.get(lang);
×
UNCOV
59
              if (!rules) {
×
UNCOV
60
                rules = new Intl.PluralRules(lang);
×
UNCOV
61
                pluralRules.set(lang, rules);
×
62
              }
63

UNCOV
64
              const pluralForms = msg;
×
UNCOV
65
              msg = (number) =>
×
UNCOV
66
                (number === 0 && pluralForms.zero) ||
×
67
                pluralForms[rules.select(number)] ||
68
                pluralForms.other ||
69
                "";
70
            }
UNCOV
71
            break;
×
72
          }
73
        }
74
      }
75
    }
76

UNCOV
77
    if (!msg) {
×
UNCOV
78
      if (translate) {
×
UNCOV
79
        msg = translate(key, context);
×
80
      }
UNCOV
81
      if (!msg) {
×
UNCOV
82
        msg = key;
×
NEW
83
        if (dictionary.size || translate) {
×
UNCOV
84
          console.warn(
×
85
            `Missing translation: "${key}"${context ? ` [${context}]` : ""}`,
×
86
          );
87
        }
88
      }
89
    }
90

UNCOV
91
    cache.set(cacheKey, msg);
×
92
  }
93

UNCOV
94
  return typeof msg === "function" ? msg(args[0]) : msg;
×
95
}
96

97
function getKeyInChromeI18nFormat(key) {
UNCOV
98
  return key
×
99
    .replace("$", "@")
100
    .replace(/[^a-zA-Z0-9_@]/g, "_")
101
    .toLowerCase();
102
}
103

104
export function localize(lang, messages) {
UNCOV
105
  switch (typeof lang) {
×
106
    case "function": {
UNCOV
107
      const options = messages || {};
×
108

UNCOV
109
      if (options.format === "chrome.i18n") {
×
UNCOV
110
        const cachedKeys = new Map();
×
UNCOV
111
        translate = (key, context) => {
×
UNCOV
112
          key = context ? `${key} | ${context}` : key;
×
113

UNCOV
114
          let cachedKey = cachedKeys.get(key);
×
UNCOV
115
          if (!cachedKey) {
×
UNCOV
116
            cachedKey = getKeyInChromeI18nFormat(key);
×
UNCOV
117
            cachedKeys.set(key, cachedKey);
×
118
          }
119

UNCOV
120
          return lang(cachedKey, context);
×
121
        };
122
      } else {
UNCOV
123
        translate = lang;
×
124
      }
125

UNCOV
126
      break;
×
127
    }
128
    case "string": {
UNCOV
129
      if (!messages || typeof messages !== "object") {
×
UNCOV
130
        throw TypeError("Messages must be an object");
×
131
      }
132

UNCOV
133
      if (lang === "default") {
×
UNCOV
134
        languages.add("default");
×
135
      }
136

UNCOV
137
      const current = dictionary.get(lang) || {};
×
UNCOV
138
      dictionary.set(lang, { ...current, ...messages });
×
UNCOV
139
      break;
×
140
    }
141
    default:
UNCOV
142
      throw TypeError("The first argument must be a string or a function");
×
143
  }
144
}
145

146
Object.defineProperty(localize, "languages", {
2✔
UNCOV
147
  get: () => Array.from(languages),
×
148
});
149

150
function getString(parts, args) {
UNCOV
151
  let string = "";
×
152

UNCOV
153
  for (const [index, part] of parts.entries()) {
×
UNCOV
154
    string += index ? `\${${index - 1}}${part}` : part;
×
155
  }
156

UNCOV
157
  const [key, , context = ""] = string.split("|");
×
158

UNCOV
159
  return get(key, context, args);
×
160
}
161

162
const EXP_REGEX = /\$\{(\d+)\}/g;
2✔
163

164
export function msg(parts, ...args) {
UNCOV
165
  return getString(parts, args).replace(EXP_REGEX, (_, index) => args[index]);
×
166
}
167

168
const PLACEHOLDER_SVG = getPlaceholder("svg");
2✔
169
const PLACEHOLDER_MSG = getPlaceholder("msg");
2✔
170

171
msg.html = function html(parts, ...args) {
2✔
UNCOV
172
  const input = getString(parts, args);
×
173

UNCOV
174
  return compile(
×
UNCOV
175
    input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)),
×
176
    args,
177
    input + PLACEHOLDER_MSG,
178
    false,
179
    true,
180
  );
181
};
182

183
msg.svg = function svg(parts, ...args) {
2✔
UNCOV
184
  const input = getString(parts, args);
×
185

UNCOV
186
  return compile(
×
UNCOV
187
    input.replace(EXP_REGEX, (_, index) => getPlaceholder(index)),
×
188
    args,
189
    input + PLACEHOLDER_SVG + PLACEHOLDER_MSG,
190
    true,
191
    true,
192
  );
193
};
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