-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy path__init__.py
More file actions
130 lines (109 loc) · 4.27 KB
/
__init__.py
File metadata and controls
130 lines (109 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from __future__ import absolute_import, unicode_literals
import six
import babel
import babel.numbers
import babel.plural
from fluent.syntax import FluentParser
from fluent.syntax.ast import Message, Term
from .builtins import BUILTINS
from .prepare import Compiler
from .resolver import ResolverEnvironment, CurrentEnvironment, TextElement
from .utils import native_to_fluent
from .fallback import FluentLocalization, AbstractResourceLoader, FluentResourceLoader
__all__ = [
'FluentLocalization',
'AbstractResourceLoader',
'FluentResourceLoader',
'FluentResource',
'FluentBundle',
]
def FluentResource(source):
parser = FluentParser()
return parser.parse(source)
class FluentBundle(object):
"""
Bundles are single-language stores of translations. They are
aggregate parsed Fluent resources in the Fluent syntax and can
format translation units (entities) to strings.
Always use `FluentBundle.get_message` to retrieve translation units from
a bundle. Generate the localized string by using `format_pattern` on
`message.value` or `message.attributes['attr']`.
Translations can contain references to other entities or
external arguments, conditional logic in form of select expressions, traits
which describe their grammatical features, and can use Fluent builtins.
See the documentation of the Fluent syntax for more information.
"""
def __init__(self, locales, functions=None, use_isolating=True):
self.locales = locales
_functions = BUILTINS.copy()
if functions:
_functions.update(functions)
self._functions = _functions
self.use_isolating = use_isolating
self._messages = {}
self._terms = {}
self._compiled = {}
self._compiler = Compiler()
self._babel_locale = self._get_babel_locale()
self._plural_form = babel.plural.to_python(self._babel_locale.plural_form)
def add_resource(self, resource, allow_overrides=False):
# TODO - warn/error about duplicates
for item in resource.body:
if not isinstance(item, (Message, Term)):
continue
map_ = self._messages if isinstance(item, Message) else self._terms
full_id = item.id.name
if full_id not in map_ or allow_overrides:
map_[full_id] = item
def has_message(self, message_id):
return message_id in self._messages
def get_message(self, message_id):
return self._lookup(message_id)
def _lookup(self, entry_id, term=False):
if term:
compiled_id = '-' + entry_id
else:
compiled_id = entry_id
try:
return self._compiled[compiled_id]
except LookupError:
pass
entry = self._terms[entry_id] if term else self._messages[entry_id]
self._compiled[compiled_id] = self._compiler(entry)
return self._compiled[compiled_id]
def format_to_parts(self, pattern, errors, args=None):
if isinstance(pattern, TextElement):
yield pattern.value
return
if args is not None:
fluent_args = {
argname: native_to_fluent(argvalue)
for argname, argvalue in args.items()
}
else:
fluent_args = {}
env = ResolverEnvironment(context=self,
current=CurrentEnvironment(args=fluent_args),
errors=errors)
for part in pattern(env):
yield part
def format_pattern(self, pattern, args=None):
errors = []
try:
result = ''.join(self.format_part(part) for part in self.format_to_parts(pattern, errors, args=args))
except ValueError as e:
errors.append(e)
result = '{???}'
return [result, errors]
def format_part(self, fluentish):
if isinstance(fluentish, six.string_types):
return fluentish
return fluentish.format(self._babel_locale)
def _get_babel_locale(self):
for l in self.locales:
try:
return babel.Locale.parse(l.replace('-', '_'))
except babel.UnknownLocaleError:
continue
# TODO - log error
return babel.Locale.default()