Files
wnsrc/gamemodes/terrortown/gamemode/cl_lang.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

379 lines
10 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
---- Clientside language stuff
-- Need to build custom tables of strings. Can't use language.Add as there is no
-- way to access the translated string in Lua. Identifiers only get translated
-- when Source/gmod print them. By using our own table and our own lookup, we
-- have far more control. Maybe it's slower, but maybe not, we aren't scanning
-- strings for "#identifiers" after all.
LANG.Strings = {}
local ttt_language = CreateConVar("ttt_language", "auto", FCVAR_ARCHIVE)
LANG.DefaultLanguage = "english"
LANG.ActiveLanguage = LANG.DefaultLanguage
LANG.ServerLanguage = "english"
local cached_default, cached_active
function LANG.CreateLanguage(raw_lang_name)
if not raw_lang_name then return end
lang_name = string.lower(raw_lang_name)
if not LANG.IsLanguage(lang_name) then
LANG.Strings[lang_name] = {
-- Empty string is very convenient to have, so init with that.
[""] = "",
-- Presentational, not-lowercased language name
language_name = raw_lang_name
}
end
if lang_name == LANG.DefaultLanguage then
cached_default = LANG.Strings[lang_name]
-- when a string is not found in the active or the default language, an
-- error message is shown
setmetatable(LANG.Strings[lang_name],
{
__index = function(tbl, name)
return Format("[ERROR: Translation of %s not found]", name), false
end
})
end
return LANG.Strings[lang_name]
end
-- Add a string to a language. Should not be used in a language file, only for
-- adding strings elsewhere, such as a SWEP script.
function LANG.AddToLanguage(lang_name, string_name, string_text)
lang_name = lang_name and string.lower(lang_name)
if not LANG.IsLanguage(lang_name) then
ErrorNoHalt(Format("Failed to add '%s' to language '%s': language does not exist.\n", tostring(string_name), tostring(lang_name)))
return false
end
LANG.Strings[lang_name][string_name] = string_text
return string_name
end
-- Simple and fastest name->string lookup
function LANG.GetTranslation(name)
return cached_active[name]
end
-- Lookup with no error upon failback, just nil. Slightly slower, but best way
-- to handle lookup of strings that may legitimately fail to exist
-- (eg. SWEP-defined).
function LANG.GetRawTranslation(name)
return rawget(cached_active, name) or rawget(cached_default, name)
end
-- A common idiom
local GetRaw = LANG.GetRawTranslation
function LANG.TryTranslation(name)
return GetRaw(name) or name
end
local interp = string.Interp
-- Parameterised version, performs string interpolation. Slower than
-- GetTranslation.
function LANG.GetParamTranslation(name, params)
return interp(cached_active[name], params)
end
LANG.GetPTranslation = LANG.GetParamTranslation
function LANG.GetTranslationFromLanguage(name, lang_name)
return rawget(LANG.Strings[lang_name], name)
end
-- Ability to perform lookups in the current language table directly is of
-- interest to consumers in draw/think hooks. Grabbing a translation directly
-- from the table is very fast, and much simpler than a local caching solution.
-- Modifying it would typically be a bad idea.
function LANG.GetUnsafeLanguageTable() return cached_active end
function LANG.GetUnsafeNamed(name) return LANG.Strings[name] end
-- Safe and slow access, not sure if it's ever useful.
function LANG.GetLanguageTable(lang_name)
lang_name = lang_name or LANG.ActiveLanguage
local cpy = table.Copy(LANG.Strings[lang_name])
SetFallback(cpy)
return cpy
end
local function SetFallback(tbl)
-- languages may deal with this themselves, or may already have the fallback
local m = getmetatable(tbl)
if m and m.__index then return end
-- Set the __index of the metatable to use the default lang, which makes any
-- keys not found in the table to be looked up in the default. This is faster
-- than using branching ("return lang[x] or default[x] or errormsg") and
-- allows fallback to occur even when consumer code directly accesses the
-- lang table for speed (eg. in a rendering hook).
setmetatable(tbl,
{
__index = cached_default
})
end
function LANG.SetActiveLanguage(lang_name)
lang_name = lang_name and string.lower(lang_name)
if LANG.IsLanguage(lang_name) then
local old_name = LANG.ActiveLanguage
LANG.ActiveLanguage = lang_name
-- cache ref to table to avoid hopping through LANG and Strings every time
cached_active = LANG.Strings[lang_name]
-- set the default lang as fallback, if it hasn't yet
SetFallback(cached_active)
-- some interface elements will want to know so they can update themselves
if old_name != lang_name then
hook.Call("TTTLanguageChanged", GAMEMODE, old_name, lang_name)
end
else
MsgN(Format("The language '%s' does not exist on this server. Falling back to English...", lang_name))
-- fall back to default if possible
if lang_name != LANG.DefaultLanguage then
LANG.SetActiveLanguage(LANG.DefaultLanguage)
end
end
end
function LANG.Init()
local lang_name = ttt_language:GetString()
-- if we want to use the server language, we'll be switching to it as soon as
-- we hear from the server which one it is, for now use default
if LANG.IsServerDefault(lang_name) then
lang_name = LANG.ServerLanguage
end
LANG.SetActiveLanguage(lang_name)
end
function LANG.IsServerDefault(lang_name)
lang_name = string.lower(lang_name)
return lang_name == "server default" or lang_name == "auto"
end
function LANG.IsLanguage(lang_name)
lang_name = lang_name and string.lower(lang_name)
return LANG.Strings[lang_name]
end
local function LanguageChanged(cv, old, new)
if new and new != LANG.ActiveLanguage then
if LANG.IsServerDefault(new) then
new = LANG.ServerLanguage
end
LANG.SetActiveLanguage(new)
end
end
cvars.AddChangeCallback("ttt_language", LanguageChanged)
local function ForceReload()
LANG.SetActiveLanguage("english")
end
concommand.Add("ttt_reloadlang", ForceReload)
-- Get a copy of all available languages (keys in the Strings tbl)
function LANG.GetLanguages()
local langs = {}
for lang, strings in pairs(LANG.Strings) do
table.insert(langs, lang)
end
return langs
end
function LANG.GetLanguageNames()
-- Typically preferable to GetLanguages but separate to avoid API breakage.
local lang_names = {}
for lang, strings in pairs(LANG.Strings) do
lang_names[lang] = strings["language_name"] or string.Capitalize(lang)
end
return lang_names
end
---- Styling
local bgcolor = {
[ROLE_TRAITOR] = Color(150, 0, 0, 200),
[ROLE_DETECTIVE] = Color(0, 0, 150, 200),
[ROLE_INNOCENT] = Color(0, 50, 0, 200)
};
-- Table of styles that can take a string and display it in some position,
-- colour, etc.
LANG.Styles = {
default = function(text)
MSTACK:AddMessage(text)
print("TTT: " .. text)
end,
rolecolour = function(text)
MSTACK:AddColoredBgMessage(text,
bgcolor[ LocalPlayer():GetRole() ])
print("TTT: " .. text)
end,
chat_warn = function(text)
chat.AddText(COLOR_RED, text)
end,
chat_plain = chat.AddText
};
-- Table mapping message name => message style name. If no message style is
-- defined, the default style is used. This is the case for the vast majority of
-- messages at the time of writing.
LANG.MsgStyle = {}
-- Access of message styles
function LANG.GetStyle(name)
return LANG.MsgStyle[name] or LANG.Styles.default
end
-- Set a style by name or directly as style-function
function LANG.SetStyle(name, style)
if isstring(style) then
style = LANG.Styles[style]
end
LANG.MsgStyle[name] = style
end
function LANG.ShowStyledMsg(text, style)
style(text)
end
function LANG.ProcessMsg(name, params)
local raw = LANG.GetTranslation(name)
local text = raw
if params then
-- some of our params may be string names themselves
for k, v in pairs(params) do
if isstring(v) then
local name = LANG.GetNameParam(v)
if not name then continue end
params[k] = LANG.GetTranslation(name)
end
end
text = interp(raw, params)
end
LANG.ShowStyledMsg(text, LANG.GetStyle(name))
end
--- Message style declarations
-- Rather than having a big list of LANG.SetStyle calls, we specify it the other
-- way around here and churn through it in code. This is convenient because
-- we're doing it en-masse for some random interface things spread out over the
-- place.
--
-- Styles of custom SWEP messages and such should use LANG.SetStyle in their
-- script. The SWEP stuff here might be moved out to the SWEPS too.
local styledmessages = {
rolecolour = {
"round_traitors_one",
"round_traitors_more",
"buy_no_stock",
"buy_pending",
"buy_received",
"xfer_no_recip",
"xfer_no_credits",
"xfer_success",
"xfer_received",
"c4_no_disarm",
"tele_failed",
"tele_no_mark",
"tele_marked",
"dna_identify",
"dna_notfound",
"dna_limit",
"dna_decayed",
"dna_killer",
"dna_no_killer",
"dna_armed",
"dna_object",
"dna_gone",
"credit_det_all",
"credit_tr_all",
"credit_kill"
},
chat_plain = {
"body_call",
"disg_turned_on",
"disg_turned_off",
"mute_all",
"mute_off",
"mute_specs",
"mute_living"
},
chat_warn = {
"spec_mode_warning",
"radar_not_owned",
"radar_charging",
"drop_no_room",
"body_burning",
"tele_no_ground",
"tele_no_crouch",
"tele_no_mark_ground",
"tele_no_mark_crouch",
"drop_no_ammo"
}
};
local set_style = LANG.SetStyle
for style, msgs in pairs(styledmessages) do
for _, name in ipairs(msgs) do
set_style(name, style)
end
end