Skip to content

Instantly share code, notes, and snippets.

@rzuf79
Created July 15, 2024 11:49
Show Gist options
  • Save rzuf79/48ee94a4731901df488b99f6fd6e53c3 to your computer and use it in GitHub Desktop.
Save rzuf79/48ee94a4731901df488b99f6fd6e53c3 to your computer and use it in GitHub Desktop.
McLingo - an edgy localization module for Defold
--[[
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