Bug 1426063 - Add PLATFORM built-in to Fluent in Gecko. r=Pike
authorZibi Braniecki <zbraniecki@mozilla.com>
Wed, 20 Dec 2017 14:25:10 -0800
changeset 403306 2b7d42d527af582a465e49187fe387442d524a7c
parent 403305 346018e93a218e86f344d509a41a93e2087d1aba
child 403307 4d27404fd3f2b01dc6a2c9a584c6c480ed79b7bc
child 403309 216fac39a9f44b708a4242a47a8ee8296eed3a34
child 403313 cd9b76bd5a5279cc5db71240b0ca264df5cf7ba2
push id33422
push usernbeleuzu@mozilla.com
push dateSun, 11 Feb 2018 09:44:16 +0000
treeherdermozilla-central@2b7d42d527af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersPike
bugs1426063
milestone60.0a1
first release with
nightly linux32
2b7d42d527af / 60.0a1 / 20180211100408 / files
nightly linux64
2b7d42d527af / 60.0a1 / 20180211100408 / files
nightly mac
2b7d42d527af / 60.0a1 / 20180211100408 / files
nightly win32
2b7d42d527af / 60.0a1 / 20180211100408 / files
nightly win64
2b7d42d527af / 60.0a1 / 20180211100408 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1426063 - Add PLATFORM built-in to Fluent in Gecko. r=Pike In order to allow localizations to produce different string depending on the platform, we need to add a Gecko-specific built-in function to the MessageContext. I'm explicitly listing the variables which we pass, rather than just passing the value in order to ensure we don't start returning values we didn't plan for in case the AppConstants.platform gets updated. MozReview-Commit-ID: 1KZ6bf6zIY2
intl/l10n/L10nRegistry.jsm
intl/l10n/test/test_localization.js
--- a/intl/l10n/L10nRegistry.jsm
+++ b/intl/l10n/L10nRegistry.jsm
@@ -1,8 +1,9 @@
+const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
 const { Services } = ChromeUtils.import('resource://gre/modules/Services.jsm', {});
 const { MessageContext } = ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
 Components.utils.importGlobalProperties(["fetch"]); /* globals fetch */
 
 /**
  * L10nRegistry is a localization resource management system for Gecko.
  *
  * It manages the list of resource sources provided with the app and allows
@@ -213,16 +214,38 @@ async function* generateContextsForLocal
     } else {
       // otherwise recursively load another generator that walks over the
       // partially resolved list of sources.
       yield * generateContextsForLocale(locale, sourcesOrder, resourceIds, order);
     }
   }
 }
 
+const  MSG_CONTEXT_OPTIONS = {
+  functions: {
+    /**
+     * PLATFORM is a built-in allowing localizers to differentiate message
+     * variants depending on the target platform.
+     */
+    PLATFORM: () => {
+      switch (AppConstants.platform) {
+        case "linux":
+        case "android":
+          return AppConstants.platform;
+        case "win":
+          return "windows";
+        case "macosx":
+          return "macos";
+        default:
+          return "other";
+      }
+    }
+  }
+}
+
 /**
  * Generates a single MessageContext by loading all resources
  * from the listed sources for a given locale.
  *
  * The function casts all error cases into a Promise that resolves with
  * value `null`.
  * This allows the caller to be an async generator without using
  * try/catch clauses.
@@ -239,17 +262,17 @@ function generateContext(locale, sources
   }
 
   const fetchPromises = resourceIds.map((resourceId, i) => {
     return L10nRegistry.sources.get(sourcesOrder[i]).fetchFile(locale, resourceId);
   });
 
   const ctxPromise = Promise.all(fetchPromises).then(
     dataSets => {
-      const ctx = new MessageContext(locale);
+      const ctx = new MessageContext(locale, MSG_CONTEXT_OPTIONS);
       for (const data of dataSets) {
         if (data === null) {
           return null;
         }
         ctx.addMessages(data);
       }
       return ctx;
     },
--- a/intl/l10n/test/test_localization.js
+++ b/intl/l10n/test/test_localization.js
@@ -1,11 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
 const { Localization } = ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
 
 add_task(function test_methods_presence() {
   equal(typeof Localization.prototype.formatValues, "function");
   equal(typeof Localization.prototype.formatMessages, "function");
   equal(typeof Localization.prototype.formatValue, "function");
 });
 
@@ -25,17 +26,17 @@ add_task(async function test_methods_cal
 
   L10nRegistry.load = async function(url) {
     return fs[url];
   }
 
   const source = new FileSource('test', ['de', 'en-US'], '/localization/{locale}');
   L10nRegistry.registerSource(source);
 
-  async function * generateMessages(resIds) {
+  async function* generateMessages(resIds) {
     yield * await L10nRegistry.generateContexts(['de', 'en-US'], resIds);
   }
 
   const l10n = new Localization([
     '/browser/menu.ftl'
   ], generateMessages);
 
   let values = await l10n.formatValues([['key'], ['key2']]);
@@ -43,8 +44,52 @@ add_task(async function test_methods_cal
   equal(values[0], '[de] Value2');
   equal(values[1], '[en] Value3');
 
   L10nRegistry.sources.clear();
   L10nRegistry.load = originalLoad;
   LocaleService.setRequestedLocales(originalRequested);
 });
 
+add_task(async function test_builtins() {
+  const { L10nRegistry, FileSource } =
+    Components.utils.import("resource://gre/modules/L10nRegistry.jsm", {});
+
+  const known_platforms = {
+    'linux': 'linux',
+    'win': 'windows',
+    'macosx': 'macos',
+    'android': 'android',
+  };
+
+  const fs = {
+    '/localization/en-US/test.ftl': `
+key = { PLATFORM() ->
+        ${ Object.values(known_platforms).map(
+              name => `      [${ name }] ${ name.toUpperCase() } Value\n`).join('') }
+       *[other] OTHER Value
+    }`,
+  };
+  const originalLoad = L10nRegistry.load;
+
+  L10nRegistry.load = async function(url) {
+    return fs[url];
+  }
+
+  const source = new FileSource('test', ['en-US'], '/localization/{locale}');
+  L10nRegistry.registerSource(source);
+
+  async function* generateMessages(resIds) {
+    yield * await L10nRegistry.generateContexts(['en-US'], resIds);
+  }
+
+  const l10n = new Localization([
+    '/test.ftl'
+  ], generateMessages);
+
+  let values = await l10n.formatValues([['key']]);
+
+  ok(values[0].includes(
+    `${ known_platforms[AppConstants.platform].toUpperCase() } Value`));
+
+  L10nRegistry.sources.clear();
+  L10nRegistry.load = originalLoad;
+});