mirror of
https://github.com/Hopiu/django-rosetta.git
synced 2026-03-17 05:40:26 +00:00
Merge branch 'develop' into reflang
This commit is contained in:
commit
a4a7ddb589
23 changed files with 568 additions and 298 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -8,3 +8,9 @@ build
|
|||
rosetta/locale/xx/LC_MESSAGES/*.mo
|
||||
/.settings
|
||||
/.project
|
||||
testproject/coverage.xml
|
||||
testproject/htmlcov/
|
||||
testproject/rosetta.db
|
||||
testproject/src/
|
||||
testproject/.coverage
|
||||
venv_*
|
||||
|
|
|
|||
18
CHANGES
18
CHANGES
|
|
@ -1,6 +1,20 @@
|
|||
Version 0.7.2
|
||||
-------------
|
||||
* Fix for when settings imports unicode_literals for some reason (Issue #67)
|
||||
|
||||
Version 0.7.1
|
||||
-------------
|
||||
* Fix: value missing in context
|
||||
|
||||
Version 0.7.0
|
||||
-------------
|
||||
* Support for Django 1.5 and HEAD, support for Python 3.
|
||||
* Upgraded bundled polib to version 1.0.3 - http://pypi.python.org/pypi/polib/1.0.3
|
||||
* Support timezones on the last modified PO header. Thanks @jmoiron (Issue #43)
|
||||
* Actually move to the next block when submitting a lot of translations (Issue #13)
|
||||
* Add msgctxt to the entry hash to differentiate entries with context. Thanks @metalpriest (Issue #39)
|
||||
* Better discovery of locale files on Django 1.4+ Thanks @tijs (Issues #63, #64)
|
||||
* List apps in alphabetical order
|
||||
|
||||
Version 0.6.8
|
||||
-------------
|
||||
|
|
@ -16,7 +30,7 @@ Version 0.6.7
|
|||
Version 0.6.6
|
||||
-------------
|
||||
* Django 1.4 support (Issue #30, #33)
|
||||
* Better handling of translation callbacks on Bing's translation API and support of composite locales (Issue #26)
|
||||
* Better handling of translation callbacks on Bing's translation API and support of composite locales (Issue #26)
|
||||
|
||||
Version 0.6.5
|
||||
-------------
|
||||
|
|
@ -26,7 +40,7 @@ Version 0.6.5
|
|||
|
||||
Version 0.6.4
|
||||
-------------
|
||||
* Added ROSETTA_REQUIRES_AUTH option to grant access to non authenticated users (False by default)
|
||||
* Added ROSETTA_REQUIRES_AUTH option to grant access to non authenticated users (False by default)
|
||||
|
||||
Version 0.6.3
|
||||
-------------
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
include MANIFEST.in
|
||||
include LICENSE
|
||||
exclude *.pyc
|
||||
exclude *.sh
|
||||
recursive-include rosetta/locale *
|
||||
recursive-include rosetta/tests *
|
||||
recursive-include rosetta/templates *
|
||||
prune testproject
|
||||
prune rosetta/tests/__pycache__
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Features
|
|||
Requirements
|
||||
************
|
||||
|
||||
Rosetta requires Django 1.3 or later
|
||||
Rosetta requires Django 1.3, 1.4 or 1.5. When running with Django 1.5, Python 3.x is supported.
|
||||
|
||||
************
|
||||
Installation
|
||||
|
|
@ -105,7 +105,7 @@ Usage
|
|||
Generate a batch of files to translate
|
||||
--------------------------------------
|
||||
|
||||
See `Django's documentation on Internationalization <http://www.djangoproject.com/documentation/i18n/>`_ to setup your project to use i18n and create the ``gettext`` catalog files.
|
||||
See `Django's documentation on Internationalization <https://docs.djangoproject.com/en/1.5/topics/i18n/translation/>`_ to setup your project to use i18n and create the ``gettext`` catalog files.
|
||||
|
||||
Translate away!
|
||||
---------------
|
||||
|
|
@ -141,5 +141,5 @@ By default Rosetta hides its own catalog files in the file selection interface (
|
|||
Acknowledgments
|
||||
***************
|
||||
|
||||
* Rosetta uses the excellent `polib <http://code.google.com/p/polib/>`_ library to parse and handle gettext files.
|
||||
* Rosetta uses the excellent `polib <https://bitbucket.org/izi/polib>`_ library to parse and handle gettext files.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
VERSION = (0, 7, 0)
|
||||
VERSION = (0, 7, 2)
|
||||
|
||||
|
||||
def get_version(svn=False, limit=3):
|
||||
|
|
|
|||
265
rosetta/polib.py
265
rosetta/polib.py
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# -* coding: utf-8 -*-
|
||||
#
|
||||
# License: MIT (see LICENSE file provided)
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
|
||||
|
|
@ -12,10 +12,10 @@ modify entries, comments or metadata, etc. or create new po files from scratch.
|
|||
:func:`~polib.mofile` convenience functions.
|
||||
"""
|
||||
|
||||
__author__ = 'David Jean Louis <izimobil@gmail.com>'
|
||||
__version__ = '0.7.0'
|
||||
__all__ = ['pofile', 'POFile', 'POEntry', 'mofile', 'MOFile', 'MOEntry',
|
||||
'detect_encoding', 'escape', 'unescape', 'detect_encoding',]
|
||||
__author__ = 'David Jean Louis <izimobil@gmail.com>'
|
||||
__version__ = '1.0.3'
|
||||
__all__ = ['pofile', 'POFile', 'POEntry', 'mofile', 'MOFile', 'MOEntry',
|
||||
'default_encoding', 'escape', 'unescape', 'detect_encoding', ]
|
||||
|
||||
import array
|
||||
import codecs
|
||||
|
|
@ -25,11 +25,13 @@ import struct
|
|||
import sys
|
||||
import textwrap
|
||||
|
||||
|
||||
# the default encoding to use when encoding cannot be detected
|
||||
default_encoding = 'utf-8'
|
||||
|
||||
# python 2/3 compatibility helpers {{{
|
||||
|
||||
|
||||
if sys.version_info[:2] < (3, 0):
|
||||
PY3 = False
|
||||
text_type = unicode
|
||||
|
|
@ -49,10 +51,10 @@ else:
|
|||
|
||||
def u(s):
|
||||
return s
|
||||
|
||||
# }}}
|
||||
# _pofile_or_mofile {{{
|
||||
|
||||
|
||||
def _pofile_or_mofile(f, type, **kwargs):
|
||||
"""
|
||||
Internal function used by :func:`polib.pofile` and :func:`polib.mofile` to
|
||||
|
|
@ -68,15 +70,16 @@ def _pofile_or_mofile(f, type, **kwargs):
|
|||
parser = kls(
|
||||
f,
|
||||
encoding=enc,
|
||||
check_for_duplicates=kwargs.get('check_for_duplicates', False)
|
||||
check_for_duplicates=kwargs.get('check_for_duplicates', False),
|
||||
klass=kwargs.get('klass')
|
||||
)
|
||||
instance = parser.parse()
|
||||
instance.wrapwidth = kwargs.get('wrapwidth', 78)
|
||||
return instance
|
||||
|
||||
# }}}
|
||||
# function pofile() {{{
|
||||
|
||||
|
||||
def pofile(pofile, **kwargs):
|
||||
"""
|
||||
Convenience function that parses the po or pot file ``pofile`` and returns
|
||||
|
|
@ -98,12 +101,17 @@ def pofile(pofile, **kwargs):
|
|||
``check_for_duplicates``
|
||||
whether to check for duplicate entries when adding entries to the
|
||||
file (optional, default: ``False``).
|
||||
|
||||
``klass``
|
||||
class which is used to instantiate the return value (optional,
|
||||
default: ``None``, the return value with be a :class:`~polib.POFile`
|
||||
instance).
|
||||
"""
|
||||
return _pofile_or_mofile(pofile, 'pofile', **kwargs)
|
||||
|
||||
# }}}
|
||||
# function mofile() {{{
|
||||
|
||||
|
||||
def mofile(mofile, **kwargs):
|
||||
"""
|
||||
Convenience function that parses the mo file ``mofile`` and returns a
|
||||
|
|
@ -126,12 +134,17 @@ def mofile(mofile, **kwargs):
|
|||
``check_for_duplicates``
|
||||
whether to check for duplicate entries when adding entries to the
|
||||
file (optional, default: ``False``).
|
||||
|
||||
``klass``
|
||||
class which is used to instantiate the return value (optional,
|
||||
default: ``None``, the return value with be a :class:`~polib.POFile`
|
||||
instance).
|
||||
"""
|
||||
return _pofile_or_mofile(mofile, 'mofile', **kwargs)
|
||||
|
||||
# }}}
|
||||
# function detect_encoding() {{{
|
||||
|
||||
|
||||
def detect_encoding(file, binary_mode=False):
|
||||
"""
|
||||
Try to detect the encoding used by the ``file``. The ``file`` argument can
|
||||
|
|
@ -159,7 +172,12 @@ def detect_encoding(file, binary_mode=False):
|
|||
return False
|
||||
return True
|
||||
|
||||
if not os.path.exists(file):
|
||||
try:
|
||||
is_file = os.path.exists(file)
|
||||
except (ValueError, UnicodeEncodeError):
|
||||
is_file = False
|
||||
|
||||
if not is_file:
|
||||
match = rxt.search(file)
|
||||
if match:
|
||||
enc = match.group(1).strip()
|
||||
|
|
@ -185,10 +203,10 @@ def detect_encoding(file, binary_mode=False):
|
|||
return enc
|
||||
f.close()
|
||||
return default_encoding
|
||||
|
||||
# }}}
|
||||
# function escape() {{{
|
||||
|
||||
|
||||
def escape(st):
|
||||
"""
|
||||
Escapes the characters ``\\\\``, ``\\t``, ``\\n``, ``\\r`` and ``"`` in
|
||||
|
|
@ -199,10 +217,10 @@ def escape(st):
|
|||
.replace('\r', r'\r')\
|
||||
.replace('\n', r'\n')\
|
||||
.replace('\"', r'\"')
|
||||
|
||||
# }}}
|
||||
# function unescape() {{{
|
||||
|
||||
|
||||
def unescape(st):
|
||||
"""
|
||||
Unescapes the characters ``\\\\``, ``\\t``, ``\\n``, ``\\r`` and ``"`` in
|
||||
|
|
@ -218,12 +236,12 @@ def unescape(st):
|
|||
return '\r'
|
||||
if m == '\\':
|
||||
return '\\'
|
||||
return m # handles escaped double quote
|
||||
return m # handles escaped double quote
|
||||
return re.sub(r'\\(\\|n|t|r|")', unescape_repl, st)
|
||||
|
||||
# }}}
|
||||
# class _BaseFile {{{
|
||||
|
||||
|
||||
class _BaseFile(list):
|
||||
"""
|
||||
Common base class for the :class:`~polib.POFile` and :class:`~polib.MOFile`
|
||||
|
|
@ -301,15 +319,17 @@ class _BaseFile(list):
|
|||
Overriden ``list`` method to implement the membership test (in and
|
||||
not in).
|
||||
The method considers that an entry is in the file if it finds an entry
|
||||
that has the same msgid (the test is **case sensitive**).
|
||||
that has the same msgid (the test is **case sensitive**) and the same
|
||||
msgctxt (or none for both entries).
|
||||
|
||||
Argument:
|
||||
|
||||
``entry``
|
||||
an instance of :class:`~polib._BaseEntry`.
|
||||
"""
|
||||
return self.find(entry.msgid, by='msgid') is not None
|
||||
|
||||
return self.find(entry.msgid, by='msgid', msgctxt=entry.msgctxt) \
|
||||
is not None
|
||||
|
||||
def __eq__(self, other):
|
||||
return str(self) == str(other)
|
||||
|
||||
|
|
@ -420,7 +440,7 @@ class _BaseFile(list):
|
|||
entries = [e for e in self if not e.obsolete]
|
||||
for e in entries:
|
||||
if getattr(e, by) == st:
|
||||
if msgctxt and e.msgctxt != msgctxt:
|
||||
if msgctxt is not False and e.msgctxt != msgctxt:
|
||||
continue
|
||||
return e
|
||||
return None
|
||||
|
|
@ -428,7 +448,7 @@ class _BaseFile(list):
|
|||
def ordered_metadata(self):
|
||||
"""
|
||||
Convenience method that returns an ordered version of the metadata
|
||||
dictionnary. The return value is list of tuples (metadata name,
|
||||
dictionary. The return value is list of tuples (metadata name,
|
||||
metadata_value).
|
||||
"""
|
||||
# copy the dict first
|
||||
|
|
@ -464,6 +484,7 @@ class _BaseFile(list):
|
|||
"""
|
||||
offsets = []
|
||||
entries = self.translated_entries()
|
||||
|
||||
# the keys are sorted in the .mo file
|
||||
def cmp(_self, other):
|
||||
# msgfmt compares entries with msgctxt if it exists
|
||||
|
|
@ -500,11 +521,11 @@ class _BaseFile(list):
|
|||
msgid += self._encode(e.msgid)
|
||||
msgstr = self._encode(e.msgstr)
|
||||
offsets.append((len(ids), len(msgid), len(strs), len(msgstr)))
|
||||
ids += msgid + b('\0')
|
||||
ids += msgid + b('\0')
|
||||
strs += msgstr + b('\0')
|
||||
|
||||
# The header is 7 32-bit unsigned integers.
|
||||
keystart = 7*4+16*entries_len
|
||||
keystart = 7 * 4 + 16 * entries_len
|
||||
# and the values start after the keys
|
||||
valuestart = keystart + len(ids)
|
||||
koffsets = []
|
||||
|
|
@ -512,8 +533,8 @@ class _BaseFile(list):
|
|||
# The string table first has the list of keys, then the list of values.
|
||||
# Each entry has first the size of the string, then the file offset.
|
||||
for o1, l1, o2, l2 in offsets:
|
||||
koffsets += [l1, o1+keystart]
|
||||
voffsets += [l2, o2+valuestart]
|
||||
koffsets += [l1, o1 + keystart]
|
||||
voffsets += [l2, o2 + valuestart]
|
||||
offsets = koffsets + voffsets
|
||||
# check endianness for magic number
|
||||
if struct.pack('@h', 1) == struct.pack('<h', 1):
|
||||
|
|
@ -523,13 +544,19 @@ class _BaseFile(list):
|
|||
|
||||
output = struct.pack(
|
||||
"Iiiiiii",
|
||||
magic_number, # Magic number
|
||||
0, # Version
|
||||
entries_len, # # of entries
|
||||
7*4, # start of key index
|
||||
7*4+entries_len*8, # start of value index
|
||||
0, keystart # size and offset of hash table
|
||||
# Important: we don't use hash tables
|
||||
# Magic number
|
||||
magic_number,
|
||||
# Version
|
||||
0,
|
||||
# number of entries
|
||||
entries_len,
|
||||
# start of key index
|
||||
7 * 4,
|
||||
# start of value index
|
||||
7 * 4 + entries_len * 8,
|
||||
# size and offset of hash table, we don't use hash tables
|
||||
0, keystart
|
||||
|
||||
)
|
||||
if PY3 and sys.version_info.minor > 1: # python 3.2 or superior
|
||||
output += array.array("i", offsets).tobytes()
|
||||
|
|
@ -547,10 +574,10 @@ class _BaseFile(list):
|
|||
if isinstance(mixed, text_type):
|
||||
mixed = mixed.encode(self.encoding)
|
||||
return mixed
|
||||
|
||||
# }}}
|
||||
# class POFile {{{
|
||||
|
||||
|
||||
class POFile(_BaseFile):
|
||||
"""
|
||||
Po (or Pot) file reader/writer.
|
||||
|
|
@ -606,7 +633,7 @@ class POFile(_BaseFile):
|
|||
"""
|
||||
Convenience method that returns the list of untranslated entries.
|
||||
"""
|
||||
return [e for e in self if not e.translated() and not e.obsolete \
|
||||
return [e for e in self if not e.translated() and not e.obsolete
|
||||
and not 'fuzzy' in e.flags]
|
||||
|
||||
def fuzzy_entries(self):
|
||||
|
|
@ -637,32 +664,36 @@ class POFile(_BaseFile):
|
|||
``refpot``
|
||||
object POFile, the reference catalog.
|
||||
"""
|
||||
# Store entries in dict/set for faster access
|
||||
self_entries = dict((entry.msgid, entry) for entry in self)
|
||||
refpot_msgids = set(entry.msgid for entry in refpot)
|
||||
# Merge entries that are in the refpot
|
||||
for entry in refpot:
|
||||
e = self.find(entry.msgid, include_obsolete_entries=True)
|
||||
e = self_entries.get(entry.msgid)
|
||||
if e is None:
|
||||
e = POEntry()
|
||||
self.append(e)
|
||||
e.merge(entry)
|
||||
# ok, now we must "obsolete" entries that are not in the refpot anymore
|
||||
for entry in self:
|
||||
if refpot.find(entry.msgid) is None:
|
||||
if entry.msgid not in refpot_msgids:
|
||||
entry.obsolete = True
|
||||
|
||||
# }}}
|
||||
# class MOFile {{{
|
||||
|
||||
|
||||
class MOFile(_BaseFile):
|
||||
"""
|
||||
Mo file reader/writer.
|
||||
This class inherits the :class:`~polib._BaseFile` class and, by
|
||||
extension, the python ``list`` type.
|
||||
"""
|
||||
BIG_ENDIAN = 0xde120495
|
||||
BIG_ENDIAN = 0xde120495
|
||||
LITTLE_ENDIAN = 0x950412de
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
Constructor, accepts all keywords arguments accepted by
|
||||
Constructor, accepts all keywords arguments accepted by
|
||||
:class:`~polib._BaseFile` class.
|
||||
"""
|
||||
_BaseFile.__init__(self, *args, **kwargs)
|
||||
|
|
@ -720,10 +751,10 @@ class MOFile(_BaseFile):
|
|||
Convenience method to keep the same interface with POFile instances.
|
||||
"""
|
||||
return []
|
||||
|
||||
# }}}
|
||||
# class _BaseEntry {{{
|
||||
|
||||
|
||||
class _BaseEntry(object):
|
||||
"""
|
||||
Base class for :class:`~polib.POEntry` and :class:`~polib.MOEntry` classes.
|
||||
|
|
@ -775,12 +806,14 @@ class _BaseEntry(object):
|
|||
ret = []
|
||||
# write the msgctxt if any
|
||||
if self.msgctxt is not None:
|
||||
ret += self._str_field("msgctxt", delflag, "", self.msgctxt, wrapwidth)
|
||||
ret += self._str_field("msgctxt", delflag, "", self.msgctxt,
|
||||
wrapwidth)
|
||||
# write the msgid
|
||||
ret += self._str_field("msgid", delflag, "", self.msgid, wrapwidth)
|
||||
# write the msgid_plural if any
|
||||
if self.msgid_plural:
|
||||
ret += self._str_field("msgid_plural", delflag, "", self.msgid_plural, wrapwidth)
|
||||
ret += self._str_field("msgid_plural", delflag, "",
|
||||
self.msgid_plural, wrapwidth)
|
||||
if self.msgstr_plural:
|
||||
# write the msgstr_plural if any
|
||||
msgstrs = self.msgstr_plural
|
||||
|
|
@ -789,10 +822,12 @@ class _BaseEntry(object):
|
|||
for index in keys:
|
||||
msgstr = msgstrs[index]
|
||||
plural_index = '[%s]' % index
|
||||
ret += self._str_field("msgstr", delflag, plural_index, msgstr, wrapwidth)
|
||||
ret += self._str_field("msgstr", delflag, plural_index, msgstr,
|
||||
wrapwidth)
|
||||
else:
|
||||
# otherwise write the msgstr
|
||||
ret += self._str_field("msgstr", delflag, "", self.msgstr, wrapwidth)
|
||||
ret += self._str_field("msgstr", delflag, "", self.msgstr,
|
||||
wrapwidth)
|
||||
ret.append('')
|
||||
ret = u('\n').join(ret)
|
||||
return ret
|
||||
|
|
@ -806,20 +841,21 @@ class _BaseEntry(object):
|
|||
Returns the string representation of the entry.
|
||||
"""
|
||||
return unicode(self).encode(self.encoding)
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
return str(self) == str(other)
|
||||
|
||||
def _str_field(self, fieldname, delflag, plural_index, field, wrapwidth=78):
|
||||
def _str_field(self, fieldname, delflag, plural_index, field,
|
||||
wrapwidth=78):
|
||||
lines = field.splitlines(True)
|
||||
if len(lines) > 1:
|
||||
lines = [''] + lines # start with initial empty line
|
||||
lines = [''] + lines # start with initial empty line
|
||||
else:
|
||||
escaped_field = escape(field)
|
||||
specialchars_count = 0
|
||||
for c in ['\\', '\n', '\r', '\t', '"']:
|
||||
specialchars_count += field.count(c)
|
||||
# comparison must take into account fieldname length + one space
|
||||
# comparison must take into account fieldname length + one space
|
||||
# + 2 quotes (eg. msgid "<string>")
|
||||
flength = len(fieldname) + 3
|
||||
if plural_index:
|
||||
|
|
@ -829,7 +865,7 @@ class _BaseEntry(object):
|
|||
# Wrap the line but take field name into account
|
||||
lines = [''] + [unescape(item) for item in wrap(
|
||||
escaped_field,
|
||||
wrapwidth - 2, # 2 for quotes ""
|
||||
wrapwidth - 2, # 2 for quotes ""
|
||||
drop_whitespace=False,
|
||||
break_long_words=False
|
||||
)]
|
||||
|
|
@ -845,10 +881,10 @@ class _BaseEntry(object):
|
|||
#import pdb; pdb.set_trace()
|
||||
ret.append('%s"%s"' % (delflag, escape(mstr)))
|
||||
return ret
|
||||
|
||||
# }}}
|
||||
# class POEntry {{{
|
||||
|
||||
|
||||
class POEntry(_BaseEntry):
|
||||
"""
|
||||
Represents a po file entry.
|
||||
|
|
@ -923,9 +959,9 @@ class POEntry(_BaseEntry):
|
|||
filelist.append(fpath)
|
||||
filestr = ' '.join(filelist)
|
||||
if wrapwidth > 0 and len(filestr) + 3 > wrapwidth:
|
||||
# textwrap split words that contain hyphen, this is not
|
||||
# what we want for filenames, so the dirty hack is to
|
||||
# temporally replace hyphens with a char that a file cannot
|
||||
# textwrap split words that contain hyphen, this is not
|
||||
# what we want for filenames, so the dirty hack is to
|
||||
# temporally replace hyphens with a char that a file cannot
|
||||
# contain, like "*"
|
||||
ret += [l.replace('*', '-') for l in wrap(
|
||||
filestr.replace('-', '*'),
|
||||
|
|
@ -942,7 +978,8 @@ class POEntry(_BaseEntry):
|
|||
ret.append('#, %s' % ', '.join(self.flags))
|
||||
|
||||
# previous context and previous msgid/msgid_plural
|
||||
fields = ['previous_msgctxt', 'previous_msgid', 'previous_msgid_plural']
|
||||
fields = ['previous_msgctxt', 'previous_msgid',
|
||||
'previous_msgid_plural']
|
||||
for f in fields:
|
||||
val = getattr(self, f)
|
||||
if val:
|
||||
|
|
@ -988,8 +1025,10 @@ class POEntry(_BaseEntry):
|
|||
else:
|
||||
return -1
|
||||
# Finally: Compare message ID
|
||||
if self.msgid > other.msgid: return 1
|
||||
elif self.msgid < other.msgid: return -1
|
||||
if self.msgid > other.msgid:
|
||||
return 1
|
||||
elif self.msgid < other.msgid:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def __gt__(self, other):
|
||||
|
|
@ -1050,19 +1089,19 @@ class POEntry(_BaseEntry):
|
|||
self.msgstr_plural[pos]
|
||||
except KeyError:
|
||||
self.msgstr_plural[pos] = ''
|
||||
|
||||
# }}}
|
||||
# class MOEntry {{{
|
||||
|
||||
|
||||
class MOEntry(_BaseEntry):
|
||||
"""
|
||||
Represents a mo file entry.
|
||||
"""
|
||||
pass
|
||||
|
||||
# }}}
|
||||
# class _POFileParser {{{
|
||||
|
||||
|
||||
class _POFileParser(object):
|
||||
"""
|
||||
A finite state machine to parse efficiently and correctly po
|
||||
|
|
@ -1096,7 +1135,10 @@ class _POFileParser(object):
|
|||
else:
|
||||
self.fhandle = pofile.splitlines()
|
||||
|
||||
self.instance = POFile(
|
||||
klass = kwargs.get('klass')
|
||||
if klass is None:
|
||||
klass = POFile
|
||||
self.instance = klass(
|
||||
pofile=pofile,
|
||||
encoding=enc,
|
||||
check_for_duplicates=kwargs.get('check_for_duplicates', False)
|
||||
|
|
@ -1139,7 +1181,7 @@ class _POFileParser(object):
|
|||
self.add('PP', all, 'PP')
|
||||
self.add('CT', ['ST', 'HE', 'GC', 'OC', 'FL', 'TC', 'PC', 'PM',
|
||||
'PP', 'MS', 'MX'], 'CT')
|
||||
self.add('MI', ['ST', 'HE', 'GC', 'OC', 'FL', 'CT', 'TC', 'PC',
|
||||
self.add('MI', ['ST', 'HE', 'GC', 'OC', 'FL', 'CT', 'TC', 'PC',
|
||||
'PM', 'PP', 'MS', 'MX'], 'MI')
|
||||
self.add('MP', ['TC', 'GC', 'PC', 'PM', 'PP', 'MI'], 'MP')
|
||||
self.add('MS', ['MI', 'MP', 'TC'], 'MS')
|
||||
|
|
@ -1174,6 +1216,9 @@ class _POFileParser(object):
|
|||
tokens = line.split(None, 2)
|
||||
nb_tokens = len(tokens)
|
||||
|
||||
if tokens[0] == '#~|':
|
||||
continue
|
||||
|
||||
if tokens[0] == '#~' and nb_tokens > 1:
|
||||
line = line[3:].strip()
|
||||
tokens = tokens[1:]
|
||||
|
|
@ -1186,41 +1231,56 @@ class _POFileParser(object):
|
|||
# msgid, msgid_plural, msgctxt & msgstr.
|
||||
if tokens[0] in keywords and nb_tokens > 1:
|
||||
line = line[len(tokens[0]):].lstrip()
|
||||
if re.search(r'([^\\]|^)"', line[1:-1]):
|
||||
raise IOError('Syntax error in po file %s (line %s): '
|
||||
'unescaped double quote found' %
|
||||
(self.instance.fpath, i))
|
||||
self.current_token = line
|
||||
self.process(keywords[tokens[0]], i)
|
||||
continue
|
||||
|
||||
self.current_token = line
|
||||
|
||||
if tokens[0] == '#:' and nb_tokens > 1:
|
||||
if tokens[0] == '#:':
|
||||
if nb_tokens <= 1:
|
||||
continue
|
||||
# we are on a occurrences line
|
||||
self.process('OC', i)
|
||||
|
||||
elif line[:1] == '"':
|
||||
# we are on a continuation line
|
||||
if re.search(r'([^\\]|^)"', line[1:-1]):
|
||||
raise IOError('Syntax error in po file %s (line %s): '
|
||||
'unescaped double quote found' %
|
||||
(self.instance.fpath, i))
|
||||
self.process('MC', i)
|
||||
|
||||
elif line[:7] == 'msgstr[':
|
||||
# we are on a msgstr plural
|
||||
self.process('MX', i)
|
||||
|
||||
elif tokens[0] == '#,' and nb_tokens > 1:
|
||||
elif tokens[0] == '#,':
|
||||
if nb_tokens <= 1:
|
||||
continue
|
||||
# we are on a flags line
|
||||
self.process('FL', i)
|
||||
|
||||
elif tokens[0] == '#':
|
||||
if line == '#': line += ' '
|
||||
elif tokens[0] == '#' or tokens[0].startswith('##'):
|
||||
if line == '#':
|
||||
line += ' '
|
||||
# we are on a translator comment line
|
||||
self.process('TC', i)
|
||||
|
||||
elif tokens[0] == '#.' and nb_tokens > 1:
|
||||
elif tokens[0] == '#.':
|
||||
if nb_tokens <= 1:
|
||||
continue
|
||||
# we are on a generated comment line
|
||||
self.process('GC', i)
|
||||
|
||||
elif tokens[0] == '#|':
|
||||
if nb_tokens < 2:
|
||||
self.process('??', i)
|
||||
continue
|
||||
if nb_tokens <= 1:
|
||||
raise IOError('Syntax error in po file %s (line %s)' %
|
||||
(self.instance.fpath, i))
|
||||
|
||||
# Remove the marker and any whitespace right after that.
|
||||
line = line[2:].lstrip()
|
||||
|
|
@ -1233,12 +1293,16 @@ class _POFileParser(object):
|
|||
|
||||
if nb_tokens == 2:
|
||||
# Invalid continuation line.
|
||||
self.process('??', i)
|
||||
raise IOError('Syntax error in po file %s (line %s): '
|
||||
'invalid continuation line' %
|
||||
(self.instance.fpath, i))
|
||||
|
||||
# we are on a "previous translation" comment line,
|
||||
if tokens[1] not in prev_keywords:
|
||||
# Unknown keyword in previous translation comment.
|
||||
self.process('??', i)
|
||||
raise IOError('Syntax error in po file %s (line %s): '
|
||||
'unknown keyword %s' %
|
||||
(self.instance.fpath, i, tokens[1]))
|
||||
|
||||
# Remove the keyword and any whitespace
|
||||
# between it and the starting quote.
|
||||
|
|
@ -1247,27 +1311,28 @@ class _POFileParser(object):
|
|||
self.process(prev_keywords[tokens[1]], i)
|
||||
|
||||
else:
|
||||
self.process('??', i)
|
||||
raise IOError('Syntax error in po file %s (line %s)' %
|
||||
(self.instance.fpath, i))
|
||||
|
||||
if self.current_entry:
|
||||
# since entries are added when another entry is found, we must add
|
||||
# the last entry here (only if there are lines)
|
||||
self.instance.append(self.current_entry)
|
||||
# before returning the instance, check if there's metadata and if
|
||||
# before returning the instance, check if there's metadata and if
|
||||
# so extract it in a dict
|
||||
firstentry = self.instance[0]
|
||||
if firstentry.msgid == '': # metadata found
|
||||
metadataentry = self.instance.find('')
|
||||
if metadataentry: # metadata found
|
||||
# remove the entry
|
||||
firstentry = self.instance.pop(0)
|
||||
self.instance.metadata_is_fuzzy = firstentry.flags
|
||||
self.instance.remove(metadataentry)
|
||||
self.instance.metadata_is_fuzzy = metadataentry.flags
|
||||
key = None
|
||||
for msg in firstentry.msgstr.splitlines():
|
||||
for msg in metadataentry.msgstr.splitlines():
|
||||
try:
|
||||
key, val = msg.split(':', 1)
|
||||
self.instance.metadata[key] = val.strip()
|
||||
except:
|
||||
except (ValueError, KeyError):
|
||||
if key is not None:
|
||||
self.instance.metadata[key] += '\n'+ msg.strip()
|
||||
self.instance.metadata[key] += '\n' + msg.strip()
|
||||
# close opened file
|
||||
if not isinstance(self.fhandle, list): # must be file
|
||||
self.fhandle.close()
|
||||
|
|
@ -1310,7 +1375,7 @@ class _POFileParser(object):
|
|||
if action():
|
||||
self.current_state = state
|
||||
except Exception:
|
||||
raise IOError('Syntax error in po file: %s (line %s)' % (self.instance.fpath, linenum))
|
||||
raise IOError('Syntax error in po file (line %s)' % linenum)
|
||||
|
||||
# state handlers
|
||||
|
||||
|
|
@ -1328,7 +1393,10 @@ class _POFileParser(object):
|
|||
self.current_entry = POEntry()
|
||||
if self.current_entry.tcomment != '':
|
||||
self.current_entry.tcomment += '\n'
|
||||
self.current_entry.tcomment += self.current_token[2:]
|
||||
tcomment = self.current_token.lstrip('#')
|
||||
if tcomment.startswith(' '):
|
||||
tcomment = tcomment[1:]
|
||||
self.current_entry.tcomment += tcomment
|
||||
return True
|
||||
|
||||
def handle_gc(self):
|
||||
|
|
@ -1352,10 +1420,10 @@ class _POFileParser(object):
|
|||
try:
|
||||
fil, line = occurrence.split(':')
|
||||
if not line.isdigit():
|
||||
fil = fil + line
|
||||
fil = fil + line
|
||||
line = ''
|
||||
self.current_entry.occurrences.append((fil, line))
|
||||
except:
|
||||
except (ValueError, AttributeError):
|
||||
self.current_entry.occurrences.append((occurrence, ''))
|
||||
return True
|
||||
|
||||
|
|
@ -1432,38 +1500,30 @@ class _POFileParser(object):
|
|||
"""Handle a msgid or msgstr continuation line."""
|
||||
token = unescape(self.current_token[1:-1])
|
||||
if self.current_state == 'CT':
|
||||
typ = 'msgctxt'
|
||||
self.current_entry.msgctxt += token
|
||||
elif self.current_state == 'MI':
|
||||
typ = 'msgid'
|
||||
self.current_entry.msgid += token
|
||||
elif self.current_state == 'MP':
|
||||
typ = 'msgid_plural'
|
||||
self.current_entry.msgid_plural += token
|
||||
elif self.current_state == 'MS':
|
||||
typ = 'msgstr'
|
||||
self.current_entry.msgstr += token
|
||||
elif self.current_state == 'MX':
|
||||
typ = 'msgstr[%s]' % self.msgstr_index
|
||||
self.current_entry.msgstr_plural[self.msgstr_index] += token
|
||||
elif self.current_state == 'PP':
|
||||
typ = 'previous_msgid_plural'
|
||||
token = token[3:]
|
||||
self.current_entry.previous_msgid_plural += token
|
||||
elif self.current_state == 'PM':
|
||||
typ = 'previous_msgid'
|
||||
token = token[3:]
|
||||
self.current_entry.previous_msgid += token
|
||||
elif self.current_state == 'PC':
|
||||
typ = 'previous_msgctxt'
|
||||
token = token[3:]
|
||||
self.current_entry.previous_msgctxt += token
|
||||
# don't change the current state
|
||||
return False
|
||||
|
||||
# }}}
|
||||
# class _MOFileParser {{{
|
||||
|
||||
|
||||
class _MOFileParser(object):
|
||||
"""
|
||||
A class to parse binary mo files.
|
||||
|
|
@ -1487,7 +1547,11 @@ class _MOFileParser(object):
|
|||
file (optional, default: ``False``).
|
||||
"""
|
||||
self.fhandle = open(mofile, 'rb')
|
||||
self.instance = MOFile(
|
||||
|
||||
klass = kwargs.get('klass')
|
||||
if klass is None:
|
||||
klass = MOFile
|
||||
self.instance = klass(
|
||||
fpath=mofile,
|
||||
encoding=kwargs.get('encoding', default_encoding),
|
||||
check_for_duplicates=kwargs.get('check_for_duplicates', False)
|
||||
|
|
@ -1529,7 +1593,7 @@ class _MOFileParser(object):
|
|||
|
||||
self.fhandle.seek(msgstrs_index[i][1])
|
||||
msgstr = self.fhandle.read(msgstrs_index[i][0])
|
||||
if i == 0: # metadata
|
||||
if i == 0: # metadata
|
||||
raw_metadata, metadata = msgstr.split(b('\n')), {}
|
||||
for line in raw_metadata:
|
||||
tokens = line.split(b(':'), 1)
|
||||
|
|
@ -1548,7 +1612,8 @@ class _MOFileParser(object):
|
|||
entry = self._build_entry(
|
||||
msgid=msgid_tokens[0],
|
||||
msgid_plural=msgid_tokens[1],
|
||||
msgstr_plural=dict((k,v) for k,v in enumerate(msgstr.split(b('\0'))))
|
||||
msgstr_plural=dict((k, v) for k, v in
|
||||
enumerate(msgstr.split(b('\0'))))
|
||||
)
|
||||
else:
|
||||
entry = self._build_entry(msgid=msgid, msgstr=msgstr)
|
||||
|
|
@ -1564,7 +1629,7 @@ class _MOFileParser(object):
|
|||
if len(msgctxt_msgid) > 1:
|
||||
kwargs = {
|
||||
'msgctxt': msgctxt_msgid[0].decode(encoding),
|
||||
'msgid' : msgctxt_msgid[1].decode(encoding),
|
||||
'msgid': msgctxt_msgid[1].decode(encoding),
|
||||
}
|
||||
else:
|
||||
kwargs = {'msgid': msgid.decode(encoding)}
|
||||
|
|
@ -1588,17 +1653,17 @@ class _MOFileParser(object):
|
|||
if len(tup) == 1:
|
||||
return tup[0]
|
||||
return tup
|
||||
|
||||
# }}}
|
||||
# class TextWrapper {{{
|
||||
|
||||
|
||||
class TextWrapper(textwrap.TextWrapper):
|
||||
"""
|
||||
Subclass of textwrap.TextWrapper that backport the
|
||||
drop_whitespace option.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
drop_whitespace = kwargs.pop('drop_whitespace', True)
|
||||
drop_whitespace = kwargs.pop('drop_whitespace', True)
|
||||
textwrap.TextWrapper.__init__(self, *args, **kwargs)
|
||||
self.drop_whitespace = drop_whitespace
|
||||
|
||||
|
|
@ -1662,7 +1727,7 @@ class TextWrapper(textwrap.TextWrapper):
|
|||
self._handle_long_word(chunks, cur_line, cur_len, width)
|
||||
|
||||
# If the last chunk on this line is all whitespace, drop it.
|
||||
if self.drop_whitespace and cur_line and cur_line[-1].strip() == '':
|
||||
if self.drop_whitespace and cur_line and not cur_line[-1].strip():
|
||||
del cur_line[-1]
|
||||
|
||||
# Convert current line back to a string and store it in list
|
||||
|
|
@ -1671,10 +1736,10 @@ class TextWrapper(textwrap.TextWrapper):
|
|||
lines.append(indent + ''.join(cur_line))
|
||||
|
||||
return lines
|
||||
|
||||
# }}}
|
||||
# function wrap() {{{
|
||||
|
||||
|
||||
def wrap(text, width=70, **kwargs):
|
||||
"""
|
||||
Wrap a single paragraph of text, returning a list of wrapped lines.
|
||||
|
|
@ -1683,4 +1748,4 @@ def wrap(text, width=70, **kwargs):
|
|||
return TextWrapper(width=width, **kwargs).wrap(text)
|
||||
return textwrap.wrap(text, width=width, **kwargs)
|
||||
|
||||
#}}}
|
||||
# }}}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import os
|
||||
import django
|
||||
from django.conf import settings
|
||||
from rosetta.conf import settings as rosetta_settings
|
||||
from django.core.cache import cache
|
||||
from datetime import datetime
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from rosetta.conf import settings as rosetta_settings
|
||||
import django
|
||||
import os
|
||||
import six
|
||||
try:
|
||||
from django.utils import timezone
|
||||
except:
|
||||
|
|
@ -46,7 +47,10 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
|
|||
project = __import__(parts[0], {}, {}, [])
|
||||
abs_project_path = os.path.normpath(os.path.abspath(os.path.dirname(project.__file__)))
|
||||
if project_apps:
|
||||
paths.append(os.path.abspath(os.path.join(os.path.dirname(project.__file__), 'locale')))
|
||||
if os.path.exists(os.path.abspath(os.path.join(os.path.dirname(project.__file__), 'locale'))):
|
||||
paths.append(os.path.abspath(os.path.join(os.path.dirname(project.__file__), 'locale')))
|
||||
if os.path.exists(os.path.abspath(os.path.join(os.path.dirname(project.__file__), '..', 'locale'))):
|
||||
paths.append(os.path.abspath(os.path.join(os.path.dirname(project.__file__), '..', 'locale')))
|
||||
|
||||
# django/locale
|
||||
if django_apps:
|
||||
|
|
@ -70,12 +74,11 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
|
|||
continue
|
||||
p = appname.rfind('.')
|
||||
if p >= 0:
|
||||
app = getattr(__import__(appname[:p], {}, {}, [appname[p + 1:]]), appname[p + 1:])
|
||||
app = getattr(__import__(appname[:p], {}, {}, [str(appname[p + 1:])]), appname[p + 1:])
|
||||
else:
|
||||
app = __import__(appname, {}, {}, [])
|
||||
|
||||
apppath = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(app.__file__), 'locale')))
|
||||
|
||||
|
||||
# django apps
|
||||
if 'contrib' in apppath and 'django' in apppath and not django_apps:
|
||||
|
|
@ -84,52 +87,57 @@ def find_pos(lang, project_apps=True, django_apps=False, third_party_apps=False)
|
|||
# third party external
|
||||
if not third_party_apps and abs_project_path not in apppath:
|
||||
continue
|
||||
|
||||
|
||||
# local apps
|
||||
if not project_apps and abs_project_path in apppath:
|
||||
continue
|
||||
|
||||
|
||||
|
||||
if os.path.isdir(apppath):
|
||||
paths.append(apppath)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ret = set()
|
||||
langs = (lang,)
|
||||
if u'-' in lang:
|
||||
_l,_c = map(lambda x:x.lower(),lang.split(u'-'))
|
||||
langs += (u'%s_%s' %(_l, _c), u'%s_%s' %(_l, _c.upper()), )
|
||||
_l, _c = map(lambda x: x.lower(), lang.split(u'-'))
|
||||
langs += (u'%s_%s' % (_l, _c), u'%s_%s' % (_l, _c.upper()), )
|
||||
elif u'_' in lang:
|
||||
_l,_c = map(lambda x:x.lower(),lang.split(u'_'))
|
||||
langs += (u'%s-%s' %(_l, _c), u'%s-%s' %(_l, _c.upper()), )
|
||||
|
||||
_l, _c = map(lambda x: x.lower(), lang.split(u'_'))
|
||||
langs += (u'%s-%s' % (_l, _c), u'%s-%s' % (_l, _c.upper()), )
|
||||
|
||||
paths = map(os.path.normpath, paths)
|
||||
paths = list(set(paths))
|
||||
for path in paths:
|
||||
for lang_ in langs:
|
||||
dirname = os.path.join(path, lang_, 'LC_MESSAGES')
|
||||
for fn in ('django.po','djangojs.po',):
|
||||
for fn in ('django.po', 'djangojs.po',):
|
||||
filename = os.path.join(dirname, fn)
|
||||
if os.path.isfile(filename):
|
||||
ret.add(os.path.abspath(filename))
|
||||
return list(ret)
|
||||
|
||||
def pagination_range(first,last,current):
|
||||
|
||||
def pagination_range(first, last, current):
|
||||
r = []
|
||||
|
||||
|
||||
r.append(first)
|
||||
if first + 1 < last: r.append(first+1)
|
||||
|
||||
if current -2 > first and current -2 < last: r.append(current-2)
|
||||
if current -1 > first and current -1 < last: r.append(current-1)
|
||||
if current > first and current < last: r.append(current)
|
||||
if current + 1 < last and current+1 > first: r.append(current+1)
|
||||
if current + 2 < last and current+2 > first: r.append(current+2)
|
||||
|
||||
if last-1 > first: r.append(last-1)
|
||||
if first + 1 < last:
|
||||
r.append(first + 1)
|
||||
|
||||
if current - 2 > first and current - 2 < last:
|
||||
r.append(current - 2)
|
||||
if current - 1 > first and current - 1 < last:
|
||||
r.append(current - 1)
|
||||
if current > first and current < last:
|
||||
r.append(current)
|
||||
if current + 1 < last and current + 1 > first:
|
||||
r.append(current + 1)
|
||||
if current + 2 < last and current + 2 > first:
|
||||
r.append(current + 2)
|
||||
|
||||
if last - 1 > first:
|
||||
r.append(last - 1)
|
||||
r.append(last)
|
||||
|
||||
|
||||
r = list(set(r))
|
||||
r.sort()
|
||||
prev = 10000
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from django.utils import importlib
|
|||
from django.core.exceptions import ImproperlyConfigured
|
||||
import hashlib
|
||||
import time
|
||||
import six
|
||||
|
||||
|
||||
class BaseRosettaStorage(object):
|
||||
|
|
@ -62,7 +63,7 @@ class CacheRosettaStorage(BaseRosettaStorage):
|
|||
if 'rosetta_cache_storage_key_prefix' in self.request.session:
|
||||
self._key_prefix = self.request.session['rosetta_cache_storage_key_prefix']
|
||||
else:
|
||||
self._key_prefix = hashlib.new('sha1', str(time.time())).hexdigest()
|
||||
self._key_prefix = hashlib.new('sha1', six.text_type(time.time()).encode('utf8')).hexdigest()
|
||||
self.request.session['rosetta_cache_storage_key_prefix'] = self._key_prefix
|
||||
|
||||
if self.request.session['rosetta_cache_storage_key_prefix'] != self._key_prefix:
|
||||
|
|
@ -86,7 +87,7 @@ class CacheRosettaStorage(BaseRosettaStorage):
|
|||
|
||||
def set(self, key, val):
|
||||
#print ('set', self._key_prefix + key)
|
||||
cache.set(self._key_prefix + key, val)
|
||||
cache.set(self._key_prefix + key, val, 86400)
|
||||
|
||||
def has(self, key):
|
||||
#print ('has', self._key_prefix + key)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<script type="text/javascript">
|
||||
//<!--
|
||||
google.load("jquery", "1.3");
|
||||
{% if ENABLE_TRANSLATION_SUGGESTIONS %}google.load("language", "1");{% endif %}
|
||||
{% if rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS %}google.load("language", "1");{% endif %}
|
||||
{% include 'rosetta/js/rosetta.js' %}
|
||||
//-->
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@ google.setOnLoadCallback(function() {
|
|||
$('.hide', $(this).parent()).hide();
|
||||
});
|
||||
|
||||
{% if ENABLE_TRANSLATION_SUGGESTIONS and BING_APP_ID %}
|
||||
{% if rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS and rosetta_settings.BING_APP_ID %}
|
||||
$('a.suggest').click(function(e){
|
||||
e.preventDefault();
|
||||
var a = $(this);
|
||||
var str = a.html();
|
||||
var orig = $('.original .message', a.parents('tr')).html();
|
||||
var trans=$('textarea',a.parent());
|
||||
var sourceLang = '{{ MESSAGES_SOURCE_LANGUAGE_CODE }}';
|
||||
var sourceLang = '{{ rosetta_settings.MESSAGES_SOURCE_LANGUAGE_CODE }}';
|
||||
var destLang = '{{ rosetta_i18n_lang_code }}';
|
||||
var app_id = '{{ BING_APP_ID }}';
|
||||
var app_id = '{{ rosetta_settings.BING_APP_ID }}';
|
||||
var apiUrl = "http://api.microsofttranslator.com/V2/Ajax.svc/Translate";
|
||||
|
||||
orig = unescape(orig).replace(/<br\s?\/?>/g,'\n').replace(/<code>/,'').replace(/<\/code>/g,'').replace(/>/g,'>').replace(/</g,'<');
|
||||
|
|
@ -50,7 +50,7 @@ google.setOnLoadCallback(function() {
|
|||
$($('.part',td).get(j)).css('top',textareaY + 'px');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('.translation textarea').blur(function() {
|
||||
if($(this).val()) {
|
||||
$('.alert', $(this).parents('tr')).remove();
|
||||
|
|
@ -70,7 +70,7 @@ google.setOnLoadCallback(function() {
|
|||
} else {
|
||||
if (!(origs === null && trads === null)) {
|
||||
$(this).before(error);
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -78,5 +78,5 @@ google.setOnLoadCallback(function() {
|
|||
});
|
||||
|
||||
$('.translation textarea').eq(0).focus();
|
||||
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
{% endblock %}
|
||||
|
||||
{% block pagetitle %}{{block.super}} - {{MESSAGES_SOURCE_LANGUAGE_NAME}} - {{rosetta_i18n_lang_name}} ({{ rosetta_i18n_pofile.percent_translated|floatformat:0 }}%){% endblock %}
|
||||
{% block pagetitle %}{{block.super}} - {{rosetta_settings.MESSAGES_SOURCE_LANGUAGE_NAME}} - {{rosetta_i18n_lang_name}} ({{ rosetta_i18n_pofile.percent_translated|floatformat:0 }}%){% endblock %}
|
||||
|
||||
{% block breadcumbs %}
|
||||
<div>
|
||||
|
|
@ -33,10 +33,10 @@
|
|||
|
||||
<ul class="object-tools">
|
||||
<li class="nobubble">{% trans "Display:" %}</li>
|
||||
<li {% ifequal rosetta_i18n_filter 'untranslated' %}class="active"{% endifequal %}><a href="?filter=untranslated">{% trans "Untranslated only" %}</a></li>
|
||||
<li {% ifequal rosetta_i18n_filter 'translated' %}class="active"{% endifequal %}><a href="?filter=translated">{% trans "Translated only" %}</a></li>
|
||||
<li {% ifequal rosetta_i18n_filter 'fuzzy' %}class="active"{% endifequal %}><a href="?filter=fuzzy">{% trans "Fuzzy only" %}</a></li>
|
||||
<li {% ifequal rosetta_i18n_filter 'all' %}class="active"{% endifequal %}><a href="?filter=all">{% trans "All" %}</a></li>
|
||||
<li {% if rosetta_i18n_filter == 'untranslated' %}class="active"{% endif %}><a href="?filter=untranslated">{% trans "Untranslated only" %}</a></li>
|
||||
<li {% if rosetta_i18n_filter == 'translated' %}class="active"{% endif %}><a href="?filter=translated">{% trans "Translated only" %}</a></li>
|
||||
<li {% if rosetta_i18n_filter == 'fuzzy' %}class="active"{% endif %}><a href="?filter=fuzzy">{% trans "Fuzzy only" %}</a></li>
|
||||
<li {% if rosetta_i18n_filter == 'all' %}class="active"{% endif %}><a href="?filter=all">{% trans "All" %}</a></li>
|
||||
</ul>
|
||||
|
||||
{% if ENABLE_REFLANG %}
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
<div id="changelist" class="module{% if rosetta_i18n_lang_bidi %} rtl{% endif %}">
|
||||
<div id="toolbar">
|
||||
<form id="changelist-search" action="" method="post">
|
||||
<div><!-- DIV needed for valid HTML -->
|
||||
<div>
|
||||
{% rosetta_csrf_token %}
|
||||
<label for="searchbar"><img src="{{ADMIN_IMAGE_DIR}}/icon_searchbox.png" alt="{% trans "Search" %}" /></label>
|
||||
<input type="text" size="40" name="query" value="{% if query %}{{query}}{% endif %}" id="searchbar" tabindex="0" />
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
</div>
|
||||
</form>
|
||||
{% comment %}
|
||||
{% if ENABLE_TRANSLATION_SUGGESTIONS %}
|
||||
{% if rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS %}
|
||||
<form id="translate-all">
|
||||
<div>
|
||||
<input type="submit" class="googleall" value="{% trans "Suggest All Translations" %}" />
|
||||
|
|
@ -123,7 +123,7 @@
|
|||
{% if main_language %}<td class="original">{{ message.main_lang|format_message|linebreaksbr }}</td>{% endif %}
|
||||
<td class="translation">
|
||||
<textarea rows="{{message.msgid|format_message|lines_count}}" cols="40" name="m_{{message.md5hash}}" tabindex="{% increment tab_idx %}">{{message.msgstr}}</textarea>
|
||||
{% if ENABLE_TRANSLATION_SUGGESTIONS %}<a href="#" class="suggest">{% trans "suggest" %}</a>{% endif %}
|
||||
{% if rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS %}<a href="#" class="suggest">{% trans "suggest" %}</a>{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="c">
|
||||
|
|
@ -152,15 +152,15 @@
|
|||
{% if needs_pagination %}
|
||||
{% trans "Skip to page:" %}
|
||||
{% for i in page_range %}
|
||||
{% ifequal i '...' %}
|
||||
{% if i == '...' %}
|
||||
<span class="space">{{ i }}</span>
|
||||
{% else %}
|
||||
{% ifequal i page %}
|
||||
{% if i == page %}
|
||||
<span class="this-page">{{i}}</span>
|
||||
{% else %}
|
||||
<a href="?page={{i}}{% if query %}&query={{query}}{% endif %}">{{i}}</a>
|
||||
{% endifequal %}
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% trans "Displaying:" %}
|
||||
|
|
|
|||
|
|
@ -3,44 +3,48 @@ from django.utils.safestring import mark_safe
|
|||
from django.utils.html import escape
|
||||
import re
|
||||
from django.template import Node
|
||||
from django.utils.encoding import smart_str, smart_unicode
|
||||
from django.template.defaultfilters import stringfilter
|
||||
import six
|
||||
|
||||
|
||||
register = template.Library()
|
||||
rx = re.compile(r'(%(\([^\s\)]*\))?[sd])')
|
||||
|
||||
|
||||
def format_message(message):
|
||||
return mark_safe(rx.sub('<code>\\1</code>', escape(message).replace(r'\n','<br />\n')))
|
||||
format_message=register.filter(format_message)
|
||||
return mark_safe(rx.sub('<code>\\1</code>', escape(message).replace(r'\n', '<br />\n')))
|
||||
format_message = register.filter(format_message)
|
||||
|
||||
|
||||
def lines_count(message):
|
||||
return 1 + sum([len(line)/50 for line in message.split('\n')])
|
||||
lines_count=register.filter(lines_count)
|
||||
return 1 + sum([len(line) / 50 for line in message.split('\n')])
|
||||
lines_count = register.filter(lines_count)
|
||||
|
||||
def mult(a,b):
|
||||
return int(a)*int(b)
|
||||
mult=register.filter(mult)
|
||||
|
||||
def minus(a,b):
|
||||
def mult(a, b):
|
||||
return int(a) * int(b)
|
||||
mult = register.filter(mult)
|
||||
|
||||
|
||||
def minus(a, b):
|
||||
try:
|
||||
return int(a) - int(b)
|
||||
except:
|
||||
return 0
|
||||
minus=register.filter(minus)
|
||||
minus = register.filter(minus)
|
||||
|
||||
def gt(a,b):
|
||||
|
||||
def gt(a, b):
|
||||
try:
|
||||
return int(a) > int(b)
|
||||
except:
|
||||
return False
|
||||
gt=register.filter(gt)
|
||||
gt = register.filter(gt)
|
||||
|
||||
|
||||
def do_incr(parser, token):
|
||||
args = token.split_contents()
|
||||
if len(args) < 2:
|
||||
raise TemplateSyntaxError("'incr' tag requires at least one argument")
|
||||
raise SyntaxError("'incr' tag requires at least one argument")
|
||||
name = args[1]
|
||||
if not hasattr(parser, '_namedIncrNodes'):
|
||||
parser._namedIncrNodes = {}
|
||||
|
|
@ -51,26 +55,28 @@ do_incr = register.tag('increment', do_incr)
|
|||
|
||||
|
||||
class IncrNode(template.Node):
|
||||
def __init__(self, init_val=0):
|
||||
self.val = init_val
|
||||
def __init__(self, init_val=0):
|
||||
self.val = init_val
|
||||
|
||||
def render(self, context):
|
||||
self.val += 1
|
||||
return six.text_type(self.val)
|
||||
|
||||
|
||||
def render(self, context):
|
||||
self.val += 1
|
||||
return smart_unicode(self.val)
|
||||
|
||||
|
||||
def is_fuzzy(message):
|
||||
return message and hasattr(message, 'flags') and 'fuzzy' in message.flags
|
||||
is_fuzzy = register.filter(is_fuzzy)
|
||||
|
||||
|
||||
class RosettaCsrfTokenPlaceholder(Node):
|
||||
def render(self, context):
|
||||
return mark_safe(u"<!-- csrf token placeholder -->")
|
||||
|
||||
|
||||
def rosetta_csrf_token(parser, token):
|
||||
try:
|
||||
from django.template.defaulttags import csrf_token
|
||||
return csrf_token(parser,token)
|
||||
return csrf_token(parser, token)
|
||||
except ImportError:
|
||||
return RosettaCsrfTokenPlaceholder()
|
||||
rosetta_csrf_token=register.tag(rosetta_csrf_token)
|
||||
rosetta_csrf_token = register.tag(rosetta_csrf_token)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from rosetta.conf import settings as rosetta_settings
|
|||
from rosetta.signals import entry_changed, post_save
|
||||
import os
|
||||
import shutil
|
||||
import six
|
||||
import django
|
||||
|
||||
|
||||
|
|
@ -59,14 +60,14 @@ class RosettaTestCase(TestCase):
|
|||
def test_1_ListLoading(self):
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in r.content)
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content))
|
||||
|
||||
def test_2_PickFile(self):
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0,), kwargs=dict()) + '?rosetta')
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
|
||||
self.assertTrue('dummy language' in r.content)
|
||||
self.assertTrue('dummy language' in str(r.content))
|
||||
|
||||
def test_3_DownloadZIP(self):
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
|
|
@ -86,10 +87,10 @@ class RosettaTestCase(TestCase):
|
|||
r = self.client.get(reverse('rosetta-home') + '?filter=untranslated')
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
# make sure both strings are untranslated
|
||||
self.assertTrue('dummy language' in r.content)
|
||||
self.assertTrue('String 1' in r.content)
|
||||
self.assertTrue('String 2' in r.content)
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content)
|
||||
self.assertTrue('dummy language' in str(r.content))
|
||||
self.assertTrue('String 1' in str(r.content))
|
||||
self.assertTrue('String 2' in str(r.content))
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
|
||||
|
||||
# post a translation
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
|
|
@ -100,17 +101,17 @@ class RosettaTestCase(TestCase):
|
|||
r = self.client.get(reverse('rosetta-home'))
|
||||
|
||||
# the translated string no longer is up for translation
|
||||
self.assertTrue('String 1' in r.content)
|
||||
self.assertTrue('String 2' not in r.content)
|
||||
self.assertTrue('String 1' in str(r.content))
|
||||
self.assertTrue('String 2' not in str(r.content))
|
||||
|
||||
# display only translated strings
|
||||
r = self.client.get(reverse('rosetta-home') + '?filter=translated')
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
|
||||
# The tranlsation was persisted
|
||||
self.assertTrue('String 1' not in r.content)
|
||||
self.assertTrue('String 2' in r.content)
|
||||
self.assertTrue('Hello, world' in r.content)
|
||||
self.assertTrue('String 1' not in str(r.content))
|
||||
self.assertTrue('String 2' in str(r.content))
|
||||
self.assertTrue('Hello, world' in str(r.content))
|
||||
|
||||
def test_5_TestIssue67(self):
|
||||
# testcase for issue 67: http://code.google.com/p/django-rosetta/issues/detail?id=67
|
||||
|
|
@ -119,8 +120,8 @@ class RosettaTestCase(TestCase):
|
|||
f_ = open(self.dest_file, 'rb')
|
||||
content = f_.read()
|
||||
f_.close()
|
||||
self.assertTrue(u'Hello, world' not in content)
|
||||
self.assertTrue(u'|| n%100>=20) ? 1 : 2)' in content)
|
||||
self.assertTrue('Hello, world' not in six.text_type(content))
|
||||
self.assertTrue('|| n%100>=20) ? 1 : 2)' in six.text_type(content))
|
||||
del(content)
|
||||
|
||||
# Load the template file
|
||||
|
|
@ -131,10 +132,10 @@ class RosettaTestCase(TestCase):
|
|||
r = self.client.get(reverse('rosetta-home'))
|
||||
|
||||
# make sure all strings are untranslated
|
||||
self.assertTrue('dummy language' in r.content)
|
||||
self.assertTrue('String 1' in r.content)
|
||||
self.assertTrue('String 2' in r.content)
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content)
|
||||
self.assertTrue('dummy language' in str(r.content))
|
||||
self.assertTrue('String 1' in str(r.content))
|
||||
self.assertTrue('String 2' in str(r.content))
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
|
||||
|
||||
# post a translation
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
|
|
@ -143,9 +144,9 @@ class RosettaTestCase(TestCase):
|
|||
f_ = open(self.dest_file, 'rb')
|
||||
content = f_.read()
|
||||
f_.close()
|
||||
self.assertTrue(u'Hello, world' in content)
|
||||
self.assertTrue(u'|| n%100>=20) ? 1 : 2)' in content)
|
||||
self.assertTrue(u'or n%100>=20) ? 1 : 2)' not in content)
|
||||
self.assertTrue('Hello, world' in str(content))
|
||||
self.assertTrue('|| n%100>=20) ? 1 : 2)' in str(content))
|
||||
self.assertTrue('or n%100>=20) ? 1 : 2)' not in str(content))
|
||||
del(content)
|
||||
|
||||
def test_6_ExcludedApps(self):
|
||||
|
|
@ -154,21 +155,21 @@ class RosettaTestCase(TestCase):
|
|||
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' not in r.content)
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' not in str(r.content))
|
||||
|
||||
rosetta_settings.EXCLUDED_APPLICATIONS = ()
|
||||
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?rosetta')
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' in r.content)
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' in str(r.content))
|
||||
|
||||
def test_7_selfInApplist(self):
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' in r.content)
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' in str(r.content))
|
||||
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=project')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' not in r.content)
|
||||
self.assertTrue('rosetta/locale/xx/LC_MESSAGES/django.po' not in str(r.content))
|
||||
|
||||
def test_8_hideObsoletes(self):
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
|
|
@ -178,12 +179,12 @@ class RosettaTestCase(TestCase):
|
|||
# not in listing
|
||||
for p in range(1, 5):
|
||||
r = self.client.get(reverse('rosetta-home') + '?page=%d' % p)
|
||||
self.assertTrue('dummy language' in r.content)
|
||||
self.assertTrue('Les deux' not in r.content)
|
||||
self.assertTrue('dummy language' in str(r.content))
|
||||
self.assertTrue('Les deux' not in str(r.content))
|
||||
|
||||
r = self.client.get(reverse('rosetta-home') + '?query=Les%20Deux')
|
||||
self.assertTrue('dummy language' in r.content)
|
||||
self.assertTrue('Les deux' not in r.content)
|
||||
self.assertTrue('dummy language' in str(r.content))
|
||||
self.assertTrue('Les deux' not in str(r.content))
|
||||
|
||||
def test_9_concurrency(self):
|
||||
shutil.copy(os.path.normpath(os.path.join(self.curdir, './django.po.template')), self.dest_file)
|
||||
|
|
@ -200,34 +201,34 @@ class RosettaTestCase(TestCase):
|
|||
r2 = self.client2.get(reverse('rosetta-home') + '?filter=untranslated')
|
||||
r2 = self.client2.get(reverse('rosetta-home'))
|
||||
|
||||
self.assertTrue('String 1' in r.content)
|
||||
self.assertTrue('String 1' in r2.content)
|
||||
self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in r.content)
|
||||
self.assertTrue('String 1' in str(r.content))
|
||||
self.assertTrue('String 1' in str(r2.content))
|
||||
self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in str(r.content))
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_08e4e11e2243d764fc45a5a4fba5d0f2='Hello, world', _next='_next'), follow=True)
|
||||
r2 = self.client2.get(reverse('rosetta-home'))
|
||||
|
||||
# Client 2 reloads the home, forces a reload of the catalog,
|
||||
# the untranslated string1 is now translated
|
||||
self.assertTrue('String 1' not in r2.content)
|
||||
self.assertTrue('String 2' in r2.content)
|
||||
self.assertTrue('String 1' not in str(r2.content))
|
||||
self.assertTrue('String 2' in str(r2.content))
|
||||
|
||||
r = self.client.get(reverse('rosetta-home') + '?filter=untranslated')
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
r2 = self.client2.get(reverse('rosetta-home') + '?filter=untranslated')
|
||||
r2 = self.client2.get(reverse('rosetta-home'))
|
||||
|
||||
self.assertTrue('String 2' in r2.content and 'm_e48f149a8b2e8baa81b816c0edf93890' in r2.content)
|
||||
self.assertTrue('String 2' in r.content and 'm_e48f149a8b2e8baa81b816c0edf93890' in r.content)
|
||||
self.assertTrue('String 2' in str(r2.content) and 'm_e48f149a8b2e8baa81b816c0edf93890' in str(r2.content))
|
||||
self.assertTrue('String 2' in str(r.content) and 'm_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
|
||||
|
||||
# client 2 posts!
|
||||
r2 = self.client2.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world, from client two!', _next='_next'), follow=True)
|
||||
|
||||
self.assertTrue('save-conflict' not in r2.content)
|
||||
self.assertTrue('save-conflict' not in str(r2.content))
|
||||
|
||||
# uh-oh here comes client 1
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world, from client one!', _next='_next'), follow=True)
|
||||
# An error message is displayed
|
||||
self.assertTrue('save-conflict' in r.content)
|
||||
self.assertTrue('save-conflict' in str(r.content))
|
||||
|
||||
# client 2 won
|
||||
pofile_content = open(self.dest_file, 'r').read()
|
||||
|
|
@ -235,34 +236,34 @@ class RosettaTestCase(TestCase):
|
|||
|
||||
# Both clients show all strings, error messages are gone
|
||||
r = self.client.get(reverse('rosetta-home') + '?filter=translated')
|
||||
self.assertTrue('save-conflict' not in r.content)
|
||||
self.assertTrue('save-conflict' not in str(r.content))
|
||||
r2 = self.client2.get(reverse('rosetta-home') + '?filter=translated')
|
||||
self.assertTrue('save-conflict' not in r2.content)
|
||||
self.assertTrue('save-conflict' not in str(r2.content))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('save-conflict' not in r.content)
|
||||
self.assertTrue('save-conflict' not in str(r.content))
|
||||
r2 = self.client2.get(reverse('rosetta-home'))
|
||||
self.assertTrue('save-conflict' not in r2.content)
|
||||
self.assertTrue('save-conflict' not in str(r2.content))
|
||||
|
||||
# Both have client's two version
|
||||
self.assertTrue('Hello, world, from client two!' in r.content)
|
||||
self.assertTrue('Hello, world, from client two!' in r2.content)
|
||||
self.assertTrue('save-conflict' not in r2.content)
|
||||
self.assertTrue('save-conflict' not in r.content)
|
||||
self.assertTrue('Hello, world, from client two!' in str(r.content))
|
||||
self.assertTrue('Hello, world, from client two!' in str(r2.content))
|
||||
self.assertTrue('save-conflict' not in str(r2.content))
|
||||
self.assertTrue('save-conflict' not in str(r.content))
|
||||
|
||||
def test_10_issue_79_num_entries(self):
|
||||
shutil.copy(os.path.normpath(os.path.join(self.curdir, './django.po.issue79.template')), self.dest_file)
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
|
||||
self.assertTrue('<td class="ch-messages r">1</td>' in r.content)
|
||||
self.assertTrue('<td class="ch-progress r">%s%%</td>' % str(floatformat(0.0, 2)) in r.content)
|
||||
self.assertTrue('<td class="ch-obsolete r">1</td>' in r.content)
|
||||
self.assertTrue('<td class="ch-messages r">1</td>' in str(r.content))
|
||||
self.assertTrue('<td class="ch-progress r">%s%%</td>' % str(floatformat(0.0, 2)) in str(r.content))
|
||||
self.assertTrue('<td class="ch-obsolete r">1</td>' in str(r.content))
|
||||
|
||||
def test_11_issue_80_tab_indexes(self):
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0,), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('tabindex="3"' in r.content)
|
||||
self.assertTrue('tabindex="3"' in str(r.content))
|
||||
|
||||
def test_12_issue_82_staff_user(self):
|
||||
settings.ROSETTA_REQUIRES_AUTH = True
|
||||
|
|
@ -286,35 +287,35 @@ class RosettaTestCase(TestCase):
|
|||
settings.LANGUAGES = (('fr', 'French'), ('xx', 'Dummy Language'),)
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in r.content)
|
||||
self.assertTrue(('contrib') not in r.content)
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content))
|
||||
self.assertTrue(('contrib') not in str(r.content))
|
||||
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=django')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in r.content)
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in str(r.content))
|
||||
|
||||
if self.django_version_major >= 1 and self.django_version_minor >= 3:
|
||||
self.assertTrue(('contrib') in r.content)
|
||||
self.assertTrue(('contrib') in str(r.content))
|
||||
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=all')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in r.content)
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content))
|
||||
|
||||
if self.django_version_major >= 1 and self.django_version_minor >= 3:
|
||||
self.assertTrue(('contrib') in r.content)
|
||||
self.assertTrue(('contrib') in str(r.content))
|
||||
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=project')
|
||||
r = self.client.get(reverse('rosetta-pick-file'))
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in r.content)
|
||||
self.assertTrue(os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in str(r.content))
|
||||
if self.django_version_major >= 1 and self.django_version_minor >= 3:
|
||||
self.assertTrue(('contrib') not in r.content)
|
||||
self.assertTrue(('contrib') not in str(r.content))
|
||||
|
||||
def test_14_issue_99_context_and_comments(self):
|
||||
self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('This is a text of the base template' in r.content)
|
||||
self.assertTrue('Context hint' in r.content)
|
||||
self.assertTrue('This is a text of the base template' in str(r.content))
|
||||
self.assertTrue('Context hint' in str(r.content))
|
||||
|
||||
def test_14_issue_87_entry_changed_signal(self):
|
||||
# copy the template file
|
||||
|
|
@ -329,7 +330,7 @@ class RosettaTestCase(TestCase):
|
|||
self.test_old_msgstr = kwargs.get('old_msgstr')
|
||||
self.test_new_msgstr = sender.msgstr
|
||||
self.test_msg_id = sender.msgid
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content)
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
|
||||
|
||||
# post a translation
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
|
|
@ -350,7 +351,7 @@ class RosettaTestCase(TestCase):
|
|||
def test_receiver(sender, **kwargs):
|
||||
self.test_sig_lang = kwargs.get('language_code')
|
||||
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content)
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
|
||||
|
||||
# post a translation
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
|
|
@ -369,7 +370,7 @@ class RosettaTestCase(TestCase):
|
|||
def test_receiver(sender, **kwargs):
|
||||
self.test_16_has_request = 'request' in kwargs
|
||||
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content)
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
|
||||
|
||||
# post a translation
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
|
|
@ -385,7 +386,7 @@ class RosettaTestCase(TestCase):
|
|||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0, ), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
|
||||
self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in r.content)
|
||||
self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in str(r.content))
|
||||
|
||||
# post a translation, it should have properly wrapped lines
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_bb9d8fe6159187b9ea494c1b313d23d4='Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium.', _next='_next'))
|
||||
|
|
@ -397,7 +398,7 @@ class RosettaTestCase(TestCase):
|
|||
self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0, ), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in r.content)
|
||||
self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in str(r.content))
|
||||
rosetta_settings.POFILE_WRAP_WIDTH = 0
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_bb9d8fe6159187b9ea494c1b313d23d4='Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium.', _next='_next'))
|
||||
pofile_content = open(self.dest_file, 'r').read()
|
||||
|
|
@ -409,9 +410,9 @@ class RosettaTestCase(TestCase):
|
|||
self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0, ), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_1' in r.content)
|
||||
self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_0' in r.content)
|
||||
self.assertTrue('m_09f7e02f1290be211da707a266f153b3' in r.content)
|
||||
self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_1' in str(r.content))
|
||||
self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_0' in str(r.content))
|
||||
self.assertTrue('m_09f7e02f1290be211da707a266f153b3' in str(r.content))
|
||||
|
||||
# post a translation, it should have properly wrapped lines
|
||||
r = self.client.post(reverse('rosetta-home'), dict(
|
||||
|
|
@ -438,7 +439,7 @@ class RosettaTestCase(TestCase):
|
|||
self.client.get(reverse('rosetta-language-selection', args=('xx', 0, ), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertFalse(len(str(self.client.cookies.get('sessionid'))) > 4096)
|
||||
self.assertTrue('m_9efd113f7919952523f06e0d88da9c54' in r.content)
|
||||
self.assertTrue('m_9efd113f7919952523f06e0d88da9c54' in str(r.content))
|
||||
r = self.client.post(reverse('rosetta-home'), dict(
|
||||
m_9efd113f7919952523f06e0d88da9c54='Testing cookie length',
|
||||
_next='_next'
|
||||
|
|
@ -448,8 +449,8 @@ class RosettaTestCase(TestCase):
|
|||
|
||||
self.client.get(reverse('rosetta-home') + '?filter=translated')
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('Testing cookie length' in r.content)
|
||||
self.assertTrue('m_9f6c442c6d579707440ba9dada0fb373' in r.content)
|
||||
self.assertTrue('Testing cookie length' in str(r.content))
|
||||
self.assertTrue('m_9f6c442c6d579707440ba9dada0fb373' in str(r.content))
|
||||
|
||||
# Two, the cookie backend
|
||||
rosetta_settings.STORAGE_CLASS = 'rosetta.storage.SessionRosettaStorage'
|
||||
|
|
@ -461,10 +462,10 @@ class RosettaTestCase(TestCase):
|
|||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue(len(str(self.client.cookies.get('sessionid'))) > 4096)
|
||||
# boom: be a good browser, truncate the cookie
|
||||
self.client.cookies['sessionid'] = unicode(self.client.cookies.get('sessionid'))[:4096]
|
||||
self.client.cookies['sessionid'] = six.text_type(self.client.cookies.get('sessionid'))[:4096]
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
|
||||
self.assertFalse('m_9efd113f7919952523f06e0d88da9c54' in r.content)
|
||||
self.assertFalse('m_9efd113f7919952523f06e0d88da9c54' in str(r.content))
|
||||
|
||||
def test_20_concurrency_of_cache_backend(self):
|
||||
rosetta_settings.STORAGE_CLASS = 'rosetta.storage.CacheRosettaStorage'
|
||||
|
|
@ -485,6 +486,54 @@ class RosettaTestCase(TestCase):
|
|||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
# We have distinct hashes, even though the msgid and msgstr are identical
|
||||
self.assertTrue('m_4765f7de94996d3de5975fa797c3451f' in r.content)
|
||||
self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in r.content)
|
||||
#print (r.content)
|
||||
self.assertTrue('m_4765f7de94996d3de5975fa797c3451f' in str(r.content))
|
||||
self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in str(r.content))
|
||||
|
||||
def test_22_save_header_data(self):
|
||||
shutil.copy(os.path.normpath(os.path.join(self.curdir, './django.po.template')), self.dest_file)
|
||||
|
||||
unicode_user = User.objects.create_user('test_unicode', 'save_header_data@test.com', 'test_unicode')
|
||||
unicode_user.first_name = "aéaéaé aàaàaàa"
|
||||
unicode_user.last_name = "aâââ üüüü"
|
||||
unicode_user.is_superuser, unicode_user.is_staff = True, True
|
||||
unicode_user.save()
|
||||
|
||||
self.client.login(username='test_unicode', password='test_unicode')
|
||||
|
||||
# Load the template file
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home') + '?filter=untranslated')
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
# make sure both strings are untranslated
|
||||
self.assertTrue('dummy language' in str(r.content))
|
||||
self.assertTrue('String 1' in str(r.content))
|
||||
self.assertTrue('String 2' in str(r.content))
|
||||
self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content))
|
||||
|
||||
# post a translation
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
# read the result
|
||||
f_ = open(self.dest_file, 'rb')
|
||||
content = six.text_type(f_.read())
|
||||
f_.close()
|
||||
#print (content)
|
||||
# make sure unicode data was properly converted to ascii
|
||||
self.assertTrue('Hello, world' in content)
|
||||
self.assertTrue('save_header_data@test.com' in content)
|
||||
self.assertTrue('aeaeae aaaaaaa aaaa uuuu' in content)
|
||||
|
||||
def test_23_percent_transaltion(self):
|
||||
shutil.copy(os.path.normpath(os.path.join(self.curdir, './django.po.template')), self.dest_file)
|
||||
|
||||
# Load the template file
|
||||
r = self.client.get(reverse('rosetta-pick-file') + '?filter=third-party')
|
||||
r = self.client.get(reverse('rosetta-language-selection', args=('xx', 0), kwargs=dict()))
|
||||
r = self.client.get(reverse('rosetta-home') + '?filter=untranslated')
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
|
||||
self.assertTrue('Progress: 0.00%' in str(r.content))
|
||||
r = self.client.post(reverse('rosetta-home'), dict(m_e48f149a8b2e8baa81b816c0edf93890='Hello, world', _next='_next'))
|
||||
r = self.client.get(reverse('rosetta-home'))
|
||||
self.assertTrue('Progress: 25.00%' in str(r.content))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
from django.conf.urls.defaults import *
|
||||
try:
|
||||
from django.conf.urls import patterns, include, url
|
||||
except ImportError:
|
||||
from django.conf.urls.defaults import patterns, include, url
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^rosetta/',include('rosetta.urls')),
|
||||
url(r'^admin/$','rosetta.tests.views.dummy', name='dummy-login')
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
from django.conf.urls.defaults import url, patterns
|
||||
try:
|
||||
from django.conf.urls import patterns, url
|
||||
except ImportError:
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
|
||||
urlpatterns = patterns('rosetta.views',
|
||||
url(r'^$', 'home', name='rosetta-home'),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse
|
|||
from django.http import Http404, HttpResponseRedirect, HttpResponse
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.utils.encoding import smart_unicode, iri_to_uri
|
||||
from django.utils.encoding import iri_to_uri
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.decorators.cache import never_cache
|
||||
from rosetta.conf import settings as rosetta_settings
|
||||
|
|
@ -15,12 +15,14 @@ from rosetta.signals import entry_changed, post_save
|
|||
from rosetta.storage import get_storage
|
||||
import re
|
||||
import rosetta
|
||||
import datetime
|
||||
import unicodedata
|
||||
import hashlib
|
||||
import os
|
||||
import six
|
||||
|
||||
|
||||
@never_cache
|
||||
@user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)
|
||||
def home(request):
|
||||
"""
|
||||
Displays a list of messages to be translated
|
||||
|
|
@ -47,7 +49,7 @@ def home(request):
|
|||
return out_
|
||||
|
||||
storage = get_storage(request)
|
||||
version = rosetta.get_version(True)
|
||||
query = ''
|
||||
if storage.has('rosetta_i18n_fn'):
|
||||
rosetta_i18n_fn = storage.get('rosetta_i18n_fn')
|
||||
|
||||
|
|
@ -59,9 +61,9 @@ def home(request):
|
|||
rosetta_i18n_pofile = pofile(rosetta_i18n_fn, wrapwidth=rosetta_settings.POFILE_WRAP_WIDTH)
|
||||
for entry in rosetta_i18n_pofile:
|
||||
entry.md5hash = hashlib.md5(
|
||||
entry.msgid.encode("utf8") +
|
||||
entry.msgstr.encode("utf8") +
|
||||
(entry.msgctxt and entry.msgctxt.encode("utf8") or "")
|
||||
(six.text_type(entry.msgid) +
|
||||
six.text_type(entry.msgstr) +
|
||||
six.text_type(entry.msgctxt or "")).encode('utf8')
|
||||
).hexdigest()
|
||||
|
||||
else:
|
||||
|
|
@ -88,7 +90,7 @@ def home(request):
|
|||
# polib parses .po files into unicode strings, but
|
||||
# doesn't bother to convert plural indexes to int,
|
||||
# so we need unicode here.
|
||||
plural_id = unicode(rx_plural.match(key).groups()[1])
|
||||
plural_id = six.text_type(rx_plural.match(key).groups()[1])
|
||||
|
||||
elif rx.match(key):
|
||||
md5hash = str(rx.match(key).groups()[0])
|
||||
|
|
@ -179,13 +181,12 @@ def home(request):
|
|||
if 'page' in request.GET:
|
||||
query_arg += '&page=%d&_next=1' % int(request.GET.get('page'))
|
||||
return HttpResponseRedirect(reverse('rosetta-home') + iri_to_uri(query_arg))
|
||||
rosetta_i18n_lang_name = _(storage.get('rosetta_i18n_lang_name'))
|
||||
rosetta_i18n_lang_code = storage.get('rosetta_i18n_lang_code')
|
||||
|
||||
if 'query' in request.REQUEST and request.REQUEST.get('query', '').strip():
|
||||
query = request.REQUEST.get('query').strip()
|
||||
rx = re.compile(re.escape(query), re.IGNORECASE)
|
||||
paginator = Paginator([e for e in rosetta_i18n_pofile if not e.obsolete and rx.search(smart_unicode(e.msgstr) + smart_unicode(e.msgid) + u''.join([o[0] for o in e.occurrences]))], rosetta_settings.MESSAGES_PER_PAGE)
|
||||
paginator = Paginator([e for e in rosetta_i18n_pofile if not e.obsolete and rx.search(six.text_type(e.msgstr) + six.text_type(e.msgid) + u''.join([o[0] for o in e.occurrences]))], rosetta_settings.MESSAGES_PER_PAGE)
|
||||
else:
|
||||
if rosetta_i18n_filter == 'untranslated':
|
||||
paginator = Paginator(rosetta_i18n_pofile.untranslated_entries(), rosetta_settings.MESSAGES_PER_PAGE)
|
||||
|
|
@ -232,9 +233,8 @@ def home(request):
|
|||
return HttpResponseRedirect(reverse('rosetta-home') + iri_to_uri(query_arg))
|
||||
|
||||
rosetta_messages = paginator.page(page).object_list
|
||||
main_language = None
|
||||
if rosetta_settings.MAIN_LANGUAGE and rosetta_settings.MAIN_LANGUAGE != rosetta_i18n_lang_code:
|
||||
|
||||
main_language = None
|
||||
for language in settings.LANGUAGES:
|
||||
if language[0] == rosetta_settings.MAIN_LANGUAGE:
|
||||
main_language = _(language[1])
|
||||
|
|
@ -243,7 +243,6 @@ def home(request):
|
|||
fl = ("/%s/" % rosetta_settings.MAIN_LANGUAGE).join(rosetta_i18n_fn.split("/%s/" % rosetta_i18n_lang_code))
|
||||
po = pofile(fl)
|
||||
|
||||
main_messages = []
|
||||
for message in rosetta_messages:
|
||||
message.main_lang = po.find(message.msgid).msgstr
|
||||
|
||||
|
|
@ -259,24 +258,42 @@ def home(request):
|
|||
except AttributeError:
|
||||
ADMIN_MEDIA_PREFIX = settings.STATIC_URL + 'admin/'
|
||||
ADMIN_IMAGE_DIR = ADMIN_MEDIA_PREFIX + 'img/'
|
||||
ENABLE_TRANSLATION_SUGGESTIONS = rosetta_settings.BING_APP_ID and rosetta_settings.ENABLE_TRANSLATION_SUGGESTIONS
|
||||
BING_APP_ID = rosetta_settings.BING_APP_ID
|
||||
MESSAGES_SOURCE_LANGUAGE_NAME = rosetta_settings.MESSAGES_SOURCE_LANGUAGE_NAME
|
||||
MESSAGES_SOURCE_LANGUAGE_CODE = rosetta_settings.MESSAGES_SOURCE_LANGUAGE_CODE
|
||||
|
||||
if storage.has('rosetta_last_save_error'):
|
||||
storage.delete('rosetta_last_save_error')
|
||||
rosetta_last_save_error = True
|
||||
else:
|
||||
rosetta_last_save_error = False
|
||||
|
||||
return render_to_response('rosetta/pofile.html', locals(), context_instance=RequestContext(request))
|
||||
return render_to_response('rosetta/pofile.html', dict(
|
||||
version=rosetta.get_version(True),
|
||||
ADMIN_MEDIA_PREFIX=ADMIN_MEDIA_PREFIX,
|
||||
ADMIN_IMAGE_DIR=ADMIN_IMAGE_DIR,
|
||||
rosetta_settings=rosetta_settings,
|
||||
rosetta_i18n_lang_name=_(storage.get('rosetta_i18n_lang_name')),
|
||||
rosetta_i18n_lang_code=rosetta_i18n_lang_code,
|
||||
rosetta_i18n_lang_bidi=rosetta_i18n_lang_bidi,
|
||||
rosetta_last_save_error=rosetta_last_save_error,
|
||||
rosetta_i18n_filter=rosetta_i18n_filter,
|
||||
rosetta_i18n_write=rosetta_i18n_write,
|
||||
rosetta_messages=rosetta_messages,
|
||||
page_range=needs_pagination and page_range,
|
||||
needs_pagination=needs_pagination,
|
||||
main_language=main_language,
|
||||
rosetta_i18n_app=rosetta_i18n_app,
|
||||
page=page,
|
||||
query=query,
|
||||
paginator=paginator,
|
||||
rosetta_i18n_pofile=rosetta_i18n_pofile
|
||||
), context_instance=RequestContext(request))
|
||||
else:
|
||||
return list_languages(request, do_session_warn=True)
|
||||
home = never_cache(home)
|
||||
home = user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)(home)
|
||||
|
||||
|
||||
@never_cache
|
||||
@user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)
|
||||
def download_file(request):
|
||||
import zipfile
|
||||
from StringIO import StringIO
|
||||
storage = get_storage(request)
|
||||
# original filename
|
||||
rosetta_i18n_fn = storage.get('rosetta_i18n_fn', None)
|
||||
|
|
@ -294,9 +311,9 @@ def download_file(request):
|
|||
offered_fn = rosetta_i18n_fn.split('/')[-1]
|
||||
po_fn = str(rosetta_i18n_fn.split('/')[-1])
|
||||
mo_fn = str(po_fn.replace('.po', '.mo')) # not so smart, huh
|
||||
zipdata = StringIO()
|
||||
zipdata = six.BytesIO()
|
||||
zipf = zipfile.ZipFile(zipdata, mode="w")
|
||||
zipf.writestr(po_fn, unicode(rosetta_i18n_pofile).encode("utf8"))
|
||||
zipf.writestr(po_fn, six.text_type(rosetta_i18n_pofile).encode("utf8"))
|
||||
zipf.writestr(mo_fn, rosetta_i18n_pofile.to_binary())
|
||||
zipf.close()
|
||||
zipdata.seek(0)
|
||||
|
|
@ -308,10 +325,10 @@ def download_file(request):
|
|||
|
||||
except Exception:
|
||||
return HttpResponseRedirect(reverse('rosetta-home'))
|
||||
download_file = never_cache(download_file)
|
||||
download_file = user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)(download_file)
|
||||
|
||||
|
||||
@never_cache
|
||||
@user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)
|
||||
def list_languages(request, do_session_warn=False):
|
||||
"""
|
||||
Lists the languages for the current project, the gettext catalog files
|
||||
|
|
@ -339,19 +356,22 @@ def list_languages(request, do_session_warn=False):
|
|||
languages.append(
|
||||
(language[0],
|
||||
_(language[1]),
|
||||
[(get_app_name(l), os.path.realpath(l), pofile(l)) for l in pos],
|
||||
sorted([(get_app_name(l), os.path.realpath(l), pofile(l)) for l in pos], key=lambda app: app[0]),
|
||||
)
|
||||
)
|
||||
try:
|
||||
ADMIN_MEDIA_PREFIX = settings.ADMIN_MEDIA_PREFIX
|
||||
except AttributeError:
|
||||
ADMIN_MEDIA_PREFIX = settings.STATIC_URL + 'admin/'
|
||||
|
||||
version = rosetta.get_version(True)
|
||||
do_session_warn = do_session_warn and 'SessionRosettaStorage' in rosetta_settings.STORAGE_CLASS and 'signed_cookies' in settings.SESSION_ENGINE
|
||||
return render_to_response('rosetta/languages.html', locals(), context_instance=RequestContext(request))
|
||||
list_languages = never_cache(list_languages)
|
||||
list_languages = user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)(list_languages)
|
||||
|
||||
return render_to_response('rosetta/languages.html', dict(
|
||||
version=rosetta.get_version(True),
|
||||
ADMIN_MEDIA_PREFIX=ADMIN_MEDIA_PREFIX,
|
||||
do_session_warn=do_session_warn,
|
||||
languages=languages,
|
||||
has_pos=has_pos
|
||||
), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def get_app_name(path):
|
||||
|
|
@ -359,6 +379,8 @@ def get_app_name(path):
|
|||
return app
|
||||
|
||||
|
||||
@never_cache
|
||||
@user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)
|
||||
def lang_sel(request, langid, idx):
|
||||
"""
|
||||
Selects a file to be translated
|
||||
|
|
@ -376,14 +398,14 @@ def lang_sel(request, langid, idx):
|
|||
file_ = find_pos(langid, project_apps=project_apps, django_apps=django_apps, third_party_apps=third_party_apps)[int(idx)]
|
||||
|
||||
storage.set('rosetta_i18n_lang_code', langid)
|
||||
storage.set('rosetta_i18n_lang_name', unicode([l[1] for l in settings.LANGUAGES if l[0] == langid][0]))
|
||||
storage.set('rosetta_i18n_lang_name', six.text_type([l[1] for l in settings.LANGUAGES if l[0] == langid][0]))
|
||||
storage.set('rosetta_i18n_fn', file_)
|
||||
po = pofile(file_)
|
||||
for entry in po:
|
||||
entry.md5hash = hashlib.md5(
|
||||
entry.msgid.encode("utf8") +
|
||||
entry.msgstr.encode("utf8") +
|
||||
(entry.msgctxt and entry.msgctxt.encode("utf8") or "")
|
||||
entry.md5hash = hashlib.new('md5',
|
||||
(six.text_type(entry.msgid) +
|
||||
six.text_type(entry.msgstr) +
|
||||
six.text_type(entry.msgctxt or "")).encode('utf8')
|
||||
).hexdigest()
|
||||
|
||||
storage.set('rosetta_i18n_pofile', po)
|
||||
|
|
@ -394,8 +416,6 @@ def lang_sel(request, langid, idx):
|
|||
storage.set('rosetta_i18n_write', False)
|
||||
|
||||
return HttpResponseRedirect(reverse('rosetta-home'))
|
||||
lang_sel = never_cache(lang_sel)
|
||||
lang_sel = user_passes_test(lambda user: can_translate(user), settings.LOGIN_URL)(lang_sel)
|
||||
|
||||
def ref_sel(request, langid):
|
||||
storage = get_storage(request)
|
||||
|
|
|
|||
11
runtests.sh
11
runtests.sh
|
|
@ -1,5 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ ! -d venv_13 ]
|
||||
then
|
||||
virtualenv --no-site-packages --distribute --python=python2 venv_13
|
||||
. venv_13/bin/activate
|
||||
pip install Django==1.3 coverage python-memcached six
|
||||
deactivate
|
||||
fi
|
||||
|
||||
|
||||
. venv_13/bin/activate
|
||||
cd testproject
|
||||
python manage.py test rosetta
|
||||
cd ..
|
||||
deactivate
|
||||
|
|
|
|||
16
runtests_coverage.sh
Normal file
16
runtests_coverage.sh
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ ! -d venv_13 ]
|
||||
then
|
||||
virtualenv --no-site-packages --distribute --python=python2 venv_13
|
||||
. venv_13/bin/activate
|
||||
pip install Django==1.3 coverage python-memcached six
|
||||
deactivate
|
||||
fi
|
||||
|
||||
. venv_13/bin/activate
|
||||
cd testproject
|
||||
coverage run --rcfile=.coveragerc manage.py test --failfast rosetta
|
||||
coverage xml
|
||||
coverage html
|
||||
cd ..
|
||||
|
|
@ -1,5 +1,36 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ ! -d venv_13 ]
|
||||
then
|
||||
virtualenv --no-site-packages --distribute --python=python2 venv_13
|
||||
. venv_13/bin/activate
|
||||
pip install Django==1.3 coverage python-memcached six
|
||||
deactivate
|
||||
fi
|
||||
if [ ! -d venv_14 ]
|
||||
then
|
||||
virtualenv --no-site-packages --distribute --python=python2 venv_14
|
||||
. venv_14/bin/activate
|
||||
pip install Django==1.4 coverage python-memcached six
|
||||
deactivate
|
||||
fi
|
||||
if [ ! -d venv_15 ]
|
||||
then
|
||||
virtualenv --no-site-packages --distribute --python=python2 venv_15
|
||||
. venv_15/bin/activate
|
||||
pip install Django==1.5 coverage python-memcached six
|
||||
deactivate
|
||||
fi
|
||||
if [ ! -d venv_15_p3 ]
|
||||
then
|
||||
virtualenv --no-site-packages --distribute --python=python3 venv_15_p3
|
||||
. venv_15_p3/bin/activate
|
||||
pip install Django==1.5 coverage python3-memcached six
|
||||
deactivate
|
||||
fi
|
||||
|
||||
|
||||
|
||||
. venv_13/bin/activate
|
||||
cd testproject
|
||||
python manage.py --version
|
||||
|
|
@ -21,3 +52,11 @@ python manage.py test rosetta
|
|||
cd ..
|
||||
deactivate
|
||||
|
||||
. venv_15_p3/bin/activate
|
||||
cd testproject
|
||||
python manage.py --version
|
||||
python --version
|
||||
python manage.py test rosetta
|
||||
cd ..
|
||||
deactivate
|
||||
|
||||
|
|
|
|||
6
setup.py
6
setup.py
|
|
@ -20,5 +20,9 @@ setup(
|
|||
'Framework :: Django',
|
||||
],
|
||||
include_package_data=True,
|
||||
zip_safe=False
|
||||
zip_safe=False,
|
||||
install_requires=[
|
||||
'six >=1.2.0',
|
||||
'Django >= 1.3'
|
||||
]
|
||||
)
|
||||
|
|
|
|||
10
testproject/.coveragerc
Normal file
10
testproject/.coveragerc
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[run]
|
||||
branch = True
|
||||
source =
|
||||
rosetta
|
||||
omit =
|
||||
../*migrations*
|
||||
../*tests*
|
||||
../*polib*
|
||||
[report]
|
||||
precision = 2
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#from __future__ import unicode_literals
|
||||
import django
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
from django.conf.urls.defaults import patterns, include, url
|
||||
try:
|
||||
from django.conf.urls import patterns, include, url
|
||||
except ImportError:
|
||||
from django.conf.urls.defaults import patterns, include, url
|
||||
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
from django.contrib import admin
|
||||
|
|
@ -16,3 +21,5 @@ urlpatterns = patterns('',
|
|||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^rosetta/', include('rosetta.urls'))
|
||||
)
|
||||
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
|
|
|
|||
Loading…
Reference in a new issue