Skip to content

Commit

Permalink
FEATURE: Show experimental language switcher for anon users (#198)
Browse files Browse the repository at this point in the history
This commit allows anon users to select a language using a language switcher, this feature is behind an experimental site setting experimental_anon_language_switcher. The set of languages are defined by the same one available to existing users of the site, with the required allow_user_locale setting turned on:

https://github.com/discourse/discourse/blob/de7e213052c850aab0258131c47da777ec679621/app/models/locale_site_setting.rb#L37-L50

In the near future, we may consider shortening this list of languages, and will also expand this feature into user-contributed content.
  • Loading branch information
nattsw authored Jan 24, 2025
1 parent a776c55 commit 48682c2
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 4 deletions.
16 changes: 16 additions & 0 deletions app/validators/language_switcher_setting_validator.rb
Original file line number Diff line number Diff line change
@@ -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
64 changes: 64 additions & 0 deletions assets/javascripts/discourse/components/language-switcher.gjs
Original file line number Diff line number Diff line change
@@ -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;
}

<template>
<DMenu
@identifier="discourse-translator_language-switcher"
title="Language switcher"
@icon="language"
class="btn-flat btn-icon icon"
@onRegisterApi={{this.onRegisterApi}}
>
<:content>
<DropdownMenu as |dropdown|>
{{#each this.localeOptions as |option|}}
<dropdown.item
class="discourse-translator_locale-option"
data-menu-option-id={{option.value}}
>
<DButton
@translatedLabel={{option.label}}
@action={{fn this.changeLocale option.value}}
/>
</dropdown.item>
{{/each}}
</DropdownMenu>
</:content>
</DMenu>
</template>
}
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions assets/stylesheets/common/common.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[data-identifier="discourse-translator_language-switcher"]
.fk-d-menu__inner-content {
max-height: 50vh;
}
3 changes: 3 additions & 0 deletions config/locales/server.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
4 changes: 4 additions & 0 deletions config/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@ discourse_translator:
default: ""
client: true
type: group_list
experimental_anon_language_switcher:
default: false
client: true
validator: "LanguageSwitcherSettingValidator"
1 change: 1 addition & 0 deletions plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions spec/system/anon_language_switcher_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 48682c2

Please sign in to comment.