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

mozilla / fx-private-relay / 85cda637-2ac9-4d08-9727-ee8e8a702875

06 Sep 2024 09:48PM CUT coverage: 85.506%. First build
85cda637-2ac9-4d08-9727-ee8e8a702875

push

circleci

jwhitlock
Handle Twilio errors when relaying messages

When a Relay user replies 'STOP', they are unsubscribed from Twilio and
we can't send them any more messages.

When a contact replies 'STOP', the Relay user can't reply to them
anymore.

There are other errors, but those haven't happened to us yet. Log as
errors.

4111 of 5264 branches covered (78.1%)

Branch coverage included in aggregate %.

133 of 134 new or added lines in 2 files covered. (99.25%)

16130 of 18408 relevant lines covered (87.62%)

10.34 hits per line

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

0.0
/frontend/src/functions/getL10n.ts
1
import { FluentBundle, FluentResource } from "@fluent/bundle";
×
2
import { negotiateLanguages } from "@fluent/langneg";
×
3
import { MarkupParser, ReactLocalization } from "@fluent/react";
×
4

5
/**
6
 * @param options Set `deterministicLocales` to `true` to ensure the `en` bundle is loaded.
7
 * @returns Initialise `@fluent/react`.
8
 * @todo Get the relevant .ftl injected by the server.
9
 */
10
export function getL10n(options: { deterministicLocales: boolean }) {
×
11
  // Store all translations as a simple object which is available
12
  // synchronously and bundled with the rest of the code.
13
  // Also, `require` isn't usually valid JS, so skip type checking for that:
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
  const translationsContext = (require as any).context(
×
16
    "../../../privaterelay/locales",
17
    true,
18
    /\.ftl$/,
19
  );
20
  const RESOURCES: Record<string, FluentResource[]> = {};
×
21

22
  for (const fileName of translationsContext.keys()) {
×
23
    // Filenames are formatted as `./<locale>/<module>.ftl`.
24
    // Example: ./en/bundle.ftl
25
    const locale = fileName.split("/")[1];
×
26

27
    if (locale) {
×
28
      RESOURCES[locale] ??= [];
×
29
      RESOURCES[locale].push(new FluentResource(translationsContext(fileName)));
×
30
    }
31
  }
32

33
  // A generator function responsible for building the sequence
34
  // of FluentBundle instances in the order of user's language
35
  // preferences.
36
  function* generateBundles(userLocales: typeof navigator.languages) {
37
    // Choose locales that are best for the user.
38
    const currentLocales = negotiateLanguages(
×
39
      // During pre-render, no locales are available yet. To avoid a mismatch
40
      // between pre-rendered HTML and the DOM generated in the first render,
41
      // we don't load locales dependent on the user's preferences on first
42
      // render either:
43
      options.deterministicLocales ? [] : (userLocales as string[]),
×
44
      Object.keys(RESOURCES),
45
      { defaultLocale: "en" },
46
    );
47

48
    for (const locale of currentLocales) {
×
49
      if (typeof RESOURCES[locale] === "undefined") {
×
50
        throw new Error(
×
51
          `Locale [${locale}] not found. You might want to run \`git submodule update --remote\` at the root of this repository?`,
52
        );
53
      }
54
      const bundle = new FluentBundle(locale);
×
55
      RESOURCES[locale].forEach((resource) => {
×
56
        bundle.addResource(resource);
×
57
      });
58
      if (locale === "en") {
×
59
        // `require` isn't usually valid JS, so skip type checking for that:
60
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
        const pendingTranslations = (require as any)(
×
62
          "../../pendingTranslations.ftl",
63
        );
64
        const pendingTranslationsResource = new FluentResource(
×
65
          pendingTranslations,
66
        );
67
        bundle.addResource(pendingTranslationsResource);
×
68
        if (process.env.NEXT_PUBLIC_DEBUG === "true") {
×
69
          // All string IDs in `pendingTranslations.ftl`:
70
          const pendingTranslationsIds = pendingTranslationsResource.body.map(
×
71
            (stringData) => stringData.id,
×
72
          );
73
          // All string IDs in all English FTL files:
74
          const mergedEnglishTranslationIds = RESOURCES.en.flatMap(
×
75
            (enResource) => {
76
              return enResource.body.map((stringData) => stringData.id);
×
77
            },
78
          );
79
          const unmergedStrings = pendingTranslationsIds.filter(
×
80
            (id) => !mergedEnglishTranslationIds.includes(id),
×
81
          );
82
          if (unmergedStrings.length > 0) {
×
83
            console.warn(
×
84
              `The following ${unmergedStrings.length} strings have not yet been merged into the l10n repository, and thus cannot be translated yet:`,
85
              unmergedStrings,
86
            );
87
          }
88
        }
89
      }
90
      yield bundle;
×
91
    }
92
  }
93

94
  // To enable server-side rendering, all tags are converted to plain text nodes.
95
  // They will be upgraded to regular HTML elements in the browser:
96
  const parseMarkup: MarkupParser | undefined =
97
    typeof document === "undefined"
×
98
      ? (str: string) => [
×
99
          {
100
            nodeName: "#text",
101
            textContent: str.replace(/<(.*?)>/g, ""),
102
          } as Node,
103
        ]
104
      : undefined;
105

106
  // The ReactLocalization instance stores and caches the sequence of generated
107
  // bundles. You can store it in your app's state.
108
  const l10n = new ReactLocalization(
×
109
    generateBundles(
110
      typeof navigator !== "undefined" ? navigator.languages : [],
×
111
    ),
112
    parseMarkup,
113
  );
114
  return l10n;
×
115
}
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

© 2025 Coveralls, Inc