mirror of
https://github.com/koreader/koreader
synced 2024-10-31 21:20:20 +00:00
0067c8f29e
References https://github.com/koreader/koreader/issues/5232 Given an entry in the PO file like the following: ``` msgctxt "systemstat" msgid " Total" msgstr "Totaal" ``` It can be addressed using: ```lua local _ = require("gettext") local C_ = _.pgettext C_("systemstat", " Total") ``` This allows to distinguish between separate instances of the same string, for example "Pages" meaning "Number of pages" and "Pages" meaning "Display of pages". Extraction of this code pattern is not yet implemented by nightswatcher. xgettext didn't yet support Lua back in 2013 when all this was first added to the program, but now it does. Therefore it might make the most sense to replace the current Python extraction script with xgettext itself. By default it only understands gettext.pgettext(), but that can be addressed by passing some extra command line arguments, for example: ``` xgettext -l lua -c --keyword=C_:1c,2 *.lua ```
132 lines
4.0 KiB
Lua
132 lines
4.0 KiB
Lua
local isAndroid, android = pcall(require, "android")
|
|
local logger = require("logger")
|
|
|
|
local GetText = {
|
|
translation = {},
|
|
current_lang = "C",
|
|
dirname = "l10n",
|
|
textdomain = "koreader"
|
|
}
|
|
|
|
local GetText_mt = {
|
|
__index = {}
|
|
}
|
|
|
|
function GetText_mt.__call(gettext, msgstr)
|
|
return gettext.translation[msgstr] or msgstr
|
|
end
|
|
|
|
local function c_escape(what)
|
|
if what == "\n" then return ""
|
|
elseif what == "a" then return "\a"
|
|
elseif what == "b" then return "\b"
|
|
elseif what == "f" then return "\f"
|
|
elseif what == "n" then return "\n"
|
|
elseif what == "r" then return "\r"
|
|
elseif what == "t" then return "\t"
|
|
elseif what == "v" then return "\v"
|
|
elseif what == "0" then return "\0" -- shouldn't happen, though
|
|
else
|
|
return what
|
|
end
|
|
end
|
|
|
|
-- for PO file syntax, see
|
|
-- https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
|
|
-- we only implement a sane subset for now
|
|
|
|
function GetText_mt.__index.changeLang(new_lang)
|
|
GetText.context = {}
|
|
GetText.translation = {}
|
|
GetText.current_lang = "C"
|
|
|
|
-- the "C" locale disables localization altogether
|
|
-- can be various things such as `en_US` or `en_US:en`
|
|
if new_lang == "C" or new_lang == nil or new_lang == ""
|
|
or new_lang:match("^en_US") == "en_US" then return end
|
|
|
|
-- strip encoding suffix in locale like "zh_CN.utf8"
|
|
new_lang = new_lang:sub(1, new_lang:find(".%."))
|
|
|
|
local file = GetText.dirname .. "/" .. new_lang .. "/" .. GetText.textdomain .. ".po"
|
|
local po = io.open(file, "r")
|
|
|
|
if not po then
|
|
logger.dbg("cannot open translation file:", file)
|
|
return false
|
|
end
|
|
|
|
local data = {}
|
|
local what = nil
|
|
while true do
|
|
local line = po:read("*l")
|
|
if line == nil or line == "" then
|
|
if data.msgid and data.msgstr and data.msgstr ~= "" then
|
|
local unescaped_string = string.gsub(data.msgstr, "\\(.)", c_escape)
|
|
if data.msgctxt and data.msgctxt ~= "" then
|
|
if not GetText.context[data.msgctxt] then
|
|
GetText.context[data.msgctxt] = {}
|
|
end
|
|
GetText.context[data.msgctxt][data.msgid] = unescaped_string
|
|
else
|
|
GetText.translation[data.msgid] = unescaped_string
|
|
end
|
|
end
|
|
-- stop at EOF:
|
|
if line == nil then break end
|
|
data = {}
|
|
what = nil
|
|
else
|
|
-- comment
|
|
if not line:match("^#") then
|
|
-- new data item (msgid, msgstr, ...
|
|
local w, s = line:match("^%s*(%a+)%s+\"(.*)\"%s*$")
|
|
if w then
|
|
what = w
|
|
else
|
|
-- string continuation
|
|
s = line:match("^%s*\"(.*)\"%s*$")
|
|
end
|
|
if what and s then
|
|
-- unescape \n or msgid won't match
|
|
s = s:gsub("\\n", "\n")
|
|
-- unescape " or msgid won't match
|
|
s = s:gsub('\\"', '"')
|
|
data[what] = (data[what] or "") .. s
|
|
end
|
|
end
|
|
end
|
|
end
|
|
GetText.current_lang = new_lang
|
|
end
|
|
|
|
function GetText_mt.__index.pgettext(msgctxt, msgstr)
|
|
return GetText.context[msgctxt] and GetText.context[msgctxt][msgstr] or msgstr
|
|
end
|
|
|
|
setmetatable(GetText, GetText_mt)
|
|
|
|
if os.getenv("LANGUAGE") then
|
|
GetText.changeLang(os.getenv("LANGUAGE"))
|
|
elseif os.getenv("LC_ALL") then
|
|
GetText.changeLang(os.getenv("LC_ALL"))
|
|
elseif os.getenv("LC_MESSAGES") then
|
|
GetText.changeLang(os.getenv("LC_MESSAGES"))
|
|
elseif os.getenv("LANG") then
|
|
GetText.changeLang(os.getenv("LANG"))
|
|
end
|
|
|
|
if isAndroid then
|
|
local ffi = require("ffi")
|
|
local buf = ffi.new("char[?]", 16)
|
|
android.lib.AConfiguration_getLanguage(android.app.config, buf)
|
|
local lang = ffi.string(buf)
|
|
android.lib.AConfiguration_getCountry(android.app.config, buf)
|
|
local country = ffi.string(buf)
|
|
if lang and country then
|
|
GetText.changeLang(lang.."_"..country)
|
|
end
|
|
end
|
|
|
|
return GetText
|