Aleksei-bird 7c9c708c92 Первый фикс
Пачки некоторых позиций увеличены
2024-03-01 20:54:33 +03:00

425 lines
14 KiB
Lua

local gui = require("__flib__/gui-lite")
local mod_gui = require("__core__/lualib/mod-gui")
local table = require("__flib__/table")
--- @diagnostic disable
--- @deprecated Use 'dictionary-lite' instead.
local flib_dictionary = {}
local inner_separator = "" -- U+E000
local separator = "" -- U+E001
local translation_timeout = 600
-- Depending on the value of `use_local_storage`, this will be tied to `global` or will be re-generated during `on_load`
local raw = { _total_strings = 0 }
local use_local_storage = false
local function key_value(key, value)
return key .. inner_separator .. value .. separator
end
local RawDictionary = {}
--- @deprecated Use 'dictionary-lite' instead.
function RawDictionary:add(internal, translation)
local to_add = { "", internal, inner_separator, { "?", translation, "FLIB_TRANSLATION_FAILED" }, separator }
local ref = self.ref
local i = self.batch_i + 1
-- Due to network saturation concerns, only group five strings together
-- See https://github.com/factoriolib/flib/issues/45
if i < 5 then
ref[i] = to_add --- @diagnostic disable-line
self.batch_i = i
else
local s_i = self.dict_i + 1
self.dict_i = s_i
local new_set = { "", to_add }
self.ref = new_set
self.strings[s_i] = new_set
self.batch_i = 2
end
self.total = self.total + 1
raw._total_strings = raw._total_strings + 1
end
--- @class RawDictionary
function flib_dictionary.new(name, keep_untranslated, initial_contents)
if raw[name] then
error("Dictionary with the name `" .. name .. "` already exists.")
end
--- @type LocalisedString
local initial_string = { "" }
--- @class RawDictionary
local self = {
-- Indices
batch_i = 1,
dict_i = 1,
total = 1,
-- Internal
ref = initial_string,
--- @type LocalisedString
strings = { initial_string },
-- Meta
name = name,
}
setmetatable(self, { __index = RawDictionary })
for key, value in pairs(initial_contents or {}) do
self:add(key, value)
end
--- @diagnostic disable-next-line
raw[name] = { strings = self.strings, keep_untranslated = keep_untranslated }
return self
end
--- @deprecated Use 'dictionary-lite' instead.
function flib_dictionary.init()
if not global.__flib then
global.__flib = {}
end
global.__flib.dictionary = {
in_process = {},
players = {},
raw = { _total_strings = 0 },
translated = {},
}
if use_local_storage then
raw = { _total_strings = 0 }
else
raw = global.__flib.dictionary.raw
end
end
--- @deprecated Use 'dictionary-lite' instead.
function flib_dictionary.load()
if not use_local_storage and global.__flib and global.__flib.dictionary then
raw = global.__flib.dictionary.raw
end
end
--- @deprecated Use 'dictionary-lite' instead.
function flib_dictionary.translate(player)
if not player.connected then
error("Player must be connected to the game before this function can be called!")
end
local player_data = global.__flib.dictionary.players[player.index]
if player_data then
return
end
global.__flib.dictionary.players[player.index] = {
player = player,
status = "get_language",
requested_tick = game.tick,
}
player.request_translation({ "", "FLIB_LOCALE_IDENTIFIER", separator, { "locale-identifier" } })
end
local function request_translation(player_data)
local string = raw[player_data.dictionary].strings[player_data.i]
-- We use `while` instead of `if` here just in case a dictionary doesn't have any strings in it
while not string do
local next_dictionary = next(raw, player_data.dictionary)
if next_dictionary then
-- Set the next dictionary and reset index
player_data.dictionary = next_dictionary
player_data.i = 1
string = raw[next_dictionary].strings[1]
else
-- We're done!
player_data.status = "finished"
return
end
end
player_data.player.request_translation({
"",
key_value("FLIB_DICTIONARY_MOD", script.mod_name),
key_value("FLIB_DICTIONARY_NAME", player_data.dictionary),
key_value("FLIB_DICTIONARY_LANGUAGE", player_data.language),
key_value("FLIB_DICTIONARY_STRING_INDEX", player_data.i),
string,
})
player_data.requested_tick = game.tick
end
--- @deprecated Use 'dictionary-lite' instead.
function flib_dictionary.check_skipped()
local script_data = global.__flib.dictionary
local tick = game.tick
for _, player_data in pairs(script_data.players) do
-- If it's been longer than the timeout, request the string again
-- This is to solve a very rare edge case where translations requested on the same tick that a singleplayer game
-- is saved will not be returned when that save is loaded
if (player_data.requested_tick or 0) + translation_timeout <= tick then
if player_data.status == "get_language" then
player_data.player.request_translation({ "", "FLIB_LOCALE_IDENTIFIER", separator, { "locale-identifier" } })
end
if player_data.status == "translating" then
request_translation(player_data)
end
end
end
end
--- Escape match special characters
local function match_literal(s)
return string.gsub(s, "%-", "%%-")
end
--- @param dict_lang string
local function clean_gui(dict_lang)
for _, player in pairs(game.players) do
local window = mod_gui.get_frame_flow(player).flib_translation_progress
if window then
local pane = window.pane
local mod_flow = pane[script.mod_name]
if mod_flow then
local lang_flow = mod_flow[dict_lang]
if lang_flow then
lang_flow.destroy()
end
if #mod_flow.children == 1 then
mod_flow.destroy()
end
end
if #pane.children == 0 then
window.destroy()
end
end
end
end
local dictionary_match_string = key_value("^FLIB_DICTIONARY_MOD", match_literal(script.mod_name))
.. key_value("FLIB_DICTIONARY_NAME", "(.-)")
.. key_value("FLIB_DICTIONARY_LANGUAGE", "(.-)")
.. key_value("FLIB_DICTIONARY_STRING_INDEX", "(%d-)")
.. "(.*)$"
--- @deprecated Use 'dictionary-lite' instead.
function flib_dictionary.process_translation(event_data)
if not event_data.translated then
return
end
local script_data = global.__flib.dictionary
if string.find(event_data.result, "FLIB_DICTIONARY_NAME") then
local _, _, dict_name, dict_lang, string_index, translation =
string.find(event_data.result, dictionary_match_string)
if dict_name and dict_lang and string_index and translation then
local language_data = script_data.in_process[dict_lang]
-- In some cases, this can fire before on_configuration_changed
if not language_data then
return
end
local dictionary = language_data.dictionaries[dict_name]
if not dictionary then
return
end
local dict_data = raw[dict_name]
local player_data = script_data.players[event_data.player_index]
-- If this number does not match, this is a duplicate, so ignore it
if tonumber(string_index) == player_data.i then
-- Extract current string's translations
for str in string.gmatch(translation, "(.-)" .. separator) do
local _, _, key, value = string.find(str, "^(.-)" .. inner_separator .. "(.-)$")
if key then
-- If `keep_untranslated` is true, then use the key as the value if it failed
local failed = string.find(value, "FLIB_TRANSLATION_FAILED")
if failed and dict_data.keep_untranslated then
value = key
elseif failed then
value = nil
end
if value then
dictionary[key] = value
end
language_data.translated_i = language_data.translated_i + 1
end
end
-- Request next translation
player_data.i = player_data.i + 1
request_translation(player_data)
-- GUI
for _, player in pairs(game.players) do
--- @type LuaGuiElement
local flow = mod_gui.get_frame_flow(player)
if not flow.flib_translation_progress then
gui.add(flow, {
type = "frame",
name = "flib_translation_progress",
style = mod_gui.frame_style,
style_mods = { width = 350 },
direction = "vertical",
{
type = "label",
style = "frame_title",
caption = { "gui.flib-translating-dictionaries" },
tooltip = { "gui.flib-translating-dictionaries-description" },
},
{
type = "frame",
name = "pane",
style = "inside_shallow_frame_with_padding",
style_mods = { top_padding = 8 },
direction = "vertical",
},
})
end
local pane = flow.flib_translation_progress.pane --[[@as LuaGuiElement]]
if not pane[script.mod_name] then
gui.add(pane, {
type = "flow",
name = script.mod_name,
direction = "vertical",
{ type = "label", style = "caption_label", style_mods = { top_margin = 4 }, caption = script.mod_name },
})
end
local mod_flow = pane[script.mod_name] --[[@as LuaGuiElement]]
if not mod_flow[dict_lang] then
gui.add(mod_flow, {
type = "flow",
name = dict_lang,
style_mods = { vertical_align = "center", horizontal_spacing = 8 },
{ type = "label", style = "bold_label", caption = dict_lang },
{ type = "progressbar", name = "bar", style_mods = { horizontally_stretchable = true } },
{ type = "label", name = "label", style = "bold_label" },
})
end
local progress = language_data.translated_i / raw._total_strings
mod_flow[dict_lang].bar.value = progress --[[@as double]]
mod_flow[dict_lang].label.caption = tostring(math.ceil(progress * 100)) .. "%"
mod_flow[dict_lang].label.tooltip = dict_name
.. "\n"
.. language_data.translated_i
.. " / "
.. raw._total_strings
end
if player_data.status == "finished" then
-- Clean up translation data
script_data.translated[dict_lang] = language_data.dictionaries
script_data.in_process[dict_lang] = nil
for _, player_index in pairs(language_data.players) do
script_data.players[player_index] = nil
end
-- Clean up GUI
clean_gui(dict_lang)
return { dictionaries = language_data.dictionaries, language = dict_lang, players = language_data.players }
end
end
end
elseif string.find(event_data.result, "^FLIB_LOCALE_IDENTIFIER") then
local _, _, language = string.find(event_data.result, "^FLIB_LOCALE_IDENTIFIER" .. separator .. "(.*)$")
if language then
local player_data = script_data.players[event_data.player_index]
-- Handle duplicates
if not player_data or player_data.status ~= "get_language" then
return
end
player_data.language = language
-- Check if this language is already translated or being translated
local dictionaries = script_data.translated[language]
if dictionaries then
script_data.players[event_data.player_index] = nil
return { dictionaries = dictionaries, language = language, players = { event_data.player_index } }
end
local in_process = script_data.in_process[language]
if in_process then
table.insert(in_process.players, event_data.player_index)
player_data.status = "waiting"
return
end
-- Set up player data for translating
player_data.status = "translating"
player_data.dictionary = next(raw, "_total_strings")
player_data.i = 1
-- Add language to in process data
script_data.in_process[language] = {
dictionaries = table.map(raw, function(_, k)
if k ~= "_total_strings" then
return {}
end
end),
players = { event_data.player_index },
translated_i = 0,
}
-- Start translating
request_translation(player_data)
end
end
end
--- @deprecated Use 'dictionary-lite' instead.
function flib_dictionary.cancel_translation(player_index)
local script_data = global.__flib.dictionary
local player_data = script_data.players[player_index]
if not player_data then
return
end
-- Delete this player's data from global
script_data.players[player_index] = nil
local in_process = script_data.in_process[player_data.language]
if not in_process then
return
end
-- Remove this player from the players table
local i = table.find(in_process.players, player_index)
if i then
table.remove(in_process.players, i)
end
if player_data.status ~= "translating" then
return
end
-- Find the next player in the list with valid data
local next_player_data
for _, player_index in pairs(in_process.players) do
local player_data = script_data.players[player_index]
if player_data then
next_player_data = player_data
break
end
end
-- If there are no more valid players
if not next_player_data then
-- Completely cancel the translation
script_data.in_process[player_data.language] = nil
clean_gui(player_data.language)
return
end
--Update player info
next_player_data.status = "translating"
next_player_data.dictionary = player_data.dictionary
next_player_data.i = player_data.i
-- Resume translating with the new player
request_translation(next_player_data)
end
--- @deprecated Use 'dictionary-lite' instead.
function flib_dictionary.set_use_local_storage(value)
use_local_storage = value
end
return flib_dictionary