diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index bfe17fc5..f776bee4 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -115,6 +115,18 @@ if(CONFIG_USE_AUDIO_PROCESSING) list(APPEND SOURCES "audio_processing/audio_processor.cc" "audio_processing/wake_word_detect.cc") endif() +# 根据Kconfig选择语言目录 +if(CONFIG_LANGUAGE_ZH_CN) + set(LANG_DIR "zh-CN") +elseif(CONFIG_LANGUAGE_EN_US) + set(LANG_DIR "en-US") +endif() + +# 定义生成路径 +set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/language.json") +set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h") +file(GLOB ASSETS ${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/*.p3) + # 如果目标芯片是 ESP32,则排除特定文件 if(CONFIG_IDF_TARGET_ESP32) # 排除 "audio_codecs/box_audio_codec.cc" 和 "audio_codecs/cores3_audio_codec.cc" @@ -123,8 +135,6 @@ if(CONFIG_IDF_TARGET_ESP32) "audio_codecs/es8388_audio_codec.cc") endif() -file(GLOB ASSETS ${CMAKE_CURRENT_SOURCE_DIR}/assets/zh/*.p3) - idf_component_register(SRCS ${SOURCES} EMBED_FILES ${ASSETS} INCLUDE_DIRS ${INCLUDE_DIRS} @@ -136,17 +146,6 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" ) -# 根据Kconfig选择语言目录 -if(CONFIG_LANGUAGE_ZH) - set(LANG_DIR "zh") -elseif(CONFIG_LANGUAGE_EN) - set(LANG_DIR "en-US") -endif() - -# 定义生成路径 -set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/language.json") -set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h") - # 添加生成规则 add_custom_command( OUTPUT ${LANG_HEADER} diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 69549510..ffb923cd 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -9,13 +9,13 @@ config OTA_VERSION_URL choice prompt "语言选择" - default LANGUAGE_ZH + default LANGUAGE_ZH_CN help Select device display language - config LANGUAGE_ZH + config LANGUAGE_ZH_CN bool "Chinese" - config LANGUAGE_EN + config LANGUAGE_EN_US bool "English" endchoice diff --git a/main/application.cc b/main/application.cc index 25fc7197..74c0e1b4 100644 --- a/main/application.cc +++ b/main/application.cc @@ -1,7 +1,6 @@ #include "application.h" #include "board.h" #include "display.h" -#include "ssd1306_display.h" #include "system_info.h" #include "ml307_ssl_transport.h" #include "audio_codec.h" @@ -9,7 +8,6 @@ #include "websocket_protocol.h" #include "font_awesome_symbols.h" #include "iot/thing_manager.h" -#include "assets/zh/binary.h" #include "assets/lang_config.h" #include @@ -60,17 +58,17 @@ void Application::CheckNewVersion() { if (!ota_.CheckVersion()) { retry_count++; if (retry_count >= MAX_RETRY) { - ESP_LOGE(TAG, "版本检查失败次数过多,退出检查"); + ESP_LOGE(TAG, "Too many retries, exit version check"); return; } - ESP_LOGW(TAG, "版本检查失败,%d秒后重试 (%d/%d)", 60, retry_count, MAX_RETRY); + ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", 60, retry_count, MAX_RETRY); vTaskDelay(pdMS_TO_TICKS(60000)); continue; } retry_count = 0; if (ota_.HasNewVersion()) { - Alert("OTA 升级", "正在升级系统", "happy", std::string_view(p3_upgrade_start, p3_upgrade_end - p3_upgrade_start)); + Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "happy", Lang::Sounds::P3_UPGRADE); // Wait for the chat state to be idle do { vTaskDelay(pdMS_TO_TICKS(3000)); @@ -81,7 +79,8 @@ void Application::CheckNewVersion() { SetDeviceState(kDeviceStateUpgrading); display->SetIcon(FONT_AWESOME_DOWNLOAD); - display->SetChatMessage("system", "新版本 " + ota_.GetFirmwareVersion()); + std::string message = std::string(Lang::Strings::NEW_VERSION) + ota_.GetFirmwareVersion(); + display->SetChatMessage("system", message.c_str()); auto& board = Board::GetInstance(); board.SetPowerSaveMode(false); @@ -108,7 +107,7 @@ void Application::CheckNewVersion() { }); // If upgrade success, the device will reboot and never reach here - display->SetStatus("更新失败"); + display->SetStatus(Lang::Strings::UPGRADE_FAILED); ESP_LOGI(TAG, "Firmware upgrade failed..."); vTaskDelay(pdMS_TO_TICKS(3000)); Reboot(); @@ -119,7 +118,8 @@ void Application::CheckNewVersion() { // No new version, mark the current version as valid ota_.MarkCurrentVersionValid(); - display->ShowNotification(Lang::Strings::VERSION + " " + ota_.GetCurrentVersion()); + std::string message = std::string(Lang::Strings::VERSION) + ota_.GetCurrentVersion(); + display->ShowNotification(message.c_str()); if (ota_.HasActivationCode()) { // Activation code is valid @@ -144,24 +144,23 @@ void Application::ShowActivationCode() { struct digit_sound { char digit; - const char* data; - size_t size; + const std::string_view& sound; }; static const std::array digit_sounds{{ - digit_sound{'0', p3_0_start, size_t(p3_0_end - p3_0_start)}, - digit_sound{'1', p3_1_start, size_t(p3_1_end - p3_1_start)}, - digit_sound{'2', p3_2_start, size_t(p3_2_end - p3_2_start)}, - digit_sound{'3', p3_3_start, size_t(p3_3_end - p3_3_start)}, - digit_sound{'4', p3_4_start, size_t(p3_4_end - p3_4_start)}, - digit_sound{'5', p3_5_start, size_t(p3_5_end - p3_5_start)}, - digit_sound{'6', p3_6_start, size_t(p3_6_end - p3_6_start)}, - digit_sound{'7', p3_7_start, size_t(p3_7_end - p3_7_start)}, - digit_sound{'8', p3_8_start, size_t(p3_8_end - p3_8_start)}, - digit_sound{'9', p3_9_start, size_t(p3_9_end - p3_9_start)} + digit_sound{'0', Lang::Sounds::P3_0}, + digit_sound{'1', Lang::Sounds::P3_1}, + digit_sound{'2', Lang::Sounds::P3_2}, + digit_sound{'3', Lang::Sounds::P3_3}, + digit_sound{'4', Lang::Sounds::P3_4}, + digit_sound{'5', Lang::Sounds::P3_5}, + digit_sound{'6', Lang::Sounds::P3_6}, + digit_sound{'7', Lang::Sounds::P3_7}, + digit_sound{'8', Lang::Sounds::P3_8}, + digit_sound{'9', Lang::Sounds::P3_9} }}; // This sentence uses 9KB of SRAM, so we need to wait for it to finish - Alert("激活设备", message, "happy", std::string_view(p3_activation_start, p3_activation_end - p3_activation_start)); + Alert(Lang::Strings::ACTIVATION, message.c_str(), "happy", Lang::Sounds::P3_ACTIVATION); vTaskDelay(pdMS_TO_TICKS(1000)); background_task_->WaitForCompletion(); @@ -169,13 +168,13 @@ void Application::ShowActivationCode() { auto it = std::find_if(digit_sounds.begin(), digit_sounds.end(), [digit](const digit_sound& ds) { return ds.digit == digit; }); if (it != digit_sounds.end()) { - PlayLocalFile(it->data, it->size); + PlayLocalFile(it->sound.data(), it->sound.size()); } } } -void Application::Alert(const std::string& status, const std::string& message, const std::string& emotion, const std::string_view& sound) { - ESP_LOGW(TAG, "Alert %s: %s [%s]", status.c_str(), message.c_str(), emotion.c_str()); +void Application::Alert(const char* status, const char* message, const char* emotion, const std::string_view& sound) { + ESP_LOGW(TAG, "Alert %s: %s [%s]", status, message, emotion); auto display = Board::GetInstance().GetDisplay(); display->SetStatus(status); display->SetEmotion(emotion); @@ -220,7 +219,7 @@ void Application::ToggleChatState() { if (device_state_ == kDeviceStateIdle) { SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { - Alert("ERROR", Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad"); + Alert(Lang::Strings::ERROR, Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad"); SetDeviceState(kDeviceStateIdle); return; } @@ -254,7 +253,7 @@ void Application::StartListening() { SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { SetDeviceState(kDeviceStateIdle); - Alert("ERROR", Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad"); + Alert(Lang::Strings::ERROR, Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad"); return; } } @@ -328,14 +327,14 @@ void Application::Start() { board.StartNetwork(); // Initialize the protocol - display->SetStatus(Lang::Strings::LOADING_PROTOCOL + "..."); + display->SetStatus(Lang::Strings::LOADING_PROTOCOL); #ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET protocol_ = std::make_unique(); #else protocol_ = std::make_unique(); #endif protocol_->OnNetworkError([this](const std::string& message) { - Alert("ERROR", message, "sad"); + Alert(Lang::Strings::ERROR, message.c_str(), "sad"); }); protocol_->OnIncomingAudio([this](std::vector&& data) { std::lock_guard lock(mutex_); @@ -346,11 +345,11 @@ void Application::Start() { protocol_->OnAudioChannelOpened([this, codec, &board]() { board.SetPowerSaveMode(false); if (protocol_->server_sample_rate() != codec->output_sample_rate()) { - ESP_LOGW(TAG, "服务器的音频采样率 %d 与设备输出的采样率 %d 不一致,重采样后可能会失真", + ESP_LOGW(TAG, "Server sample rate %d does not match device output sample rate %d, resampling may cause distortion", protocol_->server_sample_rate(), codec->output_sample_rate()); } SetDecodeSampleRate(protocol_->server_sample_rate()); - // 物联网设备描述符 + // IoT device descriptors last_iot_states_.clear(); auto& thing_manager = iot::ThingManager::GetInstance(); protocol_->SendIotDescriptors(thing_manager.GetDescriptorsJson()); @@ -392,7 +391,7 @@ void Application::Start() { if (text != NULL) { ESP_LOGI(TAG, "<< %s", text->valuestring); Schedule([this, display, message = std::string(text->valuestring)]() { - display->SetChatMessage("assistant", message); + display->SetChatMessage("assistant", message.c_str()); }); } } @@ -401,14 +400,14 @@ void Application::Start() { if (text != NULL) { ESP_LOGI(TAG, ">> %s", text->valuestring); Schedule([this, display, message = std::string(text->valuestring)]() { - display->SetChatMessage("user", message); + display->SetChatMessage("user", message.c_str()); }); } } else if (strcmp(type->valuestring, "llm") == 0) { auto emotion = cJSON_GetObjectItem(root, "emotion"); if (emotion != NULL) { Schedule([this, display, emotion_str = std::string(emotion->valuestring)]() { - display->SetEmotion(emotion_str); + display->SetEmotion(emotion_str.c_str()); }); } } else if (strcmp(type->valuestring, "iot") == 0) { @@ -428,6 +427,8 @@ void Application::Start() { ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL); ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str()); ota_.SetHeader("Client-Id", board.GetUuid()); + ota_.SetHeader("X-Language", Lang::CODE); + xTaskCreate([](void* arg) { Application* app = (Application*)arg; app->CheckNewVersion(); @@ -664,18 +665,18 @@ void Application::SetDeviceState(DeviceState state) { switch (state) { case kDeviceStateUnknown: case kDeviceStateIdle: - display->SetStatus(Lang::Strings::STANDING_BY); + display->SetStatus(Lang::Strings::STANDBY); display->SetEmotion("neutral"); #ifdef CONFIG_USE_AUDIO_PROCESSING audio_processor_.Stop(); #endif break; case kDeviceStateConnecting: - display->SetStatus(Lang::Strings::CONNECTING+"..."); + display->SetStatus(Lang::Strings::CONNECTING); display->SetChatMessage("system", ""); break; case kDeviceStateListening: - display->SetStatus(Lang::Strings::LISTENING+"..."); + display->SetStatus(Lang::Strings::LISTENING); display->SetEmotion("neutral"); ResetDecoder(); opus_encoder_->ResetState(); @@ -685,7 +686,7 @@ void Application::SetDeviceState(DeviceState state) { UpdateIotStates(); break; case kDeviceStateSpeaking: - display->SetStatus(Lang::Strings::SPEAKING+"..."); + display->SetStatus(Lang::Strings::SPEAKING); ResetDecoder(); codec->EnableOutput(true); #if CONFIG_USE_AUDIO_PROCESSING diff --git a/main/application.h b/main/application.h index cd8d7df9..03135d22 100644 --- a/main/application.h +++ b/main/application.h @@ -56,7 +56,7 @@ public: bool IsVoiceDetected() const { return voice_detected_; } void Schedule(std::function callback); void SetDeviceState(DeviceState state); - void Alert(const std::string& status, const std::string& message, const std::string& emotion = "", const std::string_view& sound = ""); + void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = ""); void AbortSpeaking(AbortReason reason); void ToggleChatState(); void StartListening(); diff --git a/main/assets/en-US/0.p3 b/main/assets/en-US/0.p3 new file mode 100644 index 00000000..f201dc21 Binary files /dev/null and b/main/assets/en-US/0.p3 differ diff --git a/main/assets/en-US/1.p3 b/main/assets/en-US/1.p3 new file mode 100644 index 00000000..27d222ee Binary files /dev/null and b/main/assets/en-US/1.p3 differ diff --git a/main/assets/en-US/2.p3 b/main/assets/en-US/2.p3 new file mode 100644 index 00000000..7c8949ea Binary files /dev/null and b/main/assets/en-US/2.p3 differ diff --git a/main/assets/en-US/3.p3 b/main/assets/en-US/3.p3 new file mode 100644 index 00000000..d5f3292f Binary files /dev/null and b/main/assets/en-US/3.p3 differ diff --git a/main/assets/en-US/4.p3 b/main/assets/en-US/4.p3 new file mode 100644 index 00000000..d4045bf5 Binary files /dev/null and b/main/assets/en-US/4.p3 differ diff --git a/main/assets/en-US/5.p3 b/main/assets/en-US/5.p3 new file mode 100644 index 00000000..735d3608 Binary files /dev/null and b/main/assets/en-US/5.p3 differ diff --git a/main/assets/en-US/6.p3 b/main/assets/en-US/6.p3 new file mode 100644 index 00000000..a52bf6b8 Binary files /dev/null and b/main/assets/en-US/6.p3 differ diff --git a/main/assets/en-US/7.p3 b/main/assets/en-US/7.p3 new file mode 100644 index 00000000..4dd383ff Binary files /dev/null and b/main/assets/en-US/7.p3 differ diff --git a/main/assets/en-US/8.p3 b/main/assets/en-US/8.p3 new file mode 100644 index 00000000..fe89fb46 Binary files /dev/null and b/main/assets/en-US/8.p3 differ diff --git a/main/assets/en-US/9.p3 b/main/assets/en-US/9.p3 new file mode 100644 index 00000000..dd9ed7b3 Binary files /dev/null and b/main/assets/en-US/9.p3 differ diff --git a/main/assets/en-US/activation.p3 b/main/assets/en-US/activation.p3 new file mode 100644 index 00000000..2a260b55 Binary files /dev/null and b/main/assets/en-US/activation.p3 differ diff --git a/main/assets/en-US/err_pin.p3 b/main/assets/en-US/err_pin.p3 new file mode 100644 index 00000000..c33346ce Binary files /dev/null and b/main/assets/en-US/err_pin.p3 differ diff --git a/main/assets/en-US/err_reg.p3 b/main/assets/en-US/err_reg.p3 new file mode 100644 index 00000000..27b5a2f5 Binary files /dev/null and b/main/assets/en-US/err_reg.p3 differ diff --git a/main/assets/en-US/language.json b/main/assets/en-US/language.json index a800ac12..18d751bd 100644 --- a/main/assets/en-US/language.json +++ b/main/assets/en-US/language.json @@ -1,34 +1,44 @@ { "language": { - "type" :"en" + "type": "en" }, "strings": { - "VERSION": "Version", - "LOADING_PROTOCOL":"Loading Protocol", - "INITIALIZING":"Initializing", - "NOTICE":"Notice", + "VERSION": "Ver ", + "LOADING_PROTOCOL": "Loading Protocol...", + "INITIALIZING": "Initializing...", + "DETECTING_MODULE": "Detecting module...", + "REGISTERING_NETWORK": "Waiting for network...", + + "ERROR": "Error", + "PIN_ERROR": "Please insert SIM card", + "REG_ERROR": "Unable to access network, please check SIM card status", - "STANDING_BY":"Standing By", - "CONNECT":"Connect", - "CONNECTING":"Connecting", - "CONNECTION_SUCCESSFUL":"Connection Successful", + "STANDBY": "Standby", + "CONNECT_TO": "Connect to ", + "CONNECTING": "Connecting...", + "CONNECTION_SUCCESSFUL": "Connection Successful", + "CONNECTED_TO": "Connected to ", - "LISTENING":"Listening", - "SPEAKING":"Speaking", + "LISTENING": "Listening...", + "SPEAKING": "Speaking...", - "UNABLE_TO_CONNECT_TO_SERVICE":"Unable to connect to service", - "WAITING_FOR_RESPONSE_TIMEOUT":"Waiting for response timeout", - "SENDING_FAILED_PLEASE_CHECK_THE_NETWORK":"Sending failed, please check the network", + "UNABLE_TO_CONNECT_TO_SERVICE": "Unable to connect to service", + "WAITING_FOR_RESPONSE_TIMEOUT": "Waiting for response timeout", + "SENDING_FAILED_PLEASE_CHECK_THE_NETWORK": "Sending failed, please check the network", - - - "CONNECT_MOBILE_PHONE_TO_HOTSPOT":"Connect mobile phone to hotspot", - "ACCESS_VIA_BROWSER":"Access via browser", - "WIFI_CONFIGURATION_MODE":"Wi-Fi Configuration Mode", - "SCANNING_WIFI":"Scanning Wi-Fi", + "CONNECT_TO_HOTSPOT": "Hotspot: ", + "ACCESS_VIA_BROWSER": " Config URL: ", + "WIFI_CONFIG_MODE": "Wi-Fi Configuration Mode", + "ENTERING_WIFI_CONFIG_MODE": "Entering Wi-Fi configuration mode...", + "SCANNING_WIFI": "Scanning Wi-Fi...", "UNABLE_TO_ESTABLISH_AUDIO_CHANNEL": "Unable to establish audio channel", - "TEST":"Test" + "TEST": "Test", + "NEW_VERSION": "New version ", + "OTA_UPGRADE": "OTA Upgrade", + "UPGRADING": "System is upgrading...", + "UPGRADE_FAILED": "Upgrade failed", + "ACTIVATION": "Activation" } } \ No newline at end of file diff --git a/main/assets/en-US/upgrade.p3 b/main/assets/en-US/upgrade.p3 new file mode 100644 index 00000000..4e050e4c Binary files /dev/null and b/main/assets/en-US/upgrade.p3 differ diff --git a/main/assets/en-US/welcome.p3 b/main/assets/en-US/welcome.p3 new file mode 100644 index 00000000..d2c35f46 Binary files /dev/null and b/main/assets/en-US/welcome.p3 differ diff --git a/main/assets/en-US/wificonfig.p3 b/main/assets/en-US/wificonfig.p3 new file mode 100644 index 00000000..3245e31a Binary files /dev/null and b/main/assets/en-US/wificonfig.p3 differ diff --git a/main/assets/zh/0.p3 b/main/assets/zh-CN/0.p3 similarity index 100% rename from main/assets/zh/0.p3 rename to main/assets/zh-CN/0.p3 diff --git a/main/assets/zh/1.p3 b/main/assets/zh-CN/1.p3 similarity index 100% rename from main/assets/zh/1.p3 rename to main/assets/zh-CN/1.p3 diff --git a/main/assets/zh/2.p3 b/main/assets/zh-CN/2.p3 similarity index 100% rename from main/assets/zh/2.p3 rename to main/assets/zh-CN/2.p3 diff --git a/main/assets/zh/3.p3 b/main/assets/zh-CN/3.p3 similarity index 100% rename from main/assets/zh/3.p3 rename to main/assets/zh-CN/3.p3 diff --git a/main/assets/zh/4.p3 b/main/assets/zh-CN/4.p3 similarity index 100% rename from main/assets/zh/4.p3 rename to main/assets/zh-CN/4.p3 diff --git a/main/assets/zh/5.p3 b/main/assets/zh-CN/5.p3 similarity index 100% rename from main/assets/zh/5.p3 rename to main/assets/zh-CN/5.p3 diff --git a/main/assets/zh/6.p3 b/main/assets/zh-CN/6.p3 similarity index 100% rename from main/assets/zh/6.p3 rename to main/assets/zh-CN/6.p3 diff --git a/main/assets/zh/7.p3 b/main/assets/zh-CN/7.p3 similarity index 100% rename from main/assets/zh/7.p3 rename to main/assets/zh-CN/7.p3 diff --git a/main/assets/zh/8.p3 b/main/assets/zh-CN/8.p3 similarity index 100% rename from main/assets/zh/8.p3 rename to main/assets/zh-CN/8.p3 diff --git a/main/assets/zh/9.p3 b/main/assets/zh-CN/9.p3 similarity index 100% rename from main/assets/zh/9.p3 rename to main/assets/zh-CN/9.p3 diff --git a/main/assets/zh/activation.p3 b/main/assets/zh-CN/activation.p3 similarity index 100% rename from main/assets/zh/activation.p3 rename to main/assets/zh-CN/activation.p3 diff --git a/main/assets/zh/err_pin.p3 b/main/assets/zh-CN/err_pin.p3 similarity index 100% rename from main/assets/zh/err_pin.p3 rename to main/assets/zh-CN/err_pin.p3 diff --git a/main/assets/zh/err_reg.p3 b/main/assets/zh-CN/err_reg.p3 similarity index 100% rename from main/assets/zh/err_reg.p3 rename to main/assets/zh-CN/err_reg.p3 diff --git a/main/assets/zh-CN/language.json b/main/assets/zh-CN/language.json new file mode 100644 index 00000000..5778f83e --- /dev/null +++ b/main/assets/zh-CN/language.json @@ -0,0 +1,42 @@ +{ + "language": { + "type" :"zh-CN" + }, + "strings": { + "ERROR":"错误", + "VERSION": "版本 ", + "LOADING_PROTOCOL":"加载协议...", + "INITIALIZING":"正在初始化...", + "PIN_ERROR":"请插入 SIM 卡", + "REG_ERROR":"无法接入网络,请检查流量卡状态", + "DETECTING_MODULE":"检测模组...", + "REGISTERING_NETWORK":"等待网络...", + + "STANDBY":"待命", + "CONNECT_TO":"连接 ", + "CONNECTING":"连接中...", + "CONNECTED_TO":"已连接 ", + + "LISTENING":"聆听中...", + "SPEAKING":"说话中...", + + "UNABLE_TO_CONNECT_TO_SERVICE":"无法连接服务", + "WAITING_FOR_RESPONSE_TIMEOUT":"等待响应超时", + "SENDING_FAILED_PLEASE_CHECK_THE_NETWORK":"发送失败,请检查网络", + + "CONNECT_TO_HOTSPOT":"手机连接热点 ", + "ACCESS_VIA_BROWSER":",浏览器访问 ", + "WIFI_CONFIG_MODE":"配网模式", + "ENTERING_WIFI_CONFIG_MODE":"进入配网模式...", + "SCANNING_WIFI":"扫描 Wi-Fi...", + + "UNABLE_TO_ESTABLISH_AUDIO_CHANNEL": "无法建立音频通道", + "TEST":"测试", + + "NEW_VERSION": "新版本 ", + "OTA_UPGRADE":"OTA 升级", + "UPGRADING":"正在升级系统...", + "UPGRADE_FAILED":"升级失败", + "ACTIVATION":"激活设备" + } +} diff --git a/main/assets/zh/upgrade.p3 b/main/assets/zh-CN/upgrade.p3 similarity index 100% rename from main/assets/zh/upgrade.p3 rename to main/assets/zh-CN/upgrade.p3 diff --git a/main/assets/zh/welcome.p3 b/main/assets/zh-CN/welcome.p3 similarity index 100% rename from main/assets/zh/welcome.p3 rename to main/assets/zh-CN/welcome.p3 diff --git a/main/assets/zh/wificonfig.p3 b/main/assets/zh-CN/wificonfig.p3 similarity index 100% rename from main/assets/zh/wificonfig.p3 rename to main/assets/zh-CN/wificonfig.p3 diff --git a/main/assets/zh/binary.h b/main/assets/zh/binary.h deleted file mode 100644 index 9407fd9e..00000000 --- a/main/assets/zh/binary.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef BINARY_H -#define BINARY_H - -extern const char p3_err_reg_start[] asm("_binary_err_reg_p3_start"); -extern const char p3_err_reg_end[] asm("_binary_err_reg_p3_end"); -extern const char p3_err_pin_start[] asm("_binary_err_pin_p3_start"); -extern const char p3_err_pin_end[] asm("_binary_err_pin_p3_end"); -extern const char p3_wificonfig_start[] asm("_binary_wificonfig_p3_start"); -extern const char p3_wificonfig_end[] asm("_binary_wificonfig_p3_end"); -extern const char p3_upgrade_start[] asm("_binary_upgrade_p3_start"); -extern const char p3_upgrade_end[] asm("_binary_upgrade_p3_end"); -extern const char p3_activation_start[] asm("_binary_activation_p3_start"); -extern const char p3_activation_end[] asm("_binary_activation_p3_end"); -extern const char p3_welcome_start[] asm("_binary_welcome_p3_start"); -extern const char p3_welcome_end[] asm("_binary_welcome_p3_end"); - -extern const char p3_0_start[] asm("_binary_0_p3_start"); -extern const char p3_0_end[] asm("_binary_0_p3_end"); -extern const char p3_1_start[] asm("_binary_1_p3_start"); -extern const char p3_1_end[] asm("_binary_1_p3_end"); -extern const char p3_2_start[] asm("_binary_2_p3_start"); -extern const char p3_2_end[] asm("_binary_2_p3_end"); -extern const char p3_3_start[] asm("_binary_3_p3_start"); -extern const char p3_3_end[] asm("_binary_3_p3_end"); -extern const char p3_4_start[] asm("_binary_4_p3_start"); -extern const char p3_4_end[] asm("_binary_4_p3_end"); -extern const char p3_5_start[] asm("_binary_5_p3_start"); -extern const char p3_5_end[] asm("_binary_5_p3_end"); -extern const char p3_6_start[] asm("_binary_6_p3_start"); -extern const char p3_6_end[] asm("_binary_6_p3_end"); -extern const char p3_7_start[] asm("_binary_7_p3_start"); -extern const char p3_7_end[] asm("_binary_7_p3_end"); -extern const char p3_8_start[] asm("_binary_8_p3_start"); -extern const char p3_8_end[] asm("_binary_8_p3_end"); -extern const char p3_9_start[] asm("_binary_9_p3_start"); -extern const char p3_9_end[] asm("_binary_9_p3_end"); - - -#endif \ No newline at end of file diff --git a/main/assets/zh/language.json b/main/assets/zh/language.json deleted file mode 100644 index ad8eea8f..00000000 --- a/main/assets/zh/language.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "language": { - "type" :"zh" - }, - "strings": { - "VERSION": "版本", - "LOADING_PROTOCOL":"加载协议", - "INITIALIZING":"正在初始化", - "NOTICE":"通知", - - "STANDING_BY":"待命", - "CONNECT":"连接", - "CONNECTING":"连接中", - "CONNECTION_SUCCESSFUL":"连接成功", - - "LISTENING":"聆听中", - "SPEAKING":"说话中", - - "UNABLE_TO_CONNECT_TO_SERVICE":"无法连接服务", - "WAITING_FOR_RESPONSE_TIMEOUT":"等待响应超时", - "SENDING_FAILED_PLEASE_CHECK_THE_NETWORK":"发送失败,请检查网络", - - - - "CONNECT_MOBILE_PHONE_TO_HOTSPOT":"手机连接热点", - "ACCESS_VIA_BROWSER":"浏览器访问", - "WIFI_CONFIGURATION_MODE":"配网模式", - "SCANNING_WIFI":"扫描 WIFI", - - "UNABLE_TO_ESTABLISH_AUDIO_CHANNEL": "无法建立音频通道", - "TEST":"测试" - - } -} diff --git a/main/assets/zh/sounds.tar.gz b/main/assets/zh/sounds.tar.gz deleted file mode 100644 index 16a3bf09..00000000 Binary files a/main/assets/zh/sounds.tar.gz and /dev/null differ diff --git a/main/boards/atoms3-echo-base/atoms3_echo_base.cc b/main/boards/atoms3-echo-base/atoms3_echo_base.cc index 04e00389..a426fa6f 100644 --- a/main/boards/atoms3-echo-base/atoms3_echo_base.cc +++ b/main/boards/atoms3-echo-base/atoms3_echo_base.cc @@ -1,12 +1,12 @@ #include "wifi_board.h" #include "audio_codecs/es8311_audio_codec.h" #include "display/lcd_display.h" -#include "display/no_display.h" #include "application.h" #include "button.h" #include "config.h" #include "i2c_device.h" #include "iot/thing_manager.h" +#include "assets/lang_config.h" #include #include @@ -110,9 +110,9 @@ private: InitializeSpi(); InitializeGc9107Display(); InitializeButtons(); - display_->SetStatus("错误"); + display_->SetStatus(Lang::Strings::ERROR); display_->SetEmotion("sad"); - display_->SetChatMessage("system", "Echo Base\n未连接"); + display_->SetChatMessage("system", "Echo Base\nnot connected"); while (1) { ESP_LOGE(TAG, "Atomic Echo Base is disconnected"); diff --git a/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc b/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc index ccf2bbc3..226dd396 100644 --- a/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc +++ b/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc @@ -1,12 +1,12 @@ #include "wifi_board.h" #include "audio_codecs/es8311_audio_codec.h" #include "display/lcd_display.h" -#include "display/no_display.h" #include "application.h" #include "button.h" #include "config.h" #include "i2c_device.h" #include "iot/thing_manager.h" +#include "assets/lang_config.h" #include #include @@ -158,9 +158,10 @@ private: InitializeSpi(); InitializeGc9107Display(); InitializeButtons(); - display_->SetStatus("错误"); + + display_->SetStatus(Lang::Strings::ERROR); display_->SetEmotion("sad"); - display_->SetChatMessage("system", "Echo Base\n未连接"); + display_->SetChatMessage("system", "Echo Base\nnot connected"); while (1) { ESP_LOGE(TAG, "Atomic Echo Base is disconnected"); diff --git a/main/boards/common/board.cc b/main/boards/common/board.cc index a3e67a69..67b9e47f 100644 --- a/main/boards/common/board.cc +++ b/main/boards/common/board.cc @@ -2,6 +2,7 @@ #include "system_info.h" #include "settings.h" #include "display/no_display.h" +#include "assets/lang_config.h" #include #include @@ -99,6 +100,7 @@ std::string Board::GetJson() { */ std::string json = "{"; json += "\"version\":2,"; + json += "\"language\":\"" + std::string(Lang::CODE) + "\","; json += "\"flash_size\":" + std::to_string(SystemInfo::GetFlashSize()) + ","; json += "\"minimum_free_heap_size\":" + std::to_string(SystemInfo::GetMinimumFreeHeapSize()) + ","; json += "\"mac_address\":\"" + SystemInfo::GetMacAddress() + "\","; diff --git a/main/boards/common/ml307_board.cc b/main/boards/common/ml307_board.cc index 7ae13ea0..587506a7 100644 --- a/main/boards/common/ml307_board.cc +++ b/main/boards/common/ml307_board.cc @@ -3,7 +3,7 @@ #include "application.h" #include "display.h" #include "font_awesome_symbols.h" -#include "assets/zh/binary.h" +#include "assets/lang_config.h" #include #include @@ -25,7 +25,7 @@ std::string Ml307Board::GetBoardType() { void Ml307Board::StartNetwork() { auto display = Board::GetInstance().GetDisplay(); - display->SetStatus("检测模组..."); + display->SetStatus(Lang::Strings::DETECTING_MODULE); modem_.SetDebug(false); modem_.SetBaudRate(921600); @@ -45,13 +45,13 @@ void Ml307Board::StartNetwork() { void Ml307Board::WaitForNetworkReady() { auto& application = Application::GetInstance(); auto display = Board::GetInstance().GetDisplay(); - display->SetStatus("等待网络..."); + display->SetStatus(Lang::Strings::REGISTERING_NETWORK); int result = modem_.WaitForNetworkReady(); if (result == -1) { - application.Alert("PIN_ERROR", "请插入SIM卡", "sad", std::string_view(p3_err_pin_start, p3_err_pin_end - p3_err_pin_start)); + application.Alert(Lang::Strings::ERROR, Lang::Strings::PIN_ERROR, "sad", Lang::Sounds::P3_ERR_PIN); return; } else if (result == -2) { - application.Alert("REG_ERROR", "无法接入网络,请检查流量卡状态", "sad", std::string_view(p3_err_reg_start, p3_err_reg_end - p3_err_reg_start)); + application.Alert(Lang::Strings::ERROR, Lang::Strings::REG_ERROR, "sad", Lang::Sounds::P3_ERR_REG); return; } diff --git a/main/boards/common/wifi_board.cc b/main/boards/common/wifi_board.cc index f0755411..6359b790 100644 --- a/main/boards/common/wifi_board.cc +++ b/main/boards/common/wifi_board.cc @@ -5,7 +5,7 @@ #include "system_info.h" #include "font_awesome_symbols.h" #include "settings.h" -#include "assets/zh/binary.h" +#include "assets/lang_config.h" #include #include @@ -20,7 +20,6 @@ #include #include #include -#include "assets/lang_config.h" static const char *TAG = "WifiBoard"; @@ -46,14 +45,14 @@ void WifiBoard::EnterWifiConfigMode() { wifi_ap.Start(); // 显示 WiFi 配置 AP 的 SSID 和 Web 服务器 URL - std::string hint = Lang::Strings::CONNECT_MOBILE_PHONE_TO_HOTSPOT + " "; + std::string hint = Lang::Strings::CONNECT_TO_HOTSPOT; hint += wifi_ap.GetSsid(); - hint += "\n"+ Lang::Strings::ACCESS_VIA_BROWSER + " "; + hint += Lang::Strings::ACCESS_VIA_BROWSER; hint += wifi_ap.GetWebServerUrl(); hint += "\n\n"; // 播报配置 WiFi 的提示 - application.Alert(Lang::Strings::WIFI_CONFIGURATION_MODE, hint, "", std::string(p3_wificonfig_start, p3_wificonfig_end - p3_wificonfig_start)); + application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::P3_WIFICONFIG); // Wait forever until reset after configuration while (true) { @@ -87,11 +86,16 @@ void WifiBoard::StartNetwork() { }); wifi_station.OnConnect([this](const std::string& ssid) { auto display = Board::GetInstance().GetDisplay(); - display->ShowNotification(std::string(Lang::Strings::CONNECT + " ") + ssid + "...", 30000); + std::string notification = Lang::Strings::CONNECT_TO; + notification += ssid; + notification += "..."; + display->ShowNotification(notification.c_str(), 30000); }); wifi_station.OnConnected([this](const std::string& ssid) { auto display = Board::GetInstance().GetDisplay(); - display->ShowNotification(std::string(Lang::Strings::CONNECTION_SUCCESSFUL) + ssid); + std::string notification = Lang::Strings::CONNECTED_TO; + notification += ssid; + display->ShowNotification(notification.c_str(), 30000); }); wifi_station.Start(); @@ -167,12 +171,12 @@ void WifiBoard::SetPowerSaveMode(bool enabled) { } void WifiBoard::ResetWifiConfiguration() { - // Reset the wifi station + // Set a flag and reboot the device to enter the network configuration mode { Settings settings("wifi", true); settings.SetInt("force_ap", 1); } - GetDisplay()->ShowNotification("Enter the network configuration mode..."); + GetDisplay()->ShowNotification(Lang::Strings::ENTERING_WIFI_CONFIG_MODE); vTaskDelay(pdMS_TO_TICKS(1000)); // Reboot the device esp_restart(); diff --git a/main/display/display.cc b/main/display/display.cc index e4172b42..17fd9d0f 100644 --- a/main/display/display.cc +++ b/main/display/display.cc @@ -67,22 +67,26 @@ Display::~Display() { } } -void Display::SetStatus(const std::string &status) { +void Display::SetStatus(const char* status) { DisplayLockGuard lock(this); if (status_label_ == nullptr) { return; } - lv_label_set_text(status_label_, status.c_str()); + lv_label_set_text(status_label_, status); lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); } void Display::ShowNotification(const std::string ¬ification, int duration_ms) { + ShowNotification(notification.c_str(), duration_ms); +} + +void Display::ShowNotification(const char* notification, int duration_ms) { DisplayLockGuard lock(this); if (notification_label_ == nullptr) { return; } - lv_label_set_text(notification_label_, notification.c_str()); + lv_label_set_text(notification_label_, notification); lv_obj_clear_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(status_label_, LV_OBJ_FLAG_HIDDEN); @@ -154,7 +158,7 @@ void Display::Update() { } -void Display::SetEmotion(const std::string &emotion) { +void Display::SetEmotion(const char* emotion) { struct Emotion { const char* icon; const char* text; @@ -185,8 +189,9 @@ void Display::SetEmotion(const std::string &emotion) { }; // 查找匹配的表情 + std::string_view emotion_view(emotion); auto it = std::find_if(emotions.begin(), emotions.end(), - [&emotion](const Emotion& e) { return e.text == emotion; }); + [&emotion_view](const Emotion& e) { return e.text == emotion_view; }); DisplayLockGuard lock(this); if (emotion_label_ == nullptr) { @@ -209,12 +214,12 @@ void Display::SetIcon(const char* icon) { lv_label_set_text(emotion_label_, icon); } -void Display::SetChatMessage(const std::string &role, const std::string &content) { +void Display::SetChatMessage(const char* role, const char* content) { DisplayLockGuard lock(this); if (chat_message_label_ == nullptr) { return; } - lv_label_set_text(chat_message_label_, content.c_str()); + lv_label_set_text(chat_message_label_, content); } void Display::SetBacklight(uint8_t brightness) { diff --git a/main/display/display.h b/main/display/display.h index 7bab9460..8b28a551 100644 --- a/main/display/display.h +++ b/main/display/display.h @@ -18,10 +18,11 @@ public: Display(); virtual ~Display(); - virtual void SetStatus(const std::string &status); + virtual void SetStatus(const char* status); + virtual void ShowNotification(const char* notification, int duration_ms = 3000); virtual void ShowNotification(const std::string ¬ification, int duration_ms = 3000); - virtual void SetEmotion(const std::string &emotion); - virtual void SetChatMessage(const std::string &role, const std::string &content); + virtual void SetEmotion(const char* emotion); + virtual void SetChatMessage(const char* role, const char* content); virtual void SetIcon(const char* icon); virtual void SetBacklight(uint8_t brightness); diff --git a/main/display/lcd_display.cc b/main/display/lcd_display.cc index 7211735b..3e8ead36 100644 --- a/main/display/lcd_display.cc +++ b/main/display/lcd_display.cc @@ -257,15 +257,14 @@ void LcdDisplay::SetupUI() { notification_label_ = lv_label_create(status_bar_); lv_obj_set_flex_grow(notification_label_, 1); lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(notification_label_, (Lang::Strings::NOTICE).c_str()); + lv_label_set_text(notification_label_, ""); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); status_label_ = lv_label_create(status_bar_); lv_obj_set_flex_grow(status_label_, 1); lv_label_set_long_mode(status_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_label_set_text(status_label_,(Lang::Strings::INITIALIZING + "...").c_str()); lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0); - + lv_label_set_text(status_label_, Lang::Strings::INITIALIZING); mute_label_ = lv_label_create(status_bar_); lv_label_set_text(mute_label_, ""); lv_obj_set_style_text_font(mute_label_, fonts_.icon_font, 0); @@ -275,7 +274,7 @@ void LcdDisplay::SetupUI() { lv_obj_set_style_text_font(battery_label_, fonts_.icon_font, 0); } -void LcdDisplay::SetEmotion(const std::string &emotion) { +void LcdDisplay::SetEmotion(const char* emotion) { struct Emotion { const char* icon; const char* text; @@ -306,8 +305,9 @@ void LcdDisplay::SetEmotion(const std::string &emotion) { }; // 查找匹配的表情 + std::string_view emotion_view(emotion); auto it = std::find_if(emotions.begin(), emotions.end(), - [&emotion](const Emotion& e) { return e.text == emotion; }); + [&emotion_view](const Emotion& e) { return e.text == emotion_view; }); DisplayLockGuard lock(this); if (emotion_label_ == nullptr) { diff --git a/main/display/lcd_display.h b/main/display/lcd_display.h index f4fba4de..04f87245 100644 --- a/main/display/lcd_display.h +++ b/main/display/lcd_display.h @@ -45,7 +45,7 @@ public: DisplayFonts fonts); ~LcdDisplay(); - virtual void SetEmotion(const std::string &emotion) override; + virtual void SetEmotion(const char* emotion) override; virtual void SetIcon(const char* icon) override; virtual void SetBacklight(uint8_t brightness) override; }; diff --git a/main/display/ssd1306_display.cc b/main/display/ssd1306_display.cc index 6ce5dba6..d6214d9d 100644 --- a/main/display/ssd1306_display.cc +++ b/main/display/ssd1306_display.cc @@ -221,12 +221,12 @@ void Ssd1306Display::SetupUI_128x64() { notification_label_ = lv_label_create(status_bar_); lv_obj_set_flex_grow(notification_label_, 1); lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0); - lv_label_set_text(notification_label_, (Lang::Strings::NOTICE).c_str()); + lv_label_set_text(notification_label_, ""); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); status_label_ = lv_label_create(status_bar_); lv_obj_set_flex_grow(status_label_, 1); - lv_label_set_text(status_label_,(Lang::Strings::INITIALIZING + "...").c_str()); + lv_label_set_text(status_label_, Lang::Strings::INITIALIZING); lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0); mute_label_ = lv_label_create(status_bar_); @@ -296,10 +296,10 @@ void Ssd1306Display::SetupUI_128x32() { status_label_ = lv_label_create(status_bar_); lv_obj_set_style_pad_left(status_label_, 2, 0); - lv_label_set_text(status_label_,(Lang::Strings::INITIALIZING + "...").c_str()); + lv_label_set_text(status_label_, Lang::Strings::INITIALIZING); notification_label_ = lv_label_create(status_bar_); - lv_label_set_text(notification_label_, (Lang::Strings::NOTICE).c_str()); + lv_label_set_text(notification_label_, ""); lv_obj_set_style_pad_left(notification_label_, 2, 0); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); diff --git a/scripts/gen_lang.py b/scripts/gen_lang.py index 9885acc0..dd0ac83b 100644 --- a/scripts/gen_lang.py +++ b/scripts/gen_lang.py @@ -6,18 +6,20 @@ import os HEADER_TEMPLATE = """// Auto-generated language config #pragma once -#include #include namespace Lang {{ // 语言元数据 - constexpr std::string_view CODE_VIEW = "{lang_code}"; - const std::string CODE = std::string(CODE_VIEW); + constexpr const char* CODE = "{lang_code}"; // 字符串资源 namespace Strings {{ -{strings_view} -{strings_string} +{strings} + }} + + // 音效资源 + namespace Sounds {{ +{sounds} }} }} """ @@ -33,18 +35,29 @@ def generate_header(input_path, output_path): lang_code = data['language']['type'] # 生成字符串常量 - strings_view = [] - strings_string = [] + strings = [] + sounds = [] for key, value in data['strings'].items(): value = value.replace('"', '\\"') - strings_view.append(f' constexpr std::string_view {key.upper()}_VIEW = "{value}";') - strings_string.append(f' const std::string {key.upper()} = std::string({key.upper()}_VIEW);') + strings.append(f' constexpr const char* {key.upper()} = "{value}";') + + # 生成音效常量 + for file in os.listdir(os.path.dirname(input_path)): + if file.endswith('.p3'): + base_name = os.path.splitext(file)[0] + sounds.append(f''' + extern const char p3_{base_name}_start[] asm("_binary_{base_name}_p3_start"); + extern const char p3_{base_name}_end[] asm("_binary_{base_name}_p3_end"); + static const std::string_view P3_{base_name.upper()} {{ + static_cast(p3_{base_name}_start), + static_cast(p3_{base_name}_end - p3_{base_name}_start) + }};''') # 填充模板 content = HEADER_TEMPLATE.format( lang_code=lang_code, - strings_view="\n".join(sorted(strings_view)), - strings_string="\n".join(sorted(strings_string)) + strings="\n".join(sorted(strings)), + sounds="\n".join(sorted(sounds)) ) # 写入文件