Add JSON language support for sysmodule notifications.
Load Ultrahand locale translations from /config/horizon-oc/lang so user-facing hoc-clk alerts match the overlay language.
This commit is contained in:
141
Source/hoc-clk/sysmodule/src/hos/lang.cpp
Normal file
141
Source/hoc-clk/sysmodule/src/hos/lang.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lang.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <minIni.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../file/file_utils.hpp"
|
||||
|
||||
namespace lang {
|
||||
namespace {
|
||||
|
||||
constexpr const char *kUltrahandConfigPath = "sdmc:/config/ultrahand/config.ini";
|
||||
constexpr const char *kLangDir = "sdmc:" FILE_CONFIG_DIR "/lang/";
|
||||
|
||||
std::string gLoadedLang;
|
||||
std::unordered_map<std::string, std::string> gCache;
|
||||
|
||||
void NormalizeNewlines(std::string &s) {
|
||||
size_t n = 0;
|
||||
while ((n = s.find("\\n", n)) != std::string::npos) {
|
||||
s.replace(n, 2, "\n");
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Lightweight Ultrahand-compatible flat JSON string map parser.
|
||||
void ParseJsonContent(const std::string &content, std::unordered_map<std::string, std::string> &result) {
|
||||
size_t pos = 0;
|
||||
|
||||
while ((pos = content.find('"', pos)) != std::string::npos) {
|
||||
const size_t keyStart = pos + 1;
|
||||
const size_t keyEnd = content.find('"', keyStart);
|
||||
if (keyEnd == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
const size_t colonPos = content.find(':', keyEnd);
|
||||
if (colonPos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
const size_t valueStart = content.find('"', colonPos);
|
||||
const size_t valueEnd = content.find('"', valueStart + 1);
|
||||
if (valueStart == std::string::npos || valueEnd == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string key = content.substr(keyStart, keyEnd - keyStart);
|
||||
std::string value = content.substr(valueStart + 1, valueEnd - valueStart - 1);
|
||||
|
||||
NormalizeNewlines(key);
|
||||
NormalizeNewlines(value);
|
||||
|
||||
result[std::move(key)] = std::move(value);
|
||||
pos = valueEnd + 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadFileContent(const std::string &filePath, std::string &content) {
|
||||
FILE *file = fopen(filePath.c_str(), "r");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
while (fgets(buffer, sizeof(buffer), file) != nullptr) {
|
||||
content += buffer;
|
||||
}
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadLangFile(const std::string &langCode) {
|
||||
const std::string path = std::string(kLangDir) + langCode + ".json";
|
||||
std::string content;
|
||||
if (!ReadFileContent(path, content)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gCache.clear();
|
||||
ParseJsonContent(content, gCache);
|
||||
gLoadedLang = langCode;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetDefaultLang() {
|
||||
char buf[32] = {};
|
||||
ini_gets("ultrahand", "default_lang", "en", buf, sizeof(buf), kUltrahandConfigPath);
|
||||
if (buf[0] == '\0') {
|
||||
return "en";
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void EnsureLoaded() {
|
||||
const std::string langCode = GetDefaultLang();
|
||||
if (langCode == gLoadedLang && !gCache.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LoadLangFile(langCode) && langCode != "en") {
|
||||
LoadLangFile("en");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string translate(const std::string &text) {
|
||||
if (text.empty()) {
|
||||
return text;
|
||||
}
|
||||
|
||||
EnsureLoaded();
|
||||
|
||||
const auto it = gCache.find(text);
|
||||
if (it != gCache.end() && !it->second.empty()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
} // namespace lang
|
||||
29
Source/hoc-clk/sysmodule/src/hos/lang.hpp
Normal file
29
Source/hoc-clk/sysmodule/src/hos/lang.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace lang {
|
||||
|
||||
// Translates text using Ultrahand's language setting and
|
||||
// /config/horizon-oc/lang/<lang>.json (same format as the overlay).
|
||||
// Returns the original text when no translation is available.
|
||||
std::string translate(const std::string &text);
|
||||
|
||||
} // namespace lang
|
||||
@@ -15,6 +15,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lang.hpp"
|
||||
#include "notification.h"
|
||||
|
||||
namespace notification {
|
||||
@@ -27,13 +28,15 @@ namespace notification {
|
||||
}
|
||||
fclose(flagFile);
|
||||
|
||||
const std::string translated = lang::translate(message);
|
||||
|
||||
std::string filename = "hoc-" + std::to_string(std::time(nullptr)) + ".notify";
|
||||
std::string fullPath = "sdmc:/config/ultrahand/notifications/" + filename;
|
||||
|
||||
FILE *file = fopen(fullPath.c_str(), "w");
|
||||
if (file) {
|
||||
fprintf(file, "{\n");
|
||||
fprintf(file, " \"text\": \"%s\",\n", message.c_str());
|
||||
fprintf(file, " \"text\": \"%s\",\n", translated.c_str());
|
||||
fprintf(file, " \"fontSize\": 28\n");
|
||||
fprintf(file, "}\n");
|
||||
fclose(file);
|
||||
|
||||
Reference in New Issue
Block a user