Skip to content

Commit

Permalink
Fallback to no-context translation internally, add gettext imports in…
Browse files Browse the repository at this point in the history
… sample application, remove mocks from tests

Closes #6
  • Loading branch information
Maciej Olko committed Aug 16, 2021
1 parent 4f7d3e1 commit 99e1658
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 65 deletions.
17 changes: 2 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,32 +51,19 @@ OK, but let's say we miss a context translation:
msgid "user"
msgstr "" # <-- missing translation

``AttributiveTranslation`` class without fallback by default falls back context
translation to original English string:
``AttributiveTranslation`` class by default falls back to no-context
translation of the original English string:

>>> user = AttributiveTranslations(…).gettext('user')
>>> 'Wybierz {.accusative} do zmiany'.format(user)
'Wybierz user do zmiany'

``NoContextFallbackTranslations`` class makes us fall back to no-context
translation, which may be more desirable in our use case:

>>> translations = AttributiveTranslations(…)
>>> translations.add_fallback(NoContextFallbackTranslations(…))
>>> user = translations.gettext('user')
>>> 'Wybierz {.accusative} do zmiany'.format(user)
'Wybierz użytkownik do zmiany'

### Example installation

from gettext import translation
from translations import AttributiveTranslations
from translations import NoContextFallbackTranslations

pl = translation('messages', 'locale', ['pl'], AttributiveTranslations)
pl.add_fallback(
translation('messages', 'locale', ['pl'], NoContextFallbackTranslations)
)
pl.install(('pgettext',))

### Example usage
Expand Down
Binary file modified locale/pl/LC_MESSAGES/messages.mo
Binary file not shown.
30 changes: 7 additions & 23 deletions messages.po
Original file line number Diff line number Diff line change
@@ -1,40 +1,24 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-04-20 20:14+0200\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"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgstr "Content-Type: charset=UTF-8"

#: sample_application.py:12
#: sample_application.py:8
msgid "user"
msgstr "użytkownik"

#: sample_application.py:13
#: sample_application.py:9
msgctxt "accusative"
msgid "user"
msgstr "użytkownika"

#: sample_application.py:15
#: sample_application.py:11
msgid "group"
msgstr "grupa"

#: sample_application.py:16
#: sample_application.py:12
msgctxt "accusative"
msgid "group"
msgstr "grupę"

#: sample_application.py:21
#: sample_application.py:17
msgid "Select {} to change"
msgstr "Wybierz {.accusative} to change"
msgstr "Wybierz {.accusative} do zmiany"
10 changes: 3 additions & 7 deletions sample_application.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from gettext import translation
from gettext import translation, gettext as _, pgettext
from translations import AttributiveTranslations
from translations import NoContextFallbackTranslations

pl = translation('messages', 'locale', ['pl'], AttributiveTranslations)
pl.add_fallback(
translation('messages', 'locale', ['pl'], NoContextFallbackTranslations)
)
pl.install(('pgettext',))


Expand All @@ -23,8 +19,8 @@

# translation cycle:
# make changes
# run ``xgettext sample_application.py --keyword=pgettext:1c,2`` to regenerate PO file
# specify UTF-8 charset by replacing CHARSET text
# run ``xgettext sample_application.py --keyword=pgettext:1c,2 --omit-header`` to regenerate PO file
# specify UTF-8 charset by adding empty msgid with msgstr equal to "Content-Type: charset=UTF-8"
# translate the file
# run ``msgfmt messages.po --output locale/pl/LC_MESSAGES/messages.mo`` to compile translation into proper location
# run localized application
20 changes: 7 additions & 13 deletions tests.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from collections import UserString
from io import BytesIO
from unittest import TestCase
from unittest.mock import Mock

from translations import AttributiveTranslations
from translations import NoContextFallbackTranslations

empty_mo_file = BytesIO(b'\x95\x04\x12\xde' # magic number, same as gettext.GNUTranslations.LE_MAGIC.to_bytes(4, 'big')
b'\x00\x00\x00\x01' # version
Expand All @@ -20,19 +18,15 @@ def test_value_returned_is_userstring(self):

def test_is_attributive(self):
t = AttributiveTranslations()
t.pgettext = Mock()
t._catalog = {}
t._catalog = {'attribute\x04foo': 'bar'}

foo = t.gettext('foo')
foo.attribute

t.pgettext.assert_called_once_with('attribute', 'foo')
self.assertEqual('bar', foo.attribute)

def test_fallbacks(self):
t = NoContextFallbackTranslations()
t.gettext = Mock()
t._catalog = {}

t.pgettext('samplecontext', 'samplemessage')
t = AttributiveTranslations()
t._catalog = {'user': 'użytkownik'}

t.gettext.assert_called_once_with('samplemessage')
message = t.gettext('user')
result = message.samplecontext
self.assertEqual('użytkownik', result)
12 changes: 5 additions & 7 deletions translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@
class AttributiveTranslations(GNUTranslations):
def gettext(self, message):
gettext = super().gettext
pgettext = self.pgettext
CONTEXT_SEPARATOR = '\x04'

class AttributiveTranslationString(UserString):
def __init__(self, data):
super().__init__(gettext(data))
self.raw_data = data

def __getattr__(self, item):
return pgettext(item, self.raw_data)
msg_with_ctxt = f'{item}{CONTEXT_SEPARATOR}{self.raw_data}'
if CONTEXT_SEPARATOR not in (result := gettext(msg_with_ctxt)):
return result
return self.data

return AttributiveTranslationString(message)


class NoContextFallbackTranslations(GNUTranslations):
def pgettext(self, context, message):
return self.gettext(message)

0 comments on commit 99e1658

Please sign in to comment.