Localization: Migrate more components (#5484)

This commit is contained in:
zubiden 2025-01-21 18:21:57 +01:00 committed by Alexander Zinchuk
parent cfad59e2f0
commit 8bac803dea
33 changed files with 445 additions and 354 deletions

View File

@ -5,14 +5,16 @@ import readFallbackStrings from '../src/util/data/readFallbackStrings';
import { pick } from '../src/util/iteratees';
const HEADER = `/* eslint-disable */
// This file is generated by dev/generateInitialLangFallback.ts. Do not edit it manually.\n`;
// This file is generated by dev/generateInitialLangFallback.ts. Do not edit it manually.\n
import type { LangPackStringValue } from '../../api/types';
import type { LangKey } from '../../types/language';\n`;
async function main() {
const data = await readFallbackStrings(true);
const selectedKeys = pick(data.langPack.strings, initialKeys);
const json = JSON.stringify(selectedKeys, undefined, 2);
const text = `${HEADER}\nexport default ${json} as Record<string, string>;\n`;
const text = `${HEADER}\nexport default ${json} as Record<LangKey, LangPackStringValue>;\n`;
writeFileSync('./src/assets/localization/initialStrings.ts', text, 'utf8');
}

View File

@ -1,5 +1,7 @@
import { FloodWaitError, RPCError } from '../../../lib/gramjs/errors';
import type { RegularLangKey } from '../../../types/language';
import type { RegularLangFnParameters } from '../../../util/localization';
import type {
ApiUpdateAuthorizationState,
ApiUpdateAuthorizationStateType,
@ -10,12 +12,12 @@ import type {
import { DEBUG } from '../../../config';
import { sendApiUpdate } from '../updates/apiUpdateEmitter';
const ApiErrors: { [k: string]: string } = {
PHONE_NUMBER_INVALID: 'Invalid phone number.',
PHONE_CODE_INVALID: 'Invalid code.',
PASSWORD_HASH_INVALID: 'Incorrect password.',
PHONE_PASSWORD_FLOOD: 'Limit exceeded. Please try again later.',
PHONE_NUMBER_BANNED: 'This phone number is banned.',
const ApiErrors: Record<string, RegularLangKey> = {
PHONE_NUMBER_INVALID: 'ErrorPhoneNumberInvalid',
PHONE_CODE_INVALID: 'ErrorCodeInvalid',
PASSWORD_HASH_INVALID: 'ErrorIncorrectPassword',
PHONE_PASSWORD_FLOOD: 'ErrorPasswordFlood',
PHONE_NUMBER_BANNED: 'ErrorPhoneBanned',
};
const authController: {
@ -85,19 +87,30 @@ export function onRequestQrCode(qrCode: { token: Buffer; expires: number }) {
}
export function onAuthError(err: Error) {
let message: string;
let messageKey: RegularLangFnParameters | undefined;
if (err instanceof FloodWaitError) {
const hours = Math.ceil(Number(err.seconds) / 60 / 60);
message = `Too many attempts. Try again in ${hours > 1 ? `${hours} hours` : 'an hour'}`;
messageKey = {
key: 'ErrorFlood',
variables: { hour: hours },
options: { pluralValue: hours },
};
} else if (err instanceof RPCError) {
message = ApiErrors[err.errorMessage];
} else {
message = err.message;
messageKey = {
key: ApiErrors[err.errorMessage],
};
} else if (err.message) {
messageKey = {
key: 'ErrorUnexpectedMessage',
variables: { error: err.message },
};
}
if (!message) {
message = 'Unexpected Error';
if (!messageKey) {
messageKey = {
key: 'ErrorUnexpected',
};
if (DEBUG) {
// eslint-disable-next-line no-console
@ -107,7 +120,7 @@ export function onAuthError(err: Error) {
sendApiUpdate({
'@type': 'updateAuthorizationError',
message,
errorKey: messageKey,
});
}

View File

@ -17,8 +17,8 @@ import type {
import {
ACCEPTABLE_USERNAME_ERRORS,
BLOCKED_LIST_LIMIT,
LANG_PACK,
MAX_INT_32,
OLD_DEFAULT_LANG_PACK,
} from '../../../config';
import { buildCollectionByKey } from '../../../util/iteratees';
import { getServerTime } from '../../../util/serverTime';
@ -36,7 +36,6 @@ import {
buildApiWebSession,
buildLangStrings,
oldBuildLangPack,
oldBuildLangPackString,
} from '../apiBuilders/misc';
import { getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
import {
@ -468,7 +467,7 @@ export async function fetchLangDifference({
export async function fetchLanguages(): Promise<ApiLanguage[] | undefined> {
const result = await invokeRequest(new GramJs.langpack.GetLanguages({
langPack: OLD_DEFAULT_LANG_PACK,
langPack: LANG_PACK,
}));
if (!result) {
return undefined;
@ -495,6 +494,27 @@ export async function fetchLanguage({
return buildApiLanguage(result);
}
export async function fetchLangStrings({
langPack,
langCode,
keys,
}: {
langPack: string;
langCode: string;
keys: string[];
}) {
const result = await invokeRequest(new GramJs.langpack.GetStrings({
langPack,
langCode,
keys,
}));
if (!result) {
return undefined;
}
return buildLangStrings(result);
}
export async function oldFetchLangPack({ sourceLangPacks, langCode }: {
sourceLangPacks: typeof LANG_PACKS;
langCode: string;
@ -514,22 +534,6 @@ export async function oldFetchLangPack({ sourceLangPacks, langCode }: {
return { langPack: Object.assign({}, ...collections.reverse()) as typeof collections[0] };
}
export async function oldFetchLangStrings({ langPack, langCode, keys }: {
langPack: string; langCode: string; keys: string[];
}) {
const result = await invokeRequest(new GramJs.langpack.GetStrings({
langPack,
langCode: BETA_LANG_CODES.includes(langCode) ? `${langCode}-raw` : langCode,
keys,
}));
if (!result) {
return undefined;
}
return result.map(oldBuildLangPackString);
}
export async function fetchPrivacySettings(privacyKey: ApiPrivacyKey) {
const key = buildInputPrivacyKey(privacyKey);
const result = await invokeRequest(new GramJs.account.GetPrivacy({ key }));

View File

@ -7,6 +7,7 @@ import type {
VideoState,
} from '../../lib/secret-sauce';
import type { ThreadId } from '../../types';
import type { RegularLangFnParameters } from '../../util/localization';
import type { ApiBotMenuButton } from './bots';
import type {
ApiGroupCall, ApiPhoneCall,
@ -84,7 +85,7 @@ export type ApiUpdateSession = {
export type ApiUpdateAuthorizationError = {
'@type': 'updateAuthorizationError';
message: string;
errorKey: RegularLangFnParameters;
};
export type ApiUpdateConnectionState = {

View File

@ -162,7 +162,7 @@
"LoginQRLogin" = "Log in by QR Code";
"LoginQRTitle" = "Log in to Telegram by QR Code";
"LoginQRHelp1" = "Open Telegram on your phone";
"LoginQR2Help2" = "Go to **Settings** > **Devices** > **Link Desktop Device**";
"LoginQRHelp2" = "Go to **Settings** > **Devices** > **Link Desktop Device**";
"LoginQRHelp3" = "Point your phone at this screen to confirm login";
"LoginQRCancel" = "Log in by phone Number";
"YourName" = "Your Name";
@ -394,6 +394,8 @@
"AuthSessionsViewBrowser" = "Browser";
"AuthSessionsViewLocationInfo" = "This location estimate is based on the IP address and may not always be accurate.";
"AuthSessionsLogOutApplications" = "Disconnect All Websites";
"AuthKeepSignedIn" = "Keep me signed in";
"AuthTitle" = "Telegram";
"ClearOtherWebSessionsHelp" = "You can log in on websites that support signing in with Telegram.";
"AreYouSureWebSessions" = "Are you sure you want to disconnect all websites where you logged in with Telegram?";
"AutodownloadSizeLimitUpTo" = "up to {limit}";
@ -421,10 +423,17 @@
"SettingsSendEnter" = "Send with Enter";
"SettingsSendCmdenter" = "Send with Cmd+Enter";
"SettingsSendCtrlenter" = "Send with Ctrl+Enter";
"SettingsSendEnterDescription" = "New line by Shift + Enter";
"SettingsSendPlusEnterDescription" = "New line by Enter";
"SettingsTimeFormat" = "Time Format";
"SettingsTimeFormat12" = "12-hour";
"SettingsTimeFormat24" = "24-hour";
"SettingsKeyboard" = "Keyboard";
"SettingsTray" = "Show Icon in Menu Bar";
"SettingsOfflineNotificationUnsupported" = "Not supported";
"TextSize" = "Message Font Size";
"ChatBackground" = "Chat Wallpaper";
"Theme" = "Theme";
"VoiceOverKeyboard" = "Keyboard";
"AccDescrStickers" = "Stickers";
"DoubleTapSetting" = "Quick Reaction";
"SuggestStickers" = "Sticker suggestions by emoji";
@ -625,10 +634,49 @@
"PollsSolutionTitle" = "Explanation";
"CreatePollExplanationInfo" = "Users will see this comment after choosing a wrong answer, good for educational purposes.";
"VoipGroupPersonalAccount" = "personal account";
"MenuStickers" = "Stickers and Emoji";
"MenuAnimations" = "Animations and Performance";
"MenuStars" = "My Stars";
"MenuSendGift" = "Send a Gift";
"MenuTelegramFaq" = "Telegram FAQ";
"MenuPrivacyPolicy" = "Privacy Policy";
"MenuAskText" = "Please note that Telegram Support is done by volunteers. We try to respond as quickly as possible, but it may take a while.\n\nPlease take a look at the Telegram FAQ: it has important troubleshooting tips and answers to most questions.";
"SettingsPerformanceSliderTitle" = "Animation Level";
"SettingsPerformanceSliderSubtitle" = "Choose the desired animations amount.";
"SettingsPerformanceSliderLow" = "Power Saving";
"SettingsPerformanceSliderMedium" = "Nice and Fast";
"SettingsPerformanceSliderCustom" = "Custom";
"SettingsPerformanceSliderHigh" = "Lots of Stuff";
"SettingsPerformanceInterfaceAnimations" = "Interface Animations";
"SettingsPerformanceStickers" = "Stickers and Emoji";
"SettingsPerformanceMediaAutoplay" = "Media Autoplay";
"SettingsPerformancePageTransitions" = "Page Transitions";
"SettingsPerformanceSending" = "Message Sending Animation";
"SettingsPerformanceMediaViewer" = "Media Viewer Animations";
"SettingsPerformanceComposer" = "Message Composer Animations";
"SettingsPerformanceContextAnimation" = "Context Menu Animation";
"SettingsPerformanceContextBlur" = "Context Menu Blur";
"SettingsPerformanceRightColumn" = "Right Column Animation";
"SettingsPerformanceThanos" = "Dust-effect deletion";
"SettingsPerformanceAnimatedEmoji" = "Allow Animated Emoji";
"SettingsPerformanceLoopStickers" = "Loop Animated Stickers";
"SettingsPerformanceReactionEffects" = "Reaction Effects";
"SettingsPerformanceStickerEffects" = "Sticker Effects";
"SettingsPerformanceAutoplayGif" = "Autoplay GIFs";
"SettingsPerformanceAutoplayVideo" = "Autoplay Videos";
"FavoriteStickers" = "Favorites";
"PremiumStickers" = "Premium Stickers";
"GroupStickers" = "Group Stickers";
"ErrorSendRestrictedStickersAll" = "Sorry, sending stickers is not allowed in this group.";
"ErrorPhoneNumberInvalid" = "Invalid phone number, please try again.";
"ErrorCodeInvalid" = "Invalid code, please try again.";
"ErrorIncorrectPassword" = "Invalid password, please try again.";
"ErrorPasswordFlood" = "Too many attempts, please try again later.";
"ErrorPhoneBanned" = "This phone number is banned.";
"ErrorFlood_one" = "Too many attempts, please try again in {hour} hour";
"ErrorFlood_other" = "Too many attempts, please try again in {hour} hours";
"ErrorUnexpected" = "Unexpected error";
"ErrorUnexpectedMessage" = "Unexpected error: {error}";
"NoStickers" = "No stickers yet";
"ClearRecentEmoji" = "Clear recent emoji?";
"TextFormatAddLinkTitle" = "Add Link";
@ -1343,8 +1391,8 @@
"GiftInfoConvertTitle" = "Convert Gift to Stars";
"GiftInfoConvertDescription1" = "Do you want to convert this gift from **{user}** to **{amount}**?";
"GiftInfoConvertDescription2" = "This action cannot be undone. This will permanently destroy the gift.";
"GiftInfoConvertDescriptionPeriod_one" = "Conversion is available for the next **{count} days**."
"GiftInfoConvertDescriptionPeriod_other" = "Conversion is available for the next **{count} days**."
"GiftInfoConvertDescriptionPeriod_one" = "Conversion is available for the next **{count} days**.";
"GiftInfoConvertDescriptionPeriod_other" = "Conversion is available for the next **{count} days**.";
"GiftInfoSaved" = "This gift is visible on your profile. {link}";
"GiftInfoSavedView" = "View >";
"GiftInfoHidden" = "This gift is hidden. Only you can see it.";
@ -1363,10 +1411,10 @@
"GiftAttributeModel" = "Model";
"GiftAttributeBackdrop" = "Backdrop";
"GiftAttributeSymbol" = "Symbol";
"GiftInfoOriginalInfo" = "Gifted to {user} on {date}."
"GiftInfoOriginalInfoSender" = "Gifted by {sender} to {user} on {date}."
"GiftInfoOriginalInfoText" = "Gifted to {user} on {date} with comment \"{text}\"."
"GiftInfoOriginalInfoTextSender" = "Gifted by {sender} to {user} on {date} with comment \"{text}\"."
"GiftInfoOriginalInfo" = "Gifted to {user} on {date}.";
"GiftInfoOriginalInfoSender" = "Gifted by {sender} to {user} on {date}.";
"GiftInfoOriginalInfoText" = "Gifted to {user} on {date} with comment \"{text}\".";
"GiftInfoOriginalInfoTextSender" = "Gifted by {sender} to {user} on {date} with comment \"{text}\".";
"GiftInfoStatus" = "Status";
"GiftInfoStatusNonUnique" = "Non-Unique";
"GiftInfoViewUpgraded" = "View Upgraded Gift";
@ -1399,7 +1447,7 @@
"ReceivedGift" = "Received Gift";
"SentGift" = "Sent Gift";
"StarGiftInfoDescriptionInbound" = "You can keep this gift in your Profile or convert it to {count} Stars. {link}";
"StarGiftInfoDescriptionOutgoing" = "{user} can keep this gift in Profile or convert it to {count} Stars. {link}"
"StarGiftInfoDescriptionOutgoing" = "{user} can keep this gift in Profile or convert it to {count} Stars. {link}";
"StarGiftInfoLinkCaption" = "More About Stars >";
"StarGiftDisplayOnMyPage" = "Display on on my page";
"StarGiftConvertTo" = "Convert to";
@ -1435,12 +1483,12 @@
"BotDownloadFileDescription" = "**{bot}** suggests you to download **{filename}**";
"BotDownloadFileButton" = "Download";
"PrivacyGifts" = "Gifts";
"PrivacyGiftsTitle" = "Who can display gifts on my profile?"
"PrivacyGiftsInfo" = "Choose whether gifts from specific senders need your approval before they're visible to others on your profile."
"PrivacyValueBots" = "Mini Apps"
"CustomShareGiftsInfo" = "You can add users or entire groups as exceptions that will override the settings above."
"StarsSubscribeBotText_one" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** star per month?"
"StarsSubscribeBotText_other" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** stars per month?"
"PrivacyGiftsTitle" = "Who can display gifts on my profile?";
"PrivacyGiftsInfo" = "Choose whether gifts from specific senders need your approval before they're visible to others on your profile.";
"PrivacyValueBots" = "Mini Apps";
"CustomShareGiftsInfo" = "You can add users or entire groups as exceptions that will override the settings above.";
"StarsSubscribeBotText_one" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** star per month?";
"StarsSubscribeBotText_other" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** stars per month?";
"StarsSubscribeBotButtonMonth" = "Subscribe for {amount} / month";
"AllChatsSearchContext" = "All Chats";
"PrivateChatsSearchContext" = "Private Chats";
@ -1450,9 +1498,9 @@
"FolderLinkTitleDescription" = "Anyone with this link can add {folder} folder and {chats} selected below.";
"FolderLinkTitleDescriptionChats_one" = "the chat";
"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}**?"
"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";
@ -1496,7 +1544,7 @@
"ActionUnsupportedDescription" = "Please, use one of our apps to complete this action.";
"LocationPermissionText" = "**{name}** requests access to set your **location**. You will be able to revoke this access in the profile page of **{name}**.";
"UnlockMoreSimilarBots" = "Show More Apps";
"MoreSimilarBotsText" = "Subscribe to **Telegram Premium** to unlock up to {count} similar apps."
"MoreSimilarBotsText" = "Subscribe to **Telegram Premium** to unlock up to {count} similar apps.";
"GiftWasNotFound" = "Gift was not found";
"ViewButtonRequestJoin" = "REQUEST TO JOIN";
"ViewButtonMessage" = "VIEW MESSAGE";
@ -1511,4 +1559,5 @@
"ViewButtonStory" = "VIEW STORY";
"ViewButtonBoost" = "BOOST";
"ViewButtonStickerset" = "VIEW STICKERS";
"ViewButtonGiftUnique" = "VIEW COLLECTIBLE";
"ViewButtonGiftUnique" = "VIEW COLLECTIBLE";
"AuthContinueOnThisLanguage" = "Continue in English";

View File

@ -1,4 +1,6 @@
const INITIAL_KEYS = [
import type { LangKey } from '../../types/language';
const INITIAL_KEYS: LangKey[] = [
'WrongNumber',
'SentAppCode',
'LoginJustSentSms',
@ -11,7 +13,7 @@ const INITIAL_KEYS = [
'LoginQRTitle',
'LoginQRHelp1',
'LoginQRHelp2',
'LoginQR2Help2',
'LoginQRHelp2',
'LoginQRHelp3',
'LoginQRCancel',
'YourName',
@ -21,6 +23,14 @@ const INITIAL_KEYS = [
'LoginSelectCountryTitle',
'CountryNone',
'PleaseEnterPassword',
'ErrorPhoneNumberInvalid',
'ErrorCodeInvalid',
'ErrorIncorrectPassword',
'ErrorPasswordFlood',
'ErrorPhoneBanned',
'ErrorFlood',
'ErrorUnexpected',
'ErrorUnexpectedMessage',
];
export default INITIAL_KEYS;

View File

@ -1,19 +1,22 @@
/* eslint-disable */
// This file is generated by dev/generateInitialLangFallback.ts. Do not edit it manually.
import type { LangPackStringValue } from '../../api/types';
import type { LangKey } from '../../types/language';
export default {
"WrongNumber": "Wrong number?",
"SentAppCode": "We've sent the code to the **Telegram** app on your other device.",
"LoginJustSentSms": "We've sent you a code via SMS. Please enter it above.",
"LoginHeaderPassword": "Enter Password",
"LoginEnterPasswordDescription": "You have Two-Step Verification enabled, so your account is protected with an additional password.",
"StartText": "Please confirm your country codenand enter your phone number.",
"StartText": "Please confirm your country code\nand enter your phone number.",
"LoginPhonePlaceholder": "Your phone number",
"LoginNext": "Next",
"LoginQRLogin": "Log in by QR Code",
"LoginQRTitle": "Log in to Telegram by QR Code",
"LoginQRHelp1": "Open Telegram on your phone",
"LoginQR2Help2": "Go to **Settings** > **Devices** > **Link Desktop Device**",
"LoginQRHelp2": "Go to **Settings** > **Devices** > **Link Desktop Device**",
"LoginQRHelp3": "Point your phone at this screen to confirm login",
"LoginQRCancel": "Log in by phone Number",
"YourName": "Your Name",
@ -22,5 +25,16 @@ export default {
"LoginRegisterLastNamePlaceholder": "Last Name",
"LoginSelectCountryTitle": "Country",
"CountryNone": "Country not found",
"PleaseEnterPassword": "Enter your new password"
} as Record<string, string>;
"PleaseEnterPassword": "Enter your new password",
"ErrorPhoneNumberInvalid": "Invalid phone number, please try again.",
"ErrorCodeInvalid": "Invalid code, please try again.",
"ErrorIncorrectPassword": "Invalid password, please try again.",
"ErrorPasswordFlood": "Too many attempts, please try again later.",
"ErrorPhoneBanned": "This phone number is banned.",
"ErrorFlood": {
"one": "Too many attempts, please try again in {hour} hour",
"other": "Too many attempts, please try again in {hour} hours"
},
"ErrorUnexpected": "Unexpected error",
"ErrorUnexpectedMessage": "Unexpected error: {error}"
} as Record<LangKey, LangPackStringValue>;

View File

@ -9,16 +9,16 @@ import type { GlobalState } from '../../global/types';
import { pick } from '../../util/iteratees';
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
import renderText from '../common/helpers/renderText';
import useHistoryBack from '../../hooks/useHistoryBack';
import useOldLang from '../../hooks/useOldLang';
import useLang from '../../hooks/useLang';
import Icon from '../common/icons/Icon';
import TrackingMonkey from '../common/TrackingMonkey';
import InputText from '../ui/InputText';
import Loading from '../ui/Loading';
type StateProps = Pick<GlobalState, 'authPhoneNumber' | 'authIsCodeViaApp' | 'authIsLoading' | 'authError'>;
type StateProps = Pick<GlobalState, 'authPhoneNumber' | 'authIsCodeViaApp' | 'authIsLoading' | 'authErrorKey'>;
const CODE_LENGTH = 5;
@ -26,15 +26,15 @@ const AuthCode: FC<StateProps> = ({
authPhoneNumber,
authIsCodeViaApp,
authIsLoading,
authError,
authErrorKey,
}) => {
const {
setAuthCode,
returnToAuthPhoneNumber,
clearAuthError,
clearAuthErrorKey,
} = getActions();
const lang = useOldLang();
const lang = useLang();
// eslint-disable-next-line no-null/no-null
const inputRef = useRef<HTMLInputElement>(null);
@ -54,8 +54,8 @@ const AuthCode: FC<StateProps> = ({
});
const onCodeChange = useCallback((e: FormEvent<HTMLInputElement>) => {
if (authError) {
clearAuthError();
if (authErrorKey) {
clearAuthErrorKey();
}
const { currentTarget: target } = e;
@ -82,7 +82,7 @@ const AuthCode: FC<StateProps> = ({
if (target.value.length === CODE_LENGTH) {
setAuthCode({ code: target.value });
}
}, [authError, clearAuthError, code, isTracking, setAuthCode]);
}, [authErrorKey, code, isTracking, setAuthCode]);
function handleReturnToAuthPhoneNumber() {
returnToAuthPhoneNumber();
@ -107,11 +107,14 @@ const AuthCode: FC<StateProps> = ({
title={lang('WrongNumber')}
aria-label={lang('WrongNumber')}
>
<i className="icon icon-edit" />
<Icon name="edit" />
</div>
</h1>
<p className="note">
{renderText(lang(authIsCodeViaApp ? 'SentAppCode' : 'Login.JustSentSms'), ['simple_markdown'])}
{lang(authIsCodeViaApp ? 'SentAppCode' : 'LoginJustSentSms', undefined, {
withNodes: true,
withMarkdown: true,
})}
</p>
<InputText
ref={inputRef}
@ -119,7 +122,7 @@ const AuthCode: FC<StateProps> = ({
label={lang('Code')}
onInput={onCodeChange}
value={code}
error={authError && lang(authError)}
error={authErrorKey && lang.withRegular(authErrorKey)}
autoComplete="off"
inputMode="numeric"
/>
@ -130,5 +133,5 @@ const AuthCode: FC<StateProps> = ({
};
export default memo(withGlobal(
(global): StateProps => pick(global, ['authPhoneNumber', 'authIsCodeViaApp', 'authIsLoading', 'authError']),
(global): StateProps => pick(global, ['authPhoneNumber', 'authIsCodeViaApp', 'authIsLoading', 'authErrorKey']),
)(AuthCode));

View File

@ -6,19 +6,19 @@ import type { GlobalState } from '../../global/types';
import { pick } from '../../util/iteratees';
import useOldLang from '../../hooks/useOldLang';
import useLang from '../../hooks/useLang';
import PasswordForm from '../common/PasswordForm';
import MonkeyPassword from '../common/PasswordMonkey';
type StateProps = Pick<GlobalState, 'authIsLoading' | 'authError' | 'authHint'>;
type StateProps = Pick<GlobalState, 'authIsLoading' | 'authErrorKey' | 'authHint'>;
const AuthPassword: FC<StateProps> = ({
authIsLoading, authError, authHint,
authIsLoading, authErrorKey, authHint,
}) => {
const { setAuthPassword, clearAuthError } = getActions();
const { setAuthPassword, clearAuthErrorKey } = getActions();
const lang = useOldLang();
const lang = useLang();
const [showPassword, setShowPassword] = useState(false);
const handleChangePasswordVisibility = useCallback((isVisible) => {
@ -33,11 +33,11 @@ const AuthPassword: FC<StateProps> = ({
<div id="auth-password-form" className="custom-scroll">
<div className="auth-form">
<MonkeyPassword isPasswordVisible={showPassword} />
<h1>{lang('Login.Header.Password')}</h1>
<p className="note">{lang('Login.EnterPasswordDescription')}</p>
<h1>{lang('LoginHeaderPassword')}</h1>
<p className="note">{lang('LoginEnterPasswordDescription')}</p>
<PasswordForm
clearError={clearAuthError}
error={authError && lang(authError)}
clearError={clearAuthErrorKey}
error={authErrorKey && lang.withRegular(authErrorKey)}
hint={authHint}
isLoading={authIsLoading}
isPasswordVisible={showPassword}
@ -50,5 +50,5 @@ const AuthPassword: FC<StateProps> = ({
};
export default memo(withGlobal(
(global): StateProps => pick(global, ['authIsLoading', 'authError', 'authHint']),
(global): StateProps => pick(global, ['authIsLoading', 'authErrorKey', 'authHint']),
)(AuthPassword));

View File

@ -18,8 +18,8 @@ import { IS_SAFARI, IS_TOUCH_ENV } from '../../util/windowEnvironment';
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
import useFlag from '../../hooks/useFlag';
import useOldLang from '../../hooks/useOldLang';
import useOldLangString from '../../hooks/useOldLangString';
import useLang from '../../hooks/useLang';
import useLangString from '../../hooks/useLangString';
import Button from '../ui/Button';
import Checkbox from '../ui/Checkbox';
@ -32,7 +32,7 @@ import monkeyPath from '../../assets/monkey.svg';
type StateProps = Pick<GlobalState, (
'connectionState' | 'authState' |
'authPhoneNumber' | 'authIsLoading' |
'authIsLoadingQrCode' | 'authError' |
'authIsLoadingQrCode' | 'authErrorKey' |
'authRememberMe' | 'authNearestCountry'
)> & {
language?: string;
@ -49,7 +49,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
authPhoneNumber,
authIsLoading,
authIsLoadingQrCode,
authError,
authErrorKey,
authRememberMe,
authNearestCountry,
phoneCodeList,
@ -60,18 +60,18 @@ const AuthPhoneNumber: FC<StateProps> = ({
setAuthRememberMe,
loadNearestCountry,
loadCountryList,
clearAuthError,
clearAuthErrorKey,
goToAuthQrCode,
setSettingOption,
} = getActions();
const lang = useOldLang();
const lang = useLang();
// eslint-disable-next-line no-null/no-null
const inputRef = useRef<HTMLInputElement>(null);
const suggestedLanguage = getSuggestedLanguage();
const isConnected = connectionState === 'connectionStateReady';
const continueText = useOldLangString(isConnected ? suggestedLanguage : undefined, 'ContinueOnThisLanguage', true);
const continueText = useLangString('AuthContinueOnThisLanguage', suggestedLanguage);
const [country, setCountry] = useState<ApiCountryCode | undefined>();
const [phoneNumber, setPhoneNumber] = useState<string | undefined>();
const [isTouched, setIsTouched] = useState(false);
@ -161,8 +161,8 @@ const AuthPhoneNumber: FC<StateProps> = ({
}, []);
const handlePhoneNumberChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
if (authError) {
clearAuthError();
if (authErrorKey) {
clearAuthErrorKey();
}
// This is for further screens. We delay it until user input to speed up the initial loading.
@ -186,7 +186,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
&& value.length - fullNumber.length > 1 && !isJustPastedRef.current
);
parseFullNumber(shouldFixSafariAutoComplete ? `${country!.countryCode} ${value}` : value);
}, [authError, clearAuthError, country, fullNumber, parseFullNumber]);
}, [authErrorKey, country, fullNumber, parseFullNumber]);
const handleKeepSessionChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setAuthRememberMe(e.target.checked);
@ -214,7 +214,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
<div id="auth-phone-number-form" className="custom-scroll">
<div className="auth-form">
<div id="logo" />
<h1>Telegram</h1>
<h1>{lang('AuthTitle')}</h1>
<p className="note">{lang('StartText')}</p>
<form className="form" action="" onSubmit={handleSubmit}>
<CountryCodeInput
@ -226,29 +226,29 @@ const AuthPhoneNumber: FC<StateProps> = ({
<InputText
ref={inputRef}
id="sign-in-phone-number"
label={lang('Login.PhonePlaceholder')}
label={lang('LoginPhonePlaceholder')}
value={fullNumber}
error={authError && lang(authError)}
error={authErrorKey && lang.withRegular(authErrorKey)}
inputMode="tel"
onChange={handlePhoneNumberChange}
onPaste={IS_SAFARI ? handlePaste : undefined}
/>
<Checkbox
id="sign-in-keep-session"
label="Keep me signed in"
label={lang('AuthKeepSignedIn')}
checked={Boolean(authRememberMe)}
onChange={handleKeepSessionChange}
/>
{canSubmit && (
isAuthReady ? (
<Button type="submit" ripple isLoading={authIsLoading}>{lang('Login.Next')}</Button>
<Button type="submit" ripple isLoading={authIsLoading}>{lang('LoginNext')}</Button>
) : (
<Loading />
)
)}
{isAuthReady && (
<Button isText ripple isLoading={authIsLoadingQrCode} onClick={handleGoToAuthQrCode}>
{lang('Login.QR.Login')}
{lang('LoginQRLogin')}
</Button>
)}
{suggestedLanguage && suggestedLanguage !== language && continueText && (
@ -274,7 +274,7 @@ export default memo(withGlobal(
'authPhoneNumber',
'authIsLoading',
'authIsLoadingQrCode',
'authError',
'authErrorKey',
'authRememberMe',
'authNearestCountry',
]),

View File

@ -1,4 +1,3 @@
import type { FC } from '../../lib/teact/teact';
import React, {
memo, useCallback, useEffect, useLayoutEffect, useRef,
} from '../../lib/teact/teact';
@ -11,14 +10,13 @@ import { disableStrict, enableStrict } from '../../lib/fasterdom/stricterdom';
import buildClassName from '../../util/buildClassName';
import { oldSetLanguage } from '../../util/oldLangProvider';
import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets';
import renderText from '../common/helpers/renderText';
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
import useAsync from '../../hooks/useAsync';
import useFlag from '../../hooks/useFlag';
import useLang from '../../hooks/useLang';
import useLangString from '../../hooks/useLangString';
import useMediaTransitionDeprecated from '../../hooks/useMediaTransitionDeprecated';
import useOldLang from '../../hooks/useOldLang';
import useOldLangString from '../../hooks/useOldLangString';
import AnimatedIcon from '../common/AnimatedIcon';
import Button from '../ui/Button';
@ -44,24 +42,24 @@ function ensureQrCodeStyling() {
return qrCodeStylingPromise;
}
const AuthCode: FC<StateProps> = ({
const AuthCode = ({
connectionState,
authState,
authQrCode,
language,
}) => {
}: StateProps) => {
const {
returnToAuthPhoneNumber,
setSettingOption,
} = getActions();
const suggestedLanguage = getSuggestedLanguage();
const lang = useOldLang();
const lang = useLang();
// eslint-disable-next-line no-null/no-null
const qrCodeRef = useRef<HTMLDivElement>(null);
const isConnected = connectionState === 'connectionStateReady';
const continueText = useOldLangString(isConnected ? suggestedLanguage : undefined, 'ContinueOnThisLanguage', true);
const continueText = useLangString('AuthContinueOnThisLanguage', suggestedLanguage);
const [isLoading, markIsLoading, unmarkIsLoading] = useFlag();
const [isQrMounted, markQrMounted, unmarkQrMounted] = useFlag();
@ -173,14 +171,14 @@ const AuthCode: FC<StateProps> = ({
</div>
{!isQrMounted && <div className="qr-loading"><Loading /></div>}
</div>
<h1>{lang('Login.QR.Title')}</h1>
<h1>{lang('LoginQRTitle')}</h1>
<ol>
<li><span>{lang('Login.QR.Help1')}</span></li>
<li><span>{renderText(lang('Login.QR2.Help2'), ['simple_markdown'])}</span></li>
<li><span>{lang('Login.QR.Help3')}</span></li>
<li><span>{lang('LoginQRHelp1')}</span></li>
<li><span>{lang('LoginQRHelp2', undefined, { withNodes: true, withMarkdown: true })}</span></li>
<li><span>{lang('LoginQRHelp3')}</span></li>
</ol>
{isAuthReady && (
<Button isText onClick={habdleReturnToAuthPhoneNumber}>{lang('Login.QR.Cancel')}</Button>
<Button isText onClick={habdleReturnToAuthPhoneNumber}>{lang('LoginQRCancel')}</Button>
)}
{suggestedLanguage && suggestedLanguage !== language && continueText && (
<Button isText isLoading={isLoading} onClick={handleLangChange}>{continueText}</Button>

View File

@ -7,35 +7,35 @@ import type { GlobalState } from '../../global/types';
import { pick } from '../../util/iteratees';
import useOldLang from '../../hooks/useOldLang';
import useLang from '../../hooks/useLang';
import AvatarEditable from '../ui/AvatarEditable';
import Button from '../ui/Button';
import InputText from '../ui/InputText';
type StateProps = Pick<GlobalState, 'authIsLoading' | 'authError'>;
type StateProps = Pick<GlobalState, 'authIsLoading' | 'authErrorKey'>;
const AuthRegister: FC<StateProps> = ({
authIsLoading, authError,
authIsLoading, authErrorKey,
}) => {
const { signUp, clearAuthError, uploadProfilePhoto } = getActions();
const { signUp, clearAuthErrorKey, uploadProfilePhoto } = getActions();
const lang = useOldLang();
const lang = useLang();
const [isButtonShown, setIsButtonShown] = useState(false);
const [croppedFile, setCroppedFile] = useState<File | undefined>();
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const handleFirstNameChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
if (authError) {
clearAuthError();
if (authErrorKey) {
clearAuthErrorKey();
}
const { target } = event;
setFirstName(target.value);
setIsButtonShown(target.value.length > 0);
}, [authError, clearAuthError]);
}, [authErrorKey]);
const handleLastNameChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const { target } = event;
@ -59,18 +59,18 @@ const AuthRegister: FC<StateProps> = ({
<form action="" method="post" onSubmit={handleSubmit}>
<AvatarEditable onChange={setCroppedFile} />
<h2>{lang('YourName')}</h2>
<p className="note">{lang('Login.Register.Desc')}</p>
<p className="note">{lang('LoginRegisterDesc')}</p>
<InputText
id="registration-first-name"
label={lang('Login.Register.FirstName.Placeholder')}
label={lang('LoginRegisterFirstNamePlaceholder')}
onChange={handleFirstNameChange}
value={firstName}
error={authError && lang(authError)}
error={authErrorKey && lang.withRegular(authErrorKey)}
autoComplete="given-name"
/>
<InputText
id="registration-last-name"
label={lang('Login.Register.LastName.Placeholder')}
label={lang('LoginRegisterLastNamePlaceholder')}
onChange={handleLastNameChange}
value={lastName}
autoComplete="family-name"
@ -85,5 +85,5 @@ const AuthRegister: FC<StateProps> = ({
};
export default memo(withGlobal(
(global): StateProps => pick(global, ['authIsLoading', 'authError']),
(global): StateProps => pick(global, ['authIsLoading', 'authErrorKey']),
)(AuthRegister));

View File

@ -12,7 +12,7 @@ import { isoToEmoji } from '../../util/emoji/emoji';
import { prepareSearchWordsForNeedle } from '../../util/searchWords';
import renderText from '../common/helpers/renderText';
import useOldLang from '../../hooks/useOldLang';
import useLang from '../../hooks/useLang';
import useSyncEffect from '../../hooks/useSyncEffect';
import DropdownMenu from '../ui/DropdownMenu';
@ -42,7 +42,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
onChange,
phoneCodeList,
}) => {
const lang = useOldLang();
const lang = useLang();
// eslint-disable-next-line no-null/no-null
const inputRef = useRef<HTMLInputElement>(null);
@ -120,7 +120,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
onInput={handleCodeInput}
onKeyDown={handleInputKeyDown}
/>
<label>{lang('Login.SelectCountry.Title')}</label>
<label>{lang('LoginSelectCountryTitle')}</label>
{isLoading ? (
<Spinner color="black" />
) : (
@ -154,7 +154,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
className="no-results"
disabled
>
<span>{lang('lng_country_none')}</span>
<span>{lang('CountryNone')}</span>
</MenuItem>
)}
</DropdownMenu>

View File

@ -8,7 +8,7 @@ import { AUTODOWNLOAD_FILESIZE_MB_LIMITS } from '../../../config';
import { pick } from '../../../util/iteratees';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useOldLang from '../../../hooks/useOldLang';
import useLang from '../../../hooks/useLang';
import Checkbox from '../../ui/Checkbox';
import RangeSlider from '../../ui/RangeSlider';
@ -53,7 +53,7 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
}) => {
const { setSettingOption } = getActions();
const lang = useOldLang();
const lang = useLang();
useHistoryBack({
isActive,
@ -61,7 +61,9 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
});
const renderFileSizeCallback = useCallback((value: number) => {
return lang('AutodownloadSizeLimitUpTo', lang('FileSize.MB', String(AUTODOWNLOAD_FILESIZE_MB_LIMITS[value]), 'i'));
return lang('AutodownloadSizeLimitUpTo', {
limit: lang('FileSizeMB', { count: AUTODOWNLOAD_FILESIZE_MB_LIMITS[value] }),
});
}, [lang]);
const handleFileSizeChange = useCallback((value: number) => {
@ -98,26 +100,26 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{title}</h4>
<Checkbox
label={lang('AutoDownloadSettings.Contacts')}
label={lang('AutoDownloadSettingsContacts')}
checked={canAutoLoadFromContacts}
// TODO rewrite to support `useCallback`
// eslint-disable-next-line react/jsx-no-bind
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}FromContacts`]: isChecked })}
/>
<Checkbox
label={lang('AutoDownloadSettings.PrivateChats')}
label={lang('AutoDownloadSettingsPrivateChats')}
checked={canAutoLoadInPrivateChats}
// eslint-disable-next-line react/jsx-no-bind
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InPrivateChats`]: isChecked })}
/>
<Checkbox
label={lang('AutoDownloadSettings.GroupChats')}
label={lang('AutoDownloadSettingsGroupChats')}
checked={canAutoLoadInGroups}
// eslint-disable-next-line react/jsx-no-bind
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InGroups`]: isChecked })}
/>
<Checkbox
label={lang('AutoDownloadSettings.Channels')}
label={lang('AutoDownloadSettingsChannels')}
checked={canAutoLoadInChannels}
// eslint-disable-next-line react/jsx-no-bind
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InChannels`]: isChecked })}
@ -147,7 +149,7 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
canAutoLoadVideoInChannels,
)}
{renderAutoDownloadBlock(
'Auto-download files', // Proper translation is not available yet
lang('AutoDownloadFilesTitle'),
'File',
canAutoLoadFileFromContacts,
canAutoLoadFileInPrivateChats,

View File

@ -17,7 +17,7 @@ import {
import useAppLayout from '../../../hooks/useAppLayout';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useOldLang from '../../../hooks/useOldLang';
import useLang from '../../../hooks/useLang';
import Checkbox from '../../ui/Checkbox';
import ListItem from '../../ui/ListItem';
@ -41,14 +41,6 @@ type StateProps =
shouldUseSystemTheme: boolean;
};
const TIME_FORMAT_OPTIONS: IRadioOption[] = [{
label: '12-hour',
value: '12h',
}, {
label: '24-hour',
value: '24h',
}];
const SettingsGeneral: FC<OwnProps & StateProps> = ({
isActive,
onScreenSelect,
@ -63,28 +55,36 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
setSettingOption,
} = getActions();
const lang = useOldLang();
const lang = useLang();
const { isMobile } = useAppLayout();
const isMobileDevice = isMobile && (IS_IOS || IS_ANDROID);
const timeFormatOptions: IRadioOption[] = [{
label: lang('SettingsTimeFormat12'),
value: '12h',
}, {
label: lang('SettingsTimeFormat24'),
value: '24h',
}];
const appearanceThemeOptions: IRadioOption[] = [{
label: lang('EmptyChat.Appearance.Light'),
label: lang('EmptyChatAppearanceLight'),
value: 'light',
}, {
label: lang('EmptyChat.Appearance.Dark'),
label: lang('EmptyChatAppearanceDark'),
value: 'dark',
}, {
label: lang('EmptyChat.Appearance.System'),
label: lang('EmptyChatAppearanceSystem'),
value: 'auto',
}];
const keyboardSendOptions = !isMobileDevice ? [
{ value: 'enter', label: lang('lng_settings_send_enter'), subLabel: 'New line by Shift + Enter' },
{ value: 'enter', label: lang('SettingsSendEnter'), subLabel: lang('SettingsSendEnterDescription') },
{
value: 'ctrl-enter',
label: lang(IS_MAC_OS || IS_IOS ? 'lng_settings_send_cmdenter' : 'lng_settings_send_ctrlenter'),
subLabel: 'New line by Enter',
label: lang(IS_MAC_OS || IS_IOS ? 'SettingsSendCmdenter' : 'SettingsSendCtrlenter'),
subLabel: lang('SettingsSendPlusEnterDescription'),
},
] : undefined;
@ -134,7 +134,7 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
return (
<div className="settings-content custom-scroll">
<div className="settings-item pt-3">
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('SETTINGS')}</h4>
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('Settings')}</h4>
<RangeSlider
label={lang('TextSize')}
@ -155,7 +155,7 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
{IS_ELECTRON && IS_WINDOWS && (
<Checkbox
label={lang('GeneralSettings.StatusBarItem')}
label={lang('SettingsTray')}
checked={Boolean(isTrayIconEnabled)}
onCheck={handleIsTrayIconEnabledChange}
/>
@ -176,11 +176,11 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
<div className="settings-item">
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
Time Format
{lang('SettingsTimeFormat')}
</h4>
<RadioGroup
name="timeformat"
options={TIME_FORMAT_OPTIONS}
options={timeFormatOptions}
selected={timeFormat}
onChange={handleTimeFormatChange}
/>
@ -188,7 +188,7 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
{keyboardSendOptions && (
<div className="settings-item">
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('VoiceOver.Keyboard')}</h4>
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>{lang('SettingsKeyboard')}</h4>
<RadioGroup
name="keyboard-send-settings"

View File

@ -163,7 +163,7 @@ const SettingsHeader: FC<OwnProps> = ({
return <h3>{oldLang('NeverAllow')}</h3>;
case SettingsScreens.Performance:
return <h3>{oldLang('Animations and Performance')}</h3>;
return <h3>{lang('MenuAnimations')}</h3>;
case SettingsScreens.ActiveSessions:
return <h3>{oldLang('SessionsTitle')}</h3>;

View File

@ -16,7 +16,6 @@ import useFlag from '../../../hooks/useFlag';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useLang from '../../../hooks/useLang';
import useLastCallback from '../../../hooks/useLastCallback';
import useOldLang from '../../../hooks/useOldLang';
import StarIcon from '../../common/icons/StarIcon';
import ChatExtra from '../../common/profile/ChatExtra';
@ -62,7 +61,6 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
const [isSupportDialogOpen, openSupportDialog, closeSupportDialog] = useFlag(false);
const lang = useLang();
const oldLang = useOldLang();
useEffect(() => {
if (currentUserId) {
@ -104,7 +102,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.General)}
>
{oldLang('Telegram.GeneralSettingsViewController')}
{lang('TelegramGeneralSettingsViewController')}
</ListItem>
<ListItem
icon="animations"
@ -112,7 +110,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.Performance)}
>
{oldLang('Animations and Performance')}
{lang('MenuAnimations')}
</ListItem>
<ListItem
icon="unmute"
@ -120,7 +118,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.Notifications)}
>
{oldLang('Notifications')}
{lang('Notifications')}
</ListItem>
<ListItem
icon="data"
@ -128,7 +126,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.DataStorage)}
>
{oldLang('DataSettings')}
{lang('DataSettings')}
</ListItem>
<ListItem
icon="lock"
@ -136,7 +134,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.Privacy)}
>
{oldLang('PrivacySettings')}
{lang('PrivacySettings')}
</ListItem>
<ListItem
icon="folder"
@ -144,7 +142,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.Folders)}
>
{oldLang('Filters')}
{lang('Filters')}
</ListItem>
<ListItem
icon="active-sessions"
@ -152,7 +150,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.ActiveSessions)}
>
{oldLang('SessionsTitle')}
{lang('SessionsTitle')}
{sessionCount > 0 && (<span className="settings-item__current-value">{sessionCount}</span>)}
</ListItem>
<ListItem
@ -161,8 +159,8 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.Language)}
>
{oldLang('Language')}
<span className="settings-item__current-value">{oldLang.langName}</span>
{lang('Language')}
<span className="settings-item__current-value">{lang.languageInfo.nativeName}</span>
</ListItem>
<ListItem
icon="stickers"
@ -170,7 +168,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onScreenSelect(SettingsScreens.Stickers)}
>
{oldLang('StickersName')}
{lang('MenuStickers')}
</ListItem>
</div>
<div className="settings-main-menu">
@ -181,7 +179,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => openPremiumModal()}
>
{oldLang('TelegramPremium')}
{lang('TelegramPremium')}
</ListItem>
)}
{shouldDisplayStars && (
@ -191,7 +189,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => openStarsBalanceModal({})}
>
{oldLang('MenuTelegramStars')}
{lang('MenuStars')}
{Boolean(starsBalance) && (
<span className="settings-item__current-value">
{formatStarsAmount(lang, starsBalance)}
@ -206,7 +204,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => openGiftRecipientPicker()}
>
{oldLang('SendAGift')}
{lang('MenuSendGift')}
</ListItem>
)}
</div>
@ -216,7 +214,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
narrow
onClick={openSupportDialog}
>
{oldLang('AskAQuestion')}
{lang('AskAQuestion')}
</ListItem>
<ListItem
icon="help"
@ -224,7 +222,7 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => openUrl({ url: FAQ_URL })}
>
{oldLang('TelegramFaq')}
{lang('MenuTelegramFaq')}
</ListItem>
<ListItem
icon="privacy-policy"
@ -232,14 +230,14 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
// eslint-disable-next-line react/jsx-no-bind
onClick={() => openUrl({ url: PRIVACY_URL })}
>
{oldLang('PrivacyPolicy')}
{lang('MenuPrivacyPolicy')}
</ListItem>
</div>
<ConfirmDialog
isOpen={isSupportDialogOpen}
confirmLabel={oldLang('lng_settings_ask_ok')}
title={oldLang('AskAQuestion')}
text={oldLang('lng_settings_ask_sure')}
confirmLabel={lang('OK')}
title={lang('AskAQuestion')}
textParts={lang('MenuAskText', undefined, { withNodes: true, renderTextFilters: ['br'] })}
confirmHandler={handleOpenSupport}
onClose={closeSupportDialog}
/>

View File

@ -10,7 +10,7 @@ import {
} from '../../../util/notifications';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useOldLang from '../../../hooks/useOldLang';
import useLang from '../../../hooks/useLang';
import useRunDebounced from '../../../hooks/useRunDebounced';
import Checkbox from '../../ui/Checkbox';
@ -139,7 +139,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
runDebounced(() => playNotifySound(undefined, volume));
}, [runDebounced, updateWebNotificationSettings]);
const lang = useOldLang();
const lang = useLang();
useHistoryBack({
isActive,
@ -150,27 +150,27 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
<div className="settings-content custom-scroll">
<div className="settings-item">
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
Web notifications
{lang('NotificationsWeb')}
</h4>
<Checkbox
label="Web notifications"
// eslint-disable-next-line max-len
subLabel={lang(hasWebNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
label={lang('NotificationsWeb')}
subLabel={lang(hasWebNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
checked={hasWebNotifications}
disabled={!areNotificationsSupported}
onChange={handleWebNotificationsChange}
/>
<Checkbox
label="Offline notifications"
label={lang('NotificationsOffline')}
disabled={!hasWebNotifications || !areOfflineNotificationsSupported}
// eslint-disable-next-line max-len
subLabel={areOfflineNotificationsSupported ? lang(hasPushNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled') : 'Not supported'}
subLabel={areOfflineNotificationsSupported
? lang(hasPushNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')
: lang('SettingsOfflineNotificationUnsupported')}
checked={hasPushNotifications}
onChange={handlePushNotificationsChange}
/>
<div className="settings-item-slider">
<RangeSlider
label="Sound"
label={lang('NotificationsSound')}
min={0}
max={10}
disabled={!areNotificationsSupported}
@ -186,16 +186,16 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
<Checkbox
label={lang('NotificationsForPrivateChats')}
// eslint-disable-next-line max-len
subLabel={lang(hasPrivateChatsNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
subLabel={lang(hasPrivateChatsNotifications
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
checked={hasPrivateChatsNotifications}
onChange={handlePrivateChatsNotificationsChange}
/>
<Checkbox
label={lang('MessagePreview')}
disabled={!hasPrivateChatsNotifications}
// eslint-disable-next-line max-len
subLabel={lang(hasPrivateChatsMessagePreview ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
subLabel={lang(hasPrivateChatsMessagePreview
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
checked={hasPrivateChatsMessagePreview}
onChange={handlePrivateChatsPreviewChange}
/>
@ -206,14 +206,14 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
<Checkbox
label={lang('NotificationsForGroups')}
subLabel={lang(hasGroupNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
subLabel={lang(hasGroupNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
checked={hasGroupNotifications}
onChange={handleGroupsNotificationsChange}
/>
<Checkbox
label={lang('MessagePreview')}
disabled={!hasGroupNotifications}
subLabel={lang(hasGroupMessagePreview ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
subLabel={lang(hasGroupMessagePreview ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
checked={hasGroupMessagePreview}
onChange={handleGroupsPreviewChange}
/>
@ -224,16 +224,14 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
<Checkbox
label={lang('NotificationsForChannels')}
// eslint-disable-next-line max-len
subLabel={lang(hasBroadcastNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
subLabel={lang(hasBroadcastNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
checked={hasBroadcastNotifications}
onChange={handleChannelsNotificationsChange}
/>
<Checkbox
label={lang('MessagePreview')}
disabled={!hasBroadcastNotifications}
// eslint-disable-next-line max-len
subLabel={lang(hasBroadcastMessagePreview ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
subLabel={lang(hasBroadcastMessagePreview ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
checked={hasBroadcastMessagePreview}
onChange={handleChannelsPreviewChange}
/>

View File

@ -4,6 +4,7 @@ import React, {
import { getActions, withGlobal } from '../../../global';
import type { AnimationLevel, PerformanceType, PerformanceTypeKey } from '../../../types';
import type { RegularLangKey } from '../../../types/language';
import {
ANIMATION_LEVEL_CUSTOM, ANIMATION_LEVEL_MAX, ANIMATION_LEVEL_MED, ANIMATION_LEVEL_MIN,
@ -18,15 +19,15 @@ import { areDeepEqual } from '../../../util/areDeepEqual';
import { IS_BACKDROP_BLUR_SUPPORTED, IS_SNAP_EFFECT_SUPPORTED } from '../../../util/windowEnvironment';
import useHistoryBack from '../../../hooks/useHistoryBack';
import useOldLang from '../../../hooks/useOldLang';
import useLang from '../../../hooks/useLang';
import Checkbox from '../../ui/Checkbox';
import RangeSlider from '../../ui/RangeSlider';
type PerformanceSection = [string, PerformanceOption[]];
type PerformanceSection = [RegularLangKey, PerformanceOption[]];
type PerformanceOption = {
key: PerformanceTypeKey;
label: string;
label: RegularLangKey;
disabled?: boolean;
};
@ -39,38 +40,38 @@ type StateProps = {
performanceSettings: PerformanceType;
};
const ANIMATION_LEVEL_OPTIONS = [
'Power Saving',
'Nice and Fast',
'Lots of Stuff',
const ANIMATION_LEVEL_OPTIONS: RegularLangKey[] = [
'SettingsPerformanceSliderLow',
'SettingsPerformanceSliderMedium',
'SettingsPerformanceSliderHigh',
];
const ANIMATION_LEVEL_CUSTOM_OPTIONS = [
'Power Saving',
'Custom',
'Lots of Stuff',
const ANIMATION_LEVEL_CUSTOM_OPTIONS: RegularLangKey[] = [
'SettingsPerformanceSliderLow',
'SettingsPerformanceSliderCustom',
'SettingsPerformanceSliderHigh',
];
const PERFORMANCE_OPTIONS: PerformanceSection[] = [
['LiteMode.Key.animations.Title', [
{ key: 'pageTransitions', label: 'Page Transitions' },
{ key: 'messageSendingAnimations', label: 'Message Sending Animation' },
{ key: 'mediaViewerAnimations', label: 'Media Viewer Animations' },
{ key: 'messageComposerAnimations', label: 'Message Composer Animations' },
{ key: 'contextMenuAnimations', label: 'Context Menu Animation' },
{ key: 'contextMenuBlur', label: 'Context Menu Blur', disabled: !IS_BACKDROP_BLUR_SUPPORTED },
{ key: 'rightColumnAnimations', label: 'Right Column Animation' },
{ key: 'snapEffect', label: 'Dust-effect deletion' },
['SettingsPerformanceInterfaceAnimations', [
{ key: 'pageTransitions', label: 'SettingsPerformancePageTransitions' },
{ key: 'messageSendingAnimations', label: 'SettingsPerformanceSending' },
{ key: 'mediaViewerAnimations', label: 'SettingsPerformanceMediaViewer' },
{ key: 'messageComposerAnimations', label: 'SettingsPerformanceComposer' },
{ key: 'contextMenuAnimations', label: 'SettingsPerformanceContextAnimation' },
{ key: 'contextMenuBlur', label: 'SettingsPerformanceContextBlur', disabled: !IS_BACKDROP_BLUR_SUPPORTED },
{ key: 'rightColumnAnimations', label: 'SettingsPerformanceRightColumn' },
{ key: 'snapEffect', label: 'SettingsPerformanceThanos' },
]],
['Stickers and Emoji', [
{ key: 'animatedEmoji', label: 'Allow Animated Emoji' },
{ key: 'loopAnimatedStickers', label: 'Loop Animated Stickers' },
{ key: 'reactionEffects', label: 'Reaction Effects' },
{ key: 'stickerEffects', label: 'Full-Screen Sticker and Emoji Effects' },
['SettingsPerformanceStickers', [
{ key: 'animatedEmoji', label: 'SettingsPerformanceAnimatedEmoji' },
{ key: 'loopAnimatedStickers', label: 'SettingsPerformanceLoopStickers' },
{ key: 'reactionEffects', label: 'SettingsPerformanceReactionEffects' },
{ key: 'stickerEffects', label: 'SettingsPerformanceStickerEffects' },
]],
['AutoplayMedia', [
{ key: 'autoplayGifs', label: 'AutoplayGIF' },
{ key: 'autoplayVideos', label: 'AutoplayVideo' },
['SettingsPerformanceMediaAutoplay', [
{ key: 'autoplayGifs', label: 'SettingsPerformanceAutoplayGif' },
{ key: 'autoplayVideos', label: 'SettingsPerformanceAutoplayVideo' },
]],
];
@ -89,7 +90,7 @@ function SettingsPerformance({
onBack: onReset,
});
const lang = useOldLang();
const lang = useLang();
const [sectionExpandedStates, setSectionExpandedStates] = useState<Record<number, boolean>>({});
const sectionCheckedStates = useMemo(() => {
@ -113,9 +114,14 @@ function SettingsPerformance({
return ANIMATION_LEVEL_CUSTOM;
}, [performanceSettings]);
const animationLevelOptions = animationLevelState === ANIMATION_LEVEL_CUSTOM
? ANIMATION_LEVEL_CUSTOM_OPTIONS
: ANIMATION_LEVEL_OPTIONS;
const animationLevelOptions = useMemo(() => {
const options = animationLevelState === ANIMATION_LEVEL_CUSTOM
? ANIMATION_LEVEL_CUSTOM_OPTIONS
: ANIMATION_LEVEL_OPTIONS;
return options.map((option) => lang(option));
}, [animationLevelState, lang]);
const handleToggleSection = useCallback((e: React.MouseEvent, index?: string) => {
e.preventDefault();
@ -161,10 +167,10 @@ function SettingsPerformance({
<div className="settings-content custom-scroll">
<div className="settings-item">
<h4 className="settings-item-header" dir={lang.isRtl ? 'rtl' : undefined}>
Animation Level
{lang('SettingsPerformanceSliderTitle')}
</h4>
<p className="settings-item-description" dir={lang.isRtl ? 'rtl' : undefined}>
Choose the desired animations amount.
{lang('SettingsPerformanceSliderSubtitle')}
</p>
<RangeSlider

View File

@ -323,7 +323,6 @@ 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_CODE = 'en';
export const OLD_DEFAULT_LANG_PACK = 'android';
export const LANG_PACKS = ['android', 'ios', 'tdesktop', 'macos'] as const;
export const FEEDBACK_URL = 'https://bugs.telegram.org/?tag_ids=41&sort=time';
export const FAQ_URL = 'https://telegram.org/faq';

View File

@ -69,7 +69,7 @@ addActionHandler('setAuthPhoneNumber', (global, actions, payload): ActionReturnT
return {
...global,
authIsLoading: true,
authError: undefined,
authErrorKey: undefined,
};
});
@ -81,7 +81,7 @@ addActionHandler('setAuthCode', (global, actions, payload): ActionReturnType =>
return {
...global,
authIsLoading: true,
authError: undefined,
authErrorKey: undefined,
};
});
@ -93,7 +93,7 @@ addActionHandler('setAuthPassword', (global, actions, payload): ActionReturnType
return {
...global,
authIsLoading: true,
authError: undefined,
authErrorKey: undefined,
};
});
@ -124,7 +124,7 @@ addActionHandler('signUp', (global, actions, payload): ActionReturnType => {
return {
...global,
authIsLoading: true,
authError: undefined,
authErrorKey: undefined,
};
});
@ -133,7 +133,7 @@ addActionHandler('returnToAuthPhoneNumber', (global): ActionReturnType => {
return {
...global,
authError: undefined,
authErrorKey: undefined,
};
});
@ -143,7 +143,7 @@ addActionHandler('goToAuthQrCode', (global): ActionReturnType => {
return {
...global,
authIsLoadingQrCode: true,
authError: undefined,
authErrorKey: undefined,
};
});

View File

@ -180,10 +180,9 @@ function onUpdateAuthorizationState<T extends GlobalState>(global: T, update: Ap
}
function onUpdateAuthorizationError<T extends GlobalState>(global: T, update: ApiUpdateAuthorizationError) {
global = getGlobal();
global = {
...global,
authError: update.message,
authErrorKey: update.errorKey,
};
setGlobal(global);
}

View File

@ -217,10 +217,10 @@ addActionHandler('setAuthRememberMe', (global, actions, payload): ActionReturnTy
};
});
addActionHandler('clearAuthError', (global): ActionReturnType => {
addActionHandler('clearAuthErrorKey', (global): ActionReturnType => {
return {
...global,
authError: undefined,
authErrorKey: undefined,
};
});

View File

@ -116,7 +116,7 @@ export interface ActionPayloads {
};
returnToAuthPhoneNumber: undefined;
setAuthRememberMe: boolean;
clearAuthError: undefined;
clearAuthErrorKey: undefined;
uploadProfilePhoto: {
file: File;
isFallback?: boolean;

View File

@ -68,6 +68,7 @@ import type {
TopicsInfo,
WebPageMediaSize,
} from '../../types';
import type { RegularLangFnParameters } from '../../util/localization';
import type { TabState } from './tabState';
export type GlobalState = {
@ -145,7 +146,7 @@ export type GlobalState = {
authPhoneNumber?: string;
authIsLoading?: boolean;
authIsLoadingQrCode?: boolean;
authError?: string;
authErrorKey?: RegularLangFnParameters;
authRememberMe?: boolean;
authNearestCountry?: string;
authIsCodeViaApp?: boolean;

View File

@ -0,0 +1,30 @@
import { useEffect, useState } from '../lib/teact/teact';
import type { RegularLangKey } from '../types/language';
import { LANG_PACK } from '../config';
import { callApi } from '../api/gramjs';
import useLastCallback from './useLastCallback';
export default function useLangString(key: RegularLangKey, langCode?: string) {
const [value, setValue] = useState<string | undefined>(undefined);
const fetchLangString = useLastCallback(async () => {
if (!langCode) return undefined;
const result = await callApi('fetchLangStrings', {
langCode,
langPack: LANG_PACK,
keys: [key],
});
const langString = result?.strings[0];
if (!langString || typeof langString !== 'string') return undefined;
return langString;
});
useEffect(() => {
fetchLangString().then(setValue);
}, [key, langCode]);
return value;
}

View File

@ -1,24 +0,0 @@
import * as langProvider from '../util/oldLangProvider';
import useAsync from './useAsync';
/**
* @deprecated Migrate to `useLang`, while using needed key inside initial fallback
*/
const useOldLangString = (
langCode: string | undefined,
key: string,
shouldIgnoreSameValue = false,
): string | undefined => {
const defaultValue = shouldIgnoreSameValue ? undefined : key;
const { result } = useAsync(() => {
if (langCode) {
return langProvider.getTranslationForLangString(langCode, key);
}
return Promise.resolve();
}, [langCode, key], defaultValue);
return result || defaultValue;
};
export default useOldLangString;

View File

@ -142,7 +142,7 @@ export interface LangPair {
'LoginQRLogin': undefined;
'LoginQRTitle': undefined;
'LoginQRHelp1': undefined;
'LoginQR2Help2': undefined;
'LoginQRHelp2': undefined;
'LoginQRHelp3': undefined;
'LoginQRCancel': undefined;
'YourName': undefined;
@ -344,6 +344,8 @@ export interface LangPair {
'AuthSessionsViewBrowser': undefined;
'AuthSessionsViewLocationInfo': undefined;
'AuthSessionsLogOutApplications': undefined;
'AuthKeepSignedIn': undefined;
'AuthTitle': undefined;
'ClearOtherWebSessionsHelp': undefined;
'AreYouSureWebSessions': undefined;
'AutoDownloadMaxFileSize': undefined;
@ -369,10 +371,17 @@ export interface LangPair {
'SettingsSendEnter': undefined;
'SettingsSendCmdenter': undefined;
'SettingsSendCtrlenter': undefined;
'SettingsSendEnterDescription': undefined;
'SettingsSendPlusEnterDescription': undefined;
'SettingsTimeFormat': undefined;
'SettingsTimeFormat12': undefined;
'SettingsTimeFormat24': undefined;
'SettingsKeyboard': undefined;
'SettingsTray': undefined;
'SettingsOfflineNotificationUnsupported': undefined;
'TextSize': undefined;
'ChatBackground': undefined;
'Theme': undefined;
'VoiceOverKeyboard': undefined;
'AccDescrStickers': undefined;
'DoubleTapSetting': undefined;
'SuggestStickers': undefined;
@ -544,10 +553,46 @@ export interface LangPair {
'PollsSolutionTitle': undefined;
'CreatePollExplanationInfo': undefined;
'VoipGroupPersonalAccount': undefined;
'MenuStickers': undefined;
'MenuAnimations': undefined;
'MenuStars': undefined;
'MenuSendGift': undefined;
'MenuTelegramFaq': undefined;
'MenuPrivacyPolicy': undefined;
'MenuAskText': undefined;
'SettingsPerformanceSliderTitle': undefined;
'SettingsPerformanceSliderSubtitle': undefined;
'SettingsPerformanceSliderLow': undefined;
'SettingsPerformanceSliderMedium': undefined;
'SettingsPerformanceSliderCustom': undefined;
'SettingsPerformanceSliderHigh': undefined;
'SettingsPerformanceInterfaceAnimations': undefined;
'SettingsPerformanceStickers': undefined;
'SettingsPerformanceMediaAutoplay': undefined;
'SettingsPerformancePageTransitions': undefined;
'SettingsPerformanceSending': undefined;
'SettingsPerformanceMediaViewer': undefined;
'SettingsPerformanceComposer': undefined;
'SettingsPerformanceContextAnimation': undefined;
'SettingsPerformanceContextBlur': undefined;
'SettingsPerformanceRightColumn': undefined;
'SettingsPerformanceThanos': undefined;
'SettingsPerformanceAnimatedEmoji': undefined;
'SettingsPerformanceLoopStickers': undefined;
'SettingsPerformanceReactionEffects': undefined;
'SettingsPerformanceStickerEffects': undefined;
'SettingsPerformanceAutoplayGif': undefined;
'SettingsPerformanceAutoplayVideo': undefined;
'FavoriteStickers': undefined;
'PremiumStickers': undefined;
'GroupStickers': undefined;
'ErrorSendRestrictedStickersAll': undefined;
'ErrorPhoneNumberInvalid': undefined;
'ErrorCodeInvalid': undefined;
'ErrorIncorrectPassword': undefined;
'ErrorPasswordFlood': undefined;
'ErrorPhoneBanned': undefined;
'ErrorUnexpected': undefined;
'NoStickers': undefined;
'ClearRecentEmoji': undefined;
'TextFormatAddLinkTitle': undefined;
@ -994,7 +1039,6 @@ export interface LangPair {
'SettingsAnimationsHigh': undefined;
'Settings12HourFormat': undefined;
'Settings24HourFormat': undefined;
'SettingsSendEnterDescription': undefined;
'SettingsSendCtrlEnterDescription': undefined;
'AriaMoreButton': undefined;
'RecoveryEmailCode': undefined;
@ -1094,7 +1138,6 @@ export interface LangPair {
'HideCaption': undefined;
'ChangeRecipient': undefined;
'DragToSortAria': undefined;
'SettingsTimeFormat': undefined;
'MenuReportBug': undefined;
'MenuBetaChangelog': undefined;
'MenuSwitchToK': undefined;
@ -1239,6 +1282,7 @@ export interface LangPair {
'ViewButtonBoost': undefined;
'ViewButtonStickerset': undefined;
'ViewButtonGiftUnique': undefined;
'AuthContinueOnThisLanguage': undefined;
}
export interface LangPairWithVariables<V extends unknown = LangVariable> {
@ -1404,6 +1448,9 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
'SlowModeHint': {
'time': V;
};
'ErrorUnexpectedMessage': {
'error': V;
};
'EditedDate': {
'date': V;
};
@ -1761,6 +1808,9 @@ export interface LangPairPluralWithVariables<V extends unknown = LangVariable> {
'PreviewSenderSendFile': {
'count': V;
};
'ErrorFlood': {
'hour': V;
};
'PinnedMessageTitle': {
'index': V;
};

View File

@ -1,7 +1,7 @@
import { DEBUG } from '../../config';
export default function readStrings(data: string): Record<string, string> {
const lines = data.split(/;?\r?\n/);
const lines = data.split(/;\r?\n?/);
const result: Record<string, string> = {};
for (const line of lines) {
if (!line.startsWith('"')) continue;

View File

@ -1,33 +0,0 @@
/* eslint-disable max-len */
import type { ApiOldLangPack } from '../api/types';
export const fallbackLangPackInitial = {
WrongNumber: 'Wrong number?',
SentAppCode: 'We\'ve sent the code to the **Telegram** app on your other device.',
'Login.JustSentSms': 'We have sent you a code via SMS. Please enter it above.',
'Login.Header.Password': 'Enter Password',
'Login.EnterPasswordDescription': 'You have Two-Step Verification enabled, so your account is protected with an additional password.',
StartText: 'Please confirm your country code and enter your phone number.',
'Login.PhonePlaceholder': 'Your phone number',
'Login.Next': 'Next',
'Login.QR.Login': 'Log in by QR Code',
'Login.QR.Title': 'Log in to Telegram by QR Code',
'Login.QR.Help1': 'Open Telegram on your phone',
'Login.QR.Help2': 'Go to **Settings** > **Devices** > **Link Desktop Device**',
'Login.QR2.Help2': 'Go to **Settings** → **Devices** → **Link Desktop Device**',
'Login.QR.Help3': 'Point your phone at this screen to confirm login',
'Login.QR.Cancel': 'Log in by phone Number',
YourName: 'Your Name',
'Login.Register.Desc': 'Enter your name and add a profile picture.',
'Login.Register.FirstName.Placeholder': 'First Name',
'Login.Register.LastName.Placeholder': 'Last Name',
'Login.SelectCountry.Title': 'Country',
lng_country_none: 'Country not found',
PleaseEnterPassword: 'Enter your new password',
PHONE_NUMBER_INVALID: 'Invalid phone number',
PHONE_CODE_INVALID: 'Invalid code',
PASSWORD_HASH_INVALID: 'Incorrect password',
PHONE_PASSWORD_FLOOD: 'Limit exceeded. Please try again later.',
PHONE_NUMBER_BANNED: 'This phone number is banned.',
} as ApiOldLangPack;

View File

@ -320,10 +320,17 @@ function createTranslationFn(): LangFn {
}
return processTranslation(key, variables as Record<string, LangVariable>, options);
});
fn.withRegular = (({ key, variables, options }: RegularLangFnParameters) => {
return processTranslation(key, variables, options);
});
fn.withAdvanced = (({ key, variables, options }: AdvancedLangFnParameters) => {
return processTranslationAdvanced(key, variables, options);
});
fn.region = (code: string) => formatters?.region.of(code);
fn.conjunction = (list: string[]) => formatters?.conjunction.format(list) || list.join(', ');
fn.disjunction = (list: string[]) => formatters?.disjunction.format(list) || list.join(', ');
fn.number = (value: number) => formatters?.number.format(value) || String(value);
fn.languageInfo = language!;
return fn;
}

View File

@ -1,6 +1,7 @@
import type { TeactNode } from '../../lib/teact/teact';
import type {
ApiLanguage,
LangPackStringValue,
LangPackStringValueDeleted,
LangPackStringValuePlural,
@ -143,6 +144,8 @@ export type LangFn = {
): TeactNode;
with: (params: LangFnParameters) => TeactNode;
withRegular: (params: RegularLangFnParameters) => string;
withAdvanced: (params: AdvancedLangFnParameters) => TeactNode;
region: (code: string) => string | undefined;
conjunction: (list: string[]) => string;
disjunction: (list: string[]) => string;
@ -150,6 +153,7 @@ export type LangFn = {
isRtl?: boolean;
code: string;
pluralCode: string;
languageInfo: ApiLanguage;
};
type ListFormat = Pick<Intl.ListFormat, 'format'>;

View File

@ -4,12 +4,11 @@ import type { ApiOldLangPack, ApiOldLangString } from '../api/types';
import type { LangCode, TimeFormat } from '../types';
import {
DEFAULT_LANG_CODE, LANG_CACHE_NAME, LANG_PACKS, OLD_DEFAULT_LANG_PACK,
DEFAULT_LANG_CODE, LANG_CACHE_NAME, LANG_PACKS,
} from '../config';
import { callApi } from '../api/gramjs';
import * as cacheApi from './cacheApi';
import { createCallbackManager } from './callbacks';
import { fallbackLangPackInitial } from './fallbackLangPackInitial';
import { loadAndChangeLanguage } from './localization';
import { formatInteger } from './textFormat';
@ -129,7 +128,7 @@ function createLangFn() {
void importFallbackLangPack();
}
const langString = langPack?.[key] || fallbackLangPack?.[key] || fallbackLangPackInitial[key];
const langString = langPack?.[key] || fallbackLangPack?.[key];
if (!langString) {
return key;
}
@ -151,23 +150,6 @@ export function getTranslationFn(): LangFn {
return translationFn;
}
export async function getTranslationForLangString(langCode: string, key: string) {
let translateString: ApiOldLangString | undefined;
const cachedValue = await cacheApi.fetch(
LANG_CACHE_NAME,
`${OLD_DEFAULT_LANG_PACK}_${langCode}_${key}`,
cacheApi.Type.Json,
);
if (cachedValue) {
translateString = cachedValue.value;
} else {
translateString = await fetchRemoteString(OLD_DEFAULT_LANG_PACK, langCode, key);
}
return processTranslation(translateString, key);
}
/**
* @deprecated Migrate to `changeLanguage` in `util/localization.ts` instead
*/
@ -246,28 +228,6 @@ async function fetchRemote(langCode: string): Promise<ApiOldLangPack | undefined
return undefined;
}
async function fetchRemoteString(
remoteLangPack: typeof LANG_PACKS[number], langCode: string, key: string,
): Promise<ApiOldLangString | undefined> {
const remote = await callApi('oldFetchLangStrings', {
langPack: remoteLangPack,
langCode,
keys: [key],
});
if (remote?.length) {
const wrappedString = JSON.stringify({
value: remote[0],
});
await cacheApi.save(LANG_CACHE_NAME, `${remoteLangPack}_${langCode}_${key}`, wrappedString);
return remote[0];
}
return undefined;
}
function getPluralOption(amount: number) {
const langCode = currentLangCode || DEFAULT_LANG_CODE;
const optionIndex = PLURAL_RULES[langCode as keyof typeof PLURAL_RULES]