Skip to content

Commit 201d504

Browse files
committed
More docs
1 parent 2509311 commit 201d504

File tree

12 files changed

+462
-48
lines changed

12 files changed

+462
-48
lines changed

docs/api/convenience.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
::: liquid.parse
22
::: liquid.render
33
::: liquid.render_async
4+
::: liquid.extract_liquid

docs/api/extra.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
::: liquid.extra.BaseTranslateFilter
2+
::: liquid.extra.Currency
3+
::: liquid.extra.DateTime
4+
::: liquid.extra.GetText
5+
::: liquid.extra.JSON
6+
::: liquid.extra.NGetText
7+
::: liquid.extra.NPGetText
8+
::: liquid.extra.Number
9+
::: liquid.extra.PGetText
10+
::: liquid.extra.Translate
11+
::: liquid.extra.TranslateTag
12+
::: liquid.extra.Unit

docs/api/messages.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
::: liquid.Translations
2+
::: liquid.MessageTuple
3+
::: liquid.extract_from_template

docs/babel.md

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
This page covers i18n and l10n filter and tag configuration, and how we support translations and translation message catalogs. See the [filter reference](optional_filters.md) and [tag reference](optional_tags.md) for usage examples.
2+
3+
## Currency
4+
5+
The `currency` filter returns the input number formatted as currency for the current locale. For usage examples see [`currency`](optional_filters.md#currency) in the filter reference.
6+
7+
### Options
8+
9+
`currency` defaults to looking for a locale in a render context variable called `locale`, and a currency code in a render context variable called `currency_code`. It outputs in the locale's standard format and falls back to `en_US` and `USD` if those context variables don't exist.
10+
11+
```python
12+
from liquid import parse
13+
14+
template = parse("{{ 100457.99 | currency }}")
15+
16+
print(template.render())
17+
print(template.render(currency_code="GBP"))
18+
print(template.render(locale="de", currency_code="CAD"))
19+
```
20+
21+
```plain title="output"
22+
$100,457.99
23+
£100,457.99
24+
100.457,99 CA$
25+
```
26+
27+
To configure `currency`, register a new instance of [`Currency`](api/extra.md#liquid.extra.Currency) with an [`Environment`](environment.md#managing-tags-and-filters), then render your templates from that. See [the API reference](api/extra.md#liquid.extra.Currency) for details of all arguments accepted by `Currency`.
28+
29+
```python
30+
from liquid.extra import Currency
31+
from liquid import Environment
32+
33+
env = Environment()
34+
env.filters["currency"] = Currency(default_locale="de")
35+
```
36+
37+
### Money
38+
39+
For convenience, some "money" filters are defined that mimic Shopify's money filter behavior. These are instances of [`Currency()`](api/extra.md#liquid.extra.Currency) with specific default formats. All other currency options are set to their defaults.
40+
41+
```python
42+
from liquid import parse
43+
44+
template = parse("""\
45+
{% assign amount = 10 %}
46+
{{ amount | money }}
47+
{{ amount | money_with_currency }}
48+
{{ amount | money_without_currency }}
49+
{{ amount | money_without_trailing_zeros }}""")
50+
51+
print(template.render(currency_code="CAD", locale="en_CA"))
52+
```
53+
54+
```plain title="output"
55+
$10.00
56+
$10.00 CAD
57+
10.00
58+
$10
59+
```
60+
61+
## DateTime
62+
63+
The `datetime` filter returns the input _datetime_ formatted for the current locale. For usage examples see [`datetime`](optional_filters.md#datetime) in the filter reference.
64+
65+
### Options
66+
67+
`datetime` defaults to looking for a timezone in a render context variable called `timezone`, a locale in a render context variable called `locale` and a datetime format in a render context variable called `datetime_format`.
68+
69+
```python
70+
from liquid import parse
71+
72+
template = parse("{{ 'Apr 1, 2007, 3:30:00 PM' | datetime }}")
73+
74+
print(template.render())
75+
print(template.render(locale="de", datetime_format="long"))
76+
print(template.render(locale="de", timezone="CET", datetime_format="short"))
77+
```
78+
79+
```plain title="output"
80+
Apr 1, 2007, 3:30:00 PM
81+
1. April 2007 um 15:30:00 UTC
82+
01.04.07, 17:30
83+
```
84+
85+
To configure `datetime`, register a new instance of [`DateTime`](api/extra.md#liquid.extra.DateTime) with an [`Environment`](environment.md#managing-tags-and-filters), then render your templates from that. See [the API reference](api/extra.md#liquid.extra.DateTime) for details of all arguments accepted by `DateTime`.
86+
87+
```python
88+
from liquid.extra import DateTime
89+
from liquid import Environment
90+
91+
env = Environment()
92+
env.filters["datetime"] = DateTime(timezone_var="tz")
93+
```
94+
95+
## Decimal
96+
97+
The `decimal` filter returns the input number formatted as a decimal for the current locale. For usage examples see [`decimal`](optional_filters.md#decimal) in the filter reference.
98+
99+
### Options
100+
101+
`decimal` defaults to looking for a locale in a render context variable called `locale`. It uses the locale's standard format and falls back to `en_US` if that variable does not exist.
102+
103+
```python
104+
from liquid import parse
105+
106+
# Parse a number from a string in the default (en_US) input locale.
107+
template = parse("""\
108+
{{ '10,000.23' | decimal }}
109+
{{ '10,000.23' | decimal: group_separator: false }}
110+
""")
111+
112+
print(template.render(locale="de"))
113+
print(template.render(locale="en_GB"))
114+
```
115+
116+
```plain title="output"
117+
10.000,23
118+
10000,23
119+
120+
10,000.23
121+
10000.23
122+
```
123+
124+
To configure `decimal`, register a new instance of [`Number`](api/extra.md#liquid.extra.Number) with an [`Environment`](environment.md#managing-tags-and-filters), then render your templates from that.
125+
126+
```python
127+
from liquid.extra import Number
128+
from liquid import Environment
129+
130+
env = Environment()
131+
env.filters["decimal"] = Number(default_locale="en_GB")
132+
```
133+
134+
## Unit
135+
136+
The `unit` filter returns he input number formatted with the given units according to the current locale. For usage examples see [`unit`](optional_filters.md#unit) in the filter reference.
137+
138+
### Options
139+
140+
`unit` defaults to looking for a locale in a render context variable called `locale`, a length in a render context variable called `unit_length`, and a decimal format in a render context variable called `unit_format`.
141+
142+
```python
143+
from liquid import parse
144+
145+
template = parse("""\
146+
{{ 12 | unit: 'length-meter', format: '#,##0.00' }}
147+
{{ 150 | unit: 'kilowatt', denominator_unit: 'hour' }}
148+
""")
149+
150+
print(template.render(unit_length="long"))
151+
print(template.render(locale="de", unit_length="long"))
152+
```
153+
154+
```plain title="output"
155+
12.00 meters
156+
150 kilowatts per hour
157+
158+
12,00 Meter
159+
150 Kilowatt pro Stunde
160+
```
161+
162+
To configure `unit`, register a new instance of [`Unit`](api/extra.md#liquid.extra.Unit) with an [`Environment`](environment.md#managing-tags-and-filters), then render your templates from that.
163+
164+
```python
165+
from liquid.extra import Unit
166+
from liquid import Environment
167+
168+
env = Environment()
169+
env.filters["unit"] = Unit(locale_var="_locale")
170+
```
171+
172+
## Translations
173+
174+
Liquid Babel includes [`gettext`](optional_filters.md#gettext), [`ngettext`](optional_filters.md#ngettext), [`pgettext`](optional_filters.md#pgettext) and [`npgettext`](optional_filters.md#npgettext) filter equivalents to the functions found in [Python's gettext module](https://docs.python.org/3.10/library/gettext.html#gnu-gettext-api). Application developers can choose to use any of these filters, possibly using more user friendly filter names, and/or the more general [`t (translate)`](optional_filters.md#t) filter.
175+
176+
The [`t`](optional_filters.md#t) filter can behave like any of the \*gettext filters, depending on the arguments it is given. Where the \*gettext filters require positional arguments for `context`, `count` and `plural`, `t` reserves optional `count` and `plural` keyword arguments.
177+
178+
Liquid Babel also offers a [`{% translate %}`](optional_tags.md#translate) tag. This is similar to the [`{% trans %}`](https://jinja.palletsprojects.com/en/3.1.x/templates/#i18n) tag found in Jinja or the [`{% blocktranslate %}`](https://docs.djangoproject.com/en/4.1/topics/i18n/translation/#blocktranslate-template-tag) tag found in Django's template language. Again, application developers can configure and customize the included `translate` tag to suit an application's needs.
179+
180+
### Filters
181+
182+
[`gettext`](optional_filters.md#gettext), [`ngettext`](optional_filters.md#ngettext), [`npgettext`](optional_filters.md#npgettext), [`pgettext`](optional_filters.md#pgettext) and [`t`](optional_filters.md#t) filters all default to looking for [translations](#message-catalogs) in a render context variable called `translations`, falling back to an instance of [`NullTranslations`](https://docs.python.org/3.10/library/gettext.html#the-nulltranslations-class) if `translations` can not be resolved.
183+
184+
```python
185+
from liquid import parse
186+
187+
# You'll need to load an appropriate Translations object.
188+
# `get_translations()` is defined elsewhere.
189+
translations = get_translations(locale="de")
190+
191+
template = parse("{{ 'Hello, World!' | t }}")
192+
print(template.render(translations=translations)) # Hallo Welt!
193+
```
194+
195+
To configure [`gettext`](optional_filters.md#gettext), [`ngettext`](optional_filters.md#ngettext), [`npgettext`](optional_filters.md#npgettext), [`pgettext`](optional_filters.md#pgettext) or [`t`](optional_filters.md#t), register a new instance of [`GetText`](api/extra.md#liquid.extra.GetText), [`NGetText`](api/extra.md#liquid.extra.NGetText), [`NPGetText`](api/extra.md#liquid.extra.NPGetText), [`PGetText`](api/extra.md#liquid.extra.PGetText) or [`Translate`](api/extra.md#liquid.extra.Translate) with an [`Environment`](environment.md#managing-tags-and-filters), then render your templates from that. All of these classes inherit from [`BaseTranslateFilter`](api/extra.md#liquid.extra.BaseTranslateFilter) and accept the same constructor arguments.
196+
197+
### Message catalogs
198+
199+
By default, all translation filters and tags will look for a render context variable called `translations`, which must be an object implementing the `Translations` protocol. It is the application developer's responsibility to provide a `Translations` object, being the interface between Liquid and a message catalog.
200+
201+
The `Translations` protocol is defined as follows. It is simply a subset of the [`NullTranslations`](https://docs.python.org/3.10/library/gettext.html#gettext.NullTranslations) class found in the [gettext module](https://docs.python.org/3.10/library/gettext.html#gnu-gettext-api).
202+
203+
```python
204+
class Translations(Protocol):
205+
def gettext(self, message: str) -> str:
206+
...
207+
208+
def ngettext(self, singular: str, plural: str, n: int) -> str:
209+
...
210+
211+
def pgettext(self, context: str, message: str) -> str:
212+
...
213+
214+
def npgettext(self, context: str, singular: str, plural: str, n: int) -> str:
215+
...
216+
```
217+
218+
It could be a [`GNUTranslations`](https://docs.python.org/3.10/library/gettext.html#the-gnutranslations-class) instance, a [Babel `Translations`](https://babel.pocoo.org/en/latest/support.html#extended-translations-class) instance, or any object implementing `gettext`, `ngettext`, `pgettext` and `npgettext` methods.
219+
220+
### Message variables
221+
222+
Translatable message text can contain placeholders for variables. When using variables in strings to be translated by filters, variables are defined using percent-style formatting. Only the `s` modifier is supported and every variable must have a name. In this example `you` is the variable name.
223+
224+
```liquid
225+
{{ "Hello, %(you)s!" | t }}
226+
```
227+
228+
Filter keyword arguments are merged with the current render context before being used to replace variables in message text. All variables are converted to their string representation before substitution. Dotted property/attribute access is not supported inside message variables.
229+
230+
```liquid
231+
{{ "Hello, %(you)s!" | t: you: user.name }}
232+
```
233+
234+
The [`translate`](optional_tags.md#translate) block tag recognizes simplified Liquid output statements as translation message variables. These variables must be valid identifiers without dotted or bracketed property/attribute access, and no filters.
235+
236+
```liquid
237+
{% translate %}
238+
Hello, {{ you }}!
239+
{% endtranslate %}
240+
```
241+
242+
Keyword arguments passed to the [`translate`](optional_tags.md#translate) tag will be merged with the current render context before being used to replace variables in message text.
243+
244+
```liquid
245+
{% translate you: user.name, count: users.size %}
246+
Hello, {{ you }}!
247+
{% plural %}
248+
Hello, {{ you }}s!
249+
{% endtranslate %}
250+
```
251+
252+
### Message Extraction
253+
254+
Use the [`extract_from_templates()`](api/messages.md#liquid.extract_from_template) function to build a message [catalog](https://babel.pocoo.org/en/latest/api/messages/catalog.html#catalogs) from one or more templates. You are then free to make use of [Babel's PO file features](https://babel.pocoo.org/en/latest/api/messages/pofile.html), or convert the catalog to a more convenient internal representation.
255+
256+
```python
257+
import io
258+
259+
from babel.messages.pofile import write_po
260+
261+
from liquid import parse
262+
from liquid.messages import extract_from_templates
263+
264+
source = """
265+
{% # Translators: some comment %}
266+
{{ 'Hello, World!' | t }}
267+
{% comment %}Translators: other comment{% endcomment %}
268+
{% translate count: 2 %}
269+
Hello, {{ you }}!
270+
{% plural %}
271+
Hello, all!
272+
{% endtranslate %}
273+
"""
274+
275+
template = parse(source, name="something.liquid")
276+
catalog = extract_from_templates(template)
277+
278+
buf = io.BytesIO()
279+
write_po(buf, catalog, omit_header=True)
280+
print(buf.getvalue().decode("utf-8"))
281+
```
282+
283+
```plain title="output"
284+
#. Translators: some comment
285+
#: something.liquid:3
286+
msgid "Hello, World!"
287+
msgstr ""
288+
289+
#. Translators: other comment
290+
#: something.liquid:5
291+
#, python-format
292+
msgid "Hello, %(you)s!"
293+
msgid_plural "Hello, all!"
294+
msgstr[0] ""
295+
msgstr[1] ""
296+
```
297+
298+
### Translator Comments
299+
300+
When a Liquid comment tag immediately precedes a translatable filter or tag, and the comment starts with a string in `comment_tags`, that comment will be included as a translator comment with the message. Use the `comment_tags` argument to [`extract_liquid()`](api/convenience.md#liquid.extract_liquid), or [`extract_from_templates()`](api/messages.md#liquid.extract_from_template) to change translator comment prefixes. The default is `["Translators:"]`.
301+
302+
```python
303+
from liquid import parse
304+
from liquid.messages import extract_from_templates
305+
306+
source = """
307+
{% # Translators: some comment %}
308+
{{ 'Hello, World!' | t }}
309+
{% comment %}Translators: other comment{% endcomment %}
310+
{% translate count: 2 %}
311+
Hello, {{ you }}!
312+
{% plural %}
313+
Hello, all!
314+
{% endtranslate %}
315+
"""
316+
317+
template = parse(source, name="something.liquid")
318+
catalog = extract_from_templates(template, strip_comment_tags=True)
319+
320+
message = catalog.get("Hello, World!")
321+
print(message.auto_comments[0]) # some comment
322+
```

docs/environment.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ env = MyLiquidEnvironment(
5454

5555
## Managing tags and filters
5656

57-
As you'd expect, [`builtin.register()`](api/builtin.md#liquid.builtin.register) registers all the default tags and filters with the environment. You are encouraged to override `setup_tags_and_filters()` in your `Environment` subclasses to add optional or custom tags and filters, remove unwanted default tags and filters, and possibly replace default implementation with your own.
57+
[`builtin.register()`](api/builtin.md#liquid.builtin.register) registers all the default tags and filters with the environment. You are encouraged to override `setup_tags_and_filters()` in your `Environment` subclasses to add optional or custom tags and filters, remove unwanted default tags and filters, and possibly replace default implementation with your own.
5858

5959
It's also OK to manipulate [`Environment.tags`](api/environment.md#liquid.Environment.tags) and [`Environment.filters`](api/environment.md#liquid.Environment.filters) directly after an `Environment` instance has been created. They are just dictionaries mapping tag names to instances of [`Tag`](api/tag.md) and filter names to callables, respectively.
6060

@@ -65,6 +65,18 @@ env = Environment()
6565
del env.tags["include"]
6666
```
6767

68+
### Extra tags and filters
69+
70+
Python Liquid includes some [extra tags](optional_tags.md) and [extra filters](optional_filters.md) that are not enabled by default. If you want to enable them all, pass `extra=True` when constructing a Liquid [`Environment`](api/environment.md).
71+
72+
```python
73+
from liquid import Environment
74+
75+
env = Environment(extra=True)
76+
print(env.render("{{ 100457.99 | money }}"))
77+
# $100,457.99
78+
```
79+
6880
## Managing global variables
6981

7082
By default, global template variables attached to instances of [`Template`](api/template.md) take priority over global template variables attached to an `Environment`. You can change this priority or otherwise manipulate the `globals` dictionary for a `Template` by overriding [`Environment.make_globals()`](api/environment.md#liquid.Environment.make_globals).
@@ -88,10 +100,6 @@ class MyLiquidEnvironment(Environment):
88100
return dict(self.globals)
89101
```
90102

91-
## Extra tags and filters
92-
93-
TODO:
94-
95103
## Tolerance
96104

97105
Templates are parsed and rendered in strict mode by default. Where syntax and render-time type errors raise an exception as soon as possible. You can change the error tolerance mode with the `tolerance` argument to [`Environment`](api/environment.md).
@@ -264,7 +272,7 @@ template.render(you="something longer that exceeds our limit")
264272

265273
By default, strings in Liquid can not be looped over with the `{% for %}` tag and characters in a string can not be selected by index.
266274

267-
Setting the `string_sequences` class attribute to `True` tells Python Liquid to treat strings as sequences, meaning we can loop over Unicode characters in a string for retrieve a Unicode "character" by its index.
275+
Setting the `string_sequences` class attribute to `True` tells Python Liquid to treat strings as sequences, meaning we can loop over Unicode characters in a string or retrieve a Unicode "character" by its index.
268276

269277
## String first and last
270278

0 commit comments

Comments
 (0)