From 29ec767eb25733255a607196330012b2bdd16b88 Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:59:49 +0200 Subject: [PATCH] Localization: Respect locale number format for floats (#5863) --- src/components/common/UnreadCounter.tsx | 5 ++++- src/components/common/gift/SavedGift.tsx | 2 +- src/components/left/main/Archive.tsx | 4 ++-- src/components/left/main/ChatBadge.tsx | 4 +++- src/components/middle/ReactorListModal.tsx | 20 ++++++++++--------- src/components/middle/ScrollDownButton.tsx | 10 ++++++---- .../middle/message/CommentButton.tsx | 16 ++++++++------- src/components/middle/message/MessageMeta.tsx | 12 +++++++---- .../middle/message/SimilarChannels.tsx | 4 +++- .../middle/message/actions/StarGift.tsx | 4 +++- .../message/reactions/ReactionButton.tsx | 7 +++++-- .../modals/webApp/WebAppGridItem.tsx | 5 ++++- .../right/statistics/StatisticsOverview.tsx | 20 +++++++++++-------- .../statistics/StatisticsRecentPostMeta.tsx | 10 ++++++---- src/util/math.ts | 2 ++ src/util/textFormat.ts | 19 ++++++------------ 16 files changed, 85 insertions(+), 59 deletions(-) diff --git a/src/components/common/UnreadCounter.tsx b/src/components/common/UnreadCounter.tsx index 9380f54c6..d30c8611a 100644 --- a/src/components/common/UnreadCounter.tsx +++ b/src/components/common/UnreadCounter.tsx @@ -6,6 +6,7 @@ import { getAllNotificationsCount } from '../../util/folderManager'; import { formatIntegerCompact } from '../../util/textFormat'; import { useFolderManagerForUnreadCounters } from '../../hooks/useFolderManager'; +import useLang from '../../hooks/useLang'; interface OwnProps { isForAppBadge?: boolean; @@ -15,6 +16,8 @@ const UnreadCounter: FC = ({ isForAppBadge }) => { useFolderManagerForUnreadCounters(); const unreadNotificationsCount = getAllNotificationsCount(); + const lang = useLang(); + useEffect(() => { if (isForAppBadge) { updateAppBadge(unreadNotificationsCount); @@ -26,7 +29,7 @@ const UnreadCounter: FC = ({ isForAppBadge }) => { } return ( -
{formatIntegerCompact(unreadNotificationsCount)}
+
{formatIntegerCompact(lang, unreadNotificationsCount)}
); }; diff --git a/src/components/common/gift/SavedGift.tsx b/src/components/common/gift/SavedGift.tsx index e5d88cad8..990cc1cd5 100644 --- a/src/components/common/gift/SavedGift.tsx +++ b/src/components/common/gift/SavedGift.tsx @@ -69,7 +69,7 @@ const SavedGift = ({ const ribbonText = gift.isPinned && gift.gift.type === 'starGiftUnique' ? lang('GiftSavedNumber', { number: gift.gift.number }) : totalIssued - ? lang('ActionStarGiftLimitedRibbon', { total: formatIntegerCompact(totalIssued) }) + ? lang('ActionStarGiftLimitedRibbon', { total: formatIntegerCompact(lang, totalIssued) }) : undefined; const { diff --git a/src/components/left/main/Archive.tsx b/src/components/left/main/Archive.tsx index 5e94aebaf..819151ae7 100644 --- a/src/components/left/main/Archive.tsx +++ b/src/components/left/main/Archive.tsx @@ -118,7 +118,7 @@ const Archive: FC = ({ @@ -143,7 +143,7 @@ const Archive: FC = ({ diff --git a/src/components/left/main/ChatBadge.tsx b/src/components/left/main/ChatBadge.tsx index 21683d399..3d4285222 100644 --- a/src/components/left/main/ChatBadge.tsx +++ b/src/components/left/main/ChatBadge.tsx @@ -12,6 +12,7 @@ import { formatIntegerCompact } from '../../../util/textFormat'; import { extractCurrentThemeParams } from '../../../util/themeStyle'; import useDerivedState from '../../../hooks/useDerivedState'; +import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; @@ -52,6 +53,7 @@ const ChatBadge: FC = ({ const { requestMainWebView } = getActions(); const oldLang = useOldLang(); + const lang = useLang(); const { unreadMentionsCount = 0, unreadReactionsCount = 0, @@ -136,7 +138,7 @@ const ChatBadge: FC = ({ const unreadCountElement = (hasUnreadMark || unreadCount) ? (
- {!hasUnreadMark && } + {!hasUnreadMark && }
) : undefined; diff --git a/src/components/middle/ReactorListModal.tsx b/src/components/middle/ReactorListModal.tsx index f6379e171..e23f0253f 100644 --- a/src/components/middle/ReactorListModal.tsx +++ b/src/components/middle/ReactorListModal.tsx @@ -20,6 +20,7 @@ import { formatIntegerCompact } from '../../util/textFormat'; import useFlag from '../../hooks/useFlag'; import useInfiniteScroll from '../../hooks/useInfiniteScroll'; +import useLang from '../../hooks/useLang'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; @@ -67,7 +68,8 @@ const ReactorListModal: FC = ({ const chatsById = getGlobal().chats.byId; const usersById = getGlobal().users.byId; - const lang = useOldLang(); + const oldLang = useOldLang(); + const lang = useLang(); const [isClosing, startClosing, stopClosing] = useFlag(false); const [chosenTab, setChosenTab] = useState(undefined); const canShowFilters = reactors && reactions && reactors.count >= MIN_REACTIONS_COUNT_FOR_FILTERS @@ -143,11 +145,11 @@ const ReactorListModal: FC = ({ isOpen={isOpen && !isClosing} onClose={handleClose} className="ReactorListModal narrow" - title={lang('Reactions')} + title={oldLang('Reactions')} onCloseAnimationEnd={handleCloseAnimationEnd} > {canShowFilters && ( -
+
{allReactions.map((reaction) => { const count = reactions?.results @@ -175,14 +177,14 @@ const ReactorListModal: FC = ({ className="reaction-filter-emoji" availableReactions={availableReactions} /> - {Boolean(count) && formatIntegerCompact(count)} + {Boolean(count) && formatIntegerCompact(lang, count)} ); })}
)} -
+
{viewportIds?.length ? ( = ({ - {formatDateAtTime(lang, r.addedDate * 1000)} + {formatDateAtTime(oldLang, r.addedDate * 1000)}
{r.reaction && ( @@ -238,7 +240,7 @@ const ReactorListModal: FC = ({ userId={peerId} noStatusOrTyping avatarSize="medium" - status={seenByUser ? formatDateAtTime(lang, seenByUser * 1000) : undefined} + status={seenByUser ? formatDateAtTime(oldLang, seenByUser * 1000) : undefined} statusIcon="message-read" /> , @@ -255,7 +257,7 @@ const ReactorListModal: FC = ({ isText onClick={handleClose} > - {lang('Close')} + {oldLang('Close')} ); diff --git a/src/components/middle/ScrollDownButton.tsx b/src/components/middle/ScrollDownButton.tsx index a2cef4c9b..9fc5599a0 100644 --- a/src/components/middle/ScrollDownButton.tsx +++ b/src/components/middle/ScrollDownButton.tsx @@ -7,6 +7,7 @@ import buildClassName from '../../util/buildClassName'; import { formatIntegerCompact } from '../../util/textFormat'; import useContextMenuHandlers from '../../hooks/useContextMenuHandlers'; +import useLang from '../../hooks/useLang'; import useOldLang from '../../hooks/useOldLang'; import Icon from '../common/icons/Icon'; @@ -33,7 +34,8 @@ const ScrollDownButton: FC = ({ onReadAll, className, }) => { - const lang = useOldLang(); + const oldLang = useOldLang(); + const lang = useLang(); // eslint-disable-next-line no-null/no-null const ref = useRef(null); @@ -52,11 +54,11 @@ const ScrollDownButton: FC = ({ className={styles.button} onClick={onClick} onContextMenu={handleContextMenu} - ariaLabel={lang(ariaLabelLang)} + ariaLabel={oldLang(ariaLabelLang)} > - {Boolean(unreadCount) &&
{formatIntegerCompact(unreadCount)}
} + {Boolean(unreadCount) &&
{formatIntegerCompact(lang, unreadCount)}
} {onReadAll && ( = ({ positionX="right" positionY="bottom" > - {lang('MarkAllAsRead')} + {oldLang('MarkAllAsRead')} )}
diff --git a/src/components/middle/message/CommentButton.tsx b/src/components/middle/message/CommentButton.tsx index 8b963b671..3d1ae8fdf 100644 --- a/src/components/middle/message/CommentButton.tsx +++ b/src/components/middle/message/CommentButton.tsx @@ -8,6 +8,7 @@ import { selectIsCurrentUserFrozen, selectPeer } from '../../../global/selectors import buildClassName from '../../../util/buildClassName'; import { formatIntegerCompact } from '../../../util/textFormat'; +import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; import useAsyncRendering from '../../right/hooks/useAsyncRendering'; @@ -40,7 +41,8 @@ const CommentButton: FC = ({ const shouldRenderLoading = useAsyncRendering([isLoading], SHOW_LOADER_DELAY); - const lang = useOldLang(); + const oldLang = useOldLang(); + const lang = useLang(); const { originMessageId, chatId, messagesCount, lastMessageId, lastReadInboxMessageId, recentReplierIds, originChannelId, } = threadInfo; @@ -76,7 +78,7 @@ const CommentButton: FC = ({ function renderRecentRepliers() { return ( Boolean(recentRepliers?.length) && ( -
+
{recentRepliers!.map((peer) => ( = ({ const hasUnread = Boolean(lastReadInboxMessageId && lastMessageId && lastReadInboxMessageId < lastMessageId); - const commentsText = messagesCount ? (lang('CommentsCount', '%COMMENTS_COUNT%', undefined, messagesCount) as string) + const commentsText = messagesCount ? (oldLang('CommentsCount', '%COMMENTS_COUNT%', undefined, messagesCount)) .split('%') .map((s) => { - return (s === 'COMMENTS_COUNT' ? : s); + return (s === 'COMMENTS_COUNT' ? : s); }) : undefined; return (
= ({ isLoading && 'loading', asActionButton && 'as-action-button', )} - dir={lang.isRtl ? 'rtl' : 'ltr'} + dir={oldLang.isRtl ? 'rtl' : 'ltr'} onClick={handleClick} role="button" tabIndex={0} @@ -124,7 +126,7 @@ const CommentButton: FC = ({ {!recentRepliers?.length && } {renderRecentRepliers()}
- {messagesCount ? commentsText : lang('LeaveAComment')} + {messagesCount ? commentsText : oldLang('LeaveAComment')}
{isLoading && ( diff --git a/src/components/middle/message/MessageMeta.tsx b/src/components/middle/message/MessageMeta.tsx index e188924aa..ee172607e 100644 --- a/src/components/middle/message/MessageMeta.tsx +++ b/src/components/middle/message/MessageMeta.tsx @@ -112,10 +112,14 @@ const MessageMeta: FC = ({ const viewsTitle = useMemo(() => { if (!message.viewsCount) return undefined; - let text = lang('MessageTooltipViews', { count: message.viewsCount }, { pluralValue: message.viewsCount }); + let text = lang('MessageTooltipViews', { + count: lang.number(message.viewsCount), + }, { pluralValue: message.viewsCount }); if (message.forwardsCount) { text += '\n'; - text += lang('MessageTooltipForwards', { count: message.forwardsCount }, { pluralValue: message.forwardsCount }); + text += lang('MessageTooltipForwards', { + count: lang.number(message.forwardsCount), + }, { pluralValue: message.forwardsCount }); } return text; @@ -160,7 +164,7 @@ const MessageMeta: FC = ({ {Boolean(message.viewsCount) && ( <> - {formatIntegerCompact(message.viewsCount!)} + {formatIntegerCompact(lang, message.viewsCount!)} @@ -168,7 +172,7 @@ const MessageMeta: FC = ({ {!noReplies && Boolean(repliesThreadInfo?.messagesCount) && ( - + diff --git a/src/components/middle/message/SimilarChannels.tsx b/src/components/middle/message/SimilarChannels.tsx index 35ba4f7c2..48b28b102 100644 --- a/src/components/middle/message/SimilarChannels.tsx +++ b/src/components/middle/message/SimilarChannels.tsx @@ -19,6 +19,7 @@ import useTimeout from '../../../hooks/schedulers/useTimeout'; import useAverageColor from '../../../hooks/useAverageColor'; import useFlag from '../../../hooks/useFlag'; import useHorizontalScroll from '../../../hooks/useHorizontalScroll'; +import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; @@ -203,13 +204,14 @@ const SimilarChannels = ({ function SimilarChannel({ channel }: { channel: ApiChat }) { const { openChat } = getActions(); const color = useAverageColor(channel, DEFAULT_BADGE_COLOR); + const lang = useLang(); return (
openChat({ id: channel.id })}>
- {formatIntegerCompact(channel?.membersCount || 0)} + {formatIntegerCompact(lang, channel?.membersCount || 0)}
{channel.title} diff --git a/src/components/middle/message/actions/StarGift.tsx b/src/components/middle/message/actions/StarGift.tsx index dda8e3fff..082ec7e26 100644 --- a/src/components/middle/message/actions/StarGift.tsx +++ b/src/components/middle/message/actions/StarGift.tsx @@ -145,7 +145,9 @@ const StarGiftAction = ({ {action.gift.availabilityTotal && ( )}
diff --git a/src/components/middle/message/reactions/ReactionButton.tsx b/src/components/middle/message/reactions/ReactionButton.tsx index e94e305ac..7dc4ad415 100644 --- a/src/components/middle/message/reactions/ReactionButton.tsx +++ b/src/components/middle/message/reactions/ReactionButton.tsx @@ -15,6 +15,7 @@ import { REM } from '../../../common/helpers/mediaDimensions'; import useSelector from '../../../../hooks/data/useSelector'; import useContextMenuHandlers from '../../../../hooks/useContextMenuHandlers'; import useEffectWithPrevDeps from '../../../../hooks/useEffectWithPrevDeps'; +import useLang from '../../../../hooks/useLang'; import useLastCallback from '../../../../hooks/useLastCallback'; import usePrevious from '../../../../hooks/usePrevious'; import useShowTransition from '../../../../hooks/useShowTransition'; @@ -76,6 +77,8 @@ const ReactionButton = ({ const counterRef = useRef(null); const animationRef = useRef(); + const lang = useLang(); + const isPaid = reaction.reaction.type === 'paid'; const starsState = useSelector(selectStarsState); @@ -198,7 +201,7 @@ const ReactionButton = ({ {shouldRenderPaidCounter && ( )} @@ -216,7 +219,7 @@ const ReactionButton = ({ ) : ( )} diff --git a/src/components/modals/webApp/WebAppGridItem.tsx b/src/components/modals/webApp/WebAppGridItem.tsx index 33dd431c7..98a6cedf2 100644 --- a/src/components/modals/webApp/WebAppGridItem.tsx +++ b/src/components/modals/webApp/WebAppGridItem.tsx @@ -12,6 +12,7 @@ import buildClassName from '../../../util/buildClassName'; import { formatIntegerCompact } from '../../../util/textFormat'; import { extractCurrentThemeParams } from '../../../util/themeStyle'; +import useLang from '../../../hooks/useLang'; import useLastCallback from '../../../hooks/useLastCallback'; import PeerBadge from '../../common/PeerBadge'; @@ -33,6 +34,8 @@ function WebAppGridItem({ user, isPopularApp }: OwnProps & StateProps) { requestMainWebView, } = getActions(); + const lang = useLang(); + const handleClick = useLastCallback(() => { if (!user) { return; @@ -55,7 +58,7 @@ function WebAppGridItem({ user, isPopularApp }: OwnProps & StateProps) { const title = user?.firstName; const activeUserCount = user?.botActiveUsers; - const badgeText = activeUserCount && isPopularApp ? formatIntegerCompact(activeUserCount) : undefined; + const badgeText = activeUserCount && isPopularApp ? formatIntegerCompact(lang, activeUserCount) : undefined; return (
= ({ className, subtitle, }) => { - const lang = useOldLang(); + const oldLang = useOldLang(); + const lang = useLang(); const renderOverviewItemValue = ({ change, percentage }: StatisticsOverviewItem) => { if (!change) { @@ -132,7 +134,9 @@ const StatisticsOverview: FC = ({ return ( - {isChangeNegative ? `-${formatIntegerCompact(Math.abs(change))}` : `+${formatIntegerCompact(change)}`} + {isChangeNegative + ? `-${formatIntegerCompact(lang, Math.abs(change))}` + : `+${formatIntegerCompact(lang, change)}`} {percentage && ( <> {' '} @@ -156,7 +160,7 @@ const StatisticsOverview: FC = ({ ≈ ${integerUsdPart}.{decimalUsdPart} -

{lang(text)}

+

{oldLang(text)}

); }; @@ -177,7 +181,7 @@ const StatisticsOverview: FC = ({ {period && (
- {formatFullDate(lang, period.minDate * 1000)} — {formatFullDate(lang, period.maxDate * 1000)} + {formatFullDate(oldLang, period.minDate * 1000)} — {formatFullDate(oldLang, period.maxDate * 1000)}
)}
@@ -202,7 +206,7 @@ const StatisticsOverview: FC = ({ {`${cell.isApproximate ? '≈' : ''}${formatInteger(field)}`} -

{lang(cell.title)}

+

{oldLang(cell.title)}

); } @@ -218,7 +222,7 @@ const StatisticsOverview: FC = ({ {field.percentage}% -

{lang(cell.title)}

+

{oldLang(cell.title)}

); } @@ -226,11 +230,11 @@ const StatisticsOverview: FC = ({ return ( - {formatIntegerCompact(field.current)} + {formatIntegerCompact(lang, field.current)} {' '} {renderOverviewItemValue(field)} -

{lang(cell.title)}

+

{oldLang(cell.title)}

); })} diff --git a/src/components/right/statistics/StatisticsRecentPostMeta.tsx b/src/components/right/statistics/StatisticsRecentPostMeta.tsx index 108e14c26..8a80b7447 100644 --- a/src/components/right/statistics/StatisticsRecentPostMeta.tsx +++ b/src/components/right/statistics/StatisticsRecentPostMeta.tsx @@ -4,6 +4,7 @@ import type { StatisticsMessageInteractionCounter, StatisticsStoryInteractionCou import { formatIntegerCompact } from '../../../util/textFormat'; +import useLang from '../../../hooks/useLang'; import useOldLang from '../../../hooks/useOldLang'; import Icon from '../../common/icons/Icon'; @@ -15,25 +16,26 @@ interface OwnProps { } function StatisticsRecentPostMeta({ postStatistic }: OwnProps) { - const lang = useOldLang(); + const oldLang = useOldLang(); + const lang = useLang(); return (
{postStatistic.reactionsCount > 0 && ( - {formatIntegerCompact(postStatistic.reactionsCount)} + {formatIntegerCompact(lang, postStatistic.reactionsCount)} )} {postStatistic.forwardsCount > 0 && ( - {formatIntegerCompact(postStatistic.forwardsCount)} + {formatIntegerCompact(lang, postStatistic.forwardsCount)} )} {!postStatistic.forwardsCount && !postStatistic.reactionsCount - && lang('ChannelStats.SharesCount_ZeroValueHolder')} + && oldLang('ChannelStats.SharesCount_ZeroValueHolder')}
); } diff --git a/src/util/math.ts b/src/util/math.ts index c9561d226..1407a71f7 100644 --- a/src/util/math.ts +++ b/src/util/math.ts @@ -1,6 +1,8 @@ export const clamp = (num: number, min: number, max: number) => (Math.min(max, Math.max(min, num))); export const isBetween = (num: number, min: number, max: number) => (num >= min && num <= max); export const round = (num: number, decimals: number = 0) => Math.round(num * 10 ** decimals) / 10 ** decimals; +export const ceil = (num: number, decimals: number = 0) => Math.ceil(num * 10 ** decimals) / 10 ** decimals; +export const floor = (num: number, decimals: number = 0) => Math.floor(num * 10 ** decimals) / 10 ** decimals; export const lerp = (start: number, end: number, interpolationRatio: number) => { return (1 - interpolationRatio) * start + interpolationRatio * end; }; diff --git a/src/util/textFormat.ts b/src/util/textFormat.ts index 5c21cbf8f..10cbf6886 100644 --- a/src/util/textFormat.ts +++ b/src/util/textFormat.ts @@ -1,32 +1,25 @@ import type { OldLangFn } from '../hooks/useOldLang'; +import type { LangFn } from './localization'; import EMOJI_REGEX from '../lib/twemojiRegex'; import fixNonStandardEmoji from './emoji/fixNonStandardEmoji'; +import { floor } from './math'; import withCache from './withCache'; export function formatInteger(value: number) { return String(value).replace(/\d(?=(\d{3})+$)/g, '$& '); } -function formatFixedNumber(number: number) { - const fixed = String(number.toFixed(1)); - if (fixed.substr(-2) === '.0') { - return Math.floor(number); - } - - return number.toFixed(1).replace('.', ','); -} - -export function formatIntegerCompact(views: number) { +export function formatIntegerCompact(lang: LangFn, views: number) { if (views < 1e3) { - return views.toString(); + return lang.number(views); } if (views < 1e6) { - return `${formatFixedNumber(views / 1e3)}K`; + return `${lang.number(floor(views / 1e3, 1))}K`; } - return `${formatFixedNumber(views / 1e6)}M`; + return `${lang.number(floor(views / 1e6, 1))}M`; } export function formatPercent(value: number, fractionDigits = 1) {