From 68f28b9405074886f179c02c07c5fa9eec4fec0d Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sat, 28 Jan 2023 02:15:16 +0100 Subject: [PATCH] [Dev] Localization: Reassign lang function for memo (#2341) --- src/components/common/Audio.tsx | 3 +- .../common/helpers/renderTextWithEntities.tsx | 4 +- src/global/actions/api/accounts.ts | 6 +- src/global/actions/api/chats.ts | 6 +- src/global/actions/api/messages.ts | 6 +- src/global/actions/api/symbols.ts | 18 ++--- src/global/actions/api/users.ts | 2 +- src/global/actions/apiUpdaters/calls.async.ts | 2 +- src/global/actions/apiUpdaters/payments.ts | 4 +- src/global/actions/ui/calls.ts | 10 +-- src/global/actions/ui/messages.ts | 2 +- src/hooks/useLang.ts | 4 +- src/util/langProvider.ts | 67 +++++++++++-------- src/util/notifications.ts | 12 ++-- 14 files changed, 79 insertions(+), 67 deletions(-) diff --git a/src/components/common/Audio.tsx b/src/components/common/Audio.tsx index 0efb60462..97d15bcb2 100644 --- a/src/components/common/Audio.tsx +++ b/src/components/common/Audio.tsx @@ -25,7 +25,6 @@ import buildClassName from '../../util/buildClassName'; import { formatMediaDateTime, formatMediaDuration, formatPastTimeShort } from '../../util/dateFormat'; import { decodeWaveform, interpolateArray } from '../../util/waveform'; import { makeTrackId } from '../../util/audioPlayer'; -import { getTranslation } from '../../util/langProvider'; import useMediaWithLoadProgress from '../../hooks/useMediaWithLoadProgress'; import useShowTransition from '../../hooks/useShowTransition'; import type { BufferedRange } from '../../hooks/useBuffering'; @@ -443,7 +442,7 @@ function renderAudio( title, performer, fileName, } = audio; const showSeekline = isPlaying || (playProgress > 0 && playProgress < 1); - const { isRtl } = getTranslation; + const { isRtl } = lang; return (
diff --git a/src/components/common/helpers/renderTextWithEntities.tsx b/src/components/common/helpers/renderTextWithEntities.tsx index 0bcbcc07b..54738e324 100644 --- a/src/components/common/helpers/renderTextWithEntities.tsx +++ b/src/components/common/helpers/renderTextWithEntities.tsx @@ -10,7 +10,7 @@ import type { TextFilter } from './renderText'; import buildClassName from '../../../util/buildClassName'; import renderText from './renderText'; import { copyTextToClipboard } from '../../../util/clipboard'; -import { getTranslation } from '../../../util/langProvider'; +import { translate } from '../../../util/langProvider'; import { buildCustomEmojiHtmlFromEntity } from '../../middle/composer/helpers/customEmoji'; import MentionLink from '../../middle/message/MentionLink'; @@ -550,6 +550,6 @@ function handleHashtagClick(e: React.MouseEvent) { function handleCodeClick(e: React.MouseEvent) { copyTextToClipboard(e.currentTarget.innerText); getActions().showNotification({ - message: getTranslation('TextCopied'), + message: translate('TextCopied'), }); } diff --git a/src/global/actions/api/accounts.ts b/src/global/actions/api/accounts.ts index 05cebc30b..bc9f1cdca 100644 --- a/src/global/actions/api/accounts.ts +++ b/src/global/actions/api/accounts.ts @@ -1,7 +1,7 @@ import { addActionHandler, getGlobal, setGlobal } from '../../index'; import { selectChat } from '../../selectors'; import { callApi } from '../../../api/gramjs'; -import { getTranslation } from '../../../util/langProvider'; +import { translate } from '../../../util/langProvider'; import { addUsers } from '../../reducers'; import { buildCollectionByKey } from '../../../util/iteratees'; @@ -28,7 +28,7 @@ addActionHandler('reportPeer', async (global, actions, payload) => { actions.showNotification({ message: result - ? getTranslation('ReportPeer.AlertSuccess') + ? translate('ReportPeer.AlertSuccess') : 'An error occurred while submitting your report. Please, try again later.', }); }); @@ -58,7 +58,7 @@ addActionHandler('reportProfilePhoto', async (global, actions, payload) => { actions.showNotification({ message: result - ? getTranslation('ReportPeer.AlertSuccess') + ? translate('ReportPeer.AlertSuccess') : 'An error occurred while submitting your report. Please, try again later.', }); }); diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index f03c98e43..85b957112 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -636,7 +636,7 @@ addActionHandler('openChatByPhoneNumber', async (global, actions, payload) => { if (!chat) { actions.openPreviousChat(); actions.showNotification({ - message: langProvider.getTranslation('lng_username_by_phone_not_found').replace('{phone}', phoneNumber), + message: langProvider.translate('lng_username_by_phone_not_found').replace('{phone}', phoneNumber), }); return; } @@ -1525,7 +1525,7 @@ addActionHandler('toggleTopicPinned', (global, actions, payload) => { if (isPinned && Object.values(chat.topics).filter((topic) => topic.isPinned).length >= topicsPinnedLimit) { actions.showNotification({ - message: langProvider.getTranslation('LimitReachedPinnedTopics', topicsPinnedLimit, 'i'), + message: langProvider.translate('LimitReachedPinnedTopics', topicsPinnedLimit, 'i'), }); return; } @@ -1953,7 +1953,7 @@ async function getAttachBotOrNotify(global: GlobalState, username: string) { global = getGlobal(); if (!result) { - getActions().showNotification({ message: langProvider.getTranslation('WebApp.AddToAttachmentUnavailableError') }); + getActions().showNotification({ message: langProvider.translate('WebApp.AddToAttachmentUnavailableError') }); return undefined; } diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index 6f19c166e..c91ec20e7 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -84,7 +84,7 @@ import { import { getMessageOriginalId, getUserFullName, isDeletedUser, isServiceNotificationMessage, isUserBot, } from '../../helpers'; -import { getTranslation } from '../../../util/langProvider'; +import { translate } from '../../../util/langProvider'; import { ensureProtocol } from '../../../util/ensureProtocol'; const AUTOLOGIN_TOKEN_KEY = 'autologin_token'; @@ -494,7 +494,7 @@ addActionHandler('reportMessages', async (global, actions, payload) => { actions.showNotification({ message: result - ? getTranslation('ReportPeer.AlertSuccess') + ? translate('ReportPeer.AlertSuccess') : 'An error occurred while submitting your report. Please, try again later.', }); }); @@ -1336,7 +1336,7 @@ addActionHandler('setForwardChatOrTopic', async (global, actions, payload) => { if (user?.fullInfo!.noVoiceMessages) { actions.showDialog({ data: { - message: getTranslation('VoiceMessagesRestrictedByPrivacy', getUserFullName(user)), + message: translate('VoiceMessagesRestrictedByPrivacy', getUserFullName(user)), }, }); return; diff --git a/src/global/actions/api/symbols.ts b/src/global/actions/api/symbols.ts index 96ea4dffc..c55d3e621 100644 --- a/src/global/actions/api/symbols.ts +++ b/src/global/actions/api/symbols.ts @@ -18,7 +18,7 @@ import { } from '../../reducers'; import searchWords from '../../../util/searchWords'; import { selectIsCurrentUserPremium, selectStickerSet } from '../../selectors'; -import { getTranslation } from '../../../util/langProvider'; +import { translate } from '../../../util/langProvider'; import { selectCurrentLimit, selectPremiumLimit } from '../../selectors/limits'; import * as langProvider from '../../../util/langProvider'; import { buildCollectionByKey } from '../../../util/iteratees'; @@ -273,9 +273,9 @@ addActionHandler('saveGif', async (global, actions, payload) => { if (!shouldUnsave && length && length >= limit) { actions.showNotification({ - title: langProvider.getTranslation('LimitReachedFavoriteGifs', limit.toString()), - message: isPremium ? langProvider.getTranslation('LimitReachedFavoriteGifsSubtitlePremium') - : langProvider.getTranslation('LimitReachedFavoriteGifsSubtitle', + title: langProvider.translate('LimitReachedFavoriteGifs', limit.toString()), + message: isPremium ? langProvider.translate('LimitReachedFavoriteGifsSubtitlePremium') + : langProvider.translate('LimitReachedFavoriteGifsSubtitle', premiumLimit.toString()), ...(!isPremium && { action: actions.openPremiumModal }), className: 'bold-link', @@ -312,9 +312,9 @@ addActionHandler('faveSticker', (global, actions, payload) => { if (current >= limit) { actions.showNotification({ - title: langProvider.getTranslation('LimitReachedFavoriteStickers', limit.toString()), - message: isPremium ? langProvider.getTranslation('LimitReachedFavoriteStickersSubtitlePremium') - : langProvider.getTranslation('LimitReachedFavoriteStickersSubtitle', + title: langProvider.translate('LimitReachedFavoriteStickers', limit.toString()), + message: isPremium ? langProvider.translate('LimitReachedFavoriteStickersSubtitlePremium') + : langProvider.translate('LimitReachedFavoriteStickersSubtitle', premiumLimit.toString()), ...(!isPremium && { action: actions.openPremiumModal }), className: 'bold-link', @@ -476,7 +476,7 @@ async function loadStickers(stickerSetInfo: ApiStickerSetInfo) { if (!stickerSet) { onTickEnd(() => { getActions().showNotification({ - message: getTranslation('StickerPack.ErrorNotFound'), + message: translate('StickerPack.ErrorNotFound'), }); }); if ('shortName' in stickerSetInfo && global.openedStickerSetShortName === stickerSetInfo.shortName) { @@ -621,7 +621,7 @@ addActionHandler('openStickerSet', async (global, actions, payload) => { const set = selectStickerSet(global, stickerSetInfo); if (!set?.shortName) { actions.showNotification({ - message: getTranslation('StickerPack.ErrorNotFound'), + message: translate('StickerPack.ErrorNotFound'), }); return; } diff --git a/src/global/actions/api/users.ts b/src/global/actions/api/users.ts index df168822a..3b043ebce 100644 --- a/src/global/actions/api/users.ts +++ b/src/global/actions/api/users.ts @@ -302,7 +302,7 @@ addActionHandler('importContact', async (global, actions, payload) => { const result = await callApi('importContact', { phone, firstName, lastName }); if (!result) { actions.showNotification({ - message: langProvider.getTranslation('Contacts.PhoneNumber.NotRegistred'), + message: langProvider.translate('Contacts.PhoneNumber.NotRegistred'), }); return; diff --git a/src/global/actions/apiUpdaters/calls.async.ts b/src/global/actions/apiUpdaters/calls.async.ts index cef586eb2..9b6f5d5f6 100644 --- a/src/global/actions/apiUpdaters/calls.async.ts +++ b/src/global/actions/apiUpdaters/calls.async.ts @@ -105,7 +105,7 @@ addActionHandler('apiUpdate', (global, actions, update) => { if (!verifyPhoneCallProtocol(call.protocol)) { const user = selectPhoneCallUser(global); actions.hangUp(); - actions.showNotification({ message: langProvider.getTranslation('VoipPeerIncompatible', user?.firstName) }); + actions.showNotification({ message: langProvider.translate('VoipPeerIncompatible', user?.firstName) }); return undefined; } } diff --git a/src/global/actions/apiUpdaters/payments.ts b/src/global/actions/apiUpdaters/payments.ts index ace516d36..228839dc0 100644 --- a/src/global/actions/apiUpdaters/payments.ts +++ b/src/global/actions/apiUpdaters/payments.ts @@ -18,8 +18,8 @@ addActionHandler('apiUpdate', (global, actions, update) => { const { amount, currency, title } = message.content.invoice; actions.showNotification({ - message: langProvider.getTranslation('PaymentInfoHint', [ - formatCurrency(amount, currency, langProvider.getTranslation.code), + message: langProvider.translate('PaymentInfoHint', [ + formatCurrency(amount, currency, langProvider.getTranslationFn().code), title, ]), }); diff --git a/src/global/actions/ui/calls.ts b/src/global/actions/ui/calls.ts index 9b53a59f5..2595013a3 100644 --- a/src/global/actions/ui/calls.ts +++ b/src/global/actions/ui/calls.ts @@ -196,7 +196,7 @@ addActionHandler('joinVoiceChatByLink', async (global, actions, payload) => { const chat = await fetchChatByUsername(username); if (!chat) { - actions.showNotification({ message: langProvider.getTranslation('NoUsernameFound') }); + actions.showNotification({ message: langProvider.translate('NoUsernameFound') }); return; } @@ -383,7 +383,7 @@ export function checkNavigatorUserMediaPermissions(isVideo?: boolean) { .then((stream) => { if (stream.getVideoTracks().length === 0) { getActions().showNotification({ - message: langProvider.getTranslation('Call.Camera.Error'), + message: langProvider.translate('Call.Camera.Error'), }); } else { checkMicrophonePermission(); @@ -391,7 +391,7 @@ export function checkNavigatorUserMediaPermissions(isVideo?: boolean) { }) .catch(() => { getActions().showNotification({ - message: langProvider.getTranslation('Call.Camera.Error'), + message: langProvider.translate('Call.Camera.Error'), }); }); } else { @@ -404,13 +404,13 @@ function checkMicrophonePermission() { .then((stream) => { if (stream.getAudioTracks().length === 0) { getActions().showNotification({ - message: langProvider.getTranslation('RequestAcces.Error.HaveNotAccess.Call'), + message: langProvider.translate('RequestAcces.Error.HaveNotAccess.Call'), }); } }) .catch(() => { getActions().showNotification({ - message: langProvider.getTranslation('RequestAcces.Error.HaveNotAccess.Call'), + message: langProvider.translate('RequestAcces.Error.HaveNotAccess.Call'), }); }); } diff --git a/src/global/actions/ui/messages.ts b/src/global/actions/ui/messages.ts index 29dbc7ddd..d32d34577 100644 --- a/src/global/actions/ui/messages.ts +++ b/src/global/actions/ui/messages.ts @@ -751,7 +751,7 @@ addActionHandler('copyMessagesByIds', (global, actions, payload: { messageIds?: function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) { const { type: messageListType, threadId } = selectCurrentMessageList(global) || {}; - const lang = langProvider.getTranslation; + const lang = langProvider.translate; const chatMessages = messageListType === 'scheduled' ? selectChatScheduledMessages(global, chatId) diff --git a/src/hooks/useLang.ts b/src/hooks/useLang.ts index c174a5d77..6ccfc8e69 100644 --- a/src/hooks/useLang.ts +++ b/src/hooks/useLang.ts @@ -2,7 +2,7 @@ import * as langProvider from '../util/langProvider'; import useForceUpdate from './useForceUpdate'; import useOnChange from './useOnChange'; -export type LangFn = typeof langProvider.getTranslation; +export type LangFn = langProvider.LangFn; const useLang = (): LangFn => { const forceUpdate = useForceUpdate(); @@ -11,7 +11,7 @@ const useLang = (): LangFn => { return langProvider.addCallback(forceUpdate); }, [forceUpdate]); - return langProvider.getTranslation; + return langProvider.getTranslationFn(); }; export default useLang; diff --git a/src/util/langProvider.ts b/src/util/langProvider.ts index 37d7ecbb3..a514159b5 100644 --- a/src/util/langProvider.ts +++ b/src/util/langProvider.ts @@ -11,7 +11,7 @@ import { callApi } from '../api/gramjs'; import { createCallbackManager } from './callbacks'; import { formatInteger } from './textFormat'; -interface LangFn { +export interface LangFn { (key: string, value?: any, format?: 'i', pluralValue?: number): string; isRtl?: boolean; @@ -94,30 +94,42 @@ export { addCallback, removeCallback }; let currentLangCode: string | undefined; let currentTimeFormat: TimeFormat | undefined; -export const getTranslation: LangFn = (key: string, value?: any, format?: 'i', pluralValue?: number) => { - if (value !== undefined) { - const cacheValue = Array.isArray(value) ? JSON.stringify(value) : value; - const cached = cache.get(`${key}_${cacheValue}_${format}${pluralValue ? `_${pluralValue}` : ''}`); - if (cached) { - return cached; - } - } - - if (!langPack && !fallbackLangPack) { - return key; - } - - const langString = (langPack?.[key]) || (fallbackLangPack?.[key]); - if (!langString) { - if (!fallbackLangPack) { - void importFallbackLangPack(); +function createLangFn() { + return (key: string, value?: any, format?: 'i', pluralValue?: number) => { + if (value !== undefined) { + const cacheValue = Array.isArray(value) ? JSON.stringify(value) : value; + const cached = cache.get(`${key}_${cacheValue}_${format}${pluralValue ? `_${pluralValue}` : ''}`); + if (cached) { + return cached; + } } - return key; - } + if (!langPack && !fallbackLangPack) { + return key; + } - return processTranslation(langString, key, value, format, pluralValue); -}; + const langString = (langPack?.[key]) || (fallbackLangPack?.[key]); + if (!langString) { + if (!fallbackLangPack) { + void importFallbackLangPack(); + } + + return key; + } + + return processTranslation(langString, key, value, format, pluralValue); + }; +} + +let translationFn: LangFn = createLangFn(); + +export function translate(...args: Parameters) { + return translationFn(...args); +} + +export function getTranslationFn(): LangFn { + return translationFn; +} export async function getTranslationForLangString(langCode: string, key: string) { let translateString: ApiLangString | undefined = await cacheApi.fetch( @@ -162,10 +174,11 @@ export async function setLanguage(langCode: LangCode, callback?: NoneToVoidFunct const { languages, timeFormat } = getGlobal().settings.byKey; const langInfo = languages?.find((lang) => lang.langCode === langCode); - getTranslation.isRtl = Boolean(langInfo?.rtl); - getTranslation.code = langCode.replace('-raw', '') as LangCode; - getTranslation.langName = langInfo?.nativeName; - getTranslation.timeFormat = timeFormat; + translationFn = createLangFn(); + translationFn.isRtl = Boolean(langInfo?.rtl); + translationFn.code = langCode.replace('-raw', '') as LangCode; + translationFn.langName = langInfo?.nativeName; + translationFn.timeFormat = timeFormat; if (callback) { callback(); @@ -180,7 +193,7 @@ export function setTimeFormat(timeFormat: TimeFormat) { } currentTimeFormat = timeFormat; - getTranslation.timeFormat = timeFormat; + translationFn.timeFormat = timeFormat; runCallbacks(); } diff --git a/src/util/notifications.ts b/src/util/notifications.ts index 8cd48ff4c..0a49b2ca2 100644 --- a/src/util/notifications.ts +++ b/src/util/notifications.ts @@ -29,7 +29,7 @@ import { selectUser, } from '../global/selectors'; import { IS_SERVICE_WORKER_SUPPORTED, IS_TOUCH_ENV } from './environment'; -import { getTranslation } from './langProvider'; +import { translate } from './langProvider'; import * as mediaLoader from './mediaLoader'; import { debounce } from './schedulers'; @@ -300,7 +300,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: A const isChat = chat && (isChatChannel(chat) || message.senderId === message.chatId); body = renderActionMessageText( - getTranslation, + translate, message, !isChat ? messageSender : undefined, isChat ? chat : undefined, @@ -312,8 +312,8 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: A ) as string; } else { // TODO[forums] Support ApiChat - const senderName = getMessageSenderName(getTranslation, chat.id, messageSender); - const summary = getMessageSummaryText(getTranslation, message, false, 60, false); + const senderName = getMessageSenderName(translate, chat.id, messageSender); + const summary = getMessageSummaryText(translate, message, false, 60, false); body = senderName ? `${senderName}: ${summary}` : summary; } @@ -321,7 +321,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: A body = 'New message'; } - let title = isScreenLocked ? APP_NAME : getChatTitle(getTranslation, chat, privateChatUser); + let title = isScreenLocked ? APP_NAME : getChatTitle(translate, chat, privateChatUser); if (message.isSilent) { title += ' 🔕'; @@ -364,7 +364,7 @@ export async function notifyAboutCall({ options.vibrate = [200, 100, 200]; } - const notification = new Notification(getTranslation('VoipIncoming'), options); + const notification = new Notification(translate('VoipIncoming'), options); notification.onclick = () => { notification.close();