Skip to content

Commit e28279f

Browse files
committed
Add language.md Language Features
1 parent 5609b2d commit e28279f

File tree

1 file changed

+256
-0
lines changed

1 file changed

+256
-0
lines changed

docs/i18n/language.md

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
---
2+
html_meta:
3+
"description": "Accessing and changing the language state of Plone programmatically."
4+
"property=og:description": "Accessing and changing the language state of Plone programmatically."
5+
"property=og:title": "Language functions"
6+
"keywords": "Plone, Internationalization, i18n, language, translation, localization"
7+
---
8+
9+
(language-functions-label)=
10+
11+
# Language functions
12+
13+
```{note}
14+
TODO: rework this section.
15+
```
16+
17+
```{admonition} Description
18+
Accessing and changing the language state of Plone programmatically.
19+
```
20+
21+
22+
## Introduction
23+
24+
Each page view has a language associated with it.
25+
26+
The active language is negotiated by the `plone.i18n.negotiator` module.
27+
Several factors may be involved in determining what the language should be:
28+
29+
- Cookies (setting from the language selector)
30+
- The top-level domain name (e.g., `.fi` for Finnish, or `.se` for Swedish)
31+
- Context (current content) language
32+
- Browser language headers
33+
34+
Language is negotiated at the beginning of the page view.
35+
36+
Languages are managed by [portal_languagetool](https://github.com/plone/Products.PloneLanguageTool/blob/master/Products/PloneLanguageTool/LanguageTool.py).
37+
38+
39+
## Getting the current language
40+
41+
The following is an example view/viewlet method of getting the current language.
42+
43+
```python
44+
from Products.Five.browser import BrowserView
45+
from zope.component import getMultiAdapter
46+
47+
class MyView(BrowserView):
48+
49+
#...
50+
51+
def language(self):
52+
"""
53+
@return: Two-letter string, the active language code
54+
"""
55+
context = self.context.aq_inner
56+
portal_state = getMultiAdapter((context, self.request), name=u'plone_portal_state')
57+
current_language = portal_state.language()
58+
return current_language
59+
```
60+
61+
62+
## Getting language of content item
63+
64+
All content objects do not necessarily support the `Language()` look-up defined by the `IDublinCore` interface.
65+
Below is the safe way to extract the served language on the content.
66+
67+
Example `BrowserView` method:
68+
69+
```python
70+
from Acquisition import aq_inner
71+
72+
def language(self):
73+
""" Get the language of the context.
74+
75+
Useful in producing <html> tag.
76+
You need to output language for every HTML page, see http://www.w3.org/TR/xhtml1/#strict
77+
78+
@return: The two letter language code of the current content.
79+
"""
80+
portal_state = self.context.unrestrictedTraverse("@@plone_portal_state")
81+
82+
return aq_inner(self.context).Language() or portal_state.default_language()
83+
```
84+
85+
86+
## Getting available site languages
87+
88+
The following example gets the available languages for a site.
89+
90+
```python
91+
# Python 2.6 compatible ordered dict
92+
# NOTE: API is not 1:1, but for normal dict access of
93+
# set member, iterate keys and values this is enough
94+
try:
95+
from collections import OrderedDict
96+
except ImportError:
97+
from odict import odict as OrderedDict
98+
99+
def getLanguages(self):
100+
"""
101+
Return list of active langauges as ordered dictionary, the preferred first language as the first.
102+
103+
Example output::
104+
105+
{
106+
u'fi': {u'id' : u'fi', u'flag': u'/++resource++country-flags/fi.gif', u'name': u'Finnish', u'native': u'Suomi'},
107+
u'de': {u'id' : u'de', u'flag': u'/++resource++country-flags/de.gif', u'name': u'German', u'native': u'Deutsch'},
108+
u'en': {u'id' : u'en', u'flag': u'/++resource++country-flags/gb.gif', u'name': u'English', u'native': u'English'},
109+
u'ru': {u'id' : u'ru', u'flag': u'/++resource++country-flags/ru.gif', u'name': u'Russian', u'native': u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439'}
110+
}
111+
"""
112+
result = OrderedDict()
113+
114+
portal_languages = self.context.portal_languages
115+
116+
# Get barebone language listing from portal_languages tool
117+
langs = portal_languages.getAvailableLanguages()
118+
119+
preferred = portal_languages.getPreferredLanguage()
120+
121+
# Preferred first
122+
for lang, data in langs.items():
123+
if lang == preferred:
124+
result[lang] = data
125+
126+
# Then other languages
127+
for lang, data in langs.items():
128+
if lang != preferred:
129+
result[lang] = data
130+
131+
# For convenience, include the language ISO code in the export,
132+
# so it is easier to iterate data in the templates
133+
for lang, data in result.items():
134+
data["id"] = lang
135+
136+
return result
137+
```
138+
139+
140+
## Simple language conditions in page templates
141+
142+
If full translation strings are not worth the trouble, you can use conditional expressions in page templates, as in the following example.
143+
144+
```xml
145+
<div class="main-text">
146+
<a tal:condition="python:context.restrictedTraverse('@@plone_portal_state').language() == 'fi'" href="http://www.saariselka.fi/sisalto?force-web">Siirry täydelle web-sivustolle</a>
147+
<a tal:condition="python:context.restrictedTraverse('@@plone_portal_state').language() != 'fi'" href="http://www.saariselka.fi/sisalto?force-web">Go to full website</a>
148+
</div>
149+
```
150+
151+
152+
## Set site language settings
153+
154+
The following example shows how to manually set the language for the site.
155+
156+
```python
157+
# Setup site language settings
158+
portal = context.getSite()
159+
ltool = portal.portal_languages
160+
defaultLanguage = 'en'
161+
supportedLanguages = ['en','es']
162+
ltool.manage_setLanguageSettings(defaultLanguage, supportedLanguages,
163+
setUseCombinedLanguageCodes=False)
164+
```
165+
166+
For unit testing, you need to run the following in `afterSetUp()` after setting up the languages:
167+
168+
```python
169+
# THIS IS FOR UNIT TESTING ONLY
170+
# Normally called by pretraverse hook,
171+
# but must be called manually for the unit tests
172+
# Goes only for the current request
173+
ltool.setLanguageBindings()
174+
```
175+
176+
You can also set the language for the site using `GenericSetup` and `propertiestool.xml`, as in the following example.
177+
178+
```xml
179+
<object name="portal_properties" meta_type="Plone Properties Tool">
180+
<object name="site_properties" meta_type="Plone Property Sheet">
181+
<property name="default_language" type="string">en</property>
182+
</object>
183+
</object>
184+
```
185+
186+
187+
## Customizing language selector
188+
189+
190+
### Making language flags point to different top level domains
191+
192+
If you use multiple domain names for different languages, it is often desirable to make the language selector point to a different domain.
193+
Search engines do not really like the dynamic language switchers, and will index switching links, messing up your site search results.
194+
195+
```{todo}
196+
Provide an example.
197+
```
198+
199+
200+
## Login-aware language negotiation
201+
202+
By default, language negotiation happens before authentication.
203+
If you wish to use authenticated credentials in the negotiation, you can hook the after-traversal event.
204+
205+
The following is an example event registration.
206+
207+
```xml
208+
<configure
209+
xmlns="http://namespaces.zope.org/zope"
210+
xmlns:browser="http://namespaces.zope.org/browser"
211+
xmlns:zcml="http://namespaces.zope.org/zcml"
212+
>
213+
<subscriber handler=".language_negotiation.Negotiator"/>
214+
</configure>
215+
```
216+
217+
And the following is the event's corresponding event handler.
218+
219+
```python
220+
from zope.interface import Interface
221+
from zope.component import adapter
222+
from ZPublisher.interfaces import IPubEvent,IPubAfterTraversal
223+
from Products.CMFCore.utils import getToolByName
224+
from AccessControl import getSecurityManager
225+
from zope.app.component.hooks import getSite
226+
227+
@adapter(IPubAfterTraversal)
228+
def Negotiator(event):
229+
230+
# Keep the current request language (negotiated on portal_languages)
231+
# untouched
232+
233+
site = getSite()
234+
ms = getToolByName(site, 'portal_membership')
235+
member = ms.getAuthenticatedMember()
236+
if member.getUserName() == 'Anonymous User':
237+
return
238+
239+
language = member.language
240+
if language:
241+
# Fake new language for all authenticated users
242+
event.request['LANGUAGE'] = language
243+
event.request.LANGUAGE_TOOL.LANGUAGE = language
244+
else:
245+
lt = getToolByName(site, 'portal_languages')
246+
event.request['LANGUAGE'] = lt.getDefaultLanguage()
247+
event.request.LANGUAGE_TOOL.LANGUAGE = lt.getDefaultLanguage()
248+
```
249+
250+
251+
## Additional resources
252+
253+
- https://reinout.vanrees.org/weblog/2007/12/14/translating-schemata-names.html
254+
- https://maurits.vanrees.org/weblog/archive/2007/09/i18n-locales-and-plone-3.0
255+
- https://web.archive.org/web/20110212185035/http://blogs.ingeniweb.com/blogs/user/7/tag/i18ndude/
256+
- https://web.archive.org/web/20100830122331/http://plone.org/products/archgenxml/documentation/how-to/handling-i18n-translation-files-with-archgenxml-and-i18ndude/view?searchterm=

0 commit comments

Comments
 (0)