lua-users home
lua-l archive

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