Implemented a basic translation system

This commit is contained in:
Andrew
2026-01-26 15:43:15 +03:00
committed by dec05eba
parent 3a57167d54
commit 03cacfdbf5
5 changed files with 145 additions and 0 deletions

36
include/Translation.hpp Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include <unordered_map>
namespace gsr {
class Translation {
public:
static Translation& instance();
void init(const char* translations_directory, const char* initial_language = nullptr);
bool load_language(const char* lang);
const char* translate(const char* key);
template<typename... Args>
std::string format(const char* key, Args&&... args) {
const char* fmt = translate(key);
// result buffer
char buffer[4096];
snprintf(buffer, sizeof(buffer), fmt, std::forward<Args>(args)...);
return std::string(buffer);
}
private:
std::string get_system_language();
std::string trim(const std::string& str);
private:
std::string translations_directory;
std::string current_language = "en";
std::unordered_map<std::string, std::string> translations;
};
}
#define TR(s) gsr::Translation::instance().translate(s)
#define TRF(s, ...) gsr::Translation::instance().format(s, __VA_ARGS__)

View File

@@ -55,6 +55,7 @@ src = [
'src/ClipboardFile.cpp',
'src/LedIndicator.cpp',
'src/Rpc.cpp',
'src/Translation.cpp',
'src/main.cpp',
]
@@ -142,6 +143,7 @@ executable(
install_subdir('images', install_dir : gsr_ui_resources_path)
install_subdir('fonts', install_dir : gsr_ui_resources_path)
install_subdir('translations', install_dir : gsr_ui_resources_path)
if get_option('desktop-files') == true
install_data(files('gpu-screen-recorder.desktop'), install_dir : join_paths(prefix, datadir, 'applications'))

101
src/Translation.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include "../include/Translation.hpp"
#include <cstring>
#include <unordered_map>
#include <fstream>
namespace gsr {
std::string Translation::get_system_language() {
const char* lang = getenv("LANGUAGE");
if (!lang || !lang[0]) lang = getenv("LC_ALL");
if (!lang || !lang[0]) lang = getenv("LC_MESSAGES");
if (!lang || !lang[0]) lang = getenv("LANG");
if (lang && lang[0]) {
std::string lang_str(lang);
// we usually need only two symbols
size_t underscore = lang_str.find('_');
if (underscore != std::string::npos) {
return lang_str.substr(0, underscore);
}
size_t dot = lang_str.find('.');
if (dot != std::string::npos) {
return lang_str.substr(0, dot);
}
return lang_str;
}
return "en";
}
std::string Translation::trim(const std::string& str) {
size_t start = str.find_first_not_of(" \t\r\n");
if (start == std::string::npos) return "";
size_t end = str.find_last_not_of(" \t\r\n");
return str.substr(start, end - start + 1);
}
bool Translation::load_language(const char* lang) {
translations.clear();
current_language = lang;
if (strcmp(lang, "en") == 0) {
return true; // english is the base
}
std::string paths[] = {
std::string("translations/") + lang + ".txt",
std::string(this->translations_directory) + lang + ".txt"
};
for (const auto& path : paths) {
std::ifstream file(path);
if (!file.is_open()) continue;
std::string line;
while (std::getline(file, line)) {
line = trim(line);
if (line.empty() || line[0] == '#') continue;
size_t eq_pos = line.find('=');
if (eq_pos == std::string::npos) continue;
std::string key = trim(line.substr(0, eq_pos));
std::string value = trim(line.substr(eq_pos + 1));
size_t pos = 0;
while ((pos = value.find("\\n", pos)) != std::string::npos) {
value.replace(pos, 2, "\n");
pos += 1;
}
translations[key] = value;
}
return true;
}
fprintf(stderr, "Warning: translation file for '%s' not found\n", lang);
return false;
}
Translation& Translation::instance() {
static Translation tr;
return tr;
}
void Translation::init(const char* translations_directory, const char* initial_language) {
this->translations_directory = translations_directory;
load_language(initial_language == nullptr ? get_system_language().c_str() : initial_language);
}
const char* Translation::translate(const char* key) {
auto it = translations.find(key);
if (it != translations.end()) {
return it->second.c_str();
}
return key; // falling back if nothing found
}
}

View File

@@ -4,6 +4,7 @@
#include "../include/Process.hpp"
#include "../include/Rpc.hpp"
#include "../include/Theme.hpp"
#include "../include/Translation.hpp"
#include <signal.h>
#include <string.h>

5
translations/ru.txt Normal file
View File

@@ -0,0 +1,5 @@
# Testing formatted translations
Test of %s and %s=Тест %1$s и %2$s
# General
Record=Запись