diff --git a/lib/i18n.js b/lib/i18n.js
index 130f0c94dfa92b2e2b9da0bb21f0928393fb1c4e..c6600cfeaae9c33778374f7683e26b2c8e7776a0 100644
--- a/lib/i18n.js
+++ b/lib/i18n.js
@@ -263,5 +263,7 @@ var format = exports.format = function (fmt, obj, named) {
     return fmt.replace(/%\(\s*([^)]+)\s*\)/g, function(m, v){
       return String(obj[v]);
     });
+  } else {
+    return fmt;
   }
 };
diff --git a/resources/static/common/js/lib/winchan.js b/resources/static/common/js/lib/winchan.js
index 7e8188e04bc4f11954f5c39c6eb69b8d8518ca20..b31cd2263105de65cf3f27a70f82036295b7e51e 100644
--- a/resources/static/common/js/lib/winchan.js
+++ b/resources/static/common/js/lib/winchan.js
@@ -1,5 +1,6 @@
 ;WinChan = (function() {
   var RELAY_FRAME_NAME = "__winchan_relay_frame";
+  var CLOSE_CMD = "die";
 
   // a portable addListener implementation
   function addListener(w, event, cb) {
@@ -16,7 +17,7 @@
   // checking for IE8 or above
   function isInternetExplorer() {
     var rv = -1; // Return value assumes failure.
-    if (navigator.appName == 'Microsoft Internet Explorer') {
+    if (navigator.appName === 'Microsoft Internet Explorer') {
       var ua = navigator.userAgent;
       var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
       if (re.exec(ua) != null)
@@ -28,7 +29,11 @@
   // checking Mobile Firefox (Fennec)
   function isFennec() {
     try {
-      return (navigator.userAgent.indexOf('Fennec/') != -1);
+      // We must check for both XUL and Java versions of Fennec.  Both have
+      // distinct UA strings.
+      var userAgent = navigator.userAgent;
+      return (userAgent.indexOf('Fennec/') != -1) ||  // XUL
+             (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1);   // Java
     } catch(e) {};
     return false;
   }
@@ -42,7 +47,7 @@
   // given a URL, extract the origin
   function extractOrigin(url) {
     if (!/^https?:\/\//.test(url)) url = window.location.href;
-    var m = /^(https?:\/\/[-_a-zA-Z\.0-9:]+)/.exec(url);
+    var m = /^(https?:\/\/[\-_a-zA-Z\.0-9:]+)/.exec(url);
     if (m) return m[1];
     return url;
   }
@@ -129,8 +134,16 @@
         function cleanup() {
           if (iframe) document.body.removeChild(iframe);
           iframe = undefined;
-          if (w) w.close();
-          w = undefined;
+          if (w) {
+            try {
+              w.close();
+            } catch (securityViolation) {
+              // This happens in Opera 12 sometimes
+              // see https://github.com/mozilla/browserid/issues/1844
+              messageTarget.postMessage(CLOSE_CMD, origin);
+            }
+          }
+          w = messageTarget = undefined;
         }
 
         addListener(window, 'unload', cleanup);
@@ -139,22 +152,35 @@
           try {
             var d = JSON.parse(e.data);
             if (d.a === 'ready') messageTarget.postMessage(req, origin);
-            else if (d.a === 'error') cb(d.d);
-            else if (d.a === 'response') {
+            else if (d.a === 'error') {
+              if (cb) {
+                cb(d.d);
+                cb = null;
+              }
+            } else if (d.a === 'response') {
               removeListener(window, 'message', onMessage);
               removeListener(window, 'unload', cleanup);
               cleanup();
-              cb(null, d.d);
+              if (cb) {
+                cb(null, d.d);
+                cb = null;
+              }
             }
-          } catch(e) { }
-        };
+          } catch(err) { }
+        }
 
         addListener(window, 'message', onMessage);
 
         return {
           close: cleanup,
           focus: function() {
-            if (w) w.focus();
+            if (w) {
+              try {
+                w.focus();
+              } catch (e) {
+                // IE7 blows up here, do nothing
+              }
+            }
           }
         };
       },
@@ -175,7 +201,7 @@
           o = e.origin;
           try {
             d = JSON.parse(e.data);
-          } catch(e) { }
+          } catch(err) { }
           if (cb) {
             // this setTimeout is critically important for IE8 -
             // in ie8 sometimes addListener for 'message' can synchronously
@@ -188,7 +214,14 @@
             }, 0);
           }
         }
+
+        function onDie(e) {
+          if (e.data === CLOSE_CMD) {
+            try { window.close(); } catch (o_O) {}
+          }
+        }
         addListener(isIE ? msgTarget : window, 'message', onMessage);
+        addListener(isIE ? msgTarget : window, 'message', onDie);
 
         // we cannot post to our parent that we're ready before the iframe
         // is loaded. (IE specific possible failure)
@@ -203,10 +236,11 @@
 
         // if window is unloaded and the client hasn't called cb, it's an error
         var onUnload = function() {
+          removeListener(isIE ? msgTarget : window, 'message', onDie);
           if (cb) doPost({ a: 'error', d: 'client closed window' });
           cb = undefined;
           // explicitly close the window, in case the client is trying to reload or nav
-          try { window.close(); } catch (e) { };
+          try { window.close(); } catch (e) { }
         };
         addListener(window, 'unload', onUnload);
         return {
diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js
index 3516f7af707e0fc15aa87f1f4140ec8b68b34539..2b173421215b13cfc64079fc43931af2937f550b 100644
--- a/resources/static/include_js/include.js
+++ b/resources/static/include_js/include.js
@@ -628,6 +628,7 @@
   // local embedded copy of winchan: http://github.com/lloyd/winchan
   ;WinChan = (function() {
     var RELAY_FRAME_NAME = "__winchan_relay_frame";
+    var CLOSE_CMD = "die";
 
     // a portable addListener implementation
     function addListener(w, event, cb) {
@@ -644,7 +645,7 @@
     // checking for IE8 or above
     function isInternetExplorer() {
       var rv = -1; // Return value assumes failure.
-      if (navigator.appName == 'Microsoft Internet Explorer') {
+      if (navigator.appName === 'Microsoft Internet Explorer') {
         var ua = navigator.userAgent;
         var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
         if (re.exec(ua) != null)
@@ -658,8 +659,9 @@
       try {
         // We must check for both XUL and Java versions of Fennec.  Both have
         // distinct UA strings.
+        var userAgent = navigator.userAgent;
         return (userAgent.indexOf('Fennec/') != -1) ||  // XUL
-                 (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1);   // Java
+               (userAgent.indexOf('Firefox/') != -1 && userAgent.indexOf('Android') != -1);   // Java
       } catch(e) {};
       return false;
     }
@@ -673,7 +675,7 @@
     // given a URL, extract the origin
     function extractOrigin(url) {
       if (!/^https?:\/\//.test(url)) url = window.location.href;
-      var m = /^(https?:\/\/[-_a-zA-Z\.0-9:]+)/.exec(url);
+      var m = /^(https?:\/\/[\-_a-zA-Z\.0-9:]+)/.exec(url);
       if (m) return m[1];
       return url;
     }
@@ -683,7 +685,7 @@
       var loc = window.location;
       var frames = window.opener.frames;
       var origin = loc.protocol + '//' + loc.host;
-      for (var i = frames.length - 1; i >= 0; i++) {
+      for (var i = frames.length - 1; i >= 0; i--) {
         try {
           if (frames[i].location.href.indexOf(origin) === 0 &&
               frames[i].name === RELAY_FRAME_NAME)
@@ -760,8 +762,16 @@
           function cleanup() {
             if (iframe) document.body.removeChild(iframe);
             iframe = undefined;
-            if (w) w.close();
-            w = undefined;
+            if (w) {
+              try {
+                w.close();
+              } catch (securityViolation) {
+                // This happens in Opera 12 sometimes
+                // see https://github.com/mozilla/browserid/issues/1844
+                messageTarget.postMessage(CLOSE_CMD, origin);
+              }
+            }
+            w = messageTarget = undefined;
           }
 
           addListener(window, 'unload', cleanup);
@@ -771,15 +781,21 @@
               var d = JSON.parse(e.data);
               if (d.a === 'ready') messageTarget.postMessage(req, origin);
               else if (d.a === 'error') {
-                if (cb) { cb(d.d); cb = null; }
+                if (cb) {
+                  cb(d.d);
+                  cb = null;
+                }
               } else if (d.a === 'response') {
                 removeListener(window, 'message', onMessage);
                 removeListener(window, 'unload', cleanup);
                 cleanup();
-                if (cb) { cb(null, d.d); cb = null; }
+                if (cb) {
+                  cb(null, d.d);
+                  cb = null;
+                }
               }
-            } catch(e) { }
-          };
+            } catch(err) { }
+          }
 
           addListener(window, 'message', onMessage);
 
@@ -789,9 +805,8 @@
               if (w) {
                 try {
                   w.focus();
-                }
-                catch(e) {
-                  /* IE7 blows up here, do nothing */
+                } catch (e) {
+                  // IE7 blows up here, do nothing
                 }
               }
             }
diff --git a/tests/i18n-tests.js b/tests/i18n-tests.js
index 9001546ba514aca3041a6ccb7f3270b49946f6e2..88287ec5d0ebed4d9fb3ec04b7a8134999a6ccfe 100755
--- a/tests/i18n-tests.js
+++ b/tests/i18n-tests.js
@@ -27,7 +27,7 @@ suite.addBatch({
   "format a string with named values": {
     topic: function () {
       var params = { salutation: "Hello", place: "World" };
-      return i18n.format("%(salutation)s %(place)s!", params, true);
+      return i18n.format("%(salutation) %(place)!", params);
     },
     "was interpolated": function (err, str) {
       assert.equal(str, "Hello World!");