-
-
Save kaeza/c5c9bdb9d41a68510503aba7a10aebf3 to your computer and use it in GitHub Desktop.
This file contains 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
local strfind, strsub, strbyte = string.find, string.sub, string.byte | |
local strmatch = string.match | |
local floor = math.floor | |
local function isplit(str, sep) | |
local pos, endp = 1, #str+1 | |
return function() | |
if (not pos) or pos > endp then return end | |
local s, e = strfind(str, sep, pos, true) | |
local part = strsub(str, pos, s and s-1) | |
pos = e and e + 1 | |
return part | |
end | |
end | |
local function split(str, sep) | |
local t, n = { }, 0 | |
for part in isplit(str, sep) do | |
n = n+1 | |
t[n] = part | |
end | |
t.n = n | |
return t | |
end | |
local function usplit(str, sep) | |
local t = split(str, sep) | |
return unpack(t, 1, t.n) | |
end | |
-- From: http://lua-users.org/lists/lua-l/2010-04/msg00005.html | |
-- Retrieved: 2017-01-20 05:11:36 | |
-- | |
-- Small modifications by kaeza <https://github.com/kaeza> to | |
-- support `ngettext`. | |
-- | |
-- Original header follows: | |
-- ------------------------------------------------------------- | |
-- load an mo file and return a lua table | |
-- @param mo_file name of the file to load | |
-- @return table on success | |
-- @return nil,string on failure | |
-- @copyright J.Jørgen von Bargen | |
-- @licence I provide this as public domain | |
-- @see http://www.gnu.org/software/hello/manual/gettext/MO-Files.html | |
-- ------------------------------------------------------------- | |
local function parse_mo(mo_data) | |
-------------------------------- | |
-- check format | |
-------------------------------- | |
local peek_long --localize | |
local magic = strsub(mo_data, 1, 4) | |
-- intel magic 0xde120495 | |
if magic == "\222\018\004\149" then | |
peek_long = function(offs) | |
local a, b, c, d = strbyte(mo_data, offs+1, offs+4) | |
return ((d*256+c)*256+b)*256+a | |
end | |
-- motorola magic = 0x950412de | |
elseif magic == "\149\004\018\222" then | |
peek_long = function(offs) | |
local a, b, c, d = strbyte(mo_data, offs+1, offs+4) | |
return ((a*256+b)*256+c)*256+d | |
end | |
else | |
return nil, "not a valid mo-file" | |
end | |
-------------------------------- | |
-- version | |
-------------------------------- | |
local V = peek_long(4) | |
if V ~= 0 then | |
return nil, "unsupported version" | |
end | |
------------------------------ | |
-- get number of offsets of table | |
------------------------------ | |
local N, O, T = peek_long(8), peek_long(12), peek_long(16) | |
------------------------------ | |
-- traverse and get strings | |
------------------------------ | |
local hash = { } | |
for nstr = 1, N do | |
local ol, oo = peek_long(O), peek_long(O+4) O = O+8 | |
local tl, to = peek_long(T), peek_long(T+4) T = T+8 | |
local key = strsub(mo_data, oo+1, oo+ol) | |
local val = strsub(mo_data, to+1, to+tl) | |
local nul = strfind(key, "\0", 1, true) | |
if nul then | |
local k = strsub(key, 1, nul-1) | |
local msgstr, nmsgstr = { }, 0 | |
hash[k] = msgstr | |
for v in isplit(val, "\0") do | |
msgstr[nmsgstr] = v | |
nmsgstr = nmsgstr + 1 | |
end | |
else | |
hash[key] = val | |
end | |
end | |
return hash -- return table | |
end | |
local M = { } | |
local domains = { } | |
local dgettext_cache = { } | |
local dngettext_cache = { } | |
local langs | |
local function detect_languages() | |
if langs then return langs end | |
langs = { } | |
local function addlang(l) | |
langs[#langs+1] = l | |
local sep = strfind(l, "_", 1, true) | |
if sep then | |
langs[#langs+1] = strsub(l, 1, sep-1) | |
end | |
end | |
local v | |
v = rawget(_G, "minetest") and minetest.setting_get("language") | |
if v and v~="" then | |
addlang(v) | |
return langs | |
end | |
v = os.getenv("LANGUAGE") | |
if v then | |
for item in isplit(v, ":") do | |
addlang(item) | |
end | |
return langs | |
end | |
v = os.getenv("LANG") | |
if v then | |
addlang(v) | |
return langs | |
end | |
return langs | |
end | |
local function warn(msg) | |
if rawget(_G, "minetest") then | |
minetest.log("warning", msg) | |
else | |
io.stderr:write("WARNING: ", msg, "\n") | |
end | |
end | |
-- hax! | |
-- This function converts a C expression to an equivalent Lua expression. | |
-- It handles enough stuff to parse the `Plural-Forms` header correctly. | |
-- Note that it assumes the C expression is valid to begin with. | |
local function compile_plural_forms(str) | |
local plural = strmatch(str, "plural=([^;]+);?$") | |
local function replace_ternary(str) | |
local c, t, f = strmatch(str, "^(.-)%?(.-):(.*)") | |
if c then | |
return ("__if(" | |
..replace_ternary(c) | |
..","..replace_ternary(t) | |
..","..replace_ternary(f) | |
..")") | |
end | |
return str | |
end | |
plural = replace_ternary(plural) | |
plural = (plural:gsub("&&", " and ") | |
:gsub("||", " or ") | |
:gsub("!=", "~=") | |
:gsub("!", " not ")) | |
local f, err = loadstring([[ | |
local function __if(c, t, f) | |
if c then return t else return f end | |
end | |
local function __f(n) | |
return (]]..plural..[[) | |
end | |
return (__f(...)) | |
]]) | |
if not f then return nil, err end | |
local env = { } | |
env._ENV, env._G = env, env | |
setfenv(f, env) | |
return function(n) | |
local v = f(n) | |
if type(v) == "boolean" then | |
-- Handle things like a plain `n != 1` | |
v = v and 1 or 0 | |
end | |
return v | |
end | |
end | |
local function parse_headers(str) | |
local headers = { } | |
for line in isplit(str, "\n") do | |
local k, v = strmatch(line, "^([^:]+):%s*(.*)") | |
if k then | |
headers[k] = v | |
end | |
end | |
return headers | |
end | |
local function load_catalog(filename) | |
local f, data, err | |
local function bail(msg) | |
warn(msg..(err and ": ")..(err or "")) | |
return nil | |
end | |
f, err = io.open(filename, "rb") | |
if not f then | |
return --bail("failed to open catalog") | |
end | |
data, err = f:read("*a") | |
f:close() | |
if not data then | |
return bail("failed to read catalog") | |
end | |
data, err = parse_mo(data) | |
if not data then | |
return bail("failed to parse catalog") | |
end | |
err = nil | |
local hdrs = data[""] | |
if not hdrs then | |
return bail("catalog has no headers") | |
end | |
hdrs = parse_headers(data[""]) | |
local pf = hdrs["Plural-Forms"] | |
if not pf then | |
return bail("failed to load catalog:" | |
.." catalog has no Plural-Forms header") | |
end | |
data.plural_index, err = compile_plural_forms(pf) | |
if not data.plural_index then | |
return bail("failed to compile plural forms") | |
end | |
--warn("loaded: "..filename) | |
return data | |
end | |
function M.bindtextdomain(domain, path) | |
detect_languages() | |
--print(unpack(langs)) | |
local old = domains[domain] | |
local cats = { } | |
local d = { | |
path = path, | |
catalogs = cats, | |
} | |
domains[domain] = d | |
for _, l in ipairs(langs) do | |
local full = path.."/"..l.."/LC_MESSAGES/"..domain..".mo" | |
cats[l] = load_catalog(full) | |
end | |
dgettext_cache, dngettext_cache = { }, { } | |
return old and old.path | |
end | |
function M.dgettext(domain, msgid) | |
assert(langs, "you must call bindtextdomain first") | |
local hash = domain.."\0"..msgid | |
local cached = dgettext_cache[hash] | |
if cached then | |
return cached | |
end | |
local msgstr = msgid | |
local d = domains[domain] | |
if d then | |
for _, l in ipairs(langs) do | |
local cat = d.catalogs[l] | |
local mstr = cat and cat[msgid] | |
if mstr and type(mstr) == "string" then | |
msgstr = mstr | |
break | |
end | |
end | |
end | |
dgettext_cache[hash] = msgstr | |
return msgstr | |
end | |
function M.dngettext(domain, msgid, msgid_plural, n) | |
assert(langs, "you must call bindtextdomain first") | |
n = floor(n) | |
local hash = domain.."\0"..msgid.."\0"..n | |
local cached = dngettext_cache[hash] | |
if cached then | |
return cached | |
end | |
local msgstr | |
local d = domains[domain] | |
if d then | |
for _, l in ipairs(langs) do | |
local cat = d.catalogs[l] | |
local mstr = cat and cat[msgid] | |
if mstr and type(mstr) == "table" then | |
local index = cat.plural_index(n) | |
print(index) | |
msgstr = mstr[index] | |
break | |
end | |
end | |
end | |
if not msgstr then | |
msgstr = n==1 and msgid or msgid_plural | |
end | |
dngettext_cache[hash] = msgstr | |
return msgstr | |
end | |
return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment