Created
July 15, 2024 11:49
-
-
Save rzuf79/48ee94a4731901df488b99f6fd6e53c3 to your computer and use it in GitHub Desktop.
McLingo - an edgy localization module for Defold
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
McLingo | |
an edgy localization module for Defold | |
Use At Your Own Risk edition | |
It uses localization data exported from Google Sheets (or any Excel-like soft) in a csv format. | |
A template for a localization document can be found here: | |
https://docs.google.com/spreadsheets/d/1k1gJ1OqY41UpzMrNmbnvwDZ-6MMDzdi1aWVYruX2hsI/edit?usp=sharing | |
The csv file needs to be added as a Custom Resource in the Project Settings for it to work on the web. | |
Nodes localized with tr_tree and tr_node will be auto-translated each time the set_locale function is used. | |
It only works for gui nodes for now. I have a feeling it will change soon. | |
A quick example: | |
local mclingo = require("scripts.mclingo") | |
function init(self) | |
mclingo.load_csv("some_data_folder/loca.csv") | |
mclingo.apply_system_locale("en") --figure out the system locale and use it if it's in the csv file. Fall back to "en". | |
-- or just use set_locale if you have it saved in your save data or something | |
gui.set_text("text_node_id", mclingo.tr("PLAY")) --set a localized text to a text node with a hardcoded key PLAY | |
mclingo.tr_node("other_text_node_id") --localize node using its set text as an id | |
mclingo.tr_node("tree_root_id") --traverses the node tree, finds all texts and localizes them with tr_node | |
mclingo.clean_up() --cleans up the data that tr_node and tr_tree use to work. good idea to call it during scenes load/unload | |
end | |
]]-- | |
local M = {} | |
local locale = "en" | |
local loc_data = {} | |
local nodes = {} | |
local available_locales = {} | |
M.set_locale = function(new_locale) | |
locale = new_locale | |
for node, tr_key in pairs(nodes) do | |
gui.set_text(node, M.tr(tr_key)) | |
end | |
end | |
M.get_locale = function() | |
return locale | |
end | |
M.get_system_locale = function() | |
return sys.get_sys_info().device_language or sys.get_sys_info().language | |
end | |
M.apply_system_locale = function(default_locale) | |
locale = default_locale or available_locales[1] | |
local system_locale = M.get_system_locale() | |
for _, l in ipairs(available_locales) do | |
if system_locale == l then | |
M.set_locale(l) | |
break | |
end | |
end | |
return locale | |
end | |
M.clean_up = function() | |
nodes = {} | |
end | |
M.tr = function(text_key) | |
if loc_data[text_key] and loc_data[text_key][locale] then | |
return loc_data[text_key][locale] | |
end | |
return text_key | |
end | |
M.tr_node = function(node_id, tr_key) | |
local node = type(node_id) == "userdata" and node_id or gui.get_node(node_id) | |
if nodes[node] == nil or tr_key ~= nil then | |
local current = gui.get_text(node) | |
if not tr_key and loc_data[current] then | |
tr_key = current | |
end | |
nodes[node] = tr_key | |
end | |
if nodes[node] then | |
gui.set_text(node, M.tr(nodes[node])) | |
end | |
end | |
M.tr_tree = function(tree_root) | |
tree_root = type(tree_root) == "userdata" and tree_root or gui.get_node(tree_root) | |
local tree = gui.get_tree(tree_root) | |
for _, node in pairs(tree) do | |
if gui.get_text(node) then | |
M.tr_node(node) | |
end | |
end | |
end | |
M.load_csv = function(filename) | |
loc_data = {} | |
local raw_data | |
local csv_file = io.open(filename, "r") | |
if csv_file then | |
raw_data = csv_file:read("*a") | |
csv_file:close() | |
else | |
raw_data = sys.load_resource("/"..filename) | |
end | |
-- sanitize input data by removing all the line breaks from the cell data | |
do | |
local satanized = "" | |
local in_quotes = false | |
for i = 1, #raw_data do | |
local cha = raw_data:sub(i, i) | |
if cha == '"' then | |
in_quotes = not in_quotes | |
elseif cha == '\n' and in_quotes then | |
cha = "\\n" | |
end | |
satanized = satanized..cha | |
end | |
raw_data = satanized | |
end | |
available_locales = {} | |
for line in string.gmatch(raw_data, '[^\r\n]+') do | |
local values = {} | |
local value = "" | |
local in_quotes = false | |
for i = 1, #line do | |
local cha = line:sub(i, i) | |
if cha == '"' then | |
if in_quotes and line:sub(i+1, i+1) == '"' then | |
value = value .. cha | |
end | |
in_quotes = not in_quotes | |
elseif cha == ',' and not in_quotes then | |
table.insert(values, value) | |
value = "" | |
else | |
value = value .. cha | |
end | |
end | |
table.insert(values, value) | |
if #available_locales == 0 then | |
available_locales = values | |
elseif values[1] ~= "" then | |
loc_data[values[1]] = {} | |
for i = 2, #available_locales do | |
local escaped = values[i]:gsub("\\([nt])", {n="\n", t="\t"}) | |
loc_data[values[1]][available_locales[i]] = escaped | |
end | |
end | |
end | |
table.remove(available_locales, 1) -- remove 'id' | |
end | |
-- eventually i'll figure out a way for this to work. it's supposed to help with | |
-- setting up the fonts used in the project to only use the necessary glyphs | |
M.get_unique_chars = function() | |
local unique_chars = {} | |
local out_string = "" | |
for _, locales in pairs(loc_data) do | |
for _, str in pairs(locales) do | |
for i = 1, #str do | |
local cha = str:sub(i, i) | |
if not unique_chars[cha] then | |
unique_chars[cha] = true | |
out_string = out_string..cha | |
end | |
end | |
end | |
end | |
return out_string | |
end | |
return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment