mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-03-22 07:37:29 +08:00
[feat] add Russian language support
This commit is contained in:
@@ -20,8 +20,14 @@ int ConfigCenter::Load() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
language_ = static_cast<LANGUAGE>(
|
||||
ini_.GetLongValue(section_, "language", static_cast<long>(language_)));
|
||||
const long language_value =
|
||||
ini_.GetLongValue(section_, "language", static_cast<long>(language_));
|
||||
if (language_value < static_cast<long>(LANGUAGE::CHINESE) ||
|
||||
language_value > static_cast<long>(LANGUAGE::RUSSIAN)) {
|
||||
language_ = LANGUAGE::ENGLISH;
|
||||
} else {
|
||||
language_ = static_cast<LANGUAGE>(language_value);
|
||||
}
|
||||
|
||||
video_quality_ = static_cast<VIDEO_QUALITY>(ini_.GetLongValue(
|
||||
section_, "video_quality", static_cast<long>(video_quality_)));
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace crossdesk {
|
||||
|
||||
class ConfigCenter {
|
||||
public:
|
||||
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 };
|
||||
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1, RUSSIAN = 2 };
|
||||
enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 };
|
||||
enum class VIDEO_FRAME_RATE { FPS_30 = 0, FPS_60 = 1 };
|
||||
enum class VIDEO_ENCODE_FORMAT { H264 = 0, AV1 = 1 };
|
||||
|
||||
@@ -7,240 +7,150 @@
|
||||
#define _LOCALIZATION_H_
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "localization_data.h"
|
||||
|
||||
#if _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
namespace localization {
|
||||
|
||||
static std::vector<std::string> local_desktop = {
|
||||
reinterpret_cast<const char*>(u8"本桌面"), "Local Desktop"};
|
||||
static std::vector<std::string> local_id = {
|
||||
reinterpret_cast<const char*>(u8"本机ID"), "Local ID"};
|
||||
static std::vector<std::string> local_id_copied_to_clipboard = {
|
||||
reinterpret_cast<const char*>(u8"已复制到剪贴板"), "Copied to clipboard"};
|
||||
static std::vector<std::string> password = {
|
||||
reinterpret_cast<const char*>(u8"密码"), "Password"};
|
||||
static std::vector<std::string> max_password_len = {
|
||||
reinterpret_cast<const char*>(u8"最大6个字符"), "Max 6 chars"};
|
||||
struct LanguageOption {
|
||||
std::string code;
|
||||
std::string display_name;
|
||||
};
|
||||
|
||||
static std::vector<std::string> remote_desktop = {
|
||||
reinterpret_cast<const char*>(u8"控制远程桌面"), "Control Remote Desktop"};
|
||||
static std::vector<std::string> remote_id = {
|
||||
reinterpret_cast<const char*>(u8"对端ID"), "Remote ID"};
|
||||
static std::vector<std::string> connect = {
|
||||
reinterpret_cast<const char*>(u8"连接"), "Connect"};
|
||||
static std::vector<std::string> recent_connections = {
|
||||
reinterpret_cast<const char*>(u8"近期连接"), "Recent Connections"};
|
||||
static std::vector<std::string> disconnect = {
|
||||
reinterpret_cast<const char*>(u8"断开连接"), "Disconnect"};
|
||||
static std::vector<std::string> fullscreen = {
|
||||
reinterpret_cast<const char*>(u8"全屏"), " Fullscreen"};
|
||||
static std::vector<std::string> show_net_traffic_stats = {
|
||||
reinterpret_cast<const char*>(u8"显示流量统计"), "Show Net Traffic Stats"};
|
||||
static std::vector<std::string> hide_net_traffic_stats = {
|
||||
reinterpret_cast<const char*>(u8"隐藏流量统计"), "Hide Net Traffic Stats"};
|
||||
static std::vector<std::string> video = {
|
||||
reinterpret_cast<const char*>(u8"视频"), "Video"};
|
||||
static std::vector<std::string> audio = {
|
||||
reinterpret_cast<const char*>(u8"音频"), "Audio"};
|
||||
static std::vector<std::string> data = {reinterpret_cast<const char*>(u8"数据"),
|
||||
"Data"};
|
||||
static std::vector<std::string> total = {
|
||||
reinterpret_cast<const char*>(u8"总计"), "Total"};
|
||||
static std::vector<std::string> in = {reinterpret_cast<const char*>(u8"输入"),
|
||||
"In"};
|
||||
static std::vector<std::string> out = {reinterpret_cast<const char*>(u8"输出"),
|
||||
"Out"};
|
||||
static std::vector<std::string> loss_rate = {
|
||||
reinterpret_cast<const char*>(u8"丢包率"), "Loss Rate"};
|
||||
static std::vector<std::string> exit_fullscreen = {
|
||||
reinterpret_cast<const char*>(u8"退出全屏"), "Exit fullscreen"};
|
||||
static std::vector<std::string> control_mouse = {
|
||||
reinterpret_cast<const char*>(u8"控制"), "Control"};
|
||||
static std::vector<std::string> release_mouse = {
|
||||
reinterpret_cast<const char*>(u8"释放"), "Release"};
|
||||
static std::vector<std::string> audio_capture = {
|
||||
reinterpret_cast<const char*>(u8"声音"), "Audio"};
|
||||
static std::vector<std::string> mute = {
|
||||
reinterpret_cast<const char*>(u8" 静音"), " Mute"};
|
||||
static std::vector<std::string> settings = {
|
||||
reinterpret_cast<const char*>(u8"设置"), "Settings"};
|
||||
static std::vector<std::string> language = {
|
||||
reinterpret_cast<const char*>(u8"语言:"), "Language:"};
|
||||
static std::vector<std::string> language_zh = {
|
||||
reinterpret_cast<const char*>(u8"中文"), "Chinese"};
|
||||
static std::vector<std::string> language_en = {
|
||||
reinterpret_cast<const char*>(u8"英文"), "English"};
|
||||
static std::vector<std::string> video_quality = {
|
||||
reinterpret_cast<const char*>(u8"视频质量:"), "Video Quality:"};
|
||||
static std::vector<std::string> video_frame_rate = {
|
||||
reinterpret_cast<const char*>(u8"画面采集帧率:"),
|
||||
"Video Capture Frame Rate:"};
|
||||
static std::vector<std::string> video_quality_high = {
|
||||
reinterpret_cast<const char*>(u8"高"), "High"};
|
||||
static std::vector<std::string> video_quality_medium = {
|
||||
reinterpret_cast<const char*>(u8"中"), "Medium"};
|
||||
static std::vector<std::string> video_quality_low = {
|
||||
reinterpret_cast<const char*>(u8"低"), "Low"};
|
||||
static std::vector<std::string> video_encode_format = {
|
||||
reinterpret_cast<const char*>(u8"视频编码格式:"), "Video Encode Format:"};
|
||||
static std::vector<std::string> av1 = {reinterpret_cast<const char*>(u8"AV1"),
|
||||
"AV1"};
|
||||
static std::vector<std::string> h264 = {
|
||||
reinterpret_cast<const char*>(u8"H.264"), "H.264"};
|
||||
static std::vector<std::string> enable_hardware_video_codec = {
|
||||
reinterpret_cast<const char*>(u8"启用硬件编解码器:"),
|
||||
"Enable Hardware Video Codec:"};
|
||||
static std::vector<std::string> enable_turn = {
|
||||
reinterpret_cast<const char*>(u8"启用中继服务:"), "Enable TURN Service:"};
|
||||
static std::vector<std::string> enable_srtp = {
|
||||
reinterpret_cast<const char*>(u8"启用SRTP:"), "Enable SRTP:"};
|
||||
static std::vector<std::string> self_hosted_server_config = {
|
||||
reinterpret_cast<const char*>(u8"自托管服务器配置"),
|
||||
"Self-Hosted Server Config"};
|
||||
static std::vector<std::string> self_hosted_server_settings = {
|
||||
reinterpret_cast<const char*>(u8"自托管服务器设置"),
|
||||
"Self-Hosted Server Settings"};
|
||||
static std::vector<std::string> self_hosted_server_address = {
|
||||
reinterpret_cast<const char*>(u8"服务器地址:"), "Server Address:"};
|
||||
static std::vector<std::string> self_hosted_server_port = {
|
||||
reinterpret_cast<const char*>(u8"信令服务端口:"), "Signal Service Port:"};
|
||||
static std::vector<std::string> self_hosted_server_coturn_server_port = {
|
||||
reinterpret_cast<const char*>(u8"中继服务端口:"), "Relay Service Port:"};
|
||||
static std::vector<std::string> select_a_file = {
|
||||
reinterpret_cast<const char*>(u8"请选择文件"), "Please select a file"};
|
||||
static std::vector<std::string> ok = {reinterpret_cast<const char*>(u8"确认"),
|
||||
"OK"};
|
||||
static std::vector<std::string> cancel = {
|
||||
reinterpret_cast<const char*>(u8"取消"), "Cancel"};
|
||||
class LocalizedString {
|
||||
public:
|
||||
constexpr explicit LocalizedString(const char* key) : key_(key) {}
|
||||
const std::string& operator[](int language_index) const;
|
||||
|
||||
static std::vector<std::string> new_password = {
|
||||
reinterpret_cast<const char*>(u8"请输入六位密码:"),
|
||||
"Please input a six-char password:"};
|
||||
private:
|
||||
const char* key_;
|
||||
};
|
||||
|
||||
static std::vector<std::string> input_password = {
|
||||
reinterpret_cast<const char*>(u8"请输入密码:"), "Please input password:"};
|
||||
static std::vector<std::string> validate_password = {
|
||||
reinterpret_cast<const char*>(u8"验证密码中..."), "Validate password ..."};
|
||||
static std::vector<std::string> reinput_password = {
|
||||
reinterpret_cast<const char*>(u8"请重新输入密码"),
|
||||
"Please input password again"};
|
||||
inline const std::vector<LanguageOption>& GetSupportedLanguages() {
|
||||
static const std::vector<LanguageOption> kSupportedLanguages = {
|
||||
{"zh-CN", reinterpret_cast<const char*>(u8"中文")},
|
||||
{"en-US", "English"},
|
||||
{"ru-RU", reinterpret_cast<const char*>(u8"Русский")}};
|
||||
return kSupportedLanguages;
|
||||
}
|
||||
|
||||
static std::vector<std::string> remember_password = {
|
||||
reinterpret_cast<const char*>(u8"记住密码"), "Remember password"};
|
||||
namespace detail {
|
||||
|
||||
static std::vector<std::string> signal_connected = {
|
||||
reinterpret_cast<const char*>(u8"已连接服务器"), "Connected"};
|
||||
static std::vector<std::string> signal_disconnected = {
|
||||
reinterpret_cast<const char*>(u8"未连接服务器"), "Disconnected"};
|
||||
inline int ClampLanguageIndex(int language_index) {
|
||||
if (language_index >= 0 &&
|
||||
language_index < static_cast<int>(GetSupportedLanguages().size())) {
|
||||
return language_index;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::vector<std::string> p2p_connected = {
|
||||
reinterpret_cast<const char*>(u8"对等连接已建立"), "P2P Connected"};
|
||||
static std::vector<std::string> p2p_disconnected = {
|
||||
reinterpret_cast<const char*>(u8"对等连接已断开"), "P2P Disconnected"};
|
||||
static std::vector<std::string> p2p_connecting = {
|
||||
reinterpret_cast<const char*>(u8"正在建立对等连接..."),
|
||||
"P2P Connecting ..."};
|
||||
static std::vector<std::string> receiving_screen = {
|
||||
reinterpret_cast<const char*>(u8"画面接收中..."), "Receiving screen..."};
|
||||
static std::vector<std::string> p2p_failed = {
|
||||
reinterpret_cast<const char*>(u8"对等连接失败"), "P2P Failed"};
|
||||
static std::vector<std::string> p2p_closed = {
|
||||
reinterpret_cast<const char*>(u8"对等连接已关闭"), "P2P closed"};
|
||||
using TranslationTable =
|
||||
std::unordered_map<std::string,
|
||||
std::unordered_map<std::string, std::string>>;
|
||||
|
||||
static std::vector<std::string> no_such_id = {
|
||||
reinterpret_cast<const char*>(u8"无此ID"), "No such ID"};
|
||||
inline std::unordered_map<std::string, std::string> MakeLocalizedValues(
|
||||
const TranslationRow& row) {
|
||||
return {{"zh-CN", reinterpret_cast<const char*>(row.zh)},
|
||||
{"en-US", row.en},
|
||||
{"ru-RU", reinterpret_cast<const char*>(row.ru)}};
|
||||
}
|
||||
|
||||
static std::vector<std::string> about = {
|
||||
reinterpret_cast<const char*>(u8"关于"), "About"};
|
||||
static std::vector<std::string> notification = {
|
||||
reinterpret_cast<const char*>(u8"通知"), "Notification"};
|
||||
static std::vector<std::string> new_version_available = {
|
||||
reinterpret_cast<const char*>(u8"新版本可用"), "New Version Available"};
|
||||
static std::vector<std::string> version = {
|
||||
reinterpret_cast<const char*>(u8"版本"), "Version"};
|
||||
static std::vector<std::string> release_date = {
|
||||
reinterpret_cast<const char*>(u8"发布日期: "), "Release Date: "};
|
||||
static std::vector<std::string> access_website = {
|
||||
reinterpret_cast<const char*>(u8"访问官网: "), "Access Website: "};
|
||||
static std::vector<std::string> update = {
|
||||
reinterpret_cast<const char*>(u8"更新"), "Update"};
|
||||
inline TranslationTable BuildTranslationTable() {
|
||||
TranslationTable table;
|
||||
for (const auto& row : kTranslationRows) {
|
||||
table[row.key] = MakeLocalizedValues(row);
|
||||
}
|
||||
|
||||
static std::vector<std::string> confirm_delete_connection = {
|
||||
reinterpret_cast<const char*>(u8"确认删除此连接"),
|
||||
"Confirm to delete this connection"};
|
||||
return table;
|
||||
}
|
||||
|
||||
static std::vector<std::string> enable_autostart = {
|
||||
reinterpret_cast<const char*>(u8"开机自启:"), "Auto Start:"};
|
||||
static std::vector<std::string> enable_daemon = {
|
||||
reinterpret_cast<const char*>(u8"启用守护进程:"), "Enable Daemon:"};
|
||||
static std::vector<std::string> takes_effect_after_restart = {
|
||||
reinterpret_cast<const char*>(u8"重启后生效"),
|
||||
"Takes effect after restart"};
|
||||
static std::vector<std::string> select_file = {
|
||||
reinterpret_cast<const char*>(u8"选择文件"), "Select File"};
|
||||
static std::vector<std::string> file_transfer_progress = {
|
||||
reinterpret_cast<const char*>(u8"文件传输进度"), "File Transfer Progress"};
|
||||
static std::vector<std::string> queued = {
|
||||
reinterpret_cast<const char*>(u8"队列中"), "Queued"};
|
||||
static std::vector<std::string> sending = {
|
||||
reinterpret_cast<const char*>(u8"正在传输"), "Sending"};
|
||||
static std::vector<std::string> completed = {
|
||||
reinterpret_cast<const char*>(u8"已完成"), "Completed"};
|
||||
static std::vector<std::string> failed = {
|
||||
reinterpret_cast<const char*>(u8"失败"), "Failed"};
|
||||
static std::vector<std::string> controller = {
|
||||
reinterpret_cast<const char*>(u8"控制端:"), "Controller:"};
|
||||
static std::vector<std::string> file_transfer = {
|
||||
reinterpret_cast<const char*>(u8"文件传输:"), "File Transfer:"};
|
||||
static std::vector<std::string> connection_status = {
|
||||
reinterpret_cast<const char*>(u8"连接状态:"), "Connection Status:"};
|
||||
static std::vector<std::string> file_transfer_save_path = {
|
||||
reinterpret_cast<const char*>(u8"文件接收保存路径:"),
|
||||
"File Transfer Save Path:"};
|
||||
static std::vector<std::string> browse = {
|
||||
reinterpret_cast<const char*>(u8"浏览"), "Browse"};
|
||||
static std::vector<std::string> default_desktop = {
|
||||
reinterpret_cast<const char*>(u8"桌面"), "Desktop"};
|
||||
static std::vector<std::string> minimize_to_tray = {
|
||||
reinterpret_cast<const char*>(u8"退出时最小化到系统托盘:"),
|
||||
"Minimize to system tray when exit:"};
|
||||
static std::vector<std::string> resolution = {
|
||||
reinterpret_cast<const char*>(u8"分辨率"), "Res"};
|
||||
static std::vector<std::string> connection_mode = {
|
||||
reinterpret_cast<const char*>(u8"连接模式"), "Mode"};
|
||||
static std::vector<std::string> connection_mode_direct = {
|
||||
reinterpret_cast<const char*>(u8"直连"), "Direct"};
|
||||
static std::vector<std::string> connection_mode_relay = {
|
||||
reinterpret_cast<const char*>(u8"中继"), "Relay"};
|
||||
static std::vector<std::string> online = {
|
||||
reinterpret_cast<const char*>(u8"在线"), "Online"};
|
||||
static std::vector<std::string> offline = {
|
||||
reinterpret_cast<const char*>(u8"离线"), "Offline"};
|
||||
static std::vector<std::string> device_offline = {
|
||||
reinterpret_cast<const char*>(u8"设备离线"), "Device Offline"};
|
||||
inline const TranslationTable& GetTranslationTable() {
|
||||
static const TranslationTable table = BuildTranslationTable();
|
||||
return table;
|
||||
}
|
||||
|
||||
inline const std::string& GetTranslatedText(const std::string& key,
|
||||
int language_index) {
|
||||
static const std::string kEmptyText = "";
|
||||
|
||||
const auto& table = GetTranslationTable();
|
||||
const auto key_it = table.find(key);
|
||||
if (key_it == table.end()) {
|
||||
return kEmptyText;
|
||||
}
|
||||
|
||||
const auto& localized_values = key_it->second;
|
||||
const std::string& language_code =
|
||||
GetSupportedLanguages()[ClampLanguageIndex(language_index)].code;
|
||||
|
||||
const auto exact_it = localized_values.find(language_code);
|
||||
if (exact_it != localized_values.end()) {
|
||||
return exact_it->second;
|
||||
}
|
||||
|
||||
const auto english_it = localized_values.find("en-US");
|
||||
if (english_it != localized_values.end()) {
|
||||
return english_it->second;
|
||||
}
|
||||
|
||||
const auto chinese_it = localized_values.find("zh-CN");
|
||||
if (chinese_it != localized_values.end()) {
|
||||
return chinese_it->second;
|
||||
}
|
||||
|
||||
return kEmptyText;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline const std::string& LocalizedString::operator[](
|
||||
int language_index) const {
|
||||
return detail::GetTranslatedText(key_, language_index);
|
||||
}
|
||||
|
||||
#define CROSSDESK_DECLARE_LOCALIZED_STRING(name, zh, en, ru) \
|
||||
inline const LocalizedString name(#name);
|
||||
CROSSDESK_LOCALIZATION_ALL(CROSSDESK_DECLARE_LOCALIZED_STRING)
|
||||
#undef CROSSDESK_DECLARE_LOCALIZED_STRING
|
||||
|
||||
#if _WIN32
|
||||
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
static std::vector<std::string> request_permissions = {
|
||||
reinterpret_cast<const char*>(u8"权限请求"), "Request Permissions"};
|
||||
static std::vector<std::string> screen_recording_permission = {
|
||||
reinterpret_cast<const char*>(u8"屏幕录制权限"),
|
||||
"Screen Recording Permission"};
|
||||
static std::vector<std::string> accessibility_permission = {
|
||||
reinterpret_cast<const char*>(u8"辅助功能权限"),
|
||||
"Accessibility Permission"};
|
||||
static std::vector<std::string> permission_required_message = {
|
||||
reinterpret_cast<const char*>(u8"该应用需要授权以下权限:"),
|
||||
"The application requires the following permissions:"};
|
||||
inline const wchar_t* GetExitProgramLabel(int language_index) {
|
||||
static std::vector<std::wstring> cache(GetSupportedLanguages().size());
|
||||
const int normalized_index = detail::ClampLanguageIndex(language_index);
|
||||
std::wstring& cached_text = cache[normalized_index];
|
||||
if (!cached_text.empty()) {
|
||||
return cached_text.c_str();
|
||||
}
|
||||
|
||||
const std::string& utf8_text =
|
||||
detail::GetTranslatedText("exit_program", normalized_index);
|
||||
if (utf8_text.empty()) {
|
||||
cached_text = L"Exit";
|
||||
return cached_text.c_str();
|
||||
}
|
||||
|
||||
int wide_length =
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8_text.c_str(), -1, nullptr, 0);
|
||||
if (wide_length <= 0) {
|
||||
cached_text = L"Exit";
|
||||
return cached_text.c_str();
|
||||
}
|
||||
|
||||
cached_text.resize(static_cast<size_t>(wide_length - 1));
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8_text.c_str(), -1, cached_text.data(),
|
||||
wide_length);
|
||||
return cached_text.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace localization
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
166
src/gui/assets/localization/localization_data.h
Normal file
166
src/gui/assets/localization/localization_data.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-05-29
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
#ifndef _LOCALIZATION_DATA_H_
|
||||
#define _LOCALIZATION_DATA_H_
|
||||
|
||||
namespace crossdesk {
|
||||
namespace localization {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct TranslationRow {
|
||||
const char* key;
|
||||
const char* zh;
|
||||
const char* en;
|
||||
const char* ru;
|
||||
};
|
||||
|
||||
// Single source of truth for all UI strings.
|
||||
#define CROSSDESK_LOCALIZATION_ALL(X) \
|
||||
X(local_desktop, u8"本桌面", "Local Desktop", u8"Локальный рабочий стол") \
|
||||
X(local_id, u8"本机ID", "Local ID", u8"Локальный ID") \
|
||||
X(local_id_copied_to_clipboard, u8"已复制到剪贴板", "Copied to clipboard", \
|
||||
u8"Скопировано в буфер обмена") \
|
||||
X(password, u8"密码", "Password", u8"Пароль") \
|
||||
X(max_password_len, u8"最大6个字符", "Max 6 chars", u8"Макс. 6 символов") \
|
||||
X(remote_desktop, u8"远程桌面", "Remote Desktop", \
|
||||
u8"Удаленный рабочий стол") \
|
||||
X(remote_id, u8"对端ID", "Remote ID", u8"Удаленный ID") \
|
||||
X(connect, u8"连接", "Connect", u8"Подключиться") \
|
||||
X(recent_connections, u8"近期连接", "Recent Connections", \
|
||||
u8"Недавние подключения") \
|
||||
X(disconnect, u8"断开连接", "Disconnect", u8"Отключить") \
|
||||
X(fullscreen, u8"全屏", " Fullscreen", u8"Полный экран") \
|
||||
X(show_net_traffic_stats, u8"显示流量统计", "Show Net Traffic Stats", \
|
||||
u8"Показать статистику трафика") \
|
||||
X(hide_net_traffic_stats, u8"隐藏流量统计", "Hide Net Traffic Stats", \
|
||||
u8"Скрыть статистику трафика") \
|
||||
X(video, u8"视频", "Video", u8"Видео") \
|
||||
X(audio, u8"音频", "Audio", u8"Аудио") \
|
||||
X(data, u8"数据", "Data", u8"Данные") \
|
||||
X(total, u8"总计", "Total", u8"Итого") \
|
||||
X(in, u8"输入", "In", u8"Вход") \
|
||||
X(out, u8"输出", "Out", u8"Выход") \
|
||||
X(loss_rate, u8"丢包率", "Loss Rate", u8"Потери пакетов") \
|
||||
X(exit_fullscreen, u8"退出全屏", "Exit fullscreen", \
|
||||
u8"Выйти из полноэкранного режима") \
|
||||
X(control_mouse, u8"控制", "Control", u8"Управление") \
|
||||
X(release_mouse, u8"释放", "Release", u8"Освободить") \
|
||||
X(audio_capture, u8"声音", "Audio", u8"Звук") \
|
||||
X(mute, u8" 静音", " Mute", u8"Без звука") \
|
||||
X(settings, u8"设置", "Settings", u8"Настройки") \
|
||||
X(language, u8"语言:", "Language:", u8"Язык:") \
|
||||
X(video_quality, u8"视频质量:", "Video Quality:", u8"Качество видео:") \
|
||||
X(video_frame_rate, u8"画面采集帧率:", \
|
||||
"Video Capture Frame Rate:", u8"Частота захвата видео:") \
|
||||
X(video_quality_high, u8"高", "High", u8"Высокое") \
|
||||
X(video_quality_medium, u8"中", "Medium", u8"Среднее") \
|
||||
X(video_quality_low, u8"低", "Low", u8"Низкое") \
|
||||
X(video_encode_format, u8"视频编码格式:", \
|
||||
"Video Encode Format:", u8"Формат кодека видео:") \
|
||||
X(av1, u8"AV1", "AV1", "AV1") \
|
||||
X(h264, u8"H.264", "H.264", "H.264") \
|
||||
X(enable_hardware_video_codec, u8"启用硬件编解码器:", \
|
||||
"Enable Hardware Video Codec:", u8"Использовать аппаратный кодек:") \
|
||||
X(enable_turn, u8"启用中继服务:", \
|
||||
"Enable TURN Service:", u8"Включить TURN-сервис:") \
|
||||
X(enable_srtp, u8"启用SRTP:", "Enable SRTP:", u8"Включить SRTP:") \
|
||||
X(self_hosted_server_config, u8"自托管配置", "Self-Hosted Config", \
|
||||
u8"Конфигурация self-hosted") \
|
||||
X(self_hosted_server_settings, u8"自托管设置", "Self-Hosted Settings", \
|
||||
u8"Настройки self-hosted") \
|
||||
X(self_hosted_server_address, u8"服务器地址:", \
|
||||
"Server Address:", u8"Адрес сервера:") \
|
||||
X(self_hosted_server_port, u8"信令服务端口:", \
|
||||
"Signal Service Port:", u8"Порт сигнального сервиса:") \
|
||||
X(self_hosted_server_coturn_server_port, u8"中继服务端口:", \
|
||||
"Relay Service Port:", u8"Порт реле-сервиса:") \
|
||||
X(ok, u8"确认", "OK", u8"ОК") \
|
||||
X(cancel, u8"取消", "Cancel", u8"Отмена") \
|
||||
X(new_password, u8"请输入六位密码:", \
|
||||
"Please input a six-char password:", u8"Введите шестизначный пароль:") \
|
||||
X(input_password, u8"请输入密码:", \
|
||||
"Please input password:", u8"Введите пароль:") \
|
||||
X(validate_password, u8"验证密码中...", "Validate password ...", \
|
||||
u8"Проверка пароля...") \
|
||||
X(reinput_password, u8"请重新输入密码", "Please input password again", \
|
||||
u8"Повторно введите пароль") \
|
||||
X(remember_password, u8"记住密码", "Remember password", \
|
||||
u8"Запомнить пароль") \
|
||||
X(signal_connected, u8"已连接服务器", "Connected", u8"Подключено к серверу") \
|
||||
X(signal_disconnected, u8"未连接服务器", "Disconnected", \
|
||||
u8"Нет подключения к серверу") \
|
||||
X(p2p_connected, u8"对等连接已建立", "P2P Connected", u8"P2P подключено") \
|
||||
X(p2p_disconnected, u8"对等连接已断开", "P2P Disconnected", \
|
||||
u8"P2P отключено") \
|
||||
X(p2p_connecting, u8"正在建立对等连接...", "P2P Connecting ...", \
|
||||
u8"Подключение P2P...") \
|
||||
X(receiving_screen, u8"画面接收中...", "Receiving screen...", \
|
||||
u8"Получение изображения...") \
|
||||
X(p2p_failed, u8"对等连接失败", "P2P Failed", u8"Сбой P2P") \
|
||||
X(p2p_closed, u8"对等连接已关闭", "P2P closed", u8"P2P закрыто") \
|
||||
X(no_such_id, u8"无此ID", "No such ID", u8"ID не найден") \
|
||||
X(about, u8"关于", "About", u8"О программе") \
|
||||
X(notification, u8"通知", "Notification", u8"Уведомление") \
|
||||
X(new_version_available, u8"新版本可用", "New Version Available", \
|
||||
u8"Доступна новая версия") \
|
||||
X(version, u8"版本", "Version", u8"Версия") \
|
||||
X(release_date, u8"发布日期: ", "Release Date: ", u8"Дата релиза: ") \
|
||||
X(access_website, u8"访问官网: ", \
|
||||
"Access Website: ", u8"Официальный сайт: ") \
|
||||
X(update, u8"更新", "Update", u8"Обновить") \
|
||||
X(confirm_delete_connection, u8"确认删除此连接", \
|
||||
"Confirm to delete this connection", u8"Удалить это подключение?") \
|
||||
X(enable_autostart, u8"开机自启:", "Auto Start:", u8"Автозапуск:") \
|
||||
X(enable_daemon, u8"启用守护进程:", "Enable Daemon:", u8"Включить демон:") \
|
||||
X(takes_effect_after_restart, u8"重启后生效", "Takes effect after restart", \
|
||||
u8"Вступит в силу после перезапуска") \
|
||||
X(select_file, u8"选择文件", "Select File", u8"Выбрать файл") \
|
||||
X(file_transfer_progress, u8"文件传输进度", "File Transfer Progress", \
|
||||
u8"Прогресс передачи файлов") \
|
||||
X(queued, u8"队列中", "Queued", u8"В очереди") \
|
||||
X(sending, u8"正在传输", "Sending", u8"Передача") \
|
||||
X(completed, u8"已完成", "Completed", u8"Завершено") \
|
||||
X(failed, u8"失败", "Failed", u8"Ошибка") \
|
||||
X(controller, u8"控制端:", "Controller:", u8"Контроллер:") \
|
||||
X(file_transfer, u8"文件传输:", "File Transfer:", u8"Передача файлов:") \
|
||||
X(connection_status, u8"连接状态:", \
|
||||
"Connection Status:", u8"Состояние соединения:") \
|
||||
X(file_transfer_save_path, u8"文件接收保存路径:", \
|
||||
"File Transfer Save Path:", u8"Путь сохранения файлов:") \
|
||||
X(default_desktop, u8"桌面", "Desktop", u8"Рабочий стол") \
|
||||
X(minimize_to_tray, u8"退出时最小化到系统托盘:", \
|
||||
"Minimize on Exit:", u8"Сворачивать в трей при выходе:") \
|
||||
X(resolution, u8"分辨率", "Res", u8"Разрешение") \
|
||||
X(connection_mode, u8"连接模式", "Mode", u8"Режим") \
|
||||
X(connection_mode_direct, u8"直连", "Direct", u8"Прямой") \
|
||||
X(connection_mode_relay, u8"中继", "Relay", u8"Релейный") \
|
||||
X(online, u8"在线", "Online", u8"Онлайн") \
|
||||
X(offline, u8"离线", "Offline", u8"Офлайн") \
|
||||
X(device_offline, u8"设备离线", "Device Offline", u8"Устройство офлайн") \
|
||||
X(request_permissions, u8"权限请求", "Request Permissions", \
|
||||
u8"Запрос разрешений") \
|
||||
X(screen_recording_permission, u8"屏幕录制权限", \
|
||||
"Screen Recording Permission", u8"Разрешение на запись экрана") \
|
||||
X(accessibility_permission, u8"辅助功能权限", "Accessibility Permission", \
|
||||
u8"Разрешение специальных возможностей") \
|
||||
X(permission_required_message, u8"该应用需要授权以下权限:", \
|
||||
"The application requires the following permissions:", \
|
||||
u8"Для работы приложения требуются следующие разрешения:") \
|
||||
X(exit_program, u8"退出", "Exit", u8"Выход")
|
||||
|
||||
inline constexpr TranslationRow kTranslationRows[] = {
|
||||
#define CROSSDESK_DECLARE_TRANSLATION_ROW(name, zh, en, ru) {#name, zh, en, ru},
|
||||
CROSSDESK_LOCALIZATION_ALL(CROSSDESK_DECLARE_TRANSLATION_ROW)
|
||||
#undef CROSSDESK_DECLARE_TRANSLATION_ROW
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace localization
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
@@ -36,6 +36,32 @@
|
||||
namespace crossdesk {
|
||||
|
||||
namespace {
|
||||
const ImWchar* GetMultilingualGlyphRanges() {
|
||||
static std::vector<ImWchar> glyph_ranges;
|
||||
if (glyph_ranges.empty()) {
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImFontGlyphRangesBuilder builder;
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesChineseFull());
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
|
||||
|
||||
ImVector<ImWchar> built_ranges;
|
||||
builder.BuildRanges(&built_ranges);
|
||||
glyph_ranges.assign(built_ranges.Data,
|
||||
built_ranges.Data + built_ranges.Size);
|
||||
}
|
||||
return glyph_ranges.empty() ? nullptr : glyph_ranges.data();
|
||||
}
|
||||
|
||||
bool CanReadFontFile(const char* font_path) {
|
||||
if (!font_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream font_file(font_path, std::ios::binary);
|
||||
return font_file.good();
|
||||
}
|
||||
|
||||
#if defined(__linux__) && !defined(__APPLE__)
|
||||
inline bool X11GetDisplayAndWindow(SDL_Window* window, Display** display_out,
|
||||
::Window* x11_window_out) {
|
||||
@@ -479,7 +505,8 @@ int Render::LoadSettingsFromCacheFile() {
|
||||
thumbnail_ = std::make_shared<Thumbnail>(cache_path_ + "/thumbnails/",
|
||||
aes128_key_, aes128_iv_);
|
||||
|
||||
language_button_value_ = (int)config_center_->GetLanguage();
|
||||
language_button_value_ = localization::detail::ClampLanguageIndex(
|
||||
(int)config_center_->GetLanguage());
|
||||
video_quality_button_value_ = (int)config_center_->GetVideoQuality();
|
||||
video_frame_rate_button_value_ = (int)config_center_->GetVideoFrameRate();
|
||||
video_encode_format_button_value_ =
|
||||
@@ -1195,78 +1222,88 @@ int Render::SetupFontAndStyle(ImFont** system_chinese_font_out) {
|
||||
|
||||
io.IniFilename = NULL; // disable imgui.ini
|
||||
|
||||
// Load Fonts
|
||||
// Build one merged atlas: UI font + icon font + multilingual fallback fonts.
|
||||
ImFontConfig config;
|
||||
config.FontDataOwnedByAtlas = false;
|
||||
io.Fonts->AddFontFromMemoryTTF(OPPOSans_Regular_ttf, OPPOSans_Regular_ttf_len,
|
||||
font_size, &config,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
config.MergeMode = true;
|
||||
static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
|
||||
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, 30.0f,
|
||||
&config, icon_ranges);
|
||||
|
||||
// Load system Chinese font as fallback
|
||||
config.MergeMode = false;
|
||||
config.FontDataOwnedByAtlas = false;
|
||||
|
||||
if (system_chinese_font_out) {
|
||||
*system_chinese_font_out = nullptr;
|
||||
}
|
||||
|
||||
ImFont* ui_font = io.Fonts->AddFontFromMemoryTTF(
|
||||
OPPOSans_Regular_ttf, OPPOSans_Regular_ttf_len, font_size, &config,
|
||||
io.Fonts->GetGlyphRangesDefault());
|
||||
if (!ui_font) {
|
||||
ui_font = io.Fonts->AddFontDefault(&config);
|
||||
}
|
||||
|
||||
if (!ui_font) {
|
||||
LOG_WARN("Failed to initialize base UI font");
|
||||
ImGui::StyleColorsLight();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImFontConfig icon_config = config;
|
||||
icon_config.MergeMode = true;
|
||||
static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
|
||||
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len,
|
||||
font_size, &icon_config, icon_ranges);
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows: Try Microsoft YaHei (微软雅黑) first, then SimSun (宋体)
|
||||
const char* font_paths[] = {"C:/Windows/Fonts/msyh.ttc",
|
||||
"C:/Windows/Fonts/msyhbd.ttc",
|
||||
"C:/Windows/Fonts/simsun.ttc", nullptr};
|
||||
// Cover CJK + Cyrillic on Windows.
|
||||
const char* fallback_font_paths[] = {
|
||||
"C:/Windows/Fonts/msyh.ttc", "C:/Windows/Fonts/msyhbd.ttc",
|
||||
"C:/Windows/Fonts/simsun.ttc", "C:/Windows/Fonts/arial.ttf",
|
||||
"C:/Windows/Fonts/segoeui.ttf", nullptr};
|
||||
#elif defined(__APPLE__)
|
||||
// macOS: Try PingFang SC first, then STHeiti
|
||||
const char* font_paths[] = {"/System/Library/Fonts/PingFang.ttc",
|
||||
"/System/Library/Fonts/STHeiti Light.ttc",
|
||||
"/System/Library/Fonts/STHeiti Medium.ttc",
|
||||
nullptr};
|
||||
// Cover CJK + Cyrillic on macOS.
|
||||
const char* fallback_font_paths[] = {
|
||||
"/System/Library/Fonts/PingFang.ttc",
|
||||
"/System/Library/Fonts/Hiragino Sans GB.ttc",
|
||||
"/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
|
||||
"/System/Library/Fonts/Supplemental/Arial.ttf", nullptr};
|
||||
#else
|
||||
// Linux: Try common Chinese fonts
|
||||
const char* font_paths[] = {
|
||||
// Cover CJK + Cyrillic on Linux.
|
||||
const char* fallback_font_paths[] = {
|
||||
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
|
||||
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
|
||||
"/usr/share/fonts/truetype/arphic/uming.ttc",
|
||||
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", nullptr};
|
||||
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf",
|
||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||||
nullptr};
|
||||
#endif
|
||||
|
||||
for (int i = 0; font_paths[i] != nullptr; i++) {
|
||||
std::ifstream font_file(font_paths[i], std::ios::binary);
|
||||
if (font_file.good()) {
|
||||
font_file.close();
|
||||
if (!system_chinese_font_out) {
|
||||
break;
|
||||
ImFontConfig fallback_config = config;
|
||||
fallback_config.MergeMode = true;
|
||||
const ImWchar* multilingual_ranges = GetMultilingualGlyphRanges();
|
||||
bool merged_multilingual_font = false;
|
||||
|
||||
for (int i = 0; fallback_font_paths[i] != nullptr; ++i) {
|
||||
const char* font_path = fallback_font_paths[i];
|
||||
if (!CanReadFontFile(font_path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*system_chinese_font_out =
|
||||
io.Fonts->AddFontFromFileTTF(font_paths[i], font_size, &config,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
if (*system_chinese_font_out != nullptr) {
|
||||
// Merge FontAwesome icons into the Chinese font
|
||||
config.MergeMode = true;
|
||||
static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
|
||||
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len,
|
||||
font_size, &config, icon_ranges);
|
||||
config.MergeMode = false;
|
||||
LOG_INFO("Loaded system Chinese font with icons: {}", font_paths[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no system font found, use default font
|
||||
ImFont* merged_font = io.Fonts->AddFontFromFileTTF(
|
||||
font_path, font_size, &fallback_config, multilingual_ranges);
|
||||
if (merged_font != nullptr) {
|
||||
merged_multilingual_font = true;
|
||||
if (system_chinese_font_out && *system_chinese_font_out == nullptr) {
|
||||
*system_chinese_font_out = io.Fonts->AddFontDefault(&config);
|
||||
// Merge FontAwesome icons into the default font
|
||||
config.MergeMode = true;
|
||||
static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
|
||||
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len,
|
||||
font_size, &config, icon_ranges);
|
||||
config.MergeMode = false;
|
||||
LOG_WARN("System Chinese font not found, using default font with icons");
|
||||
*system_chinese_font_out = merged_font;
|
||||
}
|
||||
LOG_INFO("Merged multilingual fallback font: {}", font_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!merged_multilingual_font) {
|
||||
LOG_WARN(
|
||||
"No multilingual fallback fonts found, non-ASCII text may not render");
|
||||
}
|
||||
|
||||
io.FontDefault = ui_font;
|
||||
if (system_chinese_font_out && *system_chinese_font_out == nullptr) {
|
||||
*system_chinese_font_out = ui_font;
|
||||
}
|
||||
|
||||
ImGui::StyleColorsLight();
|
||||
@@ -1439,10 +1476,10 @@ int Render::Run() {
|
||||
if (!latest_version_info_.empty() &&
|
||||
latest_version_info_.contains("version") &&
|
||||
latest_version_info_["version"].is_string()) {
|
||||
latest_version_ = latest_version_info_["version"];
|
||||
latest_version_ = 'v' + latest_version_info_["version"].get<std::string>();
|
||||
if (latest_version_info_.contains("releaseNotes") &&
|
||||
latest_version_info_["releaseNotes"].is_string()) {
|
||||
release_notes_ = latest_version_info_["releaseNotes"];
|
||||
release_notes_ = latest_version_info_["releaseNotes"].get<std::string>();
|
||||
} else {
|
||||
release_notes_ = "";
|
||||
}
|
||||
@@ -1503,12 +1540,16 @@ void Render::InitializeLogger() { InitLogger(exec_log_path_); }
|
||||
void Render::InitializeSettings() {
|
||||
LoadSettingsFromCacheFile();
|
||||
|
||||
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
|
||||
localization_language_index_ = language_button_value_;
|
||||
if (localization_language_index_ != 0 && localization_language_index_ != 1) {
|
||||
localization_language_index_ = 0;
|
||||
LOG_ERROR("Invalid language index: [{}], use [0] by default",
|
||||
localization_language_index_);
|
||||
localization_language_index_ =
|
||||
localization::detail::ClampLanguageIndex(language_button_value_);
|
||||
language_button_value_ = localization_language_index_;
|
||||
|
||||
if (localization_language_index_ == 0) {
|
||||
localization_language_ = ConfigCenter::LANGUAGE::CHINESE;
|
||||
} else if (localization_language_index_ == 1) {
|
||||
localization_language_ = ConfigCenter::LANGUAGE::ENGLISH;
|
||||
} else {
|
||||
localization_language_ = ConfigCenter::LANGUAGE::RUSSIAN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ bool WinTray::HandleTrayMessage(MSG* msg) {
|
||||
GetCursorPos(&pt);
|
||||
HMENU menu = CreatePopupMenu();
|
||||
AppendMenuW(menu, MF_STRING, 1001,
|
||||
localization::exit_program[language_index_]);
|
||||
localization::GetExitProgramLabel(language_index_));
|
||||
|
||||
SetForegroundWindow(hwnd_message_only_);
|
||||
int cmd =
|
||||
|
||||
@@ -49,8 +49,9 @@ bool Render::OpenUrl(const std::string& url) {
|
||||
void Render::Hyperlink(const std::string& label, const std::string& url,
|
||||
const float window_width) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 0, 255, 255));
|
||||
ImGui::SetCursorPosX(window_width * 0.1f);
|
||||
ImGui::Text("%s", label.c_str());
|
||||
ImGui::SetCursorPosX((window_width - ImGui::CalcTextSize(label.c_str()).x) /
|
||||
2.0f);
|
||||
ImGui::TextUnformatted(label.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
@@ -71,7 +72,7 @@ int Render::AboutWindow() {
|
||||
float about_window_width = title_bar_button_width_ * 7.5f;
|
||||
float about_window_height = latest_version_.empty()
|
||||
? title_bar_button_width_ * 4.0f
|
||||
: title_bar_button_width_ * 4.6f;
|
||||
: title_bar_button_width_ * 4.9f;
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(ImVec2(
|
||||
@@ -106,12 +107,14 @@ int Render::AboutWindow() {
|
||||
ImGui::Text("%s", text.c_str());
|
||||
|
||||
if (update_available_) {
|
||||
std::string latest_version =
|
||||
std::string new_version_available =
|
||||
localization::new_version_available[localization_language_index_] +
|
||||
": " + latest_version_;
|
||||
": ";
|
||||
ImGui::SetCursorPosX(about_window_width * 0.1f);
|
||||
ImGui::Text("%s", new_version_available.c_str());
|
||||
std::string access_website =
|
||||
localization::access_website[localization_language_index_];
|
||||
Hyperlink(latest_version, "https://crossdesk.cn", about_window_width);
|
||||
Hyperlink(latest_version_, "https://crossdesk.cn", about_window_width);
|
||||
}
|
||||
|
||||
ImGui::Text("");
|
||||
@@ -124,7 +127,7 @@ int Render::AboutWindow() {
|
||||
ImGui::Text("%s", license_text.c_str());
|
||||
|
||||
ImGui::SetCursorPosX(about_window_width * 0.445f);
|
||||
ImGui::SetCursorPosY(about_window_height * 0.75f);
|
||||
ImGui::SetCursorPosY(about_window_height * 0.8f);
|
||||
// OK
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str())) {
|
||||
show_about_window_ = false;
|
||||
|
||||
@@ -60,9 +60,9 @@ int Render::SettingWindow() {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
{
|
||||
const char* language_items[] = {
|
||||
localization::language_zh[localization_language_index_].c_str(),
|
||||
localization::language_en[localization_language_index_].c_str()};
|
||||
const auto& supported_languages = localization::GetSupportedLanguages();
|
||||
language_button_value_ =
|
||||
localization::detail::ClampLanguageIndex(language_button_value_);
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
@@ -77,13 +77,23 @@ int Render::SettingWindow() {
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
|
||||
if (ImGui::BeginCombo("##language",
|
||||
language_items[language_button_value_])) {
|
||||
if (ImGui::BeginCombo(
|
||||
"##language",
|
||||
localization::GetSupportedLanguages()
|
||||
[localization::detail::ClampLanguageIndex(
|
||||
language_button_value_)]
|
||||
.display_name
|
||||
.c_str())) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
for (int i = 0; i < IM_ARRAYSIZE(language_items); i++) {
|
||||
for (int i = 0; i < static_cast<int>(supported_languages.size());
|
||||
++i) {
|
||||
bool selected = (i == language_button_value_);
|
||||
if (ImGui::Selectable(language_items[i], selected))
|
||||
if (ImGui::Selectable(
|
||||
supported_languages[i].display_name.c_str(), selected))
|
||||
language_button_value_ = i;
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
@@ -438,16 +448,24 @@ int Render::SettingWindow() {
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
// Language
|
||||
language_button_value_ =
|
||||
localization::detail::ClampLanguageIndex(language_button_value_);
|
||||
if (language_button_value_ == 0) {
|
||||
config_center_->SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
|
||||
localization_language_ = ConfigCenter::LANGUAGE::CHINESE;
|
||||
} else if (language_button_value_ == 1) {
|
||||
localization_language_ = ConfigCenter::LANGUAGE::ENGLISH;
|
||||
} else {
|
||||
config_center_->SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
|
||||
localization_language_ = ConfigCenter::LANGUAGE::RUSSIAN;
|
||||
}
|
||||
config_center_->SetLanguage(localization_language_);
|
||||
language_button_value_last_ = language_button_value_;
|
||||
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
|
||||
localization_language_index_ = language_button_value_;
|
||||
LOG_INFO("Set localization language: {}",
|
||||
localization_language_index_ == 0 ? "zh" : "en");
|
||||
localization::GetSupportedLanguages()
|
||||
[localization::detail::ClampLanguageIndex(
|
||||
localization_language_index_)]
|
||||
.code
|
||||
.c_str());
|
||||
|
||||
// Video quality
|
||||
if (video_quality_button_value_ == 0) {
|
||||
|
||||
Reference in New Issue
Block a user