Added i18n using gettext. Works only for program, not web sites.

This commit is contained in:
eraviart 2004-02-08 16:38:30 +00:00
parent c00bc5f046
commit 4311af06bb
13 changed files with 323 additions and 16 deletions

View File

@ -1,2 +1,3 @@
*~
*.pyc
locale

View File

@ -1,6 +1,11 @@
PROJECT_DIR=glasnost-xml
DIST_FILE=glasnost-xml.tar.gz
PYTHON_VERSION=2.3
FIND=find
PYGETTEXT=pygettext
dist-clean:
dist: dist-clean
@ -15,3 +20,21 @@ dist: dist-clean
tar czf ../$(DIST_FILE) $(PROJECT_DIR)
rm -rf $(PROJECT_DIR)
mo: po/fr.po
-mkdir -p locale/fr/LC_MESSAGES
msgfmt --statistics -c -v -o locale/fr/LC_MESSAGES/glasnost-xml.mo po/fr.po
for F in `$(FIND) . -type f -name '*.xml.in'`; \
do \
intltool-merge -x po/ $$F `dirname $$F`/`basename $$F .in`; \
done
translations:
-cp po/fr.po po/fr.pox
for F in `$(FIND) . -type f -name '*.xml.in'`; \
do \
intltool-extract --type=gettext/xml $$F; \
done
$(PYGETTEXT) -d po/messages -k N_ \
`$(FIND) . -type f -name "*.py"` \
`$(FIND) . -type f -name "*.xml.in.h"`
msgmerge -o po/fr.po -D po fr.pox messages.pot

View File

@ -29,7 +29,7 @@
__version__ = '$Revision$'[11:-2]
applicationName = 'abracadabra-devel' # Changed on "make install".
applicationName = 'glasnost-xml' # Changed on "make install".
versionNumber = '(Unreleased CVS Version)' # Changed on "make install".
@ -42,4 +42,3 @@ __builtin__.__dict__['_'] = lambda x: x
import context
context.clear()
context.push()

1
po/.cvsignore Normal file
View File

@ -0,0 +1 @@
*.pox

105
po/fr.po Normal file
View File

@ -0,0 +1,105 @@
# Glasnost-XML
# By: Frederic Peters <fpeters@entrouvert.be>
# Emmanuel Raviart <eraviart@entrouvert.com>
# Copyright (C) 2003 Entr'ouvert & Emmanuel Raviart
# Copyright (C) 2004 Entr'ouvert, Frederic Peters & Emmanuel Raviart
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
msgid ""
msgstr ""
"Project-Id-Version: Glasnost-XML $Revision$\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: Sun Feb 8 16:54:53 2004\n"
"PO-Revision-Date: 2004-02-07 16:37+0100\n"
"Last-Translator: Emmanuel Raviart <eraviart@entrouvert.com>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: abracadabra/dataholders.py:135
msgid "Untitled Object #%s"
msgstr "Objet sans titre #%s"
#: abracadabra/descriptions.py:169
msgid ":"
msgstr " :"
#: abracadabra/descriptions.py:653
msgid "Date Selector"
msgstr "Sélecteur de date"
#: abracadabra/descriptions.py:674
msgid "Unknown Label"
msgstr "Sans étiquette"
#: abracadabra/elections.py:56
msgid "Unknown Subject"
msgstr "Sans sujet"
#: abracadabra/groups.py:45
msgid "Everybody"
msgstr "Tout le monde"
#: abracadabra/http.py:185
msgid "Alert"
msgstr "Alerte"
#: abracadabra/http.py:192
msgid "OK"
msgstr "OK"
#: abracadabra/identities.py:153
msgid "Anonymous Identity #%s"
msgstr "Identité anonyme #%s"
#: abracadabra/libertyalliance.py:292
msgid "Liberty Alliance authentication response failed (reason = %s)."
msgstr "La réponse a l'identification Liberty Alliance a échoué (raison = %s)."
#: abracadabra/libertyalliance.py:295 abracadabra/libertyalliance.py:425
#: abracadabra/libertyalliance.py:514
msgid "Failure"
msgstr "Panne"
#: abracadabra/libertyalliance.py:421
msgid "Identification creation request failed (reason = %s)."
msgstr ""
#: abracadabra/libertyalliance.py:463
msgid "Identification creation request succeeded."
msgstr ""
#: abracadabra/libertyalliance.py:465
msgid "Success"
msgstr ""
#: abracadabra/libertyalliance.py:510
msgid "Liberty Alliance authentication failed on identity provider."
msgstr ""
#: abracadabra/pagesequences.py:100
msgid "Previous"
msgstr ""
#: abracadabra/pagesequences.py:117
msgid "Next"
msgstr ""
#: server.py:400
msgid "New %s"
msgstr "Nouveau %s"
#~ msgid "Basic Functionalities"
#~ msgstr "Fonctionalités de base"

90
po/messages.pot Normal file
View File

@ -0,0 +1,90 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: Sun Feb 8 16:54:53 2004\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
"Generated-By: pygettext.py 1.5\n"
#: ./abracadabra/dataholders.py:135
msgid "Untitled Object #%s"
msgstr ""
#: ./abracadabra/descriptions.py:169
msgid ":"
msgstr ""
#: ./abracadabra/descriptions.py:653
msgid "Date Selector"
msgstr ""
#: ./abracadabra/descriptions.py:674
msgid "Unknown Label"
msgstr ""
#: ./abracadabra/elections.py:56
msgid "Unknown Subject"
msgstr ""
#: ./abracadabra/groups.py:45
msgid "Everybody"
msgstr ""
#: ./abracadabra/http.py:185
msgid "Alert"
msgstr ""
#: ./abracadabra/http.py:192
msgid "OK"
msgstr ""
#: ./abracadabra/identities.py:153
msgid "Anonymous Identity #%s"
msgstr ""
#: ./abracadabra/libertyalliance.py:292
msgid "Liberty Alliance authentication response failed (reason = %s)."
msgstr ""
#: ./abracadabra/libertyalliance.py:295 ./abracadabra/libertyalliance.py:425
#: ./abracadabra/libertyalliance.py:514
msgid "Failure"
msgstr ""
#: ./abracadabra/libertyalliance.py:421
msgid "Identification creation request failed (reason = %s)."
msgstr ""
#: ./abracadabra/libertyalliance.py:463
msgid "Identification creation request succeeded."
msgstr ""
#: ./abracadabra/libertyalliance.py:465
msgid "Success"
msgstr ""
#: ./abracadabra/libertyalliance.py:510
msgid "Liberty Alliance authentication failed on identity provider."
msgstr ""
#: ./abracadabra/pagesequences.py:100
msgid "Previous"
msgstr ""
#: ./abracadabra/pagesequences.py:117
msgid "Next"
msgstr ""
#: ./server.py:400
msgid "New %s"
msgstr ""

View File

@ -27,14 +27,13 @@
"""HTTP Server"""
# TODO :
# - Validate each XML file using its XML Schema.
import __builtin__
import cgi
import Cookie
import cStringIO
import gettext
import grp
import locale
import logging
from optparse import OptionParser
import os
@ -124,7 +123,6 @@ class Application(object):
break
else:
virtualHost = virtualHosts[0]
context.setVar('virtualHost', virtualHost)
context.push(
_level = "handleHttpCommand",
@ -139,10 +137,13 @@ class Application(object):
fieldStorage = None,
httpRequestHandler = httpRequestHandler,
httpScriptDirectoryPath = None,
languageSetInUrl = False,
readLanguages = None,
session = None,
uriAuthority = httpRequestHandler.headers.get("Host"),
uriAuthority = httpRequestHandler.headers["Host"],
uriScheme = context.getVar("httpServer").uriScheme,
user = None,
virtualHost = virtualHost,
virtualHostDirectoryPath = virtualHost.documentRoot,
)
try:
@ -201,7 +202,7 @@ class Application(object):
environ["CONTENT_TYPE"] = httpRequestHandler.headers.typeheader
if not "content-type" in fakeHeaders:
fakeHeaders["content-type"] = environ["CONTENT_TYPE"]
length = httpRequestHandler.headers.getheader("content-length")
length = httpRequestHandler.headers.get("content-length")
if length:
environ["CONTENT_LENGTH"] = length
if httpQuery:
@ -265,6 +266,25 @@ class Application(object):
if user is not None:
context.setVar("user", user)
# Look for the right languages to use for HTTP answer.
languages = []
if user is not None:
userLanguage = user.language
if userLanguage is not None:
languages = [userLanguage]
if not languages \
and httpRequestHandler.headers.has_key('Accept-Language'):
languages = [
language.strip()[:2]
for language in httpRequestHandler.headers[
'Accept-Language'].split(',')
if language.strip()]
virtualHostLanguage = virtualHost.language
if virtualHostLanguage not in languages:
languages.append(virtualHostLanguage)
context.setVar('readLanguages', languages)
self.setGettextLanguage()
try:
try:
rootDataHolder.walk(
@ -313,22 +333,48 @@ class Application(object):
"yep", "http://abracadabra.entrouvert.org/0.0")
xpaths.registerFunctions(self.xpathContext)
def setGettextLanguage(self):
virtualHost = context.getVar('virtualHost')
language = context.getVar('readLanguages')[0]
try:
locale.setlocale(locale.LC_COLLATE, (language, None))
except locale.Error:
locale.setlocale(locale.LC_COLLATE, 'C')
domains = [abracadabra.applicationName] + (virtualHost.locales or [])
localeDirectoryPath = 'locale' # FIXME: '/usr/share/locale'
translation = gettext.NullTranslations()
for domain in domains:
try:
domainTranslation = gettext.translation(
domain, localeDirectoryPath, [language])
except IOError:
continue
if not hasattr(domainTranslation, '_catalog'):
continue
if translation.__class__.__name__ == 'NullTranslations':
translation = domainTranslation
continue
translation._catalog.update(domainTranslation._catalog)
__builtin__.__dict__['_'] = translation.gettext
def stop(self):
xpaths.unregisterFunctions(self.xpathContext)
self.xpathContext.xpathFreeContext()
del self.xpathContext
class VirtualHost:
errorDocuments = None
serverAddress = None # (ip, port) tuple
serverName = None
class VirtualHost(object):
documentRoot = None
errorDocuments = None
isSsl = False
talkback = False
language = 'en'
locales = None
serverAddress = None # (ip, port) tuple
serverAdmin = None
serverName = None
SSLCertificateFile = None
SSLCertificateKeyFile = None
talkback = False
def __init__(self):
self.errorDocuments = {}
@ -474,7 +520,7 @@ if __name__ == "__main__":
action = "store_false",
default = True,
help = "run main process in foreground, for process supervisors")
(options, args) = parser.parse_args()
if options.logLevel.upper() not in logging._levelNames.keys():
@ -482,7 +528,7 @@ if __name__ == "__main__":
# Configure root logger.
logger = logging.getLogger()
handler = logging.FileHandler("/tmp/glasnost-xml.log")
handler = logging.FileHandler("/var/log/glasnost-xml.log")
logger.addHandler(handler)
logger.setLevel(logging._levelNames[options.logLevel.upper()])

View File

@ -131,6 +131,16 @@ class Identity(dataholders.Xml):
return None
return Identifications(nodes[0], dataHolder = self)
def getLanguage(self):
nodes = self.evaluateXpath("yep:language")
if nodes:
return nodes[0].content
personLocation = self.personLocation
if not personLocation:
return None
person = dataholders.walkToLocation(personLocation)
return person.language
def getPersonLocation(self):
nodes = self.evaluateXpath("yep:person/@src")
if not nodes:
@ -201,6 +211,7 @@ class Identity(dataholders.Xml):
tokenNode.setContent(voteToken)
identifications = property(getIdentifications, setIdentifications)
language = property(getLanguage)
personLocation = property(getPersonLocation, setPersonLocation)
simpleLabel = property(getSimpleLabel)

View File

@ -45,6 +45,12 @@ class Person(dataholders.Xml):
return None
return nodes[0].content
def getLanguage(self):
nodes = self.evaluateXpath("yep:language")
if not nodes:
return None
return nodes[0].content
def getSimpleLabel(self):
return self.fullName
@ -68,6 +74,7 @@ class Person(dataholders.Xml):
authorizations = property(getAuthorizations, setAuthorizations)
fullName = property(getFullName, setFullName)
language = property(getLanguage)
simpleLabel = property(getSimpleLabel)

View File

@ -32,6 +32,17 @@
<xforms:input ref="yep:person">
<xforms:label>Person</xforms:label>
</xforms:input>
<xforms:select1 appearance="minimal" ref="yep:language">
<xforms:label>Language</xforms:label>
<xforms:item>
<xforms:label>English</xforms:label>
<xforms:value>en</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Français</xforms:label>
<xforms:value>fr</xforms:value>
</xforms:item>
</xforms:select1>
</xforms:group>
</yep:body>
</yep:page>

View File

@ -17,6 +17,17 @@
<xforms:input ref="yep:email">
<xforms:label>Courriel</xforms:label>
</xforms:input>
<xforms:select1 appearance="minimal" ref="yep:language">
<xforms:label>Language</xforms:label>
<xforms:item>
<xforms:label>English</xforms:label>
<xforms:value>en</xforms:value>
</xforms:item>
<xforms:item>
<xforms:label>Français</xforms:label>
<xforms:value>fr</xforms:value>
</xforms:item>
</xforms:select1>
<xforms:select appearance="full" ref="yep:authorizations">
<xforms:label>Caractéristiques</xforms:label>
<xforms:item>

View File

@ -52,6 +52,7 @@
</xsd:complexType>
</xsd:element>
<xsd:element name="person" type="Person"/>
<xsd:element name="language" type="xsd:language"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>

View File

@ -37,6 +37,7 @@
<xsd:sequence>
<xsd:element name="fullName" type="xsd:string"/>
<xsd:element name="email" type="xsd:string"/>
<xsd:element name="language" type="xsd:language"/>
<xsd:element name="authorizations" type="xsd:string"/>
</xsd:sequence>
</xsd:extension>