From 20ec4c7faa542f77b114059839ee298204eb6fc3 Mon Sep 17 00:00:00 2001 From: 1F616EMO Date: Sun, 29 Dec 2024 23:50:10 +0800 Subject: [PATCH] Init --- .luacheckrc | 85 +++++++++++++ README.md | 41 ++++++ babelfish_chat/init.lua | 65 ++++++++++ babelfish_chat/mod.conf | 5 + babelfish_chat_history/init.lua | 105 +++++++++++++++ babelfish_chat_history/mod.conf | 5 + babelfish_core/init.lua | 177 ++++++++++++++++++++++++++ babelfish_core/mod.conf | 3 + babelfish_engine_lingva/init.lua | 162 +++++++++++++++++++++++ babelfish_engine_lingva/mod.conf | 4 + babelfish_preferred_langauge/init.lua | 97 ++++++++++++++ babelfish_preferred_langauge/mod.conf | 4 + babelfish_private_chat/init.lua | 38 ++++++ babelfish_private_chat/mod.conf | 4 + modpack.conf | 2 + 15 files changed, 797 insertions(+) create mode 100644 .luacheckrc create mode 100644 README.md create mode 100644 babelfish_chat/init.lua create mode 100644 babelfish_chat/mod.conf create mode 100644 babelfish_chat_history/init.lua create mode 100644 babelfish_chat_history/mod.conf create mode 100644 babelfish_core/init.lua create mode 100644 babelfish_core/mod.conf create mode 100644 babelfish_engine_lingva/init.lua create mode 100644 babelfish_engine_lingva/mod.conf create mode 100644 babelfish_preferred_langauge/init.lua create mode 100644 babelfish_preferred_langauge/mod.conf create mode 100644 babelfish_private_chat/init.lua create mode 100644 babelfish_private_chat/mod.conf create mode 100644 modpack.conf diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..1ae46dc --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,85 @@ +read_globals = { + "DIR_DELIM", + "INIT", + + "core", + "dump", + "dump2", + + "Raycast", + "Settings", + "PseudoRandom", + "PerlinNoise", + "VoxelManip", + "SecureRandom", + "VoxelArea", + "PerlinNoiseMap", + "PcgRandom", + "ItemStack", + "AreaStore", + + "vector", + + table = { + fields = { + "copy", + "indexof", + "insert_all", + "key_value_swap", + "shuffle", + } + }, + + string = { + fields = { + "split", + "trim", + } + }, + + math = { + fields = { + "hypot", + "sign", + "factorial" + } + }, +} + +files["babelfish_core"] = { + globals = { + "babelfish", + } +} + +files["babelfish_engine_lingva"] = { + read_globals = { + "babelfish", + } +} + +files["babelfish_chat"] = { + read_globals = { + "babelfish", + "beerchat", + } +} + +files["babelfish_chat_history"] = { + read_globals = { + "babelfish", + "beerchat", + } +} + +files["babelfish_preferred_langauge"] = { + globals = { + "babelfish", + } +} + +files["babelfish_private_chat"] = { + read_globals = { + "babelfish", + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..381a0d1 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# BabelFish... but done in another way + +This mod allows Luanti players to communicate across language barriers. + +## ... but why not the original BabalFish mod? + +BabelFish was a great mod for breaking langauge barrier between players speaking different languages. However, it was unmaintained for 7 years, and many code became messy and inefficient. This rewrite is a drop-in replacement for most end users, and provides more method for developers to interact with BabelFish. Notable changes include: + +* Guessing preferred language from the player's client language code +* Handles [Beerchat](https://content.luanti.org/packages/mt-mods/beerchat/) properly +* Shipped with [Lingva Translate](https://github.com/thedaviddelta/lingva-translate) support (Yandex Translate port will be avaliable soon) +* Register new translation engine with new mods instead of adding files into the core mod + +## How to use? + +Avaliable in `babelfish_core` mod: + +* Use `/bbcodes` to list all avaliable languages and their alias. + +Avaliable in `babelfish_chat` mod: + +* Write `%` in a message to boardcast translation to other players + * e.g. "Hello %fr" would yield "Bonjour" + * Unlike the original BabelFish, you must leave spaces between the tag and other texts. + +Avaliable in `babelfish_preferred_langauge` mod: + +* Your preferred language is guessed when you first join the server. + * Fallbacks to English if your language is not supported. +* Use `/bblang ` to set your preferred language. +* Use `/bblang` to check your preferred language. + +Avaliable in `babelfish_chat_history` mod: + +* Use `/babel ` to translate the last message sent by a user +* (Beerchat only) Use `/babel ` to translate the last message sent by a user on a channel + * If channel is unspecified, defaults to the executer's channel. + +Avaliable in `babelfish_private_chat` + +* User `/bbmsg ` to send private messages to a player in their preferred language. diff --git a/babelfish_chat/init.lua b/babelfish_chat/init.lua new file mode 100644 index 0000000..b81f2a1 --- /dev/null +++ b/babelfish_chat/init.lua @@ -0,0 +1,65 @@ +-- babelfish_redo/babelfish_chat/init.lua +-- Translate by writing % +-- Copyright (C) 2016 Tai "DuCake" Kedzierski +-- Copyright (C) 2024 1F616EMO +-- SPDX-License-Identifier: AGPL-3.0-or-later + +local S = core.get_translator("babelfish_chat") + +local function check_message(message) + local _, _, targetlang = message:find("%%([a-zA-Z-_]+)") + if targetlang then + local targetphrase = message:gsub("%%" .. targetlang, '', 1) + local new_targetlang = babelfish.validate_language(targetlang) + + if not new_targetlang then + return false, targetlang + end + return new_targetlang, targetphrase + end + return false +end + +local dosend +local function process(name, message, arg1) + local targetlang, targetphrase = check_message(message) + if not targetlang then + if targetphrase == 1 then + return core.chat_send_player(name, S("@1 is not a valid language.", targetphrase)) + end + return + end + babelfish.translate("auto", targetlang, targetphrase, function(succeed, translated) + if not succeed then + if core.get_player_by_name(name) then + return core.chat_send_player(name, S("Could not translate message: @1", translated)) + end + return + end + + return dosend(name, translated, arg1) + end) +end + +if core.global_exists("beerchat") then + dosend = function(name, translated, channel) + return beerchat.send_on_channel({ + name = name, + channel = channel, + message = "[" .. babelfish.get_engine_label() .. "]: " .. translated, + _supress_babelfish_redo = true, + }) + end + beerchat.register_callback("before_send_on_channel", function(name, msg) + if msg._supress_babelfish_redo then return end + local message = msg.message + + return process(name, message, msg.channel) + end) +else + dosend = function(name, translated) + return core.chat_send_all(core.format_chat_message(name, + "[" .. babelfish.get_engine_label() .. "]: " .. translated)) + end + core.register_on_chat_message(process) +end diff --git a/babelfish_chat/mod.conf b/babelfish_chat/mod.conf new file mode 100644 index 0000000..15c071e --- /dev/null +++ b/babelfish_chat/mod.conf @@ -0,0 +1,5 @@ +name = babelfish_chat +title = Babelfish Redo: Chatroom Translation +description = Translate by writing % +depends = babelfish_core +optional_depends = beerchat diff --git a/babelfish_chat_history/init.lua b/babelfish_chat_history/init.lua new file mode 100644 index 0000000..b410196 --- /dev/null +++ b/babelfish_chat_history/init.lua @@ -0,0 +1,105 @@ +-- babelfish_redo/babelfish_chat_history/init.lua +-- Translate messages in chat history +-- Copyright (C) 2016 Tai "DuCake" Kedzierski +-- Copyright (C) 2024 1F616EMO +-- SPDX-License-Identifier: AGPL-3.0-or-later + +local S = core.get_translator("babelfish_chat_history") + +---@type { [string]: { [string]: string } } +local chat_history = {} + +local main_channel = "main" + +local function record_message(name, channel, message) + if not chat_history[channel] then + chat_history[channel] = {} + end + + chat_history[channel][name] = message +end + +core.register_on_leaveplayer(function(player) + local name = player:get_player_name() + for channel, chn_data in pairs(chat_history) do + chn_data[name] = nil + if channel ~= main_channel and not next(chn_data) then + chat_history[channel] = nil + end + end +end) + +local get_channel +local cmd_param +local is_player_subscribed_to_channel + +if core.global_exists("beerchat") then + main_channel = beerchat.main_channel_name + beerchat.register_callback("on_send_on_channel", function(name, msg) + record_message(name, msg.channel, msg.message) + end) + cmd_param = S(" []") + get_channel = function(name) + local channel = beerchat.get_player_channel(name) + if channel then + return channel + else + return beerchat.fix_player_channel(name, true) + end + end + is_player_subscribed_to_channel = beerchat.is_player_subscribed_to_channel +else + core.register_on_chat_message(function(name, message) + record_message(name, main_channel, message) + end) + cmd_param = S("") + get_channel = function() return main_channel end + is_player_subscribed_to_channel = function() return true end +end + +core.register_chatcommand("babel", { + description = S("Translate last message sent by a player"), + params = cmd_param, + func = function(name, param) + local player = core.get_player_by_name(name) + if not player then + return false, S("You must be online to run this command.") + end + + local target_lang = babelfish.get_player_preferred_language(name) + if not target_lang then + return false, S("Error while obtaining default language.") + end + + local args = string.split(param, " ") + if not args[1] then + return false + end + + local target_player, channel = args[1], args[2] or get_channel(name) + if not channel then + return false, S("Failed to get channel.") + end + + if not is_player_subscribed_to_channel(name, channel) then + return false, S("You are not allowed to read messages from channel #@1!", channel) + end + + if not (chat_history[channel] and chat_history[channel][target_player]) then + return false, S("@1 haven't sent anythign on @2.", + target_player, channel == main_channel and S("the main channel") or ("#" .. channel)) + end + + babelfish.translate("auto", target_lang, chat_history[channel][target_player], function(succeeded, translated) + if not core.get_player_by_name(name) then return end + + if not succeeded then + return core.chat_send_player(name, S("Failed to get translation: @1", translated)) + end + + return core.chat_send_player(name, + "[" .. babelfish.get_engine_label() .. " #" .. channel .. " " .. target_player .. "]: " .. translated) + end) + return true + end, +}) diff --git a/babelfish_chat_history/mod.conf b/babelfish_chat_history/mod.conf new file mode 100644 index 0000000..9bade3a --- /dev/null +++ b/babelfish_chat_history/mod.conf @@ -0,0 +1,5 @@ +name = babelfist_chat_history +title = Babelfish Redo: Chat History +description = Translate messages in chat history +depends = babelfish_core, babelfish_preferred_language +optional_depends = beerchat diff --git a/babelfish_core/init.lua b/babelfish_core/init.lua new file mode 100644 index 0000000..4767d62 --- /dev/null +++ b/babelfish_core/init.lua @@ -0,0 +1,177 @@ +-- babelfish_redo/babelfish_core/init.lua +-- High leve API for translating texts using one of the Babelfish enginess +-- Copyright (C) 2016 Tai "DuCake" Kedzierski +-- Copyright (C) 2024 1F616EMO +-- SPDX-License-Identifier: AGPL-3.0-or-later + +local S = core.get_translator("babelfish_core") + +babelfish = {} + +local registered_on_engine_ready = {} + +---Run function when the engine is ready, or if it is already ready, run it now. +---@param func fun() +function babelfish.register_on_engine_ready(func) + if not registered_on_engine_ready then + return func() + end + registered_on_engine_ready[#registered_on_engine_ready+1] = func +end + +---@alias BabelFishCallback fun(succeed: boolean, string_or_err: string) + +---@class (exact) BabelFishEngine +---@field translate fun(source: string, target: string, query: string, callback: BabelFishCallback) +---@field language_codes { [string]: string } +---@field language_alias { [string]: string }? +---@field mt_language_map { [string] : string }? +---@field compliance string? +---@field engine_label string? +local babelfish_engine + +---Register a translate engine +---@param engine_def BabelFishEngine +function babelfish.register_engine(engine_def) + local mod_name = core.get_current_modname() or "??" + assert(type(engine_def) == "table", + "Invalid `engine_def` type (expected table, got " .. type(engine_def) .. ")") + assert(type(engine_def.translate) == "function", + "Invalid `engine_def.translate` type (expected function, got " .. type(engine_def.translate) .. ")") + assert(type(engine_def.language_codes) == "table", + "Invalid `engine_def.language_codes` type (expected table, got " .. type(engine_def.language_codes) .. ")") + if engine_def.language_alias == nil then + engine_def.language_alias = {} + else + assert(type(engine_def.language_alias) == "table", + "Invalid `engine_def.language_alias` type (expected table or nil, got " + .. type(engine_def.language_alias) .. ")") + end + if engine_def.mt_language_map == nil then + engine_def.mt_language_map = {} + else + assert(type(engine_def.mt_language_map) == "table", + "Invalid `engine_def.mt_language_map` type (expected table or nil, got " + .. type(engine_def.mt_language_map) .. ")") + end + if engine_def.engine_label == nil then + engine_def.engine_label = mod_name + else + assert(type(engine_def.engine_label) == "string", + "Invalid `engine_def.engine_label` type (expected string or nil, got " + .. type(engine_def.engine_label) .. ")") + end + if engine_def.compliance == nil then + engine_def.compliance = S("Translations are powered by @1", engine_def.engine_label) + else + assert(type(engine_def.compliance) == "string", + "Invalid `engine_def.compliance` type (expected string or nil, got " + .. type(engine_def.compliance) .. ")") + end + + engine_def.mod_name = mod_name + babelfish_engine = engine_def + babelfish.register_engine = function() + return error("[babelfish_core] Attempt to registered more than one BabelFish engine " + .. "(already registered by " .. engine_def.mod_name .. ")") + end + + for _, func in ipairs(registered_on_engine_ready) do + func() + end + registered_on_engine_ready = nil +end + +core.register_on_mods_loaded(function() + if not babelfish_engine then + return error("[babelfish_core] Please enable one (and only one) BabelFish engine mod.") + end +end) + +---Translate a given text +---@param source string Source language code. If `"auto"`, detect the language automatically. +---@param target string Target language code. +---@param query string String to translate. +---@param callback BabelFishCallback Callback to run after finishing (or failing) a request +function babelfish.translate(source, target, query, callback) + assert(type(source) == "string", + "Invalid `source` type (expected string or nil, got " .. type(source) .. ")") + assert(type(target) == "string", + "Invalid `target` type (expected string, got " .. type(target) .. ")") + assert(type(query) == "string", + "Invalid `query` type (expected string, got " .. type(query) .. ")") + + assert(source == "auto" or babelfish_engine.language_codes[source], + "Attempt to translate from unsupported language " .. source) + + assert(babelfish_engine.language_codes[target], + "Attempt to translate from unsupported language " .. target) + + return babelfish_engine.translate(source, target, query, callback) +end + +---Check whether a given language code is valid, and resolve any alias +---@param language string? +---@return string +---@nodiscard +function babelfish.validate_language(language) + if language == nil then + return "auto" + end + language = babelfish_engine.language_alias[language] or language + return babelfish_engine.language_codes[language] and language or nil +end + +---Get name of a language +---@param language string +---@return string? +function babelfish.get_language_name(language) + if language == "auto" then + return S("Detect automatically") + end + return babelfish_engine.language_codes[language] +end + +---Get language map: MT language code -> engine lanaguage code +---@return { [string]: string } +function babelfish.get_mt_language_map() + return table.copy(babelfish_engine.mt_language_map) +end + +---Get language codes +---@return { [string]: string } +function babelfish.get_language_codes() + return table.copy(babelfish_engine.language_codes) +end + +---Get engine compliance +---@return string +function babelfish.get_compliance() + return babelfish_engine.compliance +end + +---Get engine engine_label +---@return string +function babelfish.get_engine_label() + return babelfish_engine.engine_label +end + +core.register_chatcommand("bbcodes", { + description = S("List avaliable language codes"), + func = function () + local lines = {} + for code, name in pairs(babelfish_engine.language_codes) do + lines[#lines+1] = code .. ": " .. name + local alias = {} + for src, dst in pairs(babelfish_engine.language_alias) do + if dst == code then + alias[#alias+1] = src + end + end + if #alias ~= 0 then + lines[#lines] = lines[#lines] .. " " .. S("(Alias: @1)", table.concat(alias, ", ")) + end + end + return true, table.concat(lines, "\n") + end +}) diff --git a/babelfish_core/mod.conf b/babelfish_core/mod.conf new file mode 100644 index 0000000..9c08d21 --- /dev/null +++ b/babelfish_core/mod.conf @@ -0,0 +1,3 @@ +name = babelfish_core +title = Babelfish Redo: Core +description = High leve API for translating texts using one of the Babelfish engines diff --git a/babelfish_engine_lingva/init.lua b/babelfish_engine_lingva/init.lua new file mode 100644 index 0000000..6a394c5 --- /dev/null +++ b/babelfish_engine_lingva/init.lua @@ -0,0 +1,162 @@ +-- babelfish_redo/babelfish_engine_lingva/init.lua +-- Google Translate via the Lingva frontend +-- Copyright (C) 2016 Tai "DuCake" Kedzierski +-- Copyright (C) 2024 1F616EMO +-- SPDX-License-Identifier: AGPL-3.0-or-later + +local http = assert(core.request_http_api(), + "Could not get HTTP API table. Add babelfish_engine_lingva to secure.http_mods") + +local S = core.get_translator("babelfish_engine_lingva") + +local engine_status = "init" +local language_codes = {} +local language_alias = {} + +local serviceurl = core.settings:get("babelfish_engine_lingva.serviceurl") +if not serviceurl then + serviceurl = "https://lingva.ml/api/graphql" + core.log("warning", + "[babelfish_engine_lingva] babelfish_engine_lingva.serviceurl not specified, " .. + "using official instance (https://lingva.ml/api/graphql)") +end + +local function graphql_fetch(query, func) + return http.fetch({ + url = serviceurl, + method = "POST", + timeout = 10, + extra_headers = { "accept: application/graphql-response+json;charset=utf-8, application/json;charset=utf-8" }, + post_data = core.write_json({ + query = query + }), + }, function(responce) + if not responce.succeeded then + core.log("error", "[babelfish_engine_lingva] Error on requesting " .. query .. ": " .. dump(responce)) + return func(false) + end + + local data, err = core.parse_json(responce.data, nil, true) + if not data then + core.log("error", "[babelfish_engine_lingva] Error on requesting " .. query .. ": " .. err) + core.log("error", "[babelfish_engine_lingva] Raw data: " .. responce.data) + return func(false) + end + + if data.errors then + core.log("error", "[babelfish_engine_lingva] Error on requesting " .. query .. ": ") + for i, error in ipairs(data.errors) do + local location_string = "?" + if error.locations then + local location_strings = {} + for _, location in ipairs(error.locations) do + location_strings[#location_strings + 1] = location.line .. ":" .. location.column + end + location_string = table.concat(location_strings, ", ") + end + core.log("error", string.format("[babelfish_engine_lingva] (%d/%d) Line(s) %s: %s (%s)", + i, #data.errors, + location_string, error.message, error.extensions and error.extensions.code or "UNKNOWN")) + + if error.extensions and error.extensions.stacktrace then + core.log("error", "[babelfish_engine_lingva]Stacktrace:") + for _, line in ipairs(error.extensions.stacktrace) do + core.log("error", "[babelfish_engine_lingva] \t" .. line) + end + end + end + end + + if not data.data then + return func(false) + end + + return func(data.data) + end) +end + +do + local valid_alias = { + ["zh_HANT"] = { + "zht", + "zh-tw", + "zh-hant", + }, + ["zh"] = { + "zhs", + "zh-cn", + "zh-hans", + }, + } + + graphql_fetch("{languages{code,name}}", function(data) + if not data then + engine_status = "error" + return + end + + local langs_got = {} + local alias_log_strings = {} + -- We assume all langauge supports bidirectional translation + for _, langdata in ipairs(data.languages) do + if langdata.code ~= "auto" then + language_codes[langdata.code] = langdata.name + langs_got[#langs_got + 1] = langdata.code + + if valid_alias[langdata.code] then + for _, alias in ipairs(valid_alias[langdata.code]) do + language_alias[alias] = langdata.code + alias_log_strings[#alias_log_strings + 1] = + alias .. " -> " .. langdata.code + end + end + end + end + core.log("action", "[babelfish_engine_lingva] Got language list: " .. table.concat(langs_got, ", ")) + core.log("action", "[babelfish_engine_lingva] Got language alias: " .. table.concat(alias_log_strings, "; ")) + engine_status = "ready" + end) +end + +---Function for translating a given text +---@param source string Source language code. If `"auto"`, detect the language automatically. +---@param target string Target language code. +---@param query string String to translate. +---@param callback BabelFishCallback Callback to run after finishing (or failing) a request +local function translate(source, target, query, callback) + if engine_status == "error" then + return callback(false, S("Engine error while initializing.")) + elseif engine_status == "init" then + return callback(false, S("Engine not yet initialized.")) + end + + query = string.gsub(query, "\"", "\\\"") + graphql_fetch( + "{translation(source: \"" .. source .. "\", target: \"" .. target .. + "\", query: \"" .. query .. "\"){target{text}}}", + function(data) + if data then + return callback(true, data.translation.target.text) + end + return callback(false, S("Error getting translation")) + end) +end + +local mt_language_map = { + ["es_US"] = "es", + ["lzh"] = "zh_HANT", + ["zh_CN"] = "zh", + ["zh_TW"] = "zh_HANT", + ["sr_Cyrl"] = "sr", + ["sr_Latn"] = "sr", +} + +babelfish.register_engine({ + translate = translate, + language_codes = language_codes, + language_alias = language_alias, + mt_language_map = mt_language_map, + + compliance = nil, -- S("Translations are powered by Lingva"), + engine_label = "Lingva Translate", +}) \ No newline at end of file diff --git a/babelfish_engine_lingva/mod.conf b/babelfish_engine_lingva/mod.conf new file mode 100644 index 0000000..47b3cee --- /dev/null +++ b/babelfish_engine_lingva/mod.conf @@ -0,0 +1,4 @@ +name = babelfish_engine_lingva +title = Babelfish Redo: Lingva Engine +description = Google Translate via the Lingva frontend +depends = babelfish_core \ No newline at end of file diff --git a/babelfish_preferred_langauge/init.lua b/babelfish_preferred_langauge/init.lua new file mode 100644 index 0000000..852686d --- /dev/null +++ b/babelfish_preferred_langauge/init.lua @@ -0,0 +1,97 @@ +-- babelfish_redo/babelfish_preferred_language/init.lua +-- Set and get player preferred languages +-- Copyright (C) 2016 Tai "DuCake" Kedzierski +-- Copyright (C) 2024 1F616EMO +-- SPDX-License-Identifier: AGPL-3.0-or-later + +local S = core.get_translator("babelfish_preferred_language") + +local language_map +local fallback_lang +babelfish.register_on_engine_ready(function() + language_map = babelfish.get_mt_language_map() + + local settings_fallback_lang = core.settings:get("babelfish_preferred_language.fallback_lang") + fallback_lang = babelfish.validate_language(settings_fallback_lang) + if not fallback_lang or fallback_lang == "auto" then + core.log("error", "Invalid fallback language, using en") + fallback_lang = "en" -- out last hope + end +end) + +---Guess the player's preferred language from player information +---@param name string +---@return string +function babelfish.guess_player_preferred_language(name) + local player_info = core.get_player_information(name) + if not player_info then return fallback_lang end + + local lang_code = player_info.lang_code + lang_code = language_map[lang_code] or lang_code + lang_code = babelfish.validate_language(lang_code) + + if not lang_code or lang_code == "auto" then + return fallback_lang + end + return lang_code +end + +---Get a player's preferred lanaguage +---@param name string +---@return string +function babelfish.get_player_preferred_language(name) + local player = core.get_player_by_name(name) + if not player then return end + + local meta = player:get_meta() + local preferred_language = meta:get_string("babelfish:preferred_language") + preferred_language = babelfish.validate_language(preferred_language) + + if not preferred_language or preferred_language == "auto" then + preferred_language = babelfish.guess_player_preferred_language(name) + if not preferred_language then return end + meta:set_string("babelfish:preferred_language", preferred_language) + end + + return preferred_language +end + +---Set a player's preferred language +---@param name string +---@param lang string +function babelfish.set_player_preferred_languag(name, lang) + local player = core.get_player_by_name(name) + if not player then return end + + local meta = player:get_meta() + return meta:set_string("babelfish:preferred_language", lang) +end + +core.register_on_joinplayer(function(player) + -- Beautiful hack to update or generate preferred language + return babelfish.get_player_preferred_language(player:get_player_name()) +end) + +core.register_chatcommand("bblang", { + descriptio = S("Get or set preferred language"), + params = S("[]"), + func = function(name, param) + if param == "" then + local lang = babelfish.get_player_preferred_language(name) + return true, S("Preferred language: @1", lang and babelfish.get_language_name(lang) or S("Unknown")) + end + + local lang = babelfish.validate_language(param) + if not lang or lang == "auto" then + return false, S("Invalid language code: @1", param) + end + + local player = core.get_player_by_name(name) + if not player then + return false, S("You must be online to run this command.") + end + + babelfish.set_player_preferred_languag(name, lang) + return true, S("Preferred language set to @1.", babelfish.get_language_name(lang)) + end, +}) diff --git a/babelfish_preferred_langauge/mod.conf b/babelfish_preferred_langauge/mod.conf new file mode 100644 index 0000000..1524abe --- /dev/null +++ b/babelfish_preferred_langauge/mod.conf @@ -0,0 +1,4 @@ +name = babelfish_preferred_language +title = Babelfish Redo: Preferred Language +description = Set and get player preferred languages +depends = babelfish_core diff --git a/babelfish_private_chat/init.lua b/babelfish_private_chat/init.lua new file mode 100644 index 0000000..d7827f8 --- /dev/null +++ b/babelfish_private_chat/init.lua @@ -0,0 +1,38 @@ +-- babelfish_redo/babelfish_private_chat/init.lua +-- Translate private chats +-- Copyright (C) 2016 Tai "DuCake" Kedzierski +-- Copyright (C) 2024 1F616EMO +-- SPDX-License-Identifier: AGPL-3.0-or-later + +local S = core.get_translator("babelfish_private_chat") + +core.register_chatcommand("bbmsg", { + params = core.translate("__builtin", " "), + description = S("Send a direct message to a player in their preferred langauge"), + privs = { shout = true }, + func = function(name, param) + local sendto, message = param:match("^(%S+)%s(.+)$") + if not sendto then + return false + end + if not core.get_player_by_name(sendto) then + return false, core.translate("__builtin", "The player @1 is not online.", sendto) + end + local target_lang = babelfish.get_player_preferred_language(sendto) + babelfish.translate("auto", target_lang, message, function(succeeded, translated) + if not succeeded then + if core.get_player_by_name(name) then + return core.chat_send_player(name, S("Failed to get translation.")) + end + return + end + + core.log("action", "DM from " .. name .. " to " .. sendto + .. ": " .. translated) + core.chat_send_player(sendto, core.translate("__builtin", "DM from @1: @2", + name, "[" .. babelfish.get_engine_label() .. "]: " .. translated)) + end) + + return true, core.translate("__builtin", "Message sent.") + end, +}) diff --git a/babelfish_private_chat/mod.conf b/babelfish_private_chat/mod.conf new file mode 100644 index 0000000..9e37b89 --- /dev/null +++ b/babelfish_private_chat/mod.conf @@ -0,0 +1,4 @@ +name = babelfish_private_chat +title = Babelfish Redo: Private Chat +description = Translate private chats +depends = babelfish_core, babelfish_preferred_language \ No newline at end of file diff --git a/modpack.conf b/modpack.conf new file mode 100644 index 0000000..9992eaf --- /dev/null +++ b/modpack.conf @@ -0,0 +1,2 @@ +title = Babelfish Redo +description = Translate chat messages into other languages