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();