Localization: Add many translations (#1103)

This commit is contained in:
Alexander Zinchuk 2021-05-21 14:44:26 +03:00
parent d2a10a5b8b
commit ffa338d667
43 changed files with 248 additions and 227 deletions

View File

@ -18,6 +18,7 @@ import { buildCollectionByKey } from '../../../util/iteratees';
import localDb from '../localDb';
const MAX_INT_32 = 2 ** 31 - 1;
const BETA_LANG_CODES = ['ar', 'fa'];
export function updateProfile({
firstName,
@ -250,7 +251,10 @@ export async function fetchLanguages(): Promise<ApiLanguage[] | undefined> {
export async function fetchLangPack({ sourceLangPacks, langCode }: { sourceLangPacks: string[]; langCode: string }) {
const results = await Promise.all(sourceLangPacks.map((langPack) => {
return invokeRequest(new GramJs.langpack.GetLangPack({ langPack, langCode }));
return invokeRequest(new GramJs.langpack.GetLangPack({
langPack,
langCode: BETA_LANG_CODES.includes(langCode) ? `${langCode}-raw` : langCode,
}));
}));
const collections = results

View File

@ -16,6 +16,7 @@ import {
} from '../../modules/helpers';
import { pick } from '../../util/iteratees';
import useLang from '../../hooks/useLang';
import renderText from './helpers/renderText';
import Avatar from './Avatar';
import Modal from '../ui/Modal';
@ -96,55 +97,52 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
chat={chat}
isSavedMessages={isChatWithSelf}
/>
<h3 className="modal-title">{renderTitle()}</h3>
<h3 className="modal-title">{lang(renderTitle())}</h3>
</div>
);
}
function renderTitle() {
if (isChannel && !chat.isCreator) {
return 'Leave Channel?';
return 'LeaveChannel';
}
if (isChannel && chat.isCreator) {
return 'Delete and Leave Channel?';
return 'ChannelDelete';
}
if (isBasicGroup || isSuperGroup) {
return 'Leave Group?';
return 'Group.LeaveGroup';
}
return 'Delete Chat?';
return 'DeleteChatUser';
}
function renderMessage() {
if (isChannel && !chat.isCreator) {
return <p>Are you sure you want to leave channel <strong>{chatTitle}</strong>?</p>;
}
if (isChannel && chat.isCreator) {
return <p>Are you sure you want to delete and leave channel <strong>{chatTitle}</strong>?</p>;
return <p>{renderText(lang('ChatList.DeleteAndLeaveGroupConfirmation', chatTitle), ['simple_markdown'])}</p>;
}
if (isBasicGroup || isSuperGroup) {
return <p>Are you sure you want to leave group <strong>{chatTitle}</strong>?</p>;
if ((isChannel && !chat.isCreator) || isBasicGroup || isSuperGroup) {
return <p>{renderText(lang('ChannelLeaveAlertWithName', chatTitle), ['simple_markdown'])}</p>;
}
return <p>Are you sure you want to delete chat with <strong>{contactName}</strong>?</p>;
return <p>{renderText(lang('ChatList.DeleteChatConfirmation', contactName), ['simple_markdown'])}</p>;
}
function renderActionText() {
if (isChannel && !chat.isCreator) {
return 'Leave Channel';
return 'LeaveChannel';
}
if (isChannel && chat.isCreator) {
return 'Delete and Leave Channel';
return 'Chat.Input.Delete';
}
if (isBasicGroup || isSuperGroup) {
return 'Leave Group';
return 'Group.LeaveGroup';
}
return `Delete${canDeleteForAll ? ' just for me' : ''}`;
return canDeleteForAll ? 'ChatList.DeleteForCurrentUser' : 'Delete';
}
return (
@ -157,11 +155,11 @@ const DeleteChatModal: FC<OwnProps & StateProps & DispatchProps> = ({
{renderMessage()}
{canDeleteForAll && (
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
Delete for {contactName ? `me and ${contactName}` : 'Everyone'}
{contactName ? lang('ChatList.DeleteForEveryone', contactName) : lang('DeleteForAll')}
</Button>
)}
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteChat}>
{renderActionText()}
{lang(renderActionText())}
</Button>
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
</Modal>

View File

@ -91,19 +91,19 @@ const DeleteMessageModal: FC<OwnProps & StateProps & DispatchProps> = ({
>
<p>{lang('AreYouSureDeleteSingleMessage')}</p>
{willDeleteForCurrentUserOnly && (
<p>This will delete it just for you, not for other participants in the chat.</p>
<p>{lang('lng_delete_for_me_chat_hint')}</p>
)}
{willDeleteForAll && (
<p>This will delete it for everyone in this chat.</p>
<p>{lang('lng_delete_for_everyone_hint')}</p>
)}
{canDeleteForAll && (
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForAll}>
Delete for {contactName ? 'me and ' : 'Everyone'}
{contactName && renderText(contactName)}
{contactName && lang('Conversation.DeleteMessagesFor', renderText(contactName))}
{!contactName && lang('Conversation.DeleteMessagesForEveryone')}
</Button>
)}
<Button color="danger" className="confirm-dialog-button" isText onClick={handleDeleteMessageForSelf}>
Delete{canDeleteForAll ? ' just for me' : ''}
{lang(canDeleteForAll ? 'ChatList.DeleteForCurrentUser' : 'Delete')}
</Button>
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
</Modal>

View File

@ -49,7 +49,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
const pictogramId = message && `sticker-reply-thumb${message.id}`;
const mediaThumbnail = useWebpThumbnail(message);
useLang();
const lang = useLang();
const senderTitle = sender && getSenderTitle(sender);
@ -68,7 +68,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
) : isActionMessage(message) ? (
<ActionMessage message={message} isEmbedded />
) : (
renderText(getMessageSummaryText(message, Boolean(mediaThumbnail)))
renderText(getMessageSummaryText(lang, message, Boolean(mediaThumbnail)))
)}
</p>
</div>

View File

@ -4,7 +4,19 @@
justify-content: center;
color: var(--color-text-meta);
&.with-description {
flex-direction: column;
}
.AnimatedSticker {
margin: 0 auto;
}
.description {
color: var(--color-text-secondary);
font-size: .875rem;
text-align: center;
margin: 1rem 0 0;
unicode-bidi: plaintext;
}
}

View File

@ -2,21 +2,26 @@ import React, { FC, memo } from '../../lib/teact/teact';
import buildClassName from '../../util/buildClassName';
import useShowTransition from '../../hooks/useShowTransition';
import renderText from './helpers/renderText';
import useLang from '../../hooks/useLang';
import './NothingFound.scss';
interface OwnProps {
text?: string;
description?: string;
}
const DEFAULT_TEXT = 'Nothing found.';
const NothingFound: FC<OwnProps> = ({ text = DEFAULT_TEXT }) => {
const NothingFound: FC<OwnProps> = ({ text = DEFAULT_TEXT, description }) => {
const lang = useLang();
const { transitionClassNames } = useShowTransition(true);
return (
<div className={buildClassName('NothingFound', transitionClassNames)}>
<div className={buildClassName('NothingFound', transitionClassNames, description && 'with-description')}>
{text}
{description && <p className="description">{renderText(lang(description), ['br'])}</p>}
</div>
);
};

View File

@ -10,6 +10,7 @@ import { STICKER_SIZE_MODAL } from '../../config';
import { pick } from '../../util/iteratees';
import { selectStickerSet } from '../../modules/selectors';
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
import useLang from '../../hooks/useLang';
import Modal from '../ui/Modal';
import Button from '../ui/Button';
@ -43,6 +44,7 @@ const StickerSetModal: FC<OwnProps & StateProps & DispatchProps> = ({
}) => {
// eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null);
const lang = useLang();
const {
observe: observeIntersection,
@ -98,7 +100,11 @@ const StickerSetModal: FC<OwnProps & StateProps & DispatchProps> = ({
color={stickerSet.installedDate ? 'danger' : 'primary'}
onClick={handleButtonClick}
>
{`${stickerSet.installedDate ? 'Remove' : 'Add'} ${stickerSet.count} stickers`}
{lang(
stickerSet.installedDate ? 'StickerPack.RemoveStickerCount' : 'StickerPack.AddStickerCount',
stickerSet.count,
'i',
)}
</Button>
</div>
</>

View File

@ -7,6 +7,7 @@ import buildClassName from '../../util/buildClassName';
import trimText from '../../util/trimText';
import renderText from './helpers/renderText';
import { formatPastTimeShort } from '../../util/dateFormat';
import useLang from '../../hooks/useLang';
import Media from './Media';
import Link from '../ui/Link';
@ -23,13 +24,15 @@ type OwnProps = {
};
const WebLink: FC<OwnProps> = ({ message, senderTitle, onMessageClick }) => {
const lang = useLang();
let linkData: ApiWebPage | undefined = getMessageWebPage(message);
if (!linkData) {
const link = getFirstLinkInMessage(message);
if (link) {
const { url, domain } = link;
const messageText = getMessageSummaryText(message);
const messageText = getMessageSummaryText(lang, message);
linkData = {
siteName: domain.replace(/^www./, ''),

View File

@ -1,6 +1,7 @@
import React from '../../../lib/teact/teact';
import { ApiChat, ApiMessage, ApiUser } from '../../../api/types';
import { LangFn } from '../../../hooks/useLang';
import {
getChatTitle,
getMessageContent,
@ -25,6 +26,7 @@ interface ActionMessageTextOptions {
const NBSP = '\u00A0';
export function renderActionMessageText(
lang: LangFn,
message: ApiMessage,
actionOrigin?: ApiUser | ApiChat,
targetUser?: ApiUser,
@ -66,7 +68,7 @@ export function renderActionMessageText(
unprocessed,
'%message%',
targetMessage
? renderMessageContent(targetMessage, textOptions)
? renderMessageContent(lang, targetMessage, textOptions)
: 'a message',
);
unprocessed = processed.pop() as string;
@ -104,8 +106,8 @@ function renderProductContent(message: ApiMessage) {
: 'a product';
}
function renderMessageContent(message: ApiMessage, options: ActionMessageTextOptions = {}) {
const text = getMessageSummaryText(message);
function renderMessageContent(lang: LangFn, message: ApiMessage, options: ActionMessageTextOptions = {}) {
const text = getMessageSummaryText(lang, message);
const {
photo, video, document, sticker,
} = getMessageContent(message);

View File

@ -80,7 +80,7 @@ const NewChatButton: FC<OwnProps> = ({
color="primary"
className={isMenuOpen ? 'active' : ''}
onClick={toggleIsMenuOpen}
ariaLabel={isMenuOpen ? 'Close' : 'Create new chat'}
ariaLabel={lang(isMenuOpen ? 'Close' : 'NewMessageTitle')}
tabIndex={-1}
>
<i className="icon-new-chat-filled" />
@ -95,7 +95,7 @@ const NewChatButton: FC<OwnProps> = ({
>
<MenuItem icon="channel" onClick={onNewChannel}>{lang('NewChannel')}</MenuItem>
<MenuItem icon="group" onClick={onNewGroup}>{lang('NewGroup')}</MenuItem>
<MenuItem icon="user" onClick={onNewPrivateChat}>New Private Chat</MenuItem>
<MenuItem icon="user" onClick={onNewPrivateChat}>{lang('NewMessageTitle')}</MenuItem>
</Menu>
</div>
);

View File

@ -3,7 +3,7 @@ import React, {
} from '../../../lib/teact/teact';
import { withGlobal } from '../../../lib/teact/teactn';
import useLang from '../../../hooks/useLang';
import useLang, { LangFn } from '../../../hooks/useLang';
import { GlobalActions, MessageListType } from '../../../global/types';
import {
@ -205,6 +205,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
return (
<p className="last-message">
{renderText(renderActionMessageText(
lang,
lastMessage,
actionOrigin,
actionTargetUser,
@ -223,7 +224,7 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
{senderName && (
<span className="sender-name">{renderText(senderName)}</span>
)}
{renderMessageSummary(lastMessage!, mediaBlobUrl || mediaThumbnail)}
{renderMessageSummary(lang, lastMessage!, mediaBlobUrl || mediaThumbnail)}
</p>
);
}
@ -273,16 +274,16 @@ const Chat: FC<OwnProps & StateProps & DispatchProps> = ({
);
};
function renderMessageSummary(message: ApiMessage, blobUrl?: string) {
function renderMessageSummary(lang: LangFn, message: ApiMessage, blobUrl?: string) {
if (!blobUrl) {
return renderText(getMessageSummaryText(message));
return renderText(getMessageSummaryText(lang, message));
}
return (
<span className="media-preview">
<img src={blobUrl} alt="" />
{getMessageVideo(message) && <i className="icon-play" />}
{renderText(getMessageSummaryText(message, true))}
{renderText(getMessageSummaryText(lang, message, true))}
</span>
);
}

View File

@ -43,6 +43,8 @@ const ChatFolders: FC<StateProps & DispatchProps> = ({
// eslint-disable-next-line no-null/no-null
const transitionRef = useRef<HTMLDivElement>(null);
const lang = useLang();
const [activeTab, setActiveTab] = useState(0);
useEffect(() => {
@ -84,13 +86,13 @@ const ChatFolders: FC<StateProps & DispatchProps> = ({
}
return [
{ title: 'All' },
{ title: lang('FilterAllChats') },
...displayedFolders.map((folder) => ({
title: folder.title,
...(folderCountersById && folderCountersById[folder.id]),
})),
];
}, [displayedFolders, folderCountersById]);
}, [displayedFolders, folderCountersById, lang]);
const handleSwitchTab = useCallback((index: number) => {
setActiveTab(index);
@ -135,8 +137,6 @@ const ChatFolders: FC<StateProps & DispatchProps> = ({
shouldRender: shouldRenderPlaceholder, transitionClassNames,
} = useShowTransition(!orderedFolderIds, undefined, true);
const lang = useLang();
function renderCurrentTab() {
const activeFolder = Object.values(chatFoldersById)
.find(({ title }) => title === folderTabs![activeTab].title);

View File

@ -76,6 +76,7 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
setGlobalSearchDate,
setSettingOption,
}) => {
const lang = useLang();
const hasMenu = content === LeftColumnContent.ChatList;
const clearedDateSearchParam = { date: undefined };
const clearedChatSearchParam = { id: undefined };
@ -107,12 +108,12 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
color="translucent"
className={isOpen ? 'active' : ''}
onClick={hasMenu ? onTrigger : () => onReset()}
ariaLabel={hasMenu ? 'Open menu' : 'Return to chat list'}
ariaLabel={hasMenu ? lang('AccDescrOpenMenu2') : 'Return to chat list'}
>
<div className={buildClassName('animated-menu-icon', !hasMenu && 'state-back')} />
</Button>
);
}, [hasMenu, onReset]);
}, [hasMenu, lang, onReset]);
const handleSearchFocus = useCallback(() => {
if (!searchQuery) {
@ -148,8 +149,6 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
setSettingOption({ animationLevel: newLevel });
}, [animationLevel, setSettingOption]);
const lang = useLang();
const isSearchFocused = (
Boolean(globalSearchChatId)
|| content === LeftColumnContent.GlobalSearch
@ -198,10 +197,10 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
icon="darkmode"
onClick={handleDarkModeToggle}
>
<span className="menu-item-name">Dark Mode</span>
<span className="menu-item-name">{lang('lng_menu_night_mode')}</span>
<Switcher
id="darkmode"
label="Toggle Dark Mode"
label={lang(theme === 'dark' ? 'lng_settings_disable_night_theme' : 'lng_settings_enable_night_theme')}
checked={theme === 'dark'}
noAnimation
/>
@ -221,7 +220,7 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
icon="help"
onClick={openTipsChat}
>
Telegram Features
{lang('TelegramFeatures')}
</MenuItem>
<MenuItem
icon="bug"

View File

@ -14,6 +14,7 @@ import { formatMonthAndYear, toYearMonth } from '../../../util/dateFormat';
import { getSenderName } from './helpers/getSenderName';
import { throttle } from '../../../util/schedulers';
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
import useLang from '../../../hooks/useLang';
import InfiniteScroll from '../../ui/InfiniteScroll';
import Audio from '../../common/Audio';
@ -43,6 +44,7 @@ const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
focusMessage,
openAudioPlayer,
}) => {
const lang = useLang();
const currentType = isVoice ? 'voice' : 'audio';
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
@ -115,7 +117,12 @@ const AudioResults: FC<OwnProps & StateProps & DispatchProps> = ({
noFastList
>
{!canRenderContents && <Loading />}
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
{canRenderContents && (!foundIds || foundIds.length === 0) && (
<NothingFound
text={lang('ChatList.Search.NoResults')}
description={lang('ChatList.Search.NoResultsDescription')}
/>
)}
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
</InfiniteScroll>
</div>

View File

@ -22,7 +22,7 @@ import renderText from '../../common/helpers/renderText';
import { pick } from '../../../util/iteratees';
import useMedia from '../../../hooks/useMedia';
import { formatPastTimeShort } from '../../../util/dateFormat';
import useLang from '../../../hooks/useLang';
import useLang, { LangFn } from '../../../hooks/useLang';
import Avatar from '../../common/Avatar';
import VerifiedIcon from '../../common/VerifiedIcon';
@ -62,7 +62,7 @@ const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
focusMessage({ chatId, messageId: message.id });
}, [chatId, focusMessage, message.id]);
useLang();
const lang = useLang();
if (!chat) {
return undefined;
@ -96,7 +96,7 @@ const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
</div>
<div className="subtitle">
<div className="message">
{renderMessageSummary(message, mediaBlobUrl || mediaThumbnail, searchQuery)}
{renderMessageSummary(lang, message, mediaBlobUrl || mediaThumbnail, searchQuery)}
</div>
</div>
</div>
@ -104,16 +104,16 @@ const ChatMessage: FC<OwnProps & StateProps & DispatchProps> = ({
);
};
function renderMessageSummary(message: ApiMessage, blobUrl?: string, searchQuery?: string) {
function renderMessageSummary(lang: LangFn, message: ApiMessage, blobUrl?: string, searchQuery?: string) {
if (!blobUrl) {
return renderText(getMessageSummaryText(message));
return renderText(getMessageSummaryText(lang, message));
}
return (
<span className="media-preview">
<img src={blobUrl} alt="" />
{getMessageVideo(message) && <i className="icon-play" />}
{renderText(getMessageSummaryText(message, true), ['emoji', 'highlight'], { highlight: searchQuery })}
{renderText(getMessageSummaryText(lang, message, true), ['emoji', 'highlight'], { highlight: searchQuery })}
</span>
);
}

View File

@ -11,6 +11,7 @@ import { pick } from '../../../util/iteratees';
import { getMessageSummaryText } from '../../../modules/helpers';
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
import { throttle } from '../../../util/schedulers';
import useLang from '../../../hooks/useLang';
import InfiniteScroll from '../../ui/InfiniteScroll';
import ChatMessage from './ChatMessage';
@ -49,6 +50,7 @@ const ChatMessageResults: FC<OwnProps & StateProps & DispatchProps> = ({
searchMessagesGlobal,
onSearchDateSelect,
}) => {
const lang = useLang();
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
runThrottled(() => {
@ -79,7 +81,7 @@ const ChatMessageResults: FC<OwnProps & StateProps & DispatchProps> = ({
}, [foundIds, globalMessagesByChatId]);
function renderFoundMessage(message: ApiMessage) {
const text = getMessageSummaryText(message);
const text = getMessageSummaryText(lang, message);
const chat = chatsById[message.chatId];
if (!text || !chat) {
@ -113,7 +115,12 @@ const ChatMessageResults: FC<OwnProps & StateProps & DispatchProps> = ({
/>
</div>
)}
{nothingFound && <NothingFound />}
{nothingFound && (
<NothingFound
text={lang('ChatList.Search.NoResults')}
description={lang('ChatList.Search.NoResultsDescription')}
/>
)}
{!!foundMessages.length && foundMessages.map(renderFoundMessage)}
</InfiniteScroll>
</div>

View File

@ -159,7 +159,7 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
const lang = useLang();
function renderFoundMessage(message: ApiMessage) {
const text = getMessageSummaryText(message);
const text = getMessageSummaryText(lang, message);
const chat = chatsById[message.chatId];
if (!text || !chat) {
@ -199,7 +199,12 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
/>
</div>
)}
{nothingFound && <NothingFound />}
{nothingFound && (
<NothingFound
text={lang('ChatList.Search.NoResults')}
description={lang('ChatList.Search.NoResultsDescription')}
/>
)}
{!!localResults.length && (
<div className="chat-selection no-selection no-scrollbar">
{localResults.map((id) => (
@ -215,9 +220,11 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
<div className="search-section">
<h3 className="section-heading">
{localResults.length > LESS_LIST_ITEMS_AMOUNT && (
<Link onClick={handleClickShowMoreLocal}>{shouldShowMoreLocal ? 'Show less' : 'Show more'}</Link>
<Link onClick={handleClickShowMoreLocal}>
{lang(shouldShowMoreLocal ? 'ChatList.Search.ShowLess' : 'ChatList.Search.ShowMore')}
</Link>
)}
Contacts and Chats
{lang('DialogList.SearchSectionDialogs')}
</h3>
{localResults.map((id, index) => {
if (!shouldShowMoreLocal && index >= LESS_LIST_ITEMS_AMOUNT) {
@ -237,9 +244,11 @@ const ChatResults: FC<OwnProps & StateProps & DispatchProps> = ({
<div className="search-section">
<h3 className="section-heading">
{globalResults.length > LESS_LIST_ITEMS_AMOUNT && (
<Link onClick={handleClickShowMoreGlobal}>{shouldShowMoreGlobal ? 'Show less' : 'Show more'}</Link>
<Link onClick={handleClickShowMoreGlobal}>
{lang(shouldShowMoreGlobal ? 'ChatList.Search.ShowLess' : 'ChatList.Search.ShowMore')}
</Link>
)}
Global Search
{lang('DialogList.SearchSectionGlobal')}
</h3>
{globalResults.map((id, index) => {
if (!shouldShowMoreGlobal && index >= LESS_LIST_ITEMS_AMOUNT) {

View File

@ -16,6 +16,7 @@ import { getSenderName } from './helpers/getSenderName';
import { throttle } from '../../../util/schedulers';
import { getMessageDocument } from '../../../modules/helpers';
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
import useLang from '../../../hooks/useLang';
import Document from '../../common/Document';
import InfiniteScroll from '../../ui/InfiniteScroll';
@ -43,6 +44,7 @@ const FileResults: FC<OwnProps & StateProps & DispatchProps> = ({
searchMessagesGlobal,
focusMessage,
}) => {
const lang = useLang();
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
runThrottled(() => {
@ -109,7 +111,12 @@ const FileResults: FC<OwnProps & StateProps & DispatchProps> = ({
noFastList
>
{!canRenderContents && <Loading />}
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
{canRenderContents && (!foundIds || foundIds.length === 0) && (
<NothingFound
text={lang('ChatList.Search.NoResults')}
description={lang('ChatList.Search.NoResultsDescription')}
/>
)}
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
</InfiniteScroll>
</div>

View File

@ -14,6 +14,7 @@ import { formatMonthAndYear, toYearMonth } from '../../../util/dateFormat';
import { getSenderName } from './helpers/getSenderName';
import { throttle } from '../../../util/schedulers';
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
import useLang from '../../../hooks/useLang';
import InfiniteScroll from '../../ui/InfiniteScroll';
import WebLink from '../../common/WebLink';
@ -41,6 +42,7 @@ const LinkResults: FC<OwnProps & StateProps & DispatchProps> = ({
searchMessagesGlobal,
focusMessage,
}) => {
const lang = useLang();
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
runThrottled(() => {
@ -103,7 +105,12 @@ const LinkResults: FC<OwnProps & StateProps & DispatchProps> = ({
noFastList
>
{!canRenderContents && <Loading />}
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
{canRenderContents && (!foundIds || foundIds.length === 0) && (
<NothingFound
text={lang('ChatList.Search.NoResults')}
description={lang('ChatList.Search.NoResultsDescription')}
/>
)}
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
</InfiniteScroll>
</div>

View File

@ -7,17 +7,18 @@ import { GlobalActions } from '../../../global/types';
import { LoadMoreDirection, MediaViewerOrigin } from '../../../types';
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
import { SLIDE_TRANSITION_DURATION } from '../../../config';
import { createMapStateToProps, StateProps } from './helpers/createMapStateToProps';
import { pick } from '../../../util/iteratees';
import buildClassName from '../../../util/buildClassName';
import { throttle } from '../../../util/schedulers';
import useLang from '../../../hooks/useLang';
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
import InfiniteScroll from '../../ui/InfiniteScroll';
import Media from '../../common/Media';
import ChatMessage from './ChatMessage';
import NothingFound from '../../common/NothingFound';
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
import { SLIDE_TRANSITION_DURATION } from '../../../config';
import Loading from '../../ui/Loading';
export type OwnProps = {
@ -39,6 +40,7 @@ const MediaResults: FC<OwnProps & StateProps & DispatchProps> = ({
searchMessagesGlobal,
openMediaViewer,
}) => {
const lang = useLang();
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
if (lastSyncTime && direction === LoadMoreDirection.Backwards) {
runThrottled(() => {
@ -115,7 +117,12 @@ const MediaResults: FC<OwnProps & StateProps & DispatchProps> = ({
noFastList
>
{!canRenderContents && <Loading />}
{canRenderContents && (!foundIds || foundIds.length === 0) && <NothingFound />}
{canRenderContents && (!foundIds || foundIds.length === 0) && (
<NothingFound
text={lang('ChatList.Search.NoResults')}
description={lang('ChatList.Search.NoResultsDescription')}
/>
)}
{isMediaGrid && renderGallery()}
{isMessageList && renderSearchResult()}
</InfiniteScroll>

View File

@ -199,7 +199,7 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
/>
<p className="settings-item-description">
{renderText(lang('BioAbout'), ['br', 'simple_markdown'])}
{renderText(lang('lng_settings_about_bio'), ['br', 'simple_markdown'])}
</p>
</div>
@ -219,7 +219,7 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
</p>
{username && (
<p className="settings-item-description">
This link opens a chat with you:<br />
{lang('lng_username_link')}<br />
<span className="username-link">https://t.me/{username}</span>
</p>
)}

View File

@ -31,11 +31,6 @@ type StateProps = ISettings['byKey'] & {
type DispatchProps = Pick<GlobalActions, 'setSettingOption' | 'loadStickerSets' | 'loadAddedStickers'>;
const KEYBOARD_SEND_OPTIONS = !IS_TOUCH_ENV ? [
{ value: 'enter', label: 'Send by Enter', subLabel: 'New line by Shift + Enter' },
{ value: 'ctrl-enter', label: `Send by ${IS_MAC_OS ? 'Cmd' : 'Ctrl'} + Enter`, subLabel: 'New line by Enter' },
] : undefined;
const ANIMATION_LEVEL_OPTIONS = [
'Solid and Steady',
'Nice and Fast',
@ -67,6 +62,17 @@ const SettingsGeneral: FC<OwnProps & StateProps & DispatchProps> = ({
const [isModalOpen, openModal, closeModal] = useFlag();
const [sticker, setSticker] = useState<ApiSticker>();
const lang = useLang();
const KEYBOARD_SEND_OPTIONS = !IS_TOUCH_ENV ? [
{ value: 'enter', label: lang('lng_settings_send_enter'), subLabel: 'New line by Shift + Enter' },
{
value: 'ctrl-enter',
label: lang(IS_MAC_OS ? 'lng_settings_send_cmdenter' : 'lng_settings_send_ctrlenter'),
subLabel: 'New line by Enter',
},
] : undefined;
useEffect(() => {
loadStickerSets();
}, [loadStickerSets]);
@ -96,9 +102,6 @@ const SettingsGeneral: FC<OwnProps & StateProps & DispatchProps> = ({
openModal();
}, [openModal]);
const lang = useLang();
const stickerSets = stickerSetIds && stickerSetIds.map((id: string) => {
return stickerSetsById && stickerSetsById[id] && stickerSetsById[id].installedDate ? stickerSetsById[id] : false;
}).filter(Boolean);

View File

@ -83,7 +83,7 @@ const SettingsHeader: FC<OwnProps & DispatchProps> = ({
function renderHeaderContent() {
switch (currentScreen) {
case SettingsScreens.EditProfile:
return <h3>{lang('EditProfile')}</h3>;
return <h3>{lang('lng_settings_information')}</h3>;
case SettingsScreens.General:
return <h3>{lang('General')}</h3>;
case SettingsScreens.Notifications:

View File

@ -40,7 +40,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
icon="edit"
onClick={() => onScreenSelect(SettingsScreens.EditProfile)}
>
{lang('EditProfile')}
{lang('lng_settings_information')}
</ListItem>
<ListItem
icon="folder"

View File

@ -41,7 +41,7 @@ const SenderInfo: FC<OwnProps & StateProps & DispatchProps> = ({
focusMessage({ chatId, messageId });
}, [chatId, focusMessage, messageId, closeMediaViewer]);
useLang();
const lang = useLang();
if (!sender || (!message && !isAvatar)) {
return undefined;
@ -62,7 +62,7 @@ const SenderInfo: FC<OwnProps & StateProps & DispatchProps> = ({
{senderTitle && renderText(senderTitle)}
</div>
<div className="date">
{isAvatar ? 'Profile photo' : formatMediaDateTime(message!.date * 1000)}
{isAvatar ? lang('lng_mediaview_profile_photo') : formatMediaDateTime(message!.date * 1000)}
</div>
</div>
</div>

View File

@ -65,7 +65,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
useEnsureMessage(message.chatId, message.replyToMessageId, targetMessage);
useFocusMessage(ref, message.chatId, isFocused, focusDirection, noFocusHighlight);
useLang();
const lang = useLang();
const noAppearanceAnimation = appearanceOrder <= 0;
const [isShown, markShown] = useFlag(noAppearanceAnimation);
@ -79,6 +79,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
const { transitionClassNames } = useShowTransition(isShown, undefined, noAppearanceAnimation, false);
const content = renderActionMessageText(
lang,
message,
sender,
targetUser,

View File

@ -165,7 +165,9 @@ const HeaderMenuContainer: FC<OwnProps & StateProps & DispatchProps> = ({
icon="delete"
onClick={handleDelete}
>
{lang(isPrivate ? 'Delete' : (canDeleteChat ? 'Delete and Leave' : 'Leave'))}
{lang(isPrivate
? 'Delete'
: (canDeleteChat ? 'GroupInfo.DeleteAndExit' : (isChannel ? 'LeaveChannel' : 'Group.LeaveGroup')))}
</MenuItem>
)}
</Menu>

View File

@ -31,10 +31,11 @@ type OwnProps = {
const HeaderPinnedMessage: FC<OwnProps> = ({
message, count, index, customTitle, className, onUnpinMessage, onClick, onAllPinnedClick,
}) => {
const lang = useLang();
const mediaThumbnail = useWebpThumbnail(message);
const mediaBlobUrl = useMedia(getMessageMediaHash(message, 'pictogram'));
const text = getMessageSummaryText(message, Boolean(mediaThumbnail));
const text = getMessageSummaryText(lang, message, Boolean(mediaThumbnail));
const [isUnpinDialogOpen, openUnpinDialog, closeUnpinDialog] = useFlag();
const handleUnpinMessage = useCallback(() => {
@ -45,8 +46,6 @@ const HeaderPinnedMessage: FC<OwnProps> = ({
}
}, [closeUnpinDialog, onUnpinMessage, message.id]);
const lang = useLang();
return (
<div className={buildClassName('HeaderPinnedMessage-wrapper', className)}>
{count > 1 && (

View File

@ -8,9 +8,10 @@ import { GlobalActions } from '../../../global/types';
import { pick } from '../../../util/iteratees';
import { isChatPrivate } from '../../../modules/helpers';
import { formatInteger, formatIntegerCompact } from '../../../util/textFormat';
import { formatIntegerCompact } from '../../../util/textFormat';
import buildClassName from '../../../util/buildClassName';
import { selectThreadInfo } from '../../../modules/selectors';
import useLang from '../../../hooks/useLang';
import Avatar from '../../common/Avatar';
@ -32,6 +33,7 @@ type DispatchProps = Pick<GlobalActions, 'openChat'>;
const CommentButton: FC<OwnProps & StateProps & DispatchProps> = ({
disabled, threadInfo, usersById, chatsById, openChat,
}) => {
const lang = useLang();
const {
threadId, chatId, messagesCount, lastMessageId, lastReadInboxMessageId, recentReplierIds,
} = threadInfo;
@ -76,24 +78,14 @@ const CommentButton: FC<OwnProps & StateProps & DispatchProps> = ({
<i className="icon-comments-sticker" />
{(!recentRepliers || recentRepliers.length === 0) && <i className="icon-comments" />}
{renderRecentRepliers()}
<div className="label">{renderLabel(messagesCount)}</div>
<div className="label">
{messagesCount ? lang('Comments', messagesCount, 'i') : lang('LeaveAComment')}
</div>
<i className="icon-next" />
</div>
);
};
function renderLabel(messagesCount: number) {
if (messagesCount === 0) {
return 'Leave a Comment';
}
if (messagesCount === 1) {
return '1 Comment';
}
return `${formatInteger(messagesCount)} Comments`;
}
export default memo(withGlobal<OwnProps>(
(global, { message }) => {
const { threadId, chatId } = message.threadInfo!;

View File

@ -756,7 +756,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
color="translucent-white"
round
size="tiny"
ariaLabel="Forward message"
ariaLabel={lang('lng_context_forward_msg')}
onClick={isLastInDocumentGroup ? handleGroupForward : handleForward}
>
<i className="icon-share-filled" />

View File

@ -21,7 +21,7 @@ export function getMessageCopyOptions(
if (canImageBeCopied) {
options.push({
label: 'Copy Media',
label: 'lng_context_copy_image',
handler: () => {
mediaLoader.fetch(mediaHash, ApiMediaFormat.BlobUrl).then(copyImageToClipboard);
@ -57,7 +57,7 @@ export function getMessageCopyOptions(
if (onCopyLink) {
options.push({
label: 'CopyMessageLink',
label: 'lng_context_copy_message_link',
handler: () => {
onCopyLink();
@ -73,11 +73,11 @@ export function getMessageCopyOptions(
function getCopyLabel(hasSelection: boolean, canImageBeCopied: boolean): string {
if (hasSelection) {
return 'Copy Selected Text';
return 'lng_context_copy_selected';
}
if (canImageBeCopied) {
return 'Copy Text';
return 'lng_context_copy_text';
}
return 'Copy';

View File

@ -37,6 +37,7 @@ import useProfileViewportIds from './hooks/useProfileViewportIds';
import useProfileState from './hooks/useProfileState';
import useTransitionFixes from './hooks/useTransitionFixes';
import useAsyncRendering from './hooks/useAsyncRendering';
import useLang from '../../hooks/useLang';
import Transition from '../ui/Transition';
import InfiniteScroll from '../ui/InfiniteScroll';
@ -120,6 +121,8 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
// eslint-disable-next-line no-null/no-null
const transitionRef = useRef<HTMLDivElement>(null);
const lang = useLang();
const [activeTab, setActiveTab] = useState(0);
const tabs = useMemo(() => ([
@ -228,16 +231,16 @@ const Profile: FC<OwnProps & StateProps & DispatchProps> = ({
text = areMembersHidden ? 'You have no access to group members list.' : 'No members found';
break;
case 'documents':
text = 'No documents found.';
text = lang('lng_media_file_empty_search');
break;
case 'links':
text = 'No links found.';
text = lang('lng_media_link_empty_search');
break;
case 'audio':
text = 'No audio found.';
text = lang('lng_media_song_empty_search');
break;
default:
text = 'No media found.';
text = lang('SharedMedia.EmptyTitle');
}
return (

View File

@ -235,7 +235,7 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
return (
<SearchInput
value={stickerSearchQuery}
placeholder="Search Stickers"
placeholder={lang('SearchStickersHint')}
onChange={handleStickerSearchQueryChange}
/>
);

View File

@ -17,6 +17,7 @@ import {
isChatChannel,
} from '../../modules/helpers';
import renderText from '../common/helpers/renderText';
import useLang from '../../hooks/useLang';
import { orderBy, pick } from '../../util/iteratees';
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
@ -60,6 +61,8 @@ const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
searchTextMessagesLocal,
focusMessage,
}) => {
const lang = useLang();
const foundResults = useMemo(() => {
if (!query || !foundIds || !foundIds.length || !messagesById) {
return MEMO_EMPTY_ARRAY;
@ -98,7 +101,7 @@ const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
message, senderUser, senderChat, onClick,
}: Result) => {
const title = senderChat ? getChatTitle(senderChat) : getUserFullName(senderUser);
const text = getMessageSummaryText(message);
const text = getMessageSummaryText(lang, message);
return (
<ListItem className="chat-item-clickable search-result-message m-0" onClick={onClick}>

View File

@ -12,6 +12,7 @@ import { pick } from '../../util/iteratees';
import { selectShouldLoopStickers, selectStickerSet } from '../../modules/selectors';
import useFlag from '../../hooks/useFlag';
import useOnChange from '../../hooks/useOnChange';
import useLang from '../../hooks/useLang';
import Button from '../ui/Button';
import StickerButton from '../common/StickerButton';
@ -37,6 +38,7 @@ const STICKERS_TO_DISPLAY = 5;
const StickerSetResult: FC<OwnProps & StateProps & DispatchProps> = ({
stickerSetId, observeIntersection, set, shouldPlay, loadStickers, toggleStickerSet, isSomeModalOpen, onModalToggle,
}) => {
const lang = useLang();
const isAdded = set && Boolean(set.installedDate);
const areStickersLoaded = Boolean(set && set.stickers);
@ -79,7 +81,7 @@ const StickerSetResult: FC<OwnProps & StateProps & DispatchProps> = ({
<div className="sticker-set-header">
<div className="title-wrapper">
<h3 className="title">{set.title}</h3>
<p className="count">{set.count} stickers</p>
<p className="count">{lang('Stickers', set.count, 'i')}</p>
</div>
<Button
className={isAdded ? 'is-added' : undefined}
@ -89,7 +91,7 @@ const StickerSetResult: FC<OwnProps & StateProps & DispatchProps> = ({
fluid
onClick={handleAddClick}
>
{isAdded ? 'Added' : 'Add'}
{lang(isAdded ? 'Stickers.Installed' : 'Stickers.Install')}
</Button>
</div>
<div className="sticker-set-main">

View File

@ -111,7 +111,7 @@ const ManageGroupRecentActions: FC<OwnProps & StateProps> = ({ chat }) => {
</div>
<div className="section not-implemented">
<h3 className="section-heading">Admins</h3>
<h3 className="section-heading">{lang('Channel.Management.Title')}</h3>
<div className="ListItem no-selection">
<Checkbox

View File

@ -124,7 +124,7 @@ export const MAX_MEDIA_FILES_FOR_ALBUM = 10;
export const MAX_ACTIVE_PINNED_CHATS = 5;
export const SCHEDULED_WHEN_ONLINE = 0x7FFFFFFE;
export const DEFAULT_LANG_PACK = 'android';
export const LANG_PACKS = ['android', 'ios'];
export const LANG_PACKS = ['android', 'ios', 'tdesktop'];
export const TIPS_USERNAME = 'TelegramTips';
export const FEEDBACK_URL = 'https://bugs.telegram.org/?tag_ids=41&sort=time';
export const DARK_THEME_BG_COLOR = '#0F0F0F';

View File

@ -1,7 +1,10 @@
import { useMemo } from '../lib/teact/teact';
import { getDispatch } from '../lib/teact/teactn';
import { ApiChat, ApiUser } from '../api/types';
import { isChatArchived, getCanDeleteChat, isChatPrivate } from '../modules/helpers';
import {
isChatArchived, getCanDeleteChat, isChatPrivate, isChatChannel,
} from '../modules/helpers';
import useLang from './useLang';
export default ({
chat,
@ -16,6 +19,8 @@ export default ({
folderId?: number;
isPinned?: boolean;
}) => {
const lang = useLang();
const {
toggleChatPinned,
updateChatMutedState,
@ -31,23 +36,39 @@ export default ({
const isChatWithSelf = privateChatUser && privateChatUser.isSelf;
const actionUnreadMark = chat.unreadCount || chat.hasUnreadMark
? { title: 'Mark as Read', icon: 'readchats', handler: () => toggleChatUnread({ id: chat.id }) }
: { title: 'Mark as Unread', icon: 'unread', handler: () => toggleChatUnread({ id: chat.id }) };
? { title: lang('MarkAsRead'), icon: 'readchats', handler: () => toggleChatUnread({ id: chat.id }) }
: { title: lang('MarkAsUnread'), icon: 'unread', handler: () => toggleChatUnread({ id: chat.id }) };
const actionPin = isPinned
? { title: 'Unpin', icon: 'unpin', handler: () => toggleChatPinned({ id: chat.id, folderId }) }
: { title: 'Pin', icon: 'pin', handler: () => toggleChatPinned({ id: chat.id, folderId }) };
? {
title: lang('UnpinFromTop'),
icon: 'unpin',
handler: () => toggleChatPinned({ id: chat.id, folderId }),
}
: { title: lang('PinToTop'), icon: 'pin', handler: () => toggleChatPinned({ id: chat.id, folderId }) };
const actionMute = chat.isMuted
? { title: 'Unmute', icon: 'unmute', handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }) }
: { title: 'Mute', icon: 'mute', handler: () => updateChatMutedState({ chatId: chat.id, isMuted: true }) };
? {
title: lang('ChatList.Unmute'),
icon: 'unmute',
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }),
}
: {
title: lang('ChatList.Mute'),
icon: 'mute',
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: true }),
};
const actionArchive = isChatArchived(chat)
? { title: 'Unarchive', icon: 'unarchive', handler: () => toggleChatArchived({ id: chat.id }) }
: { title: 'Archive', icon: 'archive', handler: () => toggleChatArchived({ id: chat.id }) };
? { title: lang('Unarchive'), icon: 'unarchive', handler: () => toggleChatArchived({ id: chat.id }) }
: { title: lang('Archive'), icon: 'archive', handler: () => toggleChatArchived({ id: chat.id }) };
const actionDelete = {
title: isChatPrivate(chat.id) ? 'Delete' : (getCanDeleteChat(chat) ? 'Delete and Leave' : 'Leave'),
title: isChatPrivate(chat.id)
? lang('Delete')
: lang(getCanDeleteChat(chat)
? 'DeleteChat'
: (isChatChannel(chat) ? 'LeaveChannel' : 'Group.LeaveGroup')),
icon: 'delete',
destructive: true,
handler: handleDelete,
@ -63,7 +84,7 @@ export default ({
actionDelete,
];
}, [
chat, privateChatUser, handleDelete, folderId, isPinned,
toggleChatPinned, updateChatMutedState, toggleChatArchived, toggleChatUnread,
chat, privateChatUser, lang, isPinned, handleDelete, toggleChatUnread, toggleChatPinned, folderId,
updateChatMutedState, toggleChatArchived,
]);
};

View File

@ -428,7 +428,7 @@ export function getMessageSenderName(chatId: number, sender?: ApiUser) {
}
if (sender.isSelf) {
return 'You';
return getTranslation('FromYou');
}
return getUserFirstOrLastName(sender);

View File

@ -1,6 +1,7 @@
import {
ApiChat, ApiMessage, ApiMessageEntityTypes, ApiUser,
} from '../../api/types';
import { LangFn } from '../../hooks/useLang';
import { LOCAL_MESSAGE_ID_BASE, SERVICE_NOTIFICATIONS_USER_ID, RE_LINK_TEMPLATE } from '../../config';
import parseEmojiOnlyString from '../../components/common/helpers/parseEmojiOnlyString';
@ -26,62 +27,34 @@ export function getMessageOriginalId(message: ApiMessage) {
return message.previousLocalId || message.id;
}
export function getMessageSummaryText(message: ApiMessage, noEmoji = false) {
export function getMessageSummaryText(lang: LangFn, message: ApiMessage, noEmoji = false) {
const {
text, photo, video, audio, voice, document, sticker, contact, poll, invoice,
} = message.content;
if (message.groupedId) {
if (text) {
return `${noEmoji ? '' : '🖼 '}${text.text}`;
}
return 'Album';
return `${noEmoji ? '' : '🖼 '}${text ? text.text : lang('lng_in_dlg_album')}`;
}
if (photo) {
if (text) {
return `${noEmoji ? '' : '🖼 '}${text.text}`;
}
return 'Photo';
return `${noEmoji ? '' : '🖼 '}${text ? text.text : lang('AttachPhoto')}`;
}
if (video) {
if (video.isGif) {
if (text) {
return `${noEmoji ? '' : 'GIF '}${text.text}`;
}
return 'GIF';
} else {
if (text) {
return `${noEmoji ? '' : '📹 '}${text.text}`;
}
return 'Video';
}
return `${noEmoji ? '' : '📹 '}${text ? text.text : lang(video.isGif ? 'AttachGif' : 'AttachVideo')}`;
}
if (sticker) {
return `${sticker.emoji} Sticker`;
return `${sticker.emoji} ${lang('AttachSticker')} `;
}
if (audio) {
const caption = [audio.title, audio.performer].filter(Boolean).join(' — ') || (text && text.text);
if (caption) {
return `🎧 ${caption}`;
}
return 'Audio';
return `${noEmoji ? '' : '🎧 '}${caption || lang('AttachMusic')}`;
}
if (voice) {
if (text) {
return `${noEmoji ? '' : '🎤 '}${text.text}`;
}
return 'Voice Message';
return `${noEmoji ? '' : '🎤 '}${text ? text.text : lang('AttachAudio')}`;
}
if (document) {
@ -89,11 +62,11 @@ export function getMessageSummaryText(message: ApiMessage, noEmoji = false) {
}
if (contact) {
return 'Contact';
return lang('AttachContact');
}
if (poll) {
return `📊 ${poll.summary.question}`;
return `${noEmoji ? '' : '📊 '}${poll.summary.question}`;
}
if (invoice) {
@ -107,60 +80,6 @@ export function getMessageSummaryText(message: ApiMessage, noEmoji = false) {
return CONTENT_NOT_SUPPORTED;
}
export function getNotificationText(message: ApiMessage) {
const {
text, photo, video, audio, voice, document, sticker, contact, poll, invoice,
} = message.content;
if (message.groupedId) {
return `🖼 ${text ? text.text : 'Album'}`;
}
if (photo) {
return `🖼 ${text ? text.text : 'Photo'}`;
}
if (video) {
return `📹 ${text ? text.text : video.isGif ? 'GIF' : 'Video'}`;
}
if (sticker) {
return `${sticker.emoji} Sticker `;
}
if (audio) {
const caption = [audio.title, audio.performer].filter(Boolean).join(' — ') || (text && text.text);
return `🎧 ${caption || 'Audio'}`;
}
if (voice) {
return `🎤 ${text ? text.text : 'Voice Message'}`;
}
if (document) {
return `📎 ${text ? text.text : document.fileName}`;
}
if (contact) {
return 'Contact';
}
if (poll) {
return `📊 ${poll.summary.question}`;
}
if (invoice) {
return 'Invoice';
}
if (text) {
return text.text;
}
return CONTENT_NOT_SUPPORTED;
}
export function getMessageText(message: ApiMessage) {
const {
text, sticker, photo, video, audio, voice, document, poll, webPage, contact, invoice,

View File

@ -70,7 +70,7 @@ export function getUserStatus(user: ApiUser, lang: LangFn) {
}
if (user.type && user.type === 'userTypeBot') {
return 'bot';
return lang('Bot');
}
if (!user.status) {

View File

@ -7,11 +7,12 @@ import {
getChatTitle,
getMessageAction,
getMessageSenderName,
getNotificationText,
getMessageSummaryText,
getPrivateChatUserId,
isActionMessage,
isChatChannel,
} from '../modules/helpers';
import { getTranslation } from './langProvider';
import { replaceSettings } from '../modules/reducers';
import { selectChatMessage, selectUser } from '../modules/selectors';
import { IS_SERVICE_WORKER_SUPPORTED } from './environment';
@ -225,6 +226,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage) {
? chat
: messageSender;
body = renderActionMessageText(
getTranslation,
message,
actionOrigin,
actionTargetUser,
@ -234,7 +236,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage) {
) as string;
} else {
const senderName = getMessageSenderName(chat.id, messageSender);
const summary = getNotificationText(message);
const summary = getMessageSummaryText(getTranslation, message);
body = senderName ? `${senderName}: ${summary}` : summary;
}

View File

@ -137,7 +137,7 @@ export function expectCommentButton(
expect(button.querySelector('.label')).toHaveTextContent(`${commentsCount} Comments`);
expect(button.querySelectorAll('.Avatar')).toHaveLength(Math.min(authorsCount, 3));
} else {
expect(button.querySelector('.label')).toHaveTextContent('Leave a Comment');
expect(button.querySelector('.label')).toHaveTextContent('Leave a comment');
expect(button.querySelectorAll('.Avatar')).toHaveLength(0);
}