Skip to content

Commit 48682c2

Browse files
authored
FEATURE: Show experimental language switcher for anon users (#198)
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.
1 parent a776c55 commit 48682c2

File tree

8 files changed

+135
-4
lines changed

8 files changed

+135
-4
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
class LanguageSwitcherSettingValidator
4+
def initialize(opts = {})
5+
@opts = opts
6+
end
7+
8+
def valid_value?(val)
9+
return true if val == "f"
10+
SiteSetting.set_locale_from_cookie
11+
end
12+
13+
def error_message
14+
I18n.t("site_settings.errors.set_locale_cookie_requirements")
15+
end
16+
end
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import Component from "@glimmer/component";
2+
import { fn } from "@ember/helper";
3+
import { action } from "@ember/object";
4+
import { service } from "@ember/service";
5+
import DButton from "discourse/components/d-button";
6+
import DropdownMenu from "discourse/components/dropdown-menu";
7+
import cookie from "discourse/lib/cookie";
8+
import DMenu from "float-kit/components/d-menu";
9+
10+
export default class LanguageSwitcher extends Component {
11+
@service site;
12+
@service siteSettings;
13+
@service router;
14+
15+
get localeOptions() {
16+
return JSON.parse(this.siteSettings.available_locales).map(
17+
({ name, value }) => {
18+
return {
19+
label: name,
20+
value,
21+
};
22+
}
23+
);
24+
}
25+
26+
@action
27+
async changeLocale(locale) {
28+
cookie("locale", locale);
29+
this.dMenu.close();
30+
// we need a hard refresh here for the locale to take effect
31+
window.location.reload();
32+
}
33+
34+
@action
35+
onRegisterApi(api) {
36+
this.dMenu = api;
37+
}
38+
39+
<template>
40+
<DMenu
41+
@identifier="discourse-translator_language-switcher"
42+
title="Language switcher"
43+
@icon="language"
44+
class="btn-flat btn-icon icon"
45+
@onRegisterApi={{this.onRegisterApi}}
46+
>
47+
<:content>
48+
<DropdownMenu as |dropdown|>
49+
{{#each this.localeOptions as |option|}}
50+
<dropdown.item
51+
class="discourse-translator_locale-option"
52+
data-menu-option-id={{option.value}}
53+
>
54+
<DButton
55+
@translatedLabel={{option.label}}
56+
@action={{fn this.changeLocale option.value}}
57+
/>
58+
</dropdown.item>
59+
{{/each}}
60+
</DropdownMenu>
61+
</:content>
62+
</DMenu>
63+
</template>
64+
}

assets/javascripts/discourse/initializers/extend-for-translate-button.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
import { ajax } from "discourse/lib/ajax";
22
import { popupAjaxError } from "discourse/lib/ajax-error";
3+
import { withSilencedDeprecations } from "discourse/lib/deprecated";
34
import { withPluginApi } from "discourse/lib/plugin-api";
4-
import { withSilencedDeprecations } from "discourse-common/lib/deprecated";
55
import { i18n } from "discourse-i18n";
6+
import LanguageSwitcher from "../components/language-switcher";
67
import ToggleTranslationButton from "../components/post-menu/toggle-translation-button";
78
import TranslatedPost from "../components/translated-post";
89

910
function initializeTranslation(api) {
1011
const siteSettings = api.container.lookup("service:site-settings");
12+
if (!siteSettings.translator_enabled) {
13+
return;
14+
}
15+
1116
const currentUser = api.getCurrentUser();
1217

13-
if (!currentUser || !siteSettings.translator_enabled) {
14-
return;
18+
if (!currentUser && siteSettings.experimental_anon_language_switcher) {
19+
api.headerIcons.add(
20+
"discourse-translator_language-switcher",
21+
LanguageSwitcher,
22+
{ before: ["search"] }
23+
);
1524
}
1625

17-
customizePostMenu(api);
26+
if (currentUser) {
27+
customizePostMenu(api);
28+
}
1829
}
1930

2031
function customizePostMenu(api, container) {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[data-identifier="discourse-translator_language-switcher"]
2+
.fk-d-menu__inner-content {
3+
max-height: 50vh;
4+
}

config/locales/server.en.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ en:
1717
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."
1818
restrict_translation_by_group: "Only allowed groups can translate"
1919
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."
20+
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."
21+
errors:
22+
set_locale_cookie_requirements: "The experimental language switcher for anonymous users requires the `set locale from cookie` site setting to be enabled."
2023
translator:
2124
failed: "The translator is unable to translate this content (%{source_locale}) to the default language of this site (%{target_locale})."
2225
not_supported: "This language is not supported by the translator."

config/settings.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,7 @@ discourse_translator:
102102
default: ""
103103
client: true
104104
type: group_list
105+
experimental_anon_language_switcher:
106+
default: false
107+
client: true
108+
validator: "LanguageSwitcherSettingValidator"

plugin.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
enabled_site_setting :translator_enabled
1313
register_asset "stylesheets/common/post.scss"
14+
register_asset "stylesheets/common/common.scss"
1415

1516
module ::DiscourseTranslator
1617
PLUGIN_NAME = "discourse-translator".freeze
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe "Anonymous user language switcher", type: :system do
4+
fab!(:japanese_user) { Fabricate(:user, locale: "ja") }
5+
it "shows the correct language based on the selected language and login status" do
6+
SWITCHER_SELECTOR = "button[data-identifier='discourse-translator_language-switcher']"
7+
8+
visit("/")
9+
expect(page).not_to have_css(SWITCHER_SELECTOR)
10+
11+
SiteSetting.translator_enabled = true
12+
SiteSetting.allow_user_locale = true
13+
SiteSetting.set_locale_from_cookie = true
14+
SiteSetting.experimental_anon_language_switcher = true
15+
visit("/")
16+
expect(page).to have_css(SWITCHER_SELECTOR)
17+
expect(find(".nav-item_latest")).to have_content("Latest")
18+
19+
switcher = PageObjects::Components::DMenu.new(SWITCHER_SELECTOR)
20+
switcher.expand
21+
switcher.click_button("Español")
22+
expect(find(".nav-item_latest")).to have_content("Recientes")
23+
24+
sign_in(japanese_user)
25+
visit("/")
26+
expect(find(".nav-item_latest")).to have_content("最新")
27+
end
28+
end

0 commit comments

Comments
 (0)