Init
This commit is contained in:
commit
20ec4c7faa
85
.luacheckrc
Normal file
85
.luacheckrc
Normal file
@ -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",
|
||||
}
|
||||
}
|
41
README.md
Normal file
41
README.md
Normal file
@ -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 `%<language code>` 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 <language code>` to set your preferred language.
|
||||
* Use `/bblang` to check your preferred language.
|
||||
|
||||
Avaliable in `babelfish_chat_history` mod:
|
||||
|
||||
* Use `/babel <username>` to translate the last message sent by a user
|
||||
* (Beerchat only) Use `/babel <username> <channel>` 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 <username> <message>` to send private messages to a player in their preferred language.
|
65
babelfish_chat/init.lua
Normal file
65
babelfish_chat/init.lua
Normal file
@ -0,0 +1,65 @@
|
||||
-- babelfish_redo/babelfish_chat/init.lua
|
||||
-- Translate by writing %<code>
|
||||
-- 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
|
5
babelfish_chat/mod.conf
Normal file
5
babelfish_chat/mod.conf
Normal file
@ -0,0 +1,5 @@
|
||||
name = babelfish_chat
|
||||
title = Babelfish Redo: Chatroom Translation
|
||||
description = Translate by writing %<code>
|
||||
depends = babelfish_core
|
||||
optional_depends = beerchat
|
105
babelfish_chat_history/init.lua
Normal file
105
babelfish_chat_history/init.lua
Normal file
@ -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("<player name> [<channel name>]")
|
||||
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("<player name>")
|
||||
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,
|
||||
})
|
5
babelfish_chat_history/mod.conf
Normal file
5
babelfish_chat_history/mod.conf
Normal file
@ -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
|
177
babelfish_core/init.lua
Normal file
177
babelfish_core/init.lua
Normal file
@ -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
|
||||
})
|
3
babelfish_core/mod.conf
Normal file
3
babelfish_core/mod.conf
Normal file
@ -0,0 +1,3 @@
|
||||
name = babelfish_core
|
||||
title = Babelfish Redo: Core
|
||||
description = High leve API for translating texts using one of the Babelfish engines
|
162
babelfish_engine_lingva/init.lua
Normal file
162
babelfish_engine_lingva/init.lua
Normal file
@ -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",
|
||||
})
|
4
babelfish_engine_lingva/mod.conf
Normal file
4
babelfish_engine_lingva/mod.conf
Normal file
@ -0,0 +1,4 @@
|
||||
name = babelfish_engine_lingva
|
||||
title = Babelfish Redo: Lingva Engine
|
||||
description = Google Translate via the Lingva frontend
|
||||
depends = babelfish_core
|
97
babelfish_preferred_langauge/init.lua
Normal file
97
babelfish_preferred_langauge/init.lua
Normal file
@ -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("[<language code>]"),
|
||||
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,
|
||||
})
|
4
babelfish_preferred_langauge/mod.conf
Normal file
4
babelfish_preferred_langauge/mod.conf
Normal file
@ -0,0 +1,4 @@
|
||||
name = babelfish_preferred_language
|
||||
title = Babelfish Redo: Preferred Language
|
||||
description = Set and get player preferred languages
|
||||
depends = babelfish_core
|
38
babelfish_private_chat/init.lua
Normal file
38
babelfish_private_chat/init.lua
Normal file
@ -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", "<name> <message>"),
|
||||
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,
|
||||
})
|
4
babelfish_private_chat/mod.conf
Normal file
4
babelfish_private_chat/mod.conf
Normal file
@ -0,0 +1,4 @@
|
||||
name = babelfish_private_chat
|
||||
title = Babelfish Redo: Private Chat
|
||||
description = Translate private chats
|
||||
depends = babelfish_core, babelfish_preferred_language
|
2
modpack.conf
Normal file
2
modpack.conf
Normal file
@ -0,0 +1,2 @@
|
||||
title = Babelfish Redo
|
||||
description = Translate chat messages into other languages
|
Loading…
x
Reference in New Issue
Block a user