lua-users home
lua-l archive

Re: String access & metamethods

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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.
> 

AltStyle によって変換されたページ (->オリジナル) /