diff --git a/app/controllers/discourse_ai/ai_helper/assistant_controller.rb b/app/controllers/discourse_ai/ai_helper/assistant_controller.rb index 43eba6d1..9a320e80 100644 --- a/app/controllers/discourse_ai/ai_helper/assistant_controller.rb +++ b/app/controllers/discourse_ai/ai_helper/assistant_controller.rb @@ -12,6 +12,7 @@ module DiscourseAi def suggest input = get_text_param! + force_default_locale = params[:force_default_locale] || false prompt = CompletionPrompt.find_by(id: params[:mode]) @@ -31,6 +32,7 @@ module DiscourseAi prompt, input, current_user, + force_default_locale, ), status: 200 end diff --git a/assets/javascripts/discourse/connectors/after-d-editor/ai-helper-context-menu.js b/assets/javascripts/discourse/connectors/after-d-editor/ai-helper-context-menu.js index f44a599c..911721d5 100644 --- a/assets/javascripts/discourse/connectors/after-d-editor/ai-helper-context-menu.js +++ b/assets/javascripts/discourse/connectors/after-d-editor/ai-helper-context-menu.js @@ -8,6 +8,7 @@ import { popupAjaxError } from "discourse/lib/ajax-error"; import { caretPosition, getCaretPosition } from "discourse/lib/utilities"; import { INPUT_DELAY } from "discourse-common/config/environment"; import { afterRender, bind, debounce } from "discourse-common/utils/decorators"; +import I18n from "discourse-i18n"; import { showComposerAIHelper } from "../../lib/show-ai-helper"; export default class AiHelperContextMenu extends Component { @@ -76,7 +77,28 @@ export default class AiHelperContextMenu extends Component { prompts = prompts .filter((p) => p.location.includes("composer")) - .filter((p) => p.name !== "generate_titles"); + .filter((p) => p.name !== "generate_titles") + .map((p) => { + // AI helper by default returns interface locale on translations + // Since we want site default translations (and we are using: force_default_locale) + // we need to replace the translated_name with the site default locale name + const siteLocale = this.siteSettings.default_locale; + const availableLocales = JSON.parse( + this.siteSettings.available_locales + ); + const locale = availableLocales.find((l) => l.value === siteLocale); + const translatedName = I18n.t( + "discourse_ai.ai_helper.context_menu.translate_prompt", + { + language: locale.name, + } + ); + + if (p.name === "translate") { + return { ...p, translated_name: translatedName }; + } + return p; + }); // Find the custom_prompt object and move it to the beginning of the array const customPromptIndex = prompts.findIndex( @@ -367,6 +389,7 @@ export default class AiHelperContextMenu extends Component { mode: option.id, text: this.selectedText, custom_prompt: this.customPromptValue, + force_default_locale: true, }, }); diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 1aa41dca..70c4001e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -292,6 +292,7 @@ en: title: "Custom Prompt" placeholder: "Enter a custom prompt..." submit: "Send Prompt" + translate_prompt: "Translate to %{language}" post_options_menu: trigger: "Ask AI" title: "Ask AI" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 97001fce..da129ccd 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -158,7 +158,7 @@ en: errors: completion_request_failed: "Something went wrong while trying to provide suggestions. Please, try again." prompts: - translate: Translate to English + translate: Translate to %{language} generate_titles: Suggest topic titles proofread: Proofread text markdown_table: Generate Markdown table diff --git a/lib/ai_helper/assistant.rb b/lib/ai_helper/assistant.rb index 07056f80..99af0d3d 100644 --- a/lib/ai_helper/assistant.rb +++ b/lib/ai_helper/assistant.rb @@ -11,7 +11,7 @@ module DiscourseAi prompt_cache.flush! end - def available_prompts + def available_prompts(user) key = "prompt_cache_#{I18n.locale}" self .class @@ -27,9 +27,19 @@ module DiscourseAi prompts = prompts.map do |prompt| - translation = - I18n.t("discourse_ai.ai_helper.prompts.#{prompt.name}", default: nil) || - prompt.translated_name || prompt.name + if prompt.name == "translate" + locale = user.effective_locale + locale_hash = LocaleSiteSetting.language_names[locale] + translation = + I18n.t( + "discourse_ai.ai_helper.prompts.translate", + language: locale_hash["nativeName"], + ) || prompt.translated_name || prompt.name + else + translation = + I18n.t("discourse_ai.ai_helper.prompts.#{prompt.name}", default: nil) || + prompt.translated_name || prompt.name + end { id: prompt.id, @@ -44,9 +54,9 @@ module DiscourseAi end end - def custom_locale_instructions(user = nil) + def custom_locale_instructions(user = nil, force_default_locale) locale = SiteSetting.default_locale - locale = user.locale || SiteSetting.default_locale if SiteSetting.allow_user_locale && user + locale = user.effective_locale if !force_default_locale locale_hash = LocaleSiteSetting.language_names[locale] if locale != "en" && locale_hash @@ -57,15 +67,17 @@ module DiscourseAi end end - def localize_prompt!(prompt, user = nil) - locale_instructions = custom_locale_instructions(user) + def localize_prompt!(prompt, user = nil, force_default_locale) + locale_instructions = custom_locale_instructions(user, force_default_locale) if locale_instructions prompt.messages[0][:content] = prompt.messages[0][:content] + locale_instructions end if prompt.messages[0][:content].include?("%LANGUAGE%") locale = SiteSetting.default_locale - locale = user.locale if SiteSetting.allow_user_locale && user&.locale.present? + + locale = user.effective_locale if user && !force_default_locale + locale_hash = LocaleSiteSetting.language_names[locale] prompt.messages[0][:content] = prompt.messages[0][:content].gsub( @@ -75,10 +87,10 @@ module DiscourseAi end end - def generate_prompt(completion_prompt, input, user, &block) + def generate_prompt(completion_prompt, input, user, force_default_locale = false, &block) llm = DiscourseAi::Completions::Llm.proxy(SiteSetting.ai_helper_model) prompt = completion_prompt.messages_with_input(input) - localize_prompt!(prompt, user) + localize_prompt!(prompt, user, force_default_locale) llm.generate( prompt, @@ -90,8 +102,8 @@ module DiscourseAi ) end - def generate_and_send_prompt(completion_prompt, input, user) - completion_result = generate_prompt(completion_prompt, input, user) + def generate_and_send_prompt(completion_prompt, input, user, force_default_locale = false) + completion_result = generate_prompt(completion_prompt, input, user, force_default_locale) result = { type: completion_prompt.prompt_type } result[:suggestions] = ( diff --git a/lib/ai_helper/entry_point.rb b/lib/ai_helper/entry_point.rb index 3e0e1eec..bb3103a4 100644 --- a/lib/ai_helper/entry_point.rb +++ b/lib/ai_helper/entry_point.rb @@ -38,7 +38,7 @@ module DiscourseAi end, ) do ActiveModel::ArraySerializer.new( - DiscourseAi::AiHelper::Assistant.new.available_prompts, + DiscourseAi::AiHelper::Assistant.new.available_prompts(scope.user), root: false, ) end diff --git a/spec/lib/modules/ai_helper/assistant_spec.rb b/spec/lib/modules/ai_helper/assistant_spec.rb index 69553d9b..fd3f479c 100644 --- a/spec/lib/modules/ai_helper/assistant_spec.rb +++ b/spec/lib/modules/ai_helper/assistant_spec.rb @@ -16,17 +16,27 @@ RSpec.describe DiscourseAi::AiHelper::Assistant do describe("#custom_locale_instructions") do it "Properly generates the per locale system instruction" do SiteSetting.default_locale = "ko" - expect(subject.custom_locale_instructions).to eq( + expect(subject.custom_locale_instructions(user, false)).to eq( "It is imperative that you write your answer in Korean (한국어), you are interacting with a Korean (한국어) speaking user. Leave tag names in English.", ) SiteSetting.allow_user_locale = true user.update!(locale: "he") - expect(subject.custom_locale_instructions(user)).to eq( + expect(subject.custom_locale_instructions(user, false)).to eq( "It is imperative that you write your answer in Hebrew (עברית), you are interacting with a Hebrew (עברית) speaking user. Leave tag names in English.", ) end + + it "returns sytstem instructions using Site locale if force_default_locale is true" do + SiteSetting.default_locale = "ko" + SiteSetting.allow_user_locale = true + user.update!(locale: "he") + + expect(subject.custom_locale_instructions(user, true)).to eq( + "It is imperative that you write your answer in Korean (한국어), you are interacting with a Korean (한국어) speaking user. Leave tag names in English.", + ) + end end describe("#available_prompts") do @@ -36,7 +46,7 @@ RSpec.describe DiscourseAi::AiHelper::Assistant do end it "returns all available prompts" do - prompts = subject.available_prompts + prompts = subject.available_prompts(user) expect(prompts.length).to eq(6) expect(prompts.map { |p| p[:name] }).to contain_exactly( @@ -56,7 +66,7 @@ RSpec.describe DiscourseAi::AiHelper::Assistant do end it "returns the illustrate_post prompt in the list of all prompts" do - prompts = subject.available_prompts + prompts = subject.available_prompts(user) expect(prompts.length).to eq(7) expect(prompts.map { |p| p[:name] }).to contain_exactly( diff --git a/spec/lib/modules/ai_helper/entry_point_spec.rb b/spec/lib/modules/ai_helper/entry_point_spec.rb index b54b0db0..70268a8c 100644 --- a/spec/lib/modules/ai_helper/entry_point_spec.rb +++ b/spec/lib/modules/ai_helper/entry_point_spec.rb @@ -21,7 +21,7 @@ describe DiscourseAi::AiHelper::EntryPoint do parsed["current_user"]["ai_helper_prompts"].find { |prompt| prompt["name"] == "translate" } expect(translate_prompt["translated_name"]).to eq( - I18n.t("discourse_ai.ai_helper.prompts.translate"), + I18n.t("discourse_ai.ai_helper.prompts.translate", language: "English (US)"), ) I18n.with_locale("fr") do @@ -32,7 +32,7 @@ describe DiscourseAi::AiHelper::EntryPoint do parsed["current_user"]["ai_helper_prompts"].find { |prompt| prompt["name"] == "translate" } expect(translate_prompt["translated_name"]).to eq( - I18n.t("discourse_ai.ai_helper.prompts.translate", locale: "fr"), + I18n.t("discourse_ai.ai_helper.prompts.translate", locale: "fr", language: "Français"), ) end end