diff --git a/chrome/content/zotero/xpcom/connector/translator.js b/chrome/content/zotero/xpcom/connector/translator.js
index 56acb305f..7c90db0d9 100644
--- a/chrome/content/zotero/xpcom/connector/translator.js
+++ b/chrome/content/zotero/xpcom/connector/translator.js
@@ -141,66 +141,47 @@ Zotero.Translators = new function() {
 	 * @return {Promise<Array[]>} - A promise for a 2-item array containing an array of translators and
 	 *     an array of functions for converting URLs from proper to proxied forms
 	 */
-	this.getWebTranslatorsForLocation = Zotero.Promise.method(function (uri) {
+	this.getWebTranslatorsForLocation = Zotero.Promise.method(function (URI, rootURI) {
+		var isFrame = URI !== rootURI;
 		if(!_initialized) Zotero.Translators.init();
 		var allTranslators = _cache["web"];
 		var potentialTranslators = [];
-		var searchURIs = [uri];
-		
-		Zotero.debug("Translators: Looking for translators for "+uri);
-		
-		// if there is a subdomain that is also a TLD, also test against URI with the domain
-		// dropped after the TLD
-		// (i.e., www.nature.com.mutex.gmu.edu => www.nature.com)
-		var m = /^(https?:\/\/)([^\/]+)/i.exec(uri);
-		var properHosts = [];
-		var proxyHosts = [];
-		if(m) {
-			// First, drop the 0- if it exists (this is an III invention)
-			var host = m[2];
-			if(host.substr(0, 2) === "0-") host = host.substr(2);
-			var hostnames = host.split(".");
-			for(var i=1; i<hostnames.length-2; i++) {
-				if(TLDS[hostnames[i].toLowerCase()]) {
-					var properHost = hostnames.slice(0, i+1).join(".");
-					searchURIs.push(m[1]+properHost+uri.substr(m[0].length));
-					properHosts.push(properHost);
-					proxyHosts.push(hostnames.slice(i+1).join("."));
-				}
-			}
-		}
-		
 		var converterFunctions = [];
+		
+		var rootSearchURIs = this.getSearchURIs(rootURI);
+		var frameSearchURIs = isFrame ? this.getSearchURIs(URI) : rootSearchURIs;
+
+		Zotero.debug("Translators: Looking for translators for "+Object.keys(frameSearchURIs).join(', '));
+
 		for(var i=0; i<allTranslators.length; i++) {
-			for(var j=0; j<searchURIs.length; j++) {
-				// don't attempt to use translators with no target that can't be run in this browser
+			var translator = allTranslators[i];
+			if (isFrame && !translator.webRegexp.all) {
+				continue;
+			}
+			rootURIsLoop:
+			for(var rootSearchURI in rootSearchURIs) {
+				var isGeneric = !allTranslators[i].webRegexp.root;
+				// don't attempt to use generic translators that can't be run in this browser
 				// since that would require transmitting every page to Zotero host
-				if(!allTranslators[i].webRegexp
-						&& allTranslators[i].runMode !== Zotero.Translator.RUN_MODE_IN_BROWSER) {
+				if(isGeneric && allTranslators[i].runMode !== Zotero.Translator.RUN_MODE_IN_BROWSER) {
 					continue;
 				}
-				
-				if(!allTranslators[i].webRegexp
-						|| (uri.length < 8192 && allTranslators[i].webRegexp.test(searchURIs[j]))) {
-					// add translator to list
-					potentialTranslators.push(allTranslators[i]);
-					
-					if(j === 0) {
-						converterFunctions.push(null);
-					} else if(Zotero.isBrowserExt || Zotero.isSafari) {
-						// in Chrome/Safari, the converterFunction needs to be passed as JSON, so
-						// just push an array with the proper and proxyHosts
-						converterFunctions.push([properHosts[j-1], proxyHosts[j-1]]);
-					} else {
-						// in Firefox, push the converterFunction
-						converterFunctions.push(new function() {
-							var re = new RegExp('^https?://(?:[^/]\\.)?'+Zotero.Utilities.quotemeta(properHosts[j-1])+'(?=/)', "gi");
-							var proxyHost = proxyHosts[j-1].replace(/\$/g, "$$$$");
-							return function(uri) { return uri.replace(re, "$&."+proxyHost) };
-						});
+
+				var rootURIMatches = isGeneric || rootSearchURI.length < 8192 && translator.webRegexp.root.test(rootSearchURI);
+				if (translator.webRegexp.all && rootURIMatches) {
+					for (var frameSearchURI in frameSearchURIs) {
+						var frameURIMatches = frameSearchURI.length < 8192 && translator.webRegexp.all.test(frameSearchURI);
+							
+						if (frameURIMatches) {
+							potentialTranslators.push(translator);
+							converterFunctions.push(frameSearchURIs[frameSearchURI]);
+							// prevent adding the translator multiple times
+							break rootURIsLoop;
+						}
 					}
-					
-					// don't add translator more than once
+				} else if(!isFrame && (isGeneric || rootURIMatches)) {
+					potentialTranslators.push(translator);
+					converterFunctions.push(rootSearchURIs[rootSearchURI]);
 					break;
 				}
 			}
@@ -212,6 +193,47 @@ Zotero.Translators = new function() {
 		});
 	});
 	
+	/**
+	 * Get the array of searchURIs and related proxy converter functions
+	 * 
+	 * @param {String} URI to get searchURIs and converterFunctions for
+	 */
+	this.getSearchURIs = function(URI) {
+		var searchURIs = {};
+		searchURIs[URI] = null;
+		
+		// if there is a subdomain that is also a TLD, also test against URI with the domain
+		// dropped after the TLD
+		// (i.e., www.nature.com.mutex.gmu.edu => www.nature.com)
+		var m = /^(https?:\/\/)([^\/]+)/i.exec(URI);
+		if (m) {
+			// First, drop the 0- if it exists (this is an III invention)
+			var host = m[2];
+			if(host.substr(0, 2) === "0-") host = host.substr(2);
+			var hostnames = host.split(".");
+			for (var i=1; i<hostnames.length-2; i++) {
+				if (TLDS[hostnames[i].toLowerCase()]) {
+					var properHost = hostnames.slice(0, i+1).join(".");
+					var proxyHost = hostnames.slice(i+1).join(".");
+					var searchURI = m[1]+properHost+URI.substr(m[0].length);
+					if(Zotero.isBrowserExt || Zotero.isSafari) {
+						// in Chrome/Safari, the converterFunction needs to be passed as JSON, so
+						// just push an array with the proper and proxyHosts
+						searchURIs[searchURI] = [properHost, proxyHost];
+					} else {
+						// in Firefox, add a converterFunction
+						searchURIs[searchURI] = new function() {
+							var re = new RegExp('^https?://(?:[^/]+\\.)?'+Zotero.Utilities.quotemeta(properHost)+'(?=/)', "gi");
+							var _proxyHost = proxyHost.replace(/\$/g, "$$$$");
+							return function(uri) { return uri.replace(re, "$&."+_proxyHost) };
+						};
+					}
+				}
+			}
+		}
+		return searchURIs;
+	};
+	
 	/**
 	 * Converts translators to JSON-serializable objects
 	 */
@@ -361,9 +383,12 @@ Zotero.Translators.CodeGetter.prototype.getAll = function () {
 
 var TRANSLATOR_REQUIRED_PROPERTIES = ["translatorID", "translatorType", "label", "creator", "target",
 		"priority", "lastUpdated"];
+var TRANSLATOR_OPTIONAL_PROPERTIES = ["targetAll", "browserSupport", "minVersion", "maxVersion",
+		"inRepository", "configOptions", "displayOptions",
+		"hiddenPrefs", "itemType"];
 var TRANSLATOR_PASSING_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES
 		.concat(["targetAll", "browserSupport", "code", "runMode", "itemType"]);
-var TRANSLATOR_SAVE_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES.concat(["browserSupport"]);
+var TRANSLATOR_SAVE_PROPERTIES = TRANSLATOR_REQUIRED_PROPERTIES.concat(["browserSupport", "targetAll"]);
 /**
  * @class Represents an individual translator
  * @constructor
@@ -407,6 +432,12 @@ Zotero.Translator.prototype.init = function(info) {
 			this[property] = info[property];
 		}
 	}
+	for(var i=0; i<TRANSLATOR_OPTIONAL_PROPERTIES.length; i++) {
+		var property = TRANSLATOR_OPTIONAL_PROPERTIES[i];
+		if(info[property] !== undefined) {
+			this[property] = info[property];
+		}
+	}
 	
 	this.browserSupport = info["browserSupport"] ? info["browserSupport"] : "g";
 	
diff --git a/chrome/content/zotero/xpcom/server_connector.js b/chrome/content/zotero/xpcom/server_connector.js
index 030d2dd1b..c1aaf88f4 100644
--- a/chrome/content/zotero/xpcom/server_connector.js
+++ b/chrome/content/zotero/xpcom/server_connector.js
@@ -106,13 +106,10 @@ Zotero.Server.Connector.GetTranslators.prototype = {
 	
 	"_serializeTranslators":function(translators) {
 		var responseData = [];
-		for each(var translator in translators) {
-			let serializableTranslator = {};
-			for (let key of ["translatorID", "translatorType", "label", "creator", "target",
-					"minVersion", "maxVersion", "priority", "browserSupport", "inRepository", "lastUpdated"]) {
-				serializableTranslator[key] = translator[key];
-			}
-			responseData.push(serializableTranslator);
+		let properties = ["translatorID", "translatorType", "label", "creator", "target", "targetAll",
+			"minVersion", "maxVersion", "priority", "browserSupport", "inRepository", "lastUpdated"];
+		for (var translator of translators) {
+			responseData.push(translator.serialize(properties));
 		}
 		return responseData;
 	}
diff --git a/chrome/content/zotero/xpcom/translation/translators.js b/chrome/content/zotero/xpcom/translation/translators.js
index f063a2027..32bf62c29 100644
--- a/chrome/content/zotero/xpcom/translation/translators.js
+++ b/chrome/content/zotero/xpcom/translation/translators.js
@@ -278,7 +278,7 @@ Zotero.Translators = new function() {
 		
 		return this.getAllForType(type).then(function(allTranslators) {
 			var potentialTranslators = [];
-			var translatorConverterFunctions = [];
+			var converterFunctions = [];
 			
 			var rootSearchURIs = this.getSearchURIs(rootURI);
 			var frameSearchURIs = isFrame ? this.getSearchURIs(URI) : rootSearchURIs;
@@ -286,12 +286,10 @@ Zotero.Translators = new function() {
 			Zotero.debug("Translators: Looking for translators for "+Object.keys(frameSearchURIs).join(', '));
 			
 			for (let translator of allTranslators) {
-				translatorLoop:
+				rootURIsLoop:
 				for (let rootSearchURI in rootSearchURIs) {
-					let isGeneric = (!translator.webRegexp.root && translator.runMode === Zotero.Translator.RUN_MODE_IN_BROWSER);
-					if (!isGeneric && !translator.webRegexp.root) {
-						continue;
-					}
+					let isGeneric = !translator.webRegexp.root;
+					
 					let rootURIMatches = isGeneric || rootSearchURI.length < 8192 && translator.webRegexp.root.test(rootSearchURI);
 					if (translator.webRegexp.all && rootURIMatches) {
 						for (let frameSearchURI in frameSearchURIs) {
@@ -299,21 +297,21 @@ Zotero.Translators = new function() {
 								
 							if (frameURIMatches) {
 								potentialTranslators.push(translator);
-								translatorConverterFunctions.push(frameSearchURIs[frameSearchURI]);
+								converterFunctions.push(frameSearchURIs[frameSearchURI]);
 								// prevent adding the translator multiple times
-								break translatorLoop;
+								break rootURIsLoop;
 							}
 						}
 					}
 					else if(!isFrame && (isGeneric || rootURIMatches)) {
 						potentialTranslators.push(translator);
-						translatorConverterFunctions.push(rootSearchURIs[rootSearchURI]);
+						converterFunctions.push(rootSearchURIs[rootSearchURI]);
 						break;
 					}
 				}
 			}
 			
-			return [potentialTranslators, translatorConverterFunctions];
+			return [potentialTranslators, converterFunctions];
 		}.bind(this));
 	},