# HG changeset patch # User Ingo Weinzierl # Date 1286050955 -7200 # Node ID e6f81aa329b14569e694727a0aa667e4b10c7a12 # Parent cfa9e755a5d26d304a89438655b90afc905de548 Introduced i18n support; german and english translation available. diff -r cfa9e755a5d2 -r e6f81aa329b1 ChangeLog --- a/ChangeLog Sun Sep 05 15:54:44 2010 +0200 +++ b/ChangeLog Sat Oct 02 22:22:35 2010 +0200 @@ -1,3 +1,21 @@ +2010-10-02 Ingo Weinzierl + + * getan/config.py: Setup the language. + + * getan/resources.py: New module that should serve functions for working + with resources. Currently, there is just a single function to get a + translated text based on gettext. + + * getan/states.py, + getan/view.py: Replaced hard coded texts with gettext calls. + + * po/Makefile, + po/README, + po/de.po: Makefile, description and german translation for gettext. + + NOTE: It is necessary to call 'make mo' in the po directory to enable the + german language. + 2010-09-02 Ingo Weinzierl ISSUE1575 diff -r cfa9e755a5d2 -r e6f81aa329b1 getan/config.py --- a/getan/config.py Sun Sep 05 15:54:44 2010 +0200 +++ b/getan/config.py Sat Oct 02 22:22:35 2010 +0200 @@ -7,13 +7,35 @@ # For details see LICENSE coming with the source of 'getan'. # +import locale import logging +import os logger = None def initialize(): + setup_logging() + setup_locale() + + +def setup_logging(): logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s', filename='getan.log', filemode='w') logger = logging.getLogger() + + +def setup_locale(): + for var in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): + if var in os.environ: + break + else: + default_locale = locale.getdefaultlocale() + # The default is normally a tuple of two strings. It may + # contain None, objects under some circumstances, though. + if len(default_locale) > 1: + lang = default_locale[0] + if isinstance(lang, str): + os.environ["LANG"] = lang + diff -r cfa9e755a5d2 -r e6f81aa329b1 getan/resources.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/getan/resources.py Sat Oct 02 22:22:35 2010 +0200 @@ -0,0 +1,13 @@ +import gettext as py_gettext +import os +import sys + +_base_dir = os.path.join(os.path.dirname(__file__), os.pardir) +share_dir = os.path.join(_base_dir, "share") + +def gettext(message): + modir = os.path.join(share_dir, "locale") + t = py_gettext.translation("getan", modir, fallback=True) + + return t.ugettext(message) + diff -r cfa9e755a5d2 -r e6f81aa329b1 getan/states.py --- a/getan/states.py Sun Sep 05 15:54:44 2010 +0200 +++ b/getan/states.py Sat Oct 02 22:22:35 2010 +0200 @@ -12,7 +12,8 @@ import signal from datetime import datetime, timedelta -from getan.utils import human_time +from getan.resources import gettext as _ +from getan.utils import human_time logger = logging.getLogger() @@ -44,7 +45,7 @@ class PausedProjectsState(ProjectState): messages = { - 'choose_proj': u" Wählen Sie ein Projekt:" + 'choose_proj': _('Choose a project: '), } def keypress(self, key): @@ -94,8 +95,8 @@ class ExitState(ProjectState): messages = { - 'quit' : u" Wirklich beenden? (y/n)", - 'choose': u" Wählen Sie ein Projekt:" + 'quit' : _(" Really quit? (y/n)"), + 'choose': _(" Choose a project:") } def __init__(self, controller, view): @@ -120,10 +121,12 @@ class RunningProjectsState(ProjectState): messages = { - 'description': u" Geben Sie eine Beschreibung ein: ", - 'add_time' : u" Geben Sie die zu addierende Zeit ein [min]: ", - 'min_time' : u" Geben Sie die zu abzuziehende Zeit ein [min]: ", - 'paused' : u" 'Space' zum Fortzusetzen", + 'description': _("Enter a description: "), + 'add_time' : _("Enter time to add [min]: "), + 'min_time' : _("Enter time to subtract [min]: "), + 'continue' : _("Press 'Space' to continue."), + 'running' : _("Running ( %s ) on '%s'."), + 'paused' : _(" Break ( %s )%s."), } sec = 0 @@ -137,7 +140,7 @@ def handle_signal(self, signum, frame): proj = self.view.item_in_focus() if not self.break_start: - self.controller.view.set_footer_text(" Running ( %s ) on '%s'" % + self.controller.view.set_footer_text(self.msg('running') % (human_time(self.sec), proj.desc), 'running') @@ -145,9 +148,9 @@ self.sec = self.sec + 1 else: self.view.set_footer_text( - ' Break ( %s )%s' % + self.msg('paused') % (human_time((datetime.now()-self.break_start).seconds), - self.msg('paused')), + self.msg('continue')), 'paused_running') self.controller.view.loop.draw_screen() @@ -164,7 +167,7 @@ return self.stop() if '+' in key: self.view.set_footer_text(self.msg('add_time'), - 'question', 1) + 'question', 1) self.view.frame.set_focus('footer') return AddTimeState(self.controller, self.view, self) if '-' in key: @@ -283,7 +286,7 @@ class DescriptionProjectsState(HandleUserInputState): messages = { - 'choose_proj': u" Wählen Sie ein Projekt." + 'choose_proj': _(" Choose a project."), } def enter(self): @@ -361,7 +364,7 @@ class DeleteEntryState(EntryListState): messages = { - 'delete' : u" Wirklich löschen? (y/n)", + 'delete' : _("Really delete this entry? (y/n)"), } def __init__(self, state, controller, view): @@ -392,8 +395,8 @@ class MoveEntryState(EntryListState): messages = { - 'project': u" In welches Projekt möchten Sie die Einträge verschieben?", - 'really': u" Sind sie sich sicher? (y/n)", + 'project': _(" Into which project do you want to move these entries?"), + 'really': _(" Are you sure? (> %s) (y/n)"), } proj = None diff -r cfa9e755a5d2 -r e6f81aa329b1 getan/view.py --- a/getan/view.py Sun Sep 05 15:54:44 2010 +0200 +++ b/getan/view.py Sat Oct 02 22:22:35 2010 +0200 @@ -12,15 +12,9 @@ import urwid import urwid.raw_display -from getan.states import * -from getan.utils import short_time, format_datetime, format_time - -APP_TITLE = u" .: getan next generation :." -APP_REALLY_QUIT = u" Wollen Sie wirklich beenden? " -APP_CHOOSE_PROJECT = u" Wählen Sie ein Projekt: " - -PROJECTS_TITLE = u" Liste registrierter Projekte" -ENTRIES_TITLE = u" Liste der letzten Einträge" +from getan.resources import gettext as _ +from getan.states import * +from getan.utils import short_time, format_datetime, format_time logger = logging.getLogger() @@ -96,11 +90,11 @@ class ProjectNode(urwid.WidgetWrap): MODES = [ - (0, 'Gesamt'), - (1, 'Jahr'), - (2, 'Monat'), - (3, 'Woche'), - (4, 'Tag') + (0, _('Total')), + (1, _('Year')), + (2, _('Month')), + (3, _('Week')), + (4, _('Day')) ] def __init__(self, proj, mode=3): @@ -175,8 +169,8 @@ self.controller = controller self.raw_projects = rows - self.header = urwid.LineBox( - urwid.AttrWrap(urwid.Text("\n%s\n" % PROJECTS_TITLE),'project_header')) + self.header = urwid.LineBox(urwid.AttrWrap(urwid.Text("\n%s\n" % + _('List of registered projects')),'project_header')) self.footer = urwid.Edit() self.rows = [ProjectNode(x) for x in rows] self.listbox = urwid.ListBox(urwid.SimpleListWalker(self.rows)) @@ -195,7 +189,7 @@ if tmp and type(tmp) == int: total += tmp self.frame.set_footer(urwid.AttrWrap( - urwid.Text(' Alle Projekte: %s %s' + urwid.Text(_(' All projects: %s %s') % (proj.mode[1],human_time(total))), 'project_footer')) def update(self): @@ -285,7 +279,7 @@ ('fixed left', 1), ('fixed right', 1))) self.header = urwid.LineBox(urwid.AttrWrap(urwid.Text( "\n%s\n" - % ENTRIES_TITLE),'entry_header')) + % _('Last entries')),'entry_header')) self.footer = urwid.AttrWrap(urwid.Text(""), 'entry_footer') self.frame = urwid.Frame(self.body, header=self.header, footer=self.footer) @@ -332,8 +326,9 @@ urwid.Padding(self.proj_list, ('fixed left',0),('fixed right',1)), self.entr_list], 0) - self.header = urwid.AttrWrap(urwid.Text('%s\n' % APP_TITLE), 'header') - self.footer = urwid.AttrWrap(urwid.Text(APP_CHOOSE_PROJECT), + self.header = urwid.AttrWrap(urwid.Text('%s\n' % _('.: getan :.')), + 'header') + self.footer = urwid.AttrWrap(urwid.Text(_('Choose a project:')), 'question') self.col_list = self.columns.widget_list view = urwid.AttrWrap(self.columns, 'body') diff -r cfa9e755a5d2 -r e6f81aa329b1 po/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/po/Makefile Sat Oct 02 22:22:35 2010 +0200 @@ -0,0 +1,42 @@ +all: + @echo 'Usage:' + @echo ' make pot create getan.pot' + @echo ' make merge merge a new getan.pot with the *.po files' + @echo ' make mo create the mo files' + @echo ' make stat print statistics about the translation status' + +MO_DIR = ../share/locale +DOMAIN = getan + +pot: + pygettext -o $(DOMAIN).pot ../getan/*.py + +merge: + for po in *.po; do \ + lingua=`basename $$po .po`; \ + mv $$lingua.po $$lingua.old.po; \ + if msgmerge -o $$lingua.po $$lingua.old.po $(DOMAIN).pot; then\ + rm $$lingua.old.po; \ + else \ + rm -f $$lingua.po; \ + mv $$lingua.old.po $$lingua.po; \ + fi \ + done + + +mo: + for po in *.po; do\ + lingua=`basename $$po .po`; \ + install -d $(MO_DIR)/$$lingua/LC_MESSAGES/ ; \ + echo -n $$po": "; \ + msgfmt --statistics \ + -o $(MO_DIR)/$$lingua/LC_MESSAGES/$(DOMAIN).mo $$po ;\ + done + + +stat: + @for po in *.po; do\ + echo -n $$po": "; \ + msgfmt --statistics -o /dev/null $$po 2>&1 \ + | sed -e 's/ \(messages*\|translations*\)//g' -e 's/\.$$//' ; \ + done diff -r cfa9e755a5d2 -r e6f81aa329b1 po/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/po/README Sat Oct 02 22:22:35 2010 +0200 @@ -0,0 +1,34 @@ +Getan Message Translations +============================== + +The message catalogs are managed with the following software: + + - pygettext (usually distributed with Python) + + - GNU gettext (msgmerge and msgfmt) + + +Common tasks: + + - Create/Update getan.pot-File when the sources were edited and + messages have been added or changed and after a fresh checkout + + $ make pot + + - When the getan.pot has been changed, run + + $ make merge + + to merge the changes in the pot file with the messages already in the + *.po files. + + - When the translations in the po files have been changed, run + + $ make mo + + to generate new .mo files under ../share/locale + + - The make mo command prints statistics about the translations. To get + a less verbose version use this: + + $ make stat diff -r cfa9e755a5d2 -r e6f81aa329b1 po/de.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/po/de.po Sat Oct 02 22:22:35 2010 +0200 @@ -0,0 +1,106 @@ +# German translation for getan. +# Copyright (C) 2010 Intevation GmbH +# Ingo Weinzierl , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: Getan HG Repository\n" +"POT-Creation-Date: 2010-09-25 01:34+CEST\n" +"Last-Translator: Ingo Weinzierl \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Generated-By: pygettext.py 1.5\n" + + +#: ../getan/states.py:48 +msgid "Choose a project: " +msgstr "Wählen Sie ein Projekt:" + +#: ../getan/states.py:98 +msgid " Really quit? (y/n)" +msgstr " Wirklich beenden? (y/n)" + +#: ../getan/states.py:99 +msgid " Choose a project:" +msgstr "Wählen Sie ein Projekt:" + +#: ../getan/states.py:124 +msgid "Enter a description: " +msgstr "Geben Sie eine Beschreibung ein: " + +#: ../getan/states.py:125 +msgid "Enter time to add [min]: " +msgstr "Zeit zum Addieren eingeben [min]: " + +#: ../getan/states.py:126 +msgid "Enter time to subtract [min]: " +msgstr "Zeit zum Subtrahieren eingeben [min]: " + +#: ../getan/states.py:127 +msgid "Press 'Space' to continue." +msgstr "Drücken Sie 'Leertaste' um fortzusetzen." + +#: ../getan/states.py:128 +msgid "Running ( %s ) on '%s'." +msgstr "Sie arbeiten ( %s ) an '%s'." + +#: ../getan/states.py:129 +msgid " Break ( %s )%s." +msgstr " Pause ( %s )%s." + +#: ../getan/states.py:289 +msgid " Choose a project." +msgstr "Wählen Sie ein Projekt:" + +#: ../getan/states.py:367 +msgid "Really delete this entry? (y/n)" +msgstr "Wollen Sie diesen Eintrag wirklich löschen? (y/n)" + +#: ../getan/states.py:398 +msgid " Into which project do you want to move these entries?" +msgstr "In welches Projekt sollen die Einträge verschoben werden? > %s" + +#: ../getan/states.py:399 +msgid " Are you sure? (> %s) (y/n)" +msgstr " Sind Sie sicher? (> %s) (y/n)" + +#: ../getan/view.py:93 +msgid "Total" +msgstr "Gesamt" + +#: ../getan/view.py:94 +msgid "Year" +msgstr "Jahr" + +#: ../getan/view.py:95 +msgid "Month" +msgstr "Monat" + +#: ../getan/view.py:96 +msgid "Week" +msgstr "Woche" + +#: ../getan/view.py:97 +msgid "Day" +msgstr "Tag" + +#: ../getan/view.py:173 +msgid "List of registered projects" +msgstr "Liste der registrierten Projekte" + +#: ../getan/view.py:192 +msgid " All projects: %s %s" +msgstr "Alle Projekte: %s %s" + +#: ../getan/view.py:282 +msgid "Last entries" +msgstr "Letzte Einträge" + +#: ../getan/view.py:329 +msgid ".: getan :." +msgstr ".: getan :." + +#: ../getan/view.py:331 +msgid "Choose a project:" +msgstr "Wählen Sie ein Projekt:" +