mirror of
https://github.com/minetest-mods/intllib.git
synced 2025-01-09 09:30:26 +01:00
first commit
This commit is contained in:
commit
11b9e0596c
96
README.txt
Normal file
96
README.txt
Normal file
@ -0,0 +1,96 @@
|
||||
|
||||
Internationalization Lib for Minetest
|
||||
By Diego Martínez (a.k.a. "Kaeza").
|
||||
Released as WTFPL.
|
||||
|
||||
This mod is an attempt at providing internationalization support for mods
|
||||
(something Minetest currently lacks).
|
||||
|
||||
How do I use it?
|
||||
In order to enable it for your mod, copy the following code snippet and paste
|
||||
it at the beginning of your source file(s):
|
||||
|
||||
-- Boilerplate to support localized strings if intllib mod is installed.
|
||||
local S
|
||||
if (minetest.get_modpath("intllib")) then
|
||||
dofile(minetest.get_modpath("intllib").."/intllib.lua")
|
||||
S = intllib.Getter(minetest.get_current_modname())
|
||||
else
|
||||
S = function ( s ) return s end
|
||||
end
|
||||
|
||||
Note that by using this snippet, you don't need to depend on `intllib'. In
|
||||
fact, the mod's `init.lua' is a no-op; you need to explicitly execute intllib's
|
||||
`intllib.lua' file.
|
||||
Also note that if the intllib "mod" is not installed, the S() function is
|
||||
defined so it returns the string unchanged. This is done so you don't have to
|
||||
sprinkle tons of `if's (or similar constructs) to check if the lib is actually
|
||||
installed.
|
||||
|
||||
Next, for each "translatable" string in your sources, use the S() function
|
||||
(defined in the snippet) to return the translated string. For example:
|
||||
|
||||
minetest.register_node("mymod:mynode", {
|
||||
description = S("My Fabulous Node"),
|
||||
<...>
|
||||
})
|
||||
|
||||
Then, you create a `locale' directory inside your mod directory, with files
|
||||
named after the two-letter ISO Language Code of the languages you want to
|
||||
support. Here's an example for a Spanish locale file (`es.txt'):
|
||||
|
||||
# Lines beginning with a pound sign are comments and are effectively ignored
|
||||
# by the reader. Note that comments only span until the end of the line;
|
||||
# there's no support for multiline comments.
|
||||
Blank lines not containing an equals sign are also ignored.
|
||||
Hello, World! = Hola, Mundo!
|
||||
String with\nnewlines and \ttabs = Cadena con\nsaltos de linea y\ttabuladores
|
||||
String with an \= equals sign = Cadena con un signo de \= igualdad
|
||||
|
||||
Since there's currently no portable way to detect the language, this library
|
||||
tries several alternatives, and uses the first one found:
|
||||
- `language' setting in `minetest.conf'
|
||||
- `LANG' environment variable (this is always set on Unix-like OSes).
|
||||
- Default of "en".
|
||||
Note that in any case only up to the first two characters are used, so for
|
||||
example, the settings "de_DE.UTF-8", "de_DE", and "de" are all equal.
|
||||
Windows users have no `LANG' environment variable by default. To add it, do
|
||||
the following:
|
||||
- Click Start->Settings->Control Panel.
|
||||
- Start the "System" applet.
|
||||
- Click on the "Advanced" tab.
|
||||
- Click on the "Environment variables" button
|
||||
- Click "New".
|
||||
- Type "LANG" (without quotes) as name and the language code as value.
|
||||
- Click OK until all dialogs are closed.
|
||||
Alternatively for all platforms, if you don't want to modify system settings,
|
||||
you may add the following line to your `minetest.conf' file:
|
||||
language = <language code>
|
||||
|
||||
Also note that there are some problems with using accented, and in general
|
||||
non-latin characters in strings. Until a fix is found, please limit yourself
|
||||
to using only US-ASCII characters.
|
||||
|
||||
Frequently Asked Questions
|
||||
--------------------------
|
||||
Q: Were you bored when you did this?
|
||||
A: Yes.
|
||||
|
||||
Q: Why are my texts are not translated?
|
||||
A: RTFM...or ask in the topic 8-----)
|
||||
|
||||
Q: How come the README is bigger than the actual code?
|
||||
A: Because I'm adding silly unfunny questions here...and because there are
|
||||
some users that are too lazy to understand how the code works, so I have
|
||||
to document things.
|
||||
|
||||
Q: I don't like this sh*t!
|
||||
A: That's not a question.
|
||||
|
||||
Thanks for reading up to this point.
|
||||
Should you have any comments/suggestions, please post them in the forum topic.
|
||||
|
||||
Let there be translated texts! :P
|
||||
--
|
||||
Yours Truly,
|
||||
Kaeza
|
91
intllib.lua
Normal file
91
intllib.lua
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
intllib = { };
|
||||
|
||||
local strings = { };
|
||||
|
||||
local INTLLIB_DEBUG = true;
|
||||
|
||||
local LANG = minetest.setting_get("language") or os.getenv("LANG") or "en";
|
||||
LANG = LANG:sub(1, 2);
|
||||
|
||||
local TRACE;
|
||||
|
||||
if (INTLLIB_DEBUG) then
|
||||
TRACE = function ( s )
|
||||
print("*** DEBUG: "..s);
|
||||
end
|
||||
else
|
||||
TRACE = function ( ) end
|
||||
end
|
||||
|
||||
local repr2esc = {
|
||||
["n"] = "\n";
|
||||
["r"] = "";
|
||||
["t"] = "\t";
|
||||
["\\"] = "\\";
|
||||
["\""] = "\"";
|
||||
};
|
||||
|
||||
local esc2repr = {
|
||||
["\n"] = "\\n";
|
||||
["\r"] = "";
|
||||
["\t"] = "\\t";
|
||||
["\\"] = "\\\\";
|
||||
["\\\""] = "\"";
|
||||
};
|
||||
|
||||
local function parse ( s )
|
||||
return s:gsub("\\([nrt\"\'\\\\])", function ( c )
|
||||
return (repr2esc[c] or c);
|
||||
end);
|
||||
end
|
||||
|
||||
local function repr ( s )
|
||||
return s:gsub("[\n\t\"\'\\\\]", function ( c )
|
||||
return (esc2repr[c] or c);
|
||||
end);
|
||||
end
|
||||
|
||||
local function do_load_strings ( f )
|
||||
local msgstr = { };
|
||||
for line in f:lines() do
|
||||
line = line:trim();
|
||||
if ((line ~= "") and (line:sub(1, 1) ~= "#")) then
|
||||
local pos = line:find("=", 1, true);
|
||||
while (pos and (line:sub(pos - 1, pos - 1) == "\\")) do
|
||||
local pos = line:find("=", pos + 1, true);
|
||||
end
|
||||
if (pos) then
|
||||
local msgid = line:sub(1, pos - 1):trim();
|
||||
local str = line:sub(pos + 1):trim();
|
||||
msgstr[msgid] = parse(str);
|
||||
end
|
||||
end
|
||||
end
|
||||
return msgstr;
|
||||
end
|
||||
|
||||
function intllib.load_strings ( modname )
|
||||
local f, e = io.open(minetest.get_modpath(modname).."/locale/"..LANG..".txt");
|
||||
if (f) then
|
||||
local strings;
|
||||
strings = do_load_strings(f);
|
||||
f:close();
|
||||
return strings;
|
||||
else
|
||||
return nil, "Could not load '"..LANG.."' texts: "..e;
|
||||
end
|
||||
end
|
||||
|
||||
local getters = { };
|
||||
|
||||
function intllib.Getter ( modname )
|
||||
if (not modname) then modname = minetest.get_current_modname(); end
|
||||
if (not getters[modname]) then
|
||||
local msgstr = intllib.load_strings(modname) or { };
|
||||
getters[modname] = function ( s )
|
||||
return msgstr[repr(s)] or s;
|
||||
end;
|
||||
end
|
||||
return getters[modname];
|
||||
end
|
4
locale/es.txt
Normal file
4
locale/es.txt
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
Hello %s!::¡Hola %s!
|
||||
Blah blah...\nBlah blah...::Bla bla...\nBla bla...
|
||||
Bye!::¡Adios!
|
257
tools/findtext.lua
Executable file
257
tools/findtext.lua
Executable file
@ -0,0 +1,257 @@
|
||||
#! /usr/bin/env lua
|
||||
|
||||
local function listdir ( dir )
|
||||
local LS;
|
||||
if (os.getenv("WINDIR")) then
|
||||
LS = 'dir /b %s';
|
||||
else
|
||||
LS = 'ls %s';
|
||||
end
|
||||
local tmp = os.tmpname();
|
||||
local r = os.execute(LS:format(dir).." > "..tmp)
|
||||
if ((r ~= nil) and (r == 0)) then
|
||||
local list = { };
|
||||
local f = io.open(tmp);
|
||||
if (not f) then
|
||||
os.remove(tmp);
|
||||
return nil;
|
||||
end
|
||||
for line in f:lines() do
|
||||
list[#list + 1] = line;
|
||||
end
|
||||
f:close();
|
||||
os.remove(tmp);
|
||||
return list;
|
||||
end
|
||||
os.remove(tmp);
|
||||
return nil;
|
||||
end
|
||||
|
||||
local function StringOutput ( s, file )
|
||||
return {
|
||||
_buf = (s or "");
|
||||
_file = file;
|
||||
write = function ( self, ... )
|
||||
local spc = false;
|
||||
for _, v in ipairs({...}) do
|
||||
if (spc) then self._buf = self._buf.." "; end
|
||||
spc = true;
|
||||
self._buf = self._buf..tostring(v);
|
||||
end
|
||||
end;
|
||||
print = function ( self, ... )
|
||||
local spc = false;
|
||||
for _, v in ipairs({...}) do
|
||||
if (spc) then self._buf = self._buf.." "; end
|
||||
spc = true;
|
||||
self._buf = self._buf..tostring(v);
|
||||
end
|
||||
self._buf = self._buf.."\n";
|
||||
end;
|
||||
tostring = function ( self )
|
||||
return self._buf;
|
||||
end;
|
||||
flush = function ( self, file )
|
||||
local f = (file or self._file or io.stdout);
|
||||
f:write(self._buf);
|
||||
f:flush();
|
||||
end;
|
||||
};
|
||||
end
|
||||
|
||||
local function err_print ( s )
|
||||
io.stderr:write((s or "").."\n");
|
||||
end
|
||||
|
||||
local function ArgParser ( appname, usageline, description )
|
||||
return {
|
||||
prefix = ((appname and (appname..": ")) or "");
|
||||
description = description;
|
||||
options = {
|
||||
{ name = "help";
|
||||
short = "h";
|
||||
long = "help";
|
||||
help = "Show this help screen and exit.";
|
||||
},
|
||||
};
|
||||
values = { };
|
||||
inputs = { };
|
||||
no_exit = (appname == nil);
|
||||
add_option = function ( self, name, short, long, has_arg, arg_name, help )
|
||||
self.options[#self.options+1] = {
|
||||
name = name;
|
||||
short = short;
|
||||
long = long;
|
||||
has_arg = has_arg;
|
||||
arg_name = arg_name;
|
||||
help = help;
|
||||
};
|
||||
end;
|
||||
parse = function ( self, args, no_exit )
|
||||
local opts = { };
|
||||
local inputs = { };
|
||||
local i = 1;
|
||||
while (i <= #args) do
|
||||
local a = args[i];
|
||||
if (a:sub(1, 1) == '-') then
|
||||
local found = false;
|
||||
if ((a == "-h") or (a == "--help")) then
|
||||
if (no_exit or self.no_exit) then
|
||||
return nil, "--help";
|
||||
else
|
||||
self:show_usage();
|
||||
os.exit(0);
|
||||
end
|
||||
end
|
||||
for _, opt in ipairs(self.options) do
|
||||
if ((a == "-"..opt.short) or (a == "--"..opt.long)) then
|
||||
if (opt.has_arg) then
|
||||
i = i + 1;
|
||||
if (not args[i]) then
|
||||
local msg = self.prefix.."option `"..a.."' requires an argument.";
|
||||
if (no_exit) then
|
||||
return nil, msg;
|
||||
else
|
||||
err_print(msg);
|
||||
os.exit(-1);
|
||||
end
|
||||
end
|
||||
opts[opt.name] = args[i];
|
||||
else
|
||||
opts[opt.name] = true;
|
||||
end
|
||||
found = true;
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (not found) then
|
||||
local msg = self.prefix.."unrecognized option `"..a.."'. Try `--help'.";
|
||||
if (no_exit or self.no_exit) then
|
||||
return nil, msg;
|
||||
else
|
||||
err_print(msg);
|
||||
os.exit(-1);
|
||||
end
|
||||
end
|
||||
else
|
||||
inputs[#inputs + 1] = a;
|
||||
end
|
||||
i = i + 1;
|
||||
end
|
||||
return opts, inputs;
|
||||
end;
|
||||
show_usage = function ( self, extramsg )
|
||||
if (extramsg) then
|
||||
err_print(self.prefix..extramsg);
|
||||
end
|
||||
err_print("Usage: "..appname.." "..(usageline or ""));
|
||||
if (self.description) then
|
||||
err_print(self.description)
|
||||
end
|
||||
if (#self.options > 0) then
|
||||
err_print("\nAvailable Options:");
|
||||
local optwidth = 0;
|
||||
local optline = { };
|
||||
for _, opt in ipairs(self.options) do
|
||||
local sh = (opt.short and "-"..opt.short);
|
||||
local ln = (opt.long and "--"..opt.long);
|
||||
local sep = (sh and ln and ",") or " ";
|
||||
sh = sh or "";
|
||||
ln = ln or "";
|
||||
if (opt.long and opt.has_arg) then
|
||||
ln = ln.." "..opt.arg_name;
|
||||
end
|
||||
optline[#optline + 1] = sh..sep..ln;
|
||||
if (optline[#optline]:len() > optwidth) then
|
||||
optwidth = optline[#optline]:len();
|
||||
end
|
||||
end
|
||||
for i, opt in ipairs(self.options) do
|
||||
local sep = string.rep(" ", optwidth - optline[i]:len()).." ";
|
||||
err_print(" "..optline[i]..sep..opt.help);
|
||||
end
|
||||
err_print();
|
||||
if (self.footer) then
|
||||
err_print(self.footer.."\n");
|
||||
end
|
||||
end
|
||||
end;
|
||||
};
|
||||
end
|
||||
|
||||
local function main ( arg )
|
||||
|
||||
local APPNAME = "findtext.lua";
|
||||
|
||||
local ap = ArgParser(APPNAME, "[OPTIONS] FILES...");
|
||||
ap:add_option("output", "o", "output", true, "FILE", "Write translated strings to FILE. (default: stdout)");
|
||||
ap:add_option("langname", "l", "langname", true, "NAME", "Set the language name to NAME.");
|
||||
ap:add_option("author", "a", "author", true, "NAME", "Set the author to NAME.");
|
||||
ap:add_option("mail", "m", "mail", true, "EMAIL", "Set the author contact address to EMAIL.");
|
||||
ap.description = "Finds all the strings that need to be localized, by"..
|
||||
" searching for things\n like `S(\"foobar\")'.";
|
||||
ap.footer = "Report bugs to <lkaezadl3@gmail.com>.";
|
||||
|
||||
local opts, files = ap:parse(arg);
|
||||
|
||||
if (#files == 0) then
|
||||
files = listdir("*.lua");
|
||||
if ((not files) or (#files == 0)) then
|
||||
io.stderr:write(APPNAME..": no input files\n");
|
||||
os.exit(-1);
|
||||
end
|
||||
end
|
||||
|
||||
local buffer = StringOutput();
|
||||
|
||||
buffer:write("\n");
|
||||
|
||||
if (opts.langname) then
|
||||
buffer:write("# Language: "..opts.langname.."\n");
|
||||
end
|
||||
if (opts.author) then
|
||||
buffer:write("# Author: "..opts.author);
|
||||
if (opts.mail) then
|
||||
buffer:write(" <"..opts.mail..">");
|
||||
end
|
||||
buffer:write("\n");
|
||||
end
|
||||
if (opts.author or opts.langname) then
|
||||
buffer:write("\n");
|
||||
end
|
||||
|
||||
for _, file in ipairs(files) do
|
||||
local infile, e = io.open(file);
|
||||
if (not infile) then
|
||||
io.stderr:write(APPNAME..": "..e.."\n");
|
||||
os.exit(1);
|
||||
end
|
||||
for line in infile:lines() do
|
||||
local s = line:match('.-S%("([^"]*)"%).*');
|
||||
if (s) then
|
||||
buffer:write(s.." = \n");
|
||||
end
|
||||
end
|
||||
infile:close();
|
||||
end
|
||||
|
||||
local outfile, e;
|
||||
if (opts.output) then
|
||||
outfile, e = io.open(opts.output, "w");
|
||||
if (not outfile) then
|
||||
io.stderr:write(APPNAME..": "..e.."\n");
|
||||
os.exit(1);
|
||||
end
|
||||
else
|
||||
outfile = io.stdout;
|
||||
end
|
||||
|
||||
buffer:flush(outfile);
|
||||
|
||||
if (outfile ~= io.stdout) then
|
||||
outfile:close();
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
main(arg);
|
Loading…
Reference in New Issue
Block a user