Semantic search is gated by Minimum memory + semanticFeatureGate --- triggered by Nimbus smartWindowFeatureGate -- triggered by SmartWindow enrollment only + Locale matching from config (en, fr) We are using English contextual embeddings for France because they work as a fuzzy keyword matcher and perform ok with French. Those are gated by a new pref which matches locale fr-* or english/french in country "FR" Note that this has been restructured to use a Builder class for EmbeddingsGenerator to reduce the chance of a change in embeddings format if the country changes while the browser is running. If a user goes from France to the UK for several weeks and restarts their browser, the embeddings will rebuild. Differential Revision: https://phabricator.services.mozilla.com/D316141
95 lines
3.2 KiB
JavaScript
95 lines
3.2 KiB
JavaScript
/* 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/. */
|
|
|
|
/**
|
|
* Shared matching for features that ship market by market, gated on the pair
|
|
* (home region, app locale).
|
|
*
|
|
* A list is a Map-like array of `[region, localePatterns]` entries, normally
|
|
* held as JSON in a pref so that it can be adjusted through Nimbus, for example
|
|
* `[["US",["en-*"]],["FR",["en-*","fr-*"]]]`. A locale pattern ending with "*"
|
|
* matches any locale starting with its prefix, and the region "*" matches any
|
|
* region, including one that has not been detected yet. Entries are OR-ed, so
|
|
* `[["FR",["en-*"]],["*",["fr-*"]]]` reads as "France in English, or a French
|
|
* locale anywhere".
|
|
*/
|
|
|
|
/**
|
|
* @typedef {[string, string[]][]} RegionLocaleEntries
|
|
*/
|
|
|
|
/**
|
|
* An immutable region / locale list, normally built from a pref through
|
|
* {@link RegionLocaleMap.fromJSON} and queried with
|
|
* {@link RegionLocaleMap.matches}.
|
|
*/
|
|
export class RegionLocaleMap {
|
|
/** @type {Map<string, string[]>} */
|
|
#localePatternsByRegion;
|
|
|
|
/**
|
|
* @param {RegionLocaleEntries} [entries]
|
|
* Region / locale pattern pairs. Regions and patterns are normalized, so
|
|
* the casing used here doesn't matter.
|
|
*/
|
|
constructor(entries = []) {
|
|
this.#localePatternsByRegion = new Map(
|
|
entries.map(([region, localePatterns]) => [
|
|
region.toUpperCase(),
|
|
localePatterns.map(pattern => pattern.toLowerCase()),
|
|
])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Builds a map from the JSON string held by a pref.
|
|
*
|
|
* @param {string} json
|
|
* JSON encoding of {@link RegionLocaleEntries}.
|
|
* @param {object} [options]
|
|
* @param {RegionLocaleEntries} [options.fallback]
|
|
* Entries to use when `json` cannot be parsed. Note that an empty string is
|
|
* not a parse failure: users may empty a pref to disable a feature, without
|
|
* knowing it should hold a JSON string, so that yields an empty map.
|
|
* @param {Function} [options.onInvalid]
|
|
* Invoked with the unparseable value, to let the caller log it.
|
|
* @returns {RegionLocaleMap}
|
|
*/
|
|
static fromJSON(json, { fallback = [], onInvalid } = {}) {
|
|
if (json === "") {
|
|
return new RegionLocaleMap();
|
|
}
|
|
try {
|
|
return new RegionLocaleMap(JSON.parse(json));
|
|
} catch (ex) {
|
|
onInvalid?.(json);
|
|
return new RegionLocaleMap(fallback);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the given region and locale are covered by this map.
|
|
*
|
|
* @param {?string} region
|
|
* Region code, may be null before region detection has resolved. Only the
|
|
* "*" entry can then match.
|
|
* @param {string} appLocale
|
|
* BCP 47 language tag.
|
|
* @returns {boolean}
|
|
*/
|
|
matches(region, appLocale) {
|
|
// Per BCP-47 comparisons must be performed in a case-insensitive manner.
|
|
appLocale = appLocale.toLowerCase();
|
|
const localePatterns = [
|
|
...(this.#localePatternsByRegion.get(region?.toUpperCase()) ?? []),
|
|
...(this.#localePatternsByRegion.get("*") ?? []),
|
|
];
|
|
return localePatterns.some(pattern =>
|
|
pattern.endsWith("*")
|
|
? appLocale.startsWith(pattern.replace(/-?\*$/, ""))
|
|
: pattern == appLocale
|
|
);
|
|
}
|
|
}
|