summaryrefslogtreecommitdiffstats
path: root/browser/components/storybook/.storybook/fluent-utils.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/storybook/.storybook/fluent-utils.mjs')
-rw-r--r--browser/components/storybook/.storybook/fluent-utils.mjs122
1 files changed, 122 insertions, 0 deletions
diff --git a/browser/components/storybook/.storybook/fluent-utils.mjs b/browser/components/storybook/.storybook/fluent-utils.mjs
new file mode 100644
index 0000000000..f38e99770b
--- /dev/null
+++ b/browser/components/storybook/.storybook/fluent-utils.mjs
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { DOMLocalization } from "@fluent/dom";
+import { FluentBundle, FluentResource } from "@fluent/bundle";
+import { addons } from "@storybook/addons";
+import { PSEUDO_STRATEGY_TRANSFORMS } from "./l10n-pseudo.mjs";
+import {
+ FLUENT_SET_STRINGS,
+ UPDATE_STRATEGY_EVENT,
+ STRATEGY_DEFAULT,
+ PSEUDO_STRATEGIES,
+} from "./addon-pseudo-localization/constants.mjs";
+
+let loadedResources = new Map();
+let currentStrategy;
+let storybookBundle = new FluentBundle("en-US", {
+ transform(str) {
+ if (currentStrategy in PSEUDO_STRATEGY_TRANSFORMS) {
+ return PSEUDO_STRATEGY_TRANSFORMS[currentStrategy](str);
+ }
+ return str;
+ },
+});
+
+// Listen for update events from addon-pseudo-localization.
+const channel = addons.getChannel();
+channel.on(UPDATE_STRATEGY_EVENT, updatePseudoStrategy);
+channel.on(FLUENT_SET_STRINGS, ftlContents => {
+ let resource = new FluentResource(ftlContents);
+ for (let message of resource.body) {
+ let existingMessage = storybookBundle.getMessage(message.id);
+ existingMessage.value = message.value;
+ existingMessage.attributes = message.attributes;
+ }
+ document.l10n.translateRoots();
+});
+
+/**
+ * Updates "currentStrategy" when the selected pseudo localization strategy
+ * changes, which in turn changes the transform used by the Fluent bundle.
+ *
+ * @param {string} strategy
+ * Pseudo localization strategy. Can be "default", "accented", or "bidi".
+ */
+function updatePseudoStrategy(strategy = STRATEGY_DEFAULT) {
+ if (strategy !== currentStrategy && PSEUDO_STRATEGIES.includes(strategy)) {
+ currentStrategy = strategy;
+ document.l10n.translateRoots();
+ }
+}
+
+export function connectFluent() {
+ document.l10n = new DOMLocalization([], generateBundles);
+ document.l10n.connectRoot(document.documentElement);
+ document.l10n.translateRoots();
+}
+
+function* generateBundles() {
+ yield* [storybookBundle];
+}
+
+export async function insertFTLIfNeeded(fileName) {
+ if (loadedResources.has(fileName)) {
+ return;
+ }
+
+ // This should be browser, locales-preview or toolkit.
+ let [root, ...rest] = fileName.split("/");
+ let ftlContents;
+
+ // TODO(mstriemer): These seem like they could be combined but I don't want
+ // to fight with webpack anymore.
+ if (root == "toolkit") {
+ // eslint-disable-next-line no-unsanitized/method
+ let imported = await import(
+ /* webpackInclude: /.*[\/\\].*\.ftl$/ */
+ `toolkit/locales/en-US/${fileName}`
+ );
+ ftlContents = imported.default;
+ } else if (root == "browser") {
+ // eslint-disable-next-line no-unsanitized/method
+ let imported = await import(
+ /* webpackInclude: /.*[\/\\].*\.ftl$/ */
+ `browser/locales/en-US/${fileName}`
+ );
+ ftlContents = imported.default;
+ } else if (root == "locales-preview") {
+ // eslint-disable-next-line no-unsanitized/method
+ let imported = await import(
+ /* webpackInclude: /\.ftl$/ */
+ `browser/locales-preview/${rest}`
+ );
+ ftlContents = imported.default;
+ } else if (root == "branding") {
+ // eslint-disable-next-line no-unsanitized/method
+ let imported = await import(
+ /* webpackInclude: /\.ftl$/ */
+ `browser/branding/nightly/locales/en-US/${rest}`
+ );
+ ftlContents = imported.default;
+ }
+
+ if (loadedResources.has(fileName)) {
+ // Seems possible we've attempted to load this twice before the first call
+ // resolves, so once the first load is complete we can abandon the others.
+ return;
+ }
+
+ provideFluent(ftlContents, fileName);
+}
+
+export function provideFluent(ftlContents, fileName) {
+ let ftlResource = new FluentResource(ftlContents);
+ storybookBundle.addResource(ftlResource);
+ if (fileName) {
+ loadedResources.set(fileName, ftlResource);
+ }
+ document.l10n.translateRoots();
+ return ftlResource;
+}