Shared Folder: Fix localization (#5395)

This commit is contained in:
zubiden 2025-01-05 20:18:41 +01:00 committed by Alexander Zinchuk
parent 963f3cd9a0
commit 9e6aa47013
9 changed files with 147 additions and 28 deletions

View File

@ -607,7 +607,8 @@ export function buildApiChatlistInvite(
if (invite instanceof GramJs.chatlists.ChatlistInvite) { if (invite instanceof GramJs.chatlists.ChatlistInvite) {
return { return {
slug, slug,
title: invite.title.text, title: buildApiFormattedText(invite.title),
noTitleAnimations: invite.titleNoanimate,
emoticon: invite.emoticon, emoticon: invite.emoticon,
peerIds: invite.peers.map(getApiChatIdFromMtpPeer).filter(Boolean), peerIds: invite.peers.map(getApiChatIdFromMtpPeer).filter(Boolean),
}; };

View File

@ -261,7 +261,8 @@ export interface ApiTopic {
} }
export interface ApiChatlistInviteNew { export interface ApiChatlistInviteNew {
title: string; title: ApiFormattedText;
noTitleAnimations?: true;
emoticon?: string; emoticon?: string;
peerIds: string[]; peerIds: string[];
slug: string; slug: string;

View File

@ -1416,6 +1416,21 @@
"FolderLinkTitleDescription" = "Anyone with this link can add {folder} folder and {chats} selected below."; "FolderLinkTitleDescription" = "Anyone with this link can add {folder} folder and {chats} selected below.";
"FolderLinkTitleDescriptionChats_one" = "the chat"; "FolderLinkTitleDescriptionChats_one" = "the chat";
"FolderLinkTitleDescriptionChats_other" = "the {count} chats"; "FolderLinkTitleDescriptionChats_other" = "the {count} chats";
"FolderLinkSubtitleNew" = "Do you want to add a new chat folder and join its groups and channels?"
"FolderLinkSubtitleAlready" = "You have already added this folder and its chats."
"FolderLinkSubtitleAdd" = "Do you want to add {chats} to the folder **{title}**?"
"FolderLinkSubtitleAddCount_one" = "1 chat";
"FolderLinkSubtitleAddCount_other" = "{count} chats";
"FolderLinkAddFolder" = "Add Folder";
"FolderLinkNotificationDeletedTitle" = "Folder {title} Deleted";
"FolderLinkNotificationDeletedSubtitle_one" = "You also left {count} chat";
"FolderLinkNotificationDeletedSubtitle_other" = "You also left {count} chats";
"FolderLinkNotificationAddedTitle" = "Folder {title} Added";
"FolderLinkNotificationAddedSubtitle_one" = "You also joined {count} chat";
"FolderLinkNotificationAddedSubtitle_other" = "You also joined {count} chats";
"FolderLinkNotificationUpdatedTitle" = "Folder {title} Updated";
"FolderLinkNotificationUpdatedSubtitle_one" = "You have added {count} chat";
"FolderLinkNotificationUpdatedSubtitle_other" = "You have added {count} chats";
"SearchTabChats" = "Chats"; "SearchTabChats" = "Chats";
"SearchTabChannels" = "Channels"; "SearchTabChannels" = "Channels";
"SearchTabApps" = "Apps"; "SearchTabApps" = "Apps";

View File

@ -6,7 +6,9 @@ import type { ApiChatFolder, ApiChatlistInviteAlready } from '../../../api/types
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
import useLang from '../../../hooks/useLang';
import useOldLang from '../../../hooks/useOldLang'; import useOldLang from '../../../hooks/useOldLang';
import PeerPicker from '../../common/pickers/PeerPicker'; import PeerPicker from '../../common/pickers/PeerPicker';
@ -23,17 +25,28 @@ type OwnProps = {
const ChatlistAlready: FC<OwnProps> = ({ invite, folder }) => { const ChatlistAlready: FC<OwnProps> = ({ invite, folder }) => {
const { closeChatlistModal, joinChatlistInvite } = getActions(); const { closeChatlistModal, joinChatlistInvite } = getActions();
const lang = useOldLang(); const lang = useLang();
const oldLang = useOldLang();
const [selectedPeerIds, setSelectedPeerIds] = useState<string[]>(invite.missingPeerIds); const [selectedPeerIds, setSelectedPeerIds] = useState<string[]>(invite.missingPeerIds);
const hasChatsToAdd = Boolean(invite.missingPeerIds.length); const hasChatsToAdd = Boolean(invite.missingPeerIds.length);
const isNew = invite.alreadyPeerIds.length === 0;
const newChatsCount = hasChatsToAdd ? invite.missingPeerIds.length : 0; const newChatsCount = hasChatsToAdd ? invite.missingPeerIds.length : 0;
const badgeText = selectedPeerIds.length ? selectedPeerIds.length.toString() : undefined; const badgeText = selectedPeerIds.length ? selectedPeerIds.length.toString() : undefined;
const descriptionText = hasChatsToAdd const descriptionText = isNew ? lang('FolderLinkSubtitleNew')
? lang('FolderLinkSubtitleChats', [newChatsCount, folder.title], undefined, newChatsCount) : newChatsCount ? lang('FolderLinkSubtitleAdd', {
: lang('FolderLinkSubtitleAlready', folder.title); chats: lang('FolderLinkSubtitleAddCount', { count: newChatsCount }, { pluralValue: newChatsCount }),
title: renderTextWithEntities({
text: folder.title.text,
entities: folder.title.entities,
noCustomEmojiPlayback: folder.noTitleAnimations,
}),
}, {
withNodes: true,
withMarkdown: true,
}) : lang('FolderLinkSubtitleAlready');
const handleButtonClick = useCallback(() => { const handleButtonClick = useCallback(() => {
closeChatlistModal(); closeChatlistModal();
@ -61,7 +74,7 @@ const ChatlistAlready: FC<OwnProps> = ({ invite, folder }) => {
<> <>
<div className={styles.pickerHeader}> <div className={styles.pickerHeader}>
<div className={styles.pickerHeaderInfo}> <div className={styles.pickerHeaderInfo}>
{lang('FolderLinkHeaderChatsJoin', selectedPeerIds.length, 'i')} {oldLang('FolderLinkHeaderChatsJoin', selectedPeerIds.length, 'i')}
</div> </div>
<div <div
className={styles.selectionToggle} className={styles.selectionToggle}
@ -69,7 +82,8 @@ const ChatlistAlready: FC<OwnProps> = ({ invite, folder }) => {
tabIndex={0} tabIndex={0}
onClick={handleSelectionToggle} onClick={handleSelectionToggle}
> >
{selectedPeerIds.length === invite.missingPeerIds.length ? lang('DeselectAll') : lang('SelectAll')} {selectedPeerIds.length === invite.missingPeerIds.length
? oldLang('DeselectAll') : oldLang('SelectAll')}
</div> </div>
</div> </div>
<PeerPicker <PeerPicker
@ -84,7 +98,7 @@ const ChatlistAlready: FC<OwnProps> = ({ invite, folder }) => {
)} )}
<div className={styles.pickerHeader}> <div className={styles.pickerHeader}>
<div className={styles.pickerHeaderInfo}> <div className={styles.pickerHeaderInfo}>
{lang('FolderLinkHeaderAlready')} {oldLang('FolderLinkHeaderAlready')}
</div> </div>
</div> </div>
<PeerPicker <PeerPicker
@ -101,10 +115,10 @@ const ChatlistAlready: FC<OwnProps> = ({ invite, folder }) => {
onClick={handleButtonClick} onClick={handleButtonClick}
> >
<div className={styles.buttonText}> <div className={styles.buttonText}>
{!selectedPeerIds.length && lang('OK')} {!selectedPeerIds.length && oldLang('OK')}
{Boolean(selectedPeerIds.length) && ( {Boolean(selectedPeerIds.length) && (
<> <>
{lang('FolderLinkButtonJoinPlural', selectedPeerIds.length, 'i')} {oldLang('FolderLinkButtonJoinPlural', selectedPeerIds.length, 'i')}
<Badge className={styles.buttonBadge} text={badgeText} isAlternateColor /> <Badge className={styles.buttonBadge} text={badgeText} isAlternateColor />
</> </>
)} )}

View File

@ -29,7 +29,7 @@ const ChatlistDelete: FC<OwnProps> = ({
const lang = useOldLang(); const lang = useOldLang();
const [selectedPeerIds, setSelectedPeerIds] = useState<string[]>(suggestedPeerIds); const [selectedPeerIds, setSelectedPeerIds] = useState<string[]>([]);
const badgeText = selectedPeerIds.length ? selectedPeerIds.length.toString() : undefined; const badgeText = selectedPeerIds.length ? selectedPeerIds.length.toString() : undefined;

View File

@ -63,7 +63,13 @@ const ChatlistInviteModal: FC<OwnProps & StateProps> = ({
noCustomEmojiPlayback: renderingFolder.noTitleAnimations, noCustomEmojiPlayback: renderingFolder.noTitleAnimations,
}); });
} }
if (renderingInfo?.invite && 'title' in renderingInfo.invite) return renderingInfo.invite.title; if (renderingInfo?.invite && 'title' in renderingInfo.invite) {
return renderTextWithEntities({
text: renderingInfo.invite.title.text,
entities: renderingInfo.invite.title.entities,
noCustomEmojiPlayback: renderingInfo.invite.noTitleAnimations,
});
}
return undefined; return undefined;
}, [renderingFolder, renderingInfo]); }, [renderingFolder, renderingInfo]);

View File

@ -7,8 +7,8 @@ import { getActions, getGlobal } from '../../../global';
import type { ApiChatlistInviteNew } from '../../../api/types'; import type { ApiChatlistInviteNew } from '../../../api/types';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import renderText from '../../common/helpers/renderText';
import useLang from '../../../hooks/useLang';
import useOldLang from '../../../hooks/useOldLang'; import useOldLang from '../../../hooks/useOldLang';
import PeerPicker from '../../common/pickers/PeerPicker'; import PeerPicker from '../../common/pickers/PeerPicker';
@ -24,7 +24,8 @@ type OwnProps = {
const ChatlistNew: FC<OwnProps> = ({ invite }) => { const ChatlistNew: FC<OwnProps> = ({ invite }) => {
const { closeChatlistModal, joinChatlistInvite } = getActions(); const { closeChatlistModal, joinChatlistInvite } = getActions();
const lang = useOldLang(); const lang = useLang();
const oldLang = useOldLang();
const [selectedPeerIds, setSelectedPeerIds] = useState<string[]>(invite.peerIds); const [selectedPeerIds, setSelectedPeerIds] = useState<string[]>(invite.peerIds);
const joinedIds = useMemo(() => { const joinedIds = useMemo(() => {
@ -53,12 +54,12 @@ const ChatlistNew: FC<OwnProps> = ({ invite }) => {
return ( return (
<div className={styles.content}> <div className={styles.content}>
<div className={styles.description}> <div className={styles.description}>
{renderText(lang('FolderLinkSubtitle', invite.title), ['simple_markdown', 'emoji'])} {lang('FolderLinkSubtitleNew')}
</div> </div>
<div className={buildClassName(styles.pickerWrapper, 'custom-scroll')}> <div className={buildClassName(styles.pickerWrapper, 'custom-scroll')}>
<div className={styles.pickerHeader}> <div className={styles.pickerHeader}>
<div className={styles.pickerHeaderInfo}> <div className={styles.pickerHeaderInfo}>
{lang('FolderLinkHeaderChatsJoin', selectedCount, 'i')} {oldLang('FolderLinkHeaderChatsJoin', selectedCount, 'i')}
</div> </div>
<div <div
className={styles.selectionToggle} className={styles.selectionToggle}
@ -66,7 +67,7 @@ const ChatlistNew: FC<OwnProps> = ({ invite }) => {
tabIndex={0} tabIndex={0}
onClick={handleSelectionToggle} onClick={handleSelectionToggle}
> >
{selectedPeerIds.length === invite.peerIds.length ? lang('DeselectAll') : lang('SelectAll')} {selectedPeerIds.length === invite.peerIds.length ? oldLang('DeselectAll') : oldLang('SelectAll')}
</div> </div>
</div> </div>
<PeerPicker <PeerPicker
@ -85,7 +86,7 @@ const ChatlistNew: FC<OwnProps> = ({ invite }) => {
disabled={!selectedPeerIds.length} disabled={!selectedPeerIds.length}
> >
<div className={styles.buttonText}> <div className={styles.buttonText}>
{lang('FolderLinkButtonAdd', invite.title)} {lang('FolderLinkAddFolder')}
<Badge className={styles.buttonBadge} text={badgeText} isAlternateColor /> <Badge className={styles.buttonBadge} text={badgeText} isAlternateColor />
</div> </div>
</Button> </Button>

View File

@ -36,7 +36,9 @@ import { formatShareText, processDeepLink } from '../../../util/deeplink';
import { isDeepLink } from '../../../util/deepLinkParser'; import { isDeepLink } from '../../../util/deepLinkParser';
import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { getOrderedIds } from '../../../util/folderManager'; import { getOrderedIds } from '../../../util/folderManager';
import { buildCollectionByKey, omit, pick } from '../../../util/iteratees'; import {
buildCollectionByKey, omit, pick, unique,
} from '../../../util/iteratees';
import { isLocalMessageId } from '../../../util/keys/messageKey'; import { isLocalMessageId } from '../../../util/keys/messageKey';
import * as langProvider from '../../../util/oldLangProvider'; import * as langProvider from '../../../util/oldLangProvider';
import { debounce, pause, throttle } from '../../../util/schedulers'; import { debounce, pause, throttle } from '../../../util/schedulers';
@ -2329,18 +2331,54 @@ addActionHandler('joinChatlistInvite', async (global, actions, payload): Promise
const { invite, peerIds, tabId = getCurrentTabId() } = payload; const { invite, peerIds, tabId = getCurrentTabId() } = payload;
const peers = peerIds.map((peerId) => selectChat(global, peerId)).filter(Boolean); const peers = peerIds.map((peerId) => selectChat(global, peerId)).filter(Boolean);
const notJoinedCount = peers.filter((peer) => peer.isNotJoined).length; const currentNotJoinedCount = peers.filter((peer) => peer.isNotJoined).length;
const folder = 'folderId' in invite ? selectChatFolder(global, invite.folderId) : undefined; const existingFolder = 'folderId' in invite ? selectChatFolder(global, invite.folderId) : undefined;
const folderTitle = 'title' in invite ? invite.title : folder?.title; const folderTitle = ('title' in invite ? invite.title : existingFolder?.title)!;
try { try {
const result = await callApi('joinChatlistInvite', { slug: invite.slug, peers }); const result = await callApi('joinChatlistInvite', { slug: invite.slug, peers });
if (!result) return; if (!result) return;
if (existingFolder) {
actions.showNotification({
title: {
key: 'FolderLinkNotificationUpdatedTitle',
variables: {
title: folderTitle.text,
},
},
message: {
key: 'FolderLinkNotificationUpdatedSubtitle',
variables: {
count: currentNotJoinedCount,
},
options: {
pluralValue: currentNotJoinedCount,
},
},
tabId,
});
return;
}
actions.showNotification({ actions.showNotification({
title: langProvider.oldTranslate(folder ? 'FolderLinkUpdatedTitle' : 'FolderLinkAddedTitle', folderTitle), title: {
message: langProvider.oldTranslate('FolderLinkAddedSubtitle', notJoinedCount, 'i'), key: 'FolderLinkNotificationAddedTitle',
variables: {
title: folderTitle.text,
},
},
message: {
key: 'FolderLinkNotificationAddedSubtitle',
variables: {
count: currentNotJoinedCount,
},
options: {
pluralValue: currentNotJoinedCount,
},
},
tabId, tabId,
}); });
} catch (error) { } catch (error) {
@ -2362,10 +2400,24 @@ addActionHandler('leaveChatlist', async (global, actions, payload): Promise<void
const result = await callApi('leaveChatlist', { folderId, peers }); const result = await callApi('leaveChatlist', { folderId, peers });
if (!result) return; if (!result) return;
if (!folder) return;
actions.showNotification({ actions.showNotification({
title: langProvider.oldTranslate('FolderLinkDeletedTitle', folder.title), title: {
message: langProvider.oldTranslate('FolderLinkDeletedSubtitle', peers.length, 'i'), key: 'FolderLinkNotificationDeletedTitle',
variables: {
title: folder.title.text,
},
},
message: {
key: 'FolderLinkNotificationDeletedSubtitle',
variables: {
count: peers.length,
},
options: {
pluralValue: peers.length,
},
},
tabId, tabId,
}); });
}); });
@ -2549,13 +2601,14 @@ addActionHandler('openDeleteChatFolderModal', async (global, actions, payload):
if (!folder) return; if (!folder) return;
if (folder.isChatList && (!folder.hasMyInvites || isConfirmedForChatlist)) { if (folder.isChatList && (!folder.hasMyInvites || isConfirmedForChatlist)) {
const currentIds = getOrderedIds(folderId);
const suggestions = await callApi('fetchLeaveChatlistSuggestions', { folderId }); const suggestions = await callApi('fetchLeaveChatlistSuggestions', { folderId });
global = getGlobal(); global = getGlobal();
global = updateTabState(global, { global = updateTabState(global, {
chatlistModal: { chatlistModal: {
removal: { removal: {
folderId, folderId,
suggestedPeerIds: suggestions, suggestedPeerIds: unique([...(suggestions || []), ...(currentIds || [])]),
}, },
}, },
}, tabId); }, tabId);

View File

@ -1174,6 +1174,9 @@ export interface LangPair {
'PrivateChatsSearchContext': undefined; 'PrivateChatsSearchContext': undefined;
'GroupChatsSearchContext': undefined; 'GroupChatsSearchContext': undefined;
'ChannelsSearchContext': undefined; 'ChannelsSearchContext': undefined;
'FolderLinkSubtitleNew': undefined;
'FolderLinkSubtitleAlready': undefined;
'FolderLinkAddFolder': undefined;
'SearchTabChats': undefined; 'SearchTabChats': undefined;
'SearchTabChannels': undefined; 'SearchTabChannels': undefined;
'SearchTabApps': undefined; 'SearchTabApps': undefined;
@ -1613,6 +1616,19 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
'folder': V; 'folder': V;
'chats': V; 'chats': V;
}; };
'FolderLinkSubtitleAdd': {
'chats': V;
'title': V;
};
'FolderLinkNotificationDeletedTitle': {
'title': V;
};
'FolderLinkNotificationAddedTitle': {
'title': V;
};
'FolderLinkNotificationUpdatedTitle': {
'title': V;
};
} }
export interface LangPairPlural { export interface LangPairPlural {
@ -1797,6 +1813,18 @@ export interface LangPairPluralWithVariables<V extends unknown = LangVariable> {
'FolderLinkTitleDescriptionChats': { 'FolderLinkTitleDescriptionChats': {
'count': V; 'count': V;
}; };
'FolderLinkSubtitleAddCount': {
'count': V;
};
'FolderLinkNotificationDeletedSubtitle': {
'count': V;
};
'FolderLinkNotificationAddedSubtitle': {
'count': V;
};
'FolderLinkNotificationUpdatedSubtitle': {
'count': V;
};
} }
export type RegularLangKey = keyof LangPair; export type RegularLangKey = keyof LangPair;
export type RegularLangKeyWithVariables = keyof LangPairWithVariables; export type RegularLangKeyWithVariables = keyof LangPairWithVariables;