From 7f96a185cf9de44d739ebecc999bef469b9357e2 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sun, 16 Jun 2019 15:08:15 +0200
Subject: [PATCH] Add rpl interface for Lang in tr:: namespace.

---
 .../SourceFiles/codegen/lang/generator.cpp    | 172 +++++++++++++++--
 Telegram/SourceFiles/codegen/lang/generator.h |  12 +-
 Telegram/SourceFiles/lang/lang_instance.cpp   |   4 +
 Telegram/SourceFiles/lang/lang_instance.h     |   1 +
 Telegram/SourceFiles/lang/lang_tag.cpp        |  16 +-
 Telegram/SourceFiles/lang/lang_tag.h          |   2 +-
 Telegram/SourceFiles/lang/lang_values.cpp     |   9 +
 Telegram/SourceFiles/lang/lang_values.h       | 181 ++++++++++++++++++
 Telegram/SourceFiles/ui/text/text_utilities.h |   7 +
 Telegram/gyp/telegram_sources.txt             |   2 +
 10 files changed, 379 insertions(+), 27 deletions(-)
 create mode 100644 Telegram/SourceFiles/lang/lang_values.cpp
 create mode 100644 Telegram/SourceFiles/lang/lang_values.h

diff --git a/Telegram/SourceFiles/codegen/lang/generator.cpp b/Telegram/SourceFiles/codegen/lang/generator.cpp
index 7a144a12e..de9fb85b7 100644
--- a/Telegram/SourceFiles/codegen/lang/generator.cpp
+++ b/Telegram/SourceFiles/codegen/lang/generator.cpp
@@ -102,12 +102,30 @@ Generator::Generator(const LangPack &langpack, const QString &destBasePath, cons
 
 bool Generator::writeHeader() {
 	header_ = std::make_unique<common::CppFile>(basePath_ + ".h", project_);
-	header_->include("lang/lang_tag.h").newline().pushNamespace("Lang").stream() << "\
+	header_->include("lang/lang_tag.h").include("lang/lang_values.h").newline();
+
+	writeHeaderForwardDeclarations();
+	writeHeaderTagTypes();
+	writeHeaderKeyType();
+	writeHeaderTaggedMethods();
+	writeHeaderInterface();
+	writeHeaderReactiveInterface();
+
+	return header_->finalize();
+}
+
+void Generator::writeHeaderForwardDeclarations() {
+	header_->pushNamespace("Lang").stream() << "\
 \n\
-constexpr auto kTagsCount = " << langpack_.tags.size() << ";\n\
+inline constexpr auto kTagsCount = " << langpack_.tags.size() << ";\n\
 \n";
 
-	header_->popNamespace().newline();
+	header_->popNamespace().newline().stream() << "\
+enum LangKey : int;\n\
+QString lang(LangKey key);\n\n";
+}
+
+void Generator::writeHeaderTagTypes() {
 	auto index = 0;
 	for (auto &tag : langpack_.tags) {
 		if (tag.tag == kPluralTags[0]) {
@@ -123,8 +141,11 @@ constexpr auto kTagsCount = " << langpack_.tags.size() << ";\n\
 			header_->stream() << "enum lngtag_" << tag.tag << " : int { lt_" << tag.tag << " = " << index++ << " };\n";
 		}
 	}
+	header_->newline();
+}
+
+void Generator::writeHeaderKeyType() {
 	header_->stream() << "\
-\n\
 enum LangKey : int {\n";
 	for (auto &entry : langpack_.entries) {
 		header_->stream() << "\t" << getFullKey(entry) << ",\n";
@@ -133,10 +154,10 @@ enum LangKey : int {\n";
 \n\
 	kLangKeysCount,\n\
 };\n\
-\n\
-QString lang(LangKey key);\n\
 \n";
+}
 
+void Generator::writeHeaderTaggedMethods() {
 	for (auto &entry : langpack_.entries) {
 		auto isPlural = !entry.keyBase.isEmpty();
 		auto &key = entry.key;
@@ -144,7 +165,6 @@ QString lang(LangKey key);\n\
 		auto params = QStringList();
 		auto applyTags = QStringList();
 		auto plural = QString();
-		auto nonPluralTagFound = false;
 		for (auto &tagData : entry.tags) {
 			auto &tag = tagData.tag;
 			auto isPluralTag = isPlural && (tag == kPluralTags[0]);
@@ -154,26 +174,27 @@ QString lang(LangKey key);\n\
 				plural = "\tauto plural = Lang::Plural(" + key + ", " + tag + "__val, type);\n";
 				applyTags.push_back("\tresult = Lang::ReplaceTag<ResultString>::Call(std::move(result), lt_" + tag + ", Lang::StartReplacements<ResultString>::Call(std::move(plural.replacement)));\n");
 			} else {
-				nonPluralTagFound = true;
 				applyTags.push_back("\tresult = Lang::ReplaceTag<ResultString>::Call(std::move(result), lt_" + tag + ", " + tag + "__val);\n");
 			}
 		}
 		if (!entry.tags.empty() && (!isPlural || key == ComputePluralKey(entry.keyBase, 0))) {
-			auto initialString = isPlural ? ("std::move(plural.string)") : ("lang(" + getFullKey(entry) + ")");
+			auto initialString = isPlural ? ("lang(LangKey(" + key + " + plural.keyShift))") : ("lang(" + getFullKey(entry) + ")");
 			header_->stream() << "\
 template <typename ResultString>\n\
-inline ResultString " << (isPlural ? entry.keyBase : key) << "__generic(" << genericParams.join(QString(", ")) << ") {\n\
+ResultString " << (isPlural ? entry.keyBase : key) << "__generic(" << genericParams.join(QString(", ")) << ") {\n\
 " << plural << "\
 	auto result = Lang::StartReplacements<ResultString>::Call(" << initialString << ");\n\
 " << applyTags.join(QString()) << "\
 	return result;\n\
 }\n\
-constexpr auto " << (isPlural ? entry.keyBase : key) << " = &" << (isPlural ? entry.keyBase : key) << "__generic<QString>;\n\
-constexpr auto " << (isPlural ? entry.keyBase : key) << "__rich = &" << (isPlural ? entry.keyBase : key) << "__generic<TextWithEntities>;\n\
+inline constexpr auto " << (isPlural ? entry.keyBase : key) << " = &" << (isPlural ? entry.keyBase : key) << "__generic<QString>;\n\
+inline constexpr auto " << (isPlural ? entry.keyBase : key) << "__rich = &" << (isPlural ? entry.keyBase : key) << "__generic<TextWithEntities>;\n\
 \n";
 		}
 	}
+}
 
+void Generator::writeHeaderInterface() {
 	header_->pushNamespace("Lang").stream() << "\
 \n\
 const char *GetKeyName(LangKey key);\n\
@@ -182,8 +203,127 @@ LangKey GetKeyIndex(QLatin1String key);\n\
 bool IsTagReplaced(LangKey key, ushort tag);\n\
 QString GetOriginalValue(LangKey key);\n\
 \n";
+	writeHeaderTagValueLookup();
+	header_->popNamespace().newline();
+}
 
-	return header_->finalize();
+void Generator::writeHeaderTagValueLookup() {
+	header_->pushNamespace("details").stream() << "\
+\n\
+template <typename Tag> struct TagValue;\n\
+\n";
+
+	for (auto &tag : langpack_.tags) {
+		if (tag.tag != kPluralTags[0]) {
+			header_->stream() << "template <> struct TagValue<lngtag_" << tag.tag << "> : std::integral_constant<lngtag_" << tag.tag <<", lt_" << tag.tag << "> {};\n";
+		}
+	}
+
+	header_->popNamespace();
+}
+
+void Generator::writeHeaderReactiveInterface() {
+	header_->pushNamespace("tr");
+
+	writeHeaderProducersInterface();
+	writeHeaderProducersInstances();
+
+	header_->popNamespace().newline();
+}
+
+void Generator::writeHeaderProducersInterface() {
+	header_->pushNamespace("details").stream() << "\
+\n\
+struct Identity {\n\
+	QString operator()(const QString &value) const {\n\
+		return value;\n\
+	}\n\
+};\n\
+\n";
+
+	header_->popNamespace().newline();
+	header_->stream() << "\
+\n\
+struct now_t {\n\
+};\n\
+\n\
+inline constexpr now_t now{};\n\
+\n\
+template <typename ...Tags>\n\
+struct Producer;\n\
+\n";
+	std::set<QStringList> producersDeclared;
+	for (auto &entry : langpack_.entries) {
+		const auto isPlural = !entry.keyBase.isEmpty();
+		const auto &key = entry.key;
+		auto tags = QStringList();
+		auto producerArgs = QStringList();
+		auto currentArgs = QStringList();
+		auto values = QStringList();
+		values.push_back("base");
+		values.push_back("std::move(p)");
+		for (auto &tagData : entry.tags) {
+			const auto &tag = tagData.tag;
+			const auto isPluralTag = isPlural && (tag == kPluralTags[0]);
+			tags.push_back("lngtag_" + tag);
+			const auto type1 = "lngtag_" + tag;
+			const auto arg1 = type1 + (isPluralTag ? " type" : "");
+			const auto producerType2 = (isPluralTag ? "rpl::producer<float64> " : "rpl::producer<T> ");
+			const auto producerArg2 = producerType2 + tag + "__val";
+			const auto currentType2 = (isPluralTag ? "float64 " : "const T &");
+			const auto currentArg2 = currentType2 + tag + "__val";
+			producerArgs.push_back(arg1 + ", " + producerArg2);
+			currentArgs.push_back(arg1 + ", " + currentArg2);
+			if (isPluralTag) {
+				values.push_back("type");
+			}
+			values.push_back(tag + "__val");
+		}
+		producerArgs.push_back("P p = P()");
+		currentArgs.push_back("P p = P()");
+		if (!producersDeclared.emplace(tags).second) {
+			continue;
+		}
+		header_->stream() << "\
+template <>\n\
+struct Producer<" << tags.join(", ") << "> {\n\
+	template <\n\
+		typename P = details::Identity,\n\
+		typename T = decltype(std::declval<P>()(QString()))>\n\
+	rpl::producer<T> operator()(" << producerArgs.join(", ") << ") const {\n\
+		return ::Lang::details::template Producer<" << tags.join(", ") << ">::template Combine(" << values.join(", ") << ");\n\
+	}\n\
+\n\
+	template <\n\
+		typename P = details::Identity,\n\
+		typename T = decltype(std::declval<P>()(QString()))>\n\
+	T operator()(now_t, " << currentArgs.join(", ") << ") const {\n\
+		return ::Lang::details::template Producer<" << tags.join(", ") << ">::template Current(" << values.join(", ") << ");\n\
+	}\n\
+\n\
+	LangKey base;\n\
+};\n\
+\n";
+	}
+}
+
+void Generator::writeHeaderProducersInstances() {
+	auto index = 0;
+	for (auto &entry : langpack_.entries) {
+		const auto isPlural = !entry.keyBase.isEmpty();
+		const auto &key = entry.key;
+		auto tags = QStringList();
+		for (auto &tagData : entry.tags) {
+			const auto &tag = tagData.tag;
+			tags.push_back("lngtag_" + tag);
+		}
+		if (!isPlural || key == ComputePluralKey(entry.keyBase, 0)) {
+			header_->stream() << "\
+inline constexpr Producer<" << tags.join(", ") << "> " << (isPlural ? entry.keyBase : key) << "{ LangKey(" << index << ") };\n";
+		}
+		++index;
+	}
+	header_->newline();
 }
 
 bool Generator::writeSource() {
@@ -250,7 +390,7 @@ ushort GetTagIndex(QLatin1String tag) {\n\
 	auto size = tag.size();\n\
 	auto data = tag.data();\n";
 
-	auto tagsSet = std::set<QString, std::greater<QString>>();
+	auto tagsSet = std::set<QString, std::greater<>>();
 	for (auto &tag : langpack_.tags) {
 		tagsSet.insert(tag.tag);
 	}
@@ -267,7 +407,7 @@ LangKey GetKeyIndex(QLatin1String key) {\n\
 	auto data = key.data();\n";
 
 	auto taggedKeys = std::map<QString, QString>();
-	auto keysSet = std::set<QString, std::greater<QString>>();
+	auto keysSet = std::set<QString, std::greater<>>();
 	for (auto &entry : langpack_.entries) {
 		if (!entry.keyBase.isEmpty()) {
 			for (auto i = 0; i != kPluralPartCount; ++i) {
@@ -342,7 +482,7 @@ QString GetOriginalValue(LangKey key) {\n\
 }
 
 template <typename ComputeResult>
-void Generator::writeSetSearch(const std::set<QString, std::greater<QString>> &set, ComputeResult computeResult, const QString &invalidResult) {
+void Generator::writeSetSearch(const std::set<QString, std::greater<>> &set, ComputeResult computeResult, const QString &invalidResult) {
 	auto tabs = [](int size) {
 		return QString(size, '\t');
 	};
diff --git a/Telegram/SourceFiles/codegen/lang/generator.h b/Telegram/SourceFiles/codegen/lang/generator.h
index fc1821faa..958ac23a3 100644
--- a/Telegram/SourceFiles/codegen/lang/generator.h
+++ b/Telegram/SourceFiles/codegen/lang/generator.h
@@ -29,10 +29,20 @@ public:
 	bool writeSource();
 
 private:
+	void writeHeaderForwardDeclarations();
+	void writeHeaderTagTypes();
+	void writeHeaderKeyType();
+	void writeHeaderTaggedMethods();
+	void writeHeaderInterface();
+	void writeHeaderTagValueLookup();
+	void writeHeaderReactiveInterface();
+	void writeHeaderProducersInterface();
+	void writeHeaderProducersInstances();
+
 	QString getFullKey(const LangPack::Entry &entry);
 
 	template <typename ComputeResult>
-	void writeSetSearch(const std::set<QString, std::greater<QString>> &set, ComputeResult computeResult, const QString &invalidResult);
+	void writeSetSearch(const std::set<QString, std::greater<>> &set, ComputeResult computeResult, const QString &invalidResult);
 
 	const LangPack &langpack_;
 	QString basePath_, baseName_;
diff --git a/Telegram/SourceFiles/lang/lang_instance.cpp b/Telegram/SourceFiles/lang/lang_instance.cpp
index 6c5e67b45..678d85658 100644
--- a/Telegram/SourceFiles/lang/lang_instance.cpp
+++ b/Telegram/SourceFiles/lang/lang_instance.cpp
@@ -780,6 +780,10 @@ Instance &Current() {
 	return Core::App().langpack();
 }
 
+QString Current(LangKey key) {
+	return Current().getValue(key);
+}
+
 rpl::producer<QString> Viewer(LangKey key) {
 	return rpl::single(
 		Current().getValue(key)
diff --git a/Telegram/SourceFiles/lang/lang_instance.h b/Telegram/SourceFiles/lang/lang_instance.h
index 76d213c02..803eb4870 100644
--- a/Telegram/SourceFiles/lang/lang_instance.h
+++ b/Telegram/SourceFiles/lang/lang_instance.h
@@ -56,6 +56,7 @@ Language DefaultLanguage();
 class Instance;
 Instance &Current();
 
+QString Current(LangKey key);
 rpl::producer<QString> Viewer(LangKey key);
 
 enum class Pack {
diff --git a/Telegram/SourceFiles/lang/lang_tag.cpp b/Telegram/SourceFiles/lang/lang_tag.cpp
index 2495e7429..88fd4e5aa 100644
--- a/Telegram/SourceFiles/lang/lang_tag.cpp
+++ b/Telegram/SourceFiles/lang/lang_tag.cpp
@@ -962,27 +962,25 @@ PluralResult Plural(
 	const auto f = NonZeroPartToInt(fraction);
 	const auto t = f;
 
-	auto &langpack = Lang::Current();
-	auto useNonDefaultPlural = (ChoosePlural != ChoosePluralDefault)
-		&& langpack.isNonDefaultPlural(LangKey(keyBase));
-	auto shift = (useNonDefaultPlural ? ChoosePlural : ChoosePluralDefault)(
+	const auto useNonDefaultPlural = (ChoosePlural != ChoosePluralDefault)
+		&& Lang::Current().isNonDefaultPlural(LangKey(keyBase));
+	const auto shift = (useNonDefaultPlural ? ChoosePlural : ChoosePluralDefault)(
 		(integer ? i : -1),
 		i,
 		v,
 		w,
 		f,
 		t);
-	auto string = langpack.getValue(LangKey(keyBase + shift));
 	if (integer) {
 		const auto round = qRound(value);
 		if (type == lt_count_short) {
-			return { string, shortened.string };
+			return { shift, shortened.string };
 		} else if (type == lt_count_decimal) {
-			return { string, QString("%L1").arg(round) };
+			return { shift, QString("%L1").arg(round) };
 		}
-		return { string, QString::number(round) };
+		return { shift, QString::number(round) };
 	}
-	return { string, FormatDouble(value) };
+	return { shift, FormatDouble(value) };
 }
 
 void UpdatePluralRules(const QString &languageId) {
diff --git a/Telegram/SourceFiles/lang/lang_tag.h b/Telegram/SourceFiles/lang/lang_tag.h
index 12fa99e65..8ce6d6758 100644
--- a/Telegram/SourceFiles/lang/lang_tag.h
+++ b/Telegram/SourceFiles/lang/lang_tag.h
@@ -22,7 +22,7 @@ struct ShortenedCount {
 ShortenedCount FormatCountToShort(int64 number);
 
 struct PluralResult {
-	QString string;
+	int keyShift = 0;
 	QString replacement;
 };
 PluralResult Plural(
diff --git a/Telegram/SourceFiles/lang/lang_values.cpp b/Telegram/SourceFiles/lang/lang_values.cpp
new file mode 100644
index 000000000..b83ebd58c
--- /dev/null
+++ b/Telegram/SourceFiles/lang/lang_values.cpp
@@ -0,0 +1,9 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "lang/lang_values.h"
+
diff --git a/Telegram/SourceFiles/lang/lang_values.h b/Telegram/SourceFiles/lang/lang_values.h
new file mode 100644
index 000000000..aa1c28419
--- /dev/null
+++ b/Telegram/SourceFiles/lang/lang_values.h
@@ -0,0 +1,181 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "lang/lang_tag.h"
+
+enum LangKey : int;
+enum lngtag_count : int;
+
+namespace Lang {
+
+QString Current(LangKey key);
+rpl::producer<QString> Viewer(LangKey key);
+
+namespace details {
+
+inline constexpr auto kPluralCount = 6;
+
+template <typename Tag> struct TagValue;
+
+template <int Index, typename Type, typename Tuple>
+Type ReplaceUnwrapTuple(Type accumulated, const Tuple &tuple) {
+	return accumulated;
+}
+
+template <int Index, typename Type, typename Tuple, typename Tag, typename ...Tags>
+Type ReplaceUnwrapTuple(
+		Type accumulated,
+		const Tuple &tuple,
+		Tag tag,
+		Tags ...tags) {
+	return ReplaceUnwrapTuple<Index + 1>(
+		ReplaceTag<Type>::Call(
+			std::move(accumulated),
+			tag,
+			std::get<Index>(tuple)),
+		tuple,
+		tags...);
+}
+
+template <typename ...Tags>
+struct ReplaceUnwrap;
+
+template <>
+struct ReplaceUnwrap<> {
+	template <typename Type>
+	static Type Call(Type accumulated) {
+		return accumulated;
+	}
+};
+
+template <typename Tag, typename ...Tags>
+struct ReplaceUnwrap<Tag, Tags...> {
+	template <typename Type, typename Value, typename ...Values>
+	static Type Call(
+			Type accumulated,
+			const Value &value,
+			const Values &...values) {
+		return ReplaceUnwrap<Tags...>::template Call(
+			ReplaceTag<Type>::Call(
+				std::move(accumulated),
+				TagValue<Tag>::value,
+				value),
+			values...);
+	}
+};
+
+template <typename ...Tags>
+struct Producer {
+	template <
+		typename P,
+		typename T = decltype(std::declval<P>()(QString())),
+		typename ...Values>
+	static rpl::producer<T> Combine(LangKey base, P p, Values &...values) {
+		return rpl::combine(
+			Viewer(base),
+			std::move(values)...
+		) | rpl::map([p = std::move(p)](auto tuple) {
+			return ReplaceUnwrapTuple<1>(p(std::get<0>(tuple)), tuple, TagValue<Tags>::value...);
+		});
+	}
+
+	template <
+		typename P,
+		typename T = decltype(std::declval<P>()(QString())),
+		typename ...Values>
+	static T Current(LangKey base, P p, const Values &...values) {
+		return ReplaceUnwrap<Tags...>::template Call(p(Lang::Current(base)), values...);
+	}
+};
+
+template <>
+struct Producer<> {
+	template <
+		typename P,
+		typename T = decltype(std::declval<P>()(QString()))>
+	static rpl::producer<T> Combine(LangKey base, P p) {
+		return Viewer(base) | rpl::map(std::move(p));
+	}
+
+	template <
+		typename P,
+		typename T = decltype(std::declval<P>()(QString()))>
+	static T Current(LangKey base, P p) {
+		return p(Lang::Current(base));
+	}
+};
+
+template <typename ...Tags>
+struct Producer<lngtag_count, Tags...> {
+	template <
+		typename P,
+		typename T = decltype(std::declval<P>()(QString())),
+		typename ...Values>
+	static rpl::producer<T> Combine(
+			LangKey base,
+			P p,
+			lngtag_count type,
+			rpl::producer<float64> &count,
+			Values &...values) {
+		return rpl::combine(
+			Viewer(base),
+			Viewer(LangKey(base + 1)),
+			Viewer(LangKey(base + 2)),
+			Viewer(LangKey(base + 3)),
+			Viewer(LangKey(base + 4)),
+			Viewer(LangKey(base + 5)),
+			std::move(count),
+			std::move(values)...
+		) | rpl::map([base, type, p = std::move(p)](auto tuple) {
+			auto plural = Plural(base, std::get<6>(tuple), type);
+			const auto select = [&] {
+				switch (plural.keyShift) {
+				case 0: return std::get<0>(tuple);
+				case 1: return std::get<1>(tuple);
+				case 2: return std::get<2>(tuple);
+				case 3: return std::get<3>(tuple);
+				case 4: return std::get<4>(tuple);
+				case 5: return std::get<5>(tuple);
+				}
+				Unexpected("Lang shift value in Plural result.");
+			};
+			return ReplaceUnwrapTuple<7>(
+				ReplaceTag<T>::Call(
+					p(select()),
+					type,
+					StartReplacements<T>::Call(
+						std::move(plural.replacement))),
+				tuple,
+				TagValue<Tags>::value...);
+		});
+	}
+
+	template <
+		typename P,
+		typename T = decltype(std::declval<P>()(QString())),
+		typename ...Values>
+	static T Current(
+			LangKey base,
+			P p,
+			lngtag_count type,
+			float64 count,
+			const Values &...values) {
+		auto plural = Plural(base, count, type);
+		return ReplaceUnwrap<Tags...>::template Call(
+			ReplaceTag<T>::Call(
+				p(Lang::Current(base + plural.keyShift)),
+				type,
+				StartReplacements<T>::Call(
+					std::move(plural.replacement))),
+			values...);
+	}
+};
+
+} // namespace details
+} // namespace Lang
diff --git a/Telegram/SourceFiles/ui/text/text_utilities.h b/Telegram/SourceFiles/ui/text/text_utilities.h
index 164d65eb6..5f7efb9c0 100644
--- a/Telegram/SourceFiles/ui/text/text_utilities.h
+++ b/Telegram/SourceFiles/ui/text/text_utilities.h
@@ -21,6 +21,9 @@ TextWithEntities Link(
 	const QString &text,
 	const QString &url = "internal:action");
 TextWithEntities RichLangValue(const QString &text);
+inline TextWithEntities WithEntities(const QString &text) {
+	return { text };
+}
 
 inline auto ToBold() {
 	return rpl::map(Bold);
@@ -36,5 +39,9 @@ inline auto ToRichLangValue() {
 	return rpl::map(RichLangValue);
 }
 
+inline auto ToWithEntities() {
+	return rpl::map(WithEntities);
+}
+
 } // namespace Text
 } // namespace Ui
diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt
index eb5a0e9b0..0fdb69629 100644
--- a/Telegram/gyp/telegram_sources.txt
+++ b/Telegram/gyp/telegram_sources.txt
@@ -435,6 +435,8 @@
 <(src_loc)/lang/lang_tag.h
 <(src_loc)/lang/lang_translator.cpp
 <(src_loc)/lang/lang_translator.h
+<(src_loc)/lang/lang_values.cpp
+<(src_loc)/lang/lang_values.h
 <(src_loc)/main/main_account.cpp
 <(src_loc)/main/main_account.h
 <(src_loc)/media/audio/media_audio.cpp