langs improved for future translations, win version only for now

This commit is contained in:
John Preston 2014-12-18 21:40:49 +03:00
parent dec5db074c
commit d34ab1e1fe
33 changed files with 1374 additions and 927 deletions

View File

@ -0,0 +1,482 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
"lng_maintitle" = "Telegram Desktop";
"lng_menu_contacts" = "Contacts";
"lng_menu_settings" = "Settings";
"lng_menu_about" = "About";
"lng_menu_update" = "Update";
"lng_menu_restart" = "Restart";
"lng_menu_back" = "Back";
"lng_open_from_tray" = "Open Telegram";
"lng_minimize_to_tray" = "Minimize to tray";
"lng_quit_from_tray" = "Quit Telegram";
"lng_tray_icon_text" = "Telegram is still running here,\nyou can change this from settings page.\n\nIf this icon disappears from tray menu,\nyou can drag it back here from hidden icons.";
"lng_month1" = "January";
"lng_month2" = "February";
"lng_month3" = "March";
"lng_month4" = "April";
"lng_month5" = "May";
"lng_month6" = "June";
"lng_month7" = "July";
"lng_month8" = "August";
"lng_month9" = "September";
"lng_month10" = "October";
"lng_month11" = "November";
"lng_month12" = "December";
"lng_weekday1" = "Mon";
"lng_weekday2" = "Tue";
"lng_weekday3" = "Wed";
"lng_weekday4" = "Thu";
"lng_weekday5" = "Fri";
"lng_weekday6" = "Sat";
"lng_weekday7" = "Sun";
"lng_weekday1_full" = "Monday";
"lng_weekday2_full" = "Tuesday";
"lng_weekday3_full" = "Wednesday";
"lng_weekday4_full" = "Thursday";
"lng_weekday5_full" = "Friday";
"lng_weekday6_full" = "Saturday";
"lng_weekday7_full" = "Sunday";
"lng_month_day" = "{month} {day}";
"lng_cancel" = "Cancel";
"lng_continue" = "Continue";
"lng_close" = "Close";
"lng_connecting" = "Connecting..";
"lng_reconnecting" = "Reconnect {count:now|in # s|in # s}..";
"lng_reconnecting_try_now" = "Try now";
"lng_status_service_notifications" = "service notifications";
"lng_status_offline" = "last seen a long time ago";
"lng_status_recently" = "last seen recently";
"lng_status_last_week" = "last seen within a week";
"lng_status_last_month" = "last seen within a month";
"lng_status_invisible" = "invisible";
"lng_status_lastseen_now" = "last seen just now";
"lng_status_lastseen_minutes" = "last seen {count:_not_used_|# minute|# minutes} ago";
"lng_status_lastseen_hours" = "last seen {count:_not_used_|# hour|# hours} ago";
"lng_status_lastseen_today" = "last seen today at {time}";
"lng_status_lastseen_yesterday" = "last seen yesterday at {time}";
"lng_status_lastseen_date" = "last seen {date}";
"lng_status_lastseen_date_time" = "last seen {date} at {time}";
"lng_status_online" = "online";
"lng_status_connecting" = "connecting..";
"lng_chat_status_unaccessible" = "group is unaccessible";
"lng_chat_status_members" = "{count:no members|# member|# members}";
"lng_chat_status_members_online" = "{count:_not_used_|# member|# members}, {count_online:_not_used_|# online|# online}";
"lng_server_error" = "Internal server error.";
"lng_flood_error" = "Too much tries. Please try again later.";
"lng_deleted" = "Unknown";
"lng_intro" = "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.\nIt's [b]fast[/b] and [b]secure[/b].";
"lng_start_msgs" = "START MESSAGING";
"lng_intro_next" = "NEXT";
"lng_intro_finish" = "SIGN UP";
"lng_phone_ph" = "Your phone number";
"lng_phone_title" = "Your Phone";
"lng_phone_desc" = "Please confirm your country code and\nenter your phone number.";
"lng_phone_notreg" = "Note: if you don't have a Telegram account yet,\nplease [b]sign up[/b] with your [a href=\"https://telegram.org/\"]iOS / Android[/a] or {signup_start}here »{signup_end}";
"lng_country_code" = "Country Code";
"lng_bad_country_code" = "Invalid Country Code";
"lng_country_ph" = "Search";
"lng_country_done" = "Done";
"lng_country_none" = "Country not found";
"lng_country_select" = "Select Country";
"lng_code_ph" = "Your code";
"lng_code_desc" = "We have sent you a message with activation\ncode to your phone. Please enter it below.";
"lng_code_call" = "Telegram will dial your number in {minutes}:{seconds}";
"lng_code_calling" = "Requesting a call from Telegram..";
"lng_code_called" = "Telegram dialed your number";
"lng_bad_phone" = "Invalid phone number. Please try again.";
"lng_bad_phone_noreg" = "Phone number not registered.";
"lng_bad_code" = "You have entered an invalid code. Please try again.";
"lng_bad_name" = "Please enter your first and last name.";
"lng_bad_chat_title" = "Please enter new chat title.";
"lng_bad_photo" = "Bad image selected.";
"lng_signup_title" = "Information and photo";
"lng_signup_desc" = "Please enter your name and\nupload a photo.";
"lng_signup_firstname" = "First Name";
"lng_signup_lastname" = "Last Name";
"lng_dlg_filter" = "Search";
"lng_dlg_conversations" = "Conversations";
"lng_dlg_messages" = "Messages";
"lng_dlg_new_group_name" = "Group name";
"lng_dlg_create_group" = "Create";
"lng_no_contacts" = "You have no contacts";
"lng_contacts_loading" = "Loading..";
"lng_contacts_not_found" = "No contacts found";
"lng_settings_profile" = "Profile";
"lng_settings_edit" = "Edit";
"lng_settings_save" = "Save";
"lng_settings_cancel" = "Cancel";
"lng_settings_upload" = "Set Profile Photo";
"lng_settings_badsize" = "This image has bad size, please try other.";
"lng_settings_crop_profile" = "Select square area for your profile photo";
"lng_settings_uploading_photo" = "Uploading photo..";
"lng_username_title" = "Change username";
"lng_username_about" = "You can choose a username on Telegram.\nIf you do, other people will be able to find\nyou by this username and contact you\nwithout knowing your phone number.\n\nYou can use a-z, 0-9 and underscores.\nMinimum length is 5 characters.";
"lng_username_invalid" = "This name is invalid.";
"lng_username_occupied" = "This name is already occupied.";
"lng_username_too_short" = "This name is too short.";
"lng_username_bad_symbols" = "This name has bad symbols.";
"lng_username_available" = "This name is available.";
"lng_username_not_found" = "User @{user} not found.";
"lng_settings_section_contact_info" = "Contact info";
"lng_settings_phone_number" = "Phone number:";
"lng_settings_username" = "Username:";
"lng_settings_choose_username" = "choose username";
"lng_settings_change_username" = "Change";
"lng_settings_section_notify" = "Notifications";
"lng_settings_desktop_notify" = "Desktop notifications";
"lng_settings_show_name" = "Show sender's name";
"lng_settings_show_preview" = "Show message preview";
"lng_settings_sound_notify" = "Play sound";
"lng_notification_title" = "Telegram Desktop";
"lng_notification_preview" = "You have a new message";
"lng_settings_section_general" = "General";
"lng_settings_auto_update" = "Update automatically";
"lng_settings_current_version" = "Version {version}";
"lng_settings_check_now" = "Check for updates";
"lng_settings_update_checking" = "Checking for updates..";
"lng_settings_latest_installed" = "Latest version is installed";
"lng_settings_downloading" = "Downloading update {ready} / {total} Mb..";
"lng_settings_update_ready" = "New version is ready";
"lng_settings_update_now" = "Restart Now";
"lng_settings_update_fail" = "Update check failed :(";
"lng_settings_workmode_tray" = "Show tray icon";
"lng_settings_workmode_window" = "Show taskbar icon";
"lng_settings_auto_start" = "Launch Telegram when system starts";
"lng_settings_start_min" = "Launch minimized";
"lng_settings_add_sendto" = "Place Telegram in «Send to» menu";
"lng_settings_scale_label" = "Interface scale";
"lng_settings_scale_auto" = "Auto ({cur})";
"lng_settings_section_chat" = "Chat options";
"lng_settings_replace_emojis" = "Replace emojis";
"lng_settings_view_emojis" = "View list";
"lng_settings_emoji_list" = "List of supported emojis";
"lng_settings_send_enter" = "Send by Enter";
"lng_settings_send_ctrlenter" = "Send by Ctrl+Enter";
"lng_settings_send_cmdenter" = "Send by Cmd+Enter";
"lng_settings_cats_and_dogs" = "Allow cats and dogs";
"lng_download_path_dont_ask" = "Don't ask download path for each file";
"lng_download_path_label" = "Download path: ";
"lng_download_path_temp" = "temp folder";
"lng_download_path_default" = "default folder";
"lng_download_path_clear" = "Clear All";
"lng_download_path_header" = "Choose download path";
"lng_download_path_default_radio" = "Telegram folder in system «Downloads»";
"lng_download_path_temp_radio" = "Temp folder, cleared on logout or uninstall";
"lng_download_path_dir_radio" = "Custom folder, cleared only manually";
"lng_download_path_choose" = "Choose download path";
"lng_sure_clear_downloads" = "Do you want to remove all downloaded files from temp folder? It is done automatically on logout or program uninstall.";
"lng_download_path_failed" = "File download could not be started. It could happen because of a bad download location.\n\nYou can change download path in Settings.";
"lng_download_path_settings" = "Go to Settings";
"lng_download_finish_failed" = "File download could not be finished.\n\nWould you like to try again?";
"lng_download_path_clearing" = "Clearing..";
"lng_download_path_cleared" = "Cleared!";
"lng_download_path_clear_failed" = "Clear failed :(";
"lng_settings_section_cache" = "Local storage";
"lng_settings_no_images_cached" = "No cached images found!";
"lng_settings_images_cached" = "Cached: {count:_not_used_|# image|# images}, {size}";
"lng_local_images_clear" = "Clear All";
"lng_local_images_clearing" = "Clearing..";
"lng_local_images_cleared" = "Cleared!";
"lng_local_images_clear_failed" = "Clear failed :(";
"lng_settings_section_advanced" = "Advanced";
"lng_connection_type" = "Connection type:";
"lng_connection_auto_connecting" = "Default (connecting..)";
"lng_connection_auto" = "Default ({type} used)";
"lng_connection_http_proxy" = "HTTP with proxy";
"lng_connection_tcp_proxy" = "TCP with proxy";
"lng_connection_header" = "Connection type";
"lng_connection_auto_rb" = "Auto (TCP if available or HTTP)";
"lng_connection_http_proxy_rb" = "HTTP with custom http-proxy";
"lng_connection_tcp_proxy_rb" = "TCP with custom socks5-proxy";
"lng_connection_host_ph" = "Hostname";
"lng_connection_port_ph" = "Port";
"lng_connection_user_ph" = "Username";
"lng_connection_password_ph" = "Password";
"lng_connection_save" = "Save";
"lng_settings_reset" = "Terminate other sessions";
"lng_settings_reset_done" = "Other sessions terminated";
"lng_settings_logout" = "Log Out";
"lng_sure_logout" = "Are you sure you want to log out?";
"lng_settings_need_restart" = "You need to restart for applying\nsome of the new settings. Restart now?";
"lng_settings_restart_now" = "Restart";
"lng_settings_restart_later" = "Later";
"lng_profile_chat_unaccessible" = "Group is unaccessible";
"lng_topbar_info" = "Info";
"lng_profile_settings_section" = "Settings";
"lng_profile_participants_section" = "Participants";
"lng_profile_info" = "Contact info";
"lng_profile_group_info" = "Group info";
"lng_profile_add_contact" = "Add Contact";
"lng_profile_edit_contact" = "Edit";
"lng_profile_edit_group" = "Edit";
"lng_profile_enable_notifications" = "Notifications";
"lng_profile_clear_history" = "Clear history";
"lng_profile_send_message" = "Send Message";
"lng_profile_share_contact" = "Share Contact";
"lng_profile_delete_contact" = "Delete";
"lng_profile_set_group_photo" = "Set Photo";
"lng_profile_add_participant" = "Add Member";
"lng_profile_delete_and_exit" = "Leave";
"lng_profile_kick" = "Kick";
"lng_profile_sure_kick" = "Kick {user} from the group?";
"lng_profile_loading" = "Loading..";
"lng_profile_shared_media" = "Shared media";
"lng_profile_no_media" = "No media in this conversation.";
"lng_profile_photos" = "{count:_not_used_|# photo|# photos} »";
"lng_profile_photos_header" = "Photos overview";
"lng_profile_videos" = "{count:_not_used_|# video file|# video files} »";
"lng_profile_videos_header" = "Video files overview";
"lng_profile_documents" = "{count:_not_used_|# document|# documents} »";
"lng_profile_documents_header" = "Documents overview";
"lng_profile_audios" = "{count:_not_used_|# voice message|# voice messages} »";
"lng_profile_audios_header" = "Voice messages overview";
"lng_profile_show_all_types" = "Show all types";
"lng_profile_copy_phone" = "Copy phone number";
"lng_participant_filter" = "Search";
"lng_participant_invite" = "Invite";
"lng_create_new_group" = "New Group";
"lng_create_group_next" = "Next";
"lng_create_group_title" = "New Group";
"lng_sure_delete_contact" = "Are you sure, you want to delete {contact} from your contact list?";
"lng_sure_delete_history" = "Are you sure, you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
"lng_sure_delete_and_exit" = "Are you sure, you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
"lng_sure_enable_debug" = "Do you want to enable DEBUG mode?\n\nAll network events will be logged.";
"lng_message_empty" = "(empty)";
"lng_action_add_user" = "{from} added {user}";
"lng_action_kick_user" = "{from} kicked {user}";
"lng_action_user_left" = "{from} left the group";
"lng_action_user_joined" = "{from} joined the group";
"lng_action_user_photo" = "{from} added a new profile photo";
"lng_action_user_registered" = "{from} just joined Telegram";
"lng_action_removed_photo" = "{from} removed group photo";
"lng_action_changed_photo" = "{from} changed group photo";
"lng_action_changed_title" = "{from} changed group name to «{title}»";
"lng_action_created_chat" = "{from} created group «{title}»";
"lng_forwarded_from" = "Forwarded from ";
"lng_attach_failed" = "Failed";
"lng_attach_file" = "Document";
"lng_attach_photo" = "Photo";
"lng_media_type" = "Media type";
"lng_media_type_photos" = "Photos";
"lng_media_type_videos" = "Video files";
"lng_media_type_documents" = "Documents";
"lng_media_type_audios" = "Voice messages";
"lng_media_open_with" = "Open With";
"lng_media_download" = "Download";
"lng_media_cancel" = "Cancel";
"lng_media_video" = "Video file";
"lng_media_audio" = "Voice message";
"lng_in_dlg_photo" = "Photo";
"lng_in_dlg_video" = "Video";
"lng_in_dlg_geo" = "Map";
"lng_in_dlg_contact" = "Contact";
"lng_in_dlg_audio" = "Audio";
"lng_in_dlg_document" = "Document";
"lng_send_button" = "Send";
"lng_message_ph" = "Write a message..";
"lng_empty_history" = "";
"lng_willbe_history" = "Please select chat to start messaging";
"lng_message_with_from" = "[c]{from}:[/c] {message}";
"lng_from_you" = "You";
"lng_typing" = "typing";
"lng_user_typing" = "{user} is typing";
"lng_users_typing" = "{user} and {second_user} are typing";
"lng_many_typing" = "{count:_not_used_|# is|# are} typing";
"lng_unread_bar" = "{count:_not_used_|# unread message|# unread messages}";
"lng_maps_point" = "Location";
"lng_save_photo" = "Save image";
"lng_save_video" = "Save video";
"lng_save_audio" = "Save audio";
"lng_save_document" = "Save document";
"lng_save_downloaded" = "{ready} / {total} {mb}";
"lng_duration_and_size" = "{duration}, {size}";
"lng_choose_images" = "Choose images";
"lng_context_open_link" = "Open Link";
"lng_context_copy_link" = "Copy Link";
"lng_context_open_email" = "Write to this address";
"lng_context_copy_email" = "Copy email address";
"lng_context_open_hashtag" = "Search by hashtag";
"lng_context_copy_hashtag" = "Copy hashtag";
"lng_context_open_image" = "Open Image";
"lng_context_save_image" = "Save Image As..";
"lng_context_forward_image" = "Forward Image";
"lng_context_delete_image" = "Delete Image";
"lng_context_copy_image" = "Copy Image";
"lng_context_close_image" = "Close Image";
"lng_context_cancel_download" = "Cancel Download";
"lng_context_show_in_folder" = "Show in Folder";
"lng_context_show_in_finder" = "Show in Finder";
"lng_context_open_video" = "Open Video";
"lng_context_save_video" = "Save Video As..";
"lng_context_open_audio" = "Open Audio";
"lng_context_save_audio" = "Save Audio As..";
"lng_context_open_document" = "Open File";
"lng_context_save_document" = "Save File As..";
"lng_context_forward_file" = "Forward File";
"lng_context_delete_file" = "Delete File";
"lng_context_close_file" = "Close File";
"lng_context_copy_text" = "Copy Message Text";
"lng_context_to_msg" = "Go To Message";
"lng_context_forward_msg" = "Forward Message";
"lng_context_delete_msg" = "Delete Message";
"lng_context_select_msg" = "Select Message";
"lng_context_cancel_upload" = "Cancel Upload";
"lng_context_copy_selected" = "Copy Selected Text";
"lng_context_forward_selected" = "Forward Selected";
"lng_context_delete_selected" = "Delete Selected";
"lng_context_clear_selection" = "Clear Selection";
"lng_really_send_image" = "Do you want to send this image?";
"lng_really_send_file" = "Do you want to send this file?";
"lng_really_share_contact" = "Do you want to share this contact?";
"lng_send_image_compressed" = "Send compressed image";
"lng_forward_choose" = "Choose recipient..";
"lng_forward_confirm" = "Forward to {recipient}?";
"lng_forward_share_contact" = "Share contact to {recipient}?";
"lng_forward_send_file_confirm" = "Send «{name}» to {recipient}?";
"lng_forward_send_files_confirm" = "Send selected files to {recipient}?";
"lng_forward" = "Forward";
"lng_forward_send" = "Send";
"lng_contact_phone" = "Phone number";
"lng_enter_contact_data" = "New Contact";
"lng_edit_group_title" = "Edit group name";
"lng_edit_contact_title" = "Edit contact name";
"lng_edit_self_title" = "Edit your name";
"lng_confirm_contact_data" = "New Contact";
"lng_add_contact" = "Create";
"lng_add_contact_button" = "Add Contact";
"lng_contacts_header" = "Contacts";
"lng_contact_not_joined" = "Unfortunately {name} did not join Telegram yet, but you can send your friend an invitation.\n\nWe will notify you about any of your contacts who is joining Telegram.";
"lng_try_other_contact" = "Try other";
"lng_contacts_done" = "Cancel";
"lng_drag_images_here" = "Drop images here";
"lng_drag_photos_here" = "Drop photos here";
"lng_drag_files_here" = "Drop files here";
"lng_drag_to_send_quick" = "to send them in a quick way";
"lng_drag_to_send_no_compression" = "to send them without compression";
"lng_drag_to_send_documents" = "to send them as documents";
"lng_selected_clear" = "Cancel";
"lng_selected_delete" = "Delete";
"lng_selected_forward" = "Forward";
"lng_selected_count" = "{count:_not_used_|# message|# messages}";
"lng_selected_cancel_sure_this" = "Do you want to cancel this upload?";
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
"lng_selected_delete_sure" = "Do you want to delete {count:_not_used_|# message|# messages}?";
"lng_selected_delete_confirm" = "Delete";
"lng_emoji_no_recent" = "Recent emojis will be here";
"lng_about_version" = "Version {version}";
"lng_about_text" = "Official free messaging app based on [a href=\"https://core.telegram.org/mtproto\"]MTProto[/a] and\n[a href=\"https://core.telegram.org/api\"]Telegram API[/a] for speed and security\n\nThis software is licensed under [a href=\"https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\"]GNU GPL[/a] version 3,\nsource code is available on [a href=\"https://github.com/telegramdesktop/tdesktop\"]GitHub[/a].";
"lng_about_done" = "Done";
"lng_search_found_results" = "{count:No messages found|Found # message|Found # messages}";
"lng_search_global_results" = "Global search results";
"lng_mediaview_save" = "Download";
"lng_mediaview_forward" = "Forward";
"lng_mediaview_delete" = "Delete";
"lng_mediaview_single_photo" = "Single Photo";
"lng_mediaview_group_photo" = "Group Photo";
"lng_mediaview_profile_photo" = "Profile Photo";
"lng_mediaview_n_of_count" = "Photo {n} of {count}";
"lng_mediaview_doc_image" = "Document";
"lng_mediaview_saved" = "Image was saved to your [c]Downloads[/c] folder";
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Terminate other sessions.\n\nThanks,\nThe Telegram Team";
// Mac specific
"lng_mac_choose_app" = "Choose Application";
"lng_mac_choose_text" = "Choose an application to open the document \"{file}\".";
"lng_mac_enable_filter" = "Enable:";
"lng_mac_recommended_apps" = "Recommended Applications";
"lng_mac_all_apps" = "All Applications";
"lng_mac_always_open_with" = "Always Open With";
"lng_mac_this_app_can_open" = "This application can open \"{file}\".";
"lng_mac_not_known_app" = "It's not known if this application can open \"{file}\".";
"lng_mac_menu_about" = "About Telegram";
"lng_mac_menu_preferences" = "Preferences...";
"lng_mac_menu_file" = "File";
"lng_mac_menu_logout" = "Log Out";
"lng_mac_menu_edit" = "Edit";
"lng_mac_menu_undo" = "Undo";
"lng_mac_menu_redo" = "Redo";
"lng_mac_menu_cut" = "Cut";
"lng_mac_menu_copy" = "Copy";
"lng_mac_menu_paste" = "Paste";
"lng_mac_menu_delete" = "Delete";
"lng_mac_menu_select_all" = "Select All";
"lng_mac_menu_window" = "Window";
"lng_mac_menu_contacts" = "Contacts";
"lng_mac_menu_add_contact" = "Add Contact";
"lng_mac_menu_new_group" = "New Group";
"lng_mac_menu_show" = "Show Telegram";
// Keys finished

View File

@ -1,538 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
direction: "LTR";
lng_maintitle: "Telegram Desktop";
lng_menu_contacts: "Contacts";
lng_menu_settings: "Settings";
lng_menu_about: "About";
lng_menu_update: "Update";
lng_menu_restart: "Restart";
lng_menu_back: "Back";
lng_open_from_tray: "Open Telegram";
lng_minimize_to_tray: "Minimize to tray";
lng_quit_from_tray: "Quit Telegram";
lng_tray_icon_text: "Telegram is still running here,
you can change this from settings page.
If this icon disappears from tray menu,
you can drag it back here from hidden icons.";
lng_month1: "January";
lng_month2: "February";
lng_month3: "March";
lng_month4: "April";
lng_month5: "May";
lng_month6: "June";
lng_month7: "July";
lng_month8: "August";
lng_month9: "September";
lng_month10: "October";
lng_month11: "November";
lng_month12: "December";
lng_weekday1: "Mon";
lng_weekday2: "Tue";
lng_weekday3: "Wed";
lng_weekday4: "Thu";
lng_weekday5: "Fri";
lng_weekday6: "Sat";
lng_weekday7: "Sun";
lng_weekday1_full: "Monday";
lng_weekday2_full: "Tuesday";
lng_weekday3_full: "Wednesday";
lng_weekday4_full: "Thursday";
lng_weekday5_full: "Friday";
lng_weekday6_full: "Saturday";
lng_weekday7_full: "Sunday";
lng_month_day: "{month} {day}";
lng_cancel: "Cancel";
lng_continue: "Continue";
lng_close: "Close";
lng_connecting: "Connecting..";
lng_reconnecting: "Reconnect in %1 s..";
lng_reconnecting_try_now: "Try now";
lng_status_service_notifications: "service notifications";
lng_status_offline: "last seen a long time ago";
lng_status_recently: "last seen recently";
lng_status_last_week: "last seen within a week";
lng_status_last_month: "last seen within a month";
lng_status_invisible: "invisible";
lng_status_lastseen: "last seen {when}";
lng_status_lastseen_now: "just now";
lng_status_lastseen_minute: "%1 minute ago";
lng_status_lastseen_minutes: "%1 minutes ago";
lng_status_lastseen_hour: "%1 hour ago";
lng_status_lastseen_hours: "%1 hours ago";
lng_status_lastseen_today: "today at {time}";
lng_status_lastseen_yesterday: "yesterday at {time}";
lng_status_lastseen_date: "{date}";
lng_status_lastseen_date_time: "{date} at {time}";
lng_status_online: "online";
lng_status_connecting: "connecting..";
lng_chat_no_members: "Group is unaccessible";
lng_chat_members: "%1 members";
lng_chat_members_online: "%1 members, %2 online";
lng_server_error: "Internal server error.";
lng_flood_error: "Too much tries. Please try again later.";
lng_deleted: "Unknown";
lng_intro: "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.
It's [b]fast[/b] and [b]secure[/b].";
lng_start_msgs: "START MESSAGING";
lng_intro_next: "NEXT";
lng_intro_finish: "SIGN UP";
lng_phone_ph: "Your phone number";
lng_phone_title: "Your Phone";
lng_phone_desc: "Please confirm your country code and
enter your phone number.";
lng_phone_notreg: "Note: if you don't have a Telegram account yet,
please [b]sign up[/b] with your [a href=\"https://telegram.org/\"]iOS / Android[/a] or {signup}here »{/signup}";
lng_country_code: "Country Code";
lng_bad_country_code: "Invalid Country Code";
lng_country_ph: "Search";
lng_country_done: "Done";
lng_country_none: "Country not found";
lng_country_select: "Select Country";
lng_code_ph: "Your code";
lng_code_desc: "We have sent you a message with activation
code to your phone. Please enter it below.";
lng_code_call: "Telegram will dial your number in %1:%2";
lng_code_calling: "Requesting a call from Telegram..";
lng_code_called: "Telegram dialed your number";
lng_bad_phone: "Invalid phone number. Please try again.";
lng_bad_phone_noreg: "Phone number not registered.";
lng_bad_code: "You have entered an invalid code. Please try again.";
lng_bad_name: "Please enter your first and last name.";
lng_bad_chat_title: "Please enter new chat title.";
lng_bad_photo: "Bad image selected.";
lng_signup_title: "Information and photo";
lng_signup_desc: "Please enter your name and
upload a photo.";
lng_signup_firstname: "First Name";
lng_signup_lastname: "Last Name";
lng_dlg_filter: "Search";
lng_dlg_conversations: "Conversations";
lng_dlg_messages: "Messages";
lng_dlg_new_group_name: "Group name";
lng_dlg_create_group: "Create";
lng_no_contacts: "You have no contacts";
lng_contacts_loading: "Loading..";
lng_contacts_not_found: "No contacts found";
lng_settings_profile: "Profile";
lng_settings_edit: "Edit";
lng_settings_save: "Save";
lng_settings_cancel: "Cancel";
lng_settings_upload: "Set Profile Photo";
lng_settings_badsize: "This image has bad size, please try other.";
lng_settings_crop_profile: "Select square area for your profile photo";
lng_settings_uploading_photo: "Uploading photo..";
lng_username_title: "Change username";
lng_username_about: "You can choose a username on Telegram.
If you do, other people will be able to find
you by this username and contact you
without knowing your phone number.
You can use a-z, 0-9 and underscores.
Minimum length is 5 characters.";
lng_username_invalid: "This name is invalid.";
lng_username_occupied: "This name is already occupied.";
lng_username_too_short: "This name is too short.";
lng_username_bad_symbols: "This name has bad symbols.";
lng_username_available: "This name is available.";
lng_username_not_found: "User @{user} not found.";
lng_settings_section_contact_info: "Contact info";
lng_settings_phone_number: "Phone number:";
lng_settings_username: "Username:";
lng_settings_choose_username: "choose username";
lng_settings_change_username: "Change";
lng_settings_section_notify: "Notifications";
lng_settings_desktop_notify: "Desktop notifications";
lng_settings_show_name: "Show sender's name";
lng_settings_show_preview: "Show message preview";
lng_settings_sound_notify: "Play sound";
lng_notification_title: "Telegram Desktop";
lng_notification_preview: "You have a new message";
lng_settings_section_general: "General";
lng_settings_auto_update: "Update automatically";
lng_settings_current_version: "Version {version}";
lng_settings_check_now: "Check for updates";
lng_settings_update_checking: "Checking for updates..";
lng_settings_latest_installed: "Latest version is installed";
lng_settings_downloading: "Downloading update {ready} / {total} Mb..";
lng_settings_update_ready: "New version is ready";
lng_settings_update_now: "Restart Now";
lng_settings_update_fail: "Update check failed :(";
lng_settings_workmode_tray: "Show tray icon";
lng_settings_workmode_window: "Show taskbar icon";
lng_settings_auto_start: "Launch Telegram when system starts";
lng_settings_start_min: "Launch minimized";
lng_settings_add_sendto: "Place Telegram in «Send to» menu";
lng_settings_scale_label: "Interface scale";
lng_settings_scale_auto: "Auto ({cur})";
lng_settings_section_chat: "Chat options";
lng_settings_replace_emojis: "Replace emojis";
lng_settings_view_emojis: "View list";
lng_settings_emoji_list: "List of supported emojis";
lng_settings_send_enter: "Send by Enter";
lng_settings_send_ctrlenter: "Send by Ctrl+Enter";
lng_settings_send_cmdenter: "Send by Cmd+Enter";
lng_settings_cats_and_dogs: "Allow cats and dogs";
lng_download_path_dont_ask: "Don't ask download path for each file";
lng_download_path_label: "Download path: ";
lng_download_path_temp: "temp folder";
lng_download_path_default: "default folder";
lng_download_path_clear: "Clear All";
lng_download_path_header: "Choose download path";
lng_download_path_default_radio: "Telegram folder in system «Downloads»";
lng_download_path_temp_radio: "Temp folder, cleared on logout or uninstall";
lng_download_path_dir_radio: "Custom folder, cleared only manually";
lng_download_path_choose: "Choose download path";
lng_sure_clear_downloads: "Do you want to remove all downloaded files from temp folder? It is done automatically on logout or program uninstall.";
lng_download_path_failed: "File download could not be started. It could happen because of a bad download location.
You can change download path in Settings.";
lng_download_path_settings: "Go to Settings";
lng_download_finish_failed: "File download could not be finished.
Would you like to try again?";
lng_download_path_clearing: "Clearing..";
lng_download_path_cleared: "Cleared!";
lng_download_path_clear_failed: "Clear failed :(";
lng_settings_section_cache: "Local storage";
lng_settings_no_images_cached: "No cached images found!";
lng_settings_image_cached: "Cached: {count} image, {size}";
lng_settings_images_cached: "Cached: {count} images, {size}";
lng_local_images_clear: "Clear All";
lng_local_images_clearing: "Clearing..";
lng_local_images_cleared: "Cleared!";
lng_local_images_clear_failed: "Clear failed :(";
lng_settings_section_advanced: "Advanced";
lng_connection_type: "Connection type:";
lng_connection_auto_connecting: "Default (connecting..)";
lng_connection_auto: "Default ({type} used)";
lng_connection_http_proxy: "HTTP with proxy";
lng_connection_tcp_proxy: "TCP with proxy";
lng_connection_header: "Connection type";
lng_connection_auto_rb: "Auto (TCP if available or HTTP)";
lng_connection_http_proxy_rb: "HTTP with custom http-proxy";
lng_connection_tcp_proxy_rb: "TCP with custom socks5-proxy";
lng_connection_host_ph: "Hostname";
lng_connection_port_ph: "Port";
lng_connection_user_ph: "Username";
lng_connection_password_ph: "Password";
lng_connection_save: "Save";
lng_settings_reset: "Terminate other sessions";
lng_settings_reset_done: "Other sessions terminated";
lng_settings_logout: "Log Out";
lng_sure_logout: "Are you sure you want to log out?";
lng_settings_need_restart: "You need to restart for applying
some of the new settings. Restart now?";
lng_settings_restart_now: "Restart";
lng_settings_restart_later: "Later";
lng_topbar_info: "Info";
lng_profile_settings_section: "Settings";
lng_profile_participants_section: "Participants";
lng_profile_info: "Contact info";
lng_profile_group_info: "Group info";
lng_profile_add_contact: "Add Contact";
lng_profile_edit_contact: "Edit";
lng_profile_edit_group: "Edit";
lng_profile_phone: "Phone number: {phone}";
lng_profile_enable_notifications: "Notifications";
lng_profile_clear_history: "Clear history";
lng_profile_send_message: "Send Message";
lng_profile_share_contact: "Share Contact";
lng_profile_delete_contact: "Delete";
lng_profile_set_group_photo: "Set Photo";
lng_profile_add_participant: "Add Member";
lng_profile_delete_and_exit: "Leave";
lng_profile_kick: "Kick";
lng_profile_sure_kick: "Kick {user} from the group?";
lng_profile_loading: "Loading..";
lng_profile_shared_media: "Shared media";
lng_profile_no_media: "No media in this conversation.";
lng_profile_photo: "{count} photo »";
lng_profile_photos: "{count} photos »";
lng_profile_photos_header: "Photos overview";
lng_profile_video: "{count} video file »";
lng_profile_videos: "{count} video files »";
lng_profile_videos_header: "Video files overview";
lng_profile_document: "{count} document »";
lng_profile_documents: "{count} documents »";
lng_profile_documents_header: "Documents overview";
lng_profile_audio: "{count} voice message »";
lng_profile_audios: "{count} voice messages »";
lng_profile_audios_header: "Voice messages overview";
lng_profile_show_all_types: "Show all types";
lng_profile_copy_phone: "Copy phone number";
lng_participant_filter: "Search";
lng_participant_invite: "Invite";
lng_create_new_group: "New Group";
lng_create_group_next: "Next";
lng_create_group_title: "New Group";
lng_sure_delete_contact: "Are you sure, you want to delete {contact} from your contact list?";
lng_sure_delete_history: "Are you sure, you want to delete all message history with {contact}?
This action cannot be undone.";
lng_sure_delete_and_exit: "Are you sure, you want to delete all message history and leave «{group}»?
This action cannot be undone.";
lng_sure_enable_debug: "Do you want to enable DEBUG mode?
All network events will be logged.";
lng_message_empty: "(empty)";
lng_action_add_user: "{from} added {user}";
lng_action_kick_user: "{from} kicked {user}";
lng_action_user_left: "{from} left the group";
lng_action_user_joined: "{from} joined the group";
lng_action_user_photo: "{from} added a new profile photo";
lng_action_user_registered: "{from} just joined Telegram";
lng_action_removed_photo: "{from} removed group photo";
lng_action_changed_photo: "{from} changed group photo";
lng_action_changed_title: "{from} changed group name to «{title}»";
lng_action_created_chat: "{from} created group «{title}»";
lng_action_checked_in: "{from} checked in";
lng_forwarded_from: "Forwarded from ";
lng_attach_failed: "Failed";
lng_attach_file: "Document";
lng_attach_photo: "Photo";
lng_media_type: "Media type";
lng_media_type_photos: "Photos";
lng_media_type_videos: "Video files";
lng_media_type_documents: "Documents";
lng_media_type_audios: "Voice messages";
lng_media_open_with: "Open With";
lng_media_download: "Download";
lng_media_cancel: "Cancel";
lng_media_video: "Video file";
lng_media_audio: "Voice message";
lng_in_dlg_photo: "Photo";
lng_in_dlg_video: "Video";
lng_in_dlg_geo: "Map";
lng_in_dlg_contact: "Contact";
lng_in_dlg_audio: "Audio";
lng_in_dlg_document: "Document";
lng_send_button: "Send";
lng_message_ph: "Write a message..";
lng_empty_history: "";
lng_willbe_history: "Please select chat to start messaging";
lng_message_with_from: "[c]{from}:[/c] {message}";
lng_from_you: "You";
lng_typing: "typing";
lng_user_typing: "{user} is typing";
lng_users_typing: "{user1} and {user2} are typing";
lng_many_typing: "{n} are typing";
lng_unread_bar: "%1 unread messages";
lng_maps_point: "Location";
lng_save_photo: "Save image";
lng_save_video: "Save video";
lng_save_audio: "Save audio";
lng_save_document: "Save document";
lng_save_downloaded: "{ready} / {total} {mb}";
lng_duration_and_size: "{duration}, {size}";
lng_choose_images: "Choose images";
lng_context_open_link: "Open Link";
lng_context_copy_link: "Copy Link";
lng_context_open_email: "Write to this address";
lng_context_copy_email: "Copy email address";
lng_context_open_hashtag: "Search by hashtag";
lng_context_copy_hashtag: "Copy hashtag";
lng_context_open_image: "Open Image";
lng_context_save_image: "Save Image As..";
lng_context_forward_image: "Forward Image";
lng_context_delete_image: "Delete Image";
lng_context_copy_image: "Copy Image";
lng_context_close_image: "Close Image";
lng_context_cancel_download: "Cancel Download";
lng_context_show_in_folder: "Show in Folder";
lng_context_show_in_finder: "Show in Finder";
lng_context_open_video: "Open Video";
lng_context_save_video: "Save Video As..";
lng_context_open_audio: "Open Audio";
lng_context_save_audio: "Save Audio As..";
lng_context_open_document: "Open File";
lng_context_save_document: "Save File As..";
lng_context_forward_file: "Forward File";
lng_context_delete_file: "Delete File";
lng_context_close_file: "Close File";
lng_context_copy_text: "Copy Message Text";
lng_context_to_msg: "Go To Message";
lng_context_forward_msg: "Forward Message";
lng_context_delete_msg: "Delete Message";
lng_context_select_msg: "Select Message";
lng_context_cancel_upload: "Cancel Upload";
lng_context_copy_selected: "Copy Selected Text";
lng_context_forward_selected: "Forward Selected";
lng_context_delete_selected: "Delete Selected";
lng_context_clear_selection: "Clear Selection";
lng_really_send_image: "Do you want to send this image?";
lng_really_send_file: "Do you want to send this file?";
lng_really_share_contact: "Do you want to share this contact?";
lng_send_image_compressed: "Send compressed image";
lng_forward_choose: "Choose recipient..";
lng_forward_confirm: "Forward to {recipient}?";
lng_forward_share_contact: "Share contact to {recipient}?";
lng_forward_send_file_confirm: "Send «{name}» to {recipient}?";
lng_forward_send_files_confirm: "Send selected files to {recipient}?";
lng_forward: "Forward";
lng_forward_send: "Send";
lng_contact_phone: "Phone number";
lng_enter_contact_data: "New Contact";
lng_edit_group_title: "Edit group name";
lng_edit_contact_title: "Edit contact name";
lng_edit_self_title: "Edit your name";
lng_confirm_contact_data: "New Contact";
lng_add_contact: "Create";
lng_add_contact_button: "Add Contact";
lng_contacts_header: "Contacts";
lng_contact_not_joined: "Unfortunately {name} did not join Telegram yet, but you can send your friend an invitation.
We will notify you about any of your contacts who is joining Telegram.";
lng_try_other_contact: "Try other";
lng_contacts_done: "Cancel";
lng_drag_images_here: "Drop images here";
lng_drag_photos_here: "Drop photos here";
lng_drag_files_here: "Drop files here";
lng_drag_to_send_quick: "to send them in a quick way";
lng_drag_to_send_no_compression: "to send them without compression";
lng_drag_to_send_documents: "to send them as documents";
lng_selected_clear: "Cancel";
lng_selected_delete: "Delete";
lng_selected_forward: "Forward";
lng_selected_count_1: "%1 message";
lng_selected_count_5: "%1 messages";
lng_selected_cancel_sure_this: "Do you want to cancel this upload?";
lng_selected_delete_sure_this: "Do you want to delete this message?";
lng_selected_delete_sure_1: "Do you want to delete %1 message?";
lng_selected_delete_sure_5: "Do you want to delete %1 messages?";
lng_selected_delete_confirm: "Delete";
lng_emoji_no_recent: "Recent emojis will be here";
lng_about_version: "Version {version}";
lng_about_text: "Official free messaging app based on [a href=\"https://core.telegram.org/mtproto\"]MTProto[/a] and
[a href=\"https://core.telegram.org/api\"]Telegram API[/a] for speed and security
This software is licensed under [a href=\"https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\"]GNU GPL[/a] version 3,
source code is available on [a href=\"https://github.com/telegramdesktop/tdesktop\"]GitHub[/a].";
lng_about_done: "Done";
lng_search_no_results: "No messages found";
lng_search_one_result: "Found {count} message";
lng_search_n_results: "Found {count} messages";
lng_search_global_results: "Global search results";
lng_mediaview_save: "Download";
lng_mediaview_forward: "Forward";
lng_mediaview_delete: "Delete";
lng_mediaview_single_photo: "Single Photo";
lng_mediaview_group_photo: "Group Photo";
lng_mediaview_profile_photo: "Profile Photo";
lng_mediaview_n_of_count: "Photo {n} of {count}";
lng_mediaview_doc_image: "Document";
lng_mediaview_saved: "Image was saved to your [c]Downloads[/c] folder";
lng_new_authorization: "{name},
We detected a login into your account from a new device on {day}, {date} at {time}
Device: {device}
Location: {location}
If this wasn't you, you can go to Settings — Terminate other sessions.
Thanks,
The Telegram Team";
// Mac specific
lng_mac_choose_app: "Choose Application";
lng_mac_choose_text: "Choose an application to open the document \"{file}\".";
lng_mac_enable_filter: "Enable:";
lng_mac_recommended_apps: "Recommended Applications";
lng_mac_all_apps: "All Applications";
lng_mac_always_open_with: "Always Open With";
lng_mac_this_app_can_open: "This application can open \"{file}\".";
lng_mac_not_known_app: "It's not known if this application can open \"{file}\".";
lng_mac_menu_about: "About Telegram";
lng_mac_menu_preferences: "Preferences...";
lng_mac_menu_file: "File";
lng_mac_menu_logout: "Log Out";
lng_mac_menu_edit: "Edit";
lng_mac_menu_undo: "Undo";
lng_mac_menu_redo: "Redo";
lng_mac_menu_cut: "Cut";
lng_mac_menu_copy: "Copy";
lng_mac_menu_paste: "Paste";
lng_mac_menu_delete: "Delete";
lng_mac_menu_select_all: "Select All";
lng_mac_menu_window: "Window";
lng_mac_menu_contacts: "Contacts";
lng_mac_menu_add_contact: "Add Contact";
lng_mac_menu_new_group: "New Group";
lng_mac_menu_show: "Show Telegram";
// Keys finished

View File

@ -39,10 +39,20 @@ Q_IMPORT_PLUGIN(QWebpPlugin)
typedef unsigned int uint32;
QString layoutDirection;
typedef QMap<QString, QString> LangKeys;
typedef QMap<QByteArray, QString> LangKeys;
LangKeys keys;
typedef QVector<QString> KeysOrder;
typedef QMap<QByteArray, ushort> LangTags;
LangTags tags;
typedef QMap<QByteArray, QVector<QByteArray> > LangKeysTags;
LangKeysTags keysTags;
typedef QVector<QByteArray> KeysOrder;
KeysOrder keysOrder;
KeysOrder tagsOrder;
typedef QMap<QByteArray, QMap<QByteArray, QVector<QString> > > LangKeysCounted;
LangKeysCounted keysCounted;
static const QChar TextCommand(0x0010);
static const QChar TextCommandLangTag(0x0020);
bool skipWhitespaces(const char *&from, const char *end) {
while (from < end && (*from == ' ' || *from == '\n' || *from == '\t' || *from == '\r')) {
@ -86,68 +96,177 @@ bool skipJunk(const char *&from, const char *end) {
return true;
}
inline bool _lngEquals(const QByteArray &key, int from, int len, const char *value, int size) {
if (size != len || from + len > key.size()) return false;
for (const char *v = key.constData() + from, *e = v + len; v != e; ++v, ++value) {
if (*v != *value) return false;
}
return true;
}
#define LNG_EQUALS_PART(key, from, len, value) _lngEquals(key, from, len, value, sizeof(value) - 1)
#define LNG_EQUALS_TAIL(key, from, value) _lngEquals(key, from, key.size() - from, value, sizeof(value) - 1)
#define LNG_EQUALS(key, value) _lngEquals(key, 0, key.size(), value, sizeof(value) - 1)
static const int MaxCountedValues = 6;
void readKeyValue(const char *&from, const char *end) {
if (!skipJunk(from, end)) return;
const char *nameStart = from;
if (*from != '"') throw Exception(QString("Expected quote before key name!"));
const char *nameStart = ++from;
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
++from;
}
QString varName = QString::fromUtf8(nameStart, int(from - nameStart));
if (from == nameStart) throw Exception(QString("Expected key name!"));
QByteArray varName = QByteArray(nameStart, int(from - nameStart));
for (const char *t = nameStart; t + 1 < from; ++t) {
if (*t == '_') {
if (*(t + 1) == '_') throw Exception(QString("Bad key name: %1").arg(QLatin1String(varName)));
++t;
}
}
if (!skipJunk(from, end)) throw Exception("Unexpected end of file!");
if (*from != ':') throw Exception(QString("':' expected after '%1'").arg(varName));
if (from == end || *from != '"') throw Exception(QString("Expected quote after key name in key '%1'!").arg(QLatin1String(varName)));
++from;
if (!skipJunk(++from, end)) throw Exception("Unexpected end of file!");
if (*from != '"') throw Exception(QString("Expected string after '%1:'").arg(varName));
if (!skipJunk(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(QLatin1String(varName)));
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(QLatin1String(varName)));
QByteArray varValue;
const char *start = ++from;
QVector<QByteArray> tagsList;
while (from < end && *from != '"') {
if (*from == '\n') {
throw Exception(QString("Unexpected end of string in key '%1'!").arg(QLatin1String(varName)));
}
if (*from == '\\') {
if (from + 1 >= end) throw Exception("Unexpected end of file!");
if (*(from + 1) == '"' || *(from + 1) == '\\') {
if (from + 1 >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{') {
if (from > start) varValue.append(start, int(from - start));
start = ++from;
} else if (*(from + 1) == 'n') {
if (from > start) varValue.append(start, int(from - start));
varValue.append('\n');
start = (++from) + 1;
}
} else if (*from == '{') {
if (from > start) varValue.append(start, int(from - start));
const char *tagStart = ++from;
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
++from;
}
if (from == tagStart) throw Exception(QString("Expected tag name in key '%1'!").arg(QLatin1String(varName)));
QByteArray tagName = QByteArray(tagStart, int(from - tagStart));
if (from == end || (*from != '}' && *from != ':')) throw Exception(QString("Expected '}' or ':' after tag name in key '%1'!").arg(QLatin1String(varName)));
LangTags::const_iterator i = tags.constFind(tagName);
if (i == tags.cend()) {
i = tags.insert(tagName, tagsOrder.size());
tagsOrder.push_back(tagName);
}
if (0x0020 + *i > 0x00F7) throw Exception(QString("Too many different tags in key '%1'").arg(QLatin1String(varName)));
QString tagReplacer(4, TextCommand);
tagReplacer[1] = TextCommandLangTag;
tagReplacer[2] = QChar(0x0020 + *i);
varValue.append(tagReplacer.toUtf8());
for (int j = 0, s = tagsList.size(); j < s; ++j) {
if (tagsList.at(j) == tagName) throw Exception(QString("Tag '%1' double used in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
}
tagsList.push_back(tagName);
if (*from == ':') {
start = ++from;
QVector<QString> &counted(keysCounted[varName][tagName]);
QByteArray subvarValue;
bool foundtag = false;
while (from < end && *from != '"' && *from != '}') {
if (*from == '|') {
if (from > start) subvarValue.append(start, int(from - start));
counted.push_back(QString::fromUtf8(subvarValue));
subvarValue = QByteArray();
foundtag = false;
start = from + 1;
}
if (*from == '\n') {
throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
}
if (*from == '\\') {
if (from + 1 >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{' || *(from + 1) == '#') {
if (from > start) subvarValue.append(start, int(from - start));
start = ++from;
} else if (*(from + 1) == 'n') {
if (from > start) subvarValue.append(start, int(from - start));
subvarValue.append('\n');
start = (++from) + 1;
}
} else if (*from == '{') {
throw Exception(QString("Unexpected tag inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
} else if (*from == '#') {
if (foundtag) throw Exception(QString("Replacement '#' double used inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
foundtag = true;
if (from > start) subvarValue.append(start, int(from - start));
subvarValue.append(tagReplacer.toUtf8());
start = from + 1;
}
++from;
}
if (from >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (*from == '"') throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (from > start) subvarValue.append(start, int(from - start));
counted.push_back(QString::fromUtf8(subvarValue));
if (counted.size() > MaxCountedValues) {
throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
}
}
start = from + 1;
}
++from;
}
if (from >= end) throw Exception("Unexpected end of file!");
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (from > start) varValue.append(start, int(from - start));
if (!skipJunk(++from, end)) throw Exception("Unexpected end of file!");
if (*from != ';') throw Exception(QString("';' expected after '%1: \"value\"'").arg(varName));
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(QLatin1String(varName)));
skipJunk(++from, end);
if (varName == "direction") {
if (varValue == "LTR" || varValue == "RTL") {
layoutDirection = QString::fromUtf8(varValue);
} else {
throw Exception(QString("Unexpected value for 'direction' key: '%1'").arg(QString::fromUtf8(varValue)));
}
} else if (varName.midRef(0, 4) != "lng_") {
throw Exception(QString("Bad key '%1'").arg(varName));
throw Exception(QString("Unexpected value for 'direction' in key '%1'!").arg(QLatin1String(varName)));
} else if (!LNG_EQUALS_PART(varName, 0, 4, "lng_")) {
throw Exception(QString("Bad key '%1'!").arg(QLatin1String(varName)));
} else if (keys.constFind(varName) != keys.cend()) {
throw Exception(QString("Key doubled '%1'").arg(varName));
throw Exception(QString("Key '%1' doubled!").arg(QLatin1String(varName)));
} else {
keys.insert(varName, QString::fromUtf8(varValue));
keysTags.insert(varName, tagsList);
keysOrder.push_back(varName);
}
}
QString escapeCpp(const QString &key, QString value, bool wideChar) {
QString escapeCpp(const QByteArray &key, QString value, bool wideChar) {
if (value.isEmpty()) return "QString()";
value = value.replace('\\', "\\\\").replace('\n', "\\n").replace('\r', "").replace('"', "\\\"");
QString res;
res.reserve(value.size() * 10);
bool instr = false;
for (const QChar *ch = value.constData(), *e = value.constData() + value.size(); ch != e; ++ch) {
if (ch->unicode() < 32) {
throw Exception(QString("Bad value for key '%1'").arg(key));
} else if (ch->unicode() > 127) {
if (ch->unicode() > 0x00F7) {
if (instr) {
res.append('"');
instr = false;
@ -170,14 +289,39 @@ QString escapeCpp(const QString &key, QString value, bool wideChar) {
res.append('"');
instr = true;
}
res.append(*ch);
if (ch->unicode() == '\\' || ch->unicode() == '\n' || ch->unicode() == '\r' || ch->unicode() == '"') {
res.append('\\');
if (ch->unicode() == '\\' || ch->unicode() == '"') {
res.append(*ch);
} else if (ch->unicode() == '\n') {
res.append('n');
} else if (ch->unicode() == '\r') {
res.append('r');
}
} else if (ch->unicode() < 0x0020) {
if (*ch == TextCommand) {
if (ch + 3 >= e || (ch + 1)->unicode() != TextCommandLangTag || (ch + 2)->unicode() > 0x00F7 || (ch + 2)->unicode() < 0x0020 || *(ch + 3) != TextCommand) {
throw Exception(QString("Bad value for key '%1'").arg(QLatin1String(key)));
} else {
res.append('\\').append('x').append(QString("%1").arg(ch->unicode(), 2, 16, QChar('0')));
res.append('\\').append('x').append(QString("%1").arg((ch + 1)->unicode(), 2, 16, QChar('0')));
res.append('\\').append('x').append(QString("%1").arg((ch + 2)->unicode(), 2, 16, QChar('0')));
res.append('\\').append('x').append(QString("%1").arg((ch + 3)->unicode(), 2, 16, QChar('0')));
ch += 3;
}
} else {
throw Exception(QString("Bad value for key '%1'").arg(QLatin1String(key)));
}
} else {
res.append(*ch);
}
}
}
if (instr) res.append('"');
return (wideChar ? "qsl(" : "QString::fromUtf8(") + res.mid(wideChar ? 2 : 1) + ")";
}
void writeCppKey(QTextStream &tcpp, const QString &key, const QString &val) {
void writeCppKey(QTextStream &tcpp, const QByteArray &key, const QString &val) {
QString wide = escapeCpp(key, val, true), utf = escapeCpp(key, val, false);
if (wide.indexOf(" L\"") < 0) {
tcpp << "\t\t\tset(" << key << ", " << wide << ");\n";
@ -236,49 +380,62 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE\n\
Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
*/\n";
th << "#pragma once\n\n";
for (int i = 0, l = tagsOrder.size(); i < l; ++i) {
th << "enum lngtag_" << tagsOrder[i] << " { lt_" << tagsOrder[i] << " = " << i << " };\n";
}
th << "static const ushort lngtags_cnt = " << tagsOrder.size() << ";\n";
th << "static const ushort lngtags_max_counted_values = " << MaxCountedValues << ";\n";
th << "\n";
th << "enum LangKey {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
th << "\t" << keysOrder[i] << (i ? "" : " = 0") << ",\n";
if (keysTags[keysOrder[i]].isEmpty()) {
th << "\t" << keysOrder[i] << (i ? "" : " = 0") << ",\n";
} else {
th << "\t" << keysOrder[i] << "__tagged" << (i ? "" : " = 0") << ",\n";
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
if (!countedTags.isEmpty()) {
for (QMap<QByteArray, QVector<QString> >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
const QVector<QString> &counted(*j);
for (int k = 0, s = counted.size(); k < s; ++k) {
th << "\t" << keysOrder[i] << "__" + j.key() + QString::number(k).toUtf8() << ",\n";
}
}
}
}
}
th << "\n\tlng_keys_cnt\n";
th << "\n\tlngkeys_cnt\n";
th << "};\n\n";
th << "QString lang(LangKey key);\n";
th << "inline QString langDayOfMonth(const QDate &date) {\n";
th << "\tint32 month = date.month(), day = date.day();\n";
th << "\treturn (month > 0 && month <= 12) ? lang(lng_month_day).replace(qsl(\"{month}\"), lang(LangKey(lng_month1 + month - 1))).replace(qsl(\"{day}\"), QString::number(day)) : qsl(\"{err}\");\n";
th << "}\n\n";
th << "inline QString langDayOfWeek(const QDate &date) {\n";
th << "\tint32 day = date.dayOfWeek();\n";
th << "\treturn (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1 + day - 1)) : qsl(\"{err}\");\n";
th << "}\n\n";
th << "inline QString langDayOfWeekFull(const QDate &date) {\n";
th << "\tint32 day = date.dayOfWeek();\n";
th << "\treturn (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1_full + day - 1)) : qsl(\"{err}\");\n";
th << "}\n\n";
th << "Qt::LayoutDirection langDir();\n\n";
th << "class LangLoader {\n";
th << "public:\n";
th << "\tconst QString &errors() const;\n";
th << "\tconst QString &warnings() const;\n\n";
th << "protected:\n";
th << "\tLangLoader() : _checked(false) {\n";
th << "\t\tmemset(_found, 0, sizeof(_found));\n";
th << "\t}\n\n";
th << "\tbool feedKeyValue(const QString &key, const QString &value);\n\n";
th << "\tvoid error(const QString &text) {\n";
th << "\t\t_err.push_back(text);\n";
th << "\t}\n";
th << "\tvoid warning(const QString &text) {\n";
th << "\t\t_warn.push_back(text);\n";
th << "\t}\n\n";
th << "private:\n";
th << "\tmutable QStringList _err, _warn;\n";
th << "\tmutable QString _errors, _warnings;\n";
th << "\tmutable bool _checked;\n";
th << "\tbool _found[lng_keys_cnt];\n\n";
th << "\tLangLoader(const LangLoader &);\n";
th << "\tLangLoader &operator=(const LangLoader &);\n";
th << "};\n";
th << "LangString lang(LangKey key);\n\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
QVector<QByteArray> &tagsList(keysTags[keysOrder[i]]);
if (tagsList.isEmpty()) continue;
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
th << "inline LangString " << keysOrder[i] << "(";
for (int j = 0, s = tagsList.size(); j < s; ++j) {
if (countedTags[tagsList[j]].isEmpty()) {
th << "lngtag_" << tagsList[j] << ", const QString &" << tagsList[j] << "__val";
} else {
th << "lngtag_" << tagsList[j] << ", float64 " << tagsList[j] << "__val";
}
if (j + 1 < s) th << ", ";
}
th << ") {\n";
th << "\treturn lang(" << keysOrder[i] << "__tagged)";
for (int j = 0, s = tagsList.size(); j < s; ++j) {
if (countedTags[tagsList[j]].isEmpty()) {
th << ".tag(lt_" << tagsList[j] << ", " << tagsList[j] << "__val)";
} else {
th << ".tag(lt_" << tagsList[j] << ", langCounted(" << keysOrder[i] << "__" << tagsList[j] << "0, lt_" << tagsList[j] << ", " << tagsList[j] << "__val))";
}
}
th << ";\n";
th << "}\n";
}
tcpp << "\
/*\n\
@ -304,64 +461,127 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
*/\n";
tcpp << "#include \"stdafx.h\"\n#include \"lang.h\"\n\n";
tcpp << "namespace {\n";
tcpp << "\tQt::LayoutDirection _langDir = Qt::" << (layoutDirection == "LTR" ? "LeftToRight" : "RightToLeft") << ";\n";
tcpp << "\tconst char *_langKeyNames[lng_keys_cnt + 1] = {\n";
tcpp << "\tconst char *_langKeyNames[lngkeys_cnt] = {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
tcpp << "\t\t\"" << keysOrder[i] << "\",\n";
}
tcpp << "\t\t\"lng_keys_cnt\"\n";
tcpp << "\t};\n\n";
tcpp << "\tQString _langValues[lng_keys_cnt + 1];\n\n";
tcpp << "\tLangString _langValues[lngkeys_cnt];\n\n";
tcpp << "\tvoid set(LangKey key, const QString &val) {\n";
tcpp << "\t\t_langValues[key] = val;\n";
tcpp << "\t}\n\n";
tcpp << "\tclass LangInit {\n";
tcpp << "\tpublic:\n";
tcpp << "\t\tLangInit() {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
writeCppKey(tcpp, keysOrder[i], keys[keysOrder[i]]);
writeCppKey(tcpp, keysOrder[i] + (keysTags[keysOrder[i]].isEmpty() ? "" : "__tagged"), keys[keysOrder[i]]);
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
if (!countedTags.isEmpty()) {
for (QMap<QByteArray, QVector<QString> >::const_iterator j = countedTags.cbegin(), e = countedTags.cend(); j != e; ++j) {
const QVector<QString> &counted(*j);
for (int k = 0, s = counted.size(); k < s; ++k) {
writeCppKey(tcpp, keysOrder[i] + "__" + j.key() + QString::number(k).toUtf8(), counted[k]);
}
}
}
}
tcpp << "\t\t\tset(lng_keys_cnt, QString());\n";
tcpp << "\t\t}\n";
tcpp << "\t};\n\n";
tcpp << "\tLangInit _langInit;\n";
tcpp << "}\n\n";
tcpp << "QString lang(LangKey key) {\n";
tcpp << "\treturn _langValues[(key < 0 || key > lng_keys_cnt) ? lng_keys_cnt : key];\n";
tcpp << "}\n\n";
tcpp << "\tLangInit _langInit;\n\n";
tcpp << "Qt::LayoutDirection langDir() {\n";
tcpp << "\treturn _langDir;\n";
tcpp << "}\n\n";
tcpp << "bool LangLoader::feedKeyValue(const QString &key, const QString &value) {\n";
tcpp << "\tif (key == qsl(\"direction\")) {\n";
tcpp << "\t\tif (value == qsl(\"LTR\")) {\n";
tcpp << "\t\t\t_langDir = Qt::LeftToRight;\n";
tcpp << "\t\t\treturn true;\n";
tcpp << "\t\t} else if (value == qsl(\"RTL\")) {\n";
tcpp << "\t\t\t_langDir = Qt::RightToLeft;\n";
tcpp << "\t\t\treturn true;\n";
tcpp << "\t\t} else {\n";
tcpp << "\t\t\t_err.push_back(qsl(\"Bad value for 'direction' key.\"));\n";
tcpp << "\t\t\treturn false;\n";
tcpp << "\tinline bool _lngEquals(const QByteArray &key, int from, int len, const char *value, int size) {\n";
tcpp << "\t\tif (size != len || from + len > key.size()) return false;\n";
tcpp << "\t\tfor (const char *v = key.constData() + from, *e = v + len; v != e; ++v, ++value) {\n";
tcpp << "\t\t\tif (*v != *value) return false;\n";
tcpp << "\t\t}\n";
tcpp << "\t\treturn true; \n";
tcpp << "\t}\n";
tcpp << "\tif (key.size() < 5 || key.midRef(0, 4) != qsl(\"lng_\")) {\n";
tcpp << "\t\t_err.push_back(qsl(\"Bad key name '%1'\").arg(key));\n";
tcpp << "\t\treturn false;\n";
tcpp << "\t}\n\n";
tcpp << "}\n\n";
tcpp << "#define LNG_EQUALS_PART(key, from, len, value) _lngEquals(key, from, len, value, sizeof(value) - 1)\n";
tcpp << "#define LNG_EQUALS_TAIL(key, from, value) _lngEquals(key, from, key.size() - from, value, sizeof(value) - 1)\n";
tcpp << "#define LNG_EQUALS(key, value) _lngEquals(key, 0, key.size(), value, sizeof(value) - 1)\n\n";
tcpp << "LangString lang(LangKey key) {\n";
tcpp << "\treturn (key < 0 || key > lngkeys_cnt) ? QString() : _langValues[key];\n";
tcpp << "}\n\n";
tcpp << "const char *langKeyName(LangKey key) {\n";
tcpp << "\treturn (key < 0 || key > lngkeys_cnt) ? \"\" : _langKeyNames[key];\n";
tcpp << "}\n\n";
tcpp << "ushort LangLoader::tagIndex(const QByteArray &tag) const {\n";
tcpp << "\tif (tag.isEmpty()) return lngtags_cnt;\n\n";
if (!tags.isEmpty()) {
QString tab("\t");
tcpp << "\tconst char *ch = tag.constData(), *e = tag.constData() + tag.size();\n";
QByteArray current;
int depth = current.size();
tcpp << "\tswitch (*ch) {\n";
for (LangTags::const_iterator i = tags.cbegin(), j = i + 1, e = tags.cend(); i != e; ++i) {
QByteArray tag = i.key();
while (depth > 0 && tag.mid(0, depth) != current) {
tcpp << tab.repeated(depth + 1) << "}\n";
current.chop(1);
--depth;
tcpp << tab.repeated(depth + 1) << "break;\n";
}
do {
if (tag == current) break;
char ich = i.key().at(current.size());
tcpp << tab.repeated(current.size() + 1) << "case '" << ich << "':\n";
if (j == e || ich != ((j.key().size() > depth) ? j.key().at(depth) : 0)) {
if (tag == current + ich) {
tcpp << tab.repeated(depth + 1) << "\tif (ch + " << (depth + 1) << " == e) return lt_" << tag << ";\n";
} else {
tcpp << tab.repeated(depth + 1) << "\tif (LNG_EQUALS_TAIL(tag, " << (depth + 1) << ", \"" << i.key().mid(depth + 1) << "\")) return lt_" << tag << ";\n";
}
tcpp << tab.repeated(depth + 1) << "break;\n";
break;
}
++depth;
current += ich;
if (tag == current) {
tcpp << tab.repeated(depth + 1) << "if (ch + " << depth << " == e) {\n";
tcpp << tab.repeated(depth + 1) << "\treturn lt_" << tag << ";\n";
tcpp << tab.repeated(depth + 1) << "}\n";
}
tcpp << tab.repeated(depth + 1) << "if (ch + " << depth << " < e) switch (*(ch + " << depth << ")) {\n";
} while (true);
++j;
}
while (QByteArray() != current) {
tcpp << tab.repeated(depth + 1) << "}\n";
current.chop(1);
--depth;
tcpp << tab.repeated(depth + 1) << "break;\n";
}
tcpp << "\t}\n\n";
}
tcpp << "\treturn lngtags_cnt;\n";
tcpp << "}\n\n";
tcpp << "LangKey LangLoader::keyIndex(const QByteArray &key) const {\n";
tcpp << "\tif (key.size() < 5 || !LNG_EQUALS_PART(key, 0, 4, \"lng_\")) return lngkeys_cnt;\n\n";
if (!keys.isEmpty()) {
QString tab("\t");
tcpp << "\tLangKey keyIndex = lng_keys_cnt;\n";
tcpp << "\tconst QChar *ch = key.constData(), *e = key.constData() + key.size();\n";
QString current("lng_");
tcpp << "\tconst char *ch = key.constData(), *e = key.constData() + key.size();\n";
QByteArray current("lng_");
int depth = current.size();
tcpp << "\tswitch ((ch + " << depth << ")->unicode()) {\n";
tcpp << "\tswitch (*(ch + " << depth << ")) {\n";
for (LangKeys::const_iterator i = keys.cbegin(), j = i + 1, e = keys.cend(); i != e; ++i) {
QString key = i.key();
while (key.midRef(0, depth) != current) {
QByteArray key = i.key();
while (key.mid(0, depth) != current) {
tcpp << tab.repeated(depth - 3) << "}\n";
current.chop(1);
--depth;
@ -370,13 +590,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
do {
if (key == current) break;
QChar ich = i.key().at(current.size());
char ich = i.key().at(current.size());
tcpp << tab.repeated(current.size() - 3) << "case '" << ich << "':\n";
if (j == e || ich != ((j.key().size() > depth) ? j.key().at(depth) : 0)) {
if (key == current + ich) {
tcpp << tab.repeated(depth - 3) << "\tif (ch + " << (depth + 1) << " == e) keyIndex = " << key << ";\n";
tcpp << tab.repeated(depth - 3) << "\tif (ch + " << (depth + 1) << " == e) return " << key << (keysTags[key].isEmpty() ? "" : "__tagged") << ";\n";
} else {
tcpp << tab.repeated(depth - 3) << "\tif (key.midRef(" << (depth + 1) << ") == qsl(\"" << i.key().mid(depth + 1) << "\")) keyIndex = " << key << ";\n";
tcpp << tab.repeated(depth - 3) << "\tif (LNG_EQUALS_TAIL(key, " << (depth + 1) << ", \"" << i.key().mid(depth + 1) << "\")) return " << key << (keysTags[key].isEmpty() ? "" : "__tagged") << ";\n";
}
tcpp << tab.repeated(depth - 3) << "break;\n";
break;
@ -387,52 +607,78 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org\n\
if (key == current) {
tcpp << tab.repeated(depth - 3) << "if (ch + " << depth << " == e) {\n";
tcpp << tab.repeated(depth - 3) << "\tkeyIndex = " << key << ";\n";
tcpp << tab.repeated(depth - 3) << "\treturn " << key << (keysTags[key].isEmpty() ? "" : "__tagged") << ";\n";
tcpp << tab.repeated(depth - 3) << "}\n";
}
tcpp << tab.repeated(depth - 3) << "if (ch + " << depth << " < e) switch ((ch + " << depth << ")->unicode()) {\n";
tcpp << tab.repeated(depth - 3) << "if (ch + " << depth << " < e) switch (*(ch + " << depth << ")) {\n";
} while (true);
++j;
}
while (QString("lng_") != current) {
while (QByteArray("lng_") != current) {
tcpp << tab.repeated(depth - 3) << "}\n";
current.chop(1);
--depth;
tcpp << tab.repeated(depth - 3) << "break;\n";
}
tcpp << "\t}\n\n";
tcpp << "\tif (keyIndex < lng_keys_cnt) {\n";
tcpp << "\t\t_found[keyIndex] = 1;\n";
tcpp << "\t\t_langValues[keyIndex] = value;\n";
tcpp << "\t\treturn true;\n";
}
tcpp << "\treturn lngkeys_cnt;\n";
tcpp << "}\n\n";
tcpp << "bool LangLoader::tagReplaced(LangKey key, ushort tag) const {\n";
if (!tags.isEmpty()) {
tcpp << "\tswitch (key) {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
QVector<QByteArray> &tagsList(keysTags[keysOrder[i]]);
if (tagsList.isEmpty()) continue;
tcpp << "\tcase " << keysOrder[i] << "__tagged: {\n";
tcpp << "\t\tswitch (tag) {\n";
for (int j = 0, s = tagsList.size(); j < s; ++j) {
tcpp << "\t\tcase lt_" << tagsList[j] << ":\n";
}
tcpp << "\t\t\treturn true;\n";
tcpp << "\t\t}\n";
tcpp << "\t} break;\n";
}
tcpp << "\t}\n\n";
}
tcpp << "\t_err.push_back(qsl(\"Unknown key name '%1'\").arg(key));\n";
tcpp << "\treturn false;";
tcpp << "}\n\n";
tcpp << "LangKey LangLoader::subkeyIndex(LangKey key, ushort tag, ushort index) const {\n";
tcpp << "\tif (index >= lngtags_max_counted_values) return lngkeys_cnt;\n\n";
if (!tags.isEmpty()) {
tcpp << "\tswitch (key) {\n";
for (int i = 0, l = keysOrder.size(); i < l; ++i) {
QVector<QByteArray> &tagsList(keysTags[keysOrder[i]]);
if (tagsList.isEmpty()) continue;
QMap<QByteArray, QVector<QString> > &countedTags(keysCounted[keysOrder[i]]);
tcpp << "\tcase " << keysOrder[i] << "__tagged: {\n";
tcpp << "\t\tswitch (tag) {\n";
for (int j = 0, s = tagsList.size(); j < s; ++j) {
if (!countedTags[tagsList[j]].isEmpty()) {
tcpp << "\t\tcase lt_" << tagsList[j] << ": return LangKey(" << keysOrder[i] << "__" << tagsList[j] << "0 + index);\n";
}
}
tcpp << "\t\t}\n";
tcpp << "\t} break;\n";
}
tcpp << "\t}\n\n";
}
tcpp << "\treturn lngkeys_cnt;";
tcpp << "}\n\n";
tcpp << "bool LangLoader::feedKeyValue(LangKey key, const QString &value) {\n";
tcpp << "\tif (key < lngkeys_cnt) {\n";
tcpp << "\t\t_found[key] = 1;\n";
tcpp << "\t\t_langValues[key] = value;\n";
tcpp << "\t\treturn true;\n";
tcpp << "\t}\n";
tcpp << "\treturn false;\n";
tcpp << "}\n\n";
tcpp << "const QString &LangLoader::errors() const {\n";
tcpp << "\tif (_errors.isEmpty() && !_err.isEmpty()) {\n";
tcpp << "\t\t_errors = _err.join('\\n');\n";
tcpp << "\t}\n";
tcpp << "\treturn _errors;\n";
tcpp << "}\n\n";
tcpp << "const QString &LangLoader::warnings() const {\n";
tcpp << "\tif (!_checked) {\n";
tcpp << "\t\tfor (int32 i = 0; i < lng_keys_cnt; ++i) {\n";
tcpp << "\t\t\tif (!_found[i]) {\n";
tcpp << "\t\t\t\t_warn.push_back(qsl(\"No value found for key '%1'\").arg(_langKeyNames[i]));\n";
tcpp << "\t\t\t}\n";
tcpp << "\t\t}\n";
tcpp << "\t\t_checked = true;\n";
tcpp << "\t}\n";
tcpp << "\tif (_warnings.isEmpty() && !_warn.isEmpty()) {\n";
tcpp << "\t\t_warnings = _warn.join('\\n');\n";
tcpp << "\t}\n";
tcpp << "\treturn _warnings;\n";
tcpp << "}\n";
}
QFile cpp(lang_cpp), h(lang_h);

View File

@ -18,7 +18,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
#include "mlmain.h"
int main(int argc, char *argv[]) {
QString lang_in("lang.txt"), lang_out("lang");
QString lang_in("lang.strings"), lang_out("lang");
for (int i = 0; i < argc; ++i) {
if (string("-lang_in") == argv[i]) {
if (++i < argc) lang_in = argv[i];

View File

@ -230,39 +230,29 @@ namespace App {
if (precise) {
QDateTime dOnline(date(online)), dNow(date(now));
if (dOnline.date() == dNow.date()) {
when = lang(lng_status_lastseen_today).replace(qsl("{time}"), dOnline.time().toString(qsl("hh:mm")));
return lng_status_lastseen_today(lt_time, dOnline.time().toString(qsl("hh:mm")));
} else if (dOnline.date().addDays(1) == dNow.date()) {
when = lang(lng_status_lastseen_yesterday).replace(qsl("{time}"), dOnline.time().toString(qsl("hh:mm")));
} else {
when = lang(lng_status_lastseen_date_time).replace(qsl("{date}"), dOnline.date().toString(qsl("dd.MM.yy"))).replace(qsl("{time}"), dOnline.time().toString(qsl("hh:mm")));
return lng_status_lastseen_yesterday(lt_time, dOnline.time().toString(qsl("hh:mm")));
}
return lang(lng_status_lastseen).replace(qsl("{when}"), when);
return lng_status_lastseen_date_time(lt_date, dOnline.date().toString(qsl("dd.MM.yy")), lt_time, dOnline.time().toString(qsl("hh:mm")));
}
int32 minutes = (now - online) / 60;
if (!minutes) {
when = lang(lng_status_lastseen_now);
} else if (minutes == 1) {
when = lang(lng_status_lastseen_minute).arg(minutes);
return lang(lng_status_lastseen_now);
} else if (minutes < 60) {
when = lang(lng_status_lastseen_minutes).arg(minutes);
} else {
int32 hours = (now - online) / 3600;
if (hours == 1) {
when = lang(lng_status_lastseen_hour).arg(hours);
} else if (hours < 12) {
when = lang(lng_status_lastseen_hours).arg(hours);
} else {
QDateTime dOnline(date(online)), dNow(date(now));
if (dOnline.date() == dNow.date()) {
when = lang(lng_status_lastseen_today).replace(qsl("{time}"), dOnline.time().toString(qsl("hh:mm")));
} else if (dOnline.date().addDays(1) == dNow.date()) {
when = lang(lng_status_lastseen_yesterday).replace(qsl("{time}"), dOnline.time().toString(qsl("hh:mm")));
} else {
when = lang(lng_status_lastseen_date).replace(qsl("{date}"), dOnline.date().toString(qsl("dd.MM.yy")));
}
}
return lng_status_lastseen_minutes(lt_count, minutes);
}
return lang(lng_status_lastseen).replace(qsl("{when}"), when);
int32 hours = (now - online) / 3600;
if (hours < 12) {
return lng_status_lastseen_hours(lt_count, hours);
}
QDateTime dOnline(date(online)), dNow(date(now));
if (dOnline.date() == dNow.date()) {
return lng_status_lastseen_today(lt_time, dOnline.time().toString(qsl("hh:mm")));
} else if (dOnline.date().addDays(1) == dNow.date()) {
return lng_status_lastseen_yesterday(lt_time, dOnline.time().toString(qsl("hh:mm")));
}
return lng_status_lastseen_date(lt_date, dOnline.date().toString(qsl("dd.MM.yy")));
}
UserData *feedUsers(const MTPVector<MTPUser> &users) {

View File

@ -129,10 +129,11 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
cSetIntRetinaFactor(int32(cRetinaFactor()));
}
if (!cLangFile().isEmpty()) {
if (!cLangFile().isEmpty() && QFileInfo(cLangFile()).exists()) {
LangLoaderPlain loader(cLangFile());
if (!loader.errors().isEmpty()) {
LOG(("Lang load errors: %1").arg(loader.errors()));
cSetLangErrors(loader.errors());
if (!cLangErrors().isEmpty()) {
LOG(("Lang load errors: %1").arg(cLangErrors()));
} else if (!loader.warnings().isEmpty()) {
LOG(("Lang load warnings: %1").arg(loader.warnings()));
}
@ -689,6 +690,10 @@ void Application::startApp() {
}
}
}
if (!cLangErrors().isEmpty()) {
window->showLayer(new ConfirmBox("Lang failed: " + cLangFile() + "\n\nError: " + cLangErrors(), true, lang(lng_close)));
}
}
void Application::socketDisconnected() {
@ -823,7 +828,7 @@ Window *Application::wnd() {
return mainApp ? mainApp->window : 0;
}
QString Application::lang() {
QString Application::language() {
if (!lng.length()) {
lng = psCurrentLanguage();
}

View File

@ -37,7 +37,7 @@ public:
static Application *app();
static Window *wnd();
static QString lang();
static QString language();
static MainWidget *main();
void onAppUpdate(const MTPhelp_AppUpdate &response);

View File

@ -24,7 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
AboutBox::AboutBox() :
_done(this, lang(lng_about_done), st::aboutCloseButton),
_version(this, qsl("[a href=\"https://desktop.telegram.org/#changelog\"]") + textClean(lang(lng_about_version).replace(qsl("{version}"), QString::fromWCharArray(AppVersionStr))) + qsl("[/a]"), st::aboutVersion, st::defaultTextStyle),
_version(this, qsl("[a href=\"https://desktop.telegram.org/#changelog\"]") + textClean(lng_about_version(lt_version, QString::fromWCharArray(AppVersionStr))) + qsl("[/a]"), st::aboutVersion, st::defaultTextStyle),
_text(this, lang(lng_about_text), st::aboutLabel, st::aboutTextStyle),
_hiding(false), a_opacity(0, 1) {

View File

@ -188,7 +188,7 @@ void AddContactBox::paintEvent(QPaintEvent *e) {
p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, _boxTitle);
} else {
int32 h = size().height() - st::boxPadding.top() * 2 - _retryButton.height() - st::boxPadding.bottom();
p.drawText(QRect(st::boxPadding.left(), st::boxPadding.top(), _width - st::boxPadding.left() - st::boxPadding.right(), h), lang(lng_contact_not_joined).replace(qsl("{name}"), _sentName), style::al_topleft);
p.drawText(QRect(st::boxPadding.left(), st::boxPadding.top(), _width - st::boxPadding.left() - st::boxPadding.right(), h), lng_contact_not_joined(lt_name, _sentName), style::al_topleft);
}
}
} else {
@ -320,7 +320,7 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
_lastInput.hide();
_phoneInput.hide();
_retryButton.show();
int32 theight = st::addContactTitleFont->m.boundingRect(0, 0, _width - st::boxPadding.left() - st::boxPadding.right(), 1, Qt::TextWordWrap, lang(lng_contact_not_joined).replace(qsl("{name}"), _sentName)).height();
int32 theight = st::addContactTitleFont->m.boundingRect(0, 0, _width - st::boxPadding.left() - st::boxPadding.right(), 1, Qt::TextWordWrap, lng_contact_not_joined(lt_name, _sentName)).height();
int32 h = st::boxPadding.top() * 2 + theight + _retryButton.height() + st::boxPadding.bottom();
resize(_width, h);
_retryButton.move(_retryButton.x(), h - _retryButton.height());

View File

@ -120,7 +120,7 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) {
}
if (_state == SearchedState || !searchResults.isEmpty()) {
QString text = searchResults.isEmpty() ? lang(lng_search_no_results) : lang(searchedCount > 1 ? lng_search_n_results : lng_search_one_result).replace(qsl("{count}"), QString::number(searchedCount));
QString text = lng_search_found_results(lt_count, searchResults.isEmpty() ? 0 : searchedCount);
p.fillRect(0, 0, width(), st::searchedBarHeight, st::searchedBarBG->b);
p.setFont(st::searchedBarFont->f);
p.setPen(st::searchedBarColor->p);

View File

@ -287,7 +287,7 @@ QString textcmdStopColor() {
return result.append(TextCommand).append(QChar(TextCommandNoColor)).append(TextCommand);
}
const QChar *skipCommand(const QChar *from, const QChar *end, bool canLink = true) {
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink) {
const QChar *result = from + 1;
if (*from != TextCommand || result >= end) return from;
@ -328,6 +328,10 @@ const QChar *skipCommand(const QChar *from, const QChar *end, bool canLink = tru
case TextCommandSkipBlock:
result += 2;
break;
case TextCommandLangTag:
result += 1;
break;
}
return (result < end && *result == TextCommand) ? (result + 1) : from;
}
@ -434,7 +438,7 @@ public:
}
bool readCommand() {
const QChar *afterCmd = skipCommand(ptr, end, links.size() < 0x7FFF);
const QChar *afterCmd = textSkipCommand(ptr, end, links.size() < 0x7FFF);
if (afterCmd == ptr) {
return false;
}
@ -4137,7 +4141,7 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
}
if (hashtagOffset < domainOffset) {
if (hashtagOffset > nextCmd) {
const QChar *after = skipCommand(start + nextCmd, start + len);
const QChar *after = textSkipCommand(start + nextCmd, start + len);
if (after > start + nextCmd && hashtagOffset < (after - start)) {
nextCmd = offset = matchOffset = after - start;
continue;
@ -4148,7 +4152,7 @@ LinkRanges textParseLinks(const QString &text, bool rich) {
link.len = start + hashtagEnd - link.from;
} else {
if (domainOffset > nextCmd) {
const QChar *after = skipCommand(start + nextCmd, start + len);
const QChar *after = textSkipCommand(start + nextCmd, start + len);
if (after > start + nextCmd && domainOffset < (after - start)) {
nextCmd = offset = matchOffset = after - start;
continue;

View File

@ -340,6 +340,8 @@ enum TextCommands {
TextCommandColor = 0x09,
TextCommandNoColor = 0x0A,
TextCommandSkipBlock = 0x0B,
TextCommandLangTag = 0x20,
};
enum {
@ -467,3 +469,4 @@ QString textcmdLink(ushort lnkIndex, const QString &text);
QString textcmdLink(const QString &url, const QString &text);
QString textcmdStartColor(const style::color &color);
QString textcmdStopColor();
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true);

View File

@ -943,11 +943,11 @@ bool History::updateTyping(uint64 ms, uint32 dots, bool force) {
QString newTypingStr;
int32 cnt = typing.size();
if (cnt > 2) {
newTypingStr = lang(lng_many_typing).replace(qsl("{n}"), QString("%1").arg(cnt));
newTypingStr = lng_many_typing(lt_count, cnt);
} else if (cnt > 1) {
newTypingStr = lang(lng_users_typing).replace(qsl("{user1}"), typing.begin().key()->firstName).replace(qsl("{user2}"), (typing.end() - 1).key()->firstName);
newTypingStr = lng_users_typing(lt_user, typing.begin().key()->firstName, lt_second_user, (typing.end() - 1).key()->firstName);
} else if (cnt) {
newTypingStr = peer->chat ? lang(lng_user_typing).replace(qsl("{user}"), typing.begin().key()->firstName) : lang(lng_typing);
newTypingStr = peer->chat ? lng_user_typing(lt_user, typing.begin().key()->firstName) : lang(lng_typing);
}
if (!newTypingStr.isEmpty()) {
newTypingStr += qsl("...");
@ -1108,6 +1108,13 @@ Histories::Parent::iterator Histories::erase(Histories::Parent::iterator i) {
return Parent::erase(i);
}
void Histories::remove(const PeerId &peer) {
iterator i = find(peer);
if (i != cend()) {
erase(i);
}
}
HistoryItem *Histories::addToBack(const MTPmessage &msg, int msgState) {
PeerId from_id = 0, to_id = 0;
switch (msg.type()) {
@ -2353,7 +2360,7 @@ QString formatDownloadText(qint64 ready, qint64 total) {
totalStr = QString::number(totalKb);
mb = qsl("Kb");
}
return lang(lng_save_downloaded).replace(qsl("{ready}"), readyStr).replace(qsl("{total}"), totalStr).replace(qsl("{mb}"), mb);
return lng_save_downloaded(lt_ready, readyStr, lt_total, totalStr, lt_mb, mb);
}
QString formatDurationText(qint64 duration) {
@ -2362,7 +2369,7 @@ QString formatDurationText(qint64 duration) {
}
QString formatDurationAndSizeText(qint64 duration, qint64 size) {
return lang(lng_duration_and_size).replace(qsl("{duration}"), formatDurationText(duration)).replace(qsl("{size}"), formatSizeText(size));
return lng_duration_and_size(lt_duration, formatDurationText(duration), lt_size, formatSizeText(size));
}
int32 _downloadWidth = 0, _openWithWidth = 0, _cancelWidth = 0, _buttonWidth = 0;
@ -3772,18 +3779,6 @@ HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPD
initDimensions(text);
}
//HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, const MTPDgeoChatMessage &msg) :
// HistoryItem(history, block, msg.vid.v, (msg.vfrom_id.v == MTP::authedId()), false, ::date(msg.vdate), msg.vfrom_id.v), media(0), message(st::msgMinWidth) {
// QString text(qs(msg.vmessage));
// initMedia(msg.vmedia, text);
// initDimensions(text);
//}
//HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg) : message(st::msgMinWidth),
// HistoryItem(history, block, msgId, out, unread, date, from), media(0), _text(st::msgMinWidth), _textWidth(0), _textHeight(0) {
// initDimensions(textClean(msg));
//}
HistoryMessage::HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, const MTPMessageMedia &media) :
HistoryItem(history, block, msgId, out, unread, date, from)
, _text(st::msgMinWidth)
@ -4146,7 +4141,7 @@ void HistoryMessage::drawInDialog(QPainter &p, const QRect &r, bool act, const H
if (_history->peer->chat || out()) {
TextCustomTagsMap custom;
custom.insert(QChar('c'), qMakePair(textcmdStartLink(1), textcmdStopLink()));
msg = lang(lng_message_with_from).replace(qsl("{from}"), textRichPrepare((_from == App::self()) ? lang(lng_from_you) : _from->firstName)).replace(qsl("{message}"), textRichPrepare(msg));
msg = lng_message_with_from(lt_from, textRichPrepare((_from == App::self()) ? lang(lng_from_you) : _from->firstName), lt_message, textRichPrepare(msg));
cache.setRichText(st::dlgHistFont, msg, _textDlgOptions, custom);
} else {
cache.setText(st::dlgHistFont, msg, _textDlgOptions);
@ -4168,10 +4163,6 @@ QString HistoryMessage::notificationHeader() const {
QString HistoryMessage::notificationText() const {
QString msg(_media ? _media->inDialogsText() : _text.original(0, 0xFFFF, false));
if (msg.size() > 0xFF) msg = msg.mid(0, 0xFF) + qsl("..");
// subtitle used
// if (_history->peer->chat || out()) {
// msg = lang(lng_message_with_from).replace(qsl("[c]"), QString()).replace(qsl("[/c]"), QString()).replace(qsl("{from}"), textRichPrepare((_from == App::self()) ? lang(lng_from_you) : _from->firstName)).replace(qsl("{message}"), textRichPrepare(msg));
// }
return msg;
}
@ -4363,35 +4354,41 @@ void HistoryForwarded::getSymbol(uint16 &symbol, bool &after, bool &upon, int32
return HistoryMessage::getSymbol(symbol, after, upon, x, y);
}
QString HistoryServiceMsg::messageByAction(const MTPmessageAction &action, TextLinkPtr &second) {
void HistoryServiceMsg::setMessageByAction(const MTPmessageAction &action) {
TextLinkPtr second;
LangString text = lang(lng_message_empty);
QString from = textcmdLink(1, _from->name);
switch (action.type()) {
case mtpc_messageActionChatAddUser: {
const MTPDmessageActionChatAddUser &d(action.c_messageActionChatAddUser());
if (App::peerFromUser(d.vuser_id) == _from->id) {
return lang(lng_action_user_joined);
text = lng_action_user_joined(lt_from, from);
} else {
UserData *u = App::user(App::peerFromUser(d.vuser_id));
second = TextLinkPtr(new PeerLink(u));
text = lng_action_add_user(lt_from, from, lt_user, textcmdLink(2, u->name));
}
UserData *u = App::user(App::peerFromUser(d.vuser_id));
second = TextLinkPtr(new PeerLink(u));
return lang(lng_action_add_user).replace(qsl("{user}"), textcmdLink(2, u->name));
} break;
case mtpc_messageActionChatCreate: {
const MTPDmessageActionChatCreate &d(action.c_messageActionChatCreate());
return lang(lng_action_created_chat).replace(qsl("{title}"), textClean(qs(d.vtitle)));
text = lng_action_created_chat(lt_from, from, lt_title, textClean(qs(d.vtitle)));
} break;
case mtpc_messageActionChatDeletePhoto: {
return lang(lng_action_removed_photo);
text = lng_action_removed_photo(lt_from, from);
} break;
case mtpc_messageActionChatDeleteUser: {
const MTPDmessageActionChatDeleteUser &d(action.c_messageActionChatDeleteUser());
if (App::peerFromUser(d.vuser_id) == _from->id) {
return lang(lng_action_user_left);
text = lng_action_user_left(lt_from, from);
} else {
UserData *u = App::user(App::peerFromUser(d.vuser_id));
second = TextLinkPtr(new PeerLink(u));
text = lng_action_kick_user(lt_from, from, lt_user, textcmdLink(2, u->name));
}
UserData *u = App::user(App::peerFromUser(d.vuser_id));
second = TextLinkPtr(new PeerLink(u));
return lang(lng_action_kick_user).replace(qsl("{user}"), textcmdLink(2, u->name));
} break;
case mtpc_messageActionChatEditPhoto: {
@ -4399,25 +4396,25 @@ QString HistoryServiceMsg::messageByAction(const MTPmessageAction &action, TextL
if (d.vphoto.type() == mtpc_photo) {
_media = new HistoryPhoto(history()->peer, d.vphoto.c_photo(), st::msgServicePhotoWidth);
}
return lang(lng_action_changed_photo);
text = lng_action_changed_photo(lt_from, from);
} break;
case mtpc_messageActionChatEditTitle: {
const MTPDmessageActionChatEditTitle &d(action.c_messageActionChatEditTitle());
return lang(lng_action_changed_title).replace(qsl("{title}"), textClean(qs(d.vtitle)));
text = lng_action_changed_title(lt_from, from, lt_title, textClean(qs(d.vtitle)));
} break;
case mtpc_messageActionGeoChatCheckin: {
return lang(lng_action_checked_in);
} break;
case mtpc_messageActionGeoChatCreate: {
const MTPDmessageActionGeoChatCreate &d(action.c_messageActionGeoChatCreate());
return lang(lng_action_created_chat).replace(qsl("{title}"), textClean(qs(d.vtitle)));
} break;
default: from = QString(); break;
}
return lang(lng_message_empty);
_text.setText(st::msgServiceFont, text, _historySrvOptions);
if (!from.isEmpty()) {
_text.setLink(1, TextLinkPtr(new PeerLink(_from)));
}
if (second) {
_text.setLink(2, second);
}
return ;
}
HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDmessageService &msg) :
@ -4425,33 +4422,10 @@ HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, cons
, _text(st::msgMinWidth)
, _media(0)
{
TextLinkPtr second;
QString text(messageByAction(msg.vaction, second));
int32 fromPos = text.indexOf(qsl("{from}"));
if (fromPos >= 0) {
text = text.replace(qsl("{from}"), textcmdLink(1, _from->name));
}
_text.setText(st::msgServiceFont, text, _historySrvOptions);
if (fromPos >= 0) {
_text.setLink(1, TextLinkPtr(new PeerLink(_from)));
}
if (second) {
_text.setLink(2, second);
}
setMessageByAction(msg.vaction);
initDimensions();
}
/*
HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDgeoChatMessageService &msg) :
HistoryItem(history, block, msg.vid.v, (msg.vfrom_id.v == MTP::authedId()), false, ::date(msg.vdate), msg.vfrom_id.v), media(0), message(st::msgMinWidth) {
QString text(messageByAction(msg.vaction));
text = text.replace(qsl("{from}"), _from->name);
message.setText(st::msgServiceFont, text);
_maxw = message.maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
_minh = message.minHeight();
}
/**/
HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, bool out, bool unread, HistoryMedia *media) :
HistoryItem(history, block, msgId, out, unread, date, 0)
, _text(st::msgServiceFont, msg, _historySrvOptions, st::dlgMinWidth)
@ -4622,7 +4596,7 @@ void HistoryUnreadBar::initDimensions(const HistoryItem *parent) {
void HistoryUnreadBar::setCount(int32 count) {
if (!count) freezed = true;
if (freezed) return;
text = lang(lng_unread_bar).arg(count);
text = lng_unread_bar(lt_count, count);
}
void HistoryUnreadBar::draw(QPainter &p, uint32 selection) const {

View File

@ -491,6 +491,7 @@ struct Histories : public QHash<PeerId, History*>, public Animated {
void clear();
Parent::iterator erase(Parent::iterator i);
void remove(const PeerId &peer);
~Histories() {
clear();
@ -1503,8 +1504,6 @@ class HistoryMessage : public HistoryItem {
public:
HistoryMessage(History *history, HistoryBlock *block, const MTPDmessage &msg);
// HistoryMessage(History *history, HistoryBlock *block, const MTPDgeoChatMessage &msg);
// HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg);
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, const MTPMessageMedia &media);
HistoryMessage(History *history, HistoryBlock *block, MsgId msgId, bool out, bool unread, QDateTime date, int32 from, const QString &msg, HistoryMedia *media);
@ -1598,7 +1597,6 @@ class HistoryServiceMsg : public HistoryItem {
public:
HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDmessageService &msg);
// HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDgeoChatMessageService &msg);
HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, bool out = false, bool unread = false, HistoryMedia *media = 0);
void initDimensions(const HistoryItem *parent = 0);
@ -1633,7 +1631,7 @@ public:
protected:
QString messageByAction(const MTPmessageAction &action, TextLinkPtr &second);
void setMessageByAction(const MTPmessageAction &action);
Text _text;
HistoryMedia *_media;

View File

@ -1489,24 +1489,25 @@ void HistoryHider::offerPeer(PeerId peer) {
return;
}
offered = App::peer(peer);
QString phrase;
LangString phrase;
QString recipient = offered->chat ? '\xAB' + offered->name + '\xBB' : offered->name;
if (_sharedContact) {
phrase = lang(lng_forward_share_contact);
phrase = lng_forward_share_contact(lt_recipient, recipient);
} else if (_sendPath) {
if (cSendPaths().size() > 1) {
phrase = lang(lng_forward_send_files_confirm);
phrase = lng_forward_send_files_confirm(lt_recipient, recipient);
} else {
QString name(QFileInfo(cSendPaths().front()).fileName());
if (name.size() > 10) {
name = name.mid(0, 8) + '.' + '.';
}
phrase = lang(lng_forward_send_file_confirm).replace(qsl("{name}"), name);
phrase = lng_forward_send_file_confirm(lt_name, name, lt_recipient, recipient);
}
} else {
phrase = lang(lng_forward_confirm);
phrase = lng_forward_confirm(lt_recipient, recipient);
}
toText.setText(st::boxFont, phrase.replace(qsl("{recipient}"), offered->chat ? '\xAB' + offered->name + '\xBB' : offered->name), _textNameOptions);
toText.setText(st::boxFont, phrase, _textNameOptions);
toTextWidth = toText.maxWidth();
if (toTextWidth > box.width() - st::boxPadding.left() - st::boxPadding.right()) {
toTextWidth = box.width() - st::boxPadding.left() - st::boxPadding.right();
@ -2765,10 +2766,10 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
int32 t = unixtime();
if (histPeer->chat) {
ChatData *chat = histPeer->asChat();
if (chat->forbidden || chat->count <= 0) {
text = lang(lng_chat_no_members);
if (chat->forbidden) {
text = lang(lng_chat_status_unaccessible);
} else if (chat->participants.isEmpty()) {
text = titlePeerText.isEmpty() ? lang(lng_chat_members).arg(chat->count) : titlePeerText;
text = titlePeerText.isEmpty() ? lng_chat_status_members(lt_count, chat->count < 0 ? 0 : chat->count) : titlePeerText;
} else {
int32 onlineCount = 0;
bool onlyMe = true;
@ -2779,9 +2780,9 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
}
}
if (onlineCount && !onlyMe) {
text = lang(lng_chat_members_online).arg(chat->participants.size()).arg(onlineCount);
text = lng_chat_status_members_online(lt_count, chat->participants.size(), lt_count_online, onlineCount);
} else {
text = lang(lng_chat_members).arg(chat->participants.size());
text = lng_chat_status_members(lt_count, chat->participants.size());
}
}
} else {
@ -3181,6 +3182,8 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
e->ignore();
} else if (e->key() == Qt::Key_Back) {
onCancel();
} else if (e->key() == Qt::Key_PageDown) {
if ((e->modifiers() & Qt::ControlModifier) || (e->modifiers() & Qt::MetaModifier)) {
PeerData *after = 0;

View File

@ -95,9 +95,9 @@ void IntroCode::paintEvent(QPaintEvent *e) {
}
QString callText = lang(lng_code_calling);
if (waitTillCall >= 3600) {
callText = lang(lng_code_call).arg(QString("%1:%2").arg(waitTillCall / 3600).arg((waitTillCall / 60) % 60, 2, 10, QChar('0'))).arg(waitTillCall % 60, 2, 10, QChar('0'));
callText = lng_code_call(lt_minutes, qsl("%1:%2").arg(waitTillCall / 3600).arg((waitTillCall / 60) % 60, 2, 10, QChar('0')), lt_seconds, qsl("%1").arg(waitTillCall % 60, 2, 10, QChar('0')));
} else if (waitTillCall > 0) {
callText = lang(lng_code_call).arg(waitTillCall / 60).arg(waitTillCall % 60, 2, 10, QChar('0'));
callText = lng_code_call(lt_minutes, QString::number(waitTillCall / 60), lt_seconds, qsl("%1").arg(waitTillCall % 60, 2, 10, QChar('0')));
} else if (waitTillCall < 0) {
callText = lang(lng_code_called);
}

View File

@ -45,7 +45,7 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStage(parent),
next(this, lang(lng_intro_next), st::btnIntroNext),
country(this, st::introCountry),
phone(this, st::inpIntroPhone, lang(lng_phone_ph)), code(this, st::inpIntroCountryCode),
_signup(this, lang(lng_phone_notreg).replace(qsl("{signup}"), textcmdStartLink(1)).replace(qsl("{/signup}"), textcmdStopLink()), st::introErrLabel, st::introErrLabelTextStyle),
_signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), st::introErrLabel, st::introErrLabelTextStyle),
_showSignup(false) {
setVisible(false);
setGeometry(parent->innerRect());
@ -227,7 +227,7 @@ void IntroPhone::phoneCheckDone(const MTPauth_CheckedPhone &result) {
checkRequest.start(1000);
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(0), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Application::lang())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(0), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Application::language())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
} else {
showError(lang(lng_bad_phone_noreg), true);
enableAll(true);
@ -256,7 +256,7 @@ void IntroPhone::toSignUp() {
checkRequest.start(1000);
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(0), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Application::lang())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
sentRequest = MTP::send(MTPauth_SendCode(MTP_string(sentPhone), MTP_int(0), MTP_int(ApiId), MTP_string(ApiHash), MTP_string(Application::language())), rpcDone(&IntroPhone::phoneSubmitDone), rpcFail(&IntroPhone::phoneSubmitFail));
}
bool IntroPhone::phoneSubmitFail(const RPCError &error) {

View File

@ -0,0 +1,69 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "lang.h"
Qt::LayoutDirection langDir() { // current lang dependent
return Qt::LeftToRight;
}
LangString langCounted(ushort key0, ushort tag, float64 value) { // current lang dependent
int v = qFloor(value);
QString sv;
ushort key = key0;
if (v != qCeil(value)) {
key += 2;
sv = QString::number(value);
} else {
if (v == 1) {
key += 1;
} else if (v) {
key += 2;
}
sv = QString::number(v);
}
while (key > key0) {
LangString v = lang(LangKey(key));
if (!v.isEmpty()) return v.tag(tag, sv);
--key;
}
return lang(LangKey(key0)).tag(tag, sv);
}
const QString &LangLoader::errors() const {
if (_errors.isEmpty() && !_err.isEmpty()) {
_errors = _err.join('\n');
}
return _errors;
}
const QString &LangLoader::warnings() const {
if (!_checked) {
for (int32 i = 0; i < lngkeys_cnt; ++i) {
if (!_found[i]) {
_warn.push_back(qsl("No value found for key '%1'").arg(langKeyName(LangKey(i))));
}
}
_checked = true;
}
if (_warnings.isEmpty() && !_warn.isEmpty()) {
_warnings = _warn.join('\n');
}
return _warnings;
}

118
Telegram/SourceFiles/lang.h Normal file
View File

@ -0,0 +1,118 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014 John Preston, https://desktop.telegram.org
*/
#pragma once
class LangString : public QString {
public:
LangString() {
}
LangString(const QString &str) : QString(str) {
}
LangString tag(ushort tag, const QString &replacement) {
for (const QChar *s = constData(), *ch = s, *e = ch + size(); ch != e;) {
if (*ch == TextCommand) {
if (ch + 3 < e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) {
if ((ch + 2)->unicode() == 0x0020 + tag) {
LangString result;
result.reserve(size() + replacement.size() - 4);
if (ch > s) result.append(midRef(0, ch - s));
result.append(replacement);
if (ch + 4 < e) result.append(midRef(ch - s + 4));
return result;
} else {
ch += 4;
}
} else {
const QChar *next = textSkipCommand(ch, e);
if (next == ch) {
++ch;
} else {
ch = next;
}
}
} else {
++ch;
}
}
return *this;
}
LangString &operator=(const QString &str) {
QString::operator=(str);
return (*this);
}
};
LangString langCounted(ushort key0, ushort tag, float64 value);
#include "lang_auto.h"
const char *langKeyName(LangKey key);
inline LangString langDayOfMonth(const QDate &date) {
int32 month = date.month(), day = date.day();
return (month > 0 && month <= 12) ? lng_month_day(lt_month, lang(LangKey(lng_month1 + month - 1)), lt_day, QString::number(day)) : qsl("MONTH_ERR");
}
inline LangString langDayOfWeek(const QDate &date) {
int32 day = date.dayOfWeek();
return (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1 + day - 1)) : qsl("DAY_ERR");
}
inline LangString langDayOfWeekFull(const QDate &date) {
int32 day = date.dayOfWeek();
return (day > 0 && day <= 7) ? lang(LangKey(lng_weekday1_full + day - 1)) : qsl("DAY_ERR");
}
Qt::LayoutDirection langDir();
class LangLoader {
public:
const QString &errors() const;
const QString &warnings() const;
protected:
LangLoader() : _checked(false) {
memset(_found, 0, sizeof(_found));
}
ushort tagIndex(const QByteArray &tag) const;
LangKey keyIndex(const QByteArray &key) const;
bool tagReplaced(LangKey key, ushort tag) const;
LangKey subkeyIndex(LangKey key, ushort tag, ushort index) const;
bool feedKeyValue(LangKey key, const QString &value);
void error(const QString &text) {
_err.push_back(text);
}
void warning(const QString &text) {
_warn.push_back(text);
}
private:
mutable QStringList _err, _warn;
mutable QString _errors, _warnings;
mutable bool _checked;
bool _found[lngkeys_cnt];
LangLoader(const LangLoader &);
LangLoader &operator=(const LangLoader &);
};

View File

@ -61,47 +61,136 @@ namespace {
} while (start != from);
return true;
}
}
bool readKeyValue(const char *&from, const char *end, QString &name, QString &value) {
if (!skipJunk(from, end)) return false;
bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
if (!skipJunk(from, end)) return false;
const char *nameStart = from;
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
++from;
}
QString varName = QString::fromUtf8(nameStart, from - nameStart);
if (!skipJunk(from, end)) throw Exception("Unexpected end of file!");
if (*from != ':') throw Exception(QString("':' expected after '%1'").arg(varName));
if (!skipJunk(++from, end)) throw Exception("Unexpected end of file!");
if (*from != '"') throw Exception(QString("Expected string after '%1:'").arg(varName));
QByteArray varValue;
const char *start = ++from;
while (from < end && *from != '"') {
if (*from == '\\') {
if (from + 1 >= end) throw Exception("Unexpected end of file!");
if (*(from + 1) == '"' || *(from + 1) == '\\') {
if (from > start) varValue.append(start, from - start);
start = ++from;
}
}
++from;
}
if (from >= end) throw Exception("Unexpected end of file!");
if (from > start) varValue.append(start, from - start);
if (!skipJunk(++from, end)) throw Exception("Unexpected end of file!");
if (*from != ';') throw Exception(QString("';' expected after '%1: \"value\"'").arg(varName));
skipJunk(++from, end);
name = varName;
value = QString::fromUtf8(varValue);
return true;
if (*from != '"') throw Exception(QString("Expected quote after key name!"));
++from;
const char *nameStart = from;
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
++from;
}
QByteArray varName = QByteArray(nameStart, from - nameStart);
if (*from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(QLatin1String(varName)));
++from;
if (!skipJunk(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(QLatin1String(varName)));
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(QLatin1String(varName)));
LangKey varKey = keyIndex(varName);
if (varKey == lngkeys_cnt) throw Exception(QString("Unknown key '%1'!").arg(QLatin1String(varName)));
QByteArray varValue;
QMap<ushort, bool> tagsUsed;
const char *start = ++from;
while (from < end && *from != '"') {
if (*from == '\n') {
throw Exception(QString("Unexpected end of string in key '%1'!").arg(QLatin1String(varName)));
}
if (*from == '\\') {
if (from + 1 >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{') {
if (from > start) varValue.append(start, from - start);
start = ++from;
} else if (*(from + 1) == 'n') {
if (from > start) varValue.append(start, int(from - start));
varValue.append('\n');
start = (++from) + 1;
}
} else if (*from == '{') {
if (from > start) varValue.append(start, int(from - start));
const char *tagStart = ++from;
while (from < end && ((*from >= 'a' && *from <= 'z') || (*from >= 'A' && *from <= 'Z') || *from == '_' || (*from >= '0' && *from <= '9'))) {
++from;
}
if (from == tagStart) throw Exception(QString("Expected tag name in key '%1'!").arg(QLatin1String(varName)));
QByteArray tagName = QByteArray(tagStart, int(from - tagStart));
if (from == end || (*from != '}' && *from != ':')) throw Exception(QString("Expected '}' or ':' after tag name in key '%1'!").arg(QLatin1String(varName)));
ushort index = tagIndex(tagName);
if (index == lngtags_cnt) throw Exception(QString("Tag '%1' not found in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (!tagReplaced(varKey, index)) throw Exception(QString("Unexpected tag '%1' in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (tagsUsed.contains(index)) throw Exception(QString("Tag '%1' double used in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
tagsUsed.insert(index, true);
QString tagReplacer(4, TextCommand);
tagReplacer[1] = TextCommandLangTag;
tagReplacer[2] = QChar(0x0020 + index);
varValue.append(tagReplacer.toUtf8());
if (*from == ':') {
start = ++from;
QByteArray subvarValue;
bool foundtag = false;
int countedIndex = 0;
while (from < end && *from != '"' && *from != '}') {
if (*from == '|') {
if (from > start) subvarValue.append(start, int(from - start));
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (!feedKeyValue(subkeyIndex(varKey, index, countedIndex++), QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
subvarValue = QByteArray();
foundtag = false;
start = from + 1;
}
if (*from == '\n') {
throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
}
if (*from == '\\') {
if (from + 1 >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (*(from + 1) == '"' || *(from + 1) == '\\' || *(from + 1) == '{' || *(from + 1) == '#') {
if (from > start) subvarValue.append(start, int(from - start));
start = ++from;
} else if (*(from + 1) == 'n') {
if (from > start) subvarValue.append(start, int(from - start));
subvarValue.append('\n');
start = (++from) + 1;
}
} else if (*from == '{') {
throw Exception(QString("Unexpected tag inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
} else if (*from == '#') {
if (foundtag) throw Exception(QString("Replacement '#' double used inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
foundtag = true;
if (from > start) subvarValue.append(start, int(from - start));
subvarValue.append(tagReplacer.toUtf8());
start = from + 1;
}
++from;
}
if (from >= end) throw Exception(QString("Unexpected end of file inside counted tag '%1' in '%2' key!").arg(QString::fromUtf8(tagName)).arg(QString::fromUtf8(varName)));
if (*from == '"') throw Exception(QString("Unexpected end of string inside counted tag '%1' in '%2' key!").arg(QString::fromUtf8(tagName)).arg(QString::fromUtf8(varName)));
if (from > start) subvarValue.append(start, int(from - start));
if (countedIndex >= lngtags_max_counted_values) throw Exception(QString("Too many values inside counted tag '%1' in '%2' key!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
if (!feedKeyValue(subkeyIndex(varKey, index, countedIndex++), QString::fromUtf8(subvarValue))) throw Exception(QString("Tag '%1' is not counted in key '%2'!").arg(QLatin1String(tagName)).arg(QLatin1String(varName)));
}
start = from + 1;
}
++from;
}
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (from > start) varValue.append(start, from - start);
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(QLatin1String(varName)));
skipJunk(++from, end);
if (!feedKeyValue(varKey, QString::fromUtf8(varValue))) throw Exception(QString("Could not write value in key '%1'!").arg(QLatin1String(varName)));
return true;
}
LangLoaderPlain::LangLoaderPlain(const QString &file) {
@ -120,11 +209,7 @@ LangLoaderPlain::LangLoaderPlain(const QString &file) {
try {
const char *from = data.constData(), *end = data.constData() + data.size();
while (true) {
QString name, value;
if (!readKeyValue(from, end, name, value)) {
break;
}
if (!feedKeyValue(name, value)) {
if (!readKeyValue(from, end)) {
break;
}
}

View File

@ -24,4 +24,8 @@ public:
LangLoaderPlain(const QString &file);
protected:
bool readKeyValue(const char *&from, const char *end);
};

View File

@ -85,7 +85,7 @@ void TopBarWidget::onDeleteContact() {
PeerData *p = App::main() ? App::main()->profilePeer() : 0;
UserData *u = (p && !p->chat) ? p->asUser() : 0;
if (u) {
ConfirmBox *box = new ConfirmBox(lang(lng_sure_delete_contact).replace(qsl("{contact}"), p->name));
ConfirmBox *box = new ConfirmBox(lng_sure_delete_contact(lt_contact, p->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteContactSure()));
App::wnd()->showLayer(box);
}
@ -105,7 +105,7 @@ void TopBarWidget::onDeleteAndExit() {
PeerData *p = App::main() ? App::main()->profilePeer() : 0;
ChatData *c = (p && p->chat) ? p->asChat() : 0;
if (c) {
ConfirmBox *box = new ConfirmBox(lang(lng_sure_delete_and_exit).replace(qsl("{group}"), p->name));
ConfirmBox *box = new ConfirmBox(lng_sure_delete_and_exit(lt_group, p->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteAndExitSure()));
App::wnd()->showLayer(box);
}
@ -292,7 +292,7 @@ void TopBarWidget::showAll() {
void TopBarWidget::showSelected(uint32 selCount) {
PeerData *p = App::main() ? App::main()->profilePeer() : 0;
_selCount = selCount;
_selStr = (_selCount > 0) ? lang((_selCount == 1) ? lng_selected_count_1 : lng_selected_count_5).arg(_selCount) : QString();
_selStr = (_selCount > 0) ? lng_selected_count(lt_count, _selCount) : QString();
_selStrWidth = st::btnDefLink.font->m.width(_selStr);
setCursor((!p && _selCount) ? style::cur_default : style::cur_pointer);
showAll();
@ -445,7 +445,7 @@ void MainWidget::forwardLayer(int32 forwardSelected) {
}
void MainWidget::deleteLayer(int32 selectedCount) {
QString str(lang((selectedCount < -1) ? lng_selected_cancel_sure_this : (selectedCount < 0 ? lng_selected_delete_sure_this : (selectedCount > 1 ? lng_selected_delete_sure_5 : lng_selected_delete_sure_1))));
QString str((selectedCount < 0) ? lang(selectedCount < -1 ? lng_selected_cancel_sure_this : lng_selected_delete_sure_this) : lng_selected_delete_sure(lt_count, selectedCount));
ConfirmBox *box = new ConfirmBox((selectedCount < 0) ? str : str.arg(selectedCount), lang(lng_selected_delete_confirm));
if (selectedCount < 0) {
connect(box, SIGNAL(confirmed()), overview ? overview : static_cast<QWidget*>(&history), SLOT(onDeleteContextSure()));
@ -495,9 +495,10 @@ void MainWidget::dialogsActivate() {
bool MainWidget::leaveChatFailed(PeerData *peer, const RPCError &e) {
if (e.type() == "CHAT_ID_INVALID") { // left this chat already
if ((profile && profile->peer() == peer) || (overview && overview->peer() == peer) || _stack.contains(peer) || history.peer() == peer) {
showPeer(0);
showPeer(0, 0, false, true);
}
dialogs.removePeer(peer);
App::histories().remove(peer->id);
MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer));
return true;
}
@ -507,9 +508,10 @@ bool MainWidget::leaveChatFailed(PeerData *peer, const RPCError &e) {
void MainWidget::deleteHistory(PeerData *peer, const MTPmessages_StatedMessage &result) {
sentFullDataReceived(0, result);
if ((profile && profile->peer() == peer) || (overview && overview->peer() == peer) || _stack.contains(peer) || history.peer() == peer) {
showPeer(0);
showPeer(0, 0, false, true);
}
dialogs.removePeer(peer);
App::histories().remove(peer->id);
MTP::send(MTPmessages_DeleteHistory(peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, peer));
}
@ -2108,7 +2110,7 @@ void MainWidget::usernameResolveDone(const MTPUser &user) {
bool MainWidget::usernameResolveFail(QString name, const RPCError &error) {
if (error.code() == 400) {
App::wnd()->showLayer(new ConfirmBox(lang(lng_username_not_found).replace(qsl("{user}"), name), true));
App::wnd()->showLayer(new ConfirmBox(lng_username_not_found(lt_user, name), true));
}
return true;
}
@ -2575,7 +2577,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
MTPPhoto photo(App::photoFromUserPhoto(MTP_int(user->id & 0xFFFFFFFF), d.vdate, d.vphoto));
HistoryMedia *media = new HistoryPhoto(photo.c_photo(), 100);
if (App::history(user->id)->loadedAtBottom()) {
App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lang(lng_action_user_photo).replace(qsl("{from}"), user->name), false, true, media);
App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lng_action_user_photo(lt_from, user->name), false, true, media);
}
}
}
@ -2589,7 +2591,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
UserData *user = App::userLoaded(d.vuser_id.v);
if (user) {
if (App::history(user->id)->loadedAtBottom()) {
App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lang(lng_action_user_registered).replace(qsl("{from}"), user->name), false, true);
App::history(user->id)->addToBackService(clientMsgId(), date(d.vdate), lng_action_user_registered(lt_from, user->name), false, true);
}
}
} break;
@ -2654,12 +2656,10 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
const MTPDupdateNewAuthorization &d(update.c_updateNewAuthorization());
QDateTime datetime = date(d.vdate);
QString text = lang(lng_new_authorization).replace(qsl("{name}"), App::self()->firstName);
text = text.replace(qsl("{day}"), langDayOfWeekFull(datetime.date()));
text = text.replace(qsl("{date}"), langDayOfMonth(datetime.date()));
text = text.replace(qsl("{time}"), datetime.time().toString(qsl("hh:mm")));
text = text.replace(qsl("{device}"), qs(d.vdevice));
text = text.replace(qsl("{location}"), qs(d.vlocation));
QString name = App::self()->firstName;
QString day = langDayOfWeekFull(datetime.date()), date = langDayOfMonth(datetime.date()), time = datetime.time().toString(qsl("hh:mm"));
QString device = qs(d.vdevice), location = qs(d.vlocation);
LangString text = lng_new_authorization(lt_name, App::self()->firstName, lt_day, day, lt_date, date, lt_time, time, lt_device, device, lt_location, location);
App::wnd()->serviceNotification(text);
} break;

View File

@ -173,11 +173,11 @@ void MediaView::updateControls() {
}
QDateTime d(date(_photo ? _photo->date : _doc->date)), dNow(date(unixtime()));
if (d.date() == dNow.date()) {
_dateText = lang(lng_status_lastseen_today).replace(qsl("{time}"), d.time().toString(qsl("hh:mm")));
_dateText = lng_status_lastseen_today(lt_time, d.time().toString(qsl("hh:mm")));
} else if (d.date().addDays(1) == dNow.date()) {
_dateText = lang(lng_status_lastseen_yesterday).replace(qsl("{time}"), d.time().toString(qsl("hh:mm")));
_dateText = lng_status_lastseen_yesterday(lt_time, d.time().toString(qsl("hh:mm")));
} else {
_dateText = lang(lng_status_lastseen_date_time).replace(qsl("{date}"), d.date().toString(qsl("dd.MM.yy"))).replace(qsl("{time}"), d.time().toString(qsl("hh:mm")));
_dateText = lng_status_lastseen_date_time(lt_date, d.date().toString(qsl("dd.MM.yy")), lt_time, d.time().toString(qsl("hh:mm")));
}
_fromName.setText(st::medviewNameFont, _from->name);
updateHeader();
@ -1299,7 +1299,7 @@ void MediaView::updateHeader() {
count = _user->photosCount ? _user->photosCount : _user->photos.size();
}
if (_index >= 0 && _index < count && count > 1) {
_header = lang(lng_mediaview_n_of_count).replace(qsl("{n}"), QString::number(index + 1)).replace(qsl("{count}"), QString::number(count));
_header = lng_mediaview_n_of_count(lt_n, QString::number(index + 1), lt_count, QString::number(count));
_overview.setText(_header);
if (_history) {
if (_overview.isHidden()) _overview.show();

View File

@ -993,8 +993,8 @@ void OverviewInner::mouseReleaseEvent(QMouseEvent *e) {
}
void OverviewInner::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
if (_selected.isEmpty()) {
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
if (_selected.isEmpty() || e->key() == Qt::Key_Back) {
App::main()->showBackFromStack();
} else {
_overview->onClearSelected();

View File

@ -185,7 +185,7 @@ void ProfileInner::onUpdatePhoto() {
}
void ProfileInner::onClearHistory() {
ConfirmBox *box = new ConfirmBox(lang(lng_sure_delete_history).replace(qsl("{contact}"), _peer->name));
ConfirmBox *box = new ConfirmBox(lng_sure_delete_history(lt_contact, _peer->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onClearHistorySure()));
App::wnd()->showLayer(box);
}
@ -359,11 +359,11 @@ void ProfileInner::reorderParticipants() {
}
if (_peerChat->count > 0 && _participants.isEmpty() && !_loadingId) {
_loadingId = MTP::send(MTPmessages_GetFullChat(App::peerToMTP(_peerChat->id).c_peerChat().vchat_id), rpcDone(&ProfileInner::gotFullChat));
if (_onlineText.isEmpty()) _onlineText = lang(lng_chat_members).arg(_peerChat->count);
if (_onlineText.isEmpty()) _onlineText = lng_chat_status_members(lt_count, _peerChat->count);
} else if (onlineCount && !onlyMe) {
_onlineText = lang(lng_chat_members_online).arg(_participants.size()).arg(onlineCount);
_onlineText = lng_chat_status_members_online(lt_count, _participants.size(), lt_count_online, onlineCount);
} else {
_onlineText = lang(lng_chat_members).arg(_participants.size());
_onlineText = lng_chat_status_members(lt_count, _participants.size());
}
loadProfilePhotos(_lastPreload);
} else {
@ -371,7 +371,7 @@ void ProfileInner::reorderParticipants() {
if (_peerUser) {
_onlineText = App::onlineText(_peerUser, t, true);
} else {
_onlineText = lang(lng_chat_no_members);
_onlineText = lang(lng_chat_status_unaccessible);
}
}
if (was != _participants.size()) {
@ -442,10 +442,10 @@ void ProfileInner::paintEvent(QPaintEvent *e) {
top += st::profileButtonTop;
if (_peerChat && _peerChat->forbidden) {
int32 w = st::btnShareContact.font->m.width(lang(lng_chat_no_members));
int32 w = st::btnShareContact.font->m.width(lang(lng_profile_chat_unaccessible));
p.setFont(st::btnShareContact.font->f);
p.setPen(st::profileOfflineColor->p);
p.drawText(_left + (_width - w) / 2, top + st::btnShareContact.textTop + st::btnShareContact.font->ascent, lang(lng_chat_no_members));
p.drawText(_left + (_width - w) / 2, top + st::btnShareContact.textTop + st::btnShareContact.font->ascent, lang(lng_profile_chat_unaccessible));
}
top += _shareContact.height();
@ -620,7 +620,7 @@ void ProfileInner::mousePressEvent(QMouseEvent *e) {
void ProfileInner::mouseReleaseEvent(QMouseEvent *e) {
if (_kickDown && _kickDown == _kickOver) {
_kickConfirm = _kickOver;
ConfirmBox *box = new ConfirmBox(lang(lng_profile_sure_kick).replace(qsl("{user}"), _kickOver->firstName));
ConfirmBox *box = new ConfirmBox(lng_profile_sure_kick(lt_user, _kickOver->firstName));
connect(box, SIGNAL(confirmed()), this, SLOT(onKickConfirm()));
App::wnd()->showLayer(box);
}
@ -634,7 +634,7 @@ void ProfileInner::onKickConfirm() {
}
void ProfileInner::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
App::main()->showBackFromStack();
}
}
@ -873,14 +873,13 @@ void ProfileInner::showAll() {
}
QString ProfileInner::overviewLinkText(int32 type, int32 count) {
LangKey key = lng_keys_cnt;
switch (type) {
case OverviewPhotos: key = (count > 1) ? lng_profile_photos : lng_profile_photo; break;
case OverviewVideos: key = (count > 1) ? lng_profile_videos : lng_profile_video; break;
case OverviewDocuments: key = (count > 1) ? lng_profile_documents : lng_profile_document; break;
case OverviewAudios: key = (count > 1) ? lng_profile_audios : lng_profile_audio; break;
case OverviewPhotos: return lng_profile_photos(lt_count, count);
case OverviewVideos: return lng_profile_videos(lt_count, count);
case OverviewDocuments: return lng_profile_documents(lt_count, count);
case OverviewAudios: return lng_profile_audios(lt_count, count);
}
return lang(key).replace(qsl("{count}"), QString::number(count));
return QString();
}
ProfileWidget::ProfileWidget(QWidget *parent, const PeerData *peer) : QWidget(parent)

View File

@ -67,17 +67,14 @@ private:
NSString *_str;
};
typedef QMap<LangKey, QNSString> ObjcLang;
ObjcLang objcLang;
QNSString objc_lang(LangKey key) {
return QNSString(lang(key));
ObjcLang::const_iterator i = objcLang.constFind(key);
if (i == objcLang.cend()) {
i = objcLang.insert(key, lang(key));
}
return i.value();
}
QNSString objc_lang(LangKey key, LangTag tag1, const QString &replacement1 {
return QNSString(lang(key, tag1, replacement1));
}
QString objcString(NSString *str) {
return QString::fromUtf8([str cStringUsingEncoding:NSUTF8StringEncoding]);
}
@interface ObserverHelper : NSObject {
@ -535,7 +532,7 @@ void objc_openFile(const QString &f, bool openwith) {
[button setFrame:alwaysRect];
[button setAutoresizingMask:NSViewMinXMargin|NSViewMaxXMargin];
NSTextField *goodLabel = [[NSTextField alloc] init];
[goodLabel setStringValue:[objc_lang(lng_mac_this_app_can_open).s() stringByReplacingOccurrencesOfString:@"{file}" withString:name]];
[goodLabel setStringValue:objc_lang(lng_mac_this_app_can_open, lngtag_file, objcString(name)).s()];
[goodLabel setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
[goodLabel setBezeled:NO];
[goodLabel setDrawsBackground:NO];
@ -548,7 +545,7 @@ void objc_openFile(const QString &f, bool openwith) {
[goodLabel setFrame:goodFrame];
NSTextField *badLabel = [[NSTextField alloc] init];
[badLabel setStringValue:[objc_lang(lng_mac_not_known_app).s() stringByReplacingOccurrencesOfString:@"{file}" withString:name]];
[badLabel setStringValue:objc_lang(lng_mac_not_known_app, lngtag_file, objcString(name)).s()];
[badLabel setFont:[goodLabel font]];
[badLabel setBezeled:NO];
[badLabel setDrawsBackground:NO];
@ -578,7 +575,7 @@ void objc_openFile(const QString &f, bool openwith) {
[openPanel setAllowsMultipleSelection:NO];
[openPanel setResolvesAliases:YES];
[openPanel setTitle:objc_lang(lng_mac_choose_app).s()];
[openPanel setMessage:[objc_lang(lng_mac_choose_text).s() stringByReplacingOccurrencesOfString:@"{file}" withString:name]];
[openPanel setMessage:objc_lang(lng_mac_choose_text, lngtag_file, objcString(name)).s()];
NSArray *appsPaths = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationDirectory inDomains:NSLocalDomainMask];
if ([appsPaths count]) [openPanel setDirectoryURL:[appsPaths firstObject]];
@ -595,7 +592,7 @@ void objc_openFile(const QString &f, bool openwith) {
OSStatus result = LSSetDefaultRoleHandlerForContentType((CFStringRef)UTI,
kLSRolesAll,
(CFStringRef)[[NSBundle bundleWithPath:path] bundleIdentifier]);
DEBUG_LOG(("App Info: set default handler for '%1' UTI result: %2").arg([UTI cStringUsingEncoding:NSUTF8StringEncoding]).arg(result));
DEBUG_LOG(("App Info: set default handler for '%1' UTI result: %2").arg(objcString(UTI)).arg(result));
}
[UTIs release];
@ -631,9 +628,6 @@ void objc_start() {
void objc_finish() {
[_sharedDelegate release];
if (!objcLang.isEmpty()) {
objcLang.clear();
}
}
void objc_registerCustomScheme() {
@ -661,14 +655,14 @@ BOOL _execUpdater(BOOL update = YES) {
[args addObject:QNSString(cDataFile()).s()];
}
DEBUG_LOG(("Application Info: executing %1 %2").arg(QString::fromUtf8([path cStringUsingEncoding:NSUTF8StringEncoding])).arg(QString::fromUtf8([[args componentsJoinedByString:@" "] cStringUsingEncoding:NSUTF8StringEncoding])));
DEBUG_LOG(("Application Info: executing %1 %2").arg(objcString(path)).arg(objcString([[args componentsJoinedByString:@" "])));
if (![NSTask launchedTaskWithLaunchPath:path arguments:args]) {
LOG(("Task not launched while executing %1 %2").arg(QString::fromUtf8([path cStringUsingEncoding:NSUTF8StringEncoding])).arg(QString::fromUtf8([[args componentsJoinedByString:@" "] cStringUsingEncoding:NSUTF8StringEncoding])));
LOG(("Task not launched while executing %1 %2").arg(objcString(path)).arg(objcString([args componentsJoinedByString:@" "])));
return NO;
}
}
@catch (NSException *exception) {
LOG(("Exception caught while executing %1 %2").arg(QString::fromUtf8([path cStringUsingEncoding:NSUTF8StringEncoding])).arg(QString::fromUtf8([args cStringUsingEncoding:NSUTF8StringEncoding])));
LOG(("Exception caught while executing %1 %2").arg(objcString(path)).arg(objcString(args)));
return NO;
}
@finally {
@ -730,19 +724,19 @@ QString objc_downloadPath() {
QString objc_currentCountry() {
NSLocale *currentLocale = [NSLocale currentLocale]; // get the current locale.
NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
return countryCode ? QString::fromUtf8([countryCode cStringUsingEncoding:NSUTF8StringEncoding]) : QString();
return countryCode ? objcString(countryCode) : QString();
}
QString objc_currentLang() {
NSLocale *currentLocale = [NSLocale currentLocale]; // get the current locale.
NSString *currentLang = [currentLocale objectForKey:NSLocaleLanguageCode];
return currentLang ? QString::fromUtf8([currentLang cStringUsingEncoding:NSUTF8StringEncoding]) : QString();
return currentLang ? objcString(currentLang) : QString();
}
QString objc_convertFileUrl(const QString &url) {
NSString *nsurl = [[[NSURL URLWithString: [NSString stringWithUTF8String: (qsl("file://") + url).toUtf8().constData()]] filePathURL] path];
if (!nsurl) return QString();
return QString::fromUtf8([nsurl cStringUsingEncoding:NSUTF8StringEncoding]);
return objcString(nsurl);
}

View File

@ -28,6 +28,8 @@ QString gWorkingDir, gExeDir, gExeName;
QStringList gSendPaths;
QString gStartUrl;
QString gLangErrors;
QString gDialogLastPath, gDialogHelperPath; // optimize QFileDialog
bool gSoundNotify = true;
@ -69,7 +71,7 @@ DBIEmojiTab gEmojiTab = dbietRecent;
RecentEmojiPack gRecentEmojis;
RecentEmojiPreload gRecentEmojisPreload;
QString gLangFile;
QString gLangFile = qsl("testlang.strings");
bool gRetina = false;
float64 gRetinaFactor = 1.;

View File

@ -149,6 +149,8 @@ DeclareReadSetting(QString, LangFile);
DeclareSetting(QStringList, SendPaths);
DeclareSetting(QString, StartUrl);
DeclareSetting(QString, LangErrors);
DeclareSetting(bool, Retina);
DeclareSetting(float64, RetinaFactor);
DeclareSetting(int32, IntRetinaFactor);

View File

@ -132,7 +132,7 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent),
_startMinimized(this, lang(lng_settings_start_min), cStartMinimized()),
_sendToMenu(this, lang(lng_settings_add_sendto), cSendToMenu()),
_dpiAutoScale(this, lang(lng_settings_scale_auto).replace(qsl("{cur}"), scaleLabel(cScreenScale())), (cConfigScale() == dbisAuto)),
_dpiAutoScale(this, lng_settings_scale_auto(lt_cur, scaleLabel(cScreenScale())), (cConfigScale() == dbisAuto)),
_dpiSlider(this, st::dpiSlider, dbisScaleCount - 1, cEvalScale(cConfigScale()) - 1),
_dpiWidth1(st::dpiFont1->m.width(scaleLabel(dbisOne))),
_dpiWidth2(st::dpiFont2->m.width(scaleLabel(dbisOneAndQuarter))),
@ -163,7 +163,7 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent),
_imagesClearFailedWidth(st::linkFont->m.width(lang(lng_local_images_clear_failed))),
// advanced
_connectionType(this, lang(lng_connection_auto)),
_connectionType(this, lng_connection_auto(lt_type, QString())),
_resetSessions(this, lang(lng_settings_reset)),
_logOut(this, lang(lng_settings_logout), st::btnLogout),
_resetDone(false)
@ -213,7 +213,7 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent),
connect(&_dpiAutoScale, SIGNAL(changed()), this, SLOT(onScaleAuto()));
connect(&_dpiSlider, SIGNAL(changed(int32)), this, SLOT(onScaleChange()));
_curVersionText = lang(lng_settings_current_version).replace(qsl("{version}"), QString::fromWCharArray(AppVersionStr)) + ' ';
_curVersionText = lng_settings_current_version(lt_version, QString::fromWCharArray(AppVersionStr)) + ' ';
_curVersionWidth = st::linkFont->m.width(_curVersionText);
_newVersionText = lang(lng_settings_update_ready) + ' ';
_newVersionWidth = st::linkFont->m.width(_newVersionText);
@ -482,7 +482,7 @@ void SettingsInner::paintEvent(QPaintEvent *e) {
QString localImagesText = lang(lng_settings_no_images_cached);
int32 cnt = Local::hasImages();
if (cnt) {
localImagesText = lang((cnt > 1) ? lng_settings_images_cached : lng_settings_image_cached).replace(qsl("{count}"), QString::number(cnt)).replace(qsl("{size}"), formatSizeText(Local::storageFilesSize()));
localImagesText = lng_settings_images_cached(lt_count, cnt, lt_size, formatSizeText(Local::storageFilesSize()));
}
p.setFont(st::linkFont->f);
p.setPen(st::black->p);
@ -599,7 +599,7 @@ void SettingsInner::resizeEvent(QResizeEvent *e) {
}
void SettingsInner::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) {
App::wnd()->showSettings();
}
}
@ -677,7 +677,7 @@ void SettingsInner::updateConnectionType() {
if (transport.isEmpty()) {
_connectionType.setText(lang(lng_connection_auto_connecting));
} else {
_connectionType.setText(lang(lng_connection_auto).replace(qsl("{type}"), transport));
_connectionType.setText(lng_connection_auto(lt_type, transport));
}
} break;
case dbictHttpProxy: _connectionType.setText(lang(lng_connection_http_proxy)); break;
@ -1215,7 +1215,7 @@ void SettingsInner::setDownloadProgress(qint64 ready, qint64 total) {
qint64 readyTenthMb = (ready * 10 / (1024 * 1024)), totalTenthMb = (total * 10 / (1024 * 1024));
QString readyStr = QString::number(readyTenthMb / 10) + '.' + QString::number(readyTenthMb % 10);
QString totalStr = QString::number(totalTenthMb / 10) + '.' + QString::number(totalTenthMb % 10);
QString res = lang(lng_settings_downloading).replace(qsl("{ready}"), readyStr).replace(qsl("{total}"), totalStr);
QString res = lng_settings_downloading(lt_ready, readyStr, lt_total, totalStr);
if (_newVersionDownload != res) {
_newVersionDownload = res;
if (cAutoUpdate()) {

View File

@ -169,7 +169,6 @@ void TitleWidget::resizeEvent(QResizeEvent *e) {
p.setX(p.x() - _minimize.width());
_minimize.move(p);
}
_settings.move(st::titleMenuOffset, 0);
_back.move(st::titleMenuOffset, 0);
_back.resize((_minimize.isHidden() ? (_update.isHidden() ? width() : _update.x()) : _minimize.x()) - st::titleMenuOffset, _back.height());

View File

@ -591,7 +591,7 @@ void Window::updateTitleStatus() {
showConnecting(lang(lng_connecting));
}
} else if (state < 0) {
showConnecting(lang(lng_reconnecting).arg((-state) / 1000), lang(lng_reconnecting_try_now));
showConnecting(lng_reconnecting(lt_count, ((-state) / 1000) + 1), lang(lng_reconnecting_try_now));
QTimer::singleShot((-state) % 1000, this, SLOT(updateTitleStatus()));
} else {
hideConnecting();

View File

@ -586,7 +586,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="GeneratedFiles\lang.cpp" />
<ClCompile Include="GeneratedFiles\lang_auto.cpp" />
<ClCompile Include="GeneratedFiles\qrc_telegram.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PrecompiledHeader>
@ -861,6 +861,7 @@
<ClCompile Include="SourceFiles\intro\introphone.cpp" />
<ClCompile Include="SourceFiles\intro\introsignup.cpp" />
<ClCompile Include="SourceFiles\intro\introsteps.cpp" />
<ClCompile Include="SourceFiles\lang.cpp" />
<ClCompile Include="SourceFiles\langloaderplain.cpp" />
<ClCompile Include="SourceFiles\layerwidget.cpp" />
<ClCompile Include="SourceFiles\localimageloader.cpp" />
@ -930,10 +931,10 @@
<Outputs>.\GeneratedFiles\style_auto.h</Outputs>
<Command>"$(SolutionDir)$(Platform)\$(Configuration)Style\MetaStyle.exe" -classes_in ".\Resources\style_classes.txt" -classes_out ".\GeneratedFiles\style_classes.h" -styles_in ".\Resources\style.txt" -styles_out ".\GeneratedFiles\style_auto.h" -path_to_sprites ".\SourceFiles\art\\"</Command>
</CustomBuild>
<CustomBuild Include="Resources\lang.txt">
<Outputs>.\GeneratedFiles\lang.h</Outputs>
<Outputs>.\GeneratedFiles\lang.cpp</Outputs>
<Command>"$(SolutionDir)$(Platform)\$(Configuration)Lang\MetaLang.exe" -lang_in ".\Resources\lang.txt" -lang_out ".\GeneratedFiles\lang"</Command>
<CustomBuild Include="Resources\lang.strings">
<Outputs>.\GeneratedFiles\lang_auto.h</Outputs>
<Outputs>.\GeneratedFiles\lang_auto.cpp</Outputs>
<Command>"$(SolutionDir)$(Platform)\$(Configuration)Lang\MetaLang.exe" -lang_in ".\Resources\lang.strings" -lang_out ".\GeneratedFiles\lang_auto"</Command>
</CustomBuild>
<CustomBuild Include="SourceFiles\application.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing application.h...</Message>
@ -949,7 +950,7 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<ClInclude Include="GeneratedFiles\lang.h" />
<ClInclude Include="GeneratedFiles\lang_auto.h" />
<ClInclude Include="GeneratedFiles\style_auto.h" />
<ClInclude Include="GeneratedFiles\style_classes.h" />
<ClInclude Include="resource.h" />
@ -1514,6 +1515,7 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<ClInclude Include="SourceFiles\lang.h" />
<ClInclude Include="SourceFiles\langloaderplain.h" />
<CustomBuild Include="SourceFiles\localstorage.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View File

@ -233,9 +233,6 @@
<ClCompile Include="SourceFiles\gui\flatlabel.cpp">
<Filter>gui</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\lang.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\langloaderplain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -764,6 +761,12 @@
<ClCompile Include="GeneratedFiles\Release\moc_localstorage.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="GeneratedFiles\lang_auto.cpp">
<Filter>Generated Files</Filter>
</ClCompile>
<ClCompile Include="SourceFiles\lang.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SourceFiles\stdafx.h">
@ -832,9 +835,6 @@
<ClInclude Include="SourceFiles\settings.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="GeneratedFiles\lang.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="SourceFiles\langloaderplain.h">
<Filter>Source Files</Filter>
</ClInclude>
@ -844,6 +844,12 @@
<ClInclude Include="SourceFiles\mtproto\mtpSessionImpl.h">
<Filter>mtproto</Filter>
</ClInclude>
<ClInclude Include="GeneratedFiles\lang_auto.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="SourceFiles\lang.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="SourceFiles\mtproto\mtpConnection.h">
@ -992,7 +998,6 @@
<CustomBuild Include="SourceFiles\gui\flatlabel.h">
<Filter>gui</Filter>
</CustomBuild>
<CustomBuild Include="Resources\lang.txt" />
<CustomBuild Include="SourceFiles\gui\switcher.h">
<Filter>gui</Filter>
</CustomBuild>
@ -1020,6 +1025,7 @@
<CustomBuild Include="SourceFiles\localstorage.h">
<Filter>Source Files</Filter>
</CustomBuild>
<CustomBuild Include="Resources\lang.strings" />
</ItemGroup>
<ItemGroup>
<Image Include="SourceFiles\art\icon256.ico" />