mirror of
https://github.com/mastodon/mastodon.git
synced 2024-08-20 21:08:15 -07:00
Add a way for the user to select which languages they understand
Closes #29989
This commit is contained in:
parent
dfd43869c9
commit
e955a580fa
11 changed files with 42 additions and 12 deletions
|
@ -19,6 +19,6 @@ class Settings::Preferences::BaseController < Settings::BaseController
|
|||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(:locale, :time_zone, chosen_languages: [], settings_attributes: UserSettings.keys)
|
||||
params.require(:user).permit(:locale, :time_zone, spoken_languages: [], chosen_languages: [], settings_attributes: UserSettings.keys)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'
|
|||
import { Icon } from 'mastodon/components/icon';
|
||||
import PollContainer from 'mastodon/containers/poll_container';
|
||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
import { autoPlayGif, spokenLanguages, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
|
||||
|
||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||
|
@ -237,6 +237,10 @@ class StatusContent extends PureComponent {
|
|||
this.node = c;
|
||||
};
|
||||
|
||||
spokenByUser() {
|
||||
return spokenLanguages.includes(this.props.status.get('language'));
|
||||
};
|
||||
|
||||
render () {
|
||||
const { status, intl, statusContent } = this.props;
|
||||
|
||||
|
@ -244,7 +248,7 @@ class StatusContent extends PureComponent {
|
|||
const renderReadMore = this.props.onClick && status.get('collapsed');
|
||||
const contentLocale = intl.locale.replace(/[_-].*/, '');
|
||||
const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
|
||||
const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
|
||||
const renderTranslate = this.props.onTranslate && this.props.identity.signedIn && !this.spokenByUser() && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
|
||||
|
||||
const content = { __html: statusContent ?? getStatusContent(status) };
|
||||
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
|
||||
|
|
|
@ -13,7 +13,7 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
|
|||
import SearchIcon from '@/material-icons/400-24px/search.svg?react';
|
||||
import TranslateIcon from '@/material-icons/400-24px/translate.svg?react';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
import { spokenLanguages, languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
changeLanguage: { id: 'compose.language.change', defaultMessage: 'Change language' },
|
||||
|
@ -84,6 +84,9 @@ class LanguageDropdownMenu extends PureComponent {
|
|||
const { languages, value, frequentlyUsedLanguages } = this.props;
|
||||
const { searchValue } = this.state;
|
||||
|
||||
// first show spoken languages and then frequently used
|
||||
const orderedLanguages = spokenLanguages.concat(frequentlyUsedLanguages.filter((item) => spokenLanguages.indexOf(item) < 0));
|
||||
|
||||
if (searchValue === '') {
|
||||
return [...languages].sort((a, b) => {
|
||||
// Push current selection to the top of the list
|
||||
|
@ -93,10 +96,8 @@ class LanguageDropdownMenu extends PureComponent {
|
|||
} else if (b[0] === value) {
|
||||
return 1;
|
||||
} else {
|
||||
// Sort according to frequently used languages
|
||||
|
||||
const indexOfA = frequentlyUsedLanguages.indexOf(a[0]);
|
||||
const indexOfB = frequentlyUsedLanguages.indexOf(b[0]);
|
||||
const indexOfA = orderedLanguages.indexOf(a[0]);
|
||||
const indexOfB = orderedLanguages.indexOf(b[0]);
|
||||
|
||||
return ((indexOfA > -1 ? indexOfA : Infinity) - (indexOfB > -1 ? indexOfB : Infinity));
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@ export const criticalUpdatesPending = initialState?.critical_updates_pending;
|
|||
// @ts-expect-error
|
||||
export const statusPageUrl = getMeta('status_page_url');
|
||||
export const sso_redirect = getMeta('sso_redirect');
|
||||
export const spokenLanguages = getMeta('spoken_languages');
|
||||
|
||||
/**
|
||||
* @returns {string | undefined}
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
# settings :text
|
||||
# time_zone :string
|
||||
# otp_secret :string
|
||||
# spoken_languages :string default([]), not null, is an Array
|
||||
#
|
||||
|
||||
class User < ApplicationRecord
|
||||
|
@ -134,6 +135,7 @@ class User < ApplicationRecord
|
|||
normalizes :locale, with: ->(locale) { I18n.available_locales.exclude?(locale.to_sym) ? nil : locale }
|
||||
normalizes :time_zone, with: ->(time_zone) { ActiveSupport::TimeZone[time_zone].nil? ? nil : time_zone }
|
||||
normalizes :chosen_languages, with: ->(chosen_languages) { chosen_languages.compact_blank.presence }
|
||||
normalizes :spoken_languages, with: ->(spoken_languages) { spoken_languages.compact_blank }
|
||||
|
||||
has_many :session_activations, dependent: :destroy
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
store[:use_blurhash] = object_account_user.setting_use_blurhash
|
||||
store[:use_pending_items] = object_account_user.setting_use_pending_items
|
||||
store[:show_trends] = Setting.trends && object_account_user.setting_trends
|
||||
store[:spoken_languages] = object_account_user.spoken_languages
|
||||
else
|
||||
store[:auto_play_gif] = Setting.auto_play_gif
|
||||
store[:display_media] = Setting.display_media
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'),
|
||||
wrapper: :with_label
|
||||
|
||||
%h4= t 'preferences.public_timelines'
|
||||
%h4= t 'preferences.languages'
|
||||
|
||||
.fields-group
|
||||
= f.input :chosen_languages,
|
||||
|
@ -57,5 +57,16 @@
|
|||
required: false,
|
||||
wrapper: :with_block_label
|
||||
|
||||
.fields-group
|
||||
= f.input :spoken_languages,
|
||||
as: :check_boxes,
|
||||
collection_wrapper_tag: 'ul',
|
||||
collection: filterable_languages,
|
||||
include_blank: false,
|
||||
item_wrapper_tag: 'li',
|
||||
label_method: ->(locale) { native_locale_name(locale) },
|
||||
required: false,
|
||||
wrapper: :with_block_label
|
||||
|
||||
.actions
|
||||
= f.button :button, t('generic.save_changes'), type: :submit
|
||||
|
|
|
@ -1544,9 +1544,9 @@ en:
|
|||
too_few_options: must have more than one item
|
||||
too_many_options: can't contain more than %{max} items
|
||||
preferences:
|
||||
languages: Languages
|
||||
other: Other
|
||||
posting_defaults: Posting defaults
|
||||
public_timelines: Public timelines
|
||||
privacy:
|
||||
hint_html: "<strong>Customize how you want your profile and your posts to be found.</strong> A variety of features in Mastodon can help you reach a wider audience when enabled. Take a moment to review these settings to make sure they fit your use case."
|
||||
privacy: Privacy
|
||||
|
|
|
@ -131,6 +131,7 @@ en:
|
|||
user:
|
||||
chosen_languages: When checked, only posts in selected languages will be displayed in public timelines
|
||||
role: The role controls which permissions the user has
|
||||
spoken_languages: Mastodon will not suggest to translate statuses in the languages that you speak
|
||||
user_role:
|
||||
color: Color to be used for the role throughout the UI, as RGB in hex format
|
||||
highlighted: This makes the role publicly visible
|
||||
|
@ -181,7 +182,7 @@ en:
|
|||
autofollow: Invite to follow your account
|
||||
avatar: Profile picture
|
||||
bot: This is an automated account
|
||||
chosen_languages: Filter languages
|
||||
chosen_languages: Filter languages in public timelines
|
||||
confirm_new_password: Confirm new password
|
||||
confirm_password: Confirm password
|
||||
context: Filter contexts
|
||||
|
@ -228,6 +229,7 @@ en:
|
|||
setting_use_pending_items: Slow mode
|
||||
severity: Severity
|
||||
sign_in_token_attempt: Security code
|
||||
spoken_languages: Languages that you can speak
|
||||
title: Title
|
||||
type: Import type
|
||||
username: Username
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddSpokenLanguagesToUsers < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
add_column :users, :spoken_languages, :string, array: true, null: false, default: []
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.1].define(version: 2024_07_24_181224) do
|
||||
ActiveRecord::Schema[7.1].define(version: 2024_07_29_134058) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
|
@ -1205,6 +1205,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_07_24_181224) do
|
|||
t.text "settings"
|
||||
t.string "time_zone"
|
||||
t.string "otp_secret"
|
||||
t.string "spoken_languages", default: [], null: false, array: true
|
||||
t.index ["account_id"], name: "index_users_on_account_id"
|
||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||
t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id", where: "(created_by_application_id IS NOT NULL)"
|
||||
|
|
Loading…
Reference in a new issue