mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
MM-65755 Admin Console UI for Auto-Translation (#33982)
Some checks are pending
API / build (push) Waiting to run
Server CI / Compute Go Version (push) Waiting to run
Server CI / Check mocks (push) Blocked by required conditions
Server CI / Check go mod tidy (push) Blocked by required conditions
Server CI / check-style (push) Blocked by required conditions
Server CI / Check serialization methods for hot structs (push) Blocked by required conditions
Server CI / Vet API (push) Blocked by required conditions
Server CI / Check migration files (push) Blocked by required conditions
Server CI / Generate email templates (push) Blocked by required conditions
Server CI / Check store layers (push) Blocked by required conditions
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (push) Blocked by required conditions
Server CI / Postgres (FIPS) (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Waiting to run
Web App CI / check-types (push) Waiting to run
Web App CI / test (push) Waiting to run
Web App CI / build (push) Waiting to run
Some checks are pending
API / build (push) Waiting to run
Server CI / Compute Go Version (push) Waiting to run
Server CI / Check mocks (push) Blocked by required conditions
Server CI / Check go mod tidy (push) Blocked by required conditions
Server CI / check-style (push) Blocked by required conditions
Server CI / Check serialization methods for hot structs (push) Blocked by required conditions
Server CI / Vet API (push) Blocked by required conditions
Server CI / Check migration files (push) Blocked by required conditions
Server CI / Generate email templates (push) Blocked by required conditions
Server CI / Check store layers (push) Blocked by required conditions
Server CI / Check mmctl docs (push) Blocked by required conditions
Server CI / Postgres with binary parameters (push) Blocked by required conditions
Server CI / Postgres (push) Blocked by required conditions
Server CI / Postgres (FIPS) (push) Blocked by required conditions
Server CI / Generate Test Coverage (push) Blocked by required conditions
Server CI / Run mmctl tests (push) Blocked by required conditions
Server CI / Run mmctl tests (FIPS) (push) Blocked by required conditions
Server CI / Build mattermost server app (push) Blocked by required conditions
Web App CI / check-lint (push) Waiting to run
Web App CI / check-i18n (push) Waiting to run
Web App CI / check-types (push) Waiting to run
Web App CI / test (push) Waiting to run
Web App CI / build (push) Waiting to run
* AutoTranslate config settings * comment out Agents provider * Add auto translate timeout config validation * i18n messages for autotranslation config validation * fix test * validate url for libreTranslate * Feedback review * Admin Console UI for Auto-Translation * fix admin console conditional section display * i18n * removed unintentional change Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * update admin.general.localization.autoTranslateProviderDescription newline * fix lint * Fix types * UX feedback review * fix typo in i18n * Fix AutoTranslation feature flag * feedback review * Fix test default values * feedback review * re-add isHidden property to feature discovery * fix lint and external url * Add settings to playwright * Add empty as a valid value for the Provider * Add searchable strings * set apikey input type as password --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
687c1f24fe
commit
595e600eea
17 changed files with 902 additions and 34 deletions
|
|
@ -820,4 +820,17 @@ const defaultServerConfig: AdminConfig = {
|
|||
TeamAdminsAsReviewers: true,
|
||||
},
|
||||
},
|
||||
AutoTranslationSettings: {
|
||||
Enable: false,
|
||||
Provider: '',
|
||||
LibreTranslate: {
|
||||
URL: '',
|
||||
APIKey: '',
|
||||
},
|
||||
TimeoutMs: {
|
||||
NewPost: 800,
|
||||
Fetch: 2000,
|
||||
Notification: 300,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ func (f *FeatureFlags) SetDefaults() {
|
|||
f.ContentFlagging = false
|
||||
f.InteractiveDialogAppsForm = true
|
||||
f.EnableMattermostEntry = true
|
||||
|
||||
f.MobileSSOCodeExchange = true
|
||||
|
||||
// FEATURE_FLAG_REMOVAL: AutoTranslation - Remove this default when MVP is to be released
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ import {
|
|||
SystemRolesFeatureDiscovery,
|
||||
} from './feature_discovery/features';
|
||||
import AttributeBasedAccessControlFeatureDiscovery from './feature_discovery/features/attribute_based_access_control';
|
||||
import AutoTranslationFeatureDiscovery from './feature_discovery/features/auto_translation';
|
||||
import UserAttributesFeatureDiscovery from './feature_discovery/features/user_attributes';
|
||||
import FeatureFlags, {messages as featureFlagsMessages} from './feature_flags';
|
||||
import GroupDetails from './group_settings/group_details';
|
||||
|
|
@ -91,6 +92,8 @@ import IPFiltering from './ip_filtering';
|
|||
import LDAPWizard from './ldap_wizard';
|
||||
import LicenseSettings from './license_settings';
|
||||
import {searchableStrings as licenseSettingsSearchableStrings} from './license_settings/license_settings';
|
||||
import AutoTranslation, {searchableStrings as autoTranslationSearchableStrings} from './localization/auto_translation';
|
||||
import Localization, {searchableStrings as localizationSearchableStrings} from './localization/localization';
|
||||
import MessageExportSettings, {searchableStrings as messageExportSearchableStrings} from './message_export_settings';
|
||||
import OpenIdConvert from './openid_convert';
|
||||
import PasswordSettings, {searchableStrings as passwordSearchableStrings} from './password_settings';
|
||||
|
|
@ -2437,51 +2440,38 @@ const AdminDefinition: AdminDefinitionType = {
|
|||
url: 'site_config/localization',
|
||||
title: defineMessage({id: 'admin.sidebar.localization', defaultMessage: 'Localization'}),
|
||||
isHidden: it.not(it.userHasReadPermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)),
|
||||
searchableStrings: localizationSearchableStrings.concat(autoTranslationSearchableStrings),
|
||||
schema: {
|
||||
id: 'LocalizationSettings',
|
||||
name: defineMessage({id: 'admin.site.localization', defaultMessage: 'Localization'}),
|
||||
settings: [
|
||||
{
|
||||
type: 'language',
|
||||
key: 'LocalizationSettings.DefaultServerLocale',
|
||||
label: defineMessage({id: 'admin.general.localization.serverLocaleTitle', defaultMessage: 'Default Server Language:'}),
|
||||
help_text: defineMessage({id: 'admin.general.localization.serverLocaleDescription', defaultMessage: 'Default language for system messages.'}),
|
||||
type: 'custom',
|
||||
key: 'LocalizationSettings',
|
||||
component: Localization,
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)),
|
||||
},
|
||||
{
|
||||
type: 'language',
|
||||
key: 'LocalizationSettings.DefaultClientLocale',
|
||||
label: defineMessage({id: 'admin.general.localization.clientLocaleTitle', defaultMessage: 'Default Client Language:'}),
|
||||
help_text: defineMessage({id: 'admin.general.localization.clientLocaleDescription', defaultMessage: 'Default language for newly created users and pages where the user hasn\'t logged in.'}),
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)),
|
||||
type: 'custom',
|
||||
key: 'AutoTranslationSettings',
|
||||
component: AutoTranslation,
|
||||
isHidden: it.any(
|
||||
it.configIsFalse('FeatureFlags', 'AutoTranslation'),
|
||||
it.not(it.minLicenseTier(LicenseSkus.EnterpriseAdvanced)),
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'language',
|
||||
key: 'LocalizationSettings.AvailableLocales',
|
||||
label: defineMessage({id: 'admin.general.localization.availableLocalesTitle', defaultMessage: 'Available Languages:'}),
|
||||
help_text: defineMessage({id: 'admin.general.localization.availableLocalesDescription', defaultMessage: 'Set which languages are available for users in <strong>Settings > Display > Language</strong> (leave this field blank to have all supported languages available). If you\'re manually adding new languages, the <strong>Default Client Language</strong> must be added before saving this setting.\n \nWould like to help with translations? Join the <link>Mattermost Translation Server</link> to contribute.'}),
|
||||
help_text_markdown: false,
|
||||
help_text_values: {
|
||||
link: (msg: string) => (
|
||||
<ExternalLink
|
||||
location='admin_console'
|
||||
href='http://translate.mattermost.com/'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
type: 'custom',
|
||||
key: 'auto-translation-discovery',
|
||||
component: AutoTranslationFeatureDiscovery,
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.ABOUT.EDITION_AND_LICENSE)),
|
||||
isHidden: it.any(
|
||||
it.all(
|
||||
it.configIsTrue('FeatureFlags', 'AutoTranslation'),
|
||||
it.minLicenseTier(LicenseSkus.EnterpriseAdvanced),
|
||||
),
|
||||
strong: (msg: string) => <strong>{msg}</strong>,
|
||||
},
|
||||
multiple: true,
|
||||
no_result: defineMessage({id: 'admin.general.localization.availableLocalesNoResults', defaultMessage: 'No results found'}),
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)),
|
||||
},
|
||||
{
|
||||
type: 'bool',
|
||||
key: 'LocalizationSettings.EnableExperimentalLocales',
|
||||
label: defineMessage({id: 'admin.general.localization.enableExperimentalLocalesTitle', defaultMessage: 'Enable Experimental Locales:'}),
|
||||
help_text: defineMessage({id: 'admin.general.localization.enableExperimentalLocalesDescription', defaultMessage: 'When true, it allows users to select experimental (e.g., in progress) languages.'}),
|
||||
isDisabled: it.not(it.userHasWritePermissionOnResource(RESOURCE_KEYS.SITE.LOCALIZATION)),
|
||||
it.configIsFalse('FeatureFlags', 'AutoTranslation'),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import AlertBanner from 'components/alert_banner';
|
|||
import ExternalLink from 'components/external_link';
|
||||
import StartTrialBtn from 'components/learn_more_trial_modal/start_trial_btn';
|
||||
import LoadingSpinner from 'components/widgets/loading/loading_spinner';
|
||||
import SkuTag from 'components/widgets/tag/sku_tag';
|
||||
|
||||
import type {LicenseSkus} from 'utils/constants';
|
||||
import {AboutLinks, LicenseLinks} from 'utils/constants';
|
||||
|
|
@ -49,6 +50,7 @@ type Props = {
|
|||
isSubscriptionLoaded: boolean;
|
||||
isPaidSubscription: boolean;
|
||||
customer?: CloudCustomer;
|
||||
showSkuTag?: boolean;
|
||||
}
|
||||
|
||||
type State = {
|
||||
|
|
@ -203,6 +205,7 @@ export default class FeatureDiscovery extends React.PureComponent<Props, State>
|
|||
isCloud,
|
||||
isCloudTrial,
|
||||
isSubscriptionLoaded,
|
||||
showSkuTag,
|
||||
} = this.props;
|
||||
|
||||
// on first load the license information is available and we can know if it is cloud license, but the subscription is not loaded yet
|
||||
|
|
@ -277,6 +280,11 @@ export default class FeatureDiscovery extends React.PureComponent<Props, State>
|
|||
data-testid='featureDiscovery'
|
||||
>
|
||||
<div className='FeatureDiscovery_copyWrapper'>
|
||||
{showSkuTag &&
|
||||
<SkuTag
|
||||
sku={this.props.minimumSKURequiredForFeature}
|
||||
className='FeatureDiscovery_tag'
|
||||
/>}
|
||||
<div
|
||||
className='FeatureDiscovery_title'
|
||||
data-testid='featureDiscovery_title'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
@use 'utils/variables';
|
||||
|
||||
.AutoTranslationFeatureDiscovery .FeatureDiscovery {
|
||||
all: unset; // removes all inheritable and non-inheritable properties (use with caution)
|
||||
|
||||
display: grid;
|
||||
align-items: center;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
gap: 24px;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
|
||||
.FeatureDiscovery_copyWrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.FeatureDiscovery_imageWrapper {
|
||||
padding: 8px 32px;
|
||||
}
|
||||
|
||||
.SkuTag.FeatureDiscovery_tag {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.FeatureDiscovery_title {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.AutoTranslationTag {
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {defineMessage} from 'react-intl';
|
||||
|
||||
import {
|
||||
AdminSection,
|
||||
SectionContent,
|
||||
} from 'components/admin_console/system_properties/controls';
|
||||
|
||||
import {LicenseSkus} from 'utils/constants';
|
||||
|
||||
import './auto_translation.scss';
|
||||
import AutoTranslationSVG from './images/auto_translate_svg';
|
||||
|
||||
import FeatureDiscovery from '../index';
|
||||
|
||||
const AutoTranslationFeatureDiscovery: React.FC = () => {
|
||||
return (
|
||||
<AdminSection>
|
||||
<SectionContent>
|
||||
<div className='AutoTranslationFeatureDiscovery'>
|
||||
<FeatureDiscovery
|
||||
featureName='auto-translation'
|
||||
showSkuTag={true}
|
||||
minimumSKURequiredForFeature={LicenseSkus.EnterpriseAdvanced}
|
||||
title={defineMessage({
|
||||
id: 'admin.auto_translation_feature_discovery.title',
|
||||
defaultMessage: 'Remove language barriers with auto-translation',
|
||||
})}
|
||||
copy={defineMessage({
|
||||
id: 'admin.auto_translation_feature_discovery.copy',
|
||||
defaultMessage: 'Effortlessly collaborate across languages with auto-translation. Messages in shared channels are instantly translated based on each user’s language preference—no extra steps required.{br}<strong>Only available in Enterprise Advanced.</strong>',
|
||||
values: {strong: (msg: string) => <strong>{msg}</strong>, br: <br/>},
|
||||
})}
|
||||
learnMoreURL='https://docs.mattermost.com'
|
||||
featureDiscoveryImage={
|
||||
<AutoTranslationSVG
|
||||
width={158}
|
||||
height={149}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</SectionContent>
|
||||
</AdminSection>
|
||||
);
|
||||
};
|
||||
|
||||
export default AutoTranslationFeatureDiscovery;
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
type SvgProps = {
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
const AutoTranslationSVG = (props: SvgProps) => (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width={props.width ? props.width.toString() : '158'}
|
||||
height={props.height ? props.height.toString() : '149'}
|
||||
viewBox='0 0 158 149'
|
||||
fill='none'
|
||||
{...props}
|
||||
>
|
||||
<circle
|
||||
cx={89.0743}
|
||||
cy={80.0743}
|
||||
r={59.5743}
|
||||
fill='white'
|
||||
/>
|
||||
<circle
|
||||
cx={89.0743}
|
||||
cy={80.0743}
|
||||
r={60.3243}
|
||||
stroke='#3F4350'
|
||||
strokeOpacity={0.75}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<circle
|
||||
cx={91}
|
||||
cy={82}
|
||||
r={57}
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.08}
|
||||
/>
|
||||
<path
|
||||
d='M92.3795 39.1628C96.2331 40.3948 96.0752 37.9623 98.5073 40.7949C99.9077 42.4271 102.435 45.3967 105.435 46.1338C105.857 46.2391 106.794 45.3335 106.794 44.6174C106.794 43.9014 104.688 42.385 103.53 42.0691C102.371 41.7532 104.003 39.5208 102.414 38.8363C100.824 38.1519 99.0654 35.614 98.4336 33.6238C97.8019 31.6336 95.0012 33.3079 94.0431 32.9078C93.085 32.5076 91.9373 32.7498 90.8844 34.2662C89.8315 35.7825 91.3898 38.8469 92.3795 39.1628Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M113.249 94.8888C113.249 94.0569 105.626 89.9079 104.699 89.8553C103.183 89.7605 101.119 89.0023 99.7293 89.8553C99.1423 90.1886 98.644 90.6581 98.2763 91.2242C98.0131 91.7192 96.5286 90.803 95.96 91.1084C94.5912 91.835 93.8542 89.4657 94.2016 88.5601C95.1597 86.3382 91.7799 87.7387 91.4325 87.1279C91.8431 87.8545 91.3167 80.8098 90.1375 84.7797C89.2425 87.7914 85.7364 84.3479 86.1575 82.1576C86.8103 78.7774 91.9063 78.2825 93.0224 78.8827C93.5909 79.1881 95.6651 82.1893 95.9284 82.105C96.4969 81.926 95.5915 78.5668 95.4756 78.1246C95.0755 76.4818 98.2132 75.9343 98.4343 74.2178C98.6238 72.7647 99.6135 73.2701 100.54 72.5014C101.961 71.3747 100.845 69.2686 102.646 68.1945C103.267 67.826 105.594 67.5627 105.899 66.9098C106.205 66.2569 103.941 64.6879 105.899 64.2246C107.626 63.8034 109.384 64.551 109.858 62.0553C110.279 59.7913 107.752 59.2754 107.026 57.5168C106.794 56.9903 106.51 53.9576 105.436 54.5157C104.12 55.2107 103.33 56.885 102.214 54.8948C100.266 51.3039 95.444 49.756 97.7078 55.4002C98.866 58.3171 94.5491 67.1941 93.8858 58.991C93.6646 56.1373 85.1362 55.2844 88.0001 52.1253C90.864 48.9662 94.8755 48.8188 95.9495 43.9011C97.4446 37.0775 92.717 42.8481 90.3059 41.5634C86.705 39.5943 89.7584 40.6578 87.8948 43.2061C87.1893 44.1749 84.873 42.5954 83.8412 42.7533C82.8093 42.9113 80.4403 45.0068 79.7769 43.6905C78.5766 41.2791 70.1219 39.7943 67.8371 40.6473C63.0675 42.4374 61.4145 40.0155 56.6554 39.0783C55.0024 38.7518 46.8845 38.5202 47.4109 41.3318C47.7901 42.3623 48.2332 43.3681 48.7376 44.3434C49.2535 45.8913 46.9266 44.7014 46.3897 45.07C47.3376 45.9107 48.3287 46.7016 49.3588 47.4393C50.138 48.2396 46.3791 49.2084 46.0001 50.5352C45.2525 53.231 51.1698 52.4623 50.0853 55.7056C49.7484 56.6849 46.3686 58.296 46.0422 61.6131C45.979 62.2449 50.2538 57.5063 53.0124 58.454C55.4445 59.3385 55.234 52.7255 58.2768 52.7255C63.9204 52.7255 65.0154 58.2223 67.9846 61.9079C69.3744 63.6244 70.4904 64.3404 70.7431 66.5834C70.9958 68.8263 70.522 71.2167 71.2801 73.2069C72.0382 75.1971 73.4701 75.2393 74.4388 76.7767C75.597 78.5774 76.2498 80.3359 77.8186 81.9049C80.0928 84.19 83.9675 87.6861 87.5473 87.1701C88.2527 87.0648 90.9166 88.5811 91.5167 89.0339C92.3482 89.8154 93.058 90.717 93.6225 91.7086C94.2543 92.551 96.2548 92.0245 97.1287 92.4247C98.0026 92.8248 95.0966 98.7429 96.1495 100.259C97.2024 101.776 97.2971 103.892 99.0134 105.24C100.73 106.588 101.119 106.619 101.34 108.841C101.667 112.443 100.614 116.086 99.7504 119.445C99.2976 121.162 100.203 123.068 99.5293 124.711C98.6869 126.764 98.1184 128.533 98.8555 130.818C100.129 134.788 107.279 135.43 103.067 131.966C101.267 130.492 105.015 126.932 103.846 126.595C101.835 125.753 104.215 124.995 104.478 124.121C105.015 122.383 112.985 115.191 113.575 114.001C114.07 113.001 113.754 111.969 114.565 111.105C115.375 110.242 116.67 110.484 117.723 109.81C119.829 108.399 119.071 106.009 119.945 104.061C121.672 100.196 122.398 101.723 120.029 98.7007C119.134 97.6056 113.406 98.0057 113.249 94.8888Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M70.6054 36.4156C72.0584 37.9319 73.5008 35.6258 73.7641 36.4156C74.0273 37.2053 74.0483 38.7217 75.7751 38.8376C77.5018 38.9534 74.1642 40.0801 75.2802 40.554C76.3963 41.0279 78.7863 42.5442 79.6602 41.9124C80.5341 41.2806 81.5765 40.7962 81.9661 41.2701C82.3556 41.7439 84.7562 41.1963 84.0719 40.396C83.3875 39.5957 81.566 37.7108 81.6397 36.6262C81.7134 35.5416 83.7455 32.751 81.4818 32.9932C80.9356 33.0619 80.438 33.3417 80.0956 33.7727C79.7532 34.2038 79.5931 34.7518 79.6497 35.2994C79.6497 36.0997 77.5439 34.1094 77.1017 34.141C76.6595 34.1726 75.1855 31.7191 74.6274 31.7191C74.0694 31.7191 70.3316 30.5186 70.6475 31.7191C70.9633 32.9195 69.1524 34.8992 70.6054 36.4156Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M104.025 47.8094C102.898 47.1671 100.761 44.2291 99.8133 45.1769C98.8657 46.1246 100.055 48.336 100.866 49.2416C101.677 50.1472 105.015 52.2743 104.53 51.3476C104.046 50.421 106.236 49.0836 104.025 47.8094Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M72.806 27.5488C73.3851 27.4961 74.2906 29.5074 75.1435 29.7286C75.9963 29.9497 76.4175 30.9501 76.7333 31.1817C77.0492 31.4134 80.6185 30.0445 81.4608 29.7286C82.3032 29.4127 80.408 27.4435 79.7025 26.064C78.9971 24.6846 79.5445 28.3491 78.5443 27.7489C77.544 27.1486 77.6914 26.3273 76.4385 26.3273C75.1856 26.3273 76.0594 23.8316 74.5222 24.3581C72.985 24.8846 72.2269 27.6014 72.806 27.5488Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M85.6092 32.3296C84.5563 32.5402 84.7563 33.2246 84.1667 33.8143C83.8915 34.0957 83.7373 34.4737 83.7373 34.8674C83.7373 35.261 83.8915 35.639 84.1667 35.9204C84.7037 36.6048 85.546 37.8264 86.1882 36.6575C86.8305 35.4887 86.1882 34.5515 86.1882 33.7933C86.1882 33.0351 86.1251 32.2243 85.6092 32.3296Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M87.726 36.0461C87.9892 36.6779 88.4736 34.9299 89.5054 34.0243C90.5372 33.1187 89 30.8652 88.0419 32.013C87.3996 32.8028 87.4628 35.4038 87.726 36.0461Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M87.5367 36.8363C86.9892 37.6155 86.5469 39.7005 87.5367 39.869C87.7535 39.894 87.9729 39.8584 88.1707 39.7661C88.3685 39.6738 88.5367 39.5284 88.6568 39.3462C88.7768 39.1639 88.8439 38.9518 88.8507 38.7337C88.8574 38.5155 88.8034 38.2998 88.6948 38.1104C88.2315 37.3207 87.7683 36.5204 87.5367 36.8363Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M91.5479 23.3994C90.6003 23.8311 92.9377 24.6209 91.3373 25.0527C89.7369 25.4844 89.6948 26.8007 88.3155 25.6844C86.9362 24.5682 85.4306 26.0635 86.4729 26.7375C87.5153 27.4114 89.2631 29.0752 89.2104 29.9808C89.1578 30.8864 90.8635 31.3076 92.4534 31.2023C94.0433 31.097 95.9595 31.5709 96.0122 30.8864C96.0648 30.2019 96.2543 29.9071 96.0648 29.3911C95.7911 28.6435 95.1066 27.9063 93.4641 28.5487C91.8216 29.191 90.7477 28.8646 90.516 28.0643C90.2844 27.264 93.7379 25.7266 95.2646 26.7901C96.7913 27.8537 98.2443 26.211 97.1282 25.4633C96.0122 24.7157 96.7597 23.9259 97.5283 23.8206C98.0864 23.7469 98.5812 22.3358 99.0024 21.2407C96.6604 20.8618 94.2976 20.6262 91.9269 20.5352C91.9375 22.041 92.4955 22.9677 91.5479 23.3994Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M77.4286 22.0725C78.4815 22.5253 80.2294 22.0725 79.903 21.2617C78.9448 21.4302 77.9867 21.6092 77.0391 21.8093C77.1444 21.9287 77.2785 22.0193 77.4286 22.0725Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M78.081 22.9679C76.5859 22.1992 76.6702 24.0841 77.3545 24.8318C78.0389 25.5794 80.0184 23.9156 78.081 22.9679Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M87.1787 20.5355C87.6381 20.6185 88.0676 20.8207 88.4243 21.122C88.7809 21.4232 89.0522 21.8129 89.2108 22.252C89.4108 22.9259 91.0218 21.4622 91.0428 20.5039H89.5056C88.7265 20.4934 87.9579 20.5039 87.1787 20.5355Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M84.0189 22.7474C83.345 23.674 83.9241 24.7902 84.7243 23.8636C85.5245 22.9369 84.8822 20.5887 85.514 21.5996C86.1457 22.6105 87.325 24.2111 88.1146 24.2848C88.9043 24.3585 88.2199 23.3476 88.1146 22.5051C88.0094 21.6627 86.1247 20.8624 86.3353 20.5781C85.156 20.6413 83.9873 20.7361 82.8291 20.8624C83.6609 21.41 84.5348 22.084 84.0189 22.7474Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M87.5363 30.7302C88.4734 31.1093 87.5363 28.8242 87.5363 28.8242C86.5571 29.4034 86.6098 30.3406 87.5363 30.7302Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M84.1672 29.2868C84.3252 29.9292 85.3359 30.1398 85.8729 29.2868C86.4099 28.4339 85.8729 25.338 85.1148 26.3173C84.3567 27.2966 83.4828 26.8438 81.9561 26.3173C80.4294 25.7908 84.0198 28.6129 84.1672 29.2868Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M94.5484 83.5378C95.854 83.3799 96.6542 84.9699 98.1282 84.8962C99.6023 84.8225 101.834 87.0549 103.108 85.7281C104.161 84.6751 98.5599 83.78 96.7911 82.8218C95.0222 81.8635 93.2533 83.6958 94.5484 83.5378Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M101.055 23.6614C102.887 24.546 102.571 24.5144 101.455 25.3673C100.339 26.2203 100.739 28.0736 103.203 27.9473C105.667 27.8209 107.193 25.3673 108.309 28.8213C109.425 32.2752 108.625 36.045 110.057 37.1086C111.489 38.1721 110.299 38.8882 110.057 39.8781C109.815 40.8679 110.457 41.584 111.573 41.2681C112.689 40.9521 112.374 43.0266 111.573 43.9006C110.773 44.7746 109.826 46.4489 112.289 49.4817C114.753 52.5144 115.079 52.1037 115.869 53.1462C116.659 54.1887 117.312 52.7566 117.543 51.5561C117.775 50.3557 116.985 46.133 119.818 46.133C120.7 46.1235 121.555 45.8308 122.259 45.2981C122.962 44.7653 123.476 44.0208 123.724 43.174C124.04 42.458 126.272 42.5317 127.462 41.4997C128.651 40.4678 129.704 39.8043 129.736 36.2346C121.553 28.6393 111.379 23.5266 100.402 21.4922C100.139 22.3135 100.149 23.2402 101.055 23.6614Z'
|
||||
fill='#1C58D9'
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
<path
|
||||
d='M142.451 38.2923C138.342 33.1335 133.362 28.4715 127.513 24.3033C122.837 21.2256 118.091 18.7411 113.278 16.8493C101.552 12.2416 89.4221 11.1511 76.9029 13.575M150.425 51.1571C152.788 56.2284 154.513 61.6648 155.598 67.468C156.902 74.488 157.146 81.3507 156.329 88.0551M101.705 146.163C87.0488 148.79 72.9854 146.894 59.5458 140.431C56.7297 139.077 53.9409 137.522 51.1799 135.766C43.8518 130.36 37.8926 124.268 33.2968 117.486M23.0099 92.2702C21.3844 83.519 21.4382 74.9345 23.1714 66.5165C24.9228 58.0096 28.3892 49.6728 33.5706 41.5062M146.678 44.2577C147.338 45.3128 147.967 46.3861 148.564 47.4775'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity={0.48}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d='M78.288 131.22L62 147.306V64.4189C62 62.7621 63.3431 61.4189 65 61.4189H143.677C145.334 61.4189 146.677 62.7621 146.677 64.4189V127.355C146.677 129.011 145.334 130.355 143.677 130.355H80.396C79.6069 130.355 78.8495 130.666 78.288 131.22Z'
|
||||
fill='#3F4350'
|
||||
fillOpacity={0.08}
|
||||
/>
|
||||
<path
|
||||
d='M83.1266 128.801L66.8386 144.887V62C66.8386 60.3431 68.1818 59 69.8386 59H148.516C150.173 59 151.516 60.3431 151.516 62V124.936C151.516 126.593 150.173 127.936 148.516 127.936H85.2346C84.4455 127.936 83.6881 128.247 83.1266 128.801Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
d='M69.8386 59.75H148.516C149.759 59.7502 150.766 60.7575 150.766 62V124.936C150.766 126.178 149.759 127.185 148.516 127.186H85.2351C84.2487 127.186 83.3012 127.574 82.5994 128.268L67.5886 143.092V62C67.5886 60.7574 68.596 59.75 69.8386 59.75Z'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity={0.8}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d='M90.2258 80.0834H109.581M128.936 80.0834H121.677M109.581 80.0834V71.7012M109.581 80.0834H121.677M121.677 80.0834C121.677 80.0834 119.258 93.2554 110.79 100.44C102.323 107.625 92.6452 111.217 92.6452 111.217'
|
||||
stroke='#3F4350'
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M125.71 111.218C125.71 111.218 118.25 106.456 111.193 100.503C104.137 94.5511 99.0967 86.2178 99.0967 86.2178'
|
||||
stroke='#3F4350'
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M68.4217 77.8334L84.7097 93.9193V11.0322C84.7097 9.37537 83.3666 8.03223 81.7097 8.03223H3.0323C1.37544 8.03223 0.0322952 9.37537 0.0322952 11.0322V73.9679C0.0322952 75.6248 1.37543 76.9679 3.03229 76.9679H66.3137C67.1029 76.9679 67.8602 77.2789 68.4217 77.8334Z'
|
||||
fill='#3F4350'
|
||||
fillOpacity={0.08}
|
||||
/>
|
||||
<path
|
||||
d='M68.3895 70.1889L84.6775 86.2748V3.3877C84.6775 1.73084 83.3343 0.387695 81.6775 0.387695H3.00007C1.34322 0.387695 6.86646e-05 1.73084 6.86646e-05 3.38769V66.3234C6.86646e-05 67.9802 1.34321 69.3234 3.00006 69.3234H66.2815C67.0706 69.3234 67.828 69.6343 68.3895 70.1889Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
d='M81.6775 1.1377H2.99976C1.75726 1.13786 0.749756 2.14516 0.749756 3.3877V66.3232C0.749756 67.5658 1.75726 68.5731 2.99976 68.5732H66.281C67.2675 68.5732 68.2149 68.9621 68.9167 69.6553L83.9275 84.4795V3.3877C83.9275 2.14505 82.9201 1.1377 81.6775 1.1377Z'
|
||||
stroke='#3F4350'
|
||||
strokeOpacity={0.8}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<path
|
||||
d='M26.6128 51.1938L31.2344 40.3067M57.258 51.1938L52.477 40.3067M52.477 40.3067L41.6757 15.71L34.1442 33.4519L31.2344 40.3067M52.477 40.3067H31.2344'
|
||||
stroke='#3F4350'
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default AutoTranslationSVG;
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useMemo, useState} from 'react';
|
||||
import {defineMessages, FormattedMessage} from 'react-intl';
|
||||
|
||||
import type {AutoTranslationSettings} from '@mattermost/types/config';
|
||||
|
||||
import DropdownSetting from 'components/admin_console/dropdown_setting';
|
||||
import {
|
||||
AdminSection,
|
||||
SectionContent,
|
||||
SectionHeader,
|
||||
} from 'components/admin_console/system_properties/controls';
|
||||
import Toggle from 'components/toggle';
|
||||
|
||||
import AutoTranslationInfo from './auto_translation_info';
|
||||
import LibreTranslateSettings from './libreTranslate_settings';
|
||||
|
||||
import type {SystemConsoleCustomSettingsComponentProps} from '../schema_admin_settings';
|
||||
import './localization.scss';
|
||||
import type {SearchableStrings} from '../types';
|
||||
|
||||
const messages = defineMessages({
|
||||
enableAutoTranslationTitle: {
|
||||
id: 'admin.site.localization.enableAutoTranslationTitle',
|
||||
defaultMessage: 'Auto-translation',
|
||||
},
|
||||
enableAutoTranslationDescription: {
|
||||
id: 'admin.site.localization.enableAutoTranslationDescription',
|
||||
defaultMessage: 'Configure auto-translation for channels and direct messages',
|
||||
},
|
||||
});
|
||||
|
||||
export const searchableStrings: SearchableStrings = Object.values(messages);
|
||||
|
||||
export default function AutoTranslation(props: SystemConsoleCustomSettingsComponentProps) {
|
||||
const [autoTranslationSettings, setAutoTranslationSettings] = useState<AutoTranslationSettings>(props.value as AutoTranslationSettings);
|
||||
|
||||
const handleChange = useCallback((id: string, value: any) => {
|
||||
const updatedSettings = {
|
||||
...autoTranslationSettings,
|
||||
[id]: value,
|
||||
};
|
||||
setAutoTranslationSettings(updatedSettings);
|
||||
props.onChange(props.id, updatedSettings);
|
||||
}, [props, autoTranslationSettings]);
|
||||
|
||||
const handleToggle = useCallback(() => {
|
||||
handleChange('Enable', !autoTranslationSettings.Enable);
|
||||
}, [autoTranslationSettings, handleChange]);
|
||||
|
||||
const providerHelpTextValues = useMemo(() => ({
|
||||
br: <br/>,
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
}), []);
|
||||
|
||||
const on = (
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.auto_translation.on'
|
||||
defaultMessage='On'
|
||||
/>
|
||||
);
|
||||
const off = (
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.auto_translation.off'
|
||||
defaultMessage='Off'
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<AdminSection>
|
||||
<SectionHeader>
|
||||
<div className='autotranslation-section-header'>
|
||||
<hgroup>
|
||||
<h1 className='localization-section-title'>
|
||||
<FormattedMessage {...messages.enableAutoTranslationTitle}/>
|
||||
</h1>
|
||||
<h5 className='localization-section-description'>
|
||||
<FormattedMessage {...messages.enableAutoTranslationDescription}/>
|
||||
</h5>
|
||||
</hgroup>
|
||||
<div className='autotranslation-section-toggle'>
|
||||
<span style={{marginRight: '12px'}}>
|
||||
{autoTranslationSettings.Enable ? on : off}
|
||||
</span>
|
||||
<Toggle
|
||||
size='btn-md'
|
||||
disabled={props.disabled || props.setByEnv}
|
||||
toggled={autoTranslationSettings.Enable}
|
||||
id={'Enable'}
|
||||
tabIndex={-1}
|
||||
toggleClassName='btn-toggle-primary'
|
||||
onToggle={handleToggle}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SectionHeader>
|
||||
{autoTranslationSettings.Enable &&
|
||||
<SectionContent>
|
||||
<DropdownSetting
|
||||
id={'Provider'}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.autoTranslationProviderTitle'
|
||||
defaultMessage='Translation Service:'
|
||||
/>
|
||||
}
|
||||
values={[
|
||||
{value: 'libretranslate', text: 'LibreTranslate'},
|
||||
]}
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.autoTranslationProviderDescription'
|
||||
defaultMessage='<strong>NOTE:</strong> If using external translation services (e.g., cloud based),{br}message data may be processed outside of your environment.'
|
||||
values={providerHelpTextValues}
|
||||
/>
|
||||
}
|
||||
value={autoTranslationSettings.Provider || 'libretranslate'}
|
||||
disabled={props.disabled || props.setByEnv}
|
||||
setByEnv={props.setByEnv}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{autoTranslationSettings.Provider === 'libretranslate' &&
|
||||
<LibreTranslateSettings
|
||||
{...props}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
}
|
||||
<AutoTranslationInfo/>
|
||||
</SectionContent>
|
||||
}
|
||||
</AdminSection>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.autoTranslationInfo .sectionNoticeTitle {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {FormattedMessage, useIntl} from 'react-intl';
|
||||
|
||||
import './auto_translation_info.scss';
|
||||
|
||||
import SectionNotice from 'components/section_notice';
|
||||
|
||||
const AutoTranslationInfo = () => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div className='autoTranslationInfo'>
|
||||
<SectionNotice
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.autoTranslationInfo'
|
||||
defaultMessage="Auto-translation must also be enabled in each channel where it's needed."
|
||||
/>
|
||||
}
|
||||
text={intl.formatMessage({
|
||||
id: 'admin.site.localization.autoTranslationInfoSecondary',
|
||||
defaultMessage: 'When multiple languages are detected, users will be prompted to enable auto-translation. [Learn more](https://docs.mattermost.com/).',
|
||||
})}
|
||||
type='info'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(AutoTranslationInfo);
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import {defineMessage, FormattedMessage} from 'react-intl';
|
||||
|
||||
import type {AutoTranslationSettings} from '@mattermost/types/config';
|
||||
|
||||
import TextSetting from 'components/admin_console/text_setting';
|
||||
import ExternalLink from 'components/external_link';
|
||||
|
||||
import './localization.scss';
|
||||
|
||||
import {type SystemConsoleCustomSettingsComponentProps} from '../schema_admin_settings';
|
||||
|
||||
type LibreTranslateSettings = {
|
||||
URL: string;
|
||||
APIKey: string;
|
||||
}
|
||||
|
||||
export default function LibreTranslateSettings(props: SystemConsoleCustomSettingsComponentProps) {
|
||||
const values = props.value as AutoTranslationSettings;
|
||||
const [libreTranslateSettings, setLibreTranslateSettings] = useState<LibreTranslateSettings>(values.LibreTranslate);
|
||||
|
||||
const handleChange = useCallback((id: string, value: any) => {
|
||||
const updatedSettings = {
|
||||
...libreTranslateSettings,
|
||||
[id]: value,
|
||||
};
|
||||
setLibreTranslateSettings(updatedSettings);
|
||||
props.onChange('LibreTranslate', updatedSettings);
|
||||
}, [props, libreTranslateSettings]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextSetting
|
||||
id='URL'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.autoTranslationProviderLibreTranslateURLTitle'
|
||||
defaultMessage='LibreTranslate API Endpoint:'
|
||||
/>
|
||||
}
|
||||
placeholder={defineMessage({
|
||||
id: 'admin.site.localization.autoTranslationProviderLibreTranslateURLExample',
|
||||
defaultMessage: 'e.g.: "https://libretranslate.yourdomain.com"',
|
||||
})}
|
||||
type='url'
|
||||
value={libreTranslateSettings.URL}
|
||||
setByEnv={false}
|
||||
onChange={handleChange}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
<TextSetting
|
||||
id='APIKey'
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.autoTranslationProviderLibreTranslateAPIKeyTitle'
|
||||
defaultMessage='LibreTranslate API Key:'
|
||||
/>
|
||||
}
|
||||
placeholder={defineMessage({
|
||||
id: 'admin.site.localization.autoTranslationProviderLibreTranslateAPIKeyExample',
|
||||
defaultMessage: 'Enter LibreTranslate API Key',
|
||||
})}
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
id='admin.site.localization.autoTranslationProviderLibreTranslateAPIKeyDescription'
|
||||
defaultMessage='If your LibreTranslate server requires an API key, enter it here. Otherwise, leave this field blank. View <link>LibreTranslate docs</link> for API Key management.'
|
||||
values={{
|
||||
link: (msg: React.ReactNode) => (
|
||||
<ExternalLink
|
||||
location='admin_console'
|
||||
href='https://docs.libretranslate.com/guides/manage_api_keys/'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
}}
|
||||
/>}
|
||||
type='password'
|
||||
value={libreTranslateSettings.APIKey}
|
||||
setByEnv={false}
|
||||
onChange={handleChange}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
.autotranslation-section-header {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0; // prevents overflow in flex layouts
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.autotranslation-section-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.localization-section-title {
|
||||
margin: unset;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.localization-section-description {
|
||||
margin-bottom: 0;
|
||||
color: var(--center-channel-color-72);
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {useCallback, useMemo, useState} from 'react';
|
||||
import {defineMessages, FormattedMessage} from 'react-intl';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import type {LocalizationSettings} from '@mattermost/types/config';
|
||||
|
||||
import BooleanSetting from 'components/admin_console/boolean_setting';
|
||||
import DropdownSetting from 'components/admin_console/dropdown_setting';
|
||||
import MultiSelectSetting from 'components/admin_console/multiselect_settings';
|
||||
import {
|
||||
SectionContent,
|
||||
SectionHeader,
|
||||
} from 'components/admin_console/system_properties/controls';
|
||||
import ExternalLink from 'components/external_link';
|
||||
|
||||
import * as I18n from 'i18n/i18n.jsx';
|
||||
|
||||
import type {SystemConsoleCustomSettingsComponentProps} from '../schema_admin_settings';
|
||||
import type {SearchableStrings} from '../types';
|
||||
import './localization.scss';
|
||||
|
||||
const locales = I18n.getAllLanguages();
|
||||
|
||||
const AdminSection = styled.section.attrs({className: 'AdminPanel'})`
|
||||
&& {
|
||||
overflow: visible;
|
||||
margin-top: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const messages = defineMessages({
|
||||
langTitle: {
|
||||
id: 'admin.site.localization.languages.title',
|
||||
defaultMessage: 'Languages',
|
||||
},
|
||||
langDescription: {
|
||||
id: 'admin.site.localization.languages.description',
|
||||
defaultMessage: 'Choose which languages should be the defaults',
|
||||
},
|
||||
serverLocaleTitle: {
|
||||
id: 'admin.general.localization.serverLocaleTitle',
|
||||
defaultMessage: 'Default Server Language:',
|
||||
},
|
||||
serverLocaleDescription: {
|
||||
id: 'admin.general.localization.serverLocaleDescription',
|
||||
defaultMessage: 'Default language for system messages.',
|
||||
},
|
||||
clientLocaleTitle: {
|
||||
id: 'admin.general.localization.clientLocaleTitle',
|
||||
defaultMessage: 'Default Client Language:',
|
||||
},
|
||||
clientLocaleDescription: {
|
||||
id: 'admin.general.localization.clientLocaleDescription',
|
||||
defaultMessage: "Default language for newly created users and pages where the user hasn't logged in.",
|
||||
},
|
||||
availableLocalesTitle: {
|
||||
id: 'admin.general.localization.availableLocalesTitle',
|
||||
defaultMessage: 'Available Languages:',
|
||||
},
|
||||
availableLocalesDescription: {
|
||||
id: 'admin.general.localization.availableLocalesDescription',
|
||||
defaultMessage: "Set which languages are available for users in <strong>Settings > Display > Language</strong> (leave this field blank to have all supported languages available). If you're manually adding new languages, the <strong>Default Client Language</strong> must be added before saving this setting.\n \nWould like to help with translations? Join the <link>Mattermost Translation Server</link> to contribute.",
|
||||
},
|
||||
availableLocalesNoResults: {
|
||||
id: 'admin.general.localization.availableLocalesNoResults',
|
||||
defaultMessage: 'No results found',
|
||||
},
|
||||
enableExperimentalLocalesTitle: {
|
||||
id: 'admin.general.localization.enableExperimentalLocalesTitle',
|
||||
defaultMessage: 'Enable Experimental Locales:',
|
||||
},
|
||||
enableExperimentalLocalesDescription: {
|
||||
id: 'admin.general.localization.enableExperimentalLocalesDescription',
|
||||
defaultMessage: 'When true, it allows users to select experimental (e.g., in progress) languages.',
|
||||
},
|
||||
});
|
||||
|
||||
export const searchableStrings: SearchableStrings = Object.values(messages);
|
||||
|
||||
export default function Localization(props: SystemConsoleCustomSettingsComponentProps) {
|
||||
const [localizationSettings, setLocalizationSettings] = useState<LocalizationSettings>(props.value as LocalizationSettings);
|
||||
|
||||
const handleChange = useCallback((id: string, value: any) => {
|
||||
const updatedSettings = {
|
||||
...localizationSettings,
|
||||
[id]: value,
|
||||
};
|
||||
setLocalizationSettings(updatedSettings);
|
||||
props.onChange(props.id, updatedSettings);
|
||||
}, [props, localizationSettings]);
|
||||
|
||||
const availableLanguages = useMemo(() => {
|
||||
const values: Array<{value: string; text: string; order: number}> = [];
|
||||
for (const l of Object.values(locales)) {
|
||||
values.push({value: l.value, text: l.name, order: l.order});
|
||||
}
|
||||
values.sort((a, b) => a.order - b.order);
|
||||
return values;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AdminSection>
|
||||
<SectionHeader>
|
||||
<hgroup>
|
||||
<h1 className='localization-section-title'>
|
||||
<FormattedMessage {...messages.langTitle}/>
|
||||
</h1>
|
||||
<h5 className='localization-section-description'>
|
||||
<FormattedMessage {...messages.langDescription}/>
|
||||
</h5>
|
||||
</hgroup>
|
||||
</SectionHeader>
|
||||
|
||||
<SectionContent>
|
||||
<DropdownSetting
|
||||
id={'DefaultServerLocale'}
|
||||
label={
|
||||
<FormattedMessage {...messages.serverLocaleTitle}/>
|
||||
}
|
||||
values={availableLanguages}
|
||||
helpText={
|
||||
<FormattedMessage {...messages.serverLocaleDescription}/>
|
||||
}
|
||||
value={localizationSettings.DefaultServerLocale || availableLanguages[0].value}
|
||||
disabled={props.disabled}
|
||||
setByEnv={props.setByEnv}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<DropdownSetting
|
||||
id={'DefaultClientLocale'}
|
||||
label={
|
||||
<FormattedMessage {...messages.clientLocaleTitle}/>
|
||||
}
|
||||
values={availableLanguages}
|
||||
helpText={
|
||||
<FormattedMessage {...messages.clientLocaleDescription}/>
|
||||
}
|
||||
value={localizationSettings.DefaultClientLocale || availableLanguages[0].value}
|
||||
disabled={props.disabled}
|
||||
setByEnv={props.setByEnv}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<MultiSelectSetting
|
||||
id={'AvailableLocales'}
|
||||
label={
|
||||
<FormattedMessage {...messages.availableLocalesTitle}/>
|
||||
}
|
||||
values={availableLanguages}
|
||||
helpText={
|
||||
<FormattedMessage
|
||||
{...messages.availableLocalesDescription}
|
||||
values={{
|
||||
link: (msg: React.ReactNode) => (
|
||||
<ExternalLink
|
||||
location='admin_console'
|
||||
href='https://translate.mattermost.com/'
|
||||
>
|
||||
{msg}
|
||||
</ExternalLink>
|
||||
),
|
||||
strong: (msg: React.ReactNode) => <strong>{msg}</strong>,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
selected={(localizationSettings.AvailableLocales.split(',')) || []}
|
||||
disabled={props.disabled}
|
||||
setByEnv={props.setByEnv}
|
||||
onChange={(changedId, value) => handleChange(changedId, value.join(','))}
|
||||
noOptionsMessage={
|
||||
<FormattedMessage {...messages.availableLocalesNoResults}/>
|
||||
}
|
||||
/>
|
||||
<BooleanSetting
|
||||
id={'EnableExperimentalLocales'}
|
||||
label={
|
||||
<FormattedMessage {...messages.enableExperimentalLocalesTitle}/>
|
||||
}
|
||||
helpText={
|
||||
<FormattedMessage {...messages.enableExperimentalLocalesDescription}/>
|
||||
}
|
||||
value={localizationSettings.EnableExperimentalLocales}
|
||||
disabled={props.disabled}
|
||||
setByEnv={props.setByEnv}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</SectionContent>
|
||||
</AdminSection>
|
||||
);
|
||||
}
|
||||
21
webapp/channels/src/components/widgets/tag/sku_tag.test.tsx
Normal file
21
webapp/channels/src/components/widgets/tag/sku_tag.test.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {render, screen} from 'tests/react_testing_utils';
|
||||
import {LicenseSkus} from 'utils/constants';
|
||||
|
||||
import SkuTag from './sku_tag';
|
||||
|
||||
describe('components/widgets/tag/SkuTag', () => {
|
||||
test('should match the ENTRY SKU', () => {
|
||||
render(<SkuTag sku={LicenseSkus.Entry}/>);
|
||||
expect(screen.getByText('ENTRY')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should match the ENTERPRISE ADVANCED SKU', () => {
|
||||
render(<SkuTag sku={LicenseSkus.EnterpriseAdvanced}/>);
|
||||
expect(screen.getByText('ENTERPRISE ADVANCED')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
50
webapp/channels/src/components/widgets/tag/sku_tag.tsx
Normal file
50
webapp/channels/src/components/widgets/tag/sku_tag.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React, {useMemo} from 'react';
|
||||
|
||||
import {LicenseSkus} from 'utils/constants';
|
||||
|
||||
import Tag from './tag';
|
||||
import type {TagSize} from './tag';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
size?: TagSize;
|
||||
sku: LicenseSkus;
|
||||
};
|
||||
|
||||
const SkuTag = ({className = '', size = 'xs', sku}: Props) => {
|
||||
const namedSku = useMemo(() => {
|
||||
switch (sku) {
|
||||
case LicenseSkus.Starter:
|
||||
return 'STARTER';
|
||||
case LicenseSkus.Professional:
|
||||
return 'PROFESSIONAL';
|
||||
case LicenseSkus.Enterprise:
|
||||
return 'ENTERPRISE';
|
||||
case LicenseSkus.E10:
|
||||
return 'ENTERPRISE E10';
|
||||
case LicenseSkus.E20:
|
||||
return 'ENTERPRISE E20';
|
||||
case LicenseSkus.EnterpriseAdvanced:
|
||||
return 'ENTERPRISE ADVANCED';
|
||||
case LicenseSkus.Entry:
|
||||
return 'ENTRY';
|
||||
default:
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
}, [sku]);
|
||||
|
||||
return (
|
||||
<Tag
|
||||
className={classNames('SkuTag', className)}
|
||||
icon='mattermost'
|
||||
size={size}
|
||||
text={namedSku}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkuTag;
|
||||
|
|
@ -389,6 +389,8 @@
|
|||
"admin.authentication.openid": "OpenID Connect",
|
||||
"admin.authentication.saml": "SAML 2.0",
|
||||
"admin.authentication.signup": "Signup",
|
||||
"admin.auto_translation_feature_discovery.copy": "Effortlessly collaborate across languages with auto-translation. Messages in shared channels are instantly translated based on each user’s language preference—no extra steps required.{br}<strong>Only available in Enterprise Advanced.</strong>",
|
||||
"admin.auto_translation_feature_discovery.title": "Remove language barriers with auto-translation",
|
||||
"admin.banner.heading": "Note:",
|
||||
"admin.billing.company_info_display.companyDetails": "Company Details",
|
||||
"admin.billing.company_info_display.detailsProvided": "Your company name and address",
|
||||
|
|
@ -2751,6 +2753,21 @@
|
|||
"admin.site.emoji": "Emoji",
|
||||
"admin.site.fileSharingDownloads": "File Sharing and Downloads",
|
||||
"admin.site.localization": "Localization",
|
||||
"admin.site.localization.auto_translation.off": "Off",
|
||||
"admin.site.localization.auto_translation.on": "On",
|
||||
"admin.site.localization.autoTranslationInfo": "Auto-translation must also be enabled in each channel where it's needed.",
|
||||
"admin.site.localization.autoTranslationInfoSecondary": "When multiple languages are detected, users will be prompted to enable auto-translation. [Learn more](https://docs.mattermost.com/).",
|
||||
"admin.site.localization.autoTranslationProviderDescription": "<strong>NOTE:</strong> If using external translation services (e.g., cloud based),{br}message data may be processed outside of your environment.",
|
||||
"admin.site.localization.autoTranslationProviderLibreTranslateAPIKeyDescription": "If your LibreTranslate server requires an API key, enter it here. Otherwise, leave this field blank. View <link>LibreTranslate docs</link> for API Key management.",
|
||||
"admin.site.localization.autoTranslationProviderLibreTranslateAPIKeyExample": "Enter LibreTranslate API Key",
|
||||
"admin.site.localization.autoTranslationProviderLibreTranslateAPIKeyTitle": "LibreTranslate API Key:",
|
||||
"admin.site.localization.autoTranslationProviderLibreTranslateURLExample": "e.g.: \"https://libretranslate.yourdomain.com\"",
|
||||
"admin.site.localization.autoTranslationProviderLibreTranslateURLTitle": "LibreTranslate API Endpoint:",
|
||||
"admin.site.localization.autoTranslationProviderTitle": "Translation Service:",
|
||||
"admin.site.localization.enableAutoTranslationDescription": "Configure auto-translation for channels and direct messages",
|
||||
"admin.site.localization.enableAutoTranslationTitle": "Auto-translation",
|
||||
"admin.site.localization.languages.description": "Choose which languages should be the defaults",
|
||||
"admin.site.localization.languages.title": "Languages",
|
||||
"admin.site.move_thread": "Move thread",
|
||||
"admin.site.notices": "Notices",
|
||||
"admin.site.posts": "Posts",
|
||||
|
|
|
|||
|
|
@ -739,6 +739,20 @@ export type LocalizationSettings = {
|
|||
EnableExperimentalLocales: boolean;
|
||||
};
|
||||
|
||||
export type AutoTranslationSettings = {
|
||||
Enable: boolean;
|
||||
Provider: '' | 'libretranslate';
|
||||
LibreTranslate: {
|
||||
URL: string;
|
||||
APIKey: string;
|
||||
};
|
||||
TimeoutMs: {
|
||||
NewPost: number;
|
||||
Fetch: number;
|
||||
Notification: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type SamlSettings = {
|
||||
Enable: boolean;
|
||||
EnableSyncWithLdap: boolean;
|
||||
|
|
@ -1052,6 +1066,7 @@ export type AdminConfig = {
|
|||
ConnectedWorkspacesSettings: ConnectedWorkspacesSettings;
|
||||
AccessControlSettings: AccessControlSettings;
|
||||
ContentFlaggingSettings: ContentFlaggingSettings;
|
||||
AutoTranslationSettings: AutoTranslationSettings;
|
||||
};
|
||||
|
||||
export type ReplicaLagSetting = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue