MM-52261: Remove boards language selector, use Language in Display Settings (#23095)

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Caleb Roseland 2023-05-08 13:37:33 -05:00 committed by GitHub
parent a1470c77ac
commit 657c0024f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 134 additions and 1376 deletions

View File

@ -259,7 +259,6 @@
"Sidebar.no-boards-in-category": "No boards inside",
"Sidebar.product-tour": "Product tour",
"Sidebar.random-icons": "Random icons",
"Sidebar.set-language": "Set language",
"Sidebar.set-theme": "Set theme",
"Sidebar.settings": "Settings",
"Sidebar.template-from-board": "New template from board",

View File

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useEffect} from 'react'
import {IntlProvider} from 'react-intl'
import {DndProvider} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import {TouchBackend} from 'react-dnd-touch-backend'
@ -9,12 +8,10 @@ import {History} from 'history'
import TelemetryClient from './telemetry/telemetryClient'
import {getMessages} from './i18n'
import FlashMessages from './components/flashMessages'
import NewVersionBanner from './components/newVersionBanner'
import {Utils} from './utils'
import {fetchMe, getMe} from './store/users'
import {fetchLanguage, getLanguage} from './store/language'
import {useAppDispatch, useAppSelector} from './store/hooks'
import {fetchClientConfig} from './store/clientConfig'
import FocalboardRouter from './router'
@ -26,12 +23,10 @@ type Props = {
}
const App = (props: Props): JSX.Element => {
const language = useAppSelector<string>(getLanguage)
const me = useAppSelector<IUser|null>(getMe)
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(fetchLanguage())
dispatch(fetchMe())
dispatch(fetchClientConfig())
}, [])
@ -43,20 +38,15 @@ const App = (props: Props): JSX.Element => {
}, [me])
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<DndProvider backend={Utils.isMobile() ? TouchBackend : HTML5Backend}>
<FlashMessages milliseconds={2000}/>
<div id='frame'>
<div id='main'>
<NewVersionBanner/>
<FocalboardRouter history={props.history}/>
</div>
<DndProvider backend={Utils.isMobile() ? TouchBackend : HTML5Backend}>
<FlashMessages milliseconds={2000}/>
<div id='frame'>
<div id='main'>
<NewVersionBanner/>
<FocalboardRouter history={props.history}/>
</div>
</DndProvider>
</IntlProvider>
</div>
</DndProvider>
)
}

View File

@ -1,14 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useCallback, useMemo, useState} from 'react'
import {FormattedMessage, IntlProvider, useIntl} from 'react-intl'
import {FormattedMessage, useIntl} from 'react-intl'
import debounce from 'lodash/debounce'
import {SuiteWindow} from 'src/types/index'
import {getMessages} from 'src/i18n'
import {getLanguage} from 'src/store/language'
import {useWebsockets} from 'src/hooks/websockets'
import octoClient from 'src/octoClient'
@ -220,17 +217,4 @@ const BoardSelector = () => {
)
}
const IntlBoardSelector = () => {
const language = useAppSelector<string>(getLanguage)
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<BoardSelector/>
</IntlProvider>
)
}
export default IntlBoardSelector
export default BoardSelector

View File

@ -1,19 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useEffect, useState} from 'react'
import {FormattedMessage, IntlProvider, useIntl} from 'react-intl'
import {FormattedMessage, useIntl} from 'react-intl'
import WithWebSockets from 'src/components/withWebSockets'
import {useWebsockets} from 'src/hooks/websockets'
import {getLanguage} from 'src/store/language'
import {useAppSelector} from 'src/store/hooks'
import {getCurrentTeamId} from 'src/store/teams'
import {MMWebSocketClient, WSClient} from 'src/wsclient'
import manifest from 'src/manifest'
import {getMessages} from 'src/i18n'
import {Utils} from 'src/utils'
import {Block} from 'src/blocks/block'
import {Card} from 'src/blocks/card'
@ -278,17 +276,4 @@ export const BoardsUnfurl = (props: Props): JSX.Element => {
)
}
const IntlBoardsUnfurl = (props: Props) => {
const language = useAppSelector<string>(getLanguage)
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<BoardsUnfurl {...props}/>
</IntlProvider>
)
}
export default IntlBoardsUnfurl
export default BoardsUnfurl

View File

@ -7,8 +7,6 @@ import React, {
useState,
} from 'react'
import {IntlProvider, createIntl, createIntlCache} from 'react-intl'
import Select from 'react-select/async'
import {
FormatOptionLabelMeta,
@ -20,10 +18,9 @@ import {
import {CSSObject} from '@emotion/serialize'
import {getCurrentLanguage, getMessages} from 'src/i18n'
import {getLanguage} from 'src/store/language'
import {useIntl} from 'react-intl'
import CompassIcon from 'src/widgets/icons/compassIcon'
import {useAppSelector} from 'src/store/hooks'
import {mutator} from 'src/mutator'
import {useGetAllTemplates} from 'src/hooks/useGetAllTemplates'
@ -46,12 +43,6 @@ type ReactSelectItem = {
const EMPTY_BOARD = 'empty_board'
const TEMPLATE_DESCRIPTION_LENGTH = 70
const cache = createIntlCache()
const intl = createIntl({
locale: getCurrentLanguage(),
messages: getMessages(getCurrentLanguage()),
}, cache)
const {ValueContainer, Placeholder} = components
const CustomValueContainer = ({children, ...props}: any) => {
return (
@ -67,6 +58,7 @@ const CustomValueContainer = ({children, ...props}: any) => {
}
const CreateBoardFromTemplate = (props: Props) => {
const intl = useIntl()
const {formatMessage} = intl
const [addBoard, setAddBoard] = useState(false)
@ -259,17 +251,4 @@ const CreateBoardFromTemplate = (props: Props) => {
)
}
const IntlCreateBoardFromTemplate = (props: Props) => {
const language = useAppSelector<string>(getLanguage)
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<CreateBoardFromTemplate {...props}/>
</IntlProvider>
)
}
export default IntlCreateBoardFromTemplate
export default CreateBoardFromTemplate

View File

@ -238,24 +238,6 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu imports menu open should ma
</div>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option open-left"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<i
class="CompassIcon icon-chevron-right"
/>
</div>
</div>
<div>
<div
aria-label="Random icons"
@ -390,499 +372,6 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu languages menu open should
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option open-left menu-option-active"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<i
class="CompassIcon icon-chevron-right"
/>
<div
class="SubMenu Menu noselect left-bottom"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
aria-label="English"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
English
</div>
</div>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
aria-label="Español"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Español
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Deutsch"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Deutsch
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="日本語"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
日本語
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Français"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Français
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Nederlands"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Nederlands
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Pусский"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Pусский
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="中文 (繁體)"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
中文 (繁體)
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="中文 (简体)"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
中文 (简体)
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Türkçe"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Türkçe
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Occitan"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Occitan
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Português (Brasil)"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Português (Brasil)
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Català"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Català
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Ελληνικά"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Ελληνικά
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="bahasa Indonesia"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
bahasa Indonesia
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Italiano"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Italiano
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Svenska"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Svenska
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
aria-label="Cancel"
class="MenuOption TextOption menu-option menu-cancel"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Cancel
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div
aria-label="Random icons"
@ -1039,24 +528,6 @@ exports[`components/sidebar/GlobalHeaderSettingsMenu settings menu open should m
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option open-left"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<i
class="CompassIcon icon-chevron-right"
/>
</div>
</div>
<div>
<div
aria-label="Random icons"

View File

@ -3,14 +3,10 @@
//
import React from 'react'
import {Provider as ReduxProvider} from 'react-redux'
import {IntlProvider} from 'react-intl'
import {History} from 'history'
import HelpIcon from 'src/widgets/icons/help'
import store from 'src/store'
import {useAppSelector} from 'src/store/hooks'
import {getLanguage} from 'src/store/language'
import {getMessages} from 'src/i18n'
import {Constants} from 'src/constants'
@ -23,27 +19,21 @@ type HeaderItemProps = {
}
const HeaderItems = (props: HeaderItemProps) => {
const language = useAppSelector<string>(getLanguage)
const helpUrl = 'https://www.focalboard.com/fwlink/doc-boards.html?v=' + Constants.versionString
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<div className='GlobalHeaderComponent'>
<span className='spacer'/>
<a
href={helpUrl}
target='_blank'
rel='noreferrer'
className='GlobalHeaderComponent__button help-button'
>
<HelpIcon/>
</a>
<GlobalHeaderSettingsMenu history={props.history}/>
</div>
</IntlProvider>
<div className='GlobalHeaderComponent'>
<span className='spacer'/>
<a
href={helpUrl}
target='_blank'
rel='noreferrer'
className='GlobalHeaderComponent__button help-button'
>
<HelpIcon/>
</a>
<GlobalHeaderSettingsMenu history={props.history}/>
</div>
)
}

View File

@ -8,13 +8,11 @@ import {Archiver} from 'src/archiver'
import Menu from 'src/widgets/menu'
import MenuWrapper from 'src/widgets/menuWrapper'
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
import {storeLanguage} from 'src/store/language'
import {getMe, patchProps} from 'src/store/users'
import {Team, getCurrentTeam} from 'src/store/teams'
import {IUser, UserConfigPatch} from 'src/user'
import octoClient from 'src/octoClient'
import {UserSettings} from 'src/userSettings'
import CheckIcon from 'src/widgets/icons/check'
import SettingsIcon from 'src/widgets/icons/settings'
import {Constants} from 'src/constants'
@ -72,23 +70,6 @@ const GlobalHeaderSettingsMenu = (props: Props) => {
))
}
</Menu.SubMenu>
<Menu.SubMenu
id='lang'
name={intl.formatMessage({id: 'Sidebar.set-language', defaultMessage: 'Set language'})}
position='left-bottom'
>
{
Constants.languages.map((language) => (
<Menu.Text
key={language.code}
id={`${language.name}-lang`}
name={language.displayName}
onClick={async () => dispatch(storeLanguage(language.code))}
rightIcon={intl.locale.toLowerCase() === language.code ? <CheckIcon/> : null}
/>
))
}
</Menu.SubMenu>
<Menu.Switch
id='random-icons'
name={intl.formatMessage({id: 'Sidebar.random-icons', defaultMessage: 'Random icons'})}

View File

@ -1,10 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useEffect} from 'react'
import {FormattedMessage, IntlProvider, useIntl} from 'react-intl'
import {getMessages} from 'src/i18n'
import {getLanguage} from 'src/store/language'
import {FormattedMessage, useIntl} from 'react-intl'
import {useWebsockets} from 'src/hooks/websockets'
@ -169,17 +166,4 @@ const RHSChannelBoards = () => {
)
}
const IntlRHSChannelBoards = () => {
const language = useAppSelector<string>(getLanguage)
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<RHSChannelBoards/>
</IntlProvider>
)
}
export default IntlRHSChannelBoards
export default RHSChannelBoards

View File

@ -1,10 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
import {FormattedMessage, IntlProvider} from 'react-intl'
import {FormattedMessage} from 'react-intl'
import {getMessages} from 'src/i18n'
import {getLanguage} from 'src/store/language'
import {getCurrentChannel} from 'src/store/channels'
import {useAppSelector} from 'src/store/hooks'
@ -12,31 +10,25 @@ import appBarIcon from 'static/app-bar-icon.png'
const RHSChannelBoardsHeader = () => {
const currentChannel = useAppSelector(getCurrentChannel)
const language = useAppSelector<string>(getLanguage)
if (!currentChannel) {
return null
}
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<div>
<img
className='boards-rhs-header-logo'
src={appBarIcon}
<div>
<img
className='boards-rhs-header-logo'
src={appBarIcon}
/>
<span>
<FormattedMessage
id='rhs-channel-boards-header.title'
defaultMessage='Boards'
/>
<span>
<FormattedMessage
id='rhs-channel-boards-header.title'
defaultMessage='Boards'
/>
</span>
<span className='style--none sidebar--right__title__subtitle'>{currentChannel.display_name}</span>
</div>
</IntlProvider>
</span>
<span className='style--none sidebar--right__title__subtitle'>{currentChannel.display_name}</span>
</div>
)
}

View File

@ -263,24 +263,6 @@ exports[`components/sidebar/SidebarSettingsMenu imports menu open should match s
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<i
class="CompassIcon icon-chevron-right"
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option"
@ -431,499 +413,6 @@ exports[`components/sidebar/SidebarSettingsMenu languages menu open should match
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option menu-option-active"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<i
class="CompassIcon icon-chevron-right"
/>
<div
class="SubMenu Menu noselect top"
>
<div
class="menu-contents"
>
<div
class="menu-options"
>
<div
aria-label="English"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
English
</div>
</div>
<svg
class="CheckIcon Icon"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<polyline
points="20,60 40,80 80,40"
/>
</svg>
</div>
<div
aria-label="Español"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Español
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Deutsch"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Deutsch
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="日本語"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
日本語
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Français"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Français
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Nederlands"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Nederlands
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Pусский"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Pусский
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="中文 (繁體)"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
中文 (繁體)
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="中文 (简体)"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
中文 (简体)
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Türkçe"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Türkçe
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Occitan"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Occitan
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Português (Brasil)"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Português (Brasil)
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Català"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Català
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Ελληνικά"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Ελληνικά
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="bahasa Indonesia"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
bahasa Indonesia
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Italiano"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Italiano
</div>
</div>
<div
class="noicon"
/>
</div>
<div
aria-label="Svenska"
class="MenuOption TextOption menu-option"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Svenska
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
<div
class="menu-spacer hideOnWidescreen"
/>
<div
class="menu-options hideOnWidescreen"
>
<div
aria-label="Cancel"
class="MenuOption TextOption menu-option menu-cancel"
role="button"
>
<div
class="d-flex"
>
<div
class="noicon"
/>
</div>
<div
class="menu-option__content"
>
<div
class="menu-name"
>
Cancel
</div>
</div>
<div
class="noicon"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option"
@ -1094,24 +583,6 @@ exports[`components/sidebar/SidebarSettingsMenu settings menu open should match
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<i
class="CompassIcon icon-chevron-right"
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option"
@ -1262,24 +733,6 @@ exports[`components/sidebar/SidebarSettingsMenu theme menu open should match sna
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option"
id="lang"
>
<div
class="noicon"
/>
<div
class="menu-name"
>
Set language
</div>
<i
class="CompassIcon icon-chevron-right"
/>
</div>
</div>
<div>
<div
class="MenuOption SubMenuOption menu-option menu-option-active"

View File

@ -17,8 +17,7 @@ import {
} from 'src/theme'
import Menu from 'src/widgets/menu'
import MenuWrapper from 'src/widgets/menuWrapper'
import {useAppDispatch, useAppSelector} from 'src/store/hooks'
import {storeLanguage} from 'src/store/language'
import {useAppSelector} from 'src/store/hooks'
import {Team, getCurrentTeam} from 'src/store/teams'
import {UserSettings} from 'src/userSettings'
@ -34,7 +33,6 @@ type Props = {
const SidebarSettingsMenu = (props: Props) => {
const intl = useIntl()
const dispatch = useAppDispatch()
const currentTeam = useAppSelector<Team|null>(getCurrentTeam)
// we need this as the sidebar doesn't always need to re-render
@ -123,23 +121,6 @@ const SidebarSettingsMenu = (props: Props) => {
}
}}
/>
<Menu.SubMenu
id='lang'
name={intl.formatMessage({id: 'Sidebar.set-language', defaultMessage: 'Set language'})}
position='top'
>
{
Constants.languages.map((language) => (
<Menu.Text
key={language.code}
id={`${language.name}-lang`}
name={language.displayName}
onClick={async () => dispatch(storeLanguage(language.code))}
rightIcon={intl.locale.toLowerCase() === language.code ? <CheckIcon/> : null}
/>
))
}
</Menu.SubMenu>
<Menu.SubMenu
id='theme'
name={intl.formatMessage({id: 'Sidebar.set-theme', defaultMessage: 'Set theme'})}

View File

@ -5,79 +5,81 @@ import messages_ca from 'i18n/ca.json'
import messages_de from 'i18n/de.json'
import messages_el from 'i18n/el.json'
import messages_en from 'i18n/en.json'
import messages_enAu from 'i18n/en_AU.json'
import messages_es from 'i18n/es.json'
import messages_fa from 'i18n/fa.json'
import messages_fr from 'i18n/fr.json'
import messages_hu from 'i18n/hu.json'
import messages_id from 'i18n/id.json'
import messages_it from 'i18n/it.json'
import messages_ja from 'i18n/ja.json'
import messages_ko from 'i18n/ko.json'
import messages_nl from 'i18n/nl.json'
import messages_oc from 'i18n/oc.json'
import messages_pl from 'i18n/pl.json'
import messages_ptBr from 'i18n/pt_BR.json'
import messages_ru from 'i18n/ru.json'
import messages_sv from 'i18n/sv.json'
import messages_tr from 'i18n/tr.json'
import messages_uk from 'i18n/uk.json'
import messages_zhHans from 'i18n/zh_Hans.json'
import messages_zhHant from 'i18n/zh_Hant.json'
import {UserSettings} from './userSettings'
const supportedLanguages = ['ca', 'de', 'el', 'en', 'es', 'fr', 'id', 'it', 'ja', 'nl', 'oc', 'pt-br', 'ru', 'sv', 'tr', 'zh-cn', 'zh-tw']
export function getMessages(lang: string): {[key: string]: string} {
switch (lang) {
export function getMessages(locale: string): {[key: string]: string} {
switch (locale) {
// case 'bg':
// return messages_bg // TODO missing translation sourcefile
case 'ca':
return messages_ca
return messages_ca // TODO missing option in language selector
case 'de':
return messages_de
case 'el':
return messages_el
return messages_el // TODO missing option in language selector
case 'en':
default:
return messages_en
case 'en-AU':
return messages_enAu
case 'es':
return messages_es
case 'fa':
return messages_fa
case 'fr':
return messages_fr
case 'hu':
return messages_hu
case 'id':
return messages_id
return messages_id // TODO missing option in language selector
case 'it':
return messages_it
case 'ja':
return messages_ja
case 'ko':
return messages_ko
case 'nl':
return messages_nl
case 'oc':
return messages_oc
case 'pt-br':
return messages_oc // TODO missing option in language selector
case 'pl':
return messages_pl
case 'pt-BR':
return messages_ptBr
// case 'ro':
// return messages_ro // TODO missing translation sourcefile
case 'ru':
return messages_ru
case 'sv':
return messages_sv
case 'tr':
return messages_tr
case 'zh-cn':
case 'uk':
return messages_uk
case 'zh-Hans':
case 'zh-CN':
return messages_zhHans
case 'zh-Hant':
case 'zh-TW':
return messages_zhHant
case 'zh-tx':
return messages_zhHans
case 'zh-tw':
return messages_zhHans
}
return messages_en
}
export function getCurrentLanguage(): string {
let lang = UserSettings.language
if (!lang) {
if (supportedLanguages.includes(navigator.language)) {
lang = navigator.language
} else if (supportedLanguages.includes(navigator.language.split(/[-_]/)[0])) {
lang = navigator.language.split(/[-_]/)[0]
} else {
lang = 'en'
}
}
return lang
}
export function storeLanguage(lang: string): void {
UserSettings.language = lang
}

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useEffect} from 'react'
import {createIntl, createIntlCache} from 'react-intl'
import {FormattedMessage} from 'react-intl'
import {Action, Store} from 'redux'
import {Provider as ReduxProvider} from 'react-redux'
import {History, createBrowserHistory} from 'history'
@ -21,7 +21,7 @@ import {Constants} from 'src/constants'
import {setTeam} from 'src/store/teams'
import {UserSettings} from 'src/userSettings'
import {getCurrentLanguage, getMessages} from 'src/i18n'
import {getMessages} from 'src/i18n'
const windowAny = (window as SuiteWindow)
windowAny.baseURL = '/plugins/boards'
@ -232,13 +232,6 @@ export default class Plugin {
windowAny.frontendBaseURL = subpath + windowAny.frontendBaseURL
windowAny.baseURL = subpath + windowAny.baseURL
browserHistory = customHistory()
const cache = createIntlCache()
const intl = createIntl({
// modeled after <IntlProvider> in app.tsx
locale: getCurrentLanguage(),
messages: getMessages(getCurrentLanguage()),
}, cache)
this.registry = registry
@ -248,6 +241,16 @@ export default class Plugin {
const productID = 'boards'
registry.registerTranslations((locale: string) => {
try {
const messages = getMessages(locale)
return messages
} catch {
return {}
}
})
// register websocket handlers
this.registry?.registerWebSocketEventHandler(`custom_${productID}_${ACTION_UPDATE_BOARD}`, (e: any) => wsClient.updateHandler(e.data))
this.registry?.registerWebSocketEventHandler(`custom_${productID}_${ACTION_UPDATE_CATEGORY}`, (e: any) => wsClient.updateHandler(e.data))
@ -389,11 +392,25 @@ export default class Plugin {
}
if (registry.registerChannelIntroButtonAction) {
this.channelHeaderButtonId = registry.registerChannelIntroButtonAction(<FocalboardIcon/>, goToFocalboardTemplate, intl.formatMessage({id: 'ChannelIntro.CreateBoard', defaultMessage: 'Create a board'}))
this.channelHeaderButtonId = registry.registerChannelIntroButtonAction(
<FocalboardIcon/>,
goToFocalboardTemplate,
<FormattedMessage
id='ChannelIntro.CreateBoard'
defaultMessage='Create a board'
/>
)
}
if (this.registry.registerAppBarComponent) {
this.registry.registerAppBarComponent(appBarIcon, () => mmStore.dispatch(toggleRHSPlugin), intl.formatMessage({id: 'AppBar.Tooltip', defaultMessage: 'Toggle linked boards'}))
this.registry.registerAppBarComponent(
appBarIcon,
() => mmStore.dispatch(toggleRHSPlugin),
<FormattedMessage
id='AppBar.Tooltip'
defaultMessage='Toggle linked boards'
/>
)
}
if (this.registry.registerActionAfterChannelCreation) {
@ -447,13 +464,23 @@ export default class Plugin {
if (siteStats) {
return {
boards_count: {
name: intl.formatMessage({id: 'SiteStats.total_boards', defaultMessage: 'Total boards'}),
name: (
<FormattedMessage
id='SiteStats.total_boards'
defaultMessage='Total boards'
/>
),
id: 'total_boards',
icon: 'icon-product-boards',
value: siteStats.board_count,
},
cards_count: {
name: intl.formatMessage({id: 'SiteStats.total_cards', defaultMessage: 'Total cards'}),
name: (
<FormattedMessage
id='SiteStats.total_cards'
defaultMessage='Total cards'
/>
),
id: 'total_cards',
icon: 'icon-products',
value: siteStats.card_count,

View File

@ -3,7 +3,6 @@
import React from 'react'
import {Router, Switch} from 'react-router-dom'
import {IntlProvider} from 'react-intl'
import {DndProvider} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import {TouchBackend} from 'react-dnd-touch-backend'
@ -12,12 +11,9 @@ import {createBrowserHistory} from 'history'
import BoardPage from 'src/pages/boardPage/boardPage'
import FBRoute from 'src/route'
import {getMessages} from 'src/i18n'
import FlashMessages from 'src/components/flashMessages'
import NewVersionBanner from 'src/components/newVersionBanner'
import {Utils} from 'src/utils'
import {getLanguage} from 'src/store/language'
import {useAppSelector} from 'src/store/hooks'
export const publicBaseURL = () => {
return Utils.getFrontendBaseURL() + '/public'
@ -38,23 +34,16 @@ const PublicRouter = () => {
}
const PublicApp = (): JSX.Element => {
const language = useAppSelector<string>(getLanguage)
return (
<IntlProvider
locale={language.split(/[_]/)[0]}
messages={getMessages(language)}
>
<DndProvider backend={Utils.isMobile() ? TouchBackend : HTML5Backend}>
<FlashMessages milliseconds={2000}/>
<div id='frame'>
<div id='main'>
<NewVersionBanner/>
<PublicRouter/>
</div>
<DndProvider backend={Utils.isMobile() ? TouchBackend : HTML5Backend}>
<FlashMessages milliseconds={2000}/>
<div id='frame'>
<div id='main'>
<NewVersionBanner/>
<PublicRouter/>
</div>
</DndProvider>
</IntlProvider>
</div>
</DndProvider>
)
}

View File

@ -6,7 +6,6 @@ import {configureStore} from '@reduxjs/toolkit'
import {reducer as usersReducer} from './users'
import {reducer as teamsReducer} from './teams'
import {reducer as channelsReducer} from './channels'
import {reducer as languageReducer} from './language'
import {reducer as globalTemplatesReducer} from './globalTemplates'
import {reducer as boardsReducer} from './boards'
import {reducer as viewsReducer} from './views'
@ -25,7 +24,6 @@ const store = configureStore({
users: usersReducer,
teams: teamsReducer,
channels: channelsReducer,
language: languageReducer,
globalTemplates: globalTemplatesReducer,
boards: boardsReducer,
views: viewsReducer,

View File

@ -1,46 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {PayloadAction, createAsyncThunk, createSlice} from '@reduxjs/toolkit'
import {getCurrentLanguage, storeLanguage as i18nStoreLanguage} from 'src/i18n'
import {RootState} from './index'
export const fetchLanguage = createAsyncThunk(
'language/fetch',
async () => getCurrentLanguage(),
)
export const storeLanguage = createAsyncThunk(
'language/store',
(lang: string) => {
i18nStoreLanguage(lang)
return lang
},
)
const languageSlice = createSlice({
name: 'language',
initialState: {value: 'en'} as {value: string},
reducers: {
setLanguage: (state, action: PayloadAction<string>) => {
state.value = action.payload
},
},
extraReducers: (builder) => {
builder.addCase(fetchLanguage.fulfilled, (state, action) => {
state.value = action.payload
})
builder.addCase(storeLanguage.fulfilled, (state, action) => {
state.value = action.payload
})
},
})
export const {reducer} = languageSlice
export function getLanguage(state: RootState): string {
return state.language.value
}

View File

@ -8,9 +8,10 @@ import type {Channel, ChannelMembership} from '@mattermost/types/channels'
type ReactResolvable = React.ReactNode | React.ElementType
export interface PluginRegistry {
registerTranslations(getTranslationsForLocale: (locale: string) => Record<string, string>): void
registerPostTypeComponent(typeName: string, component: ReactResolvable): any
registerChannelHeaderButtonAction(icon: ReactResolvable, action: () => void, dropdownText: string, tooltipText: string): any
registerChannelIntroButtonAction(icon: ReactResolvable, action: () => void, tooltipText: string): any
registerChannelIntroButtonAction(icon: ReactResolvable, action: () => void, tooltipText: ReactResolvable): any
registerCustomRoute(route: string, component: ReactResolvable): any
registerProductRoute(route: string, component: ReactResolvable): any
unregisterComponent(componentId: string): any

View File

@ -25,7 +25,6 @@ const PluggableIntroButtons = React.memo((props: Props) => {
key={buttonProps.id}
className={'intro-links color--link channelIntroButton style--none'}
onClick={() => buttonProps.action?.(props.channel, props.channelMember)}
aria-label={buttonProps.text}
>
{buttonProps.icon}
{buttonProps.text}

View File

@ -208,7 +208,7 @@ export default class PluginRegistry {
// Accepts the following:
// - icon - React element to use as the button's icon
// - action - a function called when the button is clicked, passed the channel and channel member as arguments
// - text - a localized string to use as the button's text
// - text - a localized string or React element to use as the button's text
registerChannelIntroButtonAction = reArg([
'icon',
'action',
@ -220,8 +220,7 @@ export default class PluginRegistry {
}: {
icon: ReactResolvable;
action: PluginComponent['action'];
tooltipText: string;
text: string;
text: ReactResolvable;
}) => {
const id = generateId();