first commit

This commit is contained in:
Diego Martínez 2013-02-27 04:27:51 -02:00
commit 11b9e0596c
5 changed files with 448 additions and 0 deletions

96
README.txt Normal file
View 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

0
init.lua Normal file
View File

91
intllib.lua Normal file
View 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
View 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
View 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);