diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index b04f84ae6b78..e6c4fa943e6a 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1958,9 +1958,10 @@ pref("sidebar.visibility", "always-show");
pref("browser.ml.chat.enabled", false);
pref("browser.ml.chat.hideLocalhost", true);
pref("browser.ml.chat.prompt.prefix", 'I’m on page "%tabTitle%" with "%selection|12000%" selected. ');
-pref("browser.ml.chat.prompts.0", '{"label":"Summarize","value":"Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.","id":"summarize"}');
-pref("browser.ml.chat.prompts.1", '{"label":"Simplify Language","value":"Please rewrite the selection in plain, clear language suitable for a general audience without specialized knowledge. Use all of the following tactics: simple vocabulary; short sentences; active voice; headers and bulleted lists for scannability. Maintain meaning and factual accuracy.","id":"simplify"}');
-pref("browser.ml.chat.prompts.2", '{"label":"Quiz Me","value":"Please quiz me on this selection. Ask me a variety of types of questions, for example multiple choice, true or false, and short answer. Wait for my response before moving on to the next question.","id":"quiz","targeting":"!provider|regExpMatch(\'gemini\')"}');
+pref("browser.ml.chat.prompts.0", '{"id":"summarize","l10nId":"genai-prompts-summarize"}');
+pref("browser.ml.chat.prompts.1", '{"id":"simplify","l10nId":"genai-prompts-simplify"}');
+pref("browser.ml.chat.prompts.2", '{"id":"quiz","l10nId":"genai-prompts-quiz","targeting":"!provider|regExpMatch(\'gemini\')"}');
+pref("browser.ml.chat.prompts.3", '{"id":"explain","l10nId":"genai-prompts-explain","targeting":"channel==\'nightly\'"}');
pref("browser.ml.chat.provider", "");
pref("browser.ml.chat.shortcuts", true);
#ifdef NIGHTLY_BUILD
diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml
index 2e040223295d..5dd337a2ffc9 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -66,6 +66,7 @@
+
diff --git a/browser/components/genai/GenAI.sys.mjs b/browser/components/genai/GenAI.sys.mjs
index f5a360b47522..47c3cbe0fb53 100644
--- a/browser/components/genai/GenAI.sys.mjs
+++ b/browser/components/genai/GenAI.sys.mjs
@@ -16,6 +16,11 @@ ChromeUtils.defineESModuleGetters(lazy, {
clearTimeout: "resource://gre/modules/Timer.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
+ChromeUtils.defineLazyGetter(
+ lazy,
+ "l10n",
+ () => new Localization(["browser/genai.ftl"])
+);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"chatEnabled",
@@ -349,10 +354,16 @@ export const GenAI = {
if (lazy.chatShortcutsCustom) {
vbox.appendChild(document.createXULElement("toolbarseparator"));
const input = vbox.appendChild(document.createElement("input"));
- input.placeholder = `Ask ${
- this.chatProviders.get(lazy.chatProvider)?.name ??
- "AI chatbot"
- }…`;
+ const provider = this.chatProviders.get(
+ lazy.chatProvider
+ )?.name;
+ document.l10n.setAttributes(
+ input,
+ provider
+ ? "genai-input-ask-provider"
+ : "genai-input-ask-generic",
+ { provider }
+ );
input.style.margin = "var(--arrowpanel-menuitem-margin)";
input.addEventListener("mouseover", () => input.focus());
input.addEventListener("change", () => {
@@ -418,9 +429,12 @@ export const GenAI = {
if (!lazy.chatEnabled || lazy.chatProvider == "") {
return;
}
- menu.label = `Ask ${
- this.chatProviders.get(lazy.chatProvider)?.name ?? "AI Chatbot"
- }`;
+ const provider = this.chatProviders.get(lazy.chatProvider)?.name;
+ menu.ownerDocument.l10n.setAttributes(
+ menu,
+ provider ? "genai-menu-ask-provider" : "genai-menu-ask-generic",
+ { provider }
+ );
menu.menupopup?.remove();
await this.addAskChatItems(
nsContextMenu.browser,
@@ -437,9 +451,10 @@ export const GenAI = {
* @param {object} context data used for targeting
* @returns {promise} array of matching prompt objects
*/
- getContextualPrompts(context) {
+ async getContextualPrompts(context) {
// Treat prompt objects as messages to reuse targeting capabilities
const messages = [];
+ const toFormat = [];
Services.prefs.getChildList("browser.ml.chat.prompts.").forEach(pref => {
try {
const promptObj = {
@@ -457,10 +472,20 @@ export const GenAI = {
}
} catch (ex) {}
messages.push(promptObj);
+ if (promptObj.l10nId) {
+ toFormat.push(promptObj);
+ }
} catch (ex) {
console.error("Failed to get prompt pref " + pref, ex);
}
});
+
+ // Apply localized attributes for prompts
+ (await lazy.l10n.formatMessages(toFormat.map(obj => obj.l10nId))).forEach(
+ (msg, idx) =>
+ msg?.attributes.forEach(attr => (toFormat[idx][attr.name] = attr.value))
+ );
+
return lazy.ASRouterTargeting.findMatchingMessage({
messages,
returnAll: true,
diff --git a/browser/components/genai/tests/xpcshell/test_contextual_prompts.js b/browser/components/genai/tests/xpcshell/test_contextual_prompts.js
index 226bac854436..6f35ed78ec20 100644
--- a/browser/components/genai/tests/xpcshell/test_contextual_prompts.js
+++ b/browser/components/genai/tests/xpcshell/test_contextual_prompts.js
@@ -56,3 +56,24 @@ add_task(async function test_prompt_context() {
"Prompt not added for contextual targeting"
);
});
+
+/**
+ * Check that prompts can be localized
+ */
+add_task(async function test_prompt_l10n() {
+ Services.prefs.setStringPref(
+ "browser.ml.chat.prompts.test",
+ JSON.stringify({
+ l10nId: "genai-prompts-summarize",
+ label: "bad",
+ test: true,
+ })
+ );
+ const custom = (await GenAI.getContextualPrompts(DEFAULT_CONTEXT)).find(
+ prompt => prompt.id == null
+ );
+
+ Assert.ok(custom.test, "Found the custom prompt");
+ Assert.notEqual(custom.label, "bad", "Localized label takes priority");
+ Assert.ok(custom.value, "Got localized prompt");
+});
diff --git a/browser/components/preferences/preferences.xhtml b/browser/components/preferences/preferences.xhtml
index e6f8b12715d2..53bdc9c912a6 100644
--- a/browser/components/preferences/preferences.xhtml
+++ b/browser/components/preferences/preferences.xhtml
@@ -54,6 +54,7 @@
+
@@ -67,7 +68,6 @@
-
diff --git a/browser/locales-preview/genai.ftl b/browser/locales/en-US/browser/genai.ftl
similarity index 55%
rename from browser/locales-preview/genai.ftl
rename to browser/locales/en-US/browser/genai.ftl
index 5acca85bcaf0..b126acfaefce 100644
--- a/browser/locales-preview/genai.ftl
+++ b/browser/locales/en-US/browser/genai.ftl
@@ -4,8 +4,6 @@
## Generative AI (GenAI) Settings section
-genai-settings-chat-title =
- .label = AI chatbot
genai-settings-chat-description = Adds the chatbot of your choice to the sidebar, for quick access as you browse. Share feedback
genai-settings-chat-choose = Choose a chatbot
genai-settings-chat-choose-one-menuitem =
@@ -21,3 +19,40 @@ genai-settings-chat-localhost-links = Bring your own private local chatbot such
genai-settings-chat-shortcuts =
.description = Displays a shortcut to prompts when you select text. { -brand-short-name } sends the text, page title, and prompt to the chatbot.
.label = Show prompts on text select
+
+## Chatbot prompts
+## Prompts are plain language ‘instructions’ sent to a chatbot.
+## These prompts have been made concise and direct in English because some chatbot providers
+## have character restrictions and being direct reduces the chance for misinterpretation.
+## When localizing, please be concise and direct, but not at the expense of losing meaning.
+
+# Prompt purpose: help users understand what a selection covers at a glance
+genai-prompts-summarize =
+ .label = Summarize
+ .value = Please summarize the selection using precise and concise language. Use headers and bulleted lists in the summary, to make it scannable. Maintain the meaning and factual accuracy.
+# Prompt purpose: make a selection easier to read
+genai-prompts-simplify =
+ .label = Simplify language
+ .value = Please rewrite the selection using short sentences and simple words. Maintain the meaning and factual accuracy.
+# Prompt purpose: test understanding of selection in an interactive way
+genai-prompts-quiz =
+ .label = Quiz me
+ .value = Please quiz me on this selection. Ask me a variety of types of questions, for example multiple choice, true or false, and short answer. Wait for my response before moving on to the next question.
+# Prompt purpose: helps users understand words, phrases, concepts
+genai-prompts-explain =
+ .label = Explain this
+ .value = Please explain the key concepts in this selection, using simple words. Also, use examples.
+
+## Chatbot menu shortcuts
+
+genai-menu-ask-generic =
+ .label = Ask AI chatbot
+# $provider (string) - name of the provider
+genai-menu-ask-provider =
+ .label = Ask { $provider }
+
+genai-input-ask-generic =
+ .placeholder = Ask AI chatbot…
+# $provider (string) - name of the provider
+genai-input-ask-provider =
+ .placeholder = Ask { $provider }…
diff --git a/browser/locales/jar.mn b/browser/locales/jar.mn
index 857c93c2227b..fd63eaad1e36 100644
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -14,7 +14,6 @@
preview/enUS-searchFeatures.ftl (../components/urlbar/content/enUS-searchFeatures.ftl)
preview/shopping.ftl (../components/shopping/content/shopping.ftl)
preview/onboarding.ftl (../components/aboutwelcome/content/onboarding.ftl)
- preview/genai.ftl (../locales-preview/genai.ftl)
preview/translations.ftl (../locales-preview/translations.ftl)
preview/credentialChooser.ftl (../../toolkit/components/credentialmanagement/credentialChooser.ftl)
browser (%browser/**/*.ftl)
diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml
index 306079a9a96e..05e3d72a6e67 100644
--- a/toolkit/content/aboutSupport.xhtml
+++ b/toolkit/content/aboutSupport.xhtml
@@ -30,9 +30,6 @@
#ifndef ANDROID
-#if MOZ_BUILD_APP == browser
-
-#endif
#endif
diff --git a/toolkit/locales/en-US/toolkit/featuregates/features.ftl b/toolkit/locales/en-US/toolkit/featuregates/features.ftl
index 7a94d6447e26..107a77ea8368 100644
--- a/toolkit/locales/en-US/toolkit/featuregates/features.ftl
+++ b/toolkit/locales/en-US/toolkit/featuregates/features.ftl
@@ -51,6 +51,8 @@ experimental-features-auto-pip =
.label = Picture-in-Picture: auto-open on tab switch
experimental-features-auto-pip-description = Enable Picture-in-Picture on active videos when switching tabs.
+genai-settings-chat-title =
+ .label = AI chatbot
experimental-features-group-browsing =
.label = Browsing