From 92b79ce62a7461f563fd1823a6cd17ec9788fc5d Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Wed, 30 Jun 2021 00:23:11 +0300 Subject: [PATCH] Localization: Fallback to English when missing translation and while loading --- src/modules/actions/ui/initial.ts | 2 +- src/util/fallbackLangPack.ts | 1704 +++++++++++++++++++++++++++++ src/util/langProvider.ts | 44 +- 3 files changed, 1736 insertions(+), 14 deletions(-) create mode 100644 src/util/fallbackLangPack.ts diff --git a/src/modules/actions/ui/initial.ts b/src/modules/actions/ui/initial.ts index c034d712e..0d52a5811 100644 --- a/src/modules/actions/ui/initial.ts +++ b/src/modules/actions/ui/initial.ts @@ -14,7 +14,7 @@ addReducer('init', (global) => { const { animationLevel, messageTextSize, language } = global.settings.byKey; const theme = selectTheme(global); - setLanguage(language); + setLanguage(language, undefined, true); document.documentElement.style.setProperty('--message-text-size', `${messageTextSize}px`); document.body.classList.add('initial'); diff --git a/src/util/fallbackLangPack.ts b/src/util/fallbackLangPack.ts new file mode 100644 index 000000000..fa59f3a45 --- /dev/null +++ b/src/util/fallbackLangPack.ts @@ -0,0 +1,1704 @@ +/* eslint-disable max-len */ + +import { ApiLangPack } from '../api/types'; + +export default { + Search: { + key: 'Search', + value: 'Search', + }, + SavedMessages: { + key: 'SavedMessages', + value: 'Saved Messages', + }, + ArchivedChats: { + key: 'ArchivedChats', + value: 'Archived Chats', + }, + Contacts: { + key: 'Contacts', + value: 'Contacts', + }, + Settings: { + key: 'Settings', + value: 'Settings', + }, + lng_menu_night_mode: { + key: 'lng_menu_night_mode', + value: 'Night Mode', + }, + lng_settings_enable_night_theme: { + key: 'lng_settings_enable_night_theme', + value: 'Enable night mode', + }, + 'Appearance.Animations': { + key: 'Appearance.Animations', + value: 'ANIMATIONS', + }, + TelegramFeatures: { + key: 'TelegramFeatures', + value: 'Telegram Features', + }, + AccDescrOpenMenu2: { + key: 'AccDescrOpenMenu2', + value: 'Open menu', + }, + NewMessageTitle: { + key: 'NewMessageTitle', + value: 'New Message', + }, + NewChannel: { + key: 'NewChannel', + value: 'New Channel', + }, + NewGroup: { + key: 'NewGroup', + value: 'New Group', + }, + 'Common.Close': { + key: 'Common.Close', + value: 'Close', + }, + FilterAllChats: { + key: 'FilterAllChats', + value: 'All Chats', + }, + MarkAsUnread: { + key: 'MarkAsUnread', + value: 'Mark as unread', + }, + UnpinFromTop: { + key: 'UnpinFromTop', + value: 'Unpin from top', + }, + 'ChatList.Mute': { + key: 'ChatList.Mute', + value: 'Mute', + }, + Archive: { + key: 'Archive', + value: 'Archive', + }, + Delete: { + key: 'Delete', + value: 'Delete', + }, + DeleteChat: { + key: 'DeleteChat', + value: 'Delete and exit', + }, + FromYou: { + key: 'FromYou', + value: 'You', + }, + formatDateSchedule: { + key: 'formatDateSchedule', + value: 'MMM d', + }, + June: { + key: 'June', + value: 'June', + }, + 'Month.GenJune': { + key: 'Month.GenJune', + value: 'June', + }, + 'Month.ShortJune': { + key: 'Month.ShortJune', + value: 'Jun', + }, + MarkAsRead: { + key: 'MarkAsRead', + value: 'Mark as read', + }, + PinToTop: { + key: 'PinToTop', + value: 'Pin to top', + }, + 'ChatList.Unmute': { + key: 'ChatList.Unmute', + value: 'Unmute', + }, + 'Group.LeaveGroup': { + key: 'Group.LeaveGroup', + value: 'Leave Group', + }, + LeaveChannel: { + key: 'LeaveChannel', + value: 'Leave Channel', + }, + AttachPhoto: { + key: 'AttachPhoto', + value: 'Photo', + }, + UnreadMessages: { + key: 'UnreadMessages', + value: 'Unread Messages', + }, + 'Weekday.Today': { + key: 'Weekday.Today', + value: 'Today', + }, + Message: { + key: 'Message', + value: 'Message', + }, + AccDescrVoiceMessage: { + key: 'AccDescrVoiceMessage', + value: 'Record voice message', + }, + AccDescrPageDown: { + key: 'AccDescrPageDown', + value: 'Go to bottom', + }, + chatDate: { + key: 'chatDate', + value: 'MMMM d', + }, + January: { + key: 'January', + value: 'January', + }, + 'Month.GenJanuary': { + key: 'Month.GenJanuary', + value: 'January', + }, + 'Month.ShortJanuary': { + key: 'Month.ShortJanuary', + value: 'Jan', + }, + February: { + key: 'February', + value: 'February', + }, + 'Month.GenFebruary': { + key: 'Month.GenFebruary', + value: 'February', + }, + 'Month.ShortFebruary': { + key: 'Month.ShortFebruary', + value: 'Feb', + }, + March: { + key: 'March', + value: 'March', + }, + 'Month.GenMarch': { + key: 'Month.GenMarch', + value: 'March', + }, + 'Month.ShortMarch': { + key: 'Month.ShortMarch', + value: 'Mar', + }, + April: { + key: 'April', + value: 'April', + }, + 'Month.GenApril': { + key: 'Month.GenApril', + value: 'April', + }, + 'Month.ShortApril': { + key: 'Month.ShortApril', + value: 'Apr', + }, + May: { + key: 'May', + value: 'May', + }, + 'Month.GenMay': { + key: 'Month.GenMay', + value: 'May', + }, + 'Month.ShortMay': { + key: 'Month.ShortMay', + value: 'May', + }, + 'Weekday.Thursday': { + key: 'Weekday.Thursday', + value: 'Thursday', + }, + EditedMessage: { + key: 'EditedMessage', + value: 'edited', + }, + chatFullDate: { + key: 'chatFullDate', + value: 'MMMM d, yyyy', + }, + September: { + key: 'September', + value: 'September', + }, + 'Month.GenSeptember': { + key: 'Month.GenSeptember', + value: 'September', + }, + 'Month.ShortSeptember': { + key: 'Month.ShortSeptember', + value: 'Sep', + }, + November: { + key: 'November', + value: 'November', + }, + 'Month.GenNovember': { + key: 'Month.GenNovember', + value: 'November', + }, + 'Month.ShortNovember': { + key: 'Month.ShortNovember', + value: 'Nov', + }, + December: { + key: 'December', + value: 'December', + }, + 'Month.GenDecember': { + key: 'Month.GenDecember', + value: 'December', + }, + 'Month.ShortDecember': { + key: 'Month.ShortDecember', + value: 'Dec', + }, + SearchAllChatsShort: { + key: 'SearchAllChatsShort', + value: 'Chats', + }, + SharedMediaTab2: { + key: 'SharedMediaTab2', + value: 'Media', + }, + SharedLinksTab2: { + key: 'SharedLinksTab2', + value: 'Links', + }, + SharedFilesTab2: { + key: 'SharedFilesTab2', + value: 'Files', + }, + SharedMusicTab2: { + key: 'SharedMusicTab2', + value: 'Music', + }, + SharedVoiceTab2: { + key: 'SharedVoiceTab2', + value: 'Voice', + }, + 'PreviewSender.SendPhoto': { + key: 'PreviewSender.SendPhoto', + oneValue: 'Send Photo', + otherValue: 'Send %d Photos', + }, + Send: { + key: 'Send', + value: 'Send', + }, + 'Preview.Dragging.AddItems': { + key: 'Preview.Dragging.AddItems', + oneValue: 'Add Item', + otherValue: 'Add Items', + }, + Caption: { + key: 'Caption', + value: 'Caption', + }, + formatterMonthYear: { + key: 'formatterMonthYear', + value: 'MMMM yyyy', + }, + 'Weekday.ShortMonday': { + key: 'Weekday.ShortMonday', + value: 'Mon', + }, + 'Weekday.ShortSaturday': { + key: 'Weekday.ShortSaturday', + value: 'Sat', + }, + 'Weekday.ShortFriday': { + key: 'Weekday.ShortFriday', + value: 'Fri', + }, + 'Conversation.ScheduleMessage.SendOn': { + key: 'Conversation.ScheduleMessage.SendOn', + value: 'Send on %@ at %@', + }, + 'Weekday.ShortThursday': { + key: 'Weekday.ShortThursday', + value: 'Thu', + }, + 'Weekday.ShortWednesday': { + key: 'Weekday.ShortWednesday', + value: 'Wed', + }, + 'Weekday.ShortTuesday': { + key: 'Weekday.ShortTuesday', + value: 'Tue', + }, + 'LastSeen.MinutesAgo': { + key: 'LastSeen.MinutesAgo', + oneValue: 'last seen 1 minute ago', + otherValue: 'last seen %@ minutes ago', + }, + 'AttachmentMenu.PhotoOrVideo': { + key: 'AttachmentMenu.PhotoOrVideo', + value: 'Photo or Video', + }, + AttachDocument: { + key: 'AttachDocument', + value: 'File', + }, + SendWithoutSound: { + key: 'SendWithoutSound', + value: 'Send without sound', + }, + ScheduleMessage: { + key: 'ScheduleMessage', + value: 'Schedule message', + }, + 'Chat.PanelUnpinAllMessages': { + key: 'Chat.PanelUnpinAllMessages', + value: 'Unpin All Messages', + }, + 'Chat.UnpinAllMessagesConfirmation': { + key: 'Chat.UnpinAllMessagesConfirmation', + oneValue: 'Do you want to unpin %d message in this chat?', + otherValue: 'Do you want to unpin all %d messages in this chat?', + }, + DialogUnpin: { + key: 'DialogUnpin', + value: 'Unpin', + }, + Cancel: { + key: 'Cancel', + value: 'Cancel', + }, + AccDescrStickerSet: { + key: 'AccDescrStickerSet', + value: 'Sticker set', + }, + Recent: { + key: 'Recent', + value: 'Recent', + }, + DeleteChatUser: { + key: 'DeleteChatUser', + value: 'Delete chat', + }, + 'ChatList.DeleteChatConfirmation': { + key: 'ChatList.DeleteChatConfirmation', + value: 'Are you sure you want to delete the chat\nwith %@?', + }, + 'ChatList.DeleteForEveryone': { + key: 'ChatList.DeleteForEveryone', + value: 'Delete for me and %@', + }, + 'ChatList.DeleteForCurrentUser': { + key: 'ChatList.DeleteForCurrentUser', + value: 'Delete just for me', + }, + 'LastSeen.HoursAgo': { + key: 'LastSeen.HoursAgo', + oneValue: 'last seen 1 hour ago', + otherValue: 'last seen %@ hours ago', + }, + ForwardedMessage: { + key: 'ForwardedMessage', + value: 'Forwarded message', + }, + 'Weekday.Yesterday': { + key: 'Weekday.Yesterday', + value: 'Yesterday', + }, + AttachVideo: { + key: 'AttachVideo', + value: 'Video', + }, + Lately: { + key: 'Lately', + value: 'last seen recently', + }, + 'Weekday.Tuesday': { + key: 'Weekday.Tuesday', + value: 'Tuesday', + }, + 'Weekday.Wednesday': { + key: 'Weekday.Wednesday', + value: 'Wednesday', + }, + 'Weekday.Friday': { + key: 'Weekday.Friday', + value: 'Friday', + }, + 'Weekday.Saturday': { + key: 'Weekday.Saturday', + value: 'Saturday', + }, + SearchFriends: { + key: 'SearchFriends', + value: 'Search contacts', + }, + Online: { + key: 'Online', + value: 'online', + }, + 'LastSeen.JustNow': { + key: 'LastSeen.JustNow', + value: 'last seen just now', + }, + AccDescrGoBack: { + key: 'AccDescrGoBack', + value: 'Go back', + }, + SETTINGS: { + key: 'SETTINGS', + value: 'Settings', + }, + LogOutTitle: { + key: 'LogOutTitle', + value: 'Log Out', + }, + lng_settings_information: { + key: 'lng_settings_information', + value: 'Edit profile', + }, + Filters: { + key: 'Filters', + value: 'Folders', + }, + 'Telegram.GeneralSettingsViewController': { + key: 'Telegram.GeneralSettingsViewController', + value: 'General Settings', + }, + Notifications: { + key: 'Notifications', + value: 'Notifications', + }, + PrivacySettings: { + key: 'PrivacySettings', + value: 'Privacy and Security', + }, + Language: { + key: 'Language', + value: 'Language', + }, + FirstName: { + key: 'FirstName', + value: 'First name (required)', + }, + LastName: { + key: 'LastName', + value: 'Last name (optional)', + }, + UserBio: { + key: 'UserBio', + value: 'Bio', + }, + lng_settings_about_bio: { + key: 'lng_settings_about_bio', + value: 'Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco', + }, + Username: { + key: 'Username', + value: 'Username', + }, + UsernameHelp: { + key: 'UsernameHelp', + value: 'You can choose a username on **Telegram**. If you do, people will be able to find you by this username and contact you without needing your phone number.\n\nYou can use **a–z**, **0–9** and underscores. Minimum length is **5** characters.', + }, + lng_username_link: { + key: 'lng_username_link', + value: 'This link opens a chat with you:', + }, + lng_user_typing: { + key: 'lng_user_typing', + value: 'typing...', + }, + lng_send_action_record_video: { + key: 'lng_send_action_record_video', + value: 'recording video...', + }, + lng_send_action_upload_video: { + key: 'lng_send_action_record_video', + value: 'uploading video...', + }, + lng_send_action_record_audio: { + key: 'lng_send_action_record_audio', + value: 'recording audio...', + }, + lng_send_action_upload_audio: { + key: 'lng_send_action_upload_audio', + value: 'uploading audio...', + }, + lng_send_action_upload_photo: { + key: 'lng_send_action_upload_photo', + value: 'uploading photo...', + }, + lng_send_action_upload_file: { + key: 'lng_send_action_upload_file', + value: 'uploading file...', + }, + CropImage: { + key: 'CropImage', + value: 'Crop image', + }, + Chats: { + key: 'Chats', oneValue: '%1$d chat', otherValue: '%1$d chats', + }, + FilterContacts: { + key: 'FilterContacts', + value: 'Contacts', + }, + CreateNewFilterInfo: { + key: 'CreateNewFilterInfo', + value: 'Create folders for different groups of chats and quickly switch between them.', + }, + CreateNewFilter: { + key: 'CreateNewFilter', + value: 'Create New Folder', + }, + FilterNew: { + key: 'FilterNew', + value: 'New Folder', + }, + FilterIncludeInfo: { + key: 'FilterIncludeInfo', + value: 'Choose chats and types of chats that will appear in this folder.', + }, + FilterNameHint: { + key: 'FilterNameHint', + value: 'Folder name', + }, + FilterInclude: { + key: 'FilterInclude', + value: 'Included Chats', + }, + FilterAddChats: { + key: 'FilterAddChats', + value: 'Add Chats', + }, + FilterExclude: { + key: 'FilterExclude', + value: 'Excluded Chats', + }, + AutoDeleteConfirm: { + key: 'AutoDeleteConfirm', + value: 'Confirm', + }, + FilterChatTypes: { + key: 'FilterChatTypes', + value: 'Chat types', + }, + FilterNonContacts: { + key: 'FilterNonContacts', + value: 'Non Contacts', + }, + FilterGroups: { + key: 'FilterGroups', + value: 'Groups', + }, + FilterChannels: { + key: 'FilterChannels', + value: 'Channels', + }, + FilterBots: { + key: 'FilterBots', + value: 'Bots', + }, + FilterChats: { + key: 'FilterChats', + value: 'Chats', + }, + AccDescrChannel: { + key: 'AccDescrChannel', + value: 'Channel', + }, + AccDescrGroup: { + key: 'AccDescrGroup', + value: 'Group', + }, + Bot: { + key: 'Bot', + value: 'bot', + }, + ServiceNotifications: { + key: 'ServiceNotifications', + value: 'Service notifications', + }, + 'LastSeen.TodayAt': { + key: 'LastSeen.TodayAt', + value: 'last seen today at %@', + }, + ALongTimeAgo: { + key: 'ALongTimeAgo', + value: 'last seen a long time ago', + }, + formatterYearMax: { + key: 'formatterYearMax', + value: 'dd.MM.yyyy', + }, + 'LastSeen.AtDate': { + key: 'LastSeen.AtDate', + value: 'last seen %@', + }, + 'LastSeen.YesterdayAt': { + key: 'LastSeen.YesterdayAt', + value: 'last seen yesterday at %@', + }, + FilterMuted: { + key: 'FilterMuted', + value: 'Muted', + }, + FilterArchived: { + key: 'FilterArchived', + value: 'Archived', + }, + FilterRead: { + key: 'FilterRead', + value: 'Read', + }, + FilterEdit: { + key: 'FilterEdit', + value: 'Edit folder', + }, + Members: { + key: 'Members', oneValue: '%1$d member', otherValue: '%1$d members', + }, + General: { + key: 'General', + value: 'General', + }, + lng_settings_send_enter: { + key: 'lng_settings_send_enter', + value: 'Send with Enter', + }, + lng_settings_send_cmdenter: { + key: 'lng_settings_send_cmdenter', + value: 'Send with Cmd+Enter', + }, + TextSize: { + key: 'TextSize', + value: 'Message Text Size', + }, + ChatBackground: { + key: 'ChatBackground', + value: 'Chat Background', + }, + 'VoiceOver.Keyboard': { + key: 'VoiceOver.Keyboard', + value: 'Keyboard', + }, + AutoDownloadMedia: { + key: 'AutoDownloadMedia', + value: 'Auto-Download Media', + }, + AutodownloadPrivateChats: { + key: 'AutodownloadPrivateChats', + value: 'Private Chats', + }, + AutodownloadGroupChats: { + key: 'AutodownloadGroupChats', + value: 'Group Chats', + }, + AutoplayMedia: { + key: 'AutoplayMedia', + value: 'Auto-play media', + }, + GifsTab2: { + key: 'GifsTab2', + value: 'GIFs', + }, + 'DataAndStorage.Autoplay.Videos': { + key: 'DataAndStorage.Autoplay.Videos', + value: 'Videos', + }, + AccDescrStickers: { + key: 'AccDescrStickers', + value: 'Stickers', + }, + SuggestStickers: { + key: 'SuggestStickers', + value: 'Suggest stickers by emoji', + }, + LoopAnimatedStickers: { + key: 'LoopAnimatedStickers', + value: 'Loop Animated Stickers', + }, + 'StickerPack.StickerCount': { + key: 'StickerPack.StickerCount', + oneValue: '1 sticker', + otherValue: '%@ stickers', + }, + UploadImage: { + key: 'UploadImage', + value: 'Upload image', + }, + SetColor: { + key: 'SetColor', + value: 'Set a color', + }, + ThemeResetToDefaults: { + key: 'ThemeResetToDefaults', + value: 'Reset to default', + }, + BackgroundBlurred: { + key: 'BackgroundBlurred', + value: 'Blurred', + }, + NotificationsForPrivateChats: { + key: 'NotificationsForPrivateChats', + value: 'Notifications for private chats', + }, + 'UserInfo.NotificationsEnabled': { + key: 'UserInfo.NotificationsEnabled', + value: 'Enabled', + }, + MessagePreview: { + key: 'MessagePreview', + value: 'Message Preview', + }, + NotificationsForGroups: { + key: 'NotificationsForGroups', + value: 'Notifications for groups', + }, + NotificationsForChannels: { + key: 'NotificationsForChannels', + value: 'Notifications for channels', + }, + PhoneOther: { + key: 'PhoneOther', + value: 'Other', + }, + ContactJoined: { + key: 'ContactJoined', + value: 'Contact joined Telegram', + }, + 'UserInfo.NotificationsDisabled': { + key: 'UserInfo.NotificationsDisabled', + value: 'Disabled', + }, + BlockedUsers: { + key: 'BlockedUsers', + value: 'Blocked Users', + }, + TwoStepVerification: { + key: 'TwoStepVerification', + value: 'Two-Step Verification', + }, + PasswordOff: { + key: 'PasswordOff', + value: 'Off', + }, + SessionsTitle: { + key: 'SessionsTitle', + value: 'Active Sessions', + }, + PrivacyTitle: { + key: 'PrivacyTitle', + value: 'Privacy', + }, + PrivacyPhoneTitle: { + key: 'PrivacyPhoneTitle', + value: 'Who can see my phone number?', + }, + LastSeenTitle: { + key: 'LastSeenTitle', + value: 'Who can see your Last Seen time?', + }, + PrivacyProfilePhotoTitle: { + key: 'PrivacyProfilePhotoTitle', + value: 'Who can see my profile photos & videos?', + }, + PrivacyForwardsTitle: { + key: 'PrivacyForwardsTitle', + value: 'Who can add a link to my account when forwarding my messages?', + }, + WhoCanAddMe: { + key: 'WhoCanAddMe', + value: 'Who can add me to group chats?', + }, + lng_settings_sensitive_title: { + key: 'lng_settings_sensitive_title', + value: 'Sensitive content', + }, + lng_settings_sensitive_disable_filtering: { + key: 'lng_settings_sensitive_disable_filtering', + value: 'Disable filtering', + }, + lng_settings_sensitive_about: { + key: 'lng_settings_sensitive_about', + value: 'Display sensitive media in public channels on all your Telegram devices.', + }, + P2PContacts: { + key: 'P2PContacts', + value: 'My Contacts', + }, + P2PEverybody: { + key: 'P2PEverybody', + value: 'Everybody', + }, + P2PNobody: { + key: 'P2PNobody', + value: 'Nobody', + }, + Users: { + key: 'Users', oneValue: '%1$d user', otherValue: '%1$d users', + }, + PasswordOn: { + key: 'PasswordOn', + value: 'On', + }, + BlockedUsersInfo: { + key: 'BlockedUsersInfo', + value: 'Blocked users will not be able to contact you and will not see your Last Seen time.', + }, + EnabledPasswordText: { + key: 'EnabledPasswordText', + value: 'You have enabled Two-Step verification.\nYou\'ll need the password you set up here to log in to your Telegram account.', + }, + ChangePassword: { + key: 'ChangePassword', + value: 'Change Password', + }, + TurnPasswordOff: { + key: 'TurnPasswordOff', + value: 'Turn Password Off', + }, + SetRecoveryEmail: { + key: 'SetRecoveryEmail', + value: 'Set Recovery Email', + }, + PleaseEnterCurrentPassword: { + key: 'PleaseEnterCurrentPassword', + value: 'Enter your password', + }, + Next: { + key: 'Next', + value: 'Next', + }, + 'AuthSessions.CurrentSession': { + key: 'AuthSessions.CurrentSession', + value: 'CURRENT SESSION', + }, + TerminateAllSessions: { + key: 'TerminateAllSessions', + value: 'Terminate All Other Sessions', + }, + PrivacyPhone: { + key: 'PrivacyPhone', + value: 'Phone Number', + }, + PrivacyExceptions: { + key: 'PrivacyExceptions', + value: 'Exceptions', + }, + AlwaysShareWith: { + key: 'AlwaysShareWith', + value: 'Always Share With', + }, + EditAdminAddUsers: { + key: 'EditAdminAddUsers', + value: 'Add Users', + }, + NeverShareWith: { + key: 'NeverShareWith', + value: 'Never Share With', + }, + AlwaysShareWithPlaceholder: { + key: 'AlwaysShareWithPlaceholder', + value: 'Always share with users...', + }, + AlwaysShareWithTitle: { + key: 'AlwaysShareWithTitle', + value: 'Always Share', + }, + NeverShareWithPlaceholder: { + key: 'NeverShareWithPlaceholder', + value: 'Never share with users...', + }, + NeverShareWithTitle: { + key: 'NeverShareWithTitle', + value: 'Never Share', + }, + 'Privacy.ProfilePhoto': { + key: 'Privacy.ProfilePhoto', + value: 'Profile Photo', + }, + FilterNoChatsToDisplay: { + key: 'FilterNoChatsToDisplay', + value: 'Folder is empty', + }, + AttachSticker: { + key: 'AttachSticker', + value: 'Sticker', + }, + 'ChatList.Search.ShowMore': { + key: 'ChatList.Search.ShowMore', + value: 'Show more', + }, + 'DialogList.SearchSectionDialogs': { + key: 'DialogList.SearchSectionDialogs', + value: 'Chats and Contacts', + }, + SearchMessages: { + key: 'SearchMessages', + value: 'Messages', + }, + 'Weekday.ShortSunday': { + key: 'Weekday.ShortSunday', + value: 'Sun', + }, + 'DialogList.SearchSectionGlobal': { + key: 'DialogList.SearchSectionGlobal', + value: 'Global Search', + }, + Subscribers: { + key: 'Subscribers', oneValue: '%1$d subscriber', otherValue: '%1$d subscribers', + }, + ChannelLeaveAlertWithName: { + key: 'ChannelLeaveAlertWithName', + value: 'Are you sure you want to leave **%1$s**?', + }, + 'ChatList.Search.ShowLess': { + key: 'ChatList.Search.ShowLess', + value: 'Show less', + }, + ChannelDelete: { + key: 'ChannelDelete', + value: 'Delete Channel', + }, + 'ChatList.DeleteAndLeaveGroupConfirmation': { + key: 'ChatList.DeleteAndLeaveGroupConfirmation', + value: 'Are you sure you want to leave and delete %@?', + }, + 'Chat.Input.Delete': { + key: 'Chat.Input.Delete', + value: 'Delete and Leave', + }, + formatDateScheduleYear: { + key: 'formatDateScheduleYear', + value: 'MMM d yyyy', + }, + October: { + key: 'October', + value: 'October', + }, + 'Month.GenOctober': { + key: 'Month.GenOctober', + value: 'October', + }, + 'Month.ShortOctober': { + key: 'Month.ShortOctober', + value: 'Oct', + }, + August: { + key: 'August', + value: 'August', + }, + 'Month.GenAugust': { + key: 'Month.GenAugust', + value: 'August', + }, + 'Month.ShortAugust': { + key: 'Month.ShortAugust', + value: 'Aug', + }, + July: { + key: 'July', + value: 'July', + }, + 'Month.GenJuly': { + key: 'Month.GenJuly', + value: 'July', + }, + 'Month.ShortJuly': { + key: 'Month.ShortJuly', + value: 'Jul', + }, + 'ChatList.Search.NoResults': { + key: 'ChatList.Search.NoResults', + value: 'No Results', + }, + 'ChatList.Search.NoResultsDescription': { + key: 'ChatList.Search.NoResultsDescription', + value: 'There were no results.\nTry a new search.', + }, + lng_in_dlg_album: { + key: 'lng_in_dlg_album', + value: 'Album', + }, + ReportSelectMessages: { + key: 'ReportSelectMessages', + value: 'Select messages', + }, + 'VoiceOver.Chat.MessagesSelected': { + key: 'VoiceOver.Chat.MessagesSelected', + oneValue: '%@ message selected', + otherValue: '%@ messages selected', + }, + 'Conversation.DeleteManyMessages': { + key: 'Conversation.DeleteManyMessages', + value: 'Delete Messages', + }, + AreYouSureDeleteFewMessages: { + key: 'AreYouSureDeleteFewMessages', + value: 'Are you sure you want to delete these messages?', + }, + Reply: { + key: 'Reply', + value: 'Reply', + }, + Copy: { + key: 'Copy', + value: 'Copy', + }, + DialogPin: { + key: 'DialogPin', + value: 'Pin', + }, + Forward: { + key: 'Forward', + value: 'Forward', + }, + 'Common.Select': { + key: 'Common.Select', + value: 'Select', + }, + DeleteSingleMessagesTitle: { + key: 'DeleteSingleMessagesTitle', + value: 'Delete message', + }, + AreYouSureDeleteSingleMessage: { + key: 'AreYouSureDeleteSingleMessage', + value: 'Are you sure you want to delete this message?', + }, + PinMessageAlertTitle: { + key: 'PinMessageAlertTitle', + value: 'Pin message', + }, + PinMessageAlertChat: { + key: 'PinMessageAlertChat', + value: 'Do you want to pin this message at the top of the chat?', + }, + Close: { + key: 'Close', + value: 'Close', + }, + ForwardTo: { + key: 'ForwardTo', + value: 'Forward to...', + }, + SavedMessagesInfo: { + key: 'SavedMessagesInfo', + value: 'Forward here to save.', + }, + SendMessage: { + key: 'SendMessage', + value: 'Send Message', + }, + Poll: { + key: 'Poll', + value: 'Poll', + }, + GroupMembers: { + key: 'GroupMembers', + value: 'Members', + }, + Info: { + key: 'Info', + value: 'Info', + }, + EditAdminGroupDeleteMessages: { + key: 'EditAdminGroupDeleteMessages', + value: 'Delete Messages', + }, + Edit: { + key: 'Edit', + value: 'Edit', + }, + 'Common.Back': { + key: 'Common.Back', + value: 'Back', + }, + SharedMedia: { + key: 'SharedMedia', + value: 'Shared Media', + }, + lng_dlg_search_for_messages: { + key: 'lng_dlg_search_for_messages', + value: 'Search for messages', + }, + JumpToDate: { + key: 'JumpToDate', + value: 'Jump to Date', + }, + lng_search_no_results: { + key: 'lng_search_no_results', + value: 'No messages found', + }, + 'StickerPack.RemoveStickerCount': { + key: 'StickerPack.RemoveStickerCount', + oneValue: 'Remove 1 Sticker', + otherValue: 'Remove %@ Stickers', + }, + AccActionDownload: { + key: 'AccActionDownload', + value: 'Download', + }, + AccActionPlay: { + key: 'AccActionPlay', + value: 'Play', + }, + ZoomOut: { + key: 'ZoomOut', + value: 'Zoom out', + }, + AccDescrPrevious: { + key: 'AccDescrPrevious', + value: 'Previous', + }, + 'TextFormat.AddLinkTitle': { + key: 'TextFormat.AddLinkTitle', + value: 'Add Link', + }, + Save: { + key: 'Save', + value: 'Save', + }, + Updating: { + key: 'Updating', + value: 'Updating...', + }, + lng_context_forward_msg: { + key: 'lng_context_forward_msg', + value: 'Forward Message', + }, + Comments: { + key: 'Comments', oneValue: '%1$d Comment', otherValue: '%1$d Comments', + }, + ChannelSubscribers: { + key: 'ChannelSubscribers', + value: 'Subscribers', + }, + LeaveAComment: { + key: 'LeaveAComment', + value: 'Leave a comment', + }, + ChatsMute: { + key: 'ChatsMute', + value: 'Mute', + }, + 'GroupInfo.DeleteAndExit': { + key: 'GroupInfo.DeleteAndExit', + value: 'Delete and Exit', + }, + ChatsUnmute: { + key: 'ChatsUnmute', + value: 'Unmute', + }, + 'Conversation.DeleteMessagesForEveryone': { + key: 'Conversation.DeleteMessagesForEveryone', + value: 'Delete for everyone', + }, + lng_mediaview_profile_photo: { + key: 'lng_mediaview_profile_photo', + value: 'Profile Photo', + }, + lng_media_file_empty_search: { + key: 'lng_media_file_empty_search', + value: 'No files found', + }, + lng_media_song_empty_search: { + key: 'lng_media_song_empty_search', + value: 'No music files found', + }, + EnterChannelName: { + key: 'EnterChannelName', + value: 'Channel name', + }, + DescriptionPlaceholder: { + key: 'DescriptionPlaceholder', + value: 'Description', + }, + ChannelType: { + key: 'ChannelType', + value: 'Channel Type', + }, + TypePublic: { + key: 'TypePublic', + value: 'Public', + }, + Discussion: { + key: 'Discussion', + value: 'Discussion', + }, + DiscussionUnlink: { + key: 'DiscussionUnlink', + value: 'Unlink', + }, + ChannelAdministrators: { + key: 'ChannelAdministrators', + value: 'Administrators', + }, + ChannelSignMessages: { + key: 'ChannelSignMessages', + value: 'Sign Messages', + }, + ChannelDeleteAlert: { + key: 'ChannelDeleteAlert', + value: 'Wait! Deleting this channel will remove all subscribers and all messages will be lost. Delete the channel anyway?', + }, + ChannelTypeHeader: { + key: 'ChannelTypeHeader', + value: 'Channel type', + }, + ChannelPrivate: { + key: 'ChannelPrivate', + value: 'Private Channel', + }, + ChannelPrivateInfo: { + key: 'ChannelPrivateInfo', + value: 'Private channels can only be joined via an invite link.', + }, + ChannelPublic: { + key: 'ChannelPublic', + value: 'Public Channel', + }, + ChannelPublicInfo: { + key: 'ChannelPublicInfo', + value: 'Public channels can be found in search, anyone can join them.', + }, + 'Channel.Username.CreatePublicLinkHelp': { + key: 'Channel.Username.CreatePublicLinkHelp', + value: 'People can share this link with others and find your channel using Telegram search.', + }, + SetUrlPlaceholder: { + key: 'SetUrlPlaceholder', + value: 'Link', + }, + ChannelPrivateLinkHelp: { + key: 'ChannelPrivateLinkHelp', + value: 'People can join your channel by following this link. You can revoke the link any time.', + }, + RevokeLink: { + key: 'RevokeLink', + value: 'Revoke Link', + }, + RevokeAlert: { + key: 'RevokeAlert', + value: 'Are you sure you want to revoke this link? Once the link is revoked, no one will be able to join using it.', + }, + RevokeButton: { + key: 'RevokeButton', + value: 'Revoke', + }, + DiscussionUnlinkGroup: { + key: 'DiscussionUnlinkGroup', + value: 'Unlink Group', + }, + DiscussionUnlinkChannelAlert: { + key: 'DiscussionUnlinkChannelAlert', + value: 'Are you sure you want to unlink **%1$s** from this channel?', + }, + EventLog: { + key: 'EventLog', + value: 'Recent Actions', + }, + EventLogInfoDetailChannel: { + key: 'EventLogInfoDetailChannel', + value: 'This is a list of all service actions taken by the channel\'s admins in the last 48 hours.', + }, + ChannelCreator: { + key: 'ChannelCreator', + value: 'Owner', + }, + EditAdminRights: { + key: 'EditAdminRights', + value: 'Edit admin rights', + }, + EditAdminWhatCanDo: { + key: 'EditAdminWhatCanDo', + value: 'What can this admin do?', + }, + EditAdminChangeChannelInfo: { + key: 'EditAdminChangeChannelInfo', + value: 'Change Channel Info', + }, + EditAdminPostMessages: { + key: 'EditAdminPostMessages', + value: 'Post Messages', + }, + EditAdminEditMessages: { + key: 'EditAdminEditMessages', + value: 'Edit Messages of Others', + }, + EditAdminDeleteMessages: { + key: 'EditAdminDeleteMessages', + value: 'Delete Messages of Others', + }, + EditAdminAddAdmins: { + key: 'EditAdminAddAdmins', + value: 'Add New Admins', + }, + 'Group.Info.AdminLog': { + key: 'Group.Info.AdminLog', + value: 'Recent Actions', + }, + EventLogAllEvents: { + key: 'EventLogAllEvents', + value: 'All actions', + }, + EventLogFilterNewAdmins: { + key: 'EventLogFilterNewAdmins', + value: 'Admin rights', + }, + EventLogFilterNewMembers: { + key: 'EventLogFilterNewMembers', + value: 'New members', + }, + EventLogFilterChannelInfo: { + key: 'EventLogFilterChannelInfo', + value: 'Channel info', + }, + EventLogFilterDeletedMessages: { + key: 'EventLogFilterDeletedMessages', + value: 'Deleted messages', + }, + EventLogFilterEditedMessages: { + key: 'EventLogFilterEditedMessages', + value: 'Edited messages', + }, + EventLogFilterLeavingMembers: { + key: 'EventLogFilterLeavingMembers', + value: 'Leaving members', + }, + 'Channel.Management.Title': { + key: 'Channel.Management.Title', + value: 'Admins', + }, + EventLogAllAdmins: { + key: 'EventLogAllAdmins', + value: 'All admins', + }, + WithinAWeek: { + key: 'WithinAWeek', + value: 'last seen within a week', + }, + WithinAMonth: { + key: 'WithinAMonth', + value: 'last seen within a month', + }, + EventLogFilterPinnedMessages: { + key: 'EventLogFilterPinnedMessages', + value: 'Pinned messages', + }, + UnpinMessageAlertTitle: { + key: 'UnpinMessageAlertTitle', + value: 'Unpin message', + }, + PinnedMessage: { + key: 'PinnedMessage', + value: 'Pinned Message', + }, + OnlineCount: { + key: 'OnlineCount', oneValue: '%1$d online', otherValue: '%1$d online', + }, + GroupName: { + key: 'GroupName', + value: 'Group name', + }, + GroupType: { + key: 'GroupType', + value: 'Group Type', + }, + LinkedChannel: { + key: 'LinkedChannel', + value: 'Linked Channel', + }, + ChannelPermissions: { + key: 'ChannelPermissions', + value: 'Permissions', + }, + ChatHistory: { + key: 'ChatHistory', + value: 'Chat history for new members', + }, + DeleteMega: { + key: 'DeleteMega', + value: 'Delete Group', + }, + AreYouSureDeleteThisChatWithGroup: { + key: 'AreYouSureDeleteThisChatWithGroup', + value: 'Are you sure you want to delete the chat **%1$s**?', + }, + DeleteGroupForAll: { + key: 'DeleteGroupForAll', + value: 'Delete the group for all members', + }, + GroupTypeHeader: { + key: 'GroupTypeHeader', + value: 'Group type', + }, + MegaPrivate: { + key: 'MegaPrivate', + value: 'Private Group', + }, + MegaPrivateInfo: { + key: 'MegaPrivateInfo', + value: 'Private groups can only be joined if you were invited or have an invite link.', + }, + MegaPublic: { + key: 'MegaPublic', + value: 'Public Group', + }, + MegaPublicInfo: { + key: 'MegaPublicInfo', + value: 'Public groups can be found in search, chat history is available to everyone and anyone can join.', + }, + 'Group.Username.CreatePublicLinkHelp': { + key: 'Group.Username.CreatePublicLinkHelp', + value: 'People can share this link with others and find your group using Telegram search.', + }, + MegaPrivateLinkHelp: { + key: 'MegaPrivateLinkHelp', + value: 'People can join your group by following this link. You can revoke the link any time.', + }, + DiscussionUnlinkChannel: { + key: 'DiscussionUnlinkChannel', + value: 'Unlink Channel', + }, + DiscussionUnlinkGroupAlert: { + key: 'DiscussionUnlinkGroupAlert', + value: 'Are you sure you want to unlink **%1$s** from this group?', + }, + ChannelPermissionsHeader: { + key: 'ChannelPermissionsHeader', + value: 'What can members of this group do?', + }, + UserRestrictionsSend: { + key: 'UserRestrictionsSend', + value: 'Send Messages', + }, + UserRestrictionsSendMedia: { + key: 'UserRestrictionsSendMedia', + value: 'Send Media', + }, + UserRestrictionsSendStickers: { + key: 'UserRestrictionsSendStickers', + value: 'Send Stickers and GIFs', + }, + UserRestrictionsSendPolls: { + key: 'UserRestrictionsSendPolls', + value: 'Send Polls', + }, + UserRestrictionsEmbedLinks: { + key: 'UserRestrictionsEmbedLinks', + value: 'Embed Links', + }, + UserRestrictionsInviteUsers: { + key: 'UserRestrictionsInviteUsers', + value: 'Add Users', + }, + UserRestrictionsPinMessages: { + key: 'UserRestrictionsPinMessages', + value: 'Pin Messages', + }, + UserRestrictionsChangeInfo: { + key: 'UserRestrictionsChangeInfo', + value: 'Change Chat Info', + }, + ChannelBlockedUsers: { + key: 'ChannelBlockedUsers', + value: 'Removed users', + }, + ChannelAddException: { + key: 'ChannelAddException', + value: 'Add Exception', + }, + NoBlockedGroup2: { + key: 'NoBlockedGroup2', + value: 'Users removed from the group by the admins can\'t rejoin via invite links.', + }, + EventLogInfoDetail: { + key: 'EventLogInfoDetail', + value: 'This is a list of notable actions by members and admins in the last 48 hours.', + }, + EditAdminChangeGroupInfo: { + key: 'EditAdminChangeGroupInfo', + value: 'Change Group Info', + }, + EditAdminBanUsers: { + key: 'EditAdminBanUsers', + value: 'Ban Users', + }, + EditAdminPinMessages: { + key: 'EditAdminPinMessages', + value: 'Pin Messages', + }, + EditAdminSendAnonymously: { + key: 'EditAdminSendAnonymously', + value: 'Remain Anonymous', + }, + EditAdminRank: { + key: 'EditAdminRank', + value: 'Custom title', + }, + RecentStickers: { + key: 'RecentStickers', + value: 'Recently Used', + }, + Emoji1: { + key: 'Emoji1', + value: 'Smileys and people', + }, + Emoji2: { + key: 'Emoji2', + value: 'Animals and nature', + }, + Emoji3: { + key: 'Emoji3', + value: 'Food and drink', + }, + Emoji4: { + key: 'Emoji4', + value: 'Activity', + }, + Emoji5: { + key: 'Emoji5', + value: 'Travel and places', + }, + Emoji6: { + key: 'Emoji6', + value: 'Objects', + }, + Emoji7: { + key: 'Emoji7', + value: 'Symbols', + }, + Emoji8: { + key: 'Emoji8', + value: 'Flags', + }, + FavoriteStickers: { + key: 'FavoriteStickers', + value: 'Favorites', + }, + SearchStickersHint: { + key: 'SearchStickersHint', + value: 'Search sticker sets', + }, + Stickers: { + key: 'Stickers', oneValue: '%1$d sticker', otherValue: '%1$d stickers', + }, + 'Stickers.Install': { + key: 'Stickers.Install', + value: 'ADD', + }, + 'StickerPack.AddStickerCount': { + key: 'StickerPack.AddStickerCount', + oneValue: 'Add 1 Sticker', + otherValue: 'Add %@ Stickers', + }, + SearchGifsTitle: { + key: 'SearchGifsTitle', + value: 'Search GIFs', + }, + 'PreviewSender.SendFile': { + key: 'PreviewSender.SendFile', oneValue: 'Send File', otherValue: 'Send %d Files', + }, + Phone: { + key: 'Phone', + value: 'Phone', + }, + Reminders: { + key: 'Reminders', + value: 'Reminders', + }, + MessageScheduledOn: { + key: 'MessageScheduledOn', + value: 'Scheduled for %1$s', + }, + NewPoll: { + key: 'NewPoll', + value: 'New Poll', + }, + Create: { + key: 'Create', + value: 'Create', + }, + AskAQuestion: { + key: 'AskAQuestion', + value: 'Ask a Question', + }, + PollOptions: { + key: 'PollOptions', + value: 'Poll options', + }, + 'CreatePoll.AddOption': { + key: 'CreatePoll.AddOption', + value: 'Add an Option', + }, + PollAnonymous: { + key: 'PollAnonymous', + value: 'Anonymous Voting', + }, + PollMultiple: { + key: 'PollMultiple', + value: 'Multiple Answers', + }, + PollQuiz: { + key: 'PollQuiz', + value: 'Quiz Mode', + }, + PaymentReceipt: { + key: 'PaymentReceipt', + value: 'Receipt', + }, + NoMessages: { + key: 'NoMessages', + value: 'No messages here yet...', + }, + PinnedMessagesCount: { + key: 'PinnedMessagesCount', + oneValue: 'Pinned Message', + otherValue: '%1$d Pinned Messages', + }, + 'Chat.Pinned.UnpinAll': { + key: 'Chat.Pinned.UnpinAll', + oneValue: 'Unpin %d Message', + otherValue: 'Unpin All %d Messages', + }, + CommentsCount: { + key: 'CommentsCount', oneValue: '%1$d comment', otherValue: '%1$d comments', + }, + 'Conversation.DefaultRestrictedMedia': { + key: 'Conversation.DefaultRestrictedMedia', + value: 'Posting media content isn\'t allowed in this group.', + }, + DiscussionStarted: { + key: 'DiscussionStarted', + value: 'Discussion started', + }, + DiscussChannel: { + key: 'DiscussChannel', + value: 'channel', + }, + 'SharedMedia.EmptyTitle': { + key: 'SharedMedia.EmptyTitle', + value: 'No media files yet', + }, + QuizPoll: { + key: 'QuizPoll', + value: 'Quiz', + }, + PollViewResults: { + key: 'PollViewResults', + value: 'VIEW RESULTS', + }, + 'Chat.Quiz.TotalVotesEmpty': { + key: 'Chat.Quiz.TotalVotesEmpty', + value: 'No answers yet', + }, +} as ApiLangPack; diff --git a/src/util/langProvider.ts b/src/util/langProvider.ts index e0c7c1883..897cf59ce 100644 --- a/src/util/langProvider.ts +++ b/src/util/langProvider.ts @@ -13,6 +13,7 @@ interface LangFn { isRtl?: boolean; } +const FALLBACK_LANG_CODE = 'en'; const PLURAL_OPTIONS = ['value', 'zeroValue', 'oneValue', 'twoValue', 'fewValue', 'manyValue', 'otherValue'] as const; const PLURAL_RULES = { /* eslint-disable max-len */ @@ -39,7 +40,8 @@ const PLURAL_RULES = { const cache = new Map(); -let langPack: ApiLangPack; +let langPack: ApiLangPack | undefined; +let fallbackLangPack: ApiLangPack | undefined; const { addCallback, @@ -59,12 +61,16 @@ export const getTranslation: LangFn = (key: string, value?: any, format?: 'i') = } } - if (!langPack) { + if (!langPack && !fallbackLangPack) { return key; } - const langString = langPack[key]; + const langString = (langPack && langPack[key]) || (fallbackLangPack && fallbackLangPack[key]); if (!langString) { + if (!fallbackLangPack) { + void importFallbackLangPack(); + } + return key; } @@ -85,7 +91,7 @@ export const getTranslation: LangFn = (key: string, value?: any, format?: 'i') = return template; }; -export async function setLanguage(langCode: string, callback?: NoneToVoidFunction) { +export async function setLanguage(langCode: string, callback?: NoneToVoidFunction, withFallback = false) { if (langPack && langCode === currentLangCode) { if (callback) { callback(); @@ -94,9 +100,16 @@ export async function setLanguage(langCode: string, callback?: NoneToVoidFunctio return; } - const newLangPack = await fetchFromCacheOrRemote(langCode); + let newLangPack = await cacheApi.fetch(LANG_CACHE_NAME, langCode, cacheApi.Type.Json); if (!newLangPack) { - return; + if (withFallback) { + await importFallbackLangPack(); + } + + newLangPack = await fetchRemote(langCode); + if (!newLangPack) { + return; + } } cache.clear(); @@ -113,15 +126,19 @@ export async function setLanguage(langCode: string, callback?: NoneToVoidFunctio callback(); } - runCallbacks(langPack); + runCallbacks(); } -async function fetchFromCacheOrRemote(langCode: string): Promise { - const cached = await cacheApi.fetch(LANG_CACHE_NAME, langCode, cacheApi.Type.Json); - if (cached) { - return cached; +async function importFallbackLangPack() { + if (fallbackLangPack) { + return; } + fallbackLangPack = (await import('./fallbackLangPack')).default; + runCallbacks(); +} + +async function fetchRemote(langCode: string): Promise { const remote = await callApi('fetchLangPack', { sourceLangPacks: LANG_PACKS, langCode }); if (remote) { await cacheApi.save(LANG_CACHE_NAME, langCode, remote.langPack); @@ -132,8 +149,9 @@ async function fetchFromCacheOrRemote(langCode: string): Promise