application development - separating content from function

Max M maxm at mxm.dk
Thu Jun 20 19:06:48 EDT 2002


Chris Liechti wrote:

> start with:
> def _(s): return s
> 
> and write all the string you want to localize later with 
> _("hello")
> _("this can be translated later by replacing the _ function")
> 
> later you can use locale or a simple dictionary lookup to translate.
> i saw that technique in "cplay" http://www.tf.hut.fi/~flu/cplay/
> 
> simple with a dict:
> translation = {'hello':'ole'}
> def _(s):
>     	return translation.get(s,s)


I have a small class I sometimes use when I need to do i18n.

class I18n:

     "Very simple i18n"

     def __init__(self, textBundle, language='uk'):
         self.language = language
         self.textBundle = textBundle

     def __call__(self, id):
         language = getattr(self, 'language', self.language)
         return self.textBundle[language].get(id,
                                     'TRANSLATION MISSING!!')

textBundle = {}

textBundle['dk'] = {}
textBundle['dk']['name'] = 'Navn'
textBundle['dk']['address'] = 'Danmark'
textBundle['dk']['date_format'] = '%d-%m-%Y %H:%M:%S'
textBundle['dk']['money_format'] = '%.2f'
textBundle['uk'] = {}
textBundle['uk']['name'] = 'Name'
textBundle['uk']['address'] = 'Denmark'
textBundle['uk']['date_format'] = '%Y.%m.%d %H:%M:%S'
textBundle['uk']['money_format'] = '%.2f'

language = 'dk'

i18n = I18n(textBundle, language)

print i18n('names')

This works to my satisfaction most of the time.



##############################################################

A pattern I frequently use is the view- Adapter/wrapper::

The idea is that the "logic" object is a python object with python types 
and methods as attributes.

To make a view of this ie. on a html page I create an adapter that takes 
the logic view as a creation parameter, and then it puts a shell/wrapper 
around the logic object to create a view without changing anything in 
the logic object.



###############################################################

## The logic object

class User:

     def __init__(self):
         self.firstName = 'Max'
         self.lastName = 'Møller <b>Sneaky code</b>'
         self.answer = 42
         self.books = [
                 'Learning Python',
                 'Python Essential Reference',
                 'Python & XML'
         ]

## The view adapter/wrapper

# allways escape untrusted code !
import cgi

class HtmlViewAdapter:

     "this can be subclassed for several views"

     def __init__(self, obj):
         self._obj = obj

     def __getitem__(self, item):
         "Makes usage from string formatting easy"
         value = getattr(self, item)
         if callable(value):
             return str(value())
         else:
             return str(value)


class UserHtmlViewAdapter(HtmlViewAdapter):

     def firstName(self):
         return cgi.escape(self._obj.firstName)

     def lastName(self):
         return cgi.escape(self._obj.lastName)

     def answer(self):
         return self._obj.lastName

     def books(self):
         return '\n'.join(
             ['Title %s<br>' % cgi.escape(book) \
                  for book in self._obj.books])


userTemplate = """
<table>
<tr>
     <td>
         <b>First name:</b> %(firstName)s<br>
     </td>
     <td>
         <b>Last name:</b> %(lastName)s<br>
     </td>
     <td>
         <b>Answer:</b> %(answer)s<br>
     </td>
     <td>
         <b>Booklist:</b><br>
         %(books)s<br>
     </td>
</tr>
</table>
"""


user = User()

# pass the view adapter the user
userHtmlViewAdapter = UserHtmlViewAdapter(user)

# render the view
userHtmlView = userTemplate % userHtmlViewAdapter

print userHtmlView

###############################################################


The userHtmlViewAdapter can also be used for outputting directly to an 
.asp page like:

###############

<% userView = UserHtmlViewAdapter(user) %>
<%
<table>
<tr>
     <td>
         <b>First name:</b> <%= userView.firstName()%><br>
     </td>
     <td>
         <b>Last name:</b> <%= userView.lastName()%><br>
     </td>
     <td>
         <b>Answer:</b> <%= userView.answer()%><br>
     </td>
     <td>
         <b>Booklist:</b><br>
         <%= userView.books()%><br>
     </td>
</tr>
</table>
%>

This gives a good seperation of logic and view, and it's easy to see 
months later where to put the generation of view.

You can also keep as much of your code as possible in pure Python 
modules this way. Making it easier to test, debug, cross-platform etc.


regards Max M




More information about the Python-list mailing list