From 8ed433cc01ad67de8646edc5cb51f39538111cd3 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 18 Jun 2019 16:07:45 +0200
Subject: [PATCH] Use tr:: instead of langFactory in input fields.

---
 .../SourceFiles/boxes/add_contact_box.cpp     |  26 +--
 .../SourceFiles/boxes/change_phone_box.cpp    |   4 +-
 .../SourceFiles/boxes/confirm_phone_box.cpp   |   6 +-
 .../SourceFiles/boxes/confirm_phone_box.h     |   2 +-
 Telegram/SourceFiles/boxes/connection_box.cpp |  10 +-
 .../SourceFiles/boxes/create_poll_box.cpp     |   8 +-
 .../SourceFiles/boxes/edit_caption_box.cpp    |   2 +-
 Telegram/SourceFiles/boxes/edit_privacy_box.h |   2 +-
 Telegram/SourceFiles/boxes/language_box.cpp   |   2 +-
 Telegram/SourceFiles/boxes/passcode_box.cpp   |  22 +--
 Telegram/SourceFiles/boxes/peer_list_box.cpp  |   5 +-
 .../boxes/peers/edit_contact_box.cpp          |   4 +-
 .../boxes/peers/edit_peer_info_box.cpp        |   8 +-
 .../boxes/peers/edit_peer_type_box.cpp        |   2 +-
 Telegram/SourceFiles/boxes/rate_call_box.cpp  |   2 +-
 Telegram/SourceFiles/boxes/report_box.cpp     |   4 +-
 Telegram/SourceFiles/boxes/send_files_box.cpp |   8 +-
 Telegram/SourceFiles/boxes/share_box.cpp      |   4 +-
 Telegram/SourceFiles/boxes/stickers_box.cpp   |   2 +-
 Telegram/SourceFiles/boxes/username_box.cpp   |   2 +-
 .../chat_helpers/gifs_list_widget.cpp         |   2 +-
 .../chat_helpers/message_field.cpp            |   4 +-
 .../chat_helpers/stickers_list_widget.cpp     |   2 +-
 .../chat_helpers/tabbed_selector.cpp          |  16 +-
 .../SourceFiles/dialogs/dialogs_widget.cpp    |   2 +-
 .../SourceFiles/export/export_controller.cpp  |   2 +-
 .../export/view/export_view_settings.h        |   2 +-
 .../admin_log/history_admin_log_section.cpp   |   2 +-
 .../SourceFiles/history/history_widget.cpp    |  21 +--
 .../info/profile/info_profile_button.cpp      |   4 +-
 .../info/profile/info_profile_cover.cpp       |   2 +-
 Telegram/SourceFiles/intro/introcode.cpp      |   6 +-
 Telegram/SourceFiles/intro/introcode.h        |   5 +-
 Telegram/SourceFiles/intro/intropwdcheck.cpp  |   4 +-
 Telegram/SourceFiles/intro/introsignup.cpp    |   4 +-
 .../streaming/media_streaming_player.cpp      |   4 +-
 .../passport/passport_panel_controller.cpp    |   4 +-
 .../passport/passport_panel_details_row.cpp   |  12 +-
 .../passport/passport_panel_edit_contact.cpp  |  12 +-
 .../passport/passport_panel_edit_contact.h    |   2 +-
 .../passport/passport_panel_password.cpp      |   2 +-
 Telegram/SourceFiles/rpl/producer.h           |  17 +-
 .../SourceFiles/settings/settings_common.h    |   2 +-
 .../settings/settings_information.cpp         |   2 +-
 .../support/support_autocomplete.cpp          |   2 +-
 .../SourceFiles/support/support_helper.cpp    |   4 +-
 Telegram/SourceFiles/ui/countryinput.cpp      |   4 +-
 .../ui/search_field_controller.cpp            |   2 +-
 .../SourceFiles/ui/widgets/input_fields.cpp   | 158 +++++++++++-------
 .../SourceFiles/ui/widgets/input_fields.h     |  56 ++++---
 .../SourceFiles/ui/widgets/multi_select.cpp   |  18 +-
 .../SourceFiles/ui/widgets/multi_select.h     |  11 +-
 .../window/notifications_manager_default.cpp  |   2 +-
 .../window/themes/window_theme_editor.cpp     |   2 +-
 .../window/window_lock_widgets.cpp            |   2 +-
 55 files changed, 299 insertions(+), 222 deletions(-)

diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp
index 360b385ca..9a9d012ea 100644
--- a/Telegram/SourceFiles/boxes/add_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp
@@ -189,9 +189,9 @@ private:
 };
 
 AddContactBox::AddContactBox(QWidget*, QString fname, QString lname, QString phone)
-: _first(this, st::defaultInputField, langFactory(lng_signup_firstname), fname)
-, _last(this, st::defaultInputField, langFactory(lng_signup_lastname), lname)
-, _phone(this, st::defaultInputField, langFactory(lng_contact_phone), phone)
+: _first(this, st::defaultInputField, tr::lng_signup_firstname(), fname)
+, _last(this, st::defaultInputField, tr::lng_signup_lastname(), lname)
+, _phone(this, st::defaultInputField, tr::lng_contact_phone(), phone)
 , _invertOrder(langFirstNameGoesSecond()) {
 	if (!phone.isEmpty()) {
 		_phone->setDisabled(true);
@@ -200,9 +200,9 @@ AddContactBox::AddContactBox(QWidget*, QString fname, QString lname, QString pho
 
 AddContactBox::AddContactBox(QWidget*, UserData *user)
 : _user(user)
-, _first(this, st::defaultInputField, langFactory(lng_signup_firstname), user->firstName)
-, _last(this, st::defaultInputField, langFactory(lng_signup_lastname), user->lastName)
-, _phone(this, st::defaultInputField, langFactory(lng_contact_phone), user->phone())
+, _first(this, st::defaultInputField, tr::lng_signup_firstname(), user->firstName)
+, _last(this, st::defaultInputField, tr::lng_signup_lastname(), user->lastName)
+, _phone(this, st::defaultInputField, tr::lng_contact_phone(), user->phone())
 , _invertOrder(langFirstNameGoesSecond()) {
 	_phone->setDisabled(true);
 }
@@ -427,9 +427,9 @@ void GroupInfoBox::prepare() {
 	_title.create(
 		this,
 		st::defaultInputField,
-		langFactory((_type == Type::Channel)
-			? lng_dlg_new_channel_name
-			: lng_dlg_new_group_name),
+		(_type == Type::Channel
+			? tr::lng_dlg_new_channel_name
+			: tr::lng_dlg_new_group_name)(),
 		_initialTitle);
 	_title->setMaxLength(kMaxGroupChannelTitle);
 	_title->setInstantReplaces(Ui::InstantReplaces::Default());
@@ -443,7 +443,7 @@ void GroupInfoBox::prepare() {
 			this,
 			st::newGroupDescription,
 			Ui::InputField::Mode::MultiLine,
-			langFactory(lng_create_group_description));
+			tr::lng_create_group_description());
 		_description->show();
 		_description->setMaxLength(kMaxChannelDescription);
 		_description->setInstantReplaces(Ui::InstantReplaces::Default());
@@ -733,7 +733,7 @@ SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing)
 , _aboutPublicWidth(st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadio.diameter - st::defaultBoxCheckbox.textPosition.x())
 , _aboutPublic(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_public_group_about : lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth)
 , _aboutPrivate(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_private_group_about : lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth)
-, _link(this, st::setupChannelLink, Fn<QString()>(), channel->username, true) {
+, _link(this, st::setupChannelLink, nullptr, channel->username, true) {
 }
 
 void SetupChannelBox::prepare() {
@@ -1084,8 +1084,8 @@ bool SetupChannelBox::onFirstCheckFail(const RPCError &error) {
 
 EditNameBox::EditNameBox(QWidget*, not_null<UserData*> user)
 : _user(user)
-, _first(this, st::defaultInputField, langFactory(lng_signup_firstname), _user->firstName)
-, _last(this, st::defaultInputField, langFactory(lng_signup_lastname), _user->lastName)
+, _first(this, st::defaultInputField, tr::lng_signup_firstname(), _user->firstName)
+, _last(this, st::defaultInputField, tr::lng_signup_lastname(), _user->lastName)
 , _invertOrder(langFirstNameGoesSecond()) {
 }
 
diff --git a/Telegram/SourceFiles/boxes/change_phone_box.cpp b/Telegram/SourceFiles/boxes/change_phone_box.cpp
index edf34d2d5..24e19d28c 100644
--- a/Telegram/SourceFiles/boxes/change_phone_box.cpp
+++ b/Telegram/SourceFiles/boxes/change_phone_box.cpp
@@ -123,7 +123,7 @@ void ChangePhoneBox::EnterPhone::prepare() {
 	setTitle(langFactory(lng_change_phone_title));
 
 	auto phoneValue = QString();
-	_phone.create(this, st::defaultInputField, langFactory(lng_change_phone_new_title), phoneValue);
+	_phone.create(this, st::defaultInputField, tr::lng_change_phone_new_title(), phoneValue);
 
 	_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());
 	_phone->moveToLeft(st::boxPadding.left(), st::boxLittleSkip);
@@ -244,7 +244,7 @@ void ChangePhoneBox::EnterCode::prepare() {
 	description->moveToLeft(st::boxPadding.left(), 0);
 
 	auto phoneValue = QString();
-	_code.create(this, st::defaultInputField, langFactory(lng_change_phone_code_title), phoneValue);
+	_code.create(this, st::defaultInputField, tr::lng_change_phone_code_title(), phoneValue);
 	_code->setAutoSubmit(_codeLength, [=] { submit(); });
 	_code->setChangedCallback([=] { hideError(); });
 
diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp
index d2a3fc20c..b0156e2b9 100644
--- a/Telegram/SourceFiles/boxes/confirm_phone_box.cpp
+++ b/Telegram/SourceFiles/boxes/confirm_phone_box.cpp
@@ -69,9 +69,9 @@ void ShowPhoneBannedError(const QString &phone) {
 SentCodeField::SentCodeField(
 	QWidget *parent,
 	const style::InputField &st,
-	Fn<QString()> placeholderFactory,
+	rpl::producer<QString> placeholder,
 	const QString &val)
-: Ui::InputField(parent, st, std::move(placeholderFactory), val) {
+: Ui::InputField(parent, st, std::move(placeholder), val) {
 	connect(this, &Ui::InputField::changed, [this] { fix(); });
 }
 
@@ -278,7 +278,7 @@ void ConfirmPhoneBox::prepare() {
 	}
 	_about->setMarkedText(aboutText);
 
-	_code.create(this, st::confirmPhoneCodeField, langFactory(lng_code_ph));
+	_code.create(this, st::confirmPhoneCodeField, tr::lng_code_ph());
 	_code->setAutoSubmit(_sentCodeLength, [=] { sendCode(); });
 	_code->setChangedCallback([=] { showError(QString()); });
 
diff --git a/Telegram/SourceFiles/boxes/confirm_phone_box.h b/Telegram/SourceFiles/boxes/confirm_phone_box.h
index 839181db6..7350d8d42 100644
--- a/Telegram/SourceFiles/boxes/confirm_phone_box.h
+++ b/Telegram/SourceFiles/boxes/confirm_phone_box.h
@@ -23,7 +23,7 @@ public:
 	SentCodeField(
 		QWidget *parent,
 		const style::InputField &st,
-		Fn<QString()> placeholderFactory = nullptr,
+		rpl::producer<QString> placeholder = nullptr,
 		const QString &val = QString());
 
 	void setAutoSubmit(int length, Fn<void()> submitCallback);
diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp
index 4eef66538..5c083999d 100644
--- a/Telegram/SourceFiles/boxes/connection_box.cpp
+++ b/Telegram/SourceFiles/boxes/connection_box.cpp
@@ -792,12 +792,12 @@ void ProxyBox::setupSocketAddress(const ProxyData &data) {
 	_host = Ui::CreateChild<Ui::InputField>(
 		address,
 		st::connectionHostInputField,
-		langFactory(lng_connection_host_ph),
+		tr::lng_connection_host_ph(),
 		data.host);
 	_port = Ui::CreateChild<Ui::PortInput>(
 		address,
 		st::connectionPortInputField,
-		langFactory(lng_connection_port_ph),
+		tr::lng_connection_port_ph(),
 		data.port ? QString::number(data.port) : QString());
 	address->widthValue(
 	) | rpl::start_with_next([=](int width) {
@@ -820,7 +820,7 @@ void ProxyBox::setupCredentials(const ProxyData &data) {
 		object_ptr<Ui::InputField>(
 			credentials,
 			st::connectionUserInputField,
-			langFactory(lng_connection_user_ph),
+			tr::lng_connection_user_ph(),
 			data.user),
 		st::proxyEditInputPadding);
 
@@ -828,7 +828,7 @@ void ProxyBox::setupCredentials(const ProxyData &data) {
 	_password = Ui::CreateChild<Ui::PasswordInput>(
 		passwordWrap.data(),
 		st::connectionPasswordInputField,
-		langFactory(lng_connection_password_ph),
+		tr::lng_connection_password_ph(),
 		(data.type == Type::Mtproto) ? QString() : data.password);
 	_password->move(0, 0);
 	_password->heightValue(
@@ -854,7 +854,7 @@ void ProxyBox::setupMtprotoCredentials(const ProxyData &data) {
 	_secret = Ui::CreateChild<Ui::HexInput>(
 		secretWrap.data(),
 		st::connectionUserInputField,
-		langFactory(lng_connection_proxy_secret_ph),
+		tr::lng_connection_proxy_secret_ph(),
 		(data.type == Type::Mtproto) ? data.password : QString());
 	_secret->setMaxLength(ProxyData::MaxMtprotoPasswordLength());
 	_secret->move(0, 0);
diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp
index 6045602a6..dd9cc22aa 100644
--- a/Telegram/SourceFiles/boxes/create_poll_box.cpp
+++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp
@@ -186,7 +186,7 @@ Options::Option Options::Option::Create(
 				container,
 				st::createPollOptionField,
 				Ui::InputField::Mode::NoNewlines,
-				langFactory(lng_polls_create_option_add))));
+				tr::lng_polls_create_option_add())));
 	InitField(outer, field->entity());
 	field->entity()->setMaxLength(kOptionLimit + kErrorLimit);
 	result._field.reset(field);
@@ -311,7 +311,7 @@ void Options::Option::clearValue() {
 }
 
 void Options::Option::setPlaceholder() const {
-	field()->setPlaceholder(langFactory(lng_polls_create_option_add));
+	field()->setPlaceholder(tr::lng_polls_create_option_add());
 }
 
 void Options::Option::toggleRemoveAlways(bool toggled) {
@@ -323,7 +323,7 @@ not_null<Ui::InputField*> Options::Option::field() const {
 }
 
 void Options::Option::removePlaceholder() const {
-	field()->setPlaceholder(nullptr);
+	field()->setPlaceholder(rpl::single(QString()));
 }
 
 PollAnswer Options::Option::toPollAnswer(int index) const {
@@ -603,7 +603,7 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
 			container,
 			st::createPollField,
 			Ui::InputField::Mode::MultiLine,
-			langFactory(lng_polls_create_question_placeholder)),
+			tr::lng_polls_create_question_placeholder()),
 		st::createPollFieldPadding);
 	InitField(getDelegate()->outerContainer(), question);
 	question->setMaxLength(kQuestionLimit + kErrorLimit);
diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
index 0fce6d403..395ece0d7 100644
--- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp
+++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp
@@ -249,7 +249,7 @@ EditCaptionBox::EditCaptionBox(
 		this,
 		st::confirmCaptionArea,
 		Ui::InputField::Mode::MultiLine,
-		langFactory(lng_photo_caption),
+		tr::lng_photo_caption(),
 		editData);
 	_field->setMaxLength(Global::CaptionLengthMax());
 	_field->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.h b/Telegram/SourceFiles/boxes/edit_privacy_box.h
index 65376b837..0b6f96cf2 100644
--- a/Telegram/SourceFiles/boxes/edit_privacy_box.h
+++ b/Telegram/SourceFiles/boxes/edit_privacy_box.h
@@ -46,7 +46,7 @@ public:
 	[[nodiscard]] virtual rpl::producer<QString> optionsTitleKey() = 0;
 	[[nodiscard]] virtual LangKey optionLabelKey(Option option);
 	[[nodiscard]] virtual rpl::producer<QString> warning() {
-		return rpl::never<QString>();
+		return nullptr;
 	}
 	[[nodiscard]] virtual rpl::producer<QString> exceptionButtonTextKey(
 		Exception exception) = 0;
diff --git a/Telegram/SourceFiles/boxes/language_box.cpp b/Telegram/SourceFiles/boxes/language_box.cpp
index 289b99077..3b29e98b1 100644
--- a/Telegram/SourceFiles/boxes/language_box.cpp
+++ b/Telegram/SourceFiles/boxes/language_box.cpp
@@ -1122,7 +1122,7 @@ not_null<Ui::MultiSelect*> LanguageBox::createMultiSelect() {
 	const auto result = Ui::CreateChild<Ui::MultiSelect>(
 		this,
 		st::contactsMultiSelect,
-		langFactory(lng_participant_filter));
+		tr::lng_participant_filter());
 	result->resizeToWidth(st::boxWidth);
 	result->moveToLeft(0, 0);
 	return result;
diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp
index 18bee19e4..c2f109ba9 100644
--- a/Telegram/SourceFiles/boxes/passcode_box.cpp
+++ b/Telegram/SourceFiles/boxes/passcode_box.cpp
@@ -43,11 +43,11 @@ PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
 PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
 : _turningOff(turningOff)
 , _about(st::boxWidth - st::boxPadding.left() * 1.5)
-, _oldPasscode(this, st::defaultInputField, langFactory(lng_passcode_enter_old))
-, _newPasscode(this, st::defaultInputField, langFactory(Global::LocalPasscode() ? lng_passcode_enter_new : lng_passcode_enter_first))
-, _reenterPasscode(this, st::defaultInputField, langFactory(lng_passcode_confirm_new))
-, _passwordHint(this, st::defaultInputField, langFactory(lng_cloud_password_hint))
-, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
+, _oldPasscode(this, st::defaultInputField, tr::lng_passcode_enter_old())
+, _newPasscode(this, st::defaultInputField, Global::LocalPasscode() ? tr::lng_passcode_enter_new() : tr::lng_passcode_enter_first())
+, _reenterPasscode(this, st::defaultInputField, tr::lng_passcode_confirm_new())
+, _passwordHint(this, st::defaultInputField, tr::lng_cloud_password_hint())
+, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
 , _recover(this, lang(lng_signin_recover)) {
 }
 
@@ -56,11 +56,11 @@ PasscodeBox::PasscodeBox(QWidget*, const CloudFields &fields)
 , _cloudPwd(true)
 , _cloudFields(fields)
 , _about(st::boxWidth - st::boxPadding.left() * 1.5)
-, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
-, _newPasscode(this, st::defaultInputField, langFactory(fields.curRequest ? lng_cloud_password_enter_new : lng_cloud_password_enter_first))
-, _reenterPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_confirm_new))
-, _passwordHint(this, st::defaultInputField, langFactory(fields.curRequest ? lng_cloud_password_change_hint : lng_cloud_password_hint))
-, _recoverEmail(this, st::defaultInputField, langFactory(lng_cloud_password_email))
+, _oldPasscode(this, st::defaultInputField, tr::lng_cloud_password_enter_old())
+, _newPasscode(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_enter_new() : tr::lng_cloud_password_enter_first())
+, _reenterPasscode(this, st::defaultInputField, tr::lng_cloud_password_confirm_new())
+, _passwordHint(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_change_hint() : tr::lng_cloud_password_hint())
+, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
 , _recover(this, lang(lng_signin_recover)) {
 	Expects(!_turningOff || _cloudFields.curRequest);
 
@@ -896,7 +896,7 @@ RecoverBox::RecoverBox(
 	bool notEmptyPassport)
 : _pattern(st::normalFont->elided(lng_signin_recover_hint(lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
 , _notEmptyPassport(notEmptyPassport)
-, _recoverCode(this, st::defaultInputField, langFactory(lng_signin_code)) {
+, _recoverCode(this, st::defaultInputField, tr::lng_signin_code()) {
 }
 
 rpl::producer<> RecoverBox::passwordCleared() const {
diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp
index c25efafc8..141401963 100644
--- a/Telegram/SourceFiles/boxes/peer_list_box.cpp
+++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp
@@ -56,7 +56,10 @@ PeerListBox::PeerListBox(
 void PeerListBox::createMultiSelect() {
 	Expects(_select == nullptr);
 
-	auto entity = object_ptr<Ui::MultiSelect>(this, st::contactsMultiSelect, langFactory(lng_participant_filter));
+	auto entity = object_ptr<Ui::MultiSelect>(
+		this, 
+		st::contactsMultiSelect,
+		tr::lng_participant_filter());
 	_select.create(this, std::move(entity));
 	_select->heightValue(
 	) | rpl::start_with_next(
diff --git a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
index ca1d20cea..6f6f3c2a6 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp
@@ -110,13 +110,13 @@ void Controller::setupNameFields() {
 		object_ptr<Ui::InputField>(
 			_box,
 			st::defaultInputField,
-			langFactory(lng_signup_firstname),
+			tr::lng_signup_firstname(),
 			_user->firstName),
 		st::addContactFieldMargin);
 	auto preparedLast = object_ptr<Ui::InputField>(
 		_box,
 		st::defaultInputField,
-		langFactory(lng_signup_lastname),
+		tr::lng_signup_lastname(),
 		_user->lastName);
 	const auto last = inverted
 		? _box->insertRow(
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
index a4320bc48..e874dfb1c 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
@@ -368,9 +368,9 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
 		object_ptr<Ui::InputField>(
 			_wrap,
 			st::defaultInputField,
-			langFactory(_isGroup
-				? lng_dlg_new_group_name
-				: lng_dlg_new_channel_name),
+			(_isGroup
+				? tr::lng_dlg_new_group_name
+				: tr::lng_dlg_new_channel_name)(),
 			_peer->name),
 		st::editPeerTitleMargins);
 	result->entity()->setMaxLength(kMaxGroupChannelTitle);
@@ -403,7 +403,7 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
 			_wrap,
 			st::editPeerDescription,
 			Ui::InputField::Mode::MultiLine,
-			langFactory(lng_create_group_description),
+			tr::lng_create_group_description(),
 			_peer->about()),
 		st::editPeerDescriptionMargins);
 	result->entity()->setMaxLength(kMaxChannelDescription);
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
index 40c92e7cd..7b4a00c60 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp
@@ -315,7 +315,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
 		object_ptr<Ui::UsernameInput>(
 			container,
 			st::setupChannelLink,
-			Fn<QString()>(),
+			nullptr,
 			username,
 			true));
 	_controls.usernameInput->heightValue(
diff --git a/Telegram/SourceFiles/boxes/rate_call_box.cpp b/Telegram/SourceFiles/boxes/rate_call_box.cpp
index e7ff00528..056ecd3bb 100644
--- a/Telegram/SourceFiles/boxes/rate_call_box.cpp
+++ b/Telegram/SourceFiles/boxes/rate_call_box.cpp
@@ -77,7 +77,7 @@ void RateCallBox::ratingChanged(int value) {
 				this,
 				st::callRatingComment,
 				Ui::InputField::Mode::MultiLine,
-				langFactory(lng_call_rate_comment));
+				tr::lng_call_rate_comment());
 			_comment->show();
 			_comment->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
 			_comment->setMaxLength(kRateCallCommentLengthMax);
diff --git a/Telegram/SourceFiles/boxes/report_box.cpp b/Telegram/SourceFiles/boxes/report_box.cpp
index d1c9c2f28..d61f4545b 100644
--- a/Telegram/SourceFiles/boxes/report_box.cpp
+++ b/Telegram/SourceFiles/boxes/report_box.cpp
@@ -102,7 +102,7 @@ void ReportBox::reasonChanged(Reason reason) {
 				this,
 				st::profileReportReasonOther,
 				Ui::InputField::Mode::MultiLine,
-				langFactory(lng_report_reason_description));
+				tr::lng_report_reason_description());
 			_reasonOtherText->show();
 			_reasonOtherText->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
 			_reasonOtherText->setMaxLength(kReportReasonLengthMax);
@@ -192,7 +192,7 @@ bool ReportBox::reportFail(const RPCError &error) {
 void ReportBox::updateMaxHeight() {
 	const auto buttonsCount = _ids ? 5 : 4;
 	auto newHeight = st::boxOptionListPadding.top() + _reasonSpam->getMargins().top() + buttonsCount * _reasonSpam->heightNoMargins() + (buttonsCount - 1) * st::boxOptionListSkip + _reasonSpam->getMargins().bottom() + st::boxOptionListPadding.bottom();
-			
+
 	if (_reasonOtherText) {
 		newHeight += st::newGroupDescriptionPadding.top() + _reasonOtherText->height() + st::newGroupDescriptionPadding.bottom();
 	}
diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp
index 62fe0dfb4..69472da36 100644
--- a/Telegram/SourceFiles/boxes/send_files_box.cpp
+++ b/Telegram/SourceFiles/boxes/send_files_box.cpp
@@ -905,14 +905,14 @@ rpl::producer<int> SingleFilePreview::desiredHeightValue() const {
 	return rpl::single(st::boxPhotoPadding.top() + h + st::msgShadow);
 }
 
-Fn<QString()> FieldPlaceholder(
+rpl::producer<QString> FieldPlaceholder(
 		const Storage::PreparedList &list,
 		SendFilesWay way) {
 	const auto isAlbum = (way == SendFilesWay::Album);
 	const auto compressImages = (way != SendFilesWay::Files);
-	return langFactory(list.canAddCaption(isAlbum, compressImages)
-		? lng_photo_caption
-		: lng_photos_comment);
+	return list.canAddCaption(isAlbum, compressImages)
+		? tr::lng_photo_caption()
+		: tr::lng_photos_comment();
 }
 
 } // namespace
diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp
index c8ad05818..582654ac2 100644
--- a/Telegram/SourceFiles/boxes/share_box.cpp
+++ b/Telegram/SourceFiles/boxes/share_box.cpp
@@ -160,14 +160,14 @@ ShareBox::ShareBox(
 , _select(
 	this,
 	st::contactsMultiSelect,
-	langFactory(lng_participant_filter))
+	tr::lng_participant_filter())
 , _comment(
 	this,
 	object_ptr<Ui::InputField>(
 		this,
 		st::shareComment,
 		Ui::InputField::Mode::MultiLine,
-		langFactory(lng_photos_comment)),
+		tr::lng_photos_comment()),
 	st::shareCommentPadding)
 , _searchTimer([=] { searchByUsername(); }) {
 }
diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp
index 90561a7f8..10b1118dc 100644
--- a/Telegram/SourceFiles/boxes/stickers_box.cpp
+++ b/Telegram/SourceFiles/boxes/stickers_box.cpp
@@ -673,7 +673,7 @@ StickersBox::Inner::Inner(QWidget *parent, not_null<ChannelData*> megagroup) : T
 , _itemsTop(st::membersMarginTop)
 , _megagroupSet(megagroup)
 , _megagroupSetInput(_megagroupSet->mgInfo->stickerSet)
-, _megagroupSetField(this, st::groupStickersField, [] { return qsl("stickerset"); }, QString(), true)
+, _megagroupSetField(this, st::groupStickersField, rpl::single(qsl("stickerset")), QString(), true)
 , _megagroupDivider(this)
 , _megagroupSubTitle(this, lang(lng_stickers_group_from_your), st::boxTitle) {
 	_megagroupSetField->setLinkPlaceholder(Core::App().createInternalLink(qsl("addstickers/")));
diff --git a/Telegram/SourceFiles/boxes/username_box.cpp b/Telegram/SourceFiles/boxes/username_box.cpp
index e0c032daf..0c0dfc340 100644
--- a/Telegram/SourceFiles/boxes/username_box.cpp
+++ b/Telegram/SourceFiles/boxes/username_box.cpp
@@ -29,7 +29,7 @@ UsernameBox::UsernameBox(QWidget*)
 : _username(
 	this,
 	st::defaultInputField,
-	[] { return qsl("@username"); },
+	rpl::single(qsl("@username")),
 	Auth().user()->username,
 	false)
 , _link(this, QString(), st::boxLinkButton)
diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp
index 5883e6aee..042057172 100644
--- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp
+++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp
@@ -63,7 +63,7 @@ private:
 
 GifsListWidget::Footer::Footer(not_null<GifsListWidget*> parent) : InnerFooter(parent)
 , _pan(parent)
-, _field(this, st::gifsSearchField, langFactory(lng_gifs_search))
+, _field(this, st::gifsSearchField, tr::lng_gifs_search())
 , _cancel(this, st::gifsSearchCancel) {
 	connect(_field, &Ui::InputField::submitted, [=] {
 		_pan->sendInlineRequest();
diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp
index 42ac3dbb3..56449ff97 100644
--- a/Telegram/SourceFiles/chat_helpers/message_field.cpp
+++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp
@@ -112,7 +112,7 @@ void EditLinkBox::prepare() {
 		object_ptr<Ui::InputField>(
 			content,
 			st::defaultInputField,
-			langFactory(lng_formatting_link_text),
+			tr::lng_formatting_link_text(),
 			_startText),
 		st::markdownLinkFieldPadding);
 	text->setInstantReplaces(Ui::InstantReplaces::Default());
@@ -125,7 +125,7 @@ void EditLinkBox::prepare() {
 		object_ptr<Ui::InputField>(
 			content,
 			st::defaultInputField,
-			langFactory(lng_formatting_link_url),
+			tr::lng_formatting_link_url(),
 			_startLink.trimmed()),
 		st::markdownLinkFieldPadding);
 
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
index 9587ba2e4..7d5228b60 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
@@ -197,7 +197,7 @@ void StickersListWidget::Footer::initSearch() {
 	_searchField.create(
 		this,
 		st::gifsSearchField,
-		langFactory(lng_stickers_search_sets));
+		tr::lng_stickers_search_sets());
 	_searchCancel.create(this, st::gifsSearchCancel);
 	_searchField->show();
 	_searchCancel->show(anim::type::instant);
diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp
index 1bec2ac10..7fb537f03 100644
--- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp
+++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp
@@ -401,28 +401,20 @@ rpl::producer<not_null<DocumentData*>> TabbedSelector::fileChosen() const {
 }
 
 rpl::producer<not_null<PhotoData*>> TabbedSelector::photoChosen() const {
-	return full()
-		? gifs()->photoChosen()
-		: rpl::never<not_null<PhotoData*>>();
+	return full() ? gifs()->photoChosen() : nullptr;
 }
 
 auto TabbedSelector::inlineResultChosen() const
 -> rpl::producer<InlineChosen> {
-	return full()
-		? gifs()->inlineResultChosen()
-		: rpl::never<InlineChosen>();
+	return full() ? gifs()->inlineResultChosen() : nullptr;
 }
 
 rpl::producer<> TabbedSelector::cancelled() const {
-	return full()
-		? gifs()->cancelRequests()
-		: rpl::never<>();
+	return full() ? gifs()->cancelRequests() : nullptr;
 }
 
 rpl::producer<> TabbedSelector::checkForHide() const {
-	return full()
-		? stickers()->checkForHide()
-		: rpl::never<>();
+	return full() ? stickers()->checkForHide() : nullptr;
 }
 
 rpl::producer<> TabbedSelector::slideFinished() const {
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index 0d42c454b..22b8c2697 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -156,7 +156,7 @@ Widget::Widget(
 : Window::AbstractSectionWidget(parent, controller)
 , _searchControls(this)
 , _mainMenuToggle(_searchControls, st::dialogsMenuToggle)
-, _filter(_searchControls, st::dialogsFilter, langFactory(lng_dlg_filter))
+, _filter(_searchControls, st::dialogsFilter, tr::lng_dlg_filter())
 , _chooseFromUser(
 	_searchControls,
 	object_ptr<Ui::IconButton>(this, st::dialogsSearchFrom))
diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp
index bfe78ff40..2870e1bca 100644
--- a/Telegram/SourceFiles/export/export_controller.cpp
+++ b/Telegram/SourceFiles/export/export_controller.cpp
@@ -193,7 +193,7 @@ bool ControllerObject::ioCatchError(Output::Result result) {
 //}
 //
 //rpl::producer<PasswordUpdate> ControllerObject::passwordUpdate() const {
-//	return rpl::never<PasswordUpdate>();
+//	return nullptr;
 //}
 //
 //void ControllerObject::reloadPasswordState() {
diff --git a/Telegram/SourceFiles/export/view/export_view_settings.h b/Telegram/SourceFiles/export/view/export_view_settings.h
index 66325ce40..7754f1ecd 100644
--- a/Telegram/SourceFiles/export/view/export_view_settings.h
+++ b/Telegram/SourceFiles/export/view/export_view_settings.h
@@ -102,7 +102,7 @@ private:
 	Settings _internal_data;
 
 	struct Wrap {
-		Wrap(rpl::producer<> value = rpl::never<>())
+		Wrap(rpl::producer<> value = nullptr)
 		: value(std::move(value)) {
 		}
 
diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp
index b399a0d6a..c1ec04d63 100644
--- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp
+++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.cpp
@@ -104,7 +104,7 @@ FixedBar::FixedBar(
 	not_null<ChannelData*> channel) : TWidget(parent)
 , _controller(controller)
 , _channel(channel)
-, _field(this, st::historyAdminLogSearchField, langFactory(lng_dlg_filter))
+, _field(this, st::historyAdminLogSearchField, tr::lng_dlg_filter())
 , _backButton(this, lang(lng_admin_log_title_all))
 , _search(this, st::topBarSearch)
 , _cancel(this, st::historyAdminLogCancelSearch)
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 3bc2e9017..48c29bf9f 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -272,7 +272,7 @@ HistoryWidget::HistoryWidget(
 	this,
 	st::historyComposeField,
 	Ui::InputField::Mode::MultiLine,
-	langFactory(lng_message_ph))
+	tr::lng_message_ph())
 , _recordCancelWidth(st::historyRecordFont->width(lang(lng_record_cancel)))
 , _recordingAnimation([=](crl::time now) {
 	return recordingAnimationCallback(now);
@@ -3899,19 +3899,20 @@ void HistoryWidget::onCheckFieldAutocomplete() {
 
 void HistoryWidget::updateFieldPlaceholder() {
 	if (_editMsgId) {
-		_field->setPlaceholder(langFactory(lng_edit_message_text));
+		_field->setPlaceholder(tr::lng_edit_message_text());
 	} else {
 		if (_inlineBot && !_inlineLookingUpBot) {
-			auto text = _inlineBot->botInfo->inlinePlaceholder.mid(1);
-			_field->setPlaceholder([text] { return text; }, _inlineBot->username.size() + 2);
+			_field->setPlaceholder(
+				rpl::single(_inlineBot->botInfo->inlinePlaceholder.mid(1)),
+				_inlineBot->username.size() + 2);
 		} else {
 			const auto peer = _history ? _history->peer.get() : nullptr;
-			_field->setPlaceholder(langFactory(
-				(peer && peer->isChannel() && !peer->isMegagroup())
-				? (session().data().notifySilentPosts(peer)
-					? lng_broadcast_silent_ph
-					: lng_broadcast_ph)
-				: lng_message_ph));
+			_field->setPlaceholder(
+				((peer && peer->isChannel() && !peer->isMegagroup())
+					? (session().data().notifySilentPosts(peer)
+						? tr::lng_broadcast_silent_ph()
+						: tr::lng_broadcast_ph())
+					: tr::lng_message_ph()));
 		}
 	}
 	updateSendButtonType();
diff --git a/Telegram/SourceFiles/info/profile/info_profile_button.cpp b/Telegram/SourceFiles/info/profile/info_profile_button.cpp
index 83c0fd269..3f629aab8 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_button.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_button.cpp
@@ -59,14 +59,14 @@ rpl::producer<bool> Button::toggledChanges() const {
 	if (_toggle) {
 		return _toggle->checkedChanges();
 	}
-	return rpl::never<bool>();
+	return nullptr;
 }
 
 rpl::producer<bool> Button::toggledValue() const {
 	if (_toggle) {
 		return _toggle->checkedValue();
 	}
-	return rpl::never<bool>();
+	return nullptr;
 }
 
 void Button::setColorOverride(std::optional<QColor> textColorOverride) {
diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
index 16507b325..16519ae19 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
@@ -194,7 +194,7 @@ rpl::producer<bool> SectionWithToggle::toggledValue() const {
 	if (_toggle) {
 		return _toggle->checkedValue();
 	}
-	return rpl::never<bool>();
+	return nullptr;
 }
 
 rpl::producer<bool> SectionWithToggle::toggleShownValue() const {
diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp
index ff1b36788..6182e47e1 100644
--- a/Telegram/SourceFiles/intro/introcode.cpp
+++ b/Telegram/SourceFiles/intro/introcode.cpp
@@ -22,8 +22,8 @@ namespace Intro {
 CodeInput::CodeInput(
 	QWidget *parent,
 	const style::InputField &st,
-	Fn<QString()> placeholderFactory)
-: Ui::MaskedInputField(parent, st, std::move(placeholderFactory)) {
+	rpl::producer<QString> placeholder)
+: Ui::MaskedInputField(parent, st, std::move(placeholder)) {
 }
 
 void CodeInput::setDigitsCountMax(int digitsCount) {
@@ -75,7 +75,7 @@ void CodeInput::correctValue(const QString &was, int wasCursor, QString &now, in
 
 CodeWidget::CodeWidget(QWidget *parent, Widget::Data *data) : Step(parent, data)
 , _noTelegramCode(this, lang(lng_code_no_telegram), st::introLink)
-, _code(this, st::introCode, langFactory(lng_code_ph))
+, _code(this, st::introCode, tr::lng_code_ph())
 , _callTimer(this)
 , _callStatus(getData()->callStatus)
 , _callTimeout(getData()->callTimeout)
diff --git a/Telegram/SourceFiles/intro/introcode.h b/Telegram/SourceFiles/intro/introcode.h
index 5df47b8cd..7e4d34902 100644
--- a/Telegram/SourceFiles/intro/introcode.h
+++ b/Telegram/SourceFiles/intro/introcode.h
@@ -20,7 +20,10 @@ namespace Intro {
 
 class CodeInput final : public Ui::MaskedInputField {
 public:
-	CodeInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory);
+	CodeInput(
+		QWidget *parent,
+		const style::InputField &st,
+		rpl::producer<QString> placeholder);
 
 	void setDigitsCountMax(int digitsCount);
 
diff --git a/Telegram/SourceFiles/intro/intropwdcheck.cpp b/Telegram/SourceFiles/intro/intropwdcheck.cpp
index 0466329af..41605c119 100644
--- a/Telegram/SourceFiles/intro/intropwdcheck.cpp
+++ b/Telegram/SourceFiles/intro/intropwdcheck.cpp
@@ -29,9 +29,9 @@ PwdCheckWidget::PwdCheckWidget(
 , _hasRecovery(getData()->hasRecovery)
 , _notEmptyPassport(getData()->pwdNotEmptyPassport)
 , _hint(getData()->pwdHint)
-, _pwdField(this, st::introPassword, langFactory(lng_signin_password))
+, _pwdField(this, st::introPassword, tr::lng_signin_password())
 , _pwdHint(this, st::introPasswordHint)
-, _codeField(this, st::introPassword, langFactory(lng_signin_code))
+, _codeField(this, st::introPassword, tr::lng_signin_code())
 , _toRecover(this, lang(lng_signin_recover))
 , _toPassword(this, lang(lng_signin_try_password))
 , _checkRequest(this) {
diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp
index 0a426b424..b3aa28684 100644
--- a/Telegram/SourceFiles/intro/introsignup.cpp
+++ b/Telegram/SourceFiles/intro/introsignup.cpp
@@ -26,8 +26,8 @@ SignupWidget::SignupWidget(QWidget *parent, Widget::Data *data) : Step(parent, d
 	lang(lng_settings_crop_profile),
 	Ui::UserpicButton::Role::ChangePhoto,
 	st::defaultUserpicButton)
-, _first(this, st::introName, langFactory(lng_signup_firstname))
-, _last(this, st::introName, langFactory(lng_signup_lastname))
+, _first(this, st::introName, tr::lng_signup_firstname())
+, _last(this, st::introName, tr::lng_signup_lastname())
 , _invertOrder(langFirstNameGoesSecond())
 , _checkRequest(this) {
 	subscribe(Lang::Current().updated(), [this] { refreshLang(); });
diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp
index 57e833101..05d479443 100644
--- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp
+++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp
@@ -637,8 +637,8 @@ void Player::start() {
 	const auto guard = base::make_weak(&_sessionGuard);
 
 	rpl::merge(
-		_audio ? _audio->waitingForData() : rpl::never(),
-		_video ? _video->waitingForData() : rpl::never()
+		_audio ? _audio->waitingForData() : nullptr,
+		_video ? _video->waitingForData() : nullptr
 	) | rpl::filter([=] {
 		return !bothReceivedEnough(kBufferFor);
 	}) | rpl::start_with_next([=] {
diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp
index 29441a08b..a04206f7b 100644
--- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp
+++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp
@@ -487,7 +487,7 @@ EditContactScheme GetContactScheme(Scope::Type type) {
 		auto result = Scheme(ValueType::Text);
 		result.aboutExisting = lang(lng_passport_use_existing_email);
 		result.newHeader = lang(lng_passport_new_email);
-		result.newPlaceholder = langFactory(lng_passport_email_title);
+		result.newPlaceholder = tr::lng_passport_email_title();
 		result.aboutNew = lang(lng_passport_new_email_code);
 		result.validate = [](const QString &value) {
 			const auto at = value.indexOf('@');
@@ -1360,7 +1360,7 @@ void PanelController::processVerificationNeeded(
 					return field->verification.error;
 				}) | rpl::distinct_until_changed(),
 
-				rpl::never<QString>()));
+				nullptr));
 		} else {
 			Unexpected("Type in processVerificationNeeded.");
 		}
diff --git a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp
index 234be2830..f13409f35 100644
--- a/Telegram/SourceFiles/passport/passport_panel_details_row.cpp
+++ b/Telegram/SourceFiles/passport/passport_panel_details_row.cpp
@@ -29,7 +29,7 @@ public:
 	PostcodeInput(
 		QWidget *parent,
 		const style::InputField &st,
-		Fn<QString()> placeholderFactory,
+		rpl::producer<QString> placeholder,
 		const QString &val);
 
 protected:
@@ -44,9 +44,9 @@ protected:
 PostcodeInput::PostcodeInput(
 	QWidget *parent,
 	const style::InputField &st,
-	Fn<QString()> placeholderFactory,
+	rpl::producer<QString> placeholder,
 	const QString &val)
-: MaskedInputField(parent, st, std::move(placeholderFactory), val) {
+: MaskedInputField(parent, st, std::move(placeholder), val) {
 	if (!QRegularExpression("^[a-zA-Z0-9\\-]+$").match(val).hasMatch()) {
 		setText(QString());
 	}
@@ -523,7 +523,7 @@ DateRow::DateRow(
 , _day(
 	this,
 	st::passportDetailsDateField,
-	langFactory(lng_date_input_day),
+	tr::lng_date_input_day(),
 	GetDay(value))
 , _separator1(
 	this,
@@ -535,7 +535,7 @@ DateRow::DateRow(
 , _month(
 	this,
 	st::passportDetailsDateField,
-	langFactory(lng_date_input_month),
+	tr::lng_date_input_month(),
 	GetMonth(value))
 , _separator2(
 	this,
@@ -547,7 +547,7 @@ DateRow::DateRow(
 , _year(
 	this,
 	st::passportDetailsDateField,
-	langFactory(lng_date_input_year),
+	tr::lng_date_input_year(),
 	GetYear(value))
 , _value(valueCurrent()) {
 	const auto focused = [=](const object_ptr<DateInput> &field) {
diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp
index 602da990b..f78c13cfe 100644
--- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp
+++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp
@@ -108,7 +108,7 @@ void VerifyBox::setupControls(
 		object_ptr<SentCodeField>(
 			_content,
 			st::defaultInputField,
-			langFactory(lng_change_phone_code_title)),
+			tr::lng_change_phone_code_title()),
 		small);
 
 	const auto problem = _content->add(
@@ -265,21 +265,21 @@ void PanelEditContact::setupControls(
 	const auto fieldPadding = existing.isEmpty()
 		? st::passportContactFieldPadding
 		: st::passportContactNewFieldPadding;
-	const auto fieldPlaceholder = existing.isEmpty()
-		? _scheme.newPlaceholder
+	auto fieldPlaceholder = existing.isEmpty()
+		? rpl::duplicate(_scheme.newPlaceholder)
 		: nullptr;
 	auto wrap = object_ptr<Ui::RpWidget>(_content);
 	if (_scheme.type == Scheme::ValueType::Phone) {
 		_field = Ui::CreateChild<Ui::PhoneInput>(
 			wrap.data(),
 			fieldStyle,
-			fieldPlaceholder,
+			std::move(fieldPlaceholder),
 			data);
 	} else {
 		_field = Ui::CreateChild<Ui::MaskedInputField>(
 			wrap.data(),
 			fieldStyle,
-			fieldPlaceholder,
+			std::move(fieldPlaceholder),
 			data);
 	}
 
@@ -398,7 +398,7 @@ object_ptr<BoxContent> VerifyPhoneBox(
 		nullptr,
 		std::move(call),
 		std::move(error),
-		rpl::never<QString>());
+		nullptr);
 }
 
 object_ptr<BoxContent> VerifyEmailBox(
diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.h b/Telegram/SourceFiles/passport/passport_panel_edit_contact.h
index d1f8dc46a..02018d4a6 100644
--- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.h
+++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.h
@@ -31,7 +31,7 @@ struct EditContactScheme {
 
 	QString aboutExisting;
 	QString newHeader;
-	Fn<QString()> newPlaceholder;
+	rpl::producer<QString> newPlaceholder;
 	QString aboutNew;
 	Fn<bool(const QString &value)> validate;
 	Fn<QString(const QString &value)> format;
diff --git a/Telegram/SourceFiles/passport/passport_panel_password.cpp b/Telegram/SourceFiles/passport/passport_panel_password.cpp
index 093dadf50..eb8eb3ad4 100644
--- a/Telegram/SourceFiles/passport/passport_panel_password.cpp
+++ b/Telegram/SourceFiles/passport/passport_panel_password.cpp
@@ -44,7 +44,7 @@ PanelAskPassword::PanelAskPassword(
 , _password(
 	this,
 	st::defaultInputField,
-	langFactory(lng_passport_password_placeholder))
+	tr::lng_passport_password_placeholder())
 , _submit(this, langFactory(lng_passport_next), st::passportPasswordSubmit)
 , _forgot(this, lang(lng_signin_recover), st::defaultLinkButton) {
 	connect(_password, &Ui::PasswordInput::submitted, this, [=] {
diff --git a/Telegram/SourceFiles/rpl/producer.h b/Telegram/SourceFiles/rpl/producer.h
index 7aaa366d9..339f3c746 100644
--- a/Telegram/SourceFiles/rpl/producer.h
+++ b/Telegram/SourceFiles/rpl/producer.h
@@ -65,6 +65,13 @@ public:
 	type_erased_generator &operator=(
 		type_erased_generator &&other) = default;
 
+	type_erased_generator(std::nullptr_t = nullptr) {
+	}
+	type_erased_generator &operator=(std::nullptr_t) {
+		_implementation = nullptr;
+		return *this;
+	}
+
 	template <
 		typename Generator,
 		typename = std::enable_if_t<
@@ -96,7 +103,7 @@ public:
 
 	template <typename Handlers>
 	lifetime operator()(const consumer_type<Handlers> &consumer) {
-		return _implementation(consumer);
+		return _implementation ? _implementation(consumer) : lifetime();
 	}
 
 private:
@@ -166,6 +173,7 @@ public:
 			std::is_constructible_v<Generator, OtherGenerator&&>>>
 	producer_base(OtherGenerator &&generator);
 
+	producer_base() = default;
 	producer_base(const producer_base &other) = default;
 	producer_base(producer_base &&other) = default;
 	producer_base &operator=(const producer_base &other) = default;
@@ -382,8 +390,9 @@ class producer<
 		Error>;
 
 public:
-	using parent_type::parent_type;;
+	using parent_type::parent_type;
 
+	producer() = default;
 	producer(const producer &other) = default;
 	producer(producer &&other) = default;
 	producer &operator=(const producer &other) = default;
@@ -429,6 +438,10 @@ public:
 		return *this;
 	}
 
+	explicit operator bool() const {
+		return (this->_generator != nullptr);
+	}
+
 };
 
 template <
diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h
index ed15c93fb..204a53d52 100644
--- a/Telegram/SourceFiles/settings/settings_common.h
+++ b/Telegram/SourceFiles/settings/settings_common.h
@@ -46,7 +46,7 @@ public:
 	using RpWidget::RpWidget;
 
 	virtual rpl::producer<Type> sectionShowOther() {
-		return rpl::never<Type>();
+		return nullptr;
 	}
 	virtual rpl::producer<bool> sectionCanSaveChanges() {
 		return rpl::single(false);
diff --git a/Telegram/SourceFiles/settings/settings_information.cpp b/Telegram/SourceFiles/settings/settings_information.cpp
index ae6bc49f3..7c24d1797 100644
--- a/Telegram/SourceFiles/settings/settings_information.cpp
+++ b/Telegram/SourceFiles/settings/settings_information.cpp
@@ -297,7 +297,7 @@ BioManager SetupBio(
 			container,
 			*style,
 			Ui::InputField::Mode::MultiLine,
-			langFactory(lng_bio_placeholder),
+			tr::lng_bio_placeholder(),
 			*current),
 		st::settingsBioMargins);
 
diff --git a/Telegram/SourceFiles/support/support_autocomplete.cpp b/Telegram/SourceFiles/support/support_autocomplete.cpp
index 20c965fe6..2bd6931de 100644
--- a/Telegram/SourceFiles/support/support_autocomplete.cpp
+++ b/Telegram/SourceFiles/support/support_autocomplete.cpp
@@ -396,7 +396,7 @@ void Autocomplete::setupContent() {
 		object_ptr<Ui::InputField>(
 			this,
 			st::gifsSearchField,
-			[] { return "Search for templates"; }),
+			rpl::single(qsl("Search for templates"))), // #TODO hard_lang
 		st::autocompleteSearchPadding);
 	const auto input = inputWrap->entity();
 	const auto scroll = Ui::CreateChild<Ui::ScrollArea>(
diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp
index 04140b479..49c45165e 100644
--- a/Telegram/SourceFiles/support/support_helper.cpp
+++ b/Telegram/SourceFiles/support/support_helper.cpp
@@ -61,7 +61,7 @@ EditInfoBox::EditInfoBox(
 	this,
 	st::supportInfoField,
 	Ui::InputField::Mode::MultiLine,
-	[] { return QString("Support information"); },
+	rpl::single(qsl("Support information")), // #TODO hard_lang
 	text)
 , _submit(std::move(submit)) {
 	_field->setMaxLength(kMaxSupportInfoLength);
@@ -73,7 +73,7 @@ EditInfoBox::EditInfoBox(
 }
 
 void EditInfoBox::prepare() {
-	setTitle([] { return QString("Edit support information"); });
+	setTitle([] { return QString("Edit support information"); }); // #TODO hard_lang
 
 	const auto save = [=] {
 		const auto done = crl::guard(this, [=](bool success) {
diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp
index c644bf9c2..06ec12ee1 100644
--- a/Telegram/SourceFiles/ui/countryinput.cpp
+++ b/Telegram/SourceFiles/ui/countryinput.cpp
@@ -199,12 +199,12 @@ void CountryInput::setText(const QString &newText) {
 }
 
 CountrySelectBox::CountrySelectBox(QWidget*)
-: _select(this, st::contactsMultiSelect, langFactory(lng_country_ph)) {
+: _select(this, st::contactsMultiSelect, tr::lng_country_ph()) {
 }
 
 CountrySelectBox::CountrySelectBox(QWidget*, const QString &iso, Type type)
 : _type(type)
-, _select(this, st::contactsMultiSelect, langFactory(lng_country_ph)) {
+, _select(this, st::contactsMultiSelect, tr::lng_country_ph()) {
 	lastValidISO = iso;
 }
 
diff --git a/Telegram/SourceFiles/ui/search_field_controller.cpp b/Telegram/SourceFiles/ui/search_field_controller.cpp
index 780731956..5bb2277f4 100644
--- a/Telegram/SourceFiles/ui/search_field_controller.cpp
+++ b/Telegram/SourceFiles/ui/search_field_controller.cpp
@@ -101,7 +101,7 @@ base::unique_qptr<Ui::InputField> SearchFieldController::createField(
 	auto result = base::make_unique_q<Ui::InputField>(
 		parent,
 		st,
-		langFactory(lng_dlg_filter),
+		tr::lng_dlg_filter(),
 		_query.current());
 	auto field = result.get();
 	field->connect(field, &Ui::InputField::changed, [=] {
diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp
index 9ebdf31d8..170303961 100644
--- a/Telegram/SourceFiles/ui/widgets/input_fields.cpp
+++ b/Telegram/SourceFiles/ui/widgets/input_fields.cpp
@@ -886,9 +886,14 @@ const InstantReplaces &InstantReplaces::Default() {
 	return result;
 }
 
-FlatInput::FlatInput(QWidget *parent, const style::FlatInput &st, Fn<QString()> placeholderFactory, const QString &v) : TWidgetHelper<QLineEdit>(v, parent)
+FlatInput::FlatInput(
+	QWidget *parent,
+	const style::FlatInput &st,
+	rpl::producer<QString> placeholder,
+	const QString &v)
+: RpWidgetWrap<QLineEdit>(v, parent)
 , _oldtext(v)
-, _placeholderFactory(std::move(placeholderFactory))
+, _placeholderFull(std::move(placeholder))
 , _placeholderVisible(!v.length())
 , _st(st)
 , _textMrg(_st.textMrg) {
@@ -898,8 +903,10 @@ FlatInput::FlatInput(QWidget *parent, const style::FlatInput &st, Fn<QString()>
 	setFont(_st.font->f);
 	setAlignment(_st.align);
 
-	subscribe(Lang::Current().updated(), [this] { refreshPlaceholder(); });
-	refreshPlaceholder();
+	_placeholderFull.value(
+	) | rpl::start_with_next([=](const QString &text) {
+		refreshPlaceholder(text);
+	}, lifetime());
 
 	subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) {
 		if (update.paletteChanged()) {
@@ -935,15 +942,17 @@ void FlatInput::onTouchTimer() {
 	_touchRightButton = true;
 }
 
-bool FlatInput::event(QEvent *e) {
-	if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) {
-		QTouchEvent *ev = static_cast<QTouchEvent*>(e);
+bool FlatInput::eventHook(QEvent *e) {
+	if (e->type() == QEvent::TouchBegin
+		|| e->type() == QEvent::TouchUpdate
+		|| e->type() == QEvent::TouchEnd
+		|| e->type() == QEvent::TouchCancel) {
+		const auto ev = static_cast<QTouchEvent*>(e);
 		if (ev->device()->type() == QTouchDevice::TouchScreen) {
 			touchEvent(ev);
-			return QLineEdit::event(e);
 		}
 	}
-	return QLineEdit::event(e);
+	return Parent::eventHook(e);
 }
 
 void FlatInput::touchEvent(QTouchEvent *e) {
@@ -990,7 +999,7 @@ void FlatInput::touchEvent(QTouchEvent *e) {
 
 void FlatInput::setTextMrg(const QMargins &textMrg) {
 	_textMrg = textMrg;
-	refreshPlaceholder();
+	refreshPlaceholder(_placeholderFull.current());
 	update();
 }
 
@@ -1068,22 +1077,20 @@ void FlatInput::focusOutEvent(QFocusEvent *e) {
 }
 
 void FlatInput::resizeEvent(QResizeEvent *e) {
-	refreshPlaceholder();
+	refreshPlaceholder(_placeholderFull.current());
 	return QLineEdit::resizeEvent(e);
 }
 
-void FlatInput::setPlaceholder(Fn<QString()> placeholderFactory) {
-	_placeholderFactory = std::move(placeholderFactory);
-	refreshPlaceholder();
+void FlatInput::setPlaceholder(rpl::producer<QString> placeholder) {
+	_placeholderFull = std::move(placeholder);
 }
 
-void FlatInput::refreshPlaceholder() {
-	auto availw = width() - _textMrg.left() - _textMrg.right() - _st.phPos.x() - 1;
-	auto placeholderText = _placeholderFactory ? _placeholderFactory() : QString();
-	if (_st.font->width(placeholderText) > availw) {
-		_placeholder = _st.font->elided(placeholderText, availw);
+void FlatInput::refreshPlaceholder(const QString &text) {
+	const auto availw = width() - _textMrg.left() - _textMrg.right() - _st.phPos.x() - 1;
+	if (_st.font->width(text) > availw) {
+		_placeholder = _st.font->elided(text, availw);
 	} else {
-		_placeholder = placeholderText;
+		_placeholder = text;
 	}
 	update();
 }
@@ -1190,13 +1197,13 @@ void FlatInput::onTextChange(const QString &text) {
 InputField::InputField(
 	QWidget *parent,
 	const style::InputField &st,
-	Fn<QString()> placeholderFactory,
+	rpl::producer<QString> placeholder,
 	const QString &value)
 : InputField(
 	parent,
 	st,
 	Mode::SingleLine,
-	std::move(placeholderFactory),
+	std::move(placeholder),
 	{ value, {} }) {
 }
 
@@ -1204,13 +1211,13 @@ InputField::InputField(
 	QWidget *parent,
 	const style::InputField &st,
 	Mode mode,
-	Fn<QString()> placeholderFactory,
+	rpl::producer<QString> placeholder,
 	const QString &value)
 : InputField(
 	parent,
 	st,
 	mode,
-	std::move(placeholderFactory),
+	std::move(placeholder),
 	{ value, {} }) {
 }
 
@@ -1218,7 +1225,7 @@ InputField::InputField(
 	QWidget *parent,
 	const style::InputField &st,
 	Mode mode,
-	Fn<QString()> placeholderFactory,
+	rpl::producer<QString> placeholder,
 	const TextWithTags &value)
 : RpWidget(parent)
 , _st(st)
@@ -1227,7 +1234,7 @@ InputField::InputField(
 , _maxHeight(st.heightMax)
 , _inner(std::make_unique<Inner>(this))
 , _lastTextWithTags(value)
-, _placeholderFactory(std::move(placeholderFactory)) {
+, _placeholderFull(std::move(placeholder)) {
 	_inner->setDocument(Ui::CreateChild<InputDocument>(_inner.get(), _st));
 
 	_inner->setAcceptRichText(false);
@@ -1243,8 +1250,10 @@ InputField::InputField(
 		_inner->setWordWrapMode(QTextOption::NoWrap);
 	}
 
-	subscribe(Lang::Current().updated(), [=] { refreshPlaceholder(); });
-	refreshPlaceholder();
+	_placeholderFull.value(
+	) | rpl::start_with_next([=](const QString &text) {
+		refreshPlaceholder(text);
+	}, lifetime());
 
 	subscribe(Window::Theme::Background(), [=](
 			const Window::Theme::BackgroundUpdate &update) {
@@ -1666,7 +1675,7 @@ void InputField::paintEvent(QPaintEvent *e) {
 			p.restore();
 		}
 	}
-	TWidget::paintEvent(e);
+	RpWidget::paintEvent(e);
 }
 
 int InputField::placeholderSkipWidth() const {
@@ -3430,40 +3439,38 @@ void InputField::insertFromMimeDataInner(const QMimeData *source) {
 }
 
 void InputField::resizeEvent(QResizeEvent *e) {
-	refreshPlaceholder();
+	refreshPlaceholder(_placeholderFull.current());
 	_inner->setGeometry(rect().marginsRemoved(_st.textMargins));
 	_borderAnimationStart = width() / 2;
-	TWidget::resizeEvent(e);
+	RpWidget::resizeEvent(e);
 	checkContentHeight();
 }
 
-void InputField::refreshPlaceholder() {
-	auto placeholderText = _placeholderFactory ? _placeholderFactory() : QString();
-	auto availableWidth = width() - _st.textMargins.left() - _st.textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
+void InputField::refreshPlaceholder(const QString &text) {
+	const auto availableWidth = width() - _st.textMargins.left() - _st.textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
 	if (_st.placeholderScale > 0.) {
 		auto placeholderFont = _st.placeholderFont->f;
 		placeholderFont.setStyleStrategy(QFont::PreferMatch);
-		auto metrics = QFontMetrics(placeholderFont);
-		_placeholder = metrics.elidedText(placeholderText, Qt::ElideRight, availableWidth);
+		const auto metrics = QFontMetrics(placeholderFont);
+		_placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth);
 		_placeholderPath = QPainterPath();
 		if (!_placeholder.isEmpty()) {
 			_placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder);
 		}
 	} else {
-		_placeholder = _st.placeholderFont->elided(placeholderText, availableWidth);
+		_placeholder = _st.placeholderFont->elided(text, availableWidth);
 	}
 	update();
 }
 
 void InputField::setPlaceholder(
-		Fn<QString()> placeholderFactory,
+		rpl::producer<QString> placeholder,
 		int afterSymbols) {
-	_placeholderFactory = std::move(placeholderFactory);
+	_placeholderFull = std::move(placeholder);
 	if (_placeholderAfterSymbols != afterSymbols) {
 		_placeholderAfterSymbols = afterSymbols;
 		startPlaceholderAnimation();
 	}
-	refreshPlaceholder();
 }
 
 void InputField::setEditLinkCallback(
@@ -3495,19 +3502,21 @@ InputField::~InputField() = default;
 MaskedInputField::MaskedInputField(
 	QWidget *parent,
 	const style::InputField &st,
-	Fn<QString()> placeholderFactory,
+	rpl::producer<QString> placeholder,
 	const QString &val)
 : Parent(val, parent)
 , _st(st)
 , _oldtext(val)
-, _placeholderFactory(std::move(placeholderFactory)) {
+, _placeholderFull(std::move(placeholder)) {
 	resize(_st.width, _st.heightMin);
 
 	setFont(_st.font);
 	setAlignment(_st.textAlign);
 
-	subscribe(Lang::Current().updated(), [this] { refreshPlaceholder(); });
-	refreshPlaceholder();
+	_placeholderFull.value(
+	) | rpl::start_with_next([=](const QString &text) {
+		refreshPlaceholder(text);
+	}, lifetime());
 
 	subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) {
 		if (update.paletteChanged()) {
@@ -3572,7 +3581,7 @@ int MaskedInputField::borderAnimationStart() const {
 
 void MaskedInputField::setTextMargins(const QMargins &mrg) {
 	_textMargins = mrg;
-	refreshPlaceholder();
+	refreshPlaceholder(_placeholderFull.current());
 }
 
 void MaskedInputField::onTouchTimer() {
@@ -3755,36 +3764,34 @@ void MaskedInputField::setFocused(bool focused) {
 }
 
 void MaskedInputField::resizeEvent(QResizeEvent *e) {
-	refreshPlaceholder();
+	refreshPlaceholder(_placeholderFull.current());
 	_borderAnimationStart = width() / 2;
 	QLineEdit::resizeEvent(e);
 }
 
-void MaskedInputField::refreshPlaceholder() {
-	auto placeholderText = _placeholderFactory ? _placeholderFactory() : QString();
-	auto availableWidth = width() - _textMargins.left() - _textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
+void MaskedInputField::refreshPlaceholder(const QString &text) {
+	const auto availableWidth = width() - _textMargins.left() - _textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
 	if (_st.placeholderScale > 0.) {
 		auto placeholderFont = _st.placeholderFont->f;
 		placeholderFont.setStyleStrategy(QFont::PreferMatch);
-		auto metrics = QFontMetrics(placeholderFont);
-		_placeholder = metrics.elidedText(placeholderText, Qt::ElideRight, availableWidth);
+		const auto metrics = QFontMetrics(placeholderFont);
+		_placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth);
 		_placeholderPath = QPainterPath();
 		if (!_placeholder.isEmpty()) {
 			_placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder);
 		}
 	} else {
-		_placeholder = _st.placeholderFont->elided(placeholderText, availableWidth);
+		_placeholder = _st.placeholderFont->elided(text, availableWidth);
 	}
 	update();
 }
 
-void MaskedInputField::setPlaceholder(Fn<QString()> placeholderFactory) {
-	_placeholderFactory = std::move(placeholderFactory);
-	refreshPlaceholder();
+void MaskedInputField::setPlaceholder(rpl::producer<QString> placeholder) {
+	_placeholderFull = std::move(placeholder);
 }
 
 void MaskedInputField::contextMenuEvent(QContextMenuEvent *e) {
-	if (auto menu = createStandardContextMenu()) {
+	if (const auto menu = createStandardContextMenu()) {
 		(new Ui::PopupMenu(this, menu))->popup(e->globalPos());
 	}
 }
@@ -4105,11 +4112,21 @@ void PhonePartInput::onChooseCode(const QString &code) {
 	startPlaceholderAnimation();
 }
 
-PasswordInput::PasswordInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val) : MaskedInputField(parent, st, std::move(placeholderFactory), val) {
+PasswordInput::PasswordInput(
+	QWidget *parent,
+	const style::InputField &st,
+	rpl::producer<QString> placeholder,
+	const QString &val)
+: MaskedInputField(parent, st, std::move(placeholder), val) {
 	setEchoMode(QLineEdit::Password);
 }
 
-PortInput::PortInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val) : MaskedInputField(parent, st, std::move(placeholderFactory), val) {
+PortInput::PortInput(
+	QWidget *parent,
+	const style::InputField &st,
+	rpl::producer<QString> placeholder,
+	const QString &val)
+: MaskedInputField(parent, st, std::move(placeholder), val) {
 	if (!val.toInt() || val.toInt() > 65535) {
 		setText(QString());
 	}
@@ -4140,7 +4157,12 @@ void PortInput::correctValue(
 	setCorrectedText(now, nowCursor, newText, newPos);
 }
 
-HexInput::HexInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val) : MaskedInputField(parent, st, std::move(placeholderFactory), val) {
+HexInput::HexInput(
+	QWidget *parent,
+	const style::InputField &st,
+	rpl::producer<QString> placeholder,
+	const QString &val)
+: MaskedInputField(parent, st, std::move(placeholder), val) {
 	if (!QRegularExpression("^[a-fA-F0-9]+$").match(val).hasMatch()) {
 		setText(QString());
 	}
@@ -4167,8 +4189,15 @@ void HexInput::correctValue(
 	setCorrectedText(now, nowCursor, newText, newPos);
 }
 
-UsernameInput::UsernameInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val, bool isLink) : MaskedInputField(parent, st, std::move(placeholderFactory), val) {
-	setLinkPlaceholder(isLink ? Core::App().createInternalLink(QString()) : QString());
+UsernameInput::UsernameInput(
+	QWidget *parent,
+	const style::InputField &st,
+	rpl::producer<QString> placeholder,
+	const QString &val,
+	bool isLink)
+: MaskedInputField(parent, st, std::move(placeholder), val) {
+	setLinkPlaceholder(
+		isLink ? Core::App().createInternalLink(QString()) : QString());
 }
 
 void UsernameInput::setLinkPlaceholder(const QString &placeholder) {
@@ -4214,7 +4243,12 @@ void UsernameInput::correctValue(
 	setCorrectedText(now, nowCursor, now.mid(from, len), newPos);
 }
 
-PhoneInput::PhoneInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val) : MaskedInputField(parent, st, std::move(placeholderFactory), val) {
+PhoneInput::PhoneInput(
+	QWidget *parent,
+	const style::InputField &st,
+	rpl::producer<QString> placeholder,
+	const QString &val)
+: MaskedInputField(parent, st, std::move(placeholder), val) {
 	QString phone(val);
 	if (phone.isEmpty()) {
 		clearText();
diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.h b/Telegram/SourceFiles/ui/widgets/input_fields.h
index ad397e070..622d17db7 100644
--- a/Telegram/SourceFiles/ui/widgets/input_fields.h
+++ b/Telegram/SourceFiles/ui/widgets/input_fields.h
@@ -41,19 +41,20 @@ enum class InputSubmitSettings {
 	None,
 };
 
-class FlatInput : public TWidgetHelper<QLineEdit>, private base::Subscriber {
+class FlatInput : public Ui::RpWidgetWrap<QLineEdit>, private base::Subscriber {
 	// The Q_OBJECT meta info is used for qobject_cast!
 	Q_OBJECT
 
+	using Parent = RpWidgetWrap<QLineEdit>;
 public:
 	FlatInput(
 		QWidget *parent,
 		const style::FlatInput &st,
-		Fn<QString()> placeholderFactory = nullptr,
+		rpl::producer<QString> placeholder = nullptr,
 		const QString &val = QString());
 
 	void updatePlaceholder();
-	void setPlaceholder(Fn<QString()> placeholderFactory);
+	void setPlaceholder(rpl::producer<QString> placeholder);
 	QRect placeholderRect() const;
 
 	void finishAnimations();
@@ -83,7 +84,7 @@ signals:
 	void blurred();
 
 protected:
-	bool event(QEvent *e) override;
+	bool eventHook(QEvent *e) override;
 	void touchEvent(QTouchEvent *e);
 	void paintEvent(QPaintEvent *e) override;
 	void focusInEvent(QFocusEvent *e) override;
@@ -103,11 +104,11 @@ protected:
 
 private:
 	void updatePalette();
-	void refreshPlaceholder();
+	void refreshPlaceholder(const QString &text);
 
 	QString _oldtext;
+	rpl::variable<QString> _placeholderFull;
 	QString _placeholder;
-	Fn<QString()> _placeholderFactory;
 
 	bool _customUpDown = false;
 
@@ -156,19 +157,19 @@ public:
 	InputField(
 		QWidget *parent,
 		const style::InputField &st,
-		Fn<QString()> placeholderFactory,
+		rpl::producer<QString> placeholder,
 		const QString &value = QString());
 	InputField(
 		QWidget *parent,
 		const style::InputField &st,
 		Mode mode,
-		Fn<QString()> placeholderFactory,
+		rpl::producer<QString> placeholder,
 		const QString &value);
 	InputField(
 		QWidget *parent,
 		const style::InputField &st,
 		Mode mode = Mode::SingleLine,
-		Fn<QString()> placeholderFactory = nullptr,
+		rpl::producer<QString> placeholder = nullptr,
 		const TextWithTags &value = TextWithTags());
 
 	void showError();
@@ -240,7 +241,7 @@ public:
 		return _lastTextWithTags.text;
 	}
 	void setPlaceholder(
-		Fn<QString()> placeholderFactory,
+		rpl::producer<QString> placeholder,
 		int afterSymbols = 0);
 	void setPlaceholderHidden(bool forcePlaceholderHidden);
 	void setDisplayFocused(bool focused);
@@ -342,7 +343,7 @@ private:
 	void handleTouchEvent(QTouchEvent *e);
 
 	void updatePalette();
-	void refreshPlaceholder();
+	void refreshPlaceholder(const QString &text);
 	int placeholderSkipWidth() const;
 
 	bool heightAutoupdated();
@@ -465,8 +466,8 @@ private:
 	bool _customUpDown = false;
 	bool _customTab = false;
 
+	rpl::variable<QString> _placeholderFull;
 	QString _placeholder;
-	Fn<QString()> _placeholderFactory;
 	int _placeholderAfterSymbols = 0;
 	Ui::Animations::Simple _a_placeholderShifted;
 	bool _placeholderShifted = false;
@@ -510,7 +511,11 @@ class MaskedInputField
 
 	using Parent = RpWidgetWrap<QLineEdit>;
 public:
-	MaskedInputField(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory = Fn<QString()>(), const QString &val = QString());
+	MaskedInputField(
+		QWidget *parent,
+		const style::InputField &st,
+		rpl::producer<QString> placeholder = nullptr,
+		const QString &val = QString());
 
 	void showError();
 
@@ -525,7 +530,7 @@ public:
 	const QString &getLastText() const {
 		return _oldtext;
 	}
-	void setPlaceholder(Fn<QString()> placeholderFactory);
+	void setPlaceholder(rpl::producer<QString> placeholder);
 	void setPlaceholderHidden(bool forcePlaceholderHidden);
 	void setDisplayFocused(bool focused);
 	void finishAnimating();
@@ -602,7 +607,7 @@ protected:
 
 private:
 	void updatePalette();
-	void refreshPlaceholder();
+	void refreshPlaceholder(const QString &text);
 	void setErrorShown(bool error);
 
 	void setFocused(bool focused);
@@ -619,8 +624,8 @@ private:
 
 	bool _customUpDown = false;
 
+	rpl::variable<QString> _placeholderFull;
 	QString _placeholder;
-	Fn<QString()> _placeholderFactory;
 	Ui::Animations::Simple _a_placeholderShifted;
 	bool _placeholderShifted = false;
 	QPainterPath _placeholderPath;
@@ -702,13 +707,13 @@ private:
 
 class PasswordInput : public MaskedInputField {
 public:
-	PasswordInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory = Fn<QString()>(), const QString &val = QString());
+	PasswordInput(QWidget *parent, const style::InputField &st, rpl::producer<QString> placeholder = nullptr, const QString &val = QString());
 
 };
 
 class PortInput : public MaskedInputField {
 public:
-	PortInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val);
+	PortInput(QWidget *parent, const style::InputField &st, rpl::producer<QString> placeholder, const QString &val);
 
 protected:
 	void correctValue(
@@ -721,7 +726,7 @@ protected:
 
 class HexInput : public MaskedInputField {
 public:
-	HexInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val);
+	HexInput(QWidget *parent, const style::InputField &st, rpl::producer<QString> placeholder, const QString &val);
 
 protected:
 	void correctValue(
@@ -734,7 +739,12 @@ protected:
 
 class UsernameInput : public MaskedInputField {
 public:
-	UsernameInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val, bool isLink);
+	UsernameInput(
+		QWidget *parent,
+		const style::InputField &st,
+		rpl::producer<QString> placeholder,
+		const QString &val,
+		bool isLink);
 
 	void setLinkPlaceholder(const QString &placeholder);
 
@@ -753,7 +763,11 @@ private:
 
 class PhoneInput : public MaskedInputField {
 public:
-	PhoneInput(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory, const QString &val);
+	PhoneInput(
+		QWidget *parent,
+		const style::InputField &st,
+		rpl::producer<QString> placeholder,
+		const QString &val);
 
 	void clearText();
 
diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.cpp b/Telegram/SourceFiles/ui/widgets/multi_select.cpp
index 2950bb99a..9e3e5dd3d 100644
--- a/Telegram/SourceFiles/ui/widgets/multi_select.cpp
+++ b/Telegram/SourceFiles/ui/widgets/multi_select.cpp
@@ -234,13 +234,18 @@ void MultiSelect::Item::setOver(bool over) {
 MultiSelect::MultiSelect(
 	QWidget *parent,
 	const style::MultiSelect &st,
-	Fn<QString()> placeholderFactory)
+	rpl::producer<QString> placeholder)
 : RpWidget(parent)
 , _st(st)
 , _scroll(this, _st.scroll) {
-	_inner = _scroll->setOwnedWidget(object_ptr<Inner>(this, st, std::move(placeholderFactory), [this](int activeTop, int activeBottom) {
+	const auto scrollCallback = [=](int activeTop, int activeBottom) {
 		scrollTo(activeTop, activeBottom);
-	}));
+	};
+	_inner = _scroll->setOwnedWidget(object_ptr<Inner>(
+		this,
+		st,
+		std::move(placeholder),
+		scrollCallback));
 	_scroll->installEventFilter(this);
 	_inner->setResizedCallback([this](int innerHeightDelta) {
 		auto newHeight = resizeGetHeight(width());
@@ -357,7 +362,12 @@ int MultiSelect::resizeGetHeight(int newWidth) {
 	return newHeight;
 }
 
-MultiSelect::Inner::Inner(QWidget *parent, const style::MultiSelect &st, Fn<QString()> placeholder, ScrollCallback callback) : TWidget(parent)
+MultiSelect::Inner::Inner(
+	QWidget *parent,
+	const style::MultiSelect &st,
+	rpl::producer<QString> placeholder,
+	ScrollCallback callback)
+: TWidget(parent)
 , _st(st)
 , _scrollCallback(std::move(callback))
 , _field(this, _st.field, std::move(placeholder))
diff --git a/Telegram/SourceFiles/ui/widgets/multi_select.h b/Telegram/SourceFiles/ui/widgets/multi_select.h
index d403f4919..a907553b6 100644
--- a/Telegram/SourceFiles/ui/widgets/multi_select.h
+++ b/Telegram/SourceFiles/ui/widgets/multi_select.h
@@ -19,7 +19,10 @@ class ScrollArea;
 
 class MultiSelect : public RpWidget {
 public:
-	MultiSelect(QWidget *parent, const style::MultiSelect &st, Fn<QString()> placeholderFactory = Fn<QString()>());
+	MultiSelect(
+		QWidget *parent,
+		const style::MultiSelect &st,
+		rpl::producer<QString> placeholder = nullptr);
 
 	QString getQuery() const;
 	void setInnerFocus();
@@ -72,7 +75,11 @@ private:
 class MultiSelect::Inner : public TWidget {
 public:
 	using ScrollCallback = Fn<void(int activeTop, int activeBottom)>;
-	Inner(QWidget *parent, const style::MultiSelect &st, Fn<QString()> placeholderFactory, ScrollCallback callback);
+	Inner(
+		QWidget *parent,
+		const style::MultiSelect &st,
+		rpl::producer<QString> placeholder,
+		ScrollCallback callback);
 
 	QString getQuery() const;
 	bool setInnerFocus();
diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp
index 368d1d930..800fd52eb 100644
--- a/Telegram/SourceFiles/window/notifications_manager_default.cpp
+++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp
@@ -792,7 +792,7 @@ void Notification::showReplyField() {
 		this,
 		st::notifyReplyArea,
 		Ui::InputField::Mode::MultiLine,
-		langFactory(lng_message_ph));
+		tr::lng_message_ph());
 	_replyArea->resize(width() - st::notifySendReply.width - 2 * st::notifyBorderWidth, st::notifySendReply.height);
 	_replyArea->moveToLeft(st::notifyBorderWidth, st::notifyMinHeight);
 	_replyArea->show();
diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp
index 68da92db6..dba14f5bf 100644
--- a/Telegram/SourceFiles/window/themes/window_theme_editor.cpp
+++ b/Telegram/SourceFiles/window/themes/window_theme_editor.cpp
@@ -692,7 +692,7 @@ void ThemeExportBox::exportTheme() {
 Editor::Editor(QWidget*, const QString &path)
 : _scroll(this, st::themesScroll)
 , _close(this, st::contactsMultiSelect.fieldCancel)
-, _select(this, st::contactsMultiSelect, langFactory(lng_country_ph))
+, _select(this, st::contactsMultiSelect, tr::lng_country_ph())
 , _leftShadow(this)
 , _topShadow(this)
 , _export(this, lang(lng_theme_editor_export_button).toUpper(), st::dialogsUpdateButton) {
diff --git a/Telegram/SourceFiles/window/window_lock_widgets.cpp b/Telegram/SourceFiles/window/window_lock_widgets.cpp
index 144faf67e..7b01bb379 100644
--- a/Telegram/SourceFiles/window/window_lock_widgets.cpp
+++ b/Telegram/SourceFiles/window/window_lock_widgets.cpp
@@ -96,7 +96,7 @@ void LockWidget::paintContent(Painter &p) {
 
 PasscodeLockWidget::PasscodeLockWidget(QWidget *parent)
 : LockWidget(parent)
-, _passcode(this, st::passcodeInput, langFactory(lng_passcode_ph))
+, _passcode(this, st::passcodeInput, tr::lng_passcode_ph())
 , _submit(this, langFactory(lng_passcode_submit), st::passcodeSubmit)
 , _logout(this, lang(lng_passcode_logout)) {
 	connect(_passcode, &Ui::MaskedInputField::changed, [=] { changed(); });