diff --git a/app/validators/language_switcher_setting_validator.rb b/app/validators/language_switcher_setting_validator.rb new file mode 100644 index 0000000..efcaba4 --- /dev/null +++ b/app/validators/language_switcher_setting_validator.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class LanguageSwitcherSettingValidator + def initialize(opts = {}) + @opts = opts + end + + def valid_value?(val) + return true if val == "f" + SiteSetting.set_locale_from_cookie + end + + def error_message + I18n.t("site_settings.errors.set_locale_cookie_requirements") + end +end diff --git a/assets/javascripts/discourse/components/language-switcher.gjs b/assets/javascripts/discourse/components/language-switcher.gjs new file mode 100644 index 0000000..54b2994 --- /dev/null +++ b/assets/javascripts/discourse/components/language-switcher.gjs @@ -0,0 +1,64 @@ +import Component from "@glimmer/component"; +import { fn } from "@ember/helper"; +import { action } from "@ember/object"; +import { service } from "@ember/service"; +import DButton from "discourse/components/d-button"; +import DropdownMenu from "discourse/components/dropdown-menu"; +import cookie from "discourse/lib/cookie"; +import DMenu from "float-kit/components/d-menu"; + +export default class LanguageSwitcher extends Component { + @service site; + @service siteSettings; + @service router; + + get localeOptions() { + return JSON.parse(this.siteSettings.available_locales).map( + ({ name, value }) => { + return { + label: name, + value, + }; + } + ); + } + + @action + async changeLocale(locale) { + cookie("locale", locale); + this.dMenu.close(); + // we need a hard refresh here for the locale to take effect + window.location.reload(); + } + + @action + onRegisterApi(api) { + this.dMenu = api; + } + + +} diff --git a/assets/javascripts/discourse/initializers/extend-for-translate-button.js b/assets/javascripts/discourse/initializers/extend-for-translate-button.js index cb820ef..44b75c4 100644 --- a/assets/javascripts/discourse/initializers/extend-for-translate-button.js +++ b/assets/javascripts/discourse/initializers/extend-for-translate-button.js @@ -1,20 +1,31 @@ import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { withSilencedDeprecations } from "discourse/lib/deprecated"; import { withPluginApi } from "discourse/lib/plugin-api"; -import { withSilencedDeprecations } from "discourse-common/lib/deprecated"; import { i18n } from "discourse-i18n"; +import LanguageSwitcher from "../components/language-switcher"; import ToggleTranslationButton from "../components/post-menu/toggle-translation-button"; import TranslatedPost from "../components/translated-post"; function initializeTranslation(api) { const siteSettings = api.container.lookup("service:site-settings"); + if (!siteSettings.translator_enabled) { + return; + } + const currentUser = api.getCurrentUser(); - if (!currentUser || !siteSettings.translator_enabled) { - return; + if (!currentUser && siteSettings.experimental_anon_language_switcher) { + api.headerIcons.add( + "discourse-translator_language-switcher", + LanguageSwitcher, + { before: ["search"] } + ); } - customizePostMenu(api); + if (currentUser) { + customizePostMenu(api); + } } function customizePostMenu(api, container) { diff --git a/assets/stylesheets/common/common.scss b/assets/stylesheets/common/common.scss new file mode 100644 index 0000000..202aec1 --- /dev/null +++ b/assets/stylesheets/common/common.scss @@ -0,0 +1,4 @@ +[data-identifier="discourse-translator_language-switcher"] + .fk-d-menu__inner-content { + max-height: 50vh; +} diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 1950277..ff4ceb4 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -17,6 +17,9 @@ en: translator_azure_custom_subdomain: "Required if using a Virtual Network or Firewall for Azure Cognitive Services. Note: Only enter the custom subdomain not the full custom endpoint." restrict_translation_by_group: "Only allowed groups can translate" restrict_translation_by_poster_group: "Only allow translation of posts made by users in allowed groups. If empty, allow translations of posts from all users." + experimental_anon_language_switcher: "Enable experimental language switcher for anonymous users. This will allow anonymous users to switch between translated versions of Discourse and user-contributed content in topics." + errors: + set_locale_cookie_requirements: "The experimental language switcher for anonymous users requires the `set locale from cookie` site setting to be enabled." translator: failed: "The translator is unable to translate this content (%{source_locale}) to the default language of this site (%{target_locale})." not_supported: "This language is not supported by the translator." diff --git a/config/settings.yml b/config/settings.yml index 6ddc6c8..b17ece4 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -102,3 +102,7 @@ discourse_translator: default: "" client: true type: group_list + experimental_anon_language_switcher: + default: false + client: true + validator: "LanguageSwitcherSettingValidator" diff --git a/plugin.rb b/plugin.rb index 06b49f8..f74dc22 100644 --- a/plugin.rb +++ b/plugin.rb @@ -11,6 +11,7 @@ enabled_site_setting :translator_enabled register_asset "stylesheets/common/post.scss" +register_asset "stylesheets/common/common.scss" module ::DiscourseTranslator PLUGIN_NAME = "discourse-translator".freeze diff --git a/spec/system/anon_language_switcher_spec.rb b/spec/system/anon_language_switcher_spec.rb new file mode 100644 index 0000000..4eaec31 --- /dev/null +++ b/spec/system/anon_language_switcher_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.describe "Anonymous user language switcher", type: :system do + fab!(:japanese_user) { Fabricate(:user, locale: "ja") } + it "shows the correct language based on the selected language and login status" do + SWITCHER_SELECTOR = "button[data-identifier='discourse-translator_language-switcher']" + + visit("/") + expect(page).not_to have_css(SWITCHER_SELECTOR) + + SiteSetting.translator_enabled = true + SiteSetting.allow_user_locale = true + SiteSetting.set_locale_from_cookie = true + SiteSetting.experimental_anon_language_switcher = true + visit("/") + expect(page).to have_css(SWITCHER_SELECTOR) + expect(find(".nav-item_latest")).to have_content("Latest") + + switcher = PageObjects::Components::DMenu.new(SWITCHER_SELECTOR) + switcher.expand + switcher.click_button("Español") + expect(find(".nav-item_latest")).to have_content("Recientes") + + sign_in(japanese_user) + visit("/") + expect(find(".nav-item_latest")).to have_content("最新") + end +end