Skip to content

Commit 5c751da

Browse files
committed
Merge branch 'dev' of https://github.com/telegramdesktop/tdesktop into bigEmoj
# Conflicts: # Telegram/SourceFiles/core/version.h # Telegram/lib_ui
2 parents e86c1be + 66d512f commit 5c751da

File tree

322 files changed

+14868
-2476
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

322 files changed

+14868
-2476
lines changed

.cursor/rules/api_usage.mdc

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
description: For tasks requiring sending Telegram server API requests or working with generated API types.
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Telegram Desktop API Usage
7+
8+
## API Schema
9+
10+
The API definitions are described using [TL Language]\(https:/core.telegram.org/mtproto/TL) in two main schema files:
11+
12+
1. **`Telegram/SourceFiles/mtproto/scheme/mtproto.tl`**
13+
* Defines the core MTProto protocol types and methods used for basic communication, encryption, authorization, service messages, etc.
14+
* Some fundamental types and methods from this schema (like basic types, RPC calls, containers) are often implemented directly in the C++ MTProto core (`SourceFiles/mtproto/`) and may be skipped during the C++ code generation phase.
15+
* Other parts of `mtproto.tl` might still be processed by the code generator.
16+
17+
2. **`Telegram/SourceFiles/mtproto/scheme/api.tl`**
18+
* Defines the higher-level Telegram API layer, including all the methods and types related to chat functionality, user profiles, messages, channels, stickers, etc.
19+
* This is the primary schema used when making functional API requests within the application.
20+
21+
Both files use the same TL syntax to describe API methods (functions) and types (constructors).
22+
23+
## Code Generation
24+
25+
A custom code generation tool processes `api.tl` (and parts of `mtproto.tl`) to create corresponding C++ classes and types. These generated headers are typically included via the Precompiled Header (PCH) for the main `Telegram` project.
26+
27+
Generated types often follow the pattern `MTP[Type]` (e.g., `MTPUser`, `MTPMessage`) and methods correspond to functions within the `MTP` namespace or related classes (e.g., `MTPmessages_SendMessage`).
28+
29+
## Making API Requests
30+
31+
API requests are made using a standard pattern involving the `api()` object (providing access to the `MTP::Instance`), the generated `MTP...` request object, callback handlers for success (`.done()`) and failure (`.fail()`), and the `.send()` method.
32+
33+
Here's the general structure:
34+
35+
```cpp
36+
// Include necessary headers if not already in PCH
37+
38+
// Obtain the API instance (usually via api() or MTP::Instance::Get())
39+
api().request(MTPnamespace_MethodName(
40+
// Constructor arguments based on the api.tl definition for the method
41+
MTP_flags(flags_value), // Use MTP_flags if the method has flags
42+
MTP_inputPeer(peer), // Use MTP_... types for parameters
43+
MTP_string(messageText),
44+
MTP_long(randomId),
45+
// ... other arguments matching the TL definition
46+
MTP_vector<MTPMessageEntity>() // Example for a vector argument
47+
)).done([=]\(const MTPResponseType &result) {
48+
// Handle the successful response (result).
49+
// 'result' will be of the C++ type corresponding to the TL type
50+
// specified after the '=' in the api.tl method definition.
51+
// How to access data depends on whether the TL type has one or multiple constructors:
52+
53+
// 1. Multiple Constructors (e.g., User = User | UserEmpty):
54+
// Use .match() with lambdas for each constructor:
55+
result.match([&]\(const MTPDuser &data) {
56+
/* use data.vfirst_name().v, etc. */
57+
}, [&]\(const MTPDuserEmpty &data) {
58+
/* handle empty user */
59+
});
60+
61+
// Alternatively, check the type explicitly and use the constructor getter:
62+
if (result.type() == mtpc_user) {
63+
const auto &data = result.c_user(); // Asserts if type is not mtpc_user!
64+
// use data.vfirst_name().v
65+
} else if (result.type() == mtpc_userEmpty) {
66+
const auto &data = result.c_userEmpty();
67+
// handle empty user
68+
}
69+
70+
// 2. Single Constructor (e.g., Messages = messages { msgs: vector<Message> }):
71+
// Use .match() with a single lambda:
72+
result.match([&]\(const MTPDmessages &data) { /* use data.vmessages().v */ });
73+
74+
// Or check the type explicitly and use the constructor getter:
75+
if (result.type() == mtpc_messages) {
76+
const auto &data = result.c_messages(); // Asserts if type is not mtpc_messages!
77+
// use data.vmessages().v
78+
}
79+
80+
// Or use the shortcut .data() for single-constructor types:
81+
const auto &data = result.data(); // Only works for single-constructor types!
82+
// use data.vmessages().v
83+
84+
}).fail([=]\(const MTP::Error &error) {
85+
// Handle the API error (error).
86+
// 'error' is an MTP::Error object containing the error code (error.type())
87+
// and description (error.description()). Check for specific error strings.
88+
if (error.type() == u"FLOOD_WAIT_X"_q) {
89+
// Handle flood wait
90+
} else {
91+
Ui::show(Box<InformBox>(Lang::Hard::ServerError())); // Example generic error handling
92+
}
93+
}).handleFloodErrors().send(); // handleFloodErrors() is common, then send()
94+
```
95+
96+
**Key Points:**
97+
98+
* Always refer to `Telegram/SourceFiles/mtproto/scheme/api.tl` for the correct method names, parameters (names and types), and response types.
99+
* Use the generated `MTP...` types/classes for request parameters (e.g., `MTP_int`, `MTP_string`, `MTP_bool`, `MTP_vector`, `MTPInputUser`, etc.) and response handling.
100+
* The `.done()` lambda receives the specific C++ `MTP...` type corresponding to the TL return type.
101+
* For types with **multiple constructors** (e.g., `User = User | UserEmpty`), use `result.match([&]\(const MTPDuser &d){ ... }, [&]\(const MTPDuserEmpty &d){ ... })` to handle each case, or check `result.type() == mtpc_user` / `mtpc_userEmpty` and call the specific `result.c_user()` / `result.c_userEmpty()` getter (which asserts on type mismatch).
102+
* For types with a **single constructor** (e.g., `Messages = messages{...}`), you can use `result.match([&]\(const MTPDmessages &d){ ... })` with one lambda, or check `type()` and call `c_messages()`, or use the shortcut `result.data()` to access the fields directly.
103+
* The `.fail()` lambda receives an `MTP::Error` object. Check `error.type()` against known error strings (often defined as constants or using `u"..."_q` literals).
104+
* Directly construct the `MTPnamespace_MethodName(...)` object inside `request()`.
105+
* Include `.handleFloodErrors()` before `.send()` for standard flood wait handling.

.cursor/rules/localization.mdc

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
---
2+
description: For tasks requiring changing or adding user facing phrases and text parts.
3+
globs:
4+
alwaysApply: false
5+
---
6+
# Telegram Desktop Localization
7+
8+
## Coding Style Note
9+
10+
**Use `auto`:** In the actual codebase, variable types are almost always deduced using `auto` (or `const auto`, `const auto &`) rather than being written out explicitly. Examples in this guide may use explicit types for clarity, but prefer `auto` in practice.
11+
12+
```cpp
13+
// Prefer this:
14+
auto currentTitle = tr::lng_settings_title(tr::now);
15+
auto nameProducer = GetNameProducer(); // Returns rpl::producer<...>
16+
17+
// Instead of this:
18+
QString currentTitle = tr::lng_settings_title(tr::now);
19+
rpl::producer<QString> nameProducer = GetNameProducer();
20+
```
21+
22+
## String Resource File
23+
24+
Base user-facing English strings are defined in the `lang.strings` file:
25+
26+
`Telegram/Resources/langs/lang.strings`
27+
28+
This file uses a key-value format with named placeholders:
29+
30+
```
31+
"lng_settings_title" = "Settings";
32+
"lng_confirm_delete_item" = "Are you sure you want to delete {item_name}?";
33+
"lng_files_selected" = "{count} files selected"; // Simple count example (see Pluralization)
34+
```
35+
36+
Placeholders are enclosed in curly braces, e.g., `{name}`, `{user}`. A special placeholder `{count}` is used for pluralization rules.
37+
38+
### Pluralization
39+
40+
For keys that depend on a number (using the `{count}` placeholder), English typically requires two forms: singular and plural. These are defined in `lang.strings` using `#one` and `#other` suffixes:
41+
42+
```
43+
"lng_files_selected#one" = "{count} file selected";
44+
"lng_files_selected#other" = "{count} files selected";
45+
```
46+
47+
While only `#one` and `#other` are defined in the base `lang.strings`, the code generation process creates C++ accessors for all six CLDR plural categories (`#zero`, `#one`, `#two`, `#few`, `#many`, `#other`) to support languages with more complex pluralization rules.
48+
49+
## Translation Process
50+
51+
While `lang.strings` provides the base English text and the keys, the actual translations are managed via Telegram's translations platform (translations.telegram.org) and loaded dynamically at runtime from the API. The keys from `lang.strings` (including the `#one`/`#other` variants) are used on the platform.
52+
53+
## Code Generation
54+
55+
A code generation tool processes `lang.strings` to create C++ structures and accessors within the `tr` namespace. These allow type-safe access to strings and handling of placeholders and pluralization. Generated keys typically follow the pattern `tr::lng_key_name`.
56+
57+
## String Usage in Code
58+
59+
Strings are accessed in C++ code using the generated objects within the `tr::` namespace. There are two main ways to use them: reactively (returning an `rpl::producer`) or immediately (returning the current value).
60+
61+
### 1. Reactive Usage (rpl::producer)
62+
63+
Calling a generated string function directly returns a reactive producer, typically `rpl::producer<QString>`. This producer automatically updates its value whenever the application language changes.
64+
65+
```cpp
66+
// Key: "settings_title" = "Settings";
67+
auto titleProducer = tr::lng_settings_title(); // Type: rpl::producer<QString>
68+
69+
// Key: "confirm_delete_item" = "Are you sure you want to delete {item_name}?";
70+
auto itemNameProducer = /* ... */; // Type: rpl::producer<QString>
71+
auto confirmationProducer = tr::lng_confirm_delete_item( // Type: rpl::producer<QString>
72+
tr::now, // NOTE: tr::now is NOT passed here for reactive result
73+
lt_item_name,
74+
std::move(itemNameProducer)); // Placeholder producers should be moved
75+
```
76+
77+
### 2. Immediate Usage (Current Value)
78+
79+
Passing `tr::now` as the first argument retrieves the string's current value in the active language (typically as a `QString`).
80+
81+
```cpp
82+
// Key: "settings_title" = "Settings";
83+
auto currentTitle = tr::lng_settings_title(tr::now); // Type: QString
84+
85+
// Key: "confirm_delete_item" = "Are you sure you want to delete {item_name}?";
86+
const auto currentItemName = QString("My Document"); // Type: QString
87+
auto currentConfirmation = tr::lng_confirm_delete_item( // Type: QString
88+
tr::now, // Pass tr::now for immediate value
89+
lt_item_name, currentItemName); // Placeholder value is a direct QString (or convertible)
90+
```
91+
92+
### 3. Placeholders (`{tag}`)
93+
94+
Placeholders like `{item_name}` are replaced by providing arguments after `tr::now` (for immediate) or as the initial arguments (for reactive). A corresponding `lt_tag_name` constant is passed before the value.
95+
96+
* **Immediate:** Pass the direct value (e.g., `QString`, `int`).
97+
* **Reactive:** Pass an `rpl::producer` of the corresponding type (e.g., `rpl::producer<QString>`). Remember to `std::move` the producer or use `rpl::duplicate` if you need to reuse the original producer afterwards.
98+
99+
### 4. Pluralization (`{count}`)
100+
101+
Keys using `{count}` require a numeric value for the `lt_count` placeholder. The correct plural form (`#zero`, `#one`, ..., `#other`) is automatically selected based on this value and the current language rules.
102+
103+
* **Immediate (`tr::now`):** Pass a `float64` or `int` (which is auto-converted to `float64`).
104+
```cpp
105+
int count = 1;
106+
auto filesText = tr::lng_files_selected(tr::now, lt_count, count); // Type: QString
107+
count = 5;
108+
filesText = tr::lng_files_selected(tr::now, lt_count, count); // Uses "files_selected#other"
109+
```
110+
111+
* **Reactive:** Pass an `rpl::producer<float64>`. Use the `tr::to_count()` helper to convert an `rpl::producer<int>` or wrap a single value.
112+
```cpp
113+
// From an existing int producer:
114+
auto countProducer = /* ... */; // Type: rpl::producer<int>
115+
auto filesTextProducer = tr::lng_files_selected( // Type: rpl::producer<QString>
116+
lt_count,
117+
countProducer | tr::to_count()); // Use tr::to_count() for conversion
118+
119+
// From a single int value wrapped reactively:
120+
int currentCount = 5;
121+
auto filesTextProducerSingle = tr::lng_files_selected( // Type: rpl::producer<QString>
122+
lt_count,
123+
rpl::single(currentCount) | tr::to_count());
124+
// Alternative for single values (less common): rpl::single(currentCount * 1.)
125+
```
126+
127+
### 5. Custom Projectors
128+
129+
An optional final argument can be a projector function (like `Ui::Text::Upper` or `Ui::Text::WithEntities`) to transform the output.
130+
131+
* If the projector returns `OutputType`, the string function returns `OutputType` (immediate) or `rpl::producer<OutputType>` (reactive).
132+
* Placeholder values must match the projector's *input* requirements. For `Ui::Text::WithEntities`, placeholders expect `TextWithEntities` (immediate) or `rpl::producer<TextWithEntities>` (reactive).
133+
134+
```cpp
135+
// Immediate with Ui::Text::WithEntities projector
136+
// Key: "user_posted_photo" = "{user} posted a photo";
137+
const auto userName = TextWithEntities{ /* ... */ }; // Type: TextWithEntities
138+
auto message = tr::lng_user_posted_photo( // Type: TextWithEntities
139+
tr::now,
140+
lt_user,
141+
userName, // Must be TextWithEntities
142+
Ui::Text::WithEntities); // Projector
143+
144+
// Reactive with Ui::Text::WithEntities projector
145+
auto userNameProducer = /* ... */; // Type: rpl::producer<TextWithEntities>
146+
auto messageProducer = tr::lng_user_posted_photo( // Type: rpl::producer<TextWithEntities>
147+
lt_user,
148+
std::move(userNameProducer), // Move placeholder producers
149+
Ui::Text::WithEntities); // Projector
150+
```
151+
152+
## Key Summary
153+
154+
* Keys are defined in `Resources/langs/lang.strings` using `{tag}` placeholders.
155+
* Plural keys use `{count}` and have `#one`/`#other` variants in `lang.strings`.
156+
* Access keys via `tr::lng_key_name(...)` in C++.
157+
* Call with `tr::now` as the first argument for the immediate `QString` (or projected type).
158+
* Call without `tr::now` for the reactive `rpl::producer<QString>` (or projected type).
159+
* Provide placeholder values (`lt_tag_name, value`) matching the usage (direct value for immediate, `rpl::producer` for reactive). Producers should typically be moved via `std::move`.
160+
* For `{count}`:
161+
* Immediate: Pass `int` or `float64`.
162+
* Reactive: Pass `rpl::producer<float64>`, typically by converting an `int` producer using `| tr::to_count()`.
163+
* Optional projector function as the last argument modifies the output type and required placeholder types.
164+
* Actual translations are loaded at runtime from the API.

0 commit comments

Comments
 (0)