diff --git a/README.txt b/README.txt
index 3938d8b..67a4ae7 100755
--- a/README.txt
+++ b/README.txt
@@ -4,7 +4,7 @@ loops - Linked Objects for Organization and Processing Services
 
   ($Id$)
 
-The loops platform is built up of three parts:
+The loops platform consists up of three basic types of objects:
 
 (1) concepts: simple interconnected objects usually representing
     meta-information
diff --git a/browser/common.py b/browser/common.py
index a56cb7a..9a00d5c 100644
--- a/browser/common.py
+++ b/browser/common.py
@@ -106,6 +106,7 @@ class BaseView(GenericView, I18NView):
         # TODO: get rid of removeSecurityProxy() call
         self.context = removeSecurityProxy(context)
         self.setSkin(self.loopsRoot.skinName)
+        self.checkLanguage()
         try:
             if not canAccess(context, 'title'):
                 raise Unauthorized
diff --git a/browser/loops.css b/browser/loops.css
index ba29dc1..a01dd3b 100644
--- a/browser/loops.css
+++ b/browser/loops.css
@@ -31,6 +31,12 @@ table.listing td {
     margin-left: 5px;
 }
 
+.top-actions{
+    position: absolute;
+    right: 2em;
+    top: 1em;
+}
+
 /*.content-1 h1 { */
 h1 {
     font-size: 160%;
@@ -98,6 +104,14 @@ div.image {
     margin-right: 5px;
 }
 
+img.selected {
+    border: 2px solid #d6dcf6;
+}
+
+img.notselected {
+    border: 2px solid #eff8ff;
+}
+
 
 /* search stuff */
 
diff --git a/browser/node.py b/browser/node.py
index 632478e..75b3d8d 100644
--- a/browser/node.py
+++ b/browser/node.py
@@ -46,6 +46,7 @@ from cybertools.browser import configurator
 from cybertools.browser.view import GenericView
 from cybertools.typology.interfaces import IType, ITypeManager
 from cybertools.xedit.browser import ExternalEditorView
+from loops.i18n.browser import i18n_macros
 from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode
 from loops.interfaces import IViewConfiguratorSchema
 from loops.resource import MediaAsset
@@ -77,6 +78,8 @@ class NodeView(BaseView):
                     media='all', position=3)
         cm.register('js', 'loops.js', resourceName='loops.js')
         #cm.register('js', 'loops.js', resourceName='loops1.js')
+        cm.register('top_actions', 'top_actions', name='multi_actions',
+                    subMacros=[i18n_macros.macros['language_switch']])
         cm.register('portlet_left', 'navigation', title='Navigation',
                     subMacro=node_macros.macros['menu'])
         #if not IUnauthenticatedPrincipal.providedBy(self.request.principal):
diff --git a/browser/node_macros.pt b/browser/node_macros.pt
index f339b7b..0a53a44 100644
--- a/browser/node_macros.pt
+++ b/browser/node_macros.pt
@@ -200,7 +200,12 @@
 
 
 
-
+
+
+
+    blubb
+
+
 
 
       
diff --git a/helpers.txt b/helpers.txt
index 2fe8b04..29d11f9 100755
--- a/helpers.txt
+++ b/helpers.txt
@@ -10,39 +10,22 @@ package.
 
 Let's first do some basic imports
 
-  >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
-  >>> site = placefulSetUp(True)
-
   >>> from zope import interface, component
   >>> from zope.interface import Interface
   >>> from zope.publisher.browser import TestRequest
 
-and provide a relation registry:
+  >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
+  >>> site = placefulSetUp(True)
 
-  >>> from cybertools.relation.tests import IntIdsStub
-  >>> component.provideUtility(IntIdsStub())
-  >>> from cybertools.relation.registry import DummyRelationRegistry
-  >>> component.provideUtility(DummyRelationRegistry())
+  >>> from loops.tests.setup import TestSite
+  >>> t = TestSite(site)
+  >>> concepts, resources, views = t.setup()
 
-and care for some type adapter machinery:
-
-  >>> from loops.interfaces import IConcept
-  >>> from loops.interfaces import ITypeConcept
-  >>> from loops.type import TypeConcept
-  >>> component.provideAdapter(TypeConcept, (IConcept,), ITypeConcept)
-
-Now we can setup a simple loops site with its manager objects, using a
-loops setup manager:
-
-  >>> from loops.base import Loops
-  >>> loopsRoot = site['loops'] = Loops()
-
-  >>> from loops.setup import SetupManager
-  >>> setup = SetupManager(loopsRoot)
-  >>> concepts, resources, views = setup.setup()
   >>> concepts['hasType'].title
   u'has Type'
 
+  >>> loopsRoot = site['loops']
+
 We also add some example concepts,
 
   >>> from loops.concept import Concept
@@ -217,6 +200,8 @@ for controlling e.g. storage for external files.
   >>> ef1_type = IType(ef1)
   >>> IType(ef1).options
   []
+
+  >>> from loops.type import TypeConcept
   >>> extfile_ad = TypeConcept(extfile)
   >>> extfile_ad.options = ['dummy', 'storage:varsubdir',
   ...           'storage_parameters:extfiles']
@@ -239,11 +224,9 @@ get a type manager from all loops objects, always with the same context:
   True
 
   >>> types = typeManager.types
-  >>> sorted(t.token for t in types)
-  ['.loops/concepts/domain', '.loops/concepts/file', '.loops/concepts/note',
-   '.loops/concepts/predicate', '.loops/concepts/query',
-   '.loops/concepts/textdocument', '.loops/concepts/topic',
-   '.loops/concepts/type']
+  >>> typeTokens = sorted(t.token for t in types)
+  >>> len(typeTokens)
+  9
 
   >>> typeManager.getType('.loops/concepts/topic') == cc1_type
   True
@@ -252,14 +235,13 @@ The listTypes() method allows us to select types that fulfill a certain
 condition:
 
   >>> types = typeManager.listTypes(include=('concept',))
-  >>> sorted(t.token for t in types)
-  ['.loops/concepts/domain', '.loops/concepts/predicate',
-   '.loops/concepts/query', '.loops/concepts/topic', '.loops/concepts/type']
+  >>> typeTokens = sorted(t.token for t in types)
+  >>> len(typeTokens)
+  6
   >>> types = typeManager.listTypes(exclude=('concept',))
-  >>> sorted(t.token for t in types)
-  ['.loops/concepts/file',  '.loops/concepts/note',
-   '.loops/concepts/textdocument']
-
+  >>> typeTokens = sorted(t.token for t in types)
+  >>> len(typeTokens)
+  3
 
 Type-based interfaces and adapters
 ----------------------------------
@@ -273,6 +255,7 @@ will be adaptable to. The default for this is None:
 For concept objects that provide types (type providers) the value of
 the typeInterface attribute is the ITypeConcept interface:
 
+  >>> from loops.interfaces import ITypeConcept
   >>> topic_type.typeInterface is ITypeConcept
   True
 
@@ -286,6 +269,7 @@ i.e. the 'topic' concept, via an adapter:
   >>> class Topic(object):
   ...     implements(ITopic)
   ...     def __init__(self, context): pass
+  >>> from loops.interfaces import IConcept
   >>> component.provideAdapter(Topic, (IConcept,), ITopic)
 
   >>> ITypeConcept(topic).typeInterface = ITopic
@@ -306,7 +290,7 @@ browser views; among others also some important informations about the
 context object's type:
 
   >>> from loops.browser.common import BaseView
-  >>> view = BaseView(cc1, TestRequest)
+  >>> view = BaseView(cc1, TestRequest())
   >>> view.typeTitle
   u'Topic'
   >>> view.typeInterface
diff --git a/i18n/browser.py b/i18n/browser.py
index 7f5e820..fbc632e 100644
--- a/i18n/browser.py
+++ b/i18n/browser.py
@@ -24,6 +24,7 @@ $Id$
 
 from zope import interface, component
 from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.app.session.interfaces import ISession
 from zope.cachedescriptors.property import Lazy
 from zope.i18n.interfaces import IUserPreferredLanguages
 from zope.i18n.negotiator import negotiator
@@ -31,6 +32,11 @@ from zope.i18n.negotiator import negotiator
 from loops.common import adapted
 
 
+packageId = 'loops.i18n.browser'
+
+i18n_macros = ViewPageTemplateFile('i18n_macros.pt')
+
+
 class LanguageInfo(object):
 
     def __init__(self, context, request):
@@ -80,8 +86,10 @@ class I18NView(object):
         return adapted(self.context, self.languageInfo)
 
     def checkLanguage(self):
-        # get language from session
-        self.setPreferredLanguage()
+        session = ISession(self.request)[packageId]
+        lang = session.get('language')
+        if lang:
+            self.setLanguage(lang)
 
     def setLanguage(self, lang=None):
         lang = lang or self.request.form.get('lang')
@@ -89,10 +97,12 @@ class I18NView(object):
             upl = IUserPreferredLanguages(self.request)
             upl.setPreferredLanguages([lang])
 
-    def switchLanguage(self, lang=None, keep=False):
+    def switchLanguage(self):
+        lang = self.request.form.get('loops.language')
         keep = self.request.form.get('keep')
         if keep:
-            pass # set in session
-        self.setPreferredLanguage(lang)
+            session = ISession(self.request)[packageId]
+            session['language'] = lang
+        self.setLanguage(lang)
         return self()
 
diff --git a/i18n/configure.zcml b/i18n/configure.zcml
index 7a78860..539eb27 100644
--- a/i18n/configure.zcml
+++ b/i18n/configure.zcml
@@ -5,8 +5,10 @@
    xmlns="http://namespaces.zope.org/browser"
    i18n_domain="zope">
 
+  
+
   
diff --git a/i18n/flags/de.gif b/i18n/flags/de.gif
new file mode 100755
index 0000000..75728dd
Binary files /dev/null and b/i18n/flags/de.gif differ
diff --git a/i18n/flags/en.gif b/i18n/flags/en.gif
new file mode 100644
index 0000000..3c6bce1
Binary files /dev/null and b/i18n/flags/en.gif differ
diff --git a/i18n/flags/it.gif b/i18n/flags/it.gif
new file mode 100755
index 0000000..d79e90e
Binary files /dev/null and b/i18n/flags/it.gif differ
diff --git a/i18n/flags/pl.gif b/i18n/flags/pl.gif
new file mode 100755
index 0000000..bf10646
Binary files /dev/null and b/i18n/flags/pl.gif differ
diff --git a/i18n/i18n_macros.pt b/i18n/i18n_macros.pt
new file mode 100644
index 0000000..30cfe2d
--- /dev/null
+++ b/i18n/i18n_macros.pt
@@ -0,0 +1,14 @@
+
+
+
+    
+       +    
+
diff --git a/search/README.txt b/search/README.txt
index 97491e7..9768427 100755
--- a/search/README.txt
+++ b/search/README.txt
@@ -17,29 +17,17 @@ and setup a simple loops site with a concept manager and some concepts
 (with all the type machinery, what in real life is done via standard
 ZCML setup):
 
-  >>> from cybertools.relation.registry import DummyRelationRegistry
-  >>> from cybertools.relation.tests import IntIdsStub
-  >>> relations = DummyRelationRegistry()
-  >>> component.provideUtility(relations)
-  >>> component.provideUtility(IntIdsStub())
-
+  >>> from loops.concept import Concept
   >>> from loops.type import ConceptType, TypeConcept
   >>> from loops.interfaces import ITypeConcept
-  >>> component.provideAdapter(ConceptType)
-  >>> component.provideAdapter(TypeConcept)
-
   >>> from loops.base import Loops
-  >>> loopsRoot = site['loops'] = Loops()
+  >>> from loops.tests.setup import TestSite
+  >>> t = TestSite(site)
+  >>> concepts, resources, views = t.setup()
 
-  >>> from loops.setup import SetupManager
-  >>> setup = SetupManager(loopsRoot)
-  >>> concepts, resources, views = setup.setup()
-  >>> typeConcept = concepts['type']
-
-  >>> from loops.concept import Concept
-  >>> topic = concepts['topic'] = Concept(u'Topic')
-  >>> for c in (topic,): c.conceptType = typeConcept
+  >>> loopsRoot = site['loops']
   >>> query = concepts['query']
+  >>> topic = concepts['topic'] = Concept(u'Topic')
 
 In addition we create a concept that holds the search page and a node
 (page) that links to this concept:
@@ -103,7 +91,7 @@ a controller attribute for the search view.
 
   >>> searchView.submitReplacing('1.results', '1.search.form', pageView)
   'return submitReplacing("1.results", "1.search.form",
-       "http://127.0.0.1/loops/views/page/.target10/@@searchresults.html")'
+       "http://127.0.0.1/loops/views/page/.target29/@@searchresults.html")'
 
 Basic (text/title) search
 -------------------------
@@ -202,7 +190,7 @@ of the concepts' titles:
   >>> request = TestRequest(form=form)
   >>> view = Search(page, request)
   >>> view.listConcepts()
-  "[['Zope (Topic)', '12']]"
+  "[['Zope (Topic)', '33']]"
 
 Preset Concept Types on Search Forms
 ------------------------------------
@@ -213,8 +201,7 @@ a certain qualifier, via the option attribute of the type interface.
 
 Let's start with a new type, the customer type.
 
-  >>> customer = concepts['customer'] = Concept('Customer')
-  >>> customer.conceptType = typeConcept
+  >>> customer = concepts['customer']
   >>> custType = ITypeConcept(customer)
   >>> custType.options
   []
@@ -239,16 +226,16 @@ and thus include the customer type in the preset search types.
   ('concept', 'search')
   >>> searchView = Search(search, TestRequest())
   >>> list(searchView.presetSearchTypes)
-  [{'token': 'loops:concept:customer', 'title': 'Customer'}]
+  [{'token': 'loops:concept:customer', 'title': u'Customer'}]
 
   >>> searchView.conceptsForType('loops:concept:customer')
   [{'token': 'none', 'title': u'not selected'},
-   {'token': '18', 'title': u'Zope Corporation'},
-   {'token': '19', 'title': u'cyberconcepts'}]
+   {'token': '47', 'title': u'Zope Corporation'},
+   {'token': '49', 'title': u'cyberconcepts'}]
 
 Let's use this new search option for querying:
 
-  >>> form = {'search.4.text_selected': u'18'}
+  >>> form = {'search.4.text_selected': u'47'}
   >>> resultsView = SearchResults(page, TestRequest(form=form))
   >>> results = list(resultsView.results)
   >>> results[0].title
diff --git a/tests/setup.py b/tests/setup.py
index 34056eb..11fcc36 100644
--- a/tests/setup.py
+++ b/tests/setup.py
@@ -13,8 +13,11 @@ from zope.app.catalog.text import TextIndex
 from zope.app.container.interfaces import IObjectRemovedEvent
 from zope.app.security.principalregistry import principalRegistry
 from zope.app.security.interfaces import IAuthentication
+from zope.app.session.interfaces import IClientIdManager, ISessionDataContainer
+from zope.app.session import session
 from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter
 from zope.dublincore.interfaces import IZopeDublinCore
+from zope.interface import implements
 
 from cybertools.composer.schema.factory import SchemaFactory
 from cybertools.composer.schema.field import FieldInstance, NumberFieldInstance
@@ -40,6 +43,12 @@ from loops.schema import ResourceSchemaFactory, FileSchemaFactory, NoteSchemaFac
 from loops.setup import SetupManager, addObject
 from loops.type import LoopsType, ConceptType, ResourceType, TypeConcept
 
+class ClientIdManager(object):
+    """ dummy, for testing only """
+    implements(IClientIdManager)
+    def getClientId(self, request):
+        return 'dummy'
+
 
 class TestSite(object):
 
@@ -58,6 +67,10 @@ class TestSite(object):
         component.provideAdapter(ZDCAnnotatableAdapter, (ILoopsObject,), IZopeDublinCore)
         component.provideAdapter(AttributeAnnotations, (ILoopsObject,))
         component.provideUtility(principalRegistry, IAuthentication)
+        component.provideAdapter(session.ClientId)
+        component.provideAdapter(session.Session)
+        component.provideUtility(session.RAMSessionDataContainer(), ISessionDataContainer)
+        component.provideUtility(ClientIdManager())
 
         component.provideAdapter(LoopsType)
         component.provideAdapter(ConceptType)
+    
+
diff --git a/search/README.txt b/search/README.txt
index 97491e7..9768427 100755
--- a/search/README.txt
+++ b/search/README.txt
@@ -17,29 +17,17 @@ and setup a simple loops site with a concept manager and some concepts
 (with all the type machinery, what in real life is done via standard
 ZCML setup):
 
-  >>> from cybertools.relation.registry import DummyRelationRegistry
-  >>> from cybertools.relation.tests import IntIdsStub
-  >>> relations = DummyRelationRegistry()
-  >>> component.provideUtility(relations)
-  >>> component.provideUtility(IntIdsStub())
-
+  >>> from loops.concept import Concept
   >>> from loops.type import ConceptType, TypeConcept
   >>> from loops.interfaces import ITypeConcept
-  >>> component.provideAdapter(ConceptType)
-  >>> component.provideAdapter(TypeConcept)
-
   >>> from loops.base import Loops
-  >>> loopsRoot = site['loops'] = Loops()
+  >>> from loops.tests.setup import TestSite
+  >>> t = TestSite(site)
+  >>> concepts, resources, views = t.setup()
 
-  >>> from loops.setup import SetupManager
-  >>> setup = SetupManager(loopsRoot)
-  >>> concepts, resources, views = setup.setup()
-  >>> typeConcept = concepts['type']
-
-  >>> from loops.concept import Concept
-  >>> topic = concepts['topic'] = Concept(u'Topic')
-  >>> for c in (topic,): c.conceptType = typeConcept
+  >>> loopsRoot = site['loops']
   >>> query = concepts['query']
+  >>> topic = concepts['topic'] = Concept(u'Topic')
 
 In addition we create a concept that holds the search page and a node
 (page) that links to this concept:
@@ -103,7 +91,7 @@ a controller attribute for the search view.
 
   >>> searchView.submitReplacing('1.results', '1.search.form', pageView)
   'return submitReplacing("1.results", "1.search.form",
-       "http://127.0.0.1/loops/views/page/.target10/@@searchresults.html")'
+       "http://127.0.0.1/loops/views/page/.target29/@@searchresults.html")'
 
 Basic (text/title) search
 -------------------------
@@ -202,7 +190,7 @@ of the concepts' titles:
   >>> request = TestRequest(form=form)
   >>> view = Search(page, request)
   >>> view.listConcepts()
-  "[['Zope (Topic)', '12']]"
+  "[['Zope (Topic)', '33']]"
 
 Preset Concept Types on Search Forms
 ------------------------------------
@@ -213,8 +201,7 @@ a certain qualifier, via the option attribute of the type interface.
 
 Let's start with a new type, the customer type.
 
-  >>> customer = concepts['customer'] = Concept('Customer')
-  >>> customer.conceptType = typeConcept
+  >>> customer = concepts['customer']
   >>> custType = ITypeConcept(customer)
   >>> custType.options
   []
@@ -239,16 +226,16 @@ and thus include the customer type in the preset search types.
   ('concept', 'search')
   >>> searchView = Search(search, TestRequest())
   >>> list(searchView.presetSearchTypes)
-  [{'token': 'loops:concept:customer', 'title': 'Customer'}]
+  [{'token': 'loops:concept:customer', 'title': u'Customer'}]
 
   >>> searchView.conceptsForType('loops:concept:customer')
   [{'token': 'none', 'title': u'not selected'},
-   {'token': '18', 'title': u'Zope Corporation'},
-   {'token': '19', 'title': u'cyberconcepts'}]
+   {'token': '47', 'title': u'Zope Corporation'},
+   {'token': '49', 'title': u'cyberconcepts'}]
 
 Let's use this new search option for querying:
 
-  >>> form = {'search.4.text_selected': u'18'}
+  >>> form = {'search.4.text_selected': u'47'}
   >>> resultsView = SearchResults(page, TestRequest(form=form))
   >>> results = list(resultsView.results)
   >>> results[0].title
diff --git a/tests/setup.py b/tests/setup.py
index 34056eb..11fcc36 100644
--- a/tests/setup.py
+++ b/tests/setup.py
@@ -13,8 +13,11 @@ from zope.app.catalog.text import TextIndex
 from zope.app.container.interfaces import IObjectRemovedEvent
 from zope.app.security.principalregistry import principalRegistry
 from zope.app.security.interfaces import IAuthentication
+from zope.app.session.interfaces import IClientIdManager, ISessionDataContainer
+from zope.app.session import session
 from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter
 from zope.dublincore.interfaces import IZopeDublinCore
+from zope.interface import implements
 
 from cybertools.composer.schema.factory import SchemaFactory
 from cybertools.composer.schema.field import FieldInstance, NumberFieldInstance
@@ -40,6 +43,12 @@ from loops.schema import ResourceSchemaFactory, FileSchemaFactory, NoteSchemaFac
 from loops.setup import SetupManager, addObject
 from loops.type import LoopsType, ConceptType, ResourceType, TypeConcept
 
+class ClientIdManager(object):
+    """ dummy, for testing only """
+    implements(IClientIdManager)
+    def getClientId(self, request):
+        return 'dummy'
+
 
 class TestSite(object):
 
@@ -58,6 +67,10 @@ class TestSite(object):
         component.provideAdapter(ZDCAnnotatableAdapter, (ILoopsObject,), IZopeDublinCore)
         component.provideAdapter(AttributeAnnotations, (ILoopsObject,))
         component.provideUtility(principalRegistry, IAuthentication)
+        component.provideAdapter(session.ClientId)
+        component.provideAdapter(session.Session)
+        component.provideUtility(session.RAMSessionDataContainer(), ISessionDataContainer)
+        component.provideUtility(ClientIdManager())
 
         component.provideAdapter(LoopsType)
         component.provideAdapter(ConceptType)