[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: String access & metamethods
- From: Eike Decker <eike@...>
- Date: Thu, 13 Dec 2007 12:17:51 +0100
Many mails on this topic... but actually the whole discussion is maybe a bit
artificial since using strings just in place is commonly seen as a bad idea.
Let's assume that there is a general message that is displayed in various
places - and thus multiple locations in your sourcecode. If it has a typo in
it, you will need to replace it in all location. And if you miss a string to be
replaced, you end up with different messages.
Actually this whole thing does not need any metatables at all. Just use a table
like in Steve's approach and replace the message table when needed:
text = {}
language = {
english = {
error_runtime = "Runtime error",
error_nosuchlanguage = "The specified language does not exist"
},
german = {
error_runtime = "Laufzeit Fehler",
error_nosuchlanguage = "Die angegebene Sprache existiert nicht"
}
}
function setlanguage (lan)
text = assert(language[lan], text.error_nosuchlanguage)
end
setlanguage("english")
---
In order to construct strings that contain further information a function should
be called to build that string. So the __index metamethod could do that, however
it wouldn't be so wise in my opinion, since it is not very transparent. Like if
it would take the string "The language ${language} ..." and could then use the
global value of the variable "language", but maybe we need a local value to be
put in that cannot be looked up (easily). We could therefore also use a value
lookuptable with values that should be replaced in the strings, but I think
calling a string building function with the given values for the strings would
be more clever. We can use a function with an upvalue to the string so that
each textpattern is a function that is to be called with a replacement table as
argument:
function textformater(str)
return function (values)
return (str:gsub("${([^}]-)}",
function (var) return values[var] or "${"..var.."??}" end))
end
end
text = {}
language = {
english = {
error_nosuchlanguage = "I couldn't find the ${language} in my dictionary",
error_idk = "I dunno know: ${weirderror}",
msg_noidea = "no idea what is wrong now",
},
german = {
error_nosuchlanguage = "Eine Sprache namens ${language} existiert "..
"nicht in meinem Wörterbuch",
error_idk = "Keine Ahnung: ${weirderror}",
msg_noidea = "Null ahnung was nun falsch ist",
}
}
text.error_nosuchlanguage = textformater(language.english.error_nosuchlanguage)
-- ^^ make sure that this exists on first call
function setlanguage (lan)
local newtext = {}
for i,v in pairs(
assert(language[lan], text.error_nosuchlanguage {language=lan})
) do
newtext[i] = textformater(v)
end
text = newtext
end
-- end of the lib, now some examples how to use:
setlanguage("english")
print(text.error_idk { weirderror = text.msg_noidea() })
setlanguage("german")
print(text.error_idk { weirderror = text.msg_noidea() })
setlanguage("french") -- error
It requires a bit more writing, but in my experience that little extrawork is
rewarded when looking over the code again - named parameterlists much easier to
understand than argument lists.
The syntax could also be varied to make it a bit shorter.
It could also be checked if the dictionary has all the strings that are required
- by counterchecking it with the previous dictionary.
In the end, there are many flexible ways to implement a localization system. I
would just discourage the use of strings inside the source code like
error("somemessage") - I do this too, but it doesn't matter in my case since I
don't publish the scripts I make. If I would, I would extract all the strings
and replace it in one of the ways described above once I see the need for it
(like localization).
Eike
> On Dec 12, 2007 9:36 PM, Brett Kugler <bkugler@gmail.com> wrote:
> > I realize I could simplify my life with a little extra notation, but the
> > hope was to really not change my existing code at all and have the string
> > literals be reinterpreted based on a new metamethod instead. Am I
> grasping
> > at straws?
>
> This solution is akin to Stefan's: we create a special table 'msg'
> which has an __index metamethod which returns the appropriate string
> version of the message word.
>
> -- messages.lua
> local translations = {
> DidNotWork = {
> "Operation did not succeed",
> "Operasie het nie geslaag nie"
> },
> NoAnswer = {
> "No answer found",
> "Antwoord nie gefind nie",
> }
> }
>
> msg = {
> English = 1,
> Afrikaans = 2,
> }
>
> function get_translation(t,word)
> local res = translations[word][msg.language]
> if not res then return "cannot find translation" end
> return res
> end
>
> setmetatable(msg,{__index=get_translation})
>
> And it would be used like this:
>
> require 'messages"
> msg.language = msg.Afrikaans
> print(msg.DidNotWork)
> print(msg.NoAnswer)
>
>
> steve d.
>