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